2

I've written a mini example for DrawingArea which, when started, displays nothing. If I insert a raw_input() just for waiting for a keyboard press at a specific place, it functions, so this is a workaround. Here's the code:

#!/usr/bin/env python

import pygtk
pygtk.require('2.0')
import gtk

R = 300

window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.set_default_size(R, R)
drawing_area = gtk.DrawingArea()
window.add(drawing_area)
window.show_all()
gc = drawing_area.get_style().fg_gc[gtk.STATE_NORMAL]

if 0:
  raw_input()

drawing_area.window.draw_line(gc, R/10, R/10, R*9/10, R*9/10)

raw_input()

This version doesn't display the drawn line in the opening window; upon pressing enter in the shell, it will just terminate (and remove the window). But if I enable the raw_input() at the if 0: block, it waits twice for an enter in the shell and between the two enters it will display the drawn line (so in general the code works, it seems to be just a weird refresh problem).

I also tried to flush the event queue of GTK using this snippet:

while gtk.events_pending():  # drain the event pipe
  gtk.main_iteration()

I inserted it at various places, always to no avail.

I also tried the usual gtk.main() as the last command in the script (of course). But it also didn't help.

How do I do this correctly and why is that raw_input() having that strange side-effect?

Charles
  • 50,943
  • 13
  • 104
  • 142
Alfe
  • 56,346
  • 20
  • 107
  • 159
  • I found out that it seems to be a timing problem. That `raw_input()` works because it typically waits long enough (I always have to refocus on the shell window which takes about a second). If I use that draining of the event pipe code snippet and insert a `sleep(0.1)` after calling the `main_iteration()` method, a lot more events get processed which did not seem to be in the queue on the beginning. So maybe my question is rather: How do I find out whether the drawing area is ready to accept drawing attempts? Is there sth like a `window.is_ready()` or similar? – Alfe Apr 11 '13 at 14:49

1 Answers1

2

You should connect to your drawing area's expose-event signal. That is the only place that you should try to draw on the drawing area; the reason for this is that anything you draw is erased again when the window is minimized or another window moves over it. However, the expose event always happens at the right time so you can keep the drawing up-to-date whenever it is needed.

Like this:

def on_drawing_area_expose(drawing_area, event, data=None):
    # ... do your drawing here ...

drawing_area.connect('expose-event', on_drawing_area_expose)

Also check out drawing with Cairo, which is the preferred and more flexible way. Here is a tutorial.

ptomato
  • 56,175
  • 13
  • 112
  • 165
  • That's very enlighting! Thanks for this tutorial. Cairo seems to be built-in in the pygtk stuff, am I right? Actually, I'd like to set millions of dots in my drawing; to do this more than once doesn't make sense (too slow, takes hours). I should set the dots (single pixels) in a bitmap then, I guess. I was under the impression that a DrawingArea is bitmap-oriented, now I understand that it is vector-oriented. Do you also have a pointer to a tutorial for bitmap-oriented stuff? – Alfe Apr 15 '13 at 13:45
  • No, DrawingArea is actually bitmap-oriented. But you really cannot avoid drawing everything more than once. If the drawing takes too long, then the solution is to use a backing store; draw the points on that, and paint it to the DrawingArea whenever an expose event occurs. – ptomato Apr 15 '13 at 14:01
  • Hmm, lacking basics, I guess. In tkinter I could create a pixmap object, place that pixmap on a canvas and then set bits in the pixmap. The canvas would do the refresh for me then. Any hints on how to do this "backing store" stuff in pygtk? Is there an equivalent to the tkinter-pixmap object in pygtk? – Alfe Apr 15 '13 at 14:18
  • Btw, that cairo stuff seems pretty neat, as is the tutorial you pointed me to. Playing around with the stuff ... :-) – Alfe Apr 15 '13 at 14:19
  • There is an example in the [PyGTK tutorial](http://www.pygtk.org/pygtk2tutorial/ch-Scribble.html) on drawing using a backing store without Cairo. There is also a [C example](https://developer.gnome.org/gtk3/stable/gtk-getting-started.html) that uses Cairo, but it also uses the newer GTK 3 drawing API. You'll have to cobble them together yourself I'm afraid. – ptomato Apr 16 '13 at 09:43
  • Much obliged. The main answer is the fact about using the expose event. I'll have to figure out how to create and manipulate pixmaps which then easily can get redrawn in such an event. – Alfe Apr 16 '13 at 10:08