| 1 | import sys |
|---|
| 2 | |
|---|
| 3 | import gobject |
|---|
| 4 | import pango |
|---|
| 5 | import gtk |
|---|
| 6 | from gtk import gdk |
|---|
| 7 | |
|---|
| 8 | if gtk.pygtk_version < (2, 8): |
|---|
| 9 | print "PyGtk 2.8 or later required for this example" |
|---|
| 10 | raise SystemExit |
|---|
| 11 | |
|---|
| 12 | try: |
|---|
| 13 | import cairo |
|---|
| 14 | except ImportError: |
|---|
| 15 | raise SystemExit("cairo required for this example") |
|---|
| 16 | |
|---|
| 17 | TEXT = 'A GtkWidget implemented in PyGTK' |
|---|
| 18 | BORDER_WIDTH = 10 |
|---|
| 19 | |
|---|
| 20 | # A quite simple gtk.Widget subclass which demonstrates how to subclass |
|---|
| 21 | # and do realizing, sizing and drawing. |
|---|
| 22 | |
|---|
| 23 | class PyGtkWidget(gtk.Widget): |
|---|
| 24 | def __init__(self, text): |
|---|
| 25 | gtk.Widget.__init__(self) |
|---|
| 26 | self._layout = self.create_pango_layout(text) |
|---|
| 27 | self._layout.set_font_description(pango.FontDescription("Sans Serif 16")) |
|---|
| 28 | |
|---|
| 29 | # GtkWidget |
|---|
| 30 | |
|---|
| 31 | def do_realize(self): |
|---|
| 32 | # The do_realize method is responsible for creating GDK (windowing system) |
|---|
| 33 | # resources. In this example we will create a new gdk.Window which we |
|---|
| 34 | # then draw on |
|---|
| 35 | |
|---|
| 36 | # First set an internal flag telling that we're realized |
|---|
| 37 | self.set_flags(self.flags() | gtk.REALIZED) |
|---|
| 38 | |
|---|
| 39 | # Create a new gdk.Window which we can draw on. |
|---|
| 40 | # Also say that we want to receive exposure events by setting |
|---|
| 41 | # the event_mask |
|---|
| 42 | self.window = gdk.Window( |
|---|
| 43 | self.get_parent_window(), |
|---|
| 44 | width=self.allocation.width, |
|---|
| 45 | height=self.allocation.height, |
|---|
| 46 | window_type=gdk.WINDOW_CHILD, |
|---|
| 47 | wclass=gdk.INPUT_OUTPUT, |
|---|
| 48 | event_mask=self.get_events() | gdk.EXPOSURE_MASK) |
|---|
| 49 | |
|---|
| 50 | # Associate the gdk.Window with ourselves, Gtk+ needs a reference |
|---|
| 51 | # between the widget and the gdk window |
|---|
| 52 | self.window.set_user_data(self) |
|---|
| 53 | |
|---|
| 54 | # Attach the style to the gdk.Window, a style contains colors and |
|---|
| 55 | # GC contextes used for drawing |
|---|
| 56 | self.style.attach(self.window) |
|---|
| 57 | |
|---|
| 58 | # The default color of the background should be what |
|---|
| 59 | # the style (theme engine) tells us. |
|---|
| 60 | self.style.set_background(self.window, gtk.STATE_NORMAL) |
|---|
| 61 | self.window.move_resize(*self.allocation) |
|---|
| 62 | |
|---|
| 63 | def do_unrealize(self): |
|---|
| 64 | # The do_unrealized method is responsible for freeing the GDK resources |
|---|
| 65 | |
|---|
| 66 | # De-associate the window we created in do_realize with ourselves |
|---|
| 67 | self.window.set_user_data(None) |
|---|
| 68 | |
|---|
| 69 | def do_size_request(self, requisition): |
|---|
| 70 | # The do_size_request method Gtk+ is calling on a widget to ask |
|---|
| 71 | # it the widget how large it wishes to be. It's not guaranteed |
|---|
| 72 | # that gtk+ will actually give this size to the widget |
|---|
| 73 | |
|---|
| 74 | # In this case, we say that we want to be as big as the |
|---|
| 75 | # text is, plus a little border around it. |
|---|
| 76 | width, height = self._layout.get_size() |
|---|
| 77 | requisition.width = width // pango.SCALE + BORDER_WIDTH*4 |
|---|
| 78 | requisition.height = height // pango.SCALE + BORDER_WIDTH*4 |
|---|
| 79 | |
|---|
| 80 | def do_size_allocate(self, allocation): |
|---|
| 81 | # The do_size_allocate is called by when the actual size is known |
|---|
| 82 | # and the widget is told how much space could actually be allocated |
|---|
| 83 | |
|---|
| 84 | # Save the allocated space |
|---|
| 85 | self.allocation = allocation |
|---|
| 86 | |
|---|
| 87 | # If we're realized, move and resize the window to the |
|---|
| 88 | # requested coordinates/positions |
|---|
| 89 | if self.flags() & gtk.REALIZED: |
|---|
| 90 | self.window.move_resize(*allocation) |
|---|
| 91 | |
|---|
| 92 | def do_expose_event(self, event): |
|---|
| 93 | # The do_expose_event is called when the widget is asked to draw itself |
|---|
| 94 | # Remember that this will be called a lot of times, so it's usually |
|---|
| 95 | # a good idea to write this code as optimized as it can be, don't |
|---|
| 96 | # Create any resources in here. |
|---|
| 97 | |
|---|
| 98 | # In this example, draw a rectangle in the foreground color |
|---|
| 99 | x, y, w, h = self.allocation |
|---|
| 100 | cr = self.window.cairo_create() |
|---|
| 101 | cr.set_source_color(self.style.fg[self.state]) |
|---|
| 102 | cr.rectangle(BORDER_WIDTH, BORDER_WIDTH, |
|---|
| 103 | w - 2*BORDER_WIDTH, h - 2*BORDER_WIDTH) |
|---|
| 104 | cr.set_line_width(5.0) |
|---|
| 105 | cr.set_line_join(cairo.LINE_JOIN_ROUND) |
|---|
| 106 | cr.stroke() |
|---|
| 107 | |
|---|
| 108 | # And draw the text in the middle of the allocated space |
|---|
| 109 | fontw, fonth = self._layout.get_pixel_size() |
|---|
| 110 | cr.move_to((w - fontw)/2, (h - fonth)/2) |
|---|
| 111 | cr.update_layout(self._layout) |
|---|
| 112 | cr.show_layout(self._layout) |
|---|
| 113 | |
|---|
| 114 | gobject.type_register(PyGtkWidget) |
|---|
| 115 | |
|---|
| 116 | def main(args): |
|---|
| 117 | win = gtk.Window() |
|---|
| 118 | win.set_border_width(5) |
|---|
| 119 | win.set_title('Widget test') |
|---|
| 120 | win.connect('delete-event', gtk.main_quit) |
|---|
| 121 | |
|---|
| 122 | frame = gtk.Frame("Example frame") |
|---|
| 123 | win.add(frame) |
|---|
| 124 | |
|---|
| 125 | w = PyGtkWidget(TEXT) |
|---|
| 126 | frame.add(w) |
|---|
| 127 | |
|---|
| 128 | win.show_all() |
|---|
| 129 | |
|---|
| 130 | gtk.main() |
|---|
| 131 | |
|---|
| 132 | if __name__ == '__main__': |
|---|
| 133 | sys.exit(main(sys.argv)) |
|---|