[gnome-devel-docs] demos: Added image-viewer python demo



commit 510fb02aceab179e73cd4d8b7087e9c661f8d937
Author: Johannes Schmid <jhs gnome org>
Date:   Sat Mar 19 23:48:31 2011 -0400

    demos: Added image-viewer python demo

 demos/C/image-viewer.py.page         |  252 ++++++++++++++++++++++++++++++++++
 demos/C/image-viewer/image-viewer.py |   56 ++++++++
 2 files changed, 308 insertions(+), 0 deletions(-)
---
diff --git a/demos/C/image-viewer.py.page b/demos/C/image-viewer.py.page
new file mode 100644
index 0000000..8c7e3c4
--- /dev/null
+++ b/demos/C/image-viewer.py.page
@@ -0,0 +1,252 @@
+<page xmlns="http://projectmallard.org/1.0/";
+      type="topic"
+      id="image-viewer.py">
+
+  <info>
+    
+    <link type="guide" xref="index#py"/>
+    
+    <desc>A little bit more than a simple "Hello world" application - write an image viewer in GTK. </desc>
+    
+    <revision pkgversion="0.1" version="0.1" date="2011-03-19" status="review"/>
+    <credit type="author">
+      <name>Jonh Wendell</name>
+      <email>jwendell gnome org</email>
+    </credit>
+    <credit type="author">
+      <name>Johannes Schmid</name>
+      <email>jhs gnome org</email>
+    </credit>    
+    
+  </info>
+
+<title>Image Viewer</title>
+
+<synopsis>
+  <p>In this tutorial, we're going to write a very simple GTK application that loads and displays an image file. You will learn how to:</p>
+  <list>
+    <item><p>Write a basic GTK user interface in Python</p></item>
+    <item><p>Deal with events by connecting signals to signal handlers</p></item>
+    <item><p>Lay out GTK user interfaces using containers</p></item>
+    <item><p>Load and display image files</p></item>
+  </list>
+  <p>You'll need the following to be able to follow this tutorial:</p>
+  <list>
+    <item><p>An installed copy of the <link xref="getting-ready">Anjuta IDE</link></p></item>
+    <item><p>Basic knowledge of the python programming language</p></item>
+  </list>
+</synopsis>
+
+<media type="image" mime="image/png" src="media/image-viewer.png"/>
+<media type="image" mime="image/png" src="media/guitar-tuner.png"/>
+
+
+<section>
+  <title>Create a project in Anjuta</title>
+  <p>Before you start coding, you'll need to set up a new project in Anjuta. This will create all of the files you need to build and run the code later on. It's also useful for keeping everything together.</p>
+  <steps>
+    <item>
+    <p>Start Anjuta and click <guiseq><gui>File</gui><gui>New</gui><gui>Project</gui></guiseq> to open the project wizard.</p>
+    </item>
+    <item>
+    <p>Choose <gui>PyGTK</gui> from the <gui>Python</gui> tab, click <gui>Forward</gui>, and fill-out your details on the next few pages. Use <file>image-viewer</file> as project name and directory.</p>
+   	</item>
+   	<item>
+   	<p>Be sure to disable <gui>Use GtkBuilder for user interface</gui> as we will build the
+   	user interface manually in this example. For an example using the interface designer,
+   	check the <link xref="guitar-tuner.py">Guitar-Tuner demo</link>.</p>
+    </item>
+    <item>
+    <p>Click <gui>Finished</gui> and the project will be created for you. Open <file>src/image_viewer.py</file> from the <gui>Project</gui> or <gui>File</gui> tabs. It contains very basic example code.</p>
+    </item>
+  </steps>
+</section>
+
+<section>
+  <title>A first Gtk application</title>
+  <p>Let's see what a very basic Gtk application looks like in Python:</p>
+  <code mime="text/python" style="numbered"><![CDATA[
+from gi.repository import Gtk, GdkPixbuf, Gdk
+import os, sys
+
+class GUI:
+	def __init__(self):
+		window = Gtk.Window()
+		window.set_title ("Hello World")
+		window.connect_after('destroy', self.destroy)
+
+		window.show_all()
+
+	def destroy(window, self):
+		Gtk.main_quit()
+
+def main():
+	app = GUI()
+	Gtk.main()
+		
+if __name__ == "__main__":
+    sys.exit(main())
+]]>
+  </code>
+  <p>Let's take a look at what's happening:</p>
+  <list>
+    <item>
+    <p>The first line imports the Gtk namespace (that is, it includes the Gtk library). The libraries are provided by GObject Introspection (gi), which provides language bindings for many GNOME libraries.</p>
+    </item>
+    <item>
+    <p>In the <code>__init</code> method of the <code>GUI</code> class creates an
+    (empty) Gtk.Window, sets it's title and than connects a signal to quite the application
+    once the window is closed. That's pretty simple overall, more on signals later.</p>
+    </item>
+    <item>
+    <p>Next, <code>destroy</code> is defined which just quits the application. It is called
+    by the destroy signal defined above.</p>
+    </item>
+    <item>
+    <p>The rest of the file does initilisation for Gtk and shows up the GUI.</p>
+    </item>
+  </list>
+  
+  <p>This code is ready to run, so try it using <guiseq><gui>Run</gui><gui>Runâ?¦</gui></guiseq>.
+   It should show you an empty window.</p>
+</section>
+
+<section>
+  <title>Signals</title>
+  <p>Signals are one of the key concepts in Gtk programming. Whenever something happens to an object, it emits a signal; for example, when a button is clicked it gives off the <code>clicked</code> signal. If you want your program to do something when that event occurs, you must connect a function (a "signal handler") to that signal. Here's an example:</p>
+  <code mime="text/python" style="numbered"><![CDATA[
+def button_clicked () :
+  print "you clicked me!"
+
+b = new Gtk.Button ("Click me")
+b.connect_after ('clicked', button_clicked)
+]]>
+  </code>
+  <p>The last two lines create a GtkButton called <code>b</code> and connect its <code>clicked</code> signal to the <code>button_clicked</code> function, which is defined above. Every time the button is clicked, the code in the <code>button_clicked</code> function will be executed. It just prints a message here.</p>
+</section>
+
+<section>
+  <title>Containers: Laying-out the user interface</title>
+  <p>Widgets (controls, such as buttons and labels) can be arranged in the window by making use of <em>containers</em>. You can organize the layout by mixing different types of containers, like boxes and grids.</p>
+  <p>A GtkWindow is itself a type of container, but you can only put one widget directly into it. We would like to have two widgets, an image and a button, so we must put a "higher-capacity" container inside the window to hold the other widgets. A number of <link href="http://library.gnome.org/devel/gtk/stable/GtkContainer.html";>container types</link> are available, but we will use a GtkBox here. A GtkBox can hold several widgets, organized horizontally or vertically. You can do more complicated layouts by putting several boxes inside another box and so on.</p>
+  <note>
+  <p>There is a graphical user interface designer called <app>Glade</app> integrated in <app>Anjuta</app> which makes UI design really easy. For this simple example, however, we will code everything manually.</p>
+  </note>
+  <p>Let's add the box and widgets to the window. Insert the following code into the <code>__init__</code> method, immediately after the <code>window.connect_after</code> line:</p>
+<code mime="text/python" style="numbered">
+<![CDATA[
+box = Gtk.Box()
+box.set_orientation (Gtk.Orientation.VERTICAL);
+window.add (box);
+]]>
+</code>
+  <p>The first line creates a GtkBox called <code>box</code> and sets two of its properties: the <code>orientation</code> is set to vertical (so widgets are arranged in a column), and the <code>spacing</code> between the widgets is set to 5 pixels. The next line then adds the newly-created GtkBox to the window.</p>
+  <p>So far the window only contains an empty GtkBox, and if you run the program now you will see no changes at all (the GtkBox is a transparent container, so you can't see that it's there).</p>
+</section>
+
+<section>
+  <title>Packing: Adding widgets to the container</title>
+  <p>To add some widgets to the GtkBox, insert the following code directly below the <code>this.window.add (main_box)</code> line:</p>
+  <code mime="text/python" style="numbered"><![CDATA[
+self.image = Gtk.Image();
+box.pack_start (self.image, False, False, 0);]]></code>
+  <p>The first line creates a new GtkImage called <code>image</code>, which will be used to display an image file.
+As we need that later on in the signal handler, we define this is class-wide variable. You need to add <code>image = 0</code> to the beginning of the <code>GUI</code> clas. Then, the image widget is added (<em>packed</em>) into the <code>box</code> container using GtkBox's <link href="http://library.gnome.org/devel/gtk/stable/GtkBox.html#gtk-box-pack-start";><code>pack_start</code></link> method.</p>
+  <p><code>pack_start</code> takes 4 arguments: the widget that is to be added to the GtkBox (<code>child</code>); whether the GtkBox should grow larger when the new widget is added (<code>expand</code>); whether the new widget should take up all of the extra space created if the GtkBox gets bigger (<code>fill</code>); and how much space there should be, in pixels, between the widget and its neighbors inside the GtkBox (<code>padding</code>).</p>
+  <p>Gtk containers (and widgets) dynamically expand to fill the available space, if you let them. You don't position widgets by giving them a precise x,y-coordinate location in the window; rather, they are positioned relative to one another. This makes handling window resizing much easier, and widgets should automatically take a sensible size in most situations.</p>
+  <p>Also note how the widgets are organized in a hierarchy. Once packed in the GtkBox, the GtkImage is considered a <em>child</em> of the GtkBox. This allows you to treat all of the children of a widget as a group; for example, you could hide the GtkBox, which would also hide all of its children at the same time.</p>
+  <p>Now insert these two lines, below the two you just added:</p>
+  <code mime="text/python" style="numbered">
+<![CDATA[
+button = Gtk.Button ("Open a picture...");
+box.pack_start (button, False, False, 0);
+]]></code>
+  <p>These lines are similar the first two, but this time they create a GtkButton and add it to <code>box</code>. Notice that we are setting the <code>expand</code> argument (the second one) to <code>false</code> here, whereas it was set to <code>true</code> for the GtkImage. This will cause the image to take up all available space and the button to take only the space it needs. When you maximize the window, the button size will remain the same, but the image size will increase, taking up all of the rest of the window.</p>
+</section>
+
+<section>
+  <title>Loading the image: Connecting to the button's <code>clicked</code> signal</title>
+  <p>When the user clicks on the <gui>Open Image...</gui> button, a dialog should appear so that the user can choose a picture. Once chosen, the picture should be loaded and shown in the image widget.</p>
+  <p>The first step is to connect the <code>clicked</code> signal of the button to a signal handler function, which we call <code>on_open_clicked</code>. Put this code immediately after the <code>button = Gtk.Button()</code> line where the button was created:</p>
+  <code mime="text/python"><![CDATA[
+button.connect_after('clicked', self.on_open_clicked)
+]]></code>
+  <p>This will connect the <em>clicked</em>-signal to <code>on_open_clicked</code> method that we will define
+below.</p>
+</section>
+
+<section>
+  <title>Loading the image: Writing the signal's callback</title>
+  <p>Now we can create the <code>on_open_clicked</code> method. Insert the following into the <code>GUI</code> class code block, after the <code>__init__</code> method:</p>
+    <code mime="text/javascript" style="numbered"><![CDATA[
+ def on_open_clicked (self, button):
+	dialog = Gtk.FileChooserDialog ("Open Image", button.get_toplevel(), Gtk.FileChooserAction.OPEN);
+	dialog.add_button (Gtk.STOCK_CANCEL, 0)
+	dialog.add_button (Gtk.STOCK_OK, 1)
+	dialog.set_default_response(1)
+
+	filefilter = Gtk.FileFilter ()
+	filefilter.add_pixbuf_formats ()
+	dialog.set_filter(filefilter)
+
+	if dialog.run() == 1:
+		self.image.set_from_file(dialog.get_filename());
+	
+	dialog.destroy()
+  }]]></code>
+  <p>This is a bit more complicated than anything we've attempted so far, so let's break it down:</p>
+  <list>
+    <item>
+      <p>The line beginning with <code>dialog</code> creates an <gui>Open</gui> dialog, which the user can use to choose files. We set three properties: the title of the dialog; the action (type) of the dialog (it's an "open" dialog, but we could have used <code>SAVE</code> if the intention was to save a file and <code>transient_for</code>, which sets the parent window of the dialog.</p>
+    </item>
+    <item>
+    <p>The next two lines add <gui>Cancel</gui> and <gui>Open</gui> buttons to the dialog. The second argument of the <code>add_button</code> method is the (integer) value that is returned when the button is pressed: 0 for <gui>Cancel</gui> and 1 for <gui>Open</gui>.</p>
+    <p>Notice that we are using <em>stock</em> button names from Gtk, instead of manually typing "Cancel" or "Open". The advantage of using stock names is that the button labels will already be translated into the user's language.</p>
+    </item>
+    <item>
+    <p><code>set_default_response</code> determines the button that will be activated if the user double-clicks a file or presses <key>Enter</key>. In our case, we are using the <gui>Open</gui> button as default (which has the value 1).</p>
+    </item>
+    <item>
+    <p>The next three lines restrict the <gui>Open</gui> dialog to only display files which can be opened by GtkImage. A filter object is created first; we then add all kinds of files supported by GtkPixbuf (which includes most image formats like PNG and JPEG) to the filter. Finally, we set this filter to be the <gui>Open</gui> dialog's filter.</p>
+    </item>
+    <item>
+    <p><code>dialog.run ()</code> displays the <gui>Open</gui> dialog. The dialog will wait for the user to choose an image; when they do, <code>dialog.run</code> will return the value <output>1</output> (it would return <output>0</output> if the user clicked <gui>Cancel</gui>). The <code>if</code> statement tests for this.</p>
+    </item>
+    <item><p>Assuming that the user did click <gui>Open</gui>, the next line sets the <code>file</code> property of the GtkImage to the filename of the image selected by the user. The GtkImage will then load and display the chosen image.</p>
+    </item>
+    <item>
+    <p>In the final line of this method, we destroy the <gui>Open</gui> dialog because we don't need it any more.</p>
+    </item>
+  </list>
+
+  </section>
+
+<section>
+  <title>Run the application</title>
+  <p>All of the code you need should now be in place, so try running the code. That should be it; a fully-functioning image viewer (and a whistlestop tour of JavaScript and Gtk) in not much time at all!</p>
+</section>
+
+<section>
+ <title>Reference Implementation</title>
+ <p>If you run into problems with the tutorial, compare your code with this <link href="image-viewer/image-viewer.py">reference code</link>.</p>
+</section>
+
+<section>
+  <title>Next steps</title>
+  <p>Here are some ideas for how you can extend this simple demonstration:</p>
+  <list>
+   <item>
+   <p>Have the user select a directory rather than a file, and provide controls to cycle through all of the images in a directory.</p>
+   </item>
+   <item>
+   <p>Apply random filters and effects to the image when it is loaded and allow the user to save the modified image.</p>
+   <p><link href="http://www.gegl.org/api.html";>GEGL</link> provides powerful image manipulation capabilities.</p>
+   </item>
+   <item>
+   <p>Allow the user to load images from network shares, scanners, and other more complicated sources.</p>
+   <p>You can use <link href="http://library.gnome.org/devel/gio/unstable/";>GIO</link> to handle network file tranfers and the like, and <link href="http://library.gnome.org/devel/gnome-scan/unstable/";>GNOME Scan</link> to handle scanning.</p>
+   </item>
+  </list>
+</section>
+
+</page>
diff --git a/demos/C/image-viewer/image-viewer.py b/demos/C/image-viewer/image-viewer.py
new file mode 100755
index 0000000..702730d
--- /dev/null
+++ b/demos/C/image-viewer/image-viewer.py
@@ -0,0 +1,56 @@
+#!/usr/bin/python
+
+from gi.repository import Gtk, GdkPixbuf, Gdk
+import os, sys
+
+#Comment the first line and uncomment the second before installing
+#or making the tarball (alternatively, use project variables)
+UI_FILE = "image_viewer_py.ui"
+#UI_FILE = "/usr/local/share/image_viewer_py/ui/image_viewer_py.ui"
+
+class GUI:
+	image = 0	
+
+	def __init__(self):
+
+		window = Gtk.Window()
+		window.set_title ("Image Viewer")
+		window.connect_after('destroy', self.destroy)
+
+		box = Gtk.Box()
+		box.set_spacing(5);
+		box.set_orientation (Gtk.Orientation.VERTICAL);
+		window.add (box);
+
+		self.image = Gtk.Image();
+		box.pack_start (self.image, True, True, 0);
+
+		button = Gtk.Button ("Open a picture...");
+		box.pack_start (button, False, False, 0);
+		button.connect_after('clicked', self.on_open_clicked)
+
+		window.show_all()
+
+	def destroy(window, self):
+		Gtk.main_quit()
+
+	def on_open_clicked (self, button):
+		dialog = Gtk.FileChooserDialog ("Open Image", button.get_toplevel(), Gtk.FileChooserAction.OPEN);
+		dialog.add_button (Gtk.STOCK_CANCEL, 0)
+		dialog.add_button (Gtk.STOCK_OPEN, 1)
+		dialog.set_default_response(1)
+
+		filefilter = Gtk.FileFilter ()
+		filefilter.add_pixbuf_formats ()
+		dialog.set_filter(filefilter)
+
+		if dialog.run() == 1:
+			self.image.set_from_file(dialog.get_filename());
+		dialog.destroy()
+	
+def main():
+	app = GUI()
+	Gtk.main()
+		
+if __name__ == "__main__":
+    sys.exit(main())



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]