Python DevCenter
oreilly.comSafari Books Online.Conferences.

advertisement


Generating Graphics With Piddle
Pages: 1, 2

Drawing the Schedule

Now we've come to the heart of the problem: drawing the actual schedule. This brings up two important questions. First, how will we represent the data? And, second, how will we code the logic to display it? A good answer to the first question will make the second question easier, so let's start there.



Examining our chart, we can guess that our data structure will require at least the following:

  • Labels for the left column (tasks)
  • Left and right coordinates for each box on a row (timeframe)
  • Colors for the boxes (resources)

When we look at the relationship between these elements, we see:

  • Each row has one task.
  • Each task contains one label and any number of boxes.
  • Each box has one start time, one end time, and one color.

Also note that the start and end times don't necessarily coincide with our columns. To keep things simple, we'll divide each month into four equal chunks rather than into weeks or days. So a time of 3 represents the third quarter-month or (given our labels) three fourths of the way through April. We'll also set a variable called now to indicate the current time, which lets us show progress by coloring in the past.

There are many ways to translate all this into Python, but I chose to represent it with a combination of lists, dictionaries, and tuples. I feel this makes the structure easier to read than the other representations I tried.

tasks = [
  {"label": "zikeshop",       "boxes": [(3,   8,  "cornflower"),
                                        (12, 16,  "cornflower")]},
  {"label": "blogdrive",      "boxes": [(8,  12,  "cornflower")]},
  {"label": "day job",        "boxes": [(0,   4,  "red"),
                                        (16,  30, "red")]},
  {"label": "body-for-life",  "boxes": [(4,  16,  "gold"),
                                        (18, 30,  "gold")]},
  {"label": "writing",        "boxes": [(2,   4,  "limegreen"),
                                        (6,  24,  "limegreen")]},
  {"label": "linkwatcher",    "boxes": [(4,   6,  "limegreen")]},
  ]
now = 3

Because each dictionary represents a row, and each boxes value represents the boxes on that row, we can draw the whole thing with a pair of nested loops. The outer loop moves us downward, while the inner loop draws the boxes across the chart.

We need some more math to calculate the exact positions of the boxes, but it's simple. For left and right coordinates, we multiply the time (measured in quarter cell-widths) by (cellW / 4) and add the width of the left column. For top and bottom coordinates, we only have to look at cellH and topRowH.

Here's the code:

## draw "past" shading first, so it goes on bottom:
c.drawRect(lftColW,topRowH, lftColW + (now * (cellW/4)), imgH,
  edgeColor=pid.black, fillColor=pid.slategray)

## draw the charts:
for i in range(len(tasks)):

  # draw label left-aligned and approximately 
  # centered vertically
  c.drawString(tasks[i]["label"],
               5,                          # left
               topRowH + 15 + (i * cellH), # top
               pid.Font(face="sansserif",size=12,bold=1))

 # draw the boxes
 for box in tasks[i]["boxes"]:
   begin, end, color = box
   c.drawRect(lftColW + ((cellW / 4) * begin), # left
              topRowH + (i * cellH),           # top
              lftColW + ((cellW / 4) * end),   # right
              topRowH + ((i+1) * cellH),       # bottom
              edgeColor=pid.black,
              fillColor=getattr(pid, color, pid.white))

One last thing. If you run this code, you'll notice that some of the boxes extend past the right edge of the chart, and thus overwrite the right hand border. The proper time to draw the outside border is at the end of the script, after we've drawn any runaway boxes.

## redraw outer border
c.drawRect(0, 0, imgW-1, imgH-1)

Where to Next?

Now you can draw simple graphics with piddle. There's plenty more to piddle that we haven't touched on. The piddle reference manual and the examples on the piddle home page can give you some ideas of what else piddle can do. For the latest news and discussions, check out the pythonpiddle mailing list.

There's obviously a lot more we could add to the Gantt chart script. A real Gantt chart would likely have some sort of key to label the different colors, and would probably provide a more accurate timeline. It would also be nice to have a user-friendly interface for specifying the schedule and the ability to store multiple schedules in external files or a database. In fact, next time, we'll write a simple CGI interface to build a schedule and generate Gantt charts in real time on the Web.

Michal Wallace is a web developer and entrepreneur in Atlanta, Georgia.


Return to the Python DevCenter.





Sponsored by: