Andrea Corbellini has proposed merging lp:~andrea-bs/a4/display-image into lp:a4.
Requested reviews: A4 development (a4-dev) This branch is nothing special. It just adds some code to open and display SVG images. The UI is composed of three main widgets: the menu bar, the drawing area and the status bar. I haven't added a toolbar to reserve more space to the drawing area, we may add it later if it turns out to be useful. This code is fully PEP-8 compliant and almost all the exceptional cases are threated correctly (see the TODOs). -- https://code.launchpad.net/~andrea-bs/a4/display-image/+merge/26180 Your team A4 development is requested to review the proposed merge of lp:~andrea-bs/a4/display-image into lp:a4.
=== modified file 'a4' --- a4 2010-05-21 14:30:23 +0000 +++ a4 2010-05-27 14:33:28 +0000 @@ -1,3 +1,8 @@ #!/usr/bin/python # Copyright 2010 A4 Developers. This software is licensed under the # GNU General Public License version 3 (see the file LICENSE). + +from a4lib.app import main + +if __name__ == '__main__': + main() === added file 'a4lib/app.py' --- a4lib/app.py 1970-01-01 00:00:00 +0000 +++ a4lib/app.py 2010-05-27 14:33:28 +0000 @@ -0,0 +1,113 @@ +# Copyright 2010 A4 Developers. This software is licensed under the +# GNU General Public License version 3 (see the file LICENSE). + +"""The ingress point of the application.""" + +import optparse +import os +import gtk +from a4lib.svgimage import SVGImage, SVGImageError + +class WindowMain(object): + """The main window of the application.""" + + def __init__(self): + self.svg_image = None + self.builder = gtk.Builder() + # TODO Look into '/usr/share' too. + self.builder.add_from_file('ui/window_main.glade') + self.builder.connect_signals(self) + + self.gtk_window = self.builder.get_object('window_main') + self.gtk_window.show_all() + self.drawing_area = self.builder.get_object('drawing_area').window + + def set_status(self, status): + """Put the given string in the status bar.""" + label = self.builder.get_object('label_status') + label.set_text(status) + + def open_file(self, file_name): + """Open a file inside this window.""" + self.set_status('Loading {0}...'.format(file_name)) + try: + # Try to open a file. + image = SVGImage(file_name) + except SVGImageError as error: + # The file doesn't exist, or is not an SVG file. Show an error + # dialog and don't do anything else. + dialog = gtk.MessageDialog( + self.gtk_window, gtk.DIALOG_DESTROY_WITH_PARENT, + gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, + 'Cannot open {0!r}: {1[0]}'.format(file_name, error.args)) + dialog.run() + dialog.destroy() + else: + # The file was OK. Set up the environment. + widget = self.builder.get_object('drawing_area') + widget.set_size_request(image.width + 20, image.height + 20) + self.svg_image = image + finally: + # Restore the status string. + self.set_status('') + + def on_open_clicked(self, widget): + """Even called when the 'Open' button is clicked.""" + # Set up the file chooser dialog. + dialog = gtk.FileChooserDialog( + None, None, gtk.FILE_CHOOSER_ACTION_OPEN, ( + gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, + gtk.STOCK_OPEN, gtk.RESPONSE_OK) + ) + dialog.set_default_response(gtk.RESPONSE_OK) + # If an another file has been opened, use its location as starting + # folder for the dialog. + if self.svg_image is not None: + path = os.path.dirname(self.svg_image.file_name) + dialog.set_current_folder(path) + + # Show the dialog, back up the relevant properties and destroy it. + response = dialog.run() + file_name = dialog.get_filename() + dialog.destroy() + # Open the file, if necessary. + if response == gtk.RESPONSE_OK: + self.open_file(file_name) + + def on_drawing_area_expose(self, widget, event): + """This method is called everytime the drawing area should be redrawn. + """ + image = self.svg_image + if image is None: + return + context = self.drawing_area.cairo_create() + + context.rectangle( + event.area.x, event.area.y, event.area.width, event.area.height) + context.clip() + context.set_source_rgb(1, 1, 1) + context.paint() + + width, height = self.drawing_area.get_size() + context.translate( + (width - image.width) / 2, (height - image.height) / 2) + image.render_cairo(context) + + def quit(self, widget=None): + """Close the window and quit the application.""" + gtk.main_quit() + + +def parse_arguments(args=None): + """Parse the command line arguments.""" + parser = optparse.OptionParser() + return parser.parse_args(args) + +def main(args=None): + """Start the application.""" + options, files = parse_arguments(args) + window = WindowMain() + if files: + # TODO What if more than one file is given? + window.open_file(files[0]) + gtk.main() === added file 'a4lib/svgimage.py' --- a4lib/svgimage.py 1970-01-01 00:00:00 +0000 +++ a4lib/svgimage.py 2010-05-27 14:33:28 +0000 @@ -0,0 +1,28 @@ +# Copyright 2010 A4 Developers. This software is licensed under the +# GNU General Public License version 3 (see the file LICENSE). + +"""Code to deal with SVG images.""" + +import rsvg +from glib import GError + + +class SVGImageError(StandardError): + """Error raised when an operation with an image fails.""" + + +class SVGImage(object): + """An object that represents an image contained in a SVG file.""" + + def __init__(self, file_name): + self.file_name = file_name + try: + svg_data = open(file_name).read() + self._svg_handler = rsvg.Handle(data=svg_data) + except IOError as error: + raise SVGImageError(error.args[1]) + except GError as error: + raise SVGImageError('Unknown file type') + self.width = self._svg_handler.get_property('width') + self.height = self._svg_handler.get_property('height') + self.render_cairo = self._svg_handler.render_cairo === added directory 'ui' === added file 'ui/window_main.glade' --- ui/window_main.glade 1970-01-01 00:00:00 +0000 +++ ui/window_main.glade 2010-05-27 14:33:28 +0000 @@ -0,0 +1,120 @@ +<?xml version="1.0"?> +<interface> + <requires lib="gtk+" version="2.16"/> + <!-- interface-naming-policy project-wide --> + <object class="GtkWindow" id="window_main"> + <property name="title" translatable="yes">A4</property> + <property name="icon_name">emblem-documents</property> + <signal name="destroy" handler="quit"/> + <child> + <object class="GtkVBox" id="vbox1"> + <property name="visible">True</property> + <child> + <object class="GtkMenuBar" id="menubar1"> + <property name="visible">True</property> + <child> + <object class="GtkMenuItem" id="menuitem_file"> + <property name="visible">True</property> + <property name="label" translatable="yes">_File</property> + <property name="use_underline">True</property> + <child type="submenu"> + <object class="GtkMenu" id="menu_file"> + <property name="visible">True</property> + <child> + <object class="GtkImageMenuItem" id="imagemenuitem_open"> + <property name="label">gtk-open</property> + <property name="visible">True</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + <signal name="activate" handler="on_open_clicked"/> + </object> + </child> + <child> + <object class="GtkSeparatorMenuItem" id="separatormenuitem1"> + <property name="visible">True</property> + </object> + </child> + <child> + <object class="GtkImageMenuItem" id="imagemenuitem_quit"> + <property name="label">gtk-quit</property> + <property name="visible">True</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + <signal name="activate" handler="quit"/> + </object> + </child> + </object> + </child> + </object> + </child> + <child> + <object class="GtkMenuItem" id="menuitem_help"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Help</property> + <property name="use_underline">True</property> + <child type="submenu"> + <object class="GtkMenu" id="menu_help"> + <property name="visible">True</property> + <child> + <object class="GtkImageMenuItem" id="imagemenuitem_about"> + <property name="label">gtk-about</property> + <property name="visible">True</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="position">0</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow" id="scrolledwindow1"> + <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="resize_mode">queue</property> + <child> + <object class="GtkDrawingArea" id="drawing_area"> + <property name="visible">True</property> + <signal name="expose_event" handler="on_drawing_area_expose"/> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkStatusbar" id="statusbar1"> + <property name="visible">True</property> + <child> + <object class="GtkLabel" id="label_status"> + <property name="visible">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="position">0</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="position">2</property> + </packing> + </child> + </object> + </child> + </object> +</interface>
_______________________________________________ Mailing list: https://launchpad.net/~a4-dev Post to : a4-dev@lists.launchpad.net Unsubscribe : https://launchpad.net/~a4-dev More help : https://help.launchpad.net/ListHelp