refresh drawing area in gtk

10,660

You could use gtk_widget_queue_draw_area or gdk_window_invalidate_rect.This will mark the widget (or rectangle) as dirty and once the main loop is idle expose event will be received where in you can redraw. From you description it appears the updates are happening on expose event so these APIs might be of use. Also you can check this sample from the cairo site where in you can see the usage of gtk_widget_queue_draw_area.
I have not used pygtk but from Google I found that the corresponding call for gtk_widget_queue_draw_area is gtk.Widget.queue_draw_area & for gdk_window_invalidate_rect is gtk.gdk.Window.invalidate_rect
Hope this helps!

Share:
10,660
v923z
Author by

v923z

Updated on June 04, 2022

Comments

  • v923z
    v923z almost 2 years

    I have a bunch of drawing areas (they are actually cairo surfaces, but I don't think it matters too much) in a scrolled window, and I would like to refresh the drawings. However, when I redraw the images, they are not shown till I scroll the window up and down. After that the figures are correct, so I have to conclude that the drawing routine itself is proper. I have also included a

        while Gtk.events_pending():
           Gtk.main_iteration()
    

    loop to wait for all pending operations, but that does not solve the problem. Could someone point out to me what else is missing?

    Thanks,

    v923z

    OK, so the larger chunks of the code. First, a class defining the a drawing area onto which I am going to paint (note that the body is not indented properly! I don't know how to indent larger pieces of code here):

    class Preview:
    def __init__(self):
        self.frame = Gtk.Frame()
        self.frame.set_shadow_type(Gtk.ShadowType.IN)
        self.frame.show()
        self.da = Gtk.DrawingArea()
        self.da.set_size_request(200, 300)
        self.da.connect('configure-event', self.configure_event)
        self.da.connect('draw', self.on_draw)
        self.frame.add(self.da)
        self.da.show()
    
    def configure_event(self, da, event):
        allocation = da.get_allocation()
        self.surface = da.get_window().create_similar_surface(cairo.CONTENT_COLOR,
                                                                allocation.width,
                                                                allocation.height)
        cairo_ctx = cairo.Context(self.surface)
        cairo_ctx.set_source_rgb(1, 1, 1)
        cairo_ctx.paint()
        return True
    
    def on_draw(self, da, cairo_ctx):
        cairo_ctx.set_source_surface(self.surface, 0, 0)
        cairo_ctx.paint()
        return True
    
    pass
    

    Next, the point where I actually create the drawing area. viewport_preview is a viewport created in glade.

        self.previews = []
        self.widget('viewport_preview').remove(self.vbox_preview)
        self.vbox_preview = Gtk.VBox(homogeneous=False, spacing=8)
        self.widget('viewport_preview').add(self.vbox_preview)
        self.vbox_preview.show()
    
        for page in self.pages:
            preview = Preview()
            self.vbox_preview.pack_start(preview.frame, False, False, 10)
            self.previews.append(preview)
            while Gtk.events_pending():
                Gtk.main_iteration()
    
        self.draw_preview(None)
    
        return True
    

    Then the function drawing the previews. This is really just a wrapper for the next function, and I needed this only because if I delete one entry in the previews, then I have to handle that case. I believe, the while loop at the end of this function is not necessary, for it will be at the end of the next one anyway.

    def draw_preview(self, counter=None):
        if counter is not None:
            self.vbox_preview.remove(self.previews[counter].frame)
            self.previews.pop(counter)
            self.pages.pop(counter)
            self.vbox_preview.show()
            while Gtk.events_pending():
                Gtk.main_iteration()
    
        for i in range(len(self.pages)):    
            self.draw_note(self.previews[i].da, self.previews[i].surface, self.pages[i])            
    
        while Gtk.events_pending():
            Gtk.main_iteration()
    

    Finally, the drawing function itself:

    def draw_note(self, widget, surface, page):
        list_pos = '%d/%d'%(self.page + 1, len(self.pages))
        self.widget('label_status').set_text(list_pos)
        cairo_ctx = cairo.Context(surface)
        cairo_ctx.set_source_rgb(page.background[0], page.background[1], page.background[2])
        cairo_ctx.paint()
    
        width, height = widget.get_size_request()
        xmin, xmax, ymin, ymax = fujitsu.page_size(page)
    
        factor = min(height / (2.0 * self.margin + ymax - ymin), width / (2.0 * self.margin + xmax - xmin))
        factor *= 0.8
        page.scale = factor
        value = self.widget('adjustment_smooth').get_value()
        #print value
    
        for pen in page.pagecontent:
            x = self.margin + pen.path[0][0] - xmin
            y = self.margin + pen.path[0][1] - ymin
    
            cairo_ctx.move_to(x * factor, y * factor)
            if self.widget('checkbutton_smooth').get_active() == False:
                [cairo_ctx.line_to((self.margin + x - xmin) * factor, 
                            (self.margin + y - ymin) * factor) for x, y in pen.path]
            else:
                bezier_curve = bezier.expand_coords(pen.path, value)
                x = self.margin + bezier_curve[0][0][0] - xmin
                y = self.margin + bezier_curve[0][0][1] - ymin
                cairo_ctx.move_to(x * factor, y * factor)
                [cairo_ctx.curve_to((self.margin + control[1][0] - xmin) * factor,
                                    (self.margin + control[1][1] - ymin) * factor,
                                    (self.margin + control[2][0] - xmin) * factor,
                                    (self.margin + control[2][1] - ymin) * factor,
                                    (self.margin + control[3][0] - xmin) * factor,
                                    (self.margin + control[3][1] - ymin) * factor)
                                                        for control in bezier_curve]
    
            cairo_ctx.set_line_width(pen.thickness * self.zoom_factor)
            cairo_ctx.set_source_rgba(pen.colour[0], pen.colour[1], pen.colour[2], pen.colour[3])
            cairo_ctx.stroke()
    
        cairo_ctx.rectangle(0, height * 0.96, width, height)
        cairo_ctx.set_source_rgba(page.banner_text[0][0], page.banner_text[0][1], page.banner_text[0][2], page.banner_text[0][3])
        cairo_ctx.fill()
        cairo_ctx.move_to(width * 0.05, height * 0.99)
        cairo_ctx.show_text(self.filename + '   ' + list_pos)
        cairo_ctx.set_font_size(self.zoom_factor * 10.0)
        xbearing, ybearing, twidth, theight, xadvance, yadvance = (cairo_ctx.text_extents(page.banner_text[3]))
        cairo_ctx.move_to(width - 1.03 * twidth, height * 0.99)
        cairo_ctx.show_text(page.banner_text[3])
        cairo_ctx.set_source_rgba(0, 0, 0.9, 0.90)
        cairo_ctx.stroke()
        rect = widget.get_allocation()
        widget.get_window().invalidate_rect(rect, False)
        while Gtk.events_pending():
            Gtk.main_iteration()
    

    I think that's about it.

  • v923z
    v923z over 12 years
    Thanks for the reply! Unfortunately, that does not seem to solve the problem. In fact, I have had gtk.gdk.Window.invalidate_rect all along, and it is true that if I leave that out then the canvas is not updated at all. What I find quite odd is that my drawing routine paints the canvas white and then draws lines on it, and the painting is always done, but the lines are shown only if I roll the scrolled windows. So, it appears that the widget is updated at least once.
  • another.anon.coward
    another.anon.coward over 12 years
    @v923z: Ah ok! It really did seem like a case of missing expose events! Does sound strange though... you are updating the canvas in expose event callback I presume & canvas is being updated expect for a few missing lines drawings, is that so? If possible maybe you could post a few bits of code... we could try to help