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