Beginning Python - From Novice to Professional
Beginning Python - From Novice to Professional Beginning Python - From Novice to Professional
CHAPTER 21 ■ PROJECT 2: PAINTING A PRETTY PICTURE 417 Figure 21-3. A simple sunspot graph Although it is pleasing to have made a program that works, there is clearly still room for improvement. Second Implementation So, what did you learn from your prototype? You have figured out the basics of how to draw stuff with ReportLab. You have also seen how you can extract the data in a way that works well for drawing your graph. However, there are some weaknesses in the program. To position things properly, I had to add some ad hoc modifications to the values and time stamps. And the program doesn’t actually get the data from anywhere. (More specifically, it “gets” the data from a list inside the program itself, rather than reading it from an outside source.) Unlike Project 1 (in Chapter 20), the second implementation won’t be much larger or more complicated than the first. It will be an incremental improvement that will use some more appropriate features from ReportLab and actually fetch its data from the Internet. Getting the Data As you saw in Chapter 14, you can fetch files across the Internet with the standard module urllib. Its function urlopen works in a manner quite similar to open, but takes a URL instead of a file name as its argument. When you have opened the file and read its contents, you have to filter out what you don’t need. The file contains empty lines (consisting of only whitespace) and lines beginning with some special characters (# and :). The program should ignore these. (See the example file fragment in the section “Preparations” earlier in this chapter.)
418 CHAPTER 21 ■ PROJECT 2: PAINTING A PRETTY PICTURE Assuming that the URL is kept in a variable called URL, and that the variable COMMENT_CHARS has been set to the string '#:', you can get a list of rows (as in our original program) like this: data = [] for line in urlopen(URL).readlines(): if not line.isspace() and not line[0] in COMMENT_CHARS: data.append(map(float, line.split())) The preceding code will include all the columns in the data list, although we aren’t particularly interested in the ones pertaining to radio flux. However, those columns will be filtered out when we extract the columns we really need (as we did in the original program). ■Note If you are using a data source of your own (or if, by the time you read this, the data format of the sunspot file has changed), you will, of course, have to modify this code accordingly. Using the LinePlot Class If you thought getting the data was surprisingly simple, drawing a prettier line plot isn’t much of a challenge either. In a situation like this, it’s best to thumb through the documentation (in this case, the ReportLab docs) to see if there is a feature already in the system that can do what you need, so you don’t have to implement it all yourself. Luckily, there is just such a thing: the LinePlot class from the module reportlab.graphics.charts.lineplots. Of course, we could have looked for this to begin with, but in the spirit of rapid prototyping, we just used what was at hand to see what we could do. Now it’s time to go one step further. The LinePlot is instantiated without any arguments, and then you set its attributes before adding it to the Drawing. The main attributes you need to set are x, y, height, width, and data. The first four should be self-explanatory; the latter is simply a list of point-lists, where a pointlist is a list of tuples, like the one we used in our PolyLines. To top it off, let’s set the stroke color of each line. The final code is shown in Listing 21-3. The resulting figure is shown in Figure 21-4. Listing 21-3. The Final Sunspot Program from urllib import urlopen from reportlab.graphics.shapes import * from reportlab.graphics.charts.lineplots import LinePlot from reportlab.graphics.charts.textlabels import Label from reportlab.graphics import renderPDF URL = 'http://www.sec.noaa.gov/ftpdir/weekly/Predict.txt' COMMENT_CHARS = '#:' drawing = Drawing(400, 200) data = []
- Page 397 and 398: 366 CHAPTER 17 ■ EXTENDING PYTHON
- Page 399 and 400: 368 CHAPTER 17 ■ EXTENDING PYTHON
- Page 401 and 402: 370 CHAPTER 17 ■ EXTENDING PYTHON
- Page 404 and 405: CHAPTER 18 ■ ■ ■ Packaging Yo
- Page 406 and 407: CHAPTER 18 ■ PACKAGING YOUR PROGR
- Page 408 and 409: CHAPTER 18 ■ PACKAGING YOUR PROGR
- Page 410 and 411: CHAPTER 18 ■ PACKAGING YOUR PROGR
- Page 412 and 413: CHAPTER 19 ■ ■ ■ Playful Prog
- Page 414 and 415: CHAPTER 19 ■ PLAYFUL PROGRAMMING
- Page 416 and 417: CHAPTER 19 ■ PLAYFUL PROGRAMMING
- Page 418 and 419: CHAPTER 19 ■ PLAYFUL PROGRAMMING
- Page 420: CHAPTER 19 ■ PLAYFUL PROGRAMMING
- Page 423 and 424: 392 CHAPTER 20 ■ PROJECT 1: INSTA
- Page 425 and 426: 394 CHAPTER 20 ■ PROJECT 1: INSTA
- Page 427 and 428: 396 CHAPTER 20 ■ PROJECT 1: INSTA
- Page 429 and 430: 398 CHAPTER 20 ■ PROJECT 1: INSTA
- Page 431 and 432: 400 CHAPTER 20 ■ PROJECT 1: INSTA
- Page 433 and 434: 402 CHAPTER 20 ■ PROJECT 1: INSTA
- Page 435 and 436: 404 CHAPTER 20 ■ PROJECT 1: INSTA
- Page 437 and 438: 406 CHAPTER 20 ■ PROJECT 1: INSTA
- Page 439 and 440: 408 CHAPTER 20 ■ PROJECT 1: INSTA
- Page 442 and 443: CHAPTER 21 ■ ■ ■ Project 2: P
- Page 444 and 445: CHAPTER 21 ■ PROJECT 2: PAINTING
- Page 446 and 447: CHAPTER 21 ■ PROJECT 2: PAINTING
- Page 450 and 451: CHAPTER 21 ■ PROJECT 2: PAINTING
- Page 452 and 453: CHAPTER 22 ■ ■ ■ Project 3: X
- Page 454 and 455: CHAPTER 22 ■ PROJECT 3: XML FOR A
- Page 456 and 457: CHAPTER 22 ■ PROJECT 3: XML FOR A
- Page 458 and 459: CHAPTER 22 ■ PROJECT 3: XML FOR A
- Page 460 and 461: CHAPTER 22 ■ PROJECT 3: XML FOR A
- Page 462 and 463: CHAPTER 22 ■ PROJECT 3: XML FOR A
- Page 464 and 465: CHAPTER 22 ■ PROJECT 3: XML FOR A
- Page 466 and 467: CHAPTER 22 ■ PROJECT 3: XML FOR A
- Page 468: CHAPTER 22 ■ PROJECT 3: XML FOR A
- Page 471 and 472: 440 CHAPTER 23 ■ PROJECT 4: IN TH
- Page 473 and 474: 442 CHAPTER 23 ■ PROJECT 4: IN TH
- Page 475 and 476: 444 CHAPTER 23 ■ PROJECT 4: IN TH
- Page 477 and 478: 446 CHAPTER 23 ■ PROJECT 4: IN TH
- Page 479 and 480: 448 CHAPTER 23 ■ PROJECT 4: IN TH
- Page 481 and 482: 450 CHAPTER 23 ■ PROJECT 4: IN TH
- Page 483 and 484: 452 CHAPTER 23 ■ PROJECT 4: IN TH
- Page 486 and 487: CHAPTER 24 ■ ■ ■ Project 5: A
- Page 488 and 489: CHAPTER 24 ■ PROJECT 5: A VIRTUAL
- Page 490 and 491: CHAPTER 24 ■ PROJECT 5: A VIRTUAL
- Page 492 and 493: CHAPTER 24 ■ PROJECT 5: A VIRTUAL
- Page 494 and 495: CHAPTER 24 ■ PROJECT 5: A VIRTUAL
- Page 496 and 497: CHAPTER 24 ■ PROJECT 5: A VIRTUAL
418 CHAPTER 21 ■ PROJECT 2: PAINTING A PRETTY PICTURE<br />
Assuming that the URL is kept in a variable called URL, and that the variable COMMENT_CHARS<br />
has been set <strong>to</strong> the string '#:', you can get a list of rows (as in our original program) like this:<br />
data = []<br />
for line in urlopen(URL).readlines():<br />
if not line.isspace() and not line[0] in COMMENT_CHARS:<br />
data.append(map(float, line.split()))<br />
The preceding code will include all the columns in the data list, although we aren’t particularly<br />
interested in the ones pertaining <strong>to</strong> radio flux. However, those columns will be filtered<br />
out when we extract the columns we really need (as we did in the original program).<br />
■Note If you are using a data source of your own (or if, by the time you read this, the data format of the<br />
sunspot file has changed), you will, of course, have <strong>to</strong> modify this code accordingly.<br />
Using the LinePlot Class<br />
If you thought getting the data was surprisingly simple, drawing a prettier line plot isn’t much<br />
of a challenge either. In a situation like this, it’s best <strong>to</strong> thumb through the documentation (in<br />
this case, the ReportLab docs) <strong>to</strong> see if there is a feature already in the system that can do what<br />
you need, so you don’t have <strong>to</strong> implement it all yourself. Luckily, there is just such a thing: the<br />
LinePlot class from the module reportlab.graphics.charts.lineplots. Of course, we could<br />
have looked for this <strong>to</strong> begin with, but in the spirit of rapid pro<strong>to</strong>typing, we just used what was<br />
at hand <strong>to</strong> see what we could do. Now it’s time <strong>to</strong> go one step further.<br />
The LinePlot is instantiated without any arguments, and then you set its attributes before<br />
adding it <strong>to</strong> the Drawing. The main attributes you need <strong>to</strong> set are x, y, height, width, and data.<br />
The first four should be self-explana<strong>to</strong>ry; the latter is simply a list of point-lists, where a pointlist<br />
is a list of tuples, like the one we used in our PolyLines.<br />
To <strong>to</strong>p it off, let’s set the stroke color of each line. The final code is shown in Listing 21-3.<br />
The resulting figure is shown in Figure 21-4.<br />
Listing 21-3. The Final Sunspot Program<br />
from urllib import urlopen<br />
from reportlab.graphics.shapes import *<br />
from reportlab.graphics.charts.lineplots import LinePlot<br />
from reportlab.graphics.charts.textlabels import Label<br />
from reportlab.graphics import renderPDF<br />
URL = 'http://www.sec.noaa.gov/ftpdir/weekly/Predict.txt'<br />
COMMENT_CHARS = '#:'<br />
drawing = Drawing(400, 200)<br />
data = []