DrawingArea inside scrolledwindow fails to draw

306 views Asked by At

I am trying to make an image viewer in PyGTK by drawing an opened image on a drawing area within a scrolled window. For some reason, the image is not drawing. Here's the code for the viewer:

#!/usr/bin/env python

import gtk
import math
import time

class ImageViewerGTK:
    def __init__(self):

        self.filename = ""

        #Set the Glade file
        self.gladefile = "issue.glade"  
        self.builder = gtk.Builder();
        self.builder.add_from_file(self.gladefile)
        self.window = self.builder.get_object("window")
        self.image = self.builder.get_object("image")
        self.image.set_events(gtk.gdk.POINTER_MOTION_MASK)
        self.builder.connect_signals(self)
        self.builder.get_object("window").show_all()

        self.zoom_slider = self.builder.get_object("zoomlevel")
        self.constrast_slider = self.builder.get_object("contrastlevel")

        self.style = self.image.get_style()
        self.gc = self.style.fg_gc[gtk.STATE_NORMAL]
        self.thumnail_pixmap = gtk.gdk.Pixmap(self.window.get_window(), 25, 25)
        self.mouse_pressed = False

        self.scroll_window = self.builder.get_object("scrolledwindow1")
        self.scroll_vertical = self.scroll_window.get_vadjustment()
        self.scroll_horizontal = self.scroll_window.get_hadjustment()

        self.statusbar = self.builder.get_object("statusbar")

        self.clickX = 0
        self.clickY = 0

    def on_window_destroy(self, widget, data=None):
        gtk.main_quit();

    def on_open_image_activate(self, menuItem, data=None):
        filename = self.get_open_filename()
        if filename: self.load_file(filename)

    def get_open_filename(self):

        filename = None
        chooser = gtk.FileChooserDialog("Open File...", self.window,
                gtk.FILE_CHOOSER_ACTION_OPEN,
                (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, 
                    gtk.STOCK_OPEN, gtk.RESPONSE_OK))

        response = chooser.run()
        if response == gtk.RESPONSE_OK: filename = chooser.get_filename()
        chooser.destroy()

        return filename 

    def load_file(self, filename):
        self.filename = filename
        self.clickX = 0
        self.clickY = 0
        self.pixbuf = gtk.gdk.pixbuf_new_from_file(self.filename)
        pixmap, mask = self.pixbuf.render_pixmap_and_mask()
        self.image.set_size_request(self.pixbuf.get_width(), self.pixbuf.get_height())
        self.image.window.draw_drawable(self.gc, pixmap, 0, 0, 0, 0, -1, -1)

    def image_motion_notify_event_cb(self, widget, event):
       if self.filename != "":
           pixel = self.pixbuf.get_pixels_array()[event.y][event.x]
           self.statusbar.pop(3)
           message = "Gray level: " + str(self.gray_level(pixel[0], pixel[1], pixel[2])) + " @ " + str(event.x) + ", " + str(event.y)
           self.statusbar.push(3, message)

    def gray_level(self, r, g, b):
        return int(0.2126 * r + 0.7152 * g + 0.0722 * b)

if __name__ == "__main__":
   try:
       hwg= ImageViewerGTK()
       gtk.main()
   except KeyboardInterrupt:
       pass

and the gladefile:

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <requires lib="gtk+" version="2.24"/>
  <!-- interface-naming-policy project-wide -->
  <object class="GtkAccelGroup" id="accelgroup1"/>
  <object class="GtkAction" id="action1"/>
  <object class="GtkWindow" id="window">
    <property name="can_focus">False</property>
    <property name="title" translatable="yes">Image Viewer</property>
    <signal name="destroy" handler="on_window_destroy" swapped="no"/>
    <child>
      <object class="GtkVBox" id="vbox1">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <child>
          <object class="GtkMenuBar" id="menubar1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <child>
              <object class="GtkMenuItem" id="menuitem1">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="use_action_appearance">False</property>
                <property name="label" translatable="yes">_File</property>
                <property name="use_underline">True</property>
                <child type="submenu">
                  <object class="GtkMenu" id="menu1">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <child>
                      <object class="GtkImageMenuItem" id="open_image">
                        <property name="label">gtk-open</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="tooltip_text" translatable="yes">Open a new image.</property>
                        <property name="use_action_appearance">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                        <signal name="activate" handler="on_open_image_activate" swapped="no"/>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
            </child>
            <child>
              <object class="GtkMenuItem" id="menuitem2">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="use_action_appearance">False</property>
                <property name="label" translatable="yes">_Edit</property>
                <property name="use_underline">True</property>
                <child type="submenu">
                  <object class="GtkMenu" id="menu2">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem9">
                        <property name="label">gtk-delete</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_action_appearance">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
            </child>
            <child>
              <object class="GtkMenuItem" id="menuitem3">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="use_action_appearance">False</property>
                <property name="label" translatable="yes">_View</property>
                <property name="use_underline">True</property>
              </object>
            </child>
            <child>
              <object class="GtkMenuItem" id="menuitem4">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="use_action_appearance">False</property>
                <property name="label" translatable="yes">_Help</property>
                <property name="use_underline">True</property>
                <child type="submenu">
                  <object class="GtkMenu" id="menu3">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem10">
                        <property name="label">gtk-about</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_action_appearance">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkScrolledWindow" id="scrolledwindow1">
            <property name="width_request">512</property>
            <property name="height_request">512</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="hscrollbar_policy">automatic</property>
            <property name="vscrollbar_policy">automatic</property>
            <child>
              <object class="GtkViewport" id="viewport1">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <child>
                  <object class="GtkDrawingArea" id="image">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                  </object>
                </child>
              </object>
            </child>
          </object>
          <packing>
            <property name="expand">True</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkStatusbar" id="statusbar">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="spacing">2</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">2</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

The image draws properly if the set_size_request line is removed, so the act of changing the drawing area size renders it unable to draw anything. Why is this?

1

There are 1 answers

6
user4815162342 On BEST ANSWER

You are using the GtkDrawingArea incorrectly. A drawing area is not a canvas on which you can draw only once and expect the system to refresh the drawing; it is a widget that allows (and requires) you to draw on it during the expose event. This is useful when you need to process the image, e.g. by scaling it to the area. If all you just need is a widget to show an existing image, use gtk.Image instead.

To use a DrawingArea correctly, you must subclass from it and handle the expose event. For example:

import gtk, gobject

class ImageArea(gtk.DrawingArea):
    __gsignals__ = {'expose-event': 'override'}

    def __init__(self, pixbuf):
        super(ImageArea, self).__init__()
        self._pixbuf = pixbuf

    def do_expose_event(self, event):
        # clip to exposed area
        cr = self.window.cairo_create()
        cr.rectangle(tuple(event.area))
        cr.clip()
        _, _, w, h = tuple(self.allocation)

        # paint over the drawing context:
        pb = self._pixbuf
        cr.set_source_pixbuf(pb, (w - pb.get_width()) / 2, (h - pb.get_height()) / 2)
        cr.paint()

gobject.type_register(ImageArea)