I have found a couple of unusual behaviors of drawing with cairo in
Pygtk and was wondering if there were solutions or if they needed to
be reported as bugs.

The first is that a sub-class of a GtkDrawingArea widget renders
correctly on its own in a window, but if placed in a box with another
widget, only renders if it's the first of only two widgets in the
container.  It does not render at all  hen placed in a GtkTable.  This
behavior is demonstrated in test.py.

The second is that when sub-classing GtkDrawingArea,
self.get_allocation returns a GdkRectangle with the right x, height
and width values but the wrong y value.  This behavior is demonstrated
in deka.py.  There are 5 rows of polygons in each column.  If you
can't see them, make your window taller until the fifth row comes into
view.  This is the problem.

Cheers,

Adam Schreiber
#!/usr/bin/env python

import gtk
import math

class Bar(gtk.DrawingArea):
    def __init__(self):
        gtk.DrawingArea.__init__(self)
        self.connect("expose_event", self.expose)
    
    def expose(self, widget, event):
        self.context = widget.window.cairo_create()
        
        # set a clip region for the expose event
        self.context.rectangle(event.area.x, event.area.y,
                               event.area.width, event.area.height)
        self.context.clip()

        self.draw(self.context)
        
        return False
        
    def draw(self, context):
        rect = self.get_allocation()
        
        print "width = {0}, height = {1}, x = {2}, y = {3}".format(rect.width,rect.height, rect.x, rect.y)
        
        context.rectangle(gtk.gdk.Rectangle(rect.x + rect.width/3,
                                            rect.y + 5,
                                            rect.width/3,
                                            rect.height - 10))
        context.set_source_rgba(0.0, 0.0, 0.0, 0.25)
        context.fill_preserve()
        context.set_source_rgba(0.0, 0.0, 0.0, 1.0)
        context.stroke()
        
def main():
    window = gtk.Window()
    window.set_default_size(200, 200)
    vbox = gtk.VBox()
    hbox = gtk.HBox()
    button = gtk.Button()
    bar = Bar()
    
    hbox.add(bar)
    hbox.add(button)
    vbox.add(hbox)
    window.add(vbox)
    window.connect("destroy", gtk.main_quit)
    window.show_all()
    
    window = gtk.Window()
    window.set_default_size(200, 200)
    vbox = gtk.VBox()
    hbox = gtk.HBox()
    button = gtk.Button()
    bar = Bar()
    
    hbox.add(button)
    hbox.add(bar)
    vbox.add(hbox)
    window.add(vbox)
    window.connect("destroy", gtk.main_quit)
    window.show_all()
    
    gtk.main()
    
if __name__ == "__main__":
    main()
#!/usr/bin/env python

