Implement new class for buttons, using cairo.

portnov [2008-08-04 12:52:08]
Implement new class for buttons, using cairo.
Filename
qwerty.py
diff --git a/qwerty.py b/qwerty.py
index 231bca8..1e40a9c 100755
--- a/qwerty.py
+++ b/qwerty.py
@@ -7,13 +7,20 @@ import re
 from Xlib import X,display
 import Xlib.protocol.event
 import gtk
+from gtk import gdk
+import cairo

-ICON_PATHS = ["/usr/share/pixmaps", "/usr/share/icons/hicolor/48x48/apps", "/usr/share/icons/hicolor/scalable/apps"]
+ICON_PATHS = ["/usr/share/pixmaps", "/usr/share/icons/hicolor/48x48/apps", "/usr/share/icons/hicolor/scalable/apps", "/usr/share/icons"]

 GLOBAL = 0
 SECTION = 1
 KEY = 2

+BUTTON_BORDER = (0.7,0.7,0.7)
+BUTTON_BORDER_ACTIVE = (0,0,0)
+BUTTON_FILL = (0.9, 0.9, 0.9)
+BUTTON_FILL_RUNNING = (0.7, 0.7, 0.7)
+
 config_file = os.path.expanduser('~/.config/qwerty.conf')
 sections = []

@@ -25,42 +32,102 @@ def find_icon(name):
         if os.path.exists(p):
             return p

-class SuperButton(gtk.Button):
-    def __init__(self,key,label=None,icon=None):
-        gtk.Button.__init__(self)
-        self.vbox = gtk.VBox()
-        self.label = gtk.Label()
-        if label:
-            self.label.set_text(label)
+class SuperButton(gtk.DrawingArea):
+    def __init__(self,key, label=None, icon=None):
+        gtk.DrawingArea.__init__(self)
+
         self.key = key
-        self.keyl = gtk.Label()
-        self.keyl.set_markup("<b>" + key + "</b>")
-        self.icon = gtk.Image()
-        if icon:
-            self.set_icon(icon)
-        self.vbox.pack_start(self.label)
-        self.vbox.pack_start(self.icon)
-        self.vbox.pack_start(self.keyl)
-        self.add(self.vbox)
-        self.vbox.show_all()
+        self.label = label
+        self.active = False
         self.running = False
         self.cls = None
+        if icon:
+            self.icon = find_icon(icon)
+        else:
+            self.icon = None
+
+        self.connect("expose-event",self.on_expose)
+        self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.ENTER_NOTIFY_MASK | gtk.gdk.LEAVE_NOTIFY_MASK)
+        self.callback = None
+        self.connect('enter-notify-event', self.on_mouse_over)
+        self.connect('leave-notify-event', self.on_mouse_out)
+
+        self.set_size_request(90,120)
+
+        self.fg = BUTTON_BORDER
+
+    def on_expose(self,widget,event):
+        x,y,self.width,self.height,b = widget.window.get_geometry()
+        cr = widget.window.cairo_create()
+
+        if self.running:
+            cr.set_source_rgb(*BUTTON_FILL_RUNNING)
+        else:
+            cr.set_source_rgb(*BUTTON_FILL)
+        cr.rectangle(4,4,self.width-8,self.height-8)
+        cr.fill()
+        cr.set_source_rgb(*self.fg)
+        cr.rectangle(4,4,self.width-8,self.height-8)
+        cr.stroke()
+
+        if self.active:
+            cr.set_source_rgb(*BUTTON_BORDER_ACTIVE)
+
+        cr.move_to(20, self.height-50)
+        cr.set_font_size(40)
+        cr.show_text(self.key)
+
+        if not self.active:
+            return
+
+        cr.move_to(20,30)
+        cr.set_font_size(16)
+        cr.show_text(self.label)
+
+        if self.icon:
+            pb = gdk.pixbuf_new_from_file(self.icon)
+
+            w = pb.get_width()
+            h = pb.get_height()
+
+            IS = cairo.ImageSurface(cairo.FORMAT_ARGB32, w,h)
+            cr2 = cairo.Context(IS)
+            cr2 = gdk.CairoContext(cr2)
+            cr2.set_source_pixbuf(pb,0,0)
+            cr2.paint()
+
+            x = (self.width - w)/2
+            y = 45
+
+            cr.set_source_surface(IS,x,y)
+            cr.paint()
+
+    def on_mouse_over(self,widget,event):
+        if self.active:
+            self.fg = BUTTON_BORDER_ACTIVE
+            self.queue_draw()
+
+    def on_mouse_out(self,widget,event):
+        self.fg = BUTTON_BORDER
+        self.queue_draw()
+
+    def set_label(self,label):
+        self.label = label
+        self.queue_draw()
+
+    def set_icon(self,icon):
+        self.active = True
+        self.icon = find_icon(icon)
+        self.queue_draw()

     def mark_running(self,cls):
-        l = self.label.get_text()
-        self.label.set_markup("<b>[" + l + "]</b>")
         self.running = True
         self.cls = cls
+        self.queue_draw()

     def clear(self):
-        self.label.set_text('')
-        self.icon.clear()
-
-    def set_icon(self,icon):
-        self.icon.set_from_file(find_icon(icon))
-
-    def set_label(self,label):
-        self.label.set_text(label)
+        self.active = False
+        self.queue_draw()

 class Section(object):
     def __init__(self,title):
@@ -180,10 +247,10 @@ class GUI(object):

         for row in self.rows:
             for btn in row:
-                btn.connect("clicked",self.on_button)
+                btn.connect("button-press-event",self.on_button)

         for btn in self.sbuttons:
-            btn.connect("clicked",self.on_section)
+            btn.connect("button-press-event",self.on_section)

         for i,S in enumerate(sections):
             btn = self.sbuttons[i]
@@ -197,8 +264,8 @@ class GUI(object):
         self.window.connect('destroy', self.exit)
         self.window.connect('key-release-event', self.on_key)

-        self.window.fullscreen()
-#         self.window.resize(800,600)
+#         self.window.fullscreen()
+        self.window.resize(800,600)

         self.collect_windows()

@@ -254,13 +321,13 @@ class GUI(object):
 #         self.send_event(wnd, self.NET_ACTIVE_WINDOW, [X.CurrentTime])
 #         wnd.map()

-    def on_button(self,widget):
+    def on_button(self,widget,event=None):
         if widget.running:
             print "Activating", widget.label.get_text()
             self.activate_window(widget.cls)
         else:
             self.key_exec(widget.key)
-#         self.exit()
+        self.exit()

     def on_section(self,widget):
         self.show_section(widget.key)
ViewGit