[pygtk] custom cairo widget

saeed saeed.gnu at gmail.com
Sat Sep 5 22:42:05 WST 2009


That's cool...
I applied what Pietro wrote, and added pointer motion (dragging circle)
Also no need to get pointer and flag. event argument itself has these
data (event.x, event.y, event.button)
And 180 should be changed to 190  (symmetrization: 10 to 190,  OR  20 to 180)
_______________________________________
''' Custom slider '''
import gtk, cairo
from math import pi

def draw_widget(context, x, y, w, h, color=(0.0, 0.0, 0.0), radius=5):
    ''' nakresli obdelnik s radiusy '''

    cr = context
    cr.set_source_rgb(0.3, 0.4, 0.2)

    arc2bezier = 0.55228475
    if radius > w - radius: radius = w / 2
    elif radius > h - radius: radius = h / 2

    c1 = arc2bezier * radius
    c2 = arc2bezier * radius

    cr.new_path()
    cr.move_to( x + radius, y)
    cr.rel_line_to(w - 2 * radius, 0.0)
    cr.rel_curve_to(c1, 0.0, radius, c2, radius, radius)

    cr.rel_line_to(0, h - 2 * radius)
    cr.rel_curve_to(0.0, c2, c1 - radius, radius, -radius, radius)

    cr.rel_line_to(-w + 2 * radius, 0)
    cr.rel_curve_to(-c1, 0, -radius, -c2, -radius, -radius)

    cr.rel_line_to(0, -h + 2 * radius)
    cr.rel_curve_to(0.0, -c2, radius - c1, -radius, radius, -radius)
    cr.close_path()

    cr.stroke_preserve()
    cr.set_source_rgb(color[0], color[1], color[2])
    cr.fill()

class App:
    def __init__(self):
        self.wnd  = gtk.Window()
        self.wnd.set_border_width(5)
        self.wnd.connect('destroy', lambda x: gtk.main_quit())
        self.area = gtk.DrawingArea()
        self.area.set_size_request(32, 200)
        self.area.add_events(gtk.gdk.POINTER_MOTION_MASK |
gtk.gdk.BUTTON_PRESS_MASK)
        self.area.connect('expose-event', self.expose)
        self.area.connect('button-press-event', self.button_press)
        self.area.connect('motion-notify-event', self.motion_notify)
        self.wnd.add(self.area)
        self.wnd.show_all()
        self.old_y = 10


    def expose(self, widget, event):
        #print event.type
        ctx = widget.window.cairo_create()
        ctx.set_line_width(3)
        draw_widget(ctx, 10, 10, 8, 180, radius=10)
        ctx.set_source_rgb(0.5, 0.5, 0.5)
        ctx.arc(14, self.old_y, 9, 0, 2 * pi)
        ctx.stroke_preserve()
        ctx.set_source_rgb(1.0, 0.0, 0.0)
        ctx.fill()


    def button_press(self, widget, event):
        posx = event.x
        posy = event.y
        if event.button==1:
            if posx > 5 and posx < 15:
                if posy < 10: posy = 10
                if posy > 190: posy = 190 ## 190, not 180 (because the
center of circle may be in slider)
                self.old_y = posy
        self.area.queue_draw()

    def motion_notify(self, widget, event):
        posy = event.y
        if event.state & gtk.gdk.BUTTON1_MASK:
            if posy < 10: posy = 10
            if posy > 190: posy = 190 ## 190, not 180 (because the
center of circle may be in slider)
            self.old_y = posy
        self.area.queue_draw()

APP = App()
gtk.main()
______________________________
On 9/5/09, Pietro Battiston <toobaz at email.it> wrote:
> Il giorno sab, 05/09/2009 alle 12.22 +0200, M.B. ha scritto:
>> hi all,
>> im trying write my first custom widget.
>> its cairo slider. on my first look im happy :) but i know that code is
>> ugly. if this code running, Xorg is on 50% and python thread is on 49%
>> CPU !!!. ( using 'top' in console ).
>> Kick me to the right way with this code snipet pls.
>> where is error ? Why too much cpu power ?
>>
>> PS: sry for my english.
>
> Your mistake is that you continuously redraw, since "expose" calls
> "queue_redraw" which (almost immediately) calls (it's its job) "expose".
> This way, you have live animation, but you spend _all_ the time drawing.
> And indeed, notice you aren't using at all events for mouse, which
> however you did register (with self.area.add_events)!
>
> For this widget, you need _two_ handlers:
> - a handler for expose which _just draws_
> - a handler for pointer click and/or movement which updates self.old_y,
> and then calls queue_redraw
>
> bye
>
> Pietro
>


More information about the pygtk mailing list