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?
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 theexpose
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, usegtk.Image
instead.To use a
DrawingArea
correctly, you must subclass from it and handle the expose event. For example: