2

I have written a pretty convoluted script for creating graphics - via the Tkinter module.

It works as I expect, and saves the canvas as a PostScript file.

Only, I can't render the PS file in anything. At all.

I was pretty confident when I decided to use Tkinter that there would be something I could do with the PS files to get them into some more standard format.

I have seen some tutorials that suggest porting the drawings to PIL, which might work, but will be quite a task to port all the dynamic objects from tk to PIL.

I wondered if anyone had a quicker/dirtier way of getting the pixels off the widget window into an image file.

Or any windows methods of viewing/rasterising the PS file? I can place an example PS file somewhere if there is interest? (the python code is quite involved, and requires 3 MySQL tables to pull the data together)

I am trying to use the screengrab method from here: http://mail.python.org/pipermail/image-sig/2003-May/002292.html

and and struggle to get things in the right order.

tkinter code:

def drawCircles(MasterList,buildlist):
master = Tk()
w = Canvas(master, width=1000, height=1000)
w.config(bg='white')
coordsMain = MasterList[6:]
textMain = MasterList[0:2]
w.pack()
w.create_oval(*coordsMain, width=3, fill = "ivory3")
masterLabel = "Source PUID\n" + str(MasterList[3]) + "\nFiles = " + str(MasterList[4])
w.create_text(*textMain, text=masterLabel, justify = "center", font=("Helvetica", 16))
for i in buildlist:
 coordsSet = i[6:10]
 w.create_oval(*coordsSet, width=3, fill = i[5])
 set_label = i[3] + "\n" + str(i[4]) + "%"
 l=w.create_text(4,4, text=set_label, justify = "center", fill="white", font=("Helvetica", 16))
 a,b,c,d= (w.bbox(l))
 bboxArea =(c-a)*(d-b)   
 a,b,c,d = i[6:10]
 circleArea = (c-a)*(d-b)
 if bboxArea>circleArea:
  textSet = i[10:]
  j=w.create_text(*textSet, text=set_label, justify = "center", font=("Helvetica", 16))
  r=w.create_rectangle(w.bbox(j),fill="white", width=0)
 else:
  textSet = i[:2]
  j=w.create_text(*textSet, text=set_label, justify = "center", font=("Helvetica", 16))
  r=w.create_rectangle(w.bbox(j),fill=i[5], width=0) 
 w.tag_lower(r,j)
 PUID = str(MasterList[3])
 PUID = PUID.replace('/', '-')
 filename = "\images\\" + PUID + ".jpg"
mainloop()

Screen grab code:

x0 =w.winfo_rootx()
y0 =w.winfo_rooty()
x1 =x0 + w.winfo_width()
y1 =y0 + w.winfo_height()
im =ImageGrab.grab((x0, y0, x1, y1))
im.save(filename)

I can make a jpg this way, but can't seem to get the contents of the widget in the the jgp (the file that gets created declares as a jpg, but has no image payload)

If I place the screen grab code after the mainloop, it has says its destroyed the object, before mainloop, its not built the object yet....

Jay
  • 753
  • 3
  • 11
  • 19

3 Answers3

1

You could try and hand it off to ghostscript in a subprocess. That can render PostScript in almost any bitmap format there is.

Roland Smith
  • 42,427
  • 3
  • 64
  • 94
  • I have tried opening the images from command line Ghostscipt, and they don't render at all - I'm still fiddling with the settings to see if I can get it flying. – Jay Feb 29 '12 at 22:28
  • Can you put the PostScript file up somewhere? I've generated PostScript from Python before. It's not that difficult. – Roland Smith Feb 29 '12 at 23:05
  • Sure - http://dl.dropbox.com/u/59536414/fmt-15.PS that should be a 1000 x 1000 canvas – Jay Feb 29 '12 at 23:15
  • I see two problems in the postscript code. First, it sets a clipping path, and then draws all the text _outside_ it. Second, all the text is drawn in RGB 1,1,1 i.e. white. If I remove the clipping path and set the color to 0,0,0, I see a couple of overlapping texts; they are all drawn at the same place. – Roland Smith Mar 01 '12 at 00:22
  • Hmm. OK. I'll see if I can figure that out. The image save code is inculded above, and was very problematic to get working in the first place. Foolishly I didn't check the output, just that is did make a ps. The white text is probably my fault - in the code I force it to make some text, so I can measure the bbox, then potentially move the text - now I know how to use update I might be able to negate this. Thank you. – Jay Mar 01 '12 at 00:42
  • Added info - placing the c.update() call before the save call completely fixes the PS save issues. – Jay Mar 01 '12 at 22:15
1

I think what may be happening is that the actual frame isn't getting updated in the script. I can reproduce your symptoms -- file produced but payload-free -- by commenting out the cv.update() call in the following:

import Tkinter as tk
root = tk.Tk()
root.title("Simple plot")
cv = tk.Canvas(width=200, height=200, bg='white')
cv.pack()
cv.create_text(100, 100, text="hello world!")
cv.update() # comment out to make empty postscript!
cv.postscript(file="my_drawing.ps", colormode='color')
DSM
  • 342,061
  • 65
  • 592
  • 494
  • Awesome - I split the grabber code - so now it works - like this. Update, then get x,x1,y,y1, then mainloop, then the last two im lines. Thank you. I was missing the update call. How did you know to use this? – Jay Feb 29 '12 at 23:25
  • Went like this: googled "tkinter postscript". Found [this link](http://www.daniweb.com/software-development/python/code/216929), from which I stole the above code. Tried it, didn't work. Tried it under ipython, and then it did, to my surprise. Realized that the real difference was interactivity; googled "tkinter update", and the rest is history. – DSM Feb 29 '12 at 23:31
  • awesome, thank you. I really appreciate your time. I'm going to leave this open a little while longer incase I get anything useful from the PS opening thread. – Jay Feb 29 '12 at 23:34
1

It turns out the update() function is required prior to postscript call, as specified by tk canvas manpage.

pathName postscript ?option value option value ...?

Note: by default Postscript is only generated for information that appears in the canvas's window on the screen. If the canvas is freshly created it may still have its initial size of 1x1 pixel so nothing will appear in the Postscript. To get around this problem either invoke the "update" command to wait for the canvas window to reach its final size, or else use the -width and -height options to specify the area of the canvas to print.

I have marked this as the fix because the PS file is the preferred save format, and is of much higher quality than the screengrab method.

The working code now becomes:

def drawCircles(MasterList,buildlist):
master = Tk()
w = Canvas(master, width=1000, height=1000)
w.config(bg='white')
coordsMain = MasterList[6:]
textMain = MasterList[0:2]
w.pack()
w.create_oval(*coordsMain, width=3, fill = "ivory3")
masterLabel = "Source PUID\n" + str(MasterList[3]) + "\nFiles = " + str(MasterList[4])
w.create_text(*textMain, text=masterLabel, justify = "center", font=("Helvetica", 16))
for i in buildlist:
 coordsSet = i[6:10]
 w.create_oval(*coordsSet, width=3, fill = i[5])
 set_label = i[3] + "\n" + str(i[4]) + "%"
 l=w.create_text(4,4, text=set_label, justify = "center", fill="white", font=("Helvetica", 16))
 a,b,c,d= (w.bbox(l))
 bboxArea =(c-a)*(d-b)   
 a,b,c,d = i[6:10]
 circleArea = (c-a)*(d-b)
 if bboxArea>circleArea:
  textSet = i[10:]
  j=w.create_text(*textSet, text=set_label, justify = "center", font=("Helvetica", 16))
  r=w.create_rectangle(w.bbox(j),fill="white", width=0)
 else:
  textSet = i[:2]
  j=w.create_text(*textSet, text=set_label, justify = "center", font=("Helvetica", 16))
  r=w.create_rectangle(w.bbox(j),fill=i[5], width=0) 
 w.tag_lower(r,j)
 PUID = str(MasterList[3])
 PUID = PUID.replace('/', '-')
 filename = "\images\\" + PUID + ".PS"
 w.update()
mainloop()
Community
  • 1
  • 1
Jay
  • 753
  • 3
  • 11
  • 19