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