"""
    Copyright 2009 Adam Schreiber <[email protected]>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

import gtk
import math

class DrawShapesArea(gtk.DrawingArea):
    def __init__(self):
        gtk.DrawingArea.__init__(self)
        self.connect_after("expose_event", self.expose)
        self.ones = 0
        self.tens = 0
        self.hundreds = 0
        self.thousands = 0
        self.tenthousands = 0
        self.hundredthousands = 0

    def expose(self, widget, event):
        #print widget.window
        self.context = widget.window.cairo_create()
        #print self.context
        # set a clip region for the expose event
        self.context.rectangle(event.area.x, event.area.y,
                               event.area.width, event.area.height)
        self.context.clip()

        self.draw(self.context)
        
        return False
        
    def draw (self, context):
        rect = self.get_allocation()
        context.set_source_rgba(0.0, 0.0, 0.0, 1.0)
        context.move_to (rect.x+rect.width/6, rect.y)
        context.line_to (rect.x+rect.width/6, rect.y + rect.height)
        
        context.move_to (rect.x+2*rect.width/6, rect.y)
        context.line_to (rect.x+2*rect.width/6, rect.y + rect.height)
        
        context.move_to (rect.x+3*rect.width/6, rect.y)
        context.line_to (rect.x+3*rect.width/6, rect.y + rect.height)
        
        context.move_to (rect.x+4*rect.width/6, rect.y)
        context.line_to (rect.x+4*rect.width/6, rect.y + rect.height)
        
        context.move_to (rect.x+5*rect.width/6, rect.y)
        context.line_to (rect.x+5*rect.width/6, rect.y + rect.height)
        
        context.stroke()
        
        for m in range (0,5):
            for n in range (0,2):
                if (((5-m) + ((n+1)%2)*5) <= self.ones):
                    opacity = 1
                else:
                    opacity = 0.25
            
                self.drawBar (context, rect.x + 5*rect.width/6 + n*rect.width/12,
                              rect.y + m*rect.height/6, rect.width/12, rect.height/6, opacity)
                
        for m in range (0,5):
            for n in range (0,2):
                if (((5-m) + ((n+1)%2)*5) <= self.tens):
                    opacity = 1
                else:
                    opacity = 0.25
                    
                self.drawCircle (context, rect.x + 4*rect.width/6 + n*rect.width/12,
                                 rect.y + m*rect.height/6, rect.width/12, rect.height/6, opacity) 
                                 
        for m in range (0,5):
            for n in range (0,2):
                if (((5-m) + ((n+1)%2)*5) <= self.hundreds):
                    opacity = 1
                else:
                    opacity = 0.25
                    
                self.drawTriangle (context, rect.x + 3*rect.width/6 + n*rect.width/12,
                                   rect.y + m*rect.height/6, rect.width/12, rect.height/6, opacity)                         
                                   
        for m in range (0,5):
            for n in range (0,2):
                if (((5-m) + ((n+1)%2)*5) <= self.thousands):
                    opacity = 1
                else:
                    opacity = 0.25
                    
                self.drawSquare (context, rect.x + 2*rect.width/6 + n*rect.width/12,
                                 rect.y + m*rect.height/6, rect.width/12, rect.height/6, opacity)                         
        
        for m in range (0,5):
            for n in range (0,2):
                if (((5-m) + ((n+1)%2)*5) <= self.tenthousands):
                    opacity = 1
                else:
                    opacity = 0.25
                    
                self.drawPentagon (context, rect.x + 1*rect.width/6 + n*rect.width/12,
                                   rect.y + m*rect.height/6, rect.width/12, rect.height/6, opacity)                         
        for m in range (0,5):
            for n in range (0,2):
                if (((5-m) + ((n+1)%2)*5) <= self.hundredthousands):
                    opacity = 1
                else:
                    opacity = 0.25
                    
                self.drawHexagon (context, rect.x + 0*rect.width/6 + n*rect.width/12,
                                  rect.y + m*rect.height/6, rect.width/12, rect.height/6, opacity)                           
                                   
    def set_count (self, count):
        val = int(count)
    
        self.hundredthousands = int(math.floor(val/math.pow(10,5)))
        self.tenthousands = int(math.floor(math.fmod(val,math.pow(10,5))/math.pow(10,4)))
        self.thousands = int(math.floor(math.fmod(val,math.pow(10,4))/math.pow(10,3)))
        self.hundreds = int(math.floor(math.fmod(val,math.pow(10,3))/math.pow(10,2)))
        self.tens = int(math.floor(math.fmod(val,math.pow(10,2))/math.pow(10,1)))
        self.ones = int(math.floor(math.fmod(val,math.pow(10,1))))
        
        self.queue_draw ()
        
    def get_count ():
        return (self.hundredthousands * 10**5 + self.tenthousands * 10**4
                + self.thousands * 10**3 + self.hundreds * 10**2
                + self.tens * 10 + self.ones)
        
    def drawBar (self, context, x, y, width, height, opacity):
        context.rectangle(gtk.gdk.Rectangle(x + width/3,
                                            y+height/6,
                                            width/3,
                                            height*2/3))
        context.set_source_rgba(0.933, 0.510, 0.933, opacity)
        context.fill_preserve()
        context.set_source_rgba(0.933, 0.510, 0.933, 1.0)
        context.stroke()
        
    def drawCircle (self, context, x, y, width, height, opacity):
        x = x + width / 2
        y = y + height / 2
        radius = min(width / 2, height / 2) - 5
        
        context.arc(x, y, radius, 0, 2 * math.pi)
        context.set_source_rgba(0, 0, 1, opacity)
        context.fill_preserve()
        context.set_source_rgb(0, 0, 1)
        context.stroke()
        
    def drawTriangle (self, context, x, y, width, height, opacity):
        x = x + width / 2
        y = y + height / 2
        radius = min(width / 2, height / 2) - 5
        
        context.move_to (x, y - radius)
        context.line_to (x+radius*math.cos(-30.0/180.0*math.pi), y - radius*math.sin(-30.0/180.0*math.pi))
        context.line_to (x-radius*math.cos(-30.0/180.0*math.pi), y - radius*math.sin(-30.0/180.0*math.pi))
        context.line_to (x, y - radius)
        context.set_source_rgba(0.0, 1.0, 0.0, opacity)
        context.fill_preserve()
        context.set_source_rgba(0.0, 1.0, 0.0, 1.0)
        context.stroke()
        
    def drawSquare (self, context, x, y, width, height, opacity):
        x = x + width / 2
        y = y + height / 2
        radius = min(width / 2, height / 2) - 5
        
        context.rectangle (gtk.gdk.Rectangle (int(x-.5*radius*math.sqrt(2.0)),
                                              int(y-.5*radius*math.sqrt(2.0)),
                                              int(radius*math.sqrt(2.0)),
                                              int(radius*math.sqrt(2.0))))
                                              
        context.set_source_rgba(1.0, 1.0, 0.0, opacity)
        context.fill_preserve()
        context.set_source_rgba(1.0, 1.0, 0.0, 1.0)
        context.stroke()
        
    def drawPentagon (self, context, x, y, width, height, opacity):
        x = x + width / 2
        y = y + height / 2
        radius = min(width / 2, height / 2) - 5
        
        context.move_to (x, y - radius)
        context.line_to (x+radius*math.cos(13.0/180.0*math.pi), y - radius*math.sin(13.0/180.0*math.pi))
        context.line_to (x+radius*math.cos(54.0/180.0*math.pi), y + radius*math.sin(54.0/180.0*math.pi))
        context.line_to (x-radius*math.cos(54.0/180.0*math.pi), y + radius*math.sin(54.0/180.0*math.pi))
        context.line_to (x-radius*math.cos(13.0/180.0*math.pi), y - radius*math.sin(13.0/180.0*math.pi))
        context.line_to (x, y - radius)
        context.set_source_rgba(1.0, 0.647, 0.0, opacity)
        context.fill_preserve()
        context.set_source_rgba(1.0, 0.647, 0.0, 1.0)
        context.stroke()
        
    def drawHexagon (self, context, x, y, width, height, opacity):
        x = x + width / 2
        y = y + height / 2
        radius = min(width / 2, height / 2) - 5
        
        context.move_to (x+radius*math.cos(60.0/180.0*math.pi), y - radius*math.sin(60.0/180.0*math.pi))
        context.line_to (x+radius, y)
        context.line_to (x+radius*math.cos(60.0/180.0*math.pi), y + radius*math.sin(60.0/180.0*math.pi))
        context.line_to (x-radius*math.cos(60.0/180.0*math.pi), y + radius*math.sin(60.0/180.0*math.pi))
        context.line_to (x-radius, y)
        context.line_to (x - radius*math.cos(60.0/180.0*math.pi), y - radius*math.sin(60.0/180.0*math.pi))
        context.line_to (x+radius*math.cos(60.0/180.0*math.pi), y - radius*math.sin(60.0/180.0*math.pi))
        context.set_source_rgba(1.0, 0.0, 0.0, opacity)
        context.fill_preserve()
        context.set_source_rgba(1.0, 0.0, 0.0, 1.0)
        context.stroke()
def main():
    window = gtk.Window(gtk.WINDOW_TOPLEVEL)
    window.connect("delete_event", delete_event)
    window.connect("destroy", gtk.main_quit)
    window.set_border_width(10)
    window.set_default_size(640, 480)
    window.set_title("Deka")

    vbox = gtk.VBox (False, 10)
    window.add(vbox)
    
    # Scrollbar Value Label
    hbox = gtk.HBox()
    spinbut = gtk.SpinButton()
    spinbut.set_range(0,999999)
    spinbut.set_increments(1,100)
    spinbut.set_numeric(True)
    hbox.pack_start(spinbut, True, False)
    vbox.pack_start(hbox, False)
    
    # Scrollbar
    hscrollbar = gtk.HScrollbar()
    hscrollbar.set_range(0,999999)
    hscrollbar.set_increments(1,100)
    vbox.pack_start(hscrollbar, False)
    
    # Equivalent Label
    equivlab = gtk.Label("0 * 100000 + 0 * 10000 + 0 * 1000 + 0 * 100 + 0 * 10 + 0 * 1 = 0")
    vbox.pack_start(equivlab, False)
    
    # Power of 10 Equivalent Label
    powequivlab = gtk.Label("0 * 10<sup>5</sup> + 0 * 10<sup>4</sup> + 0 * 10<sup>3</sup> + 0 * 10<sup>2</sup> + 0 * 10<sup>1</sup> + 0 * 10<sup>0</sup> = 0")
    powequivlab.set_use_markup(True)
    vbox.pack_start(powequivlab, False) 

    # Start Figure Box
    drawshapesarea = DrawShapesArea()
    vbox.add(drawshapesarea)
    
    spinbut.connect("value-changed", scrollbar_changed, [spinbut, hscrollbar, equivlab, powequivlab, drawshapesarea]) 
    hscrollbar.connect("value-changed", scrollbar_changed, [spinbut, hscrollbar, equivlab, powequivlab, drawshapesarea])

    window.show_all()
    gtk.main()

def scrollbar_changed(range, data=None):
    spinbut = data[0]
    hscrollbar = data[1]
    equivlab = data[2]
    powequivlab = data[3]
    drawshapesarea = data[4]

    val = round(range.get_value())
    if abs(val) < abs(1e-2):
        val = 0
    
    val = int(val)
    
    val5 = int(math.floor(val/math.pow(10,5)))
    val4 = int(math.floor(math.fmod(val,math.pow(10,5))/math.pow(10,4)))
    val3 = int(math.floor(math.fmod(val,math.pow(10,4))/math.pow(10,3)))
    val2 = int(math.floor(math.fmod(val,math.pow(10,3))/math.pow(10,2)))
    val1 = int(math.floor(math.fmod(val,math.pow(10,2))/math.pow(10,1)))
    val0 = int(math.floor(math.fmod(val,math.pow(10,1))))
    
    spinbut.set_value(int(val))
    hscrollbar.set_value(int(val))
    
    equivlab.set_text("{0} * 100000 + {1} * 10000 + {2} * 1000 + {3} * 100 + {4} * 10 + {5} * 1 = {6}".format(val5, val4, val3, val2, val1, val0, val))
    
    powequivlab.set_markup("{0} * 10<sup>5</sup> + {1} * 10<sup>4</sup> + {2} * 10<sup>3</sup> + {3} * 10<sup>2</sup> + {4} * 10<sup>1</sup> + {5} * 10<sup>0</sup> = {6}".format(val5, val4, val3, val2, val1, val0, val))

    drawshapesarea.set_count (val)

def delete_event(widget, event, data=None):
    return False
        
if __name__ == "__main__":
    main()

_______________________________________________
pygtk mailing list   [email protected]
http://www.daa.com.au/mailman/listinfo/pygtk
Read the PyGTK FAQ: http://faq.pygtk.org/

Reply via email to