#LyX 1.6.5 created this file. For more info see http://www.lyx.org/ \lyxformat 345 \begin_document \begin_header \textclass book \begin_preamble % This file was converted to LaTeX by Writer2LaTeX ver. 0.4b % see http://www.hj-gym.dk/~hj/writer2latex for more info \usepackage{multicol} \usepackage{amsfonts} \usepackage{textcomp} \usepackage{color} \usepackage{calc} \usepackage{hyperref} \hypersetup{colorlinks=true, linkcolor=blue, filecolor=blue, pagecolor=blue, urlcolor=blue} % Text styles \newcommand{\textstyleStrongEmphasis}[1]{\textbf{#1}} % List styles \newcommand{\liststyleLi}{% \renewcommand\labelitemi{{\textbullet}} \renewcommand\labelitemii{{\textbullet}} \renewcommand\labelitemiii{{\textbullet}} \renewcommand\labelitemiv{{\textbullet}} } \newcommand{\liststyleLii}{% \renewcommand\labelitemi{{\textbullet}} \renewcommand\labelitemii{{\textbullet}} \renewcommand\labelitemiii{{\textbullet}} \renewcommand\labelitemiv{{\textbullet}} } \newcommand{\liststyleLiii}{% \renewcommand\labelitemi{{\textbullet}} \renewcommand\labelitemii{{\textbullet}} \renewcommand\labelitemiii{{\textbullet}} \renewcommand\labelitemiv{{\textbullet}} } \newcommand{\liststyleLiv}{% \renewcommand\labelitemi{${\bullet}$} \renewcommand\labelitemii{${\circ}$} \renewcommand\labelitemiii{${\blacksquare}$} \renewcommand\labelitemiv{${\bullet}$} } \newcommand{\liststyleLv}{% \renewcommand\labelitemi{${\bullet}$} \renewcommand\labelitemii{${\circ}$} \renewcommand\labelitemiii{${\blacksquare}$} \renewcommand\labelitemiv{${\bullet}$} } \newcommand{\liststyleLvi}{% \renewcommand\labelitemi{${\bullet}$} \renewcommand\labelitemii{${\circ}$} \renewcommand\labelitemiii{${\blacksquare}$} \renewcommand\labelitemiv{${\bullet}$} } \newcommand{\liststyleLvii}{% \renewcommand\labelitemi{{\textbullet}} \renewcommand\labelitemii{{\textbullet}} \renewcommand\labelitemiii{{\textbullet}} \renewcommand\labelitemiv{{\textbullet}} } % Pages styles (master pages) \makeatletter \newcommand{\ps@Standard}{% \renewcommand\@oddhead{}% \renewcommand\@evenhead{}% \renewcommand\@oddfoot{}% \renewcommand\@evenfoot{}% \setlength\paperwidth{21.59cm}\setlength\paperheight{27.94cm}\setlength\voffset{-1in}\setlength\hoffset{-1in}\setlength\topmargin{2cm}\setlength\headheight{12pt}\setlength\headsep{0cm}\setlength\footskip{12pt+0cm}\setlength\textheight{27.94cm-2cm-2cm-0cm-12pt-0cm-12pt}\setlength\oddsidemargin{2cm}\setlength\textwidth{21.59cm-2cm-2cm} \renewcommand\thepage{\arabic{page}} \setlength{\skip\footins}{0.101cm}\renewcommand\footnoterule{\vspace*{-0.018cm}\noindent\textcolor{black}{\rule{0.25\columnwidth}{0.018cm}}\vspace*{0.101cm}} } \makeatother \end_preamble \options twoside \use_default_options false \language english \inputencoding utf8 \font_roman default \font_sans default \font_typewriter default \font_default_family default \font_sc false \font_osf false \font_sf_scale 100 \font_tt_scale 100 \graphics default \paperfontsize 10 \spacing single \use_hyperref false \papersize custom \use_geometry true \use_amsmath 1 \use_esint 0 \cite_engine basic \use_bibtopic false \paperorientation portrait \branch FirstEdition \selected 0 \color #faf0e6 \end_branch \branch SecondEdition \selected 0 \color #aaffff \end_branch \paperwidth 20.95cm \paperheight 27.31cm \secnumdepth 3 \tocdepth 3 \paragraph_separation indent \defskip medskip \quotes_language english \papercolumns 1 \papersides 2 \paperpagestyle default \tracking_changes false \output_changes false \author "" \author "" \end_header \begin_body \begin_layout Standard \begin_inset Note Note status collapsed \begin_layout Plain Layout This book should have an overall theme, at the End of each chapter, using selected technologies from the chapter, have a case study where the material applied to a book long application. Perhaps a minimized version of ZenPhoto Uploader would do the trick. \end_layout \begin_layout Plain Layout \end_layout \begin_layout Plain Layout Take for example: \end_layout \begin_layout Plain Layout .Desktop file: Setup the sample applications menu \end_layout \begin_layout Plain Layout GConfig: Save the apps configuration using gconf \end_layout \begin_layout Plain Layout GStreamer: application would have a little window that shows a video tutorial \end_layout \begin_layout Plain Layout DBus: Allow control of the application using DBus \end_layout \begin_layout Plain Layout Clutter: Animated application logo/ Animated in application slideshow \end_layout \begin_layout Plain Layout Cairo: Use the printing chapter to setup printing \end_layout \end_inset \end_layout \begin_layout Title \series bold \size huge PyGTK Notebook \series default \size default \series bold \size large A Journey Through Python Gnome Technologies \end_layout \begin_layout Author \begin_inset space \hfill{} \end_inset Peter Gill \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Plain Layout \backslash newcommand{ \backslash blankpage}{ \backslash thispagestyle{empty} \backslash quad \backslash newpage} \end_layout \end_inset \begin_inset Note Note status open \begin_layout Plain Layout Defined the command \backslash blankpage \end_layout \end_inset \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Plain Layout { \backslash blankpage} \end_layout \end_inset \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Plain Layout \backslash thispagestyle{empty} \end_layout \end_inset \end_layout \begin_layout Standard \begin_inset space \hfill{} \end_inset Version 0.10 April 02, 2010 \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Plain Layout { \backslash blankpage} \end_layout \end_inset \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Plain Layout \backslash thispagestyle{empty} \end_layout \end_inset \end_layout \begin_layout Standard \begin_inset Newpage newpage \end_inset \end_layout \begin_layout Standard \begin_inset CommandInset toc LatexCommand tableofcontents \end_inset \end_layout \begin_layout Standard \begin_inset FloatList figure \end_inset \end_layout \begin_layout Section* ChangeLog \end_layout \begin_layout Subsection* Version 0.03 \end_layout \begin_layout Date 23 Dec 2008 \end_layout \begin_layout Itemize Added Secton on Widgets - \begin_inset CommandInset ref LatexCommand vref reference "sub:Widgets - What are they" \end_inset \end_layout \begin_layout Itemize Added Section on First PyGTK Application - \begin_inset CommandInset ref LatexCommand vref reference "sub:Creating your first PyGTK application" \end_inset \end_layout \begin_layout Itemize Added Section on Layout Boxes - \begin_inset CommandInset ref LatexCommand vref reference "sub:Layout - Boxes" \end_inset \end_layout \begin_layout Subsection* Version 0.04 \end_layout \begin_layout Itemize Added Section on Callbacks - \begin_inset CommandInset ref LatexCommand vref reference "sub:Callbacks - Reacting to program Events" \end_inset \end_layout \begin_layout Itemize Added PyGTK on Windows - \begin_inset CommandInset ref LatexCommand vref reference "sec:Appendix PyGTK and Windows" \end_inset \end_layout \begin_layout Subsection* Version 0.05 \end_layout \begin_layout Itemize Added Section on PyGObject (only talks about gobject.timeout_add) - \begin_inset CommandInset ref LatexCommand vref reference "sec:PyGObject" \end_inset \end_layout \begin_layout Itemize Added Section on Labels - \begin_inset CommandInset ref LatexCommand vref reference "sub:Widgets - Labels" \end_inset \end_layout \begin_layout Itemize Added Section on Check Buttons - \begin_inset CommandInset ref LatexCommand vref reference "sub:Widgets - Check Buttons" \end_inset \end_layout \begin_layout Subsection* Version 0.06 \end_layout \begin_layout Date 12 Feb 2009 \end_layout \begin_layout Itemize Added code example for gstreamer codec installer - \begin_inset CommandInset ref LatexCommand vref reference "sec: Gst - Codec Buddy" \end_inset \end_layout \begin_layout Itemize Added Section on Buttons - \begin_inset CommandInset ref LatexCommand vref reference "sub:Widgets - Buttons" \end_inset \end_layout \begin_layout Itemize Added Section on Radio Buttons - \begin_inset CommandInset ref LatexCommand vref reference "sub:Widgets - RadioButton" \end_inset \end_layout \begin_layout Itemize Added Section on Toggle Buttons - \begin_inset CommandInset ref LatexCommand vref reference "sub:Widgets - Toggle Buttons" \end_inset \end_layout \begin_layout Itemize Added Section on Text Entry - \begin_inset CommandInset ref LatexCommand vref reference "sub:Widgets - Text-Entries" \end_inset \end_layout \begin_layout Subsection* Version 0.07 \end_layout \begin_layout Itemize Added Section on MessageDialog - \begin_inset CommandInset ref LatexCommand vref reference "sub:Widgets - MessageDialog" \end_inset \end_layout \begin_layout Itemize Added Section on Statusbar - \begin_inset CommandInset ref LatexCommand vref reference "sub:Widgets - Statusbar" \end_inset \end_layout \begin_layout Subsection* Version 0.08 \end_layout \begin_layout Itemize Updated chapter on clutter to pyclutter 1.0 \end_layout \begin_layout Subsection* Version 0.09 \end_layout \begin_layout Itemize Book text license change to creative commons Attribution-ShareAlike 3.0 Unported \end_layout \begin_layout Subsection* Version 0.10 \end_layout \begin_layout Date 02 April 2010 \end_layout \begin_layout Itemize Add new chapter on IronPython and Gtk-Sharp \end_layout \begin_layout Itemize Add basic gtk-sharp and IronPython example \end_layout \begin_layout Chapter PyGTK Introduction \end_layout \begin_layout Standard \begin_inset Box Frameless position "t" hor_pos "c" has_inner_box 1 inner_pos "t" use_parbox 0 width "100col%" special "none" height "1in" height_special "totalheight" status open \begin_layout Plain Layout \begin_inset CommandInset include LatexCommand input filename "chapter-heading.lyx" \end_inset \end_layout \end_inset \end_layout \begin_layout Section Introduction \end_layout \begin_layout Standard This book has been created as a personal notebook that I may refer back to when I no longer remember how to program something I once did. There has been many a time that I have spent many hours figuring how to do something interesting just to forget how I did it, or where on the web it was found. As I have become tired of doing this I have decided to collect my notes and code samples in one location that is easy for myself to reference. Basically I am using open source code to write an open source book. \end_layout \begin_layout Standard Hopefully this information will be useful to others as I have found that many of these topics are not currently collected together in a nice package making it easy to use. \end_layout \begin_layout Standard The materials in this book are from several sources from the Internet and programming books that I have read in the past, or as in the instance of the case studies, code I have done myself. \end_layout \begin_layout Standard If anything is not cited or referenced properly I now apologize to the original author and will correct it in the next edition. \end_layout \begin_layout Standard Please check the books website regularly for updates and errata, the web site is located at: \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.majorsilence.com/pygtk_book \end_layout \end_inset \end_layout \begin_layout Section PyGTK Basics \end_layout \begin_layout Subsection Widgets - What are they? \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sub:Widgets - What are they" \end_inset \end_layout \begin_layout Standard Before creating your first program lets get out of the way what a widget is. A widget is what makes up a program. They are all the different parts that can be used and include the following: \end_layout \begin_layout Itemize Labels \end_layout \begin_layout Itemize Buttons \end_layout \begin_layout Itemize Menus \end_layout \begin_layout Itemize Text Entries \end_layout \begin_layout Itemize etc.. \end_layout \begin_layout Standard So basically that is what they are. The buttons, labels, text areas of all programs are widgets. There are many different types available when using PyGTK and many of them will be covered in this book, but to start off this chapter will only cover a few such as buttons, labels and text entry. \end_layout \begin_layout Subsection Creating your first PyGTK application \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sub:Creating your first PyGTK application" \end_inset \end_layout \begin_layout Standard First thing, create a window that will display a small message. To do this pygtk and gtk must be imported. \end_layout \begin_layout LyX-Code import pygtk \end_layout \begin_layout LyX-Code pygtk.require("2.0") \end_layout \begin_layout LyX-Code import gtk \end_layout \begin_layout Standard Now create a label and a GTK window. As you can see below to set the text of a label you just supply the text when you instantiate it. To make add it to the window that you have created you use the windows add method and supply the widget (label). Then to show everything to the user you call the windows \emph on show_all \emph default method. Last but not least you must call the \emph on gtk.main() \emph default method. \end_layout \begin_layout LyX-Code label = gtk.Label("Hello World!") \end_layout \begin_layout LyX-Code win = gtk.Window() \end_layout \begin_layout LyX-Code win.add(label) \end_layout \begin_layout LyX-Code win.show_all() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Standard If you do not call the \emph on \begin_inset Index status collapsed \begin_layout Plain Layout gtk!main \end_layout \end_inset gtk.main() \emph default method, nothing will happen. It is the main loop that waits for user input and and reactions. It runs all the code that is necessary to display your application. \end_layout \begin_layout Subsection Layout - Boxes \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sub:Layout - Boxes" \end_inset \end_layout \begin_layout Standard Adding a label to a window is good and well if not useless. What you have to do is create a layout using horizontal and vertical boxes. These boxes can hold PyGTK widgets or other vertical and horiztonal boxes. You will have one main box that will hold all other boxes, this main box will be added to the window. To add a widget to a box, or a box to another box the \emph on \begin_inset Index status collapsed \begin_layout Plain Layout gtk!HBox!pack \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset start \end_layout \end_inset pack_start \emph default and \emph on pack_end \emph default methods are used. \end_layout \begin_layout Standard Now lets expand on the first PyGTK application to include a vertical and horizontal box to layout two labels and a button. \end_layout \begin_layout LyX-Code import pygtk \end_layout \begin_layout LyX-Code pygtk.require("2.0") \end_layout \begin_layout LyX-Code import gtk \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code label_1 = gtk.Label("Hello World!") \end_layout \begin_layout LyX-Code label_2 = gtk.Label("Still in the HBox") \end_layout \begin_layout LyX-Code button = gtk.Button("This button is in the Vertical Box") \end_layout \begin_layout LyX-Code vbox = gtk.VBox() \end_layout \begin_layout LyX-Code hbox = gtk.HBox() \end_layout \begin_layout Standard Start off by creating two labels and a button. A buttons text is set when creating the same way as a labels is by including the text when you create an instance of gtk.Button. Next two layout boxes are created. \end_layout \begin_layout Standard The first box created is a vertical box and the second is a horizontal box. This boxes have the following definition gtk.HBox(homogeneous=False, spacing=0). \emph on Homogeneous \emph default is whether each object in the box has the same size. You can have a vertical box (gtk.VBox) or a horizontal box (gtk.HBox). This is how in PyGTK a program has its layout. Take some time and experiment using them. (I also recommend using Glade 3 (See \begin_inset CommandInset ref LatexCommand vref reference "sec:Glade-3" \end_inset ) to create your user interfaces instead of doing it by hand). \end_layout \begin_layout LyX-Code hbox.pack_start(label_1) \end_layout \begin_layout LyX-Code hbox.pack_start(label_2) \end_layout \begin_layout LyX-Code # Add the hbox as the first item in the vertical box \end_layout \begin_layout LyX-Code # that was created above vbox.pack_start(hbox) \end_layout \begin_layout LyX-Code # Add the button as the next item in the vertical box. \end_layout \begin_layout LyX-Code vbox.pack_start(button) \end_layout \begin_layout Standard With the layout boxes created the labels and button must be added to them. So now the pack_start method of the boxes is used. The definition of these methods is pack_start(child, expand=True, fill=True, padding=0). You have the option of using pack_start which adds the widget to the beginning of the box, or pack_end which appends the widget to the end of the box. \end_layout \begin_layout Standard So this code adds label_1 to the first position of the horizontal box then adds label_2 to the next position at the beginning after label_1. Next the horizontal (hbox) is added as the first widget in the vertical (vbox) box. Next the button is added to the next position of the vertical box. When you run this a window should open up with two labels above a button. \end_layout \begin_layout Itemize \emph on child \emph default is the widget you are adding to the box \end_layout \begin_layout Itemize \emph on expand \emph default argument is whether to fill the extra space in the box (gtk.HBox or gtk.VBox) \end_layout \begin_layout Itemize \emph on fill \emph default argument only has an effect if the expand argument is set to True. \end_layout \begin_layout Standard All that is left is to run the program. So just like in the first program a gtk.Window is created, but instead of adding a widget such as a label directly to it a layout box is added. Here the vertical box (vbox) is added as it is the top level box that we used to hold all other widgets in the code above. Then call the show_all() method on the window to make all the widgets in the window visible. Now to actually run the program the gtk.main() method must be invoked. \end_layout \begin_layout LyX-Code win = gtk.Window() \end_layout \begin_layout LyX-Code win.add(vbox) \end_layout \begin_layout LyX-Code win.show_all() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Standard Run the program and enjoy your glorious creation. \end_layout \begin_layout Subsection Callbacks - Reacting to program events \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sub:Callbacks - Reacting to program Events" \end_inset \end_layout \begin_layout Standard A program that does not react to user input is usually a useless program. To react to user input such as a mouse click there must be assigned to a widget a signal handler. A signal handler is connected to a widget such as a gtk.Button and listens for a signal. \end_layout \begin_layout Standard Take for example, a signal handler could be added to a button that reacts on a mouse click. \end_layout \begin_layout Standard So lets create a button and add a signal handler: \end_layout \begin_layout LyX-Code button = gtk.Button( \begin_inset Quotes eld \end_inset example button \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code button.connect("clicked", on_button_clicked) \end_layout \begin_layout Standard What this code does is create a button that when \begin_inset Quotes eld \end_inset \emph on clicked \emph default \begin_inset Quotes erd \end_inset will call the function \emph on on_button_clicked \emph default . In the example below we there is no longer a gtk.HBox, only a vertical gtk.VBox is used and the button has signal handler to connect \emph on clicked \emph default signals to the \emph on on_button_clicked \emph default callback function. What this means is that when the button is clicked the function named on_button _clicked will be called. \end_layout \begin_layout LyX-Code import pygtk \end_layout \begin_layout LyX-Code pygtk.require("2.0") \end_layout \begin_layout LyX-Code import gtk \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_button_clicked(widget, data=None): \end_layout \begin_layout LyX-Code label_1.set_text("Hello " + str(data)) \end_layout \begin_layout LyX-Code label_1 = gtk.Label("Hello World!") \end_layout \begin_layout LyX-Code label_2 = gtk.Label("Still in the HBox") \end_layout \begin_layout LyX-Code button = gtk.Button("Click Me") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Connect the "clicked" signal of the button to \end_layout \begin_layout LyX-Code # our callback function that we have named \end_layout \begin_layout LyX-Code # on_button_clicked. It also passes the string \end_layout \begin_layout LyX-Code # "Anything can go here" to the callback function. \end_layout \begin_layout LyX-Code button.connect("clicked", on_button_clicked, "Anything can go here") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code vbox = gtk.VBox() \end_layout \begin_layout LyX-Code vbox.pack_start(label_1) \end_layout \begin_layout LyX-Code vbox.pack_start(label_2) \end_layout \begin_layout LyX-Code vbox.pack_start(button) \end_layout \begin_layout LyX-Code win = gtk.Window() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code win.connect("destroy", lambda wid: gtk.main_quit()) \end_layout \begin_layout LyX-Code win.add(vbox) \end_layout \begin_layout LyX-Code win.show_all() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Section Widgets \end_layout \begin_layout Standard Many of the widgets that are going to be discussed here will make use of a smaller gtk gui that will be shown here. However there will be a few examples that will utilize an object oriented design. Here the basic gui that creates a window and adds a vertical box (gtk.VBox) to add our test widgets into. \begin_inset CommandInset label LatexCommand label name "sec:Widgets - BaseGui" \end_inset \end_layout \begin_layout LyX-Code #!/usr/bin/env python \end_layout \begin_layout LyX-Code import pygtk, gtk \end_layout \begin_layout LyX-Code pygtk.require('2.0') \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def main(): \end_layout \begin_layout LyX-Code win = gtk.Window(gtk.WINDOW_TOPLEVEL) \end_layout \begin_layout LyX-Code win.connect("delete_event", lambda wid, we: gtk.main_quit()) \end_layout \begin_layout LyX-Code vbox = gtk.VBox(True, 2) \end_layout \begin_layout LyX-Code win.add(vbox) \end_layout \begin_layout LyX-Code # Add widget code here \end_layout \begin_layout LyX-Code win.show_all() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if __name__ == "__main__": \end_layout \begin_layout LyX-Code main() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Standard So when adding the code, from widgets discussed below, make sure it is between the win.add(vbox) and win.show_all() lines. All the widget will be added to the widget \emph on vbox \emph default . \end_layout \begin_layout Subsection Buttons \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sub:Widgets - Buttons" \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout gtk!Button \end_layout \end_inset \end_layout \begin_layout Standard To create a button the gtk.Button class is instantiated. \end_layout \begin_layout LyX-Code button = gtk.Button( \begin_inset Quotes eld \end_inset Click Me \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code button.connect("clicked", button_callback, "Button Click Me") vbox.pack_start( button, True, True, 2) \end_layout \begin_layout Standard This code creates a button that displays the text \begin_inset Quotes eld \end_inset Click Me \begin_inset Quotes erd \end_inset on the button. It than connects the buttons when clicked to the \emph on function button_callback \emph default and sends the data \begin_inset Quotes eld \end_inset Button Click Me \begin_inset Quotes erd \end_inset as a function argument. \end_layout \begin_layout LyX-Code def button_callback(widget=None, data=None): \end_layout \begin_layout LyX-Code print "%s was clicked." % data \end_layout \begin_layout Standard The function button_callback prints the out a small message that includes the \begin_inset Quotes eld \end_inset Button Click Me \begin_inset Quotes erd \end_inset string that was sent as an argument. \end_layout \begin_layout Subsection Radio Buttons \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sub:Widgets - RadioButton" \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout gtk!RadioButton \end_layout \end_inset \end_layout \begin_layout Standard Radio buttons are created using the gtk.RadioButton(group, label) class. Groups are used so that only one radio button can be selected at a time within a group. The label of course being the text that is displayed along with the radio button. \end_layout \begin_layout Standard To create the first radio button pass the value None in for the group. Than for each radio button you want in the group pass the first button in as the group. The following code will now show this. \end_layout \begin_layout LyX-Code button1 = gtk.RadioButton(None, "Radio Button 1") \end_layout \begin_layout LyX-Code button2 = gtk.RadioButton(button1, label="Radio Button 2") \end_layout \begin_layout LyX-Code button2 = gtk.RadioButton(button1, label="Radio Button 3") \end_layout \begin_layout Standard These three lines show three radio buttons being created with the first one having a group of None. The second and third buttons however have the group set to button1. This way only one of the three buttons can be selected at one time. \end_layout \begin_layout Standard Now the buttons are connected to a callback. \end_layout \begin_layout LyX-Code button1.connect("toggled", button_callback, "Button 1") \end_layout \begin_layout LyX-Code button2.connect("toggled", button_callback, "Button 2") \end_layout \begin_layout LyX-Code button3.connect("toggled", button_callback, "Button 3") \end_layout \begin_layout Standard What this does is connect any toggled (switching from one button to another) signal to the function \emph on button_callback \emph default . \end_layout \begin_layout LyX-Code def button_callback(widget=None, data=None): \end_layout \begin_layout LyX-Code print "%s was toggled %s" % (data, ("off","on")[widget.get_active()]) \end_layout \begin_layout Standard This fuction will print out the data argument \begin_inset Quotes eld \end_inset on \begin_inset Quotes erd \end_inset when the button is selected and \begin_inset Quotes eld \end_inset off \begin_inset Quotes erd \end_inset when another button is selected. What this means is that when button1 is currently selected and then button two is clicked it will print the lines: \end_layout \begin_layout LyX-Code Button 1 was toggled off \end_layout \begin_layout LyX-Code Button 2 was toggled on \end_layout \begin_layout Subsection Toggle Buttons \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sub:Widgets - Toggle Buttons" \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout gtk!ToggleButton \end_layout \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout Toggle Buttons \end_layout \end_inset \end_layout \begin_layout Standard Toggle buttons are very much the same as normal buttons except they are either in a state of \emph on on \emph default (clicked) or \emph on off \emph default (not clicked). They work much the same say that radio and check buttons work. Toggle buttons are created using the gtk.ToggleButton class and take as an argument a label. \end_layout \begin_layout LyX-Code button1 = gtk.ToggleButton("Toggle Button 1") \end_layout \begin_layout LyX-Code button2 = gtk.ToggleButton("Toggle Button 2") \end_layout \begin_layout Standard This code shows two toggle buttons being created. To make them useful they are connected to the \emph on toggled \emph default signal to call the function \emph on button_callback \emph default with \begin_inset Quotes eld \end_inset Button 1 \begin_inset Quotes erd \end_inset and \begin_inset Quotes eld \end_inset Button 2 \begin_inset Quotes erd \end_inset as function arguments. \end_layout \begin_layout LyX-Code button1.connect("toggled", button_callback, "Button 1") \end_layout \begin_layout LyX-Code button2.connect("toggled", button_callback, "Button 2") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def button_callback(widget=None, data=None): \end_layout \begin_layout LyX-Code print "%s was toggled %s" % (data, ("off", \end_layout \begin_layout LyX-Code "on")[widget.get_active()]) \end_layout \begin_layout Standard The button_callback function will print on or off for each button as they are toggled. The widget.get_active() method can be used to decide the code path by doing one action when toggled and another action when it is toggled off. \end_layout \begin_layout Standard All that is left is to add the buttons to the gtk.VBox that is in the user interface code. \end_layout \begin_layout LyX-Code vbox.pack_start(button1, True, True, 2) \end_layout \begin_layout LyX-Code vbox.pack_start(button2, True, True, 2) \end_layout \begin_layout Subsection Check Buttons \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sub:Widgets - Check Buttons" \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout gtk!CheckButton \end_layout \end_inset \end_layout \begin_layout Standard To create a check button with a label of \begin_inset Quotes eld \end_inset Check Me \begin_inset Quotes erd \end_inset do the following \end_layout \begin_layout LyX-Code check_button = gtk.CheckButton( \begin_inset Quotes eld \end_inset Check Me \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout Standard Unlike a normal button, instead of connecting to the \emph on clicked \emph default signal, a check button connects a callback to a \emph on toggled \emph default signal. So to do some action on the above you would connect like so: \end_layout \begin_layout LyX-Code check_button.connect( \begin_inset Quotes eld \end_inset toggled \begin_inset Quotes erd \end_inset , check_button_callback, \begin_inset Quotes eld \end_inset callback data \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code \end_layout \begin_layout Standard So this will call the function named \emph on check_button_callback \emph default whenever the check box is toggled(clicked). Take a look at the following example to see how to detect whether a check button is checked or not. \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def check_button_callback(widget, data=None): \end_layout \begin_layout LyX-Code print "%s was toggled: %s" % (data, ("off", "on")[widget.get_active()]) \end_layout \begin_layout Standard This function takes the check button widget and print the string data that was passed in. It also prints \begin_inset Quotes eld \end_inset off \begin_inset Quotes erd \end_inset for when the button is not clicked and \begin_inset Quotes eld \end_inset on \begin_inset Quotes erd \end_inset when the button has been clicked. \end_layout \begin_layout Standard Below is the code that is needed to create the buttons and connect them to the \emph on check_button_callback \emph default function. \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code button1 = gtk.CheckButton("check button 1") \end_layout \begin_layout LyX-Code button1.connect("toggled", check_button_callback, "Button 1") \end_layout \begin_layout LyX-Code vbox.pack_start(button1, True, True, 2) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code button2 = gtk.CheckButton("check button 2") \end_layout \begin_layout LyX-Code button2.connect("toggled", check_button_callback, "Button 2") \end_layout \begin_layout LyX-Code vbox.pack_start(button2, True, True, 2) \end_layout \begin_layout Subsection Labels \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sub:Widgets - Labels" \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout gtk!Label \end_layout \end_inset \end_layout \begin_layout Standard To create a label just do something like this but replace the labels text with your own. \end_layout \begin_layout LyX-Code label = gtk.Label( \begin_inset Quotes eld \end_inset Your label \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout Standard If you wish to change the text later you can use the labels \emph on set_text \emph default method. \end_layout \begin_layout LyX-Code label.set_text( \begin_inset Quotes eld \end_inset My new label \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout Standard Now the label will display the text \begin_inset Quotes eld \end_inset My new label \begin_inset Quotes erd \end_inset instead of \begin_inset Quotes eld \end_inset Your label \begin_inset Quotes erd \end_inset . \end_layout \begin_layout Subsection Text Entries \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sub:Widgets - Text-Entries" \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout gtk!Text Entry \end_layout \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout gtk!Entry \end_layout \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout Text Entry \end_layout \end_inset \end_layout \begin_layout Standard The text entry example is slightly more complicated than the examples that have been shown so far. This is because besides the text entry, two buttons and a label will be used in this example. The first button called \emph on print_button \emph default is used to print retrieve the text from the text entry and place it into the label. The second button \emph on , clear_button, \emph default is used to clear the text from the text entry and label. \end_layout \begin_layout Standard To create a text entry the gtk.Entry class is used. By default it is gtk.Entry(max=0). The max argument is the is the size of characters that the entry can hold. If it is set to 0 then there is no limit. \end_layout \begin_layout Standard The following code creates a gtk.Entry called text_box with no limit on the size. \end_layout \begin_layout LyX-Code text_box = gtk.Entry() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code print_button = gtk.Button("Print Text") \end_layout \begin_layout LyX-Code print_button.connect("clicked", print_callback, text_box) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code clear_button = gtk.Button("Clear Text") \end_layout \begin_layout LyX-Code clear_button.connect("clicked", clear_callback) \end_layout \begin_layout Standard After creating a text box two buttons are created. The first, \emph on print_button \emph default , is connected to the \emph on print_callback \emph default function when it is clicked and passes as an argument the text_box gtk.Entry widget as an argument. \end_layout \begin_layout Standard The \emph on print_callback \emph default funtion receives the gtk.Entry \emph on text_box \emph default as the argument data and sets the text of the global gtk.Label label to the text that was entered in the \emph on text_box \emph default widget using the gtk.Entry method \emph on get_text() \end_layout \begin_layout LyX-Code label = gtk.Label( \begin_inset Quotes eld \end_inset Hello \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code def print_callback(widget=None, data=None): \end_layout \begin_layout LyX-Code label.set_text(data.get_text()) \end_layout \begin_layout Standard The clear_callback function clears the text in the text entry and just for fun the label as well. \end_layout \begin_layout LyX-Code def clear_callback(widget=None, text_box=None): \end_layout \begin_layout LyX-Code text_box.set_text( \begin_inset Quotes eld \end_inset \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code label.set_text( \begin_inset Quotes eld \end_inset \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout Standard Now the widgets just need to be added to the gtk.VBox that is in the user interface code. \end_layout \begin_layout LyX-Code vbox.pack_start(label, True, True, 2) \end_layout \begin_layout LyX-Code vbox.pack_start(text_box, True, True, 2) \end_layout \begin_layout LyX-Code vbox.pack_start(print_button, True, True, 2) \end_layout \begin_layout LyX-Code vbox.pack_start(clear_button, True, True, 2) \end_layout \begin_layout Standard Here are some methods available with gtk.Entry: \end_layout \begin_layout Itemize insert_text(text, position=0) \end_layout \begin_layout Itemize get_text() \end_layout \begin_layout Itemize set_text(text) \end_layout \begin_layout Itemize set_max_length(max) \end_layout \begin_layout Itemize set_editable(is_editable) - True or False \end_layout \begin_layout Itemize set_visibility(visible) - True or False \end_layout \begin_layout Itemize select_region(start, end) \end_layout \begin_layout Subsection Menus \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sub:Widgets - Menus" \end_inset \begin_inset CommandInset label LatexCommand label name "sub:gtk!Menus" \end_inset \end_layout \begin_layout Standard \begin_inset Note Note status collapsed \begin_layout Plain Layout Edition 2 should cover UIManager http://www.pygtk.org/pygtk2tutorial/sec-UIManager. html and not just the manual creation of menus using code. \end_layout \end_inset \end_layout \begin_layout Standard \begin_inset Float figure wide false sideways false status collapsed \begin_layout Plain Layout \begin_inset Graphics filename images/pygtk-introduction/menu-screenshot.png \end_inset \end_layout \begin_layout Plain Layout \begin_inset Caption \begin_layout Plain Layout File Menu Screenshot \end_layout \end_inset \end_layout \begin_layout Plain Layout \begin_inset CommandInset label LatexCommand label name "fig:Menu" \end_inset \end_layout \end_inset \end_layout \begin_layout Standard This section will cover adding menus to applications that most everyone should be used to. The standard menus such File -> Save, File -> Quit, and Help -> About. Of course after reading this section you will be more than capable to add what ever menu you wish. \end_layout \begin_layout Standard The method used this section will be using is to create the menus using straight code. There is another method using the UIManager \begin_inset Foot status collapsed \begin_layout Plain Layout \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.pygtk.org/pygtk2tutorial/sec-UIManager.html \end_layout \end_inset \end_layout \end_inset and if you would like you can look into that instead. \end_layout \begin_layout Standard There are three main class that are used in creating menus and they are: \end_layout \begin_layout Itemize gtk.MenuBar - Is added to the the programs main window and is a container for gtk.Menu and gtk.MenuItem \end_layout \begin_layout Itemize gtk.Menu - Is a container to hold sub gtk.MenuItem items \end_layout \begin_layout Itemize gtk.MenuItem - Is the actual menus items the user sees and actually clicks such as \begin_inset Quotes eld \end_inset File \begin_inset Quotes erd \end_inset , \begin_inset Quotes eld \end_inset Save \begin_inset Quotes erd \end_inset , and \begin_inset Quotes eld \end_inset Quit \begin_inset Quotes erd \end_inset \end_layout \begin_layout Standard Looking at the code below, it can be seen that the menu bar is created using the class gtk.MenuBar. This is the object that will be added to the main windows, in this case the top of the gtk.VBox that is being used in this example. \end_layout \begin_layout LyX-Code menubar = gtk.MenuBar() \end_layout \begin_layout LyX-Code file_item = gtk.MenuItem("_File") \end_layout \begin_layout LyX-Code help_item = gtk.MenuItem("_Help") \end_layout \begin_layout Standard After the MenuBar is created two MenuItems are created, \emph on file_item \emph default and \emph on help_item \emph default , these of course will have other sub menu items attached to them that will be displayed when they are clicked. These are the main menu items that are seen in most applications along the top of the window (Eg. File, Edit, View, Tools, Help, etc...) In this case only \emph on File \emph default and \emph on Help \emph default are shown. The underscores before the F and H indicate that \end_layout \begin_layout Standard Here find the menu container \emph on file_item_sub \emph default being created as a gtk.Menu object to hold the menu items that will be apended to the file_item MenuItem. Save and quit are both created as gtk.MenuItem objects. These are then added to file_item_sub. A few lines further down, file_item_sub will be added to file_item. \end_layout \begin_layout LyX-Code file_item_sub = gtk.Menu() \end_layout \begin_layout LyX-Code save = gtk.MenuItem("_Save") \end_layout \begin_layout LyX-Code quit = gtk.MenuItem("_Quit") \end_layout \begin_layout LyX-Code file_item_sub.append(save) \end_layout \begin_layout LyX-Code file_item_sub.append(quit) \end_layout \begin_layout Standard As was done with creating file_item_sub so to this done here creating help_item_ sub. This is a submenu container to hold the MenuItems for the Help MenuItem. \end_layout \begin_layout LyX-Code help_item_sub = gtk.Menu() \end_layout \begin_layout LyX-Code about = gtk.MenuItem("_About") \end_layout \begin_layout LyX-Code help_item_sub.append(about) \end_layout \begin_layout Standard Finally here can be seen the submenus being added to their respective parent MenuItems and then the parent MenItems being added to the MenuBar. \end_layout \begin_layout LyX-Code file_item.set_submenu(file_item_sub) \end_layout \begin_layout LyX-Code help_item.set_submenu(help_item_sub) \end_layout \begin_layout LyX-Code menubar.append(file_item) \end_layout \begin_layout LyX-Code menubar.append(help_item) \end_layout \begin_layout Standard To finish off each menu item that is to have a user action connects to the activate signal that is emitted on its selection, each MenuItem calling its respective callback function. And lets not forget, the menubar is added to the gtk.VBox that was created in the base user interface code ( \begin_inset CommandInset ref LatexCommand vref reference "sec:Widgets - BaseGui" \end_inset ). \end_layout \begin_layout LyX-Code save.connect("activate", save_callback) \end_layout \begin_layout LyX-Code quit.connect("activate", quit_callback) \end_layout \begin_layout LyX-Code about.connect("activate", about_callback) \end_layout \begin_layout LyX-Code vbox.pack_start(menubar, True, True, 2) \end_layout \begin_layout Standard For the sake of completness these are the callback functions; very simple and not very much, but you can use your own imagination as what should be done in your own program. \end_layout \begin_layout LyX-Code def save_callback(widget=None): \end_layout \begin_layout LyX-Code print "Save menu item was pressed" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def quit_callback(widget=None): \end_layout \begin_layout LyX-Code print "Quit menu item was pressed" \end_layout \begin_layout LyX-Code gtk.main_quit() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def about_callback(widget=None): \end_layout \begin_layout LyX-Code print "About menu item was pressed" \end_layout \begin_layout Subsection Message Dialogs \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sub:Widgets - MessageDialog" \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout MessageDialog \end_layout \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout gtk!MessageDialog \end_layout \end_inset \end_layout \begin_layout Standard \begin_inset Float figure wide false sideways false status collapsed \begin_layout Plain Layout \begin_inset Graphics filename images/pygtk-introduction/MessageDialog-screenshot.png \end_inset \end_layout \begin_layout Plain Layout \begin_inset Caption \begin_layout Plain Layout MessageDialog Example \end_layout \end_inset \end_layout \begin_layout Plain Layout \begin_inset CommandInset label LatexCommand label name "fig:MessageDialog" \end_inset \end_layout \end_inset \end_layout \begin_layout Standard Message Dialogs are small windows that are smiple and easy to use. Using them is as simple as calling the gtk.MessageDialog class. The default constructor of this class looks like this. \end_layout \begin_layout LyX-Code gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_INFO, \end_layout \begin_layout LyX-Code buttons=gtk.BUTTONS_NONE, message_format=None) \end_layout \begin_layout Standard The \emph on parent \emph default is either the parent window of None if none. \end_layout \begin_layout Standard The flags can be one of the following: \end_layout \begin_layout Itemize gtk.DIALOG_MODAL \end_layout \begin_layout Itemize gtk.DIALOG_DESTROY_WITH_PARENT \end_layout \begin_layout Itemize or 0 for no flags. \end_layout \begin_layout Standard The \emph on type \emph default can be one of the following: \end_layout \begin_layout Itemize gtk.MESSAGE_INFO - display an information icon \end_layout \begin_layout Itemize gtk.MESSAGE_WARNING - display a warning icon \end_layout \begin_layout Itemize gtk.MESSAGE_QUESTION - display a question icon \end_layout \begin_layout Itemize gtk.MESSAGE_ERROR - display an error icon \end_layout \begin_layout Standard The buttons available are: \end_layout \begin_layout Itemize gtk.BUTTONS_NONE \end_layout \begin_layout Itemize gtk.BUTTONS_OK \end_layout \begin_layout Itemize gtk.BUTTONS_CLOSE \end_layout \begin_layout Itemize gtk.BUTTONS_CANCEL \end_layout \begin_layout Itemize gtk.BUTTONS_YES_NO \end_layout \begin_layout Itemize gtk.BUTTONS_OK_CANCEL \end_layout \begin_layout Standard These are the responses to the button types: \end_layout \begin_layout Itemize gtk.RESPONSE_NONE \end_layout \begin_layout Itemize gtk.RESPONSE_REJECT \end_layout \begin_layout Itemize gtk.RESPONSE_ACCEPT \end_layout \begin_layout Itemize gtk.RESPONSE_DELETE_EVENT \end_layout \begin_layout Itemize gtk.RESPONSE_OK \end_layout \begin_layout Itemize gtk.RESPONSE_CANCEL \end_layout \begin_layout Itemize gtk.RESPONSE_CLOSE \end_layout \begin_layout Itemize gtk.RESPONSE_YES \end_layout \begin_layout Itemize gtk.RESPONSE_NO \end_layout \begin_layout Itemize gtk.RESPONSE_APPLY \end_layout \begin_layout Itemize gtk.RESPONSE_HELP \end_layout \begin_layout Standard The \emph on message_format \emph default is the message that will be displayed. So far this seems as if it is not complicated and it is not. \end_layout \begin_layout Standard Here is an example showing a MessageDialog displaying a question with buttons to answer yes or no. As can be seen the message dialog is instantied with the \emph on parent \emph default set to None, the \emph on button \emph default type is gtk.BUTTONS_YES_NO, the \emph on flag \emph default is gtk.DIALOG_DESTROY_WITH_PARENT. The \emph on type \emph default is set to gtk.MESSAGE_QUESTION to go along with the yes/no button. The message that is displayed is \begin_inset Quotes eld \end_inset Is this a good example? \begin_inset Quotes erd \end_inset . \end_layout \begin_layout LyX-Code def button_callback(widget=None): \end_layout \begin_layout LyX-Code dialog = gtk.MessageDialog(parent = None, \end_layout \begin_layout LyX-Code buttons = gtk.BUTTONS_YES_NO, \end_layout \begin_layout LyX-Code flags =gtk.DIALOG_DESTROY_WITH_PARENT, \end_layout \begin_layout LyX-Code type = gtk.MESSAGE_QUESTION, \end_layout \begin_layout LyX-Code message_format = "Is this a good example?") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code dialog.set_title("MessageDialog Example") \end_layout \begin_layout LyX-Code result = dialog.run() \end_layout \begin_layout LyX-Code dialog.destroy() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if result == gtk.RESPONSE_YES: \end_layout \begin_layout LyX-Code print "Yes was clicked" \end_layout \begin_layout LyX-Code elif result == gtk.RESPONSE_NO: \end_layout \begin_layout LyX-Code print "No was clicked" \end_layout \begin_layout Standard After the Message dialog is assigned to the variable \emph on dialog \emph default the title of the dialog window is set to \begin_inset Quotes eld \end_inset MessageDialog Example \begin_inset Quotes erd \end_inset . To run a dialog you must use the dialogs \emph on run \emph default method. The dialogs run method returns the result of the buttons that was clicked. This can be used to determine the course of action. \end_layout \begin_layout Standard As can be seen in the example if the Yes buttons is clicked the message \begin_inset Quotes eld \end_inset Yes was clicked \begin_inset Quotes erd \end_inset is printed and if No is clicked the message \begin_inset Quotes eld \end_inset No was clicked \begin_inset Quotes erd \end_inset is printed. Also make sure that you remember to also call the dialogs \emph on destroy \emph default method otherwise it will never close. So \emph on dialog.destroy() \emph default is called on the line immedialty following \emph on dialog.run() \emph default . \end_layout \begin_layout Standard Finally, lets not forget the code to display the button that will run the button_callback function: \end_layout \begin_layout LyX-Code button = gtk.Button("Show Dialog") \end_layout \begin_layout LyX-Code button.connect("clicked", button_callback) \end_layout \begin_layout LyX-Code vbox.pack_start(button, True, True, 2) \end_layout \begin_layout Standard As can be seen the message dialog is easy to use and it makes it simple to display information, warnings, errors, or questions to the user. \end_layout \begin_layout Subsection Spin Buttons \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sub:Widgets - SpinButton" \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout Spin Buttons \end_layout \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout gtk!SpinButton \end_layout \end_inset \end_layout \begin_layout Standard \begin_inset Float figure wide false sideways false status collapsed \begin_layout Plain Layout \begin_inset Graphics filename images/pygtk-introduction/spinbutton-screenshot.png \end_inset \begin_inset Caption \begin_layout Plain Layout SpinButton Screenshot \end_layout \end_inset \end_layout \begin_layout Plain Layout \begin_inset CommandInset label LatexCommand label name "fig:Widgets - SpinButton Screenshot" \end_inset \end_layout \begin_layout Plain Layout \end_layout \end_inset \end_layout \begin_layout Standard To create a spin button the gtk.SpinButton class is used. \end_layout \begin_layout LyX-Code spin_button = gtk.SpinButton(adjustment=None, climb_rate=0.0, digits=0) \end_layout \begin_layout Standard The adjustment is as follows: \end_layout \begin_layout LyX-Code adjustment = gtk.Adjustment(value=0, lower=0, upper=0, step_incr=0, \end_layout \begin_layout LyX-Code page_incr=0, page_size=0) \end_layout \begin_layout Itemize value -initial value for the Spin Button \end_layout \begin_layout Itemize lower - lower range value \end_layout \begin_layout Itemize upper - upper range value \end_layout \begin_layout Itemize step_incr - value to increment/decrement when pressing mouse button-1 on a button \end_layout \begin_layout Itemize step_incr - value to increment/decrement when pressing mouse button-2 on a button \end_layout \begin_layout Itemize page_size unused \end_layout \begin_layout Standard In this example andjustment is created with an inital value and lower limit of 0, an upper limit of 100, a step increment of 1, a page increment 5, and page size of 0) \end_layout \begin_layout LyX-Code #gtk.Adjustment(value=0, lower=0, upper=0, step_incr=0, page_incr=0, page_size= 0) \end_layout \begin_layout LyX-Code adjustment = gtk.Adjustment(0, 0, 100, 1, 5, 0) \end_layout \begin_layout LyX-Code spin = gtk.SpinButton(adjustment, 0, 0) \end_layout \begin_layout LyX-Code vbox.pack_start(spin, True, True, 2) \end_layout \begin_layout Standard Here a button is added that will call the button_callback function. \end_layout \begin_layout LyX-Code button = gtk.Button("Print SpinButton Value") \end_layout \begin_layout LyX-Code button.connect("clicked", button_callback, spin) \end_layout \begin_layout LyX-Code vbox.pack_start(button, True, True, 2) \end_layout \begin_layout Standard The button callback prints the value of the spinbutton, first as a float and secondly as an integer. \end_layout \begin_layout LyX-Code def button_callback(widget=None, spin=None): \end_layout \begin_layout LyX-Code print spin.get_value() \end_layout \begin_layout LyX-Code print spin.get_value_as_int() \end_layout \begin_layout Standard For much more information and details on the gtk.SpinButton class see the PyGTK tutorial at: \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.pygtk.org \end_layout \end_inset \end_layout \begin_layout LyX-Code \end_layout \begin_layout Subsection Combo Box \end_layout \begin_layout Standard The easy way to create and populate a ComboBox is to use one of the following functions: \end_layout \begin_layout LyX-Code # Setup up a read only combobox \end_layout \begin_layout LyX-Code item_list = gtk.combo_box_new_text() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Setup a combobox that users may add to \end_layout \begin_layout LyX-Code item_list = gtk.combo_box_entry_new_text() \end_layout \begin_layout Standard Using either of these functions setups a combo box and provides some easy to use convience functions. \begin_inset Note Note status collapsed \begin_layout Plain Layout Put a link here that links to the pygtk tutorial on combo boxes in a footnote mentiong for more advanced and indepth coverate to visit the webpage. \end_layout \end_inset These are the methods that are provided when using combo_box_new_text: \end_layout \begin_layout Itemize append_text(text) \end_layout \begin_layout Itemize prepend_text(text) \end_layout \begin_layout Itemize insert_text(position, text) \end_layout \begin_layout Itemize combobox.remove_text(position) \end_layout \begin_layout Standard The example that will be shown below will use the second function, gtk.combo_box_ entry_new_text, because it provides everything that the gtk.combo_box_new_text does plus allows the user to update the list by typing in new data directly. If this functionality is not needed then it can be avoided by using the first function and not using the code below that pertains to adding new list items. \end_layout \begin_layout Standard Now the ComboBox example will break from using the user interface supplied at the at the begginning of the widget section ( \begin_inset CommandInset ref LatexCommand vref reference "sec:Widgets - BaseGui" \end_inset ), as it will use a slightly modified version so that it will now be used within a class. The example will use the same basic code but will now be within the CodeExample class that will be created. The only reason for this is because the author (thats me) does not like using global variables when it can be avoided. \end_layout \begin_layout LyX-Code class ComboExample: \end_layout \begin_layout LyX-Code def __init__(self): \end_layout \begin_layout LyX-Code win = gtk.Window(gtk.WINDOW_TOPLEVEL) \end_layout \begin_layout LyX-Code win.connect("delete_event", lambda wid, we: gtk.main_quit()) \end_layout \begin_layout LyX-Code vbox = gtk.VBox(True, 2) \end_layout \begin_layout LyX-Code win.add(vbox) \end_layout \begin_layout Standard So far the code is the same except instead of the main function the user interface code is in the __init__ method. Now the actual code for the comboboxes. \end_layout \begin_layout Standard First the list default_items is created to hold a couple of items that will be placed in the combobox, the combo box is created right beneath this using the function combo_box_entry_new_text. Using this function means that this will combobox will allow its users to enter text directly into a text entry that is provided in the combobox. \end_layout \begin_layout LyX-Code default_items = ["hello", "World"] \end_layout \begin_layout LyX-Code self.item_list = gtk.combo_box_entry_new_text() \end_layout \begin_layout LyX-Code self.item_list.child.connect('key-press-event', \end_layout \begin_layout LyX-Code self.item_list_changed) \end_layout \begin_layout LyX-Code for x in default_items: \end_layout \begin_layout LyX-Code self.item_list.append_text(x) \end_layout \begin_layout Standard After the combobox has been created and assisigned to the variable self.item_list it is connects the key-press-event signal to all the item_list_changed method. The reason for doing this is to detect when text is entered into the combobox text entry area by the user. Following this the default_items list is appended into the combox box using the append_text method. Very simple, very easy. \end_layout \begin_layout Standard To show how to retrieve the selected item a button is added that when clicked will retrieve the combobox item that is selected by calling the print_selected_ item method. \end_layout \begin_layout LyX-Code button = gtk.Button("Print Selected Item") \end_layout \begin_layout LyX-Code button.connect("clicked", self.print_selected_item) \end_layout \begin_layout LyX-Code vbox.pack_start(self.item_list, True, True, 2) \end_layout \begin_layout LyX-Code vbox.pack_start(button, True, True, 2) \end_layout \begin_layout LyX-Code win.show_all() \end_layout \begin_layout Standard The item_list_changed method is called every time there is a changed in the combobox text entry field. What this means is everytime a character is entered by a user this method is called and checks what keyboard button is pressed. If the keyboard character pressed is Return (Enter) than the text entry is append to the item_list using the its append_text method and then sets the combobox text entry back to an empty string. \end_layout \begin_layout LyX-Code def item_list_changed(self, widget=None, event=None): \end_layout \begin_layout LyX-Code key = gtk.gdk.keyval_name(event.keyval) \end_layout \begin_layout LyX-Code if key == "Return": \end_layout \begin_layout LyX-Code self.item_list.append_text(widget.get_text()) \end_layout \begin_layout LyX-Code widget.set_text("") \end_layout \begin_layout Standard The print_selected_item method is called when the button is pressed. Its sole purpose is to retrieve what item is selected in the combox. If there are no items selected then None is returend. Else the item is printed and also returned. \end_layout \begin_layout LyX-Code def print_selected_item(self, widget=None): \end_layout \begin_layout LyX-Code model = self.item_list.get_model() \end_layout \begin_layout LyX-Code active = self.item_list.get_active() \end_layout \begin_layout LyX-Code if active < 0: \end_layout \begin_layout LyX-Code return None \end_layout \begin_layout LyX-Code print model[active][0] \end_layout \begin_layout LyX-Code return model[active][0] \end_layout \begin_layout Standard As can be seen to retrieve the selected items the combobox item_list methods \emph on get_model \emph default and \emph on get_active \emph default most be used. The model is a gtk.TreeModel. If the active number is less than 0 then there are no selected items, otherwise is the postion of the selected item. \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if __name__ == "__main__": \end_layout \begin_layout LyX-Code ComboExample() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Subsection Statusbar \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sub:Widgets - Statusbar" \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout Statusbars \end_layout \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout gtk!Statubar \end_layout \end_inset \end_layout \begin_layout Standard \begin_inset Float figure wide false sideways false status collapsed \begin_layout Plain Layout \begin_inset Graphics filename images/pygtk-introduction/statusbar-screenshot.png \end_inset \end_layout \begin_layout Plain Layout \begin_inset Caption \begin_layout Plain Layout Statusbar Example \end_layout \end_inset \end_layout \begin_layout Plain Layout \begin_inset CommandInset label LatexCommand label name "fig:Statusbar Example" \end_inset \end_layout \end_inset The status bar will break from using the user interface supplied at the beggining of this widget section, as it will use a slightly modified version so that it will now be used within a class. The example will use the same basic code but will now be within the StatusbarTe st class that will be created. The only reason for this is because the author (thats me) does not like using global variables for no particular reason, I just do not like doing it unless it is a constant variable. \end_layout \begin_layout Standard Now that the user interface is within a class it is easy to work with multiple widgets by making them class level instance variables. \end_layout \begin_layout Standard When working with the gtk.Statusbar class the important methods to know are: \end_layout \begin_layout Itemize gtk.Statusbar() - Well not really a method but create an instance of the class \end_layout \begin_layout Itemize pop(context_id) - Remove the top level message \end_layout \begin_layout Itemize push(context_id, text) - Add a new top level message \end_layout \begin_layout Itemize get_context_id(context_id) - Used to retrieve the context that is used with the \emph on pop \emph default and \emph on push \emph default methods \end_layout \begin_layout LyX-Code class StatusbarTest(object): \end_layout \begin_layout LyX-Code def __init__(self): \end_layout \begin_layout LyX-Code win = gtk.Window(gtk.WINDOW_TOPLEVEL) \end_layout \begin_layout LyX-Code win.connect("delete_event", lambda wid, we: gtk.main_quit()) \end_layout \begin_layout LyX-Code vbox = gtk.VBox(False, 2) \end_layout \begin_layout LyX-Code win.add(vbox) \end_layout \begin_layout Standard So next is the code for creating the Statusbar. As can be seen once it is created a context_id variable is assinged by using the statusbars \emph on get_context_id \emph default method using the context_id \begin_inset Quotes eld \end_inset Status Test \begin_inset Quotes erd \end_inset . So whenever a message needs to be popped or pushed it needs to use the context id that was created with the \emph on get_context_id \emph default method. \end_layout \begin_layout LyX-Code self.statusbar = gtk.Statusbar() \end_layout \begin_layout LyX-Code self.context_id = self.statusbar.get_context_id("Status Test") \end_layout \begin_layout Standard The rest of the code here is common user interface code that has been common throught the widget section, all it does is create a text entry and a button. \end_layout \begin_layout LyX-Code self.text_entry = gtk.Entry() \end_layout \begin_layout LyX-Code button = gtk.Button("Click Me") \end_layout \begin_layout LyX-Code button.connect("clicked", self.button_callback) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code vbox.pack_start(self.text_entry, False, True, 2) \end_layout \begin_layout LyX-Code vbox.pack_start(button, True, True, 2) \end_layout \begin_layout LyX-Code vbox.pack_start(self.statusbar, False, True, 2) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code win.show_all() \end_layout \begin_layout Standard Here is the rest of the interesting code. First thing that is done in the button_callback function is to remove the top level message using the pop method. Next the new message is displayed to the statusbar using the push method, the text is taken from the text entry widget. To test it out run the code, type something into the text entry and click the button. \end_layout \begin_layout LyX-Code def button_callback(self, widget=None): \end_layout \begin_layout LyX-Code self.statusbar.pop(self.context_id) \end_layout \begin_layout LyX-Code self.statusbar.push(self.context_id, self.text_entry.get_text()) \end_layout \begin_layout Standard The rest of the boring code that is needed to run the example. \end_layout \begin_layout LyX-Code if __name__ == "__main__": \end_layout \begin_layout LyX-Code StatusbarTest() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Standard See \begin_inset CommandInset ref LatexCommand vref reference "fig:Statusbar Example" \end_inset to see what this example looks like. \end_layout \begin_layout Chapter More PyGTK \end_layout \begin_layout Standard \begin_inset Box Frameless position "t" hor_pos "c" has_inner_box 1 inner_pos "t" use_parbox 0 width "100col%" special "none" height "1in" height_special "totalheight" status open \begin_layout Plain Layout \begin_inset CommandInset include LatexCommand input filename "chapter-heading.lyx" \end_inset \end_layout \end_inset \end_layout \begin_layout Section Drag and Drop \end_layout \begin_layout Standard I will not be writing very much about drag and drop \begin_inset Index status collapsed \begin_layout Plain Layout Drag and Drop \end_layout \end_inset , just enough to be useful in the slide show demonstration program that this notebook is leading towards. There are a few things we need to know. \end_layout \begin_layout Standard The only part of drag and drop that we care about for this program is drag_dest_ set(flags, targets, actions). \end_layout \begin_layout Standard flags \begin_inset Foot status collapsed \begin_layout Plain Layout Take a look at: \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://pygtk.org/pygtk2tutorial/sec-DNDMethods.html \end_layout \end_inset \end_layout \end_inset - according to the PyGTK tutorial, flags are: \end_layout \begin_layout Itemize gtk.DEST_DEFAULT_MOTION: If set for a widget, GTK+, during a drag over this widget will check if the drag matches this widget's list of possible targets and actions. GTK+ will then call drag_status() as appropriate. \end_layout \begin_layout Itemize gtk.DEST_DEFAULT_HIGHLIGHT: If set for a widget, GTK+ will draw a highlight on this widget as long as a drag is over this widget and the widget drag format and action is acceptable. \end_layout \begin_layout Itemize gtk.DEST_DEFAULT_DROP: If set for a widget, when a drop occurs, GTK+ will check if the drag matches this widget's list of possible targets and actions. If so, GTK+ will call drag_get_data() on behalf of the widget. Whether or not the drop is successful, GTK+ will call drag_finish(). If the action was a move and the drag was successful, then TRUE will be passed for the delete parameter to drag_finish(). \end_layout \begin_layout Itemize gtk.DEST_DEFAULT_ALL: If set, specifies that all default actions should be taken. \end_layout \begin_layout Standard targets -- is a list of target data types that are supported along with in app information such as mime types of those files that can be dragged along with some. \end_layout \begin_layout Standard actions -- are the actions that are to be taken with the drag and include the following: \end_layout \begin_layout Itemize gtk.gdk.ACTION_DEFAULT \end_layout \begin_layout Itemize gtk.gdk.ACTION_COPY \end_layout \begin_layout Itemize gtk.gdk.ACTION_MOVE \end_layout \begin_layout Itemize gtk.gdk.ACTION_LINK \end_layout \begin_layout Itemize gtk.gdk.ACTION_PRIVATE \end_layout \begin_layout Itemize gtk.gdk.ACTION_ASK \end_layout \begin_layout Standard The only action we will be using is gtk.gdk.ACTION_COPY and this is only on non win32 systems. For whatever reason I do not believe anything really works on a Windows system properly. I believe this actually because I have never properly been able to get a target properly specified, thus it never works on Windows so I have never bothered to go beyond drag_dest_set(0, [], 0). I see no point. \end_layout \begin_layout Standard With that you can drag a file(s) anywhere into application then bother sorting out where it goes based on the file type that it is. I am sure in more complicated applications that this would not be enough but I have never personally needed more then this. \end_layout \begin_layout Standard \begin_inset Note Note status open \begin_layout Plain Layout See if others have successfully gotten drag and drop targets working properly on windows with mime-types or whatever is going wrong. \end_layout \end_inset \end_layout \begin_layout Standard Now back to targets on anything other then Windows (Linux programs). \begin_inset space \space{} \end_inset For a target we will want to set up its file type. It will be in the form of (string, int, int). \end_layout \begin_layout Standard So what we will end up with for the target will be something such as ( \begin_inset Quotes eld \end_inset text/plain \begin_inset Quotes erd \end_inset , 0, TARGET_STRING). TARGET_STRING must be an integer assigned above. It is a number that keeps track of the target throughout the program. \end_layout \begin_layout Standard For flags we will probably just want to go with gtk.DEST_DEFAULT_ALL covering all the flags leaving us with less typing. \end_layout \begin_layout Standard As I said before we will only use gtk.gdk.ACTION_COPY for the actions part and this will only be for the part that are running on Linux systems. \end_layout \begin_layout Standard So what we end up with on Linux is a function call that looks like this: \end_layout \begin_layout LyX-Code drag_dest_set(gtk.DEST_DEFAULT_DROP, [( \begin_inset Quotes eld \end_inset text/plain \begin_inset Quotes erd \end_inset , 0, \end_layout \begin_layout LyX-Code TARGET_STRING), ( \begin_inset Quotes eld \end_inset image/* \begin_inset Quotes erd \end_inset , 0, TARGET_IMAGE)], \end_layout \begin_layout LyX-Code gtk.gdk.ACTION_COPY) \end_layout \begin_layout Standard While on windows we will only be using a much smaller: \end_layout \begin_layout LyX-Code drag_dest_set(0, [], 0) \end_layout \begin_layout Standard We will need to attach this to a widget. In our case the widget will be the main window: \end_layout \begin_layout LyX-Code win = gtk.Window() \end_layout \begin_layout LyX-Code win.set_size_request(400, 400) \end_layout \begin_layout LyX-Code if sys.platform == \begin_inset Quotes eld \end_inset win32 \begin_inset Quotes erd \end_inset : \end_layout \begin_layout LyX-Code win.drag_dest_set(0, [], 0) \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code win.drag_dest_set(gtk.DEST_DEFAULT_DROP, \end_layout \begin_layout LyX-Code [( \begin_inset Quotes eld \end_inset text/plain \begin_inset Quotes erd \end_inset , 0, TARGET_STRING), \end_layout \begin_layout LyX-Code ( \begin_inset Quotes eld \end_inset image/*, 0, TARGET_IMAGE)], \end_layout \begin_layout LyX-Code gtk.gdk.ACTION_COPY) \end_layout \begin_layout Standard The thing is that using more then the method that is being used for Windows is not needed for this program and I am only showing the other version for Linux just to introduce flags, targets, and actions. \end_layout \begin_layout Standard Now that drag_dest_set has been attached to our main window widget we need to handle three singles: \end_layout \begin_layout Itemize drag_motion \end_layout \begin_layout Itemize drag_drop \end_layout \begin_layout Itemize drag_data_received \end_layout \begin_layout Standard What we do is connect them to three functions like so: \end_layout \begin_layout LyX-Code win.connect( \begin_inset Quotes eld \end_inset drag_motion \begin_inset Quotes erd \end_inset , self.motion_cb) \end_layout \begin_layout LyX-Code win.connect( \begin_inset Quotes eld \end_inset drag_drop \begin_inset Quotes erd \end_inset , self.drop_cb) \end_layout \begin_layout LyX-Code win.connect( \begin_inset Quotes eld \end_inset drag_data_received \begin_inset Quotes erd \end_inset , self.drag_data_received) \end_layout \begin_layout Standard How this works is not very important for our purposes. We just want it accepting images for us. If you want more information on how this works check out the PyGTK drag and drop tutorial at \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://pygtk.org/pygtk2tutorial/ch-DragAndDrop.html \end_layout \end_inset or check out the drag and drop demo included in the PyGTK source code found at \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.pygtk.org \end_layout \end_inset . \end_layout \begin_layout Standard One last thing that I want to mention is that in the function drag_data_received we will be detecting if the files are in an accepted list of file types. If they are, in this example we add them to a list. What we will do in the slide show program is add them to the Item list in the GUI using a TreeView. \end_layout \begin_layout Standard What you should end up with when everything is said and done is some source code that is similar to the following. \end_layout \begin_layout LyX-Code import pygtk \end_layout \begin_layout LyX-Code import gtk \end_layout \begin_layout LyX-Code import sys \end_layout \begin_layout LyX-Code import os \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code class DragDropExample: \end_layout \begin_layout LyX-Code def __init__(self): \end_layout \begin_layout LyX-Code TARGET_STRING = 82 \end_layout \begin_layout LyX-Code TARGET_IMAGE = 83 \end_layout \begin_layout LyX-Code self.file_list=[] # list to hold our images \end_layout \begin_layout LyX-Code self.accepted_types = [ \begin_inset Quotes eld \end_inset jpg \begin_inset Quotes erd \end_inset , \begin_inset Quotes eld \end_inset jpeg \begin_inset Quotes erd \end_inset , \begin_inset Quotes eld \end_inset png \begin_inset Quotes erd \end_inset , \begin_inset Quotes eld \end_inset gif \begin_inset Quotes erd \end_inset , \begin_inset Quotes eld \end_inset bmp \begin_inset Quotes erd \end_inset ] \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code win = gtk.Window() \end_layout \begin_layout LyX-Code win.set_size_request(400, 400) \end_layout \begin_layout LyX-Code win.connect( \begin_inset Quotes eld \end_inset delete_event \begin_inset Quotes erd \end_inset , lambda w,e: gtk.main_quit()) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code vbox = gtk.VBox(False, 0) \end_layout \begin_layout LyX-Code hello = gtk.Label( \begin_inset Quotes eld \end_inset Test label to drag images to. \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code vbox.pack_start(hello, True, True, 0) \end_layout \begin_layout LyX-Code win.add(vbox) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if sys.platform== \begin_inset Quotes erd \end_inset win32 \begin_inset Quotes erd \end_inset : \end_layout \begin_layout LyX-Code # gtk.DEST_DEFAULT_DROP, does not work on windows \end_layout \begin_layout LyX-Code # because will not match list of possible target \end_layout \begin_layout LyX-Code # matches if you set anything besides a blank [] \end_layout \begin_layout LyX-Code # for target on Microsoft windows, it will not call \end_layout \begin_layout LyX-Code # drop_data_received. So we might as well leave it \end_layout \begin_layout LyX-Code # like so and do your own detecting of the files \end_layout \begin_layout LyX-Code # and what to do with them in drag_data_received. \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code win.drag_dest_set(0, [], 0) \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code win.drag_dest_set(gtk.DEST_DEFAULT_DROP, \end_layout \begin_layout LyX-Code [( \begin_inset Quotes eld \end_inset text/plain \begin_inset Quotes erd \end_inset , 0, TARGET_STRING), \end_layout \begin_layout LyX-Code ( \begin_inset Quotes eld \end_inset image/* \begin_inset Quotes erd \end_inset , 0, TARGET_IMAGE)], \end_layout \begin_layout LyX-Code gtk.gdk.ACTION_COPY) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code win.connect( \begin_inset Quotes eld \end_inset drag_motion \begin_inset Quotes erd \end_inset , self.motion_cb) \end_layout \begin_layout LyX-Code win.connect( \begin_inset Quotes eld \end_inset drag_drop \begin_inset Quotes erd \end_inset , self.drop_cb) \end_layout \begin_layout LyX-Code win.connect( \begin_inset Quotes eld \end_inset drag_data_received \begin_inset Quotes erd \end_inset , \end_layout \begin_layout LyX-Code self.drag_data_received) \end_layout \begin_layout LyX-Code win.show_all() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def motion_cb(self, wid, context, x, y, time): \end_layout \begin_layout LyX-Code context.drag_status(gtk.gdk.ACTION_COPY, time) \end_layout \begin_layout LyX-Code return True \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def drop_cb(self, wid, context, x, y, time): \end_layout \begin_layout LyX-Code print \begin_inset Quotes eld \end_inset drop \begin_inset Quotes erd \end_inset \end_layout \begin_layout LyX-Code if context.targets: \end_layout \begin_layout LyX-Code wid.drag_get_data(context, context.targets[0], time) \end_layout \begin_layout LyX-Code print \begin_inset Quotes eld \end_inset \begin_inset Quotes erd \end_inset .join([str(t) for t in context.targets]) \end_layout \begin_layout LyX-Code return True \end_layout \begin_layout LyX-Code return False \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def drag_data_received(self, img, context, x, y, data, info, time): \end_layout \begin_layout LyX-Code if data.format == 8: \end_layout \begin_layout LyX-Code print \begin_inset Quotes eld \end_inset Received %s \begin_inset Quotes erd \end_inset % data.data \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Checking for valid file types \end_layout \begin_layout LyX-Code test_data = os.path.splitext(data.data)[1][1:4].lower().strip() \end_layout \begin_layout LyX-Code if test_data in self.accepted_types: \end_layout \begin_layout LyX-Code if sys.platform== \begin_inset Quotes erd \end_inset win32 \begin_inset Quotes erd \end_inset : \end_layout \begin_layout LyX-Code # Remove the file:/// on window systems. \end_layout \begin_layout LyX-Code self.file_list.append(data.data[8:]) \end_layout \begin_layout LyX-Code print data.data[8:] \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code # Remove the file:// on linux systems. \end_layout \begin_layout LyX-Code self.file_list.append(data.data[7:]) \end_layout \begin_layout LyX-Code print data.data[7:] \end_layout \begin_layout LyX-Code context.finish(True, False, time) \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code context.finish(False, False, time) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if __name__ == \begin_inset Quotes eld \end_inset __main__ \begin_inset Quotes erd \end_inset : \end_layout \begin_layout LyX-Code DragDropExample() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Section List Boxes - gtk.TreeView \end_layout \begin_layout Standard A list box \begin_inset Index status collapsed \begin_layout Plain Layout list box \end_layout \end_inset in PyGTK is a little more difficult then programming one on Windows with winforms. With PyGTK you must use a TreeView \begin_inset Index status collapsed \begin_layout Plain Layout gtk!TreeView \end_layout \end_inset . A true view is relatively complicated to use for just a list box, but it is all that is available. A wrapper can be made around a TreeView to form a generic list box. But this will not be included in this code. \end_layout \begin_layout Standard A treeview takes the form of gtk.TreeView(model). The model is the type of the item being stored. What will be used here is gtk.ListStore(type) \begin_inset Index status collapsed \begin_layout Plain Layout gtk!ListStore \end_layout \end_inset . \end_layout \begin_layout Standard The type of a ListStore is can be any valid python type (str, int, etc...). This stores the type data and each type becomes a column in a row. \end_layout \begin_layout Standard With the information we now have we can create the tree like so: \end_layout \begin_layout LyX-Code liststore = gtk.ListStore(str) \end_layout \begin_layout LyX-Code treeview = gtk.TreeView(liststore) \end_layout \begin_layout Standard The above code will create a list box with 1 column. Also it is possible to set the type of modal of the TreeView after creating an instance. \end_layout \begin_layout LyX-Code treeview.set_model(liststore) \end_layout \begin_layout Standard Now, to make this useful a CellRenderer \begin_inset Index status collapsed \begin_layout Plain Layout gtk!CellRenderer \end_layout \end_inset is needed. I will be using a CellRendererText. \end_layout \begin_layout LyX-Code cell = gtk.CellRendererText() \end_layout \begin_layout Standard The cell is what is used to display the data from the treeview model (liststore) to the user. The cell is then added to a gtk.TreeViewColumn \begin_inset Index status collapsed \begin_layout Plain Layout gtk!TreeViewColumn \end_layout \end_inset like so: \end_layout \begin_layout LyX-Code treeviewcolumn = gtk.TreeViewColumn( \begin_inset Quotes eld \end_inset Button Pushed \begin_inset Quotes erd \end_inset , cell, text=0) \end_layout \begin_layout Standard The above code will create a TreeViewColumn with a column header of \begin_inset Quotes eld \end_inset Button Pressed \begin_inset Quotes erd \end_inset assigned the data from the CellRendererText \begin_inset Quotes eld \end_inset cell \begin_inset Quotes erd \end_inset and display the cells text to column 0. \end_layout \begin_layout Standard With the treeviewcolumn created we go ahead and append it to the treeview that we created: \end_layout \begin_layout LyX-Code treeview.append_column(treeviewcolumn) \end_layout \begin_layout Standard To append data to a treeview you use the following code: \end_layout \begin_layout LyX-Code model = treeview.get_model() \end_layout \begin_layout LyX-Code model.append([ \begin_inset Quotes eld \end_inset Your Message \begin_inset Quotes erd \end_inset ]) \end_layout \begin_layout Standard To remove a selected row from a TreeView you would use the following code: \end_layout \begin_layout LyX-Code selection = self.treeview.get_selection() \end_layout \begin_layout LyX-Code model, iter = selection.get_selected() \end_layout \begin_layout LyX-Code if iter: \end_layout \begin_layout LyX-Code model.remove(iter) \end_layout \begin_layout LyX-Code return \end_layout \begin_layout Standard If you want more then 1 column you have to create a CellRenderer and TreeViewCol umn for each and append to the treeview. You must also have a data type in the ListStore for each column that you will be using. Examine the code below to see how this is applied to making a small program with two columns. \end_layout \begin_layout LyX-Code import pygtk \end_layout \begin_layout LyX-Code pygtk.require( \begin_inset Quotes eld \end_inset 2.0 \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code import gtk \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code class TreeViewExample: \end_layout \begin_layout LyX-Code def __init__(self): \end_layout \begin_layout LyX-Code # Count the items in the item list \end_layout \begin_layout LyX-Code self.counter = 0 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.win = gtk.Window() \end_layout \begin_layout LyX-Code self.win.set_size_request(400, 400) \end_layout \begin_layout LyX-Code self.win.connect( \begin_inset Quotes eld \end_inset delete_event \begin_inset Quotes erd \end_inset , lambda w,e: gtk.main_quit()) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code vbox = gtk.VBox(False, 0) \end_layout \begin_layout LyX-Code hbox = gtk.HBox(False, 0) \end_layout \begin_layout LyX-Code add_button = gtk.Button( \begin_inset Quotes eld \end_inset Add Item \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code add_button.connect( \begin_inset Quotes eld \end_inset clicked \begin_inset Quotes erd \end_inset , self.add_button_clicked) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code remove_button = gtk.Button( \begin_inset Quotes eld \end_inset Remove Item \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code remove_button.connect( \begin_inset Quotes eld \end_inset clicked \begin_inset Quotes erd \end_inset , self.remove_button_clicked) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Treeview Stuff \end_layout \begin_layout LyX-Code self.liststore = gtk.ListStore(str, str) \end_layout \begin_layout LyX-Code self.treeview = gtk.TreeView(self.liststore) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Add cell and column. \end_layout \begin_layout LyX-Code # data added to treeview. \end_layout \begin_layout LyX-Code self.cell = gtk.CellRendererText() \end_layout \begin_layout LyX-Code self.cell2 = gtk.CellRendererText() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # text=number is the column the text is displayed from \end_layout \begin_layout LyX-Code self.treeviewcolumn = gtk.TreeViewColumn( \begin_inset Quotes eld \end_inset Button Pushed \begin_inset Quotes erd \end_inset , \end_layout \begin_layout LyX-Code self.cell, text=0) \end_layout \begin_layout LyX-Code self.treeviewcolumn2 = gtk.TreeViewColumn( \end_layout \begin_layout LyX-Code \begin_inset Quotes eld \end_inset Second Useless Column \begin_inset Quotes erd \end_inset , self.cell2, text=1) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.treeview.append_column(self.treeviewcolumn) \end_layout \begin_layout LyX-Code self.treeview.append_column(self.treeviewcolumn2) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code vbox.pack_start(self.treeview, True, True, 0) \end_layout \begin_layout LyX-Code vbox.pack_start(hbox, False, True, 0) \end_layout \begin_layout LyX-Code hbox.pack_start(add_button, True, True, 0) \end_layout \begin_layout LyX-Code hbox.pack_start(remove_button, True, True, 0) \end_layout \begin_layout LyX-Code self.win.add(vbox) \end_layout \begin_layout LyX-Code self.win.show_all() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def add_button_clicked(self, w): \end_layout \begin_layout LyX-Code self.counter += 1 \end_layout \begin_layout LyX-Code model = self.treeview.get_model() \end_layout \begin_layout LyX-Code model.append([ \begin_inset Quotes eld \end_inset Add Button Pushed %s times \begin_inset Quotes erd \end_inset \end_layout \begin_layout LyX-Code % self.counter, \begin_inset Quotes eld \end_inset Column 2 Message \begin_inset Quotes erd \end_inset ]) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def remove_button_clicked(self, w): \end_layout \begin_layout LyX-Code selection = self.treeview.get_selection() \end_layout \begin_layout LyX-Code model, iter = selection.get_selected() \end_layout \begin_layout LyX-Code if iter: \end_layout \begin_layout LyX-Code model.remove(iter) \end_layout \begin_layout LyX-Code return \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if __name__ == \begin_inset Quotes eld \end_inset __main__ \begin_inset Quotes erd \end_inset : \end_layout \begin_layout LyX-Code TreeViewExample() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Standard For a much more detailed look at the available options in a TreeView visit: \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://pygtk.org/pygtk2tutorial/ch-TreeViewWidget.html \end_layout \end_inset \end_layout \begin_layout Subsection Single Click - Multiple Select \end_layout \begin_layout Standard Say that multiple items in the list need to be selected and by single clicking. This will be difficult to accomplish quickly wading through the official documentation \begin_inset Foot status collapsed \begin_layout Plain Layout Oh do I ever know it. Talk about wasted hours of my life I am never getting back. \end_layout \end_inset . Basically a few things need to be added to the above TreeView example. \end_layout \begin_layout Standard First of all the \emph on selection \emph default that is created in \emph on remove_button_clicked \emph default needs to be removed as it will now be created in the __init__ method. Now selection is a class instance variable \emph on self.selection \emph default , change the code to match. \end_layout \begin_layout Standard So in the __init__ method after \end_layout \begin_layout LyX-Code self.treeview = gtk.TreeView(self.liststore) \end_layout \begin_layout Standard Please add the following two lines of code. \end_layout \begin_layout LyX-Code self.selection = self.treeview.get_selection() \end_layout \begin_layout LyX-Code self.selection.set_mode(gtk.SELECTION_MULTIPLE) \end_layout \begin_layout Standard These two lines create the selection as a class level instance and set it up to allow multiple selections. Now to work with this the \emph on changed \emph default signal is emitting and needs to be connected to. \end_layout \begin_layout LyX-Code self.selection.connect("changed", self.on_media_files_changed) \end_layout \begin_layout Standard The above lines connects the \emph on changed \emph default signal that is emitted by single clicks on items to call \emph on self.on_treeview_changed \emph default . \end_layout \begin_layout LyX-Code def on_media_files_changed(self, widget=None, event=None): \end_layout \begin_layout LyX-Code model, path = self.selection.get_selected_rows() \end_layout \begin_layout LyX-Code for x in path: \end_layout \begin_layout LyX-Code print model[x[0]][0] # model[path][column] \end_layout \begin_layout Standard This method does not do much in its current form. What it does do is retrieve all the selected rows and prints out their values from column one. \end_layout \begin_layout Section Status Icons \end_layout \begin_layout Standard Status Icons \begin_inset Index status collapsed \begin_layout Plain Layout Status Icon \end_layout \end_inset can be useful for different reasons. Personally I like to use them to hide long running applications such as my music player. I set it playing then just minimize it to the notification area on my panel. If I want to to do something with it I left click the status icon and my music player pops up. If I want to switch songs I right click on it and it pops up menu with some options, one of which includes moving to the next song. \end_layout \begin_layout Standard Creating a status icons \begin_inset Index status collapsed \begin_layout Plain Layout gtk!status icon \end_layout \end_inset is a matter of one line of code to make it display. \end_layout \begin_layout LyX-Code icon = gtk.status_icon_new_from_stock(gtk.STOCK_ABOUT) \end_layout \begin_layout Standard This creates a status icon with an icon set to the stock GTK icon \begin_inset Foot status collapsed \begin_layout Plain Layout For a full listing of GTK stock icons take a look at the list of stock icons on page \begin_inset CommandInset ref LatexCommand vpageref reference "sec:Appendix Stock Icons" \end_inset or the pygtk website at: \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.pygtk.org/docs/pygtk/gtk-stock-items.html \end_layout \end_inset \end_layout \end_inset about. \end_layout \begin_layout Standard Then it is a matter of adding two more lines of code to add left and right click ability to it. \end_layout \begin_layout LyX-Code icon.connect('popup-menu', on_right_click) \end_layout \begin_layout LyX-Code icon.connect('activate', on_left_click) \end_layout \begin_layout Standard The first line here adds signal handling to catch the \shape italic popup-menu \shape default signal. This is caught on when a right click happens. When the popup-menu signal is detected the on_right_click function is called. \end_layout \begin_layout Standard The second line detects the \shape italic activate \shape default signal when the status icon is left clicked and calls the on_left_click function. \end_layout \begin_layout Standard As the example below will show, the programmer is responsible for creating the popup menu. The Status Icon Example creates a status icon, and then connects to the \shape italic popup-menu \shape default and \shape italic activate \shape default signal. When the popup-menu signal is activated, the on_right_click function creates and shows a popup menu by calling the make_menu function. \end_layout \begin_layout Standard The make_menu function displays a menu with the options Open App and Close App. Clicking on Open App will call the function open_app which will display a message dialog by calling the function message. The same thing happens when Close App is clicked. \end_layout \begin_layout Standard Basically this is how a status icon works; just substitute the actions and functions here for what is needed for your application. \end_layout \begin_layout Standard Status Icon Example \end_layout \begin_layout LyX-Code #!/usr/bin/env python \end_layout \begin_layout LyX-Code import gtk \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def message(data=None): \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code Function to display messages to the user. \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code msg=gtk.MessageDialog \begin_inset Index status collapsed \begin_layout Plain Layout Message Dialog \end_layout \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout gtk!MessageDialog \end_layout \end_inset (None, gtk.DIALOG_MODAL, \end_layout \begin_layout LyX-Code gtk.MESSAGE_INFO, gtk.BUTTONS_OK, data) \end_layout \begin_layout LyX-Code msg.run() \end_layout \begin_layout LyX-Code msg.destroy() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def open_app(data=None): \end_layout \begin_layout LyX-Code message(data) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def close_app(data=None): \end_layout \begin_layout LyX-Code message(data) \end_layout \begin_layout LyX-Code gtk.main_quit() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def make_menu(event_button, event_time, data=None): \end_layout \begin_layout LyX-Code menu = gtk.Menu() \begin_inset Index status collapsed \begin_layout Plain Layout gtk!Menu \end_layout \end_inset \end_layout \begin_layout LyX-Code open_item = gtk.MenuItem("Open App") \begin_inset Index status collapsed \begin_layout Plain Layout gtk!MenuItem \end_layout \end_inset \end_layout \begin_layout LyX-Code close_item = gtk.MenuItem("Close App") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code #Append the menu items \end_layout \begin_layout LyX-Code menu.append(open_item) \end_layout \begin_layout LyX-Code menu.append(close_item) \end_layout \begin_layout LyX-Code #add callbacks \end_layout \begin_layout LyX-Code open_item.connect_object("activate", open_app, "Open App") \end_layout \begin_layout LyX-Code close_item.connect_object("activate", close_app, "Close App") \end_layout \begin_layout LyX-Code #Show the menu items \end_layout \begin_layout LyX-Code open_item.show() \end_layout \begin_layout LyX-Code close_item.show() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code #Popup the menu \end_layout \begin_layout LyX-Code menu.popup(None, None, None, event_button, event_time) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_right_click(data, event_button, event_time): \end_layout \begin_layout LyX-Code make_menu(event_button, event_time) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_left_click(event): \end_layout \begin_layout LyX-Code message("Status Icon Left Clicked") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if __name__ == '__main__': \end_layout \begin_layout LyX-Code icon = gtk.status_icon_new_from_stock(gtk.STOCK_ABOUT) \end_layout \begin_layout LyX-Code icon.connect('popup-menu', on_right_click) \end_layout \begin_layout LyX-Code icon.connect('activate', on_left_click) \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout LyX-Code \end_layout \begin_layout Section File choosers \end_layout \begin_layout Standard File choosers \begin_inset Index status collapsed \begin_layout Plain Layout File Chooser \end_layout \end_inset are used to select files to open or to display a save dialog to the user. This section will cover the gtk.FileChooserDialog, gtk.FileChooserButton, and will also cover using native Windows file choosers when on Windows. \end_layout \begin_layout Subsection gtk.FileChooserDialog \end_layout \begin_layout Standard The \begin_inset Index status collapsed \begin_layout Plain Layout gtk!FileChooserDialog \end_layout \end_inset FileChooserDialog \begin_inset Index status collapsed \begin_layout Plain Layout File Chooser!FileChooserDialog \end_layout \end_inset class provides an easy to use way to display a file chooser or save dialog to end users. It is created with a few options and then is run returning succuss or failure. To start off here is a GUI with two buttons and a file filter declard that will be used to launch the file chooser and save dialog. \begin_inset Index status collapsed \begin_layout Plain Layout File Filter \end_layout \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout File Chooser!FileFilter \end_layout \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout gtk!FileFilter \end_layout \end_inset \end_layout \begin_layout LyX-Code def main(): \end_layout \begin_layout LyX-Code #file filters used with the filechoosers \end_layout \begin_layout LyX-Code text_filter=gtk.FileFilter() \end_layout \begin_layout LyX-Code text_filter.set_name("Text files") \end_layout \begin_layout LyX-Code text_filter.add_mime_type("text/*") \end_layout \begin_layout LyX-Code all_filter=gtk.FileFilter() \end_layout \begin_layout LyX-Code all_filter.set_name("All files") \end_layout \begin_layout LyX-Code all_filter.add_pattern("*") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code window = gtk.Window(gtk.WINDOW_TOPLEVEL) \end_layout \begin_layout LyX-Code window.set_title("Filechooser Example") \end_layout \begin_layout LyX-Code window.connect("destroy", lambda wid: gtk.main_quit()) \end_layout \begin_layout LyX-Code window.connect("delete_event", lambda e1,e2:gtk.main_quit()) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code button_save = gtk.Button("Save File") \end_layout \begin_layout LyX-Code button_open = gtk.Button("Open File") \end_layout \begin_layout LyX-Code button_save.connect("clicked", on_save_clicked, text_filter, all_filter) \end_layout \begin_layout LyX-Code button_open.connect("clicked", on_open_clicked, text_filter, all_filter) \end_layout \begin_layout LyX-Code hbox = gtk.HBox(True, 0) hbox.pack_start(button_save, True, True, 5) \end_layout \begin_layout LyX-Code hbox.pack_start(button_open, True, True, 5) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code window.add(hbox) window.show_all() \end_layout \begin_layout Standard As can be seen in the code above, the first thing that is done is to seta gtk.FileFilter. One filter for text files and one filter that will be for all file types. The text that is displayed with a file filter is created with the method set_name and the pattern is set using the set_pattern method. For every pattern that is to be matched against there needs to be an instance of the gtk.FileFilter. \end_layout \begin_layout Standard Then the GTK window is created. After this two buttons are created; the button_save and button_open buttons. When these buttons are clicked they pass the filters that were created at the top of the function to their respective callback functions. \end_layout \begin_layout Standard Now to focus on on the details of filechooser dialogs \begin_inset Index status collapsed \begin_layout Plain Layout File Chooser!FileChooserDialog \end_layout \end_inset . First is the save dialog. \end_layout \begin_layout LyX-Code def on_save_clicked(widget, text_filter=None, all_filter=None): \end_layout \begin_layout LyX-Code filename=None \end_layout \begin_layout LyX-Code dialog=gtk.FileChooserDialog(title="Select a File", \end_layout \begin_layout LyX-Code action=gtk.FILE_CHOOSER_ACTION_SAVE, \end_layout \begin_layout LyX-Code buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE, \end_layout \begin_layout LyX-Code gtk.RESPONSE_OK)) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if (text_filter != None) and (all_filter != None): \end_layout \begin_layout LyX-Code dialog.add_filter(text_filter) \end_layout \begin_layout LyX-Code dialog.add_filter(all_filter) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code response = dialog.run() \end_layout \begin_layout LyX-Code if response == gtk.RESPONSE_OK: \end_layout \begin_layout LyX-Code filename = dialog.get_filename() \end_layout \begin_layout LyX-Code elif response == gtk.RESPONSE_CANCEL: \end_layout \begin_layout LyX-Code print 'Cancel Clicked' dialog.destroy() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if filename != None: \end_layout \begin_layout LyX-Code save_file=open(filename, 'w') \end_layout \begin_layout LyX-Code save_file.write("Sample Data") \end_layout \begin_layout LyX-Code save_file.close() \end_layout \begin_layout LyX-Code print filename \end_layout \begin_layout Standard The on_save_clicked function starts off by setting the filename to None and quickly sets up the dialog. The dialog title is set to \begin_inset Quotes eld \end_inset Select a File \begin_inset Quotes erd \end_inset . The action type of the dialog is set to save using gtk.FILE_CHOOSER_ACTION_SAVE \begin_inset Index status collapsed \begin_layout Plain Layout File Chooser!FILE \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset CHOOSER \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset ACTION \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset SAVE \end_layout \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout gtk!FILE \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset CHOOSER \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset ACTION \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset SAVE \end_layout \end_inset . The buttons are set with a tuple. The button uses the stock cancel using the gtk.RESPONSE_CANCEL \begin_inset Index status collapsed \begin_layout Plain Layout File Chooser!RESPONSE \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset CANCEL \end_layout \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout gtk!RESPONSE \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset CANCEL \end_layout \end_inset and the stock save button that uses the gtk.RESPONSE_OK \begin_inset Index status collapsed \begin_layout Plain Layout File Chooser!RESPONSE \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset OK \end_layout \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout gtk!RESPONSE \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset OK \end_layout \end_inset when it is clicked. \end_layout \begin_layout Standard After this the function checks to see if there are any filters that should be applied and if so it applies them. \end_layout \begin_layout Standard After the filters are added, the dialog is run with its return value assigned to the variable response. \end_layout \begin_layout LyX-Code response = dialog.run() \end_layout \begin_layout Standard It then checks the value of response to be of gtk.RESPONSE_OK and if so assigns the name of the file to the variable filename using: \end_layout \begin_layout LyX-Code filename = dialog.get_filename() \end_layout \begin_layout Standard If the response is set to gtk.RESPONSE_CANCEL, no actions are taken. \end_layout \begin_layout Standard The last action to take with the dialog is to call the destroy method. If the destroy method is not called the dialog will stay on the screen. \end_layout \begin_layout LyX-Code dialog.destroy() \end_layout \begin_layout Standard The final part of the on_save_clicked function is to save the string \begin_inset Quotes eld \end_inset Sample Data \begin_inset Quotes erd \end_inset to the file that was specified to save to. \end_layout \begin_layout Standard The on_open_clicked function is very similar to the on_save_clicked function. Instead of opening a dialog to save a file it opens a dialog to select a file for the application to load. \end_layout \begin_layout LyX-Code def on_open_clicked(widget, text_filter=None, all_filter=None): \end_layout \begin_layout LyX-Code filename=None \end_layout \begin_layout LyX-Code dialog=gtk.FileChooserDialog(title="Select a File", \end_layout \begin_layout LyX-Code action=gtk.FILE_CHOOSER_ACTION_OPEN, \end_layout \begin_layout LyX-Code buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, \end_layout \begin_layout LyX-Code gtk.STOCK_OPEN, gtk.RESPONSE_OK)) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if (text_filter != None) and (all_filter != None): \end_layout \begin_layout LyX-Code dialog.add_filter(text_filter) \end_layout \begin_layout LyX-Code dialog.add_filter(all_filter) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code response = dialog.run() \end_layout \begin_layout LyX-Code if response == gtk.RESPONSE_OK: \end_layout \begin_layout LyX-Code filename = dialog.get_filename() \end_layout \begin_layout LyX-Code elif response == gtk.RESPONSE_CANCEL: \end_layout \begin_layout LyX-Code print 'Cancel Clicked' \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code dialog.destroy() \end_layout \begin_layout LyX-Code print "File Choosen: ", filename \end_layout \begin_layout Standard Just like in the on_save_clicked function the on_open_clicked starts off by setting the filename to None. Then it sets up the open dialog using the gtk.FileChooserDialog. It sets the dialog title to \begin_inset Quotes eld \end_inset Select a File \begin_inset Quotes erd \end_inset , the action to open with gtk.FILE_CHOOSER_ACTION_OPEN. The buttons for the dialog are set as a tuple with the button type and button response next to each other. It sets a cancel button with gtk.STOCK_CANCEL with a response of gtk.RESPONSE and open button with gtk.STOCK_OPEN with a response of gtk.RESPONSE_OK. \end_layout \begin_layout Standard After it checks to see if there are filters set and if so adds filters to the dialog using the add_filter method. \end_layout \begin_layout Standard The dialog is run using the run method and assigns the response to the variable response like so: \end_layout \begin_layout LyX-Code response = dialog.run() \end_layout \begin_layout Standard The on_open_clicked function then checks the value of the response variable. If the response is gtk.RESPONSE_OK the file name is set by using the dialogs get_filename() method. \end_layout \begin_layout LyX-Code filename = dialog.get_filename() \end_layout \begin_layout Standard If the response is gtk.RESPONSE_CANCEL no action is taken. The very last action that is taken is to call the dialogs destroy method. \end_layout \begin_layout LyX-Code dialog.destroy() \end_layout \begin_layout Standard If the destroy method is not called the dialog will stay on screen. \end_layout \begin_layout Subsection gtk.FileChooserButton \end_layout \begin_layout Standard The gtk.FileChooserButton \begin_inset Index status collapsed \begin_layout Plain Layout gtk!FileChooserButton \end_layout \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout File Chooser!FileChooserButton \end_layout \end_inset eases the use of a open file dialog by taking care of the run and destroy code and also provides a button. This is easier than the previous section on the FileChooserDialog. \end_layout \begin_layout Standard File Chooser Button \end_layout \begin_layout LyX-Code def main(): \end_layout \begin_layout LyX-Code #file filters used with the filechoosers \end_layout \begin_layout LyX-Code text_filter=gtk.FileFilter() \end_layout \begin_layout LyX-Code text_filter.set_name("Text files") \end_layout \begin_layout LyX-Code text_filter.add_mime_type("text/*") \end_layout \begin_layout LyX-Code all_filter=gtk.FileFilter() \end_layout \begin_layout LyX-Code all_filter.set_name("All files") \end_layout \begin_layout LyX-Code all_filter.add_pattern("*") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code window = gtk.Window(gtk.WINDOW_TOPLEVEL) \end_layout \begin_layout LyX-Code window.set_title("Native Filechooser") \end_layout \begin_layout LyX-Code window.connect("destroy", lambda wid: gtk.main_quit()) \end_layout \begin_layout LyX-Code window.connect("delete_event", lambda e1,e2:gtk.main_quit()) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code button_open = gtk.FileChooserButton("Open File") \end_layout \begin_layout LyX-Code button_open.add_filter(text_filter) \end_layout \begin_layout LyX-Code button_open.add_filter(all_filter) \end_layout \begin_layout LyX-Code button_open.connect("selection-changed", on_file_selected) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code window.add(button_open) \end_layout \begin_layout LyX-Code window.show_all() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_file_selected(widget): \end_layout \begin_layout LyX-Code filename = widget.get_filename() \end_layout \begin_layout LyX-Code print "File Choosen: ", filename \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if __name__ == "__main__": \end_layout \begin_layout LyX-Code main() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Standard This example starts by creating two filter types using the gtk.FileFilter class. One filter for text files and one filter for any type of file. Skip a few lines and a FileChooserButton is created like this: \end_layout \begin_layout LyX-Code button_open = gtk.FileChooserButton( \begin_inset Quotes eld \end_inset Open File \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout Standard To retrieve the selected file from a FileChooserButton it must connect the \shape italic selection-changed \shape default \begin_inset Index status collapsed \begin_layout Plain Layout signals!selection-changed \end_layout \end_inset signal to a function. So this example connects the selection-changed signal to the on_file_selected function. The on_file_selected function retrieves the filename that was choosen and then prints it. \end_layout \begin_layout Subsection Windows File Chooser \end_layout \begin_layout Standard The native GTK filechoosers are generally ok, but they are very ugly if the GTK application is running on Windows. For PyGTK apps that are running on Windows the option exists to use a native Windows file chooser dialog. The following example will show how to open a file and to save a file. This example will require that the pywin32 package be installed \begin_inset Foot status collapsed \begin_layout Plain Layout See section \begin_inset CommandInset ref LatexCommand vref reference "sec:Appendix PyGTK and Windows" \end_inset for instructions on using PyGTK on Windows for more information. Or just go to \begin_inset CommandInset href LatexCommand href target "http://sourceforge.net/projects/pywin32/files/" \end_inset and download and install it. \end_layout \end_inset . \end_layout \begin_layout Standard First off the os, win32con, and win32gui modules will need to be imported along with the pygtk and gtk modules. \end_layout \begin_layout LyX-Code import os \end_layout \begin_layout LyX-Code import win32gui, win32con \end_layout \begin_layout Standard Like all the other examples about file choosers the Windows file chooser will start off with some GUI code. \end_layout \begin_layout LyX-Code def main(): \end_layout \begin_layout LyX-Code file_filter="""Text files \backslash 0*.txt \backslash 0All Files \backslash 0*.* \backslash 0""" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code window = gtk.Window(gtk.WINDOW_TOPLEVEL) \end_layout \begin_layout LyX-Code window.set_title("Windows Filechooser Example") \end_layout \begin_layout LyX-Code window.connect("destroy", lambda wid: gtk.main_quit()) \end_layout \begin_layout LyX-Code window.connect("delete_event", lambda e1,e2:gtk.main_quit()) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code button_save = gtk.Button("Save File") \end_layout \begin_layout LyX-Code button_open = gtk.Button("Open File") \end_layout \begin_layout LyX-Code button_save.connect("clicked", on_save_clicked, file_filter) \end_layout \begin_layout LyX-Code button_open.connect("clicked", on_open_clicked, file_filter) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code hbox = gtk.HBox(True, 0) \end_layout \begin_layout LyX-Code hbox.pack_start(button_save, True, True, 5) \end_layout \begin_layout LyX-Code hbox.pack_start(button_open, True, True, 5) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code window.add(hbox) window.show_all() \end_layout \begin_layout Standard First thing that is done is to create a file filter that will be used with the open and save dialogs. The file filter is in the form of \begin_inset Quotes eld \end_inset Display Name, Seperator, File Type, Seperator, Display Name, Seperator, File Type, Seperator \begin_inset Quotes erd \end_inset and looks like this: \end_layout \begin_layout LyX-Code file_filter="""Text files \backslash 0*.txt \backslash 0All Files \backslash 0*.* \backslash 0""" \end_layout \begin_layout Standard The GUI creates one button to launch the save dialog and one to launch the open dialog. The button called button_save is clicked it will call the on_save_clicked function passing along the file filter. When the button called button_open is clicked, it will call the on_open_clicked function passing along the file filter. \end_layout \begin_layout Standard The on_save_clicked and on_open_clicked function are very similar in form with some minor differences. Here is the on_save_clicked function. \end_layout \begin_layout LyX-Code def on_save_clicked(widget, file_filter=None): \end_layout \begin_layout LyX-Code filename=None \end_layout \begin_layout LyX-Code try: \end_layout \begin_layout LyX-Code filename, customfilter, flags=win32gui.GetSaveFileNameW( \end_layout \begin_layout LyX-Code InitialDir=os.path.join(os.environ['USERPROFILE'],"My Documents"), \end_layout \begin_layout LyX-Code Flags=win32con.OFN_ALLOWMULTISELECT|win32con.OFN_EXPLORER, File='', \end_layout \begin_layout LyX-Code DefExt='txt', Title='Save a File', Filter=file_filter, FilterIndex=0) \end_layout \begin_layout LyX-Code except win32gui.error: \end_layout \begin_layout LyX-Code print "Cancel clicked" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code print filename \end_layout \begin_layout LyX-Code if filename != None: \end_layout \begin_layout LyX-Code save_file = open(filename, 'w') \end_layout \begin_layout LyX-Code save_file.write("Test Save Data") \end_layout \begin_layout LyX-Code save_file.close() \end_layout \begin_layout LyX-Code return filename \end_layout \begin_layout Standard This is a simple funtion that takes a file filter as an argument and sets it as the filter for Windows save dialog. To use and display the save dialog the win32gui.GetSaveFileNameW \begin_inset Index status collapsed \begin_layout Plain Layout GetSaveFileNameW \end_layout \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout File Chooser!GetSaveFileNameW \end_layout \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout win32gui!GetSaveFileNameW \end_layout \end_inset function is used. Arguments that are used with it include Initial Directory, Flags, File, Default Extention, Title, File Filter, and FilterIndex. As can be seen the inital directory is set to the users My Documents folder. Flags are set to allow multiple selection. The default extention type is txt. When it is called it must be done by assigning its return value to three variables; filename, customfilter, flags. \end_layout \begin_layout Standard The GetSaveFileNameW function must be used with exception handling as it will through an exception if the cancel button is clicked. So this example catches win32gui.error exceptions and prints the message \begin_inset Quotes eld \end_inset Cancel clicked \begin_inset Quotes erd \end_inset instead of crashing. \end_layout \begin_layout Standard If a file has been selected to save this example saves it with the string \begin_inset Quotes eld \end_inset Test Save Data \begin_inset Quotes erd \end_inset . \end_layout \begin_layout Standard The GetOpenFileNameW \begin_inset Index status collapsed \begin_layout Plain Layout File Chooser!GetOpenFileNameW \end_layout \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout win32gui!GetOpenFileNameW \end_layout \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout GetOpenFileNameW \end_layout \end_inset function is used to select and open file on Windows. It is very simliar to the GetSaveFileNameW function covered above. Here is the on_open_clicked function that uses the Windows open dialog. \end_layout \begin_layout LyX-Code def on_open_clicked(widget, file_filter=None): \end_layout \begin_layout LyX-Code filename=None \end_layout \begin_layout LyX-Code try: \end_layout \begin_layout LyX-Code filename, customfilter, flags=win32gui.GetOpenFileNameW( \end_layout \begin_layout LyX-Code InitialDir=os.path.join(os.environ['USERPROFILE'],"My Documents"), \end_layout \begin_layout LyX-Code Flags=win32con.OFN_ALLOWMULTISELECT|win32con.OFN_EXPLORER, File='', \end_layout \begin_layout LyX-Code DefExt='txt', Title='Select a File', Filter=file_filter, FilterIndex=0) \end_layout \begin_layout LyX-Code except win32gui.error: \end_layout \begin_layout LyX-Code print "Cancel clicked" \end_layout \begin_layout LyX-Code print 'open file names:', filename \end_layout \begin_layout LyX-Code return filename \end_layout \begin_layout Standard The GetOpenFileNameW functions takes as arguments Intial Directory, Flags, File, Default Extention, Title, File Filter, and Filter Index. As can be seen the inital directory is set to the users My Documents folder. Flags are set to allow multiple selection. The default extention type is txt. When calling this function the return value must be assigned to three variables ; these being the filename, customfilter, and flags. \end_layout \begin_layout Standard Like the save GetSaveFileNameW the GetOpenFileNameW function requires that it used with exception handling as it will give win32gui.error if the cancel button is pressed. If everything goes as planed the function should continue to the end where it prints the message \begin_inset Quotes eld \end_inset open file names: filename \begin_inset Quotes erd \end_inset . \end_layout \begin_layout Section Glade 3 \begin_inset CommandInset label LatexCommand label name "sec:Glade-3" \end_inset \end_layout \begin_layout Standard \begin_inset Index status collapsed \begin_layout Plain Layout Glade \end_layout \end_inset Glade is a program that allows the creation of the user interface graphical. Windows and dialogs can be created. Widgets can be dragged and dropped into place. Names assigned to widgets, callback functions assigned. All this is saved to a xml file with an extension of .glade. \end_layout \begin_layout Standard Docked on the left side of glade is the palette. The palette contains the top level elements such as: \end_layout \begin_layout Itemize windows (gtk.Window) \end_layout \begin_layout Itemize dialogs (gtk.Dialog etc...) \end_layout \begin_layout Standard Under the Toplevels is are the Containers. The containers contain: \end_layout \begin_layout Itemize Horizontal Box (gtk.HBox) \end_layout \begin_layout Itemize Vertical box (gtk.VBox) \end_layout \begin_layout Itemize Table (gtk.Table) \end_layout \begin_layout Itemize Notebook (gtk.Notebook) \end_layout \begin_layout Itemize Frame (gtk.Frame) \end_layout \begin_layout Itemize etc... \end_layout \begin_layout Standard After and under the Containers are the Control and Display widgets, they contain: \end_layout \begin_layout Itemize Button (gtk.Button) \end_layout \begin_layout Itemize Toggle Button (gtk.ToggleButton) \end_layout \begin_layout Itemize Check Button (gtk.CheckButton) \end_layout \begin_layout Itemize Spin Button (gtk.SpinButton) \end_layout \begin_layout Itemize Raido Button (gtk.RadioButton) \end_layout \begin_layout Itemize etc... \end_layout \begin_layout Standard To create a simple application, from the Toplevels select and add a Window. Next select a Horizontal Box and add it to the Window. When prompted for how many items, select two. When this is done the window will be split in half horizontally with a line going down through the center (see figure \begin_inset CommandInset ref LatexCommand vref reference "fig:Basic Glade User Interface" \end_inset ). Each of these can hold a widget. \end_layout \begin_layout Standard \begin_inset Float figure wide false sideways false status open \begin_layout Plain Layout \begin_inset Graphics filename images/more-pygtk/Screenshot-glade-example.png scale 30 \end_inset \end_layout \begin_layout Plain Layout \begin_inset Caption \begin_layout Plain Layout Basic Glade User Interface Designer \end_layout \end_inset \begin_inset CommandInset label LatexCommand label name "fig:Basic Glade User Interface" \end_inset \end_layout \end_inset \end_layout \begin_layout Standard Next add two buttons from the container. The one on the left label Message and the one on the right label About. Also change the names to message and about. To do this click the first button. On the right hand side the editor should change for a button type (see figure \begin_inset CommandInset ref LatexCommand vref reference "fig:Glade Editor with Button" \end_inset ). As can be seen in figure \begin_inset CommandInset ref LatexCommand ref reference "fig:Glade Editor with Button" \end_inset ; the class is of type GtkButton, the name is set to message meaning that when it is called with PyGTK it uses the name message. For the Label it is set to Message. The label is what is displayed to the user as the button text. \end_layout \begin_layout Standard \begin_inset Float figure wide false sideways false status collapsed \begin_layout Plain Layout \begin_inset Graphics filename images/more-pygtk/Screenshot-glade-example-button-editor.png scale 50 \end_inset \end_layout \begin_layout Plain Layout \begin_inset Caption \begin_layout Plain Layout Glade Editor with Button \end_layout \end_inset \begin_inset CommandInset label LatexCommand label name "fig:Glade Editor with Button" \end_inset \end_layout \begin_layout Plain Layout \end_layout \end_inset \end_layout \begin_layout Standard Once the buttons have been added and setup with the names and labels then the signals that are to be caught should be added (see figure \begin_inset CommandInset ref LatexCommand vref reference "fig:Glade Signal Handler Specified" \end_inset ). To add signal methods to the buttons first select the message button. Then in the editor window select the Signals tab. Under GtkButton there will be a signal called \emph on clicked \emph default . For clicked add a handler. If the handler space is clicked it will provide a default list to choose from. To see what it should look like look at figure \begin_inset CommandInset ref LatexCommand vref reference "fig:Glade Signal Handler Specified" \end_inset . What is typed as the Handler is the function or method in the python code that will be called. \end_layout \begin_layout Standard \begin_inset Float figure wide false sideways false status collapsed \begin_layout Plain Layout \begin_inset Graphics filename images/more-pygtk/Screenshot-glade-example-signal-handler.png scale 50 \end_inset \end_layout \begin_layout Plain Layout \begin_inset Caption \begin_layout Plain Layout Signal Handler Specified \end_layout \end_inset \begin_inset CommandInset label LatexCommand label name "fig:Glade Signal Handler Specified" \end_inset \end_layout \end_inset \end_layout \begin_layout Standard Now that the buttons have been added to the main window (whose name is window1) it is time to make sure that this window is visible. Select the main window and in the editor select the Common tab. Once in the Common tab find the \emph on Visible \emph default option and make sure it is set to \emph on Yes \emph default (see figure \begin_inset CommandInset ref LatexCommand vref reference "fig:Glade Main Windows Set as Visible" \end_inset ). \end_layout \begin_layout Standard \begin_inset Float figure wide false sideways false status collapsed \begin_layout Plain Layout \begin_inset Graphics filename images/more-pygtk/Screenshot-glade-example-main-window-visible.png scale 50 \end_inset \end_layout \begin_layout Plain Layout \begin_inset Caption \begin_layout Plain Layout Main Windows Set as Visible \end_layout \end_inset \begin_inset CommandInset label LatexCommand label name "fig:Glade Main Windows Set as Visible" \end_inset \end_layout \begin_layout Plain Layout \end_layout \end_inset \end_layout \begin_layout Standard Now the main window is done. Save your work. Next an about dialog will be added. To add an about dialog it is selected from the Toplevel elements on the palette. Leave it with the default name \emph on aboutdialog1 \emph default . The about dialog will be used to show how to interact with more than one window in glade. \end_layout \begin_layout Standard A PyGTK program interacts with the created glade file using \begin_inset Index status collapsed \begin_layout Plain Layout gtk!glade \end_layout \end_inset gtk.glade. \end_layout \begin_layout LyX-Code import pygtk \end_layout \begin_layout LyX-Code pygtk.require('2.0') \end_layout \begin_layout LyX-Code import gtk \end_layout \begin_layout LyX-Code import gtk.glade \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code class GladeExample(object): \end_layout \begin_layout LyX-Code def __init__(self): \end_layout \begin_layout LyX-Code self.gladefile = gtk.glade.XML("glade-example.glade") \end_layout \begin_layout LyX-Code self.gladefile.signal_autoconnect(self) \end_layout \begin_layout LyX-Code self.main_window = self.gladefile.get_widget("window1") \end_layout \begin_layout LyX-Code self.about_dialog = self.gladefile.get_widget("aboutdialog1") \end_layout \begin_layout LyX-Code self.message_dialog = self.gladefile.get_widget("messagedialog1") \end_layout \begin_layout Standard Here the class GladeExample is declared with an intiation method that connects to the glade file that was created. The glade file is loaded using the gtk.glade.XML class. It takes as arguments the glade file and optionally a widget and translation domain. \end_layout \begin_layout Standard Then to use a widget as if it was created using PyGTK code it must be retrieved using the get_widget method. The get_widget method works by taking as an argument the name of the widget. In the glade example above the main windows name is window1, the about dialogs name is aboutdialog1, and the message dialog is messagedialog1. As can be seen the main window is assigned to self.main_window and so on with the about and message dialog. \end_layout \begin_layout Standard What can be noticed that the buttons that were adding to the glade file to launch the about and message dialog were not assigned with the get_widget method. This is because they were set to automatically call handler functions and do not need to write code for each button to connect them. This is handled with one line of code, self.gladefile.signal_autoconnect(self). This one line will automatically connect any signal handlers that were specified in the glade file without having to write any extra code. \end_layout \begin_layout LyX-Code def on_about_clicked(self, widget): \end_layout \begin_layout LyX-Code self.about_dialog.run() \end_layout \begin_layout LyX-Code self.about_dialog.destroy() \end_layout \begin_layout Standard As was specified with glade, when the about button is clicked, the method on_about_clicked is called. This method displays the about dialog that was created with glade and destroys the dialog when it is closed. \end_layout \begin_layout LyX-Code def on_message_clicked(self, widget): \end_layout \begin_layout LyX-Code self.message_dialog.run() \end_layout \begin_layout LyX-Code self.message_dialog.destroy() \end_layout \begin_layout Standard As was specified with glade, when the message button is clicked, the method on_message_clicked is called. This method displays the message dialog that was created with glade and destroys the dialog when it is closed. \end_layout \begin_layout LyX-Code def on_window1_delete_event(self, widget, event): \end_layout \begin_layout LyX-Code gtk.main_quit() \end_layout \begin_layout Standard the on_window1_delete_event will quite the PyGTK application when the main window(window1) is closed. This to is specified with glade under the main windows Signal tab; GtkWidget --> delete-event. \end_layout \begin_layout LyX-Code if __name__ == "__main__": \end_layout \begin_layout LyX-Code app = GladeExample() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Standard And of course a few lines that runs the glade example. \end_layout \begin_layout Section Builder \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sec: Gtk Builder Convert" \end_inset \end_layout \begin_layout Standard Builder refers to gtk.Builder which is the future as it is a replacement for gtk.glade. Basically what it is is including support for xml files to build applications inside of GTK itself, unlike glade which is a library. Currently the glade program does not support saving to the Builder format, but it will soon. In the mean time glade files must be converted to Builder files using \emph on gtk-builder-convert \emph default \begin_inset Foot status collapsed \begin_layout Plain Layout For more information on gtk-builder-convert visit: \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://library.gnome.org/devel/gtk/2.12/gtk-builder-convert.html \end_layout \end_inset . \end_layout \begin_layout Plain Layout Also if you plan on using gtk-builder-convert, gtk development files must be installed to have it installed. This is accomplished on Ubuntu by installing libgtk2.0-dev. \end_layout \end_inset . This program will take a glade xml file and convert it to a Builder xml file. \end_layout \begin_layout Standard To convert a glade file to a Builder file the following command is issued: \end_layout \begin_layout LyX-Code gtk-builder-convert glade-example.glade glade-example.xml \end_layout \begin_layout Standard Now instead of using gtk.glade.XML to access this new builder xml file, gtk.Builder is used as shown here. \end_layout \begin_layout LyX-Code builder = gtk.Builder() \end_layout \begin_layout LyX-Code builder.add_from_file( \begin_inset Quotes eld \end_inset glade-example.xml \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout Standard Also instead of using get_widget like in the glade example (see \begin_inset CommandInset ref LatexCommand ref reference "sec:Glade-3" \end_inset ), the method \emph on get_object \emph default is used. \end_layout \begin_layout LyX-Code main_window = builder.get_object( \begin_inset Quotes eld \end_inset window1 \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code about_dialog = builder.get_object( \begin_inset Quotes eld \end_inset aboutdialog1 \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code message_dialog = builder.get_object( \begin_inset Quotes eld \end_inset messagedialog1 \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout Standard With this done, the widgets can be used as if they were programmed normally with PyGTK. \end_layout \begin_layout Standard To auto connect the signals like is avialbe using glade the following code is used. \end_layout \begin_layout LyX-Code builder.connect_signals(self) \end_layout \begin_layout Standard Remember this needs to be done from within a class. \end_layout \begin_layout Section Loading Images \end_layout \begin_layout Standard To load an image with PyGTK an instance of the gtk.Image \begin_inset Index status collapsed \begin_layout Plain Layout gtk!Image \end_layout \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout Image Loading \end_layout \end_inset class must be created. With this becomes available several methods for loading different types of images. This example will cover loading images from file and from the GTK stock images. \end_layout \begin_layout Standard Loading Images \end_layout \begin_layout LyX-Code import pygtk, gtk \end_layout \begin_layout LyX-Code def main(): \end_layout \begin_layout LyX-Code win = gtk.Window() \end_layout \begin_layout LyX-Code win.connect("delete_event", lambda w,e: gtk.main_quit()) \end_layout \begin_layout LyX-Code vbox = gtk.VBox(False, 0) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code image1 = gtk.Image() \end_layout \begin_layout LyX-Code image1.set_from_stock(gtk.STOCK_DIALOG_INFO, gtk.ICON_SIZE_DND) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code image2 = gtk.Image() \end_layout \begin_layout LyX-Code image2.set_from_file("flower.jpg") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code vbox.pack_start(image1, False, False, 5) \end_layout \begin_layout LyX-Code vbox.pack_start(image2, False, False, 5) \end_layout \begin_layout LyX-Code win.add(vbox) \end_layout \begin_layout LyX-Code win.show_all() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if __name__ == "__main__": \end_layout \begin_layout LyX-Code main() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Standard This example creates a window with a gtk.VBox and adds two images. The first image is set from stock gtk images created with the set_from stock method. The set_from_stock method requires a GTK stock image and a stock size. The stock types available can be found in the appendix ( \begin_inset CommandInset ref LatexCommand vpageref reference "sec:Appendix Stock Icons" \end_inset ). The stock sizes include: \end_layout \begin_layout Itemize gtk.ICON_SIZE_MENU \end_layout \begin_layout Itemize gtk.ICON_SIZE_SMALL_TOOLBAR \end_layout \begin_layout Itemize gtk.ICON_SIZE_LARGE_TOOLBAR \end_layout \begin_layout Itemize gtk.ICON_SIZE_BUTTON \end_layout \begin_layout Itemize gtk.ICON_SIZE_DND \end_layout \begin_layout Itemize gtk.ICON_SIZE_DIALOG \end_layout \begin_layout Standard The second image is loaded using set_from_file method. All this method requires is location on the computer to the image. \end_layout \begin_layout Standard All that needs to be done once the images are loaded is add them to a widget. In this example they are added to gtk.VBox. \end_layout \begin_layout Standard There are many different methods for loading images and they can be found at the PyGTK reference site \begin_inset Foot status collapsed \begin_layout Plain Layout The PyGTK image class can be found at: \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.pygtk.org/docs/pygtk/class-gtkimage.html \end_layout \end_inset \end_layout \end_inset . \end_layout \begin_layout Standard \begin_inset Branch SecondEdition status open \begin_layout Section Color Selector \end_layout \begin_layout Standard This section is not yet written :) \end_layout \begin_layout Section Textarea Hyperlinks \end_layout \begin_layout Standard This section is not yet written :) \end_layout \begin_layout Section Label Hyperlinks \end_layout \begin_layout Standard This section is not yet written :) \end_layout \end_inset \end_layout \begin_layout Section Tooltips \end_layout \begin_layout Standard A tooltip \begin_inset Index status collapsed \begin_layout Plain Layout gtk!Tooltip \end_layout \end_inset is used to display useful information to the screen a user puts a mouse over a widget such as label or button. To use a simple tooltip requires only on method call on the widget: set_tooltip _text \end_layout \begin_layout LyX-Code label = gtk.Label("Display Tooltip") \end_layout \begin_layout LyX-Code label.set_tooltip_text("This is a Tooltip") \end_layout \begin_layout Standard When a mouse is placed over this label a tooltip will display the text \begin_inset Quotes eld \end_inset This is a Tooltip \begin_inset Quotes erd \end_inset . Very simple to use and there is nothing more to be said on that. \end_layout \begin_layout Standard For more fancy tooltips a custom tooltip must be created. To do this the has_tooltip property must be set to True. Then the widget that is to display the custom tooltip must connect to the query-tooltip \begin_inset Index status collapsed \begin_layout Plain Layout gtk!Tooltip!query-tooltip \end_layout \end_inset signal. For example, the callback function can create a new tooltip by creating an gtk.HBox that holds an image and text then use set_custom on the tooltip to use this hbox. \end_layout \begin_layout Standard Here is an example \begin_inset Index status collapsed \begin_layout Plain Layout gtk!Tooltip!Custom Tooltip \end_layout \end_inset . \end_layout \begin_layout LyX-Code fancy_label = gtk.Label("A fancy Tooltip") \end_layout \begin_layout LyX-Code fancy_label.props.has_tooltip = True \end_layout \begin_layout LyX-Code fancy_label.connect("query-tooltip", on_query_tooltip) \end_layout \begin_layout Standard So this creates a label, sets the tooltip to true using fancy_label.props.has_tool tip property, and then connects the query-tooltip signal to the function on_query_tooltip. \end_layout \begin_layout Standard Here is an example of the on_query_tooltip function. This function creates a label and an image that is displayed instead of plain text. \end_layout \begin_layout LyX-Code def on_query_tooltip(widget, x, y, keyboard_tip, tooltip): \end_layout \begin_layout LyX-Code hbox = gtk.HBox() \end_layout \begin_layout LyX-Code label = gtk.Label('Fancy Tooltip with an Image') \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code image = gtk.Image() \end_layout \begin_layout LyX-Code image.set_from_stock(gtk.STOCK_DIALOG_INFO, gtk.ICON_SIZE_DND) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code hbox.pack_start(image, False, False, 0) \end_layout \begin_layout LyX-Code hbox.pack_start(label, False, False, 0) \end_layout \begin_layout LyX-Code hbox.show_all() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code tooltip.set_custom(hbox) \end_layout \begin_layout LyX-Code return True \end_layout \begin_layout Standard As can be seen this creates a gtk.HBox to hold a label and an Image. It then uses the tooltip argument to set it to a custom tooltip. A custom tooltip can be anything but this example has kept it simple for understandability sake. For more tooltip options visit the PyGTK tooltip reference page \begin_inset Foot status collapsed \begin_layout Plain Layout The PyGTK tooltip reference page can be found at: \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.pygtk.org/docs/pygtk/class-gtktooltip.html \end_layout \end_inset \end_layout \end_inset . \end_layout \begin_layout Section Summary \end_layout \begin_layout Standard This section is not yet written :) \end_layout \begin_layout Chapter Cairo \end_layout \begin_layout Standard \begin_inset Box Frameless position "t" hor_pos "c" has_inner_box 1 inner_pos "t" use_parbox 0 width "100col%" special "none" height "1in" height_special "totalheight" status open \begin_layout Plain Layout \begin_inset CommandInset include LatexCommand input filename "chapter-heading.lyx" \end_inset \end_layout \end_inset \end_layout \begin_layout Section Introduction \end_layout \begin_layout Standard Welcome to the chapter on cairo \begin_inset Index status collapsed \begin_layout Plain Layout cairo \end_layout \end_inset . What is cairo? Cario is a powerful 2d graphics library that lets you output to many different surfaces. Surfaces that are supported include image surfaces (png) pdf, postscript, win32, svg, quartz, and xlib. What all these different surfaces achieve will be discussed throughout the chapter; however every surface type here supports writing to png. Besides including png write support, cairo also includes png import support. \end_layout \begin_layout Standard While reading about cairo in other sources you may find that it is suggested to think of cairo is as a canvas that you will paint on. This kind of works for me. You have the canvas that you can put different layers of paint on that when combined and finished produces your final output. But that is about as far I will be using this metaphor in this chapter. \end_layout \begin_layout Standard Things that cairo can be used for include creating graphics, combining work, doing layout for printing, or even creating reports as pdf documents. Really the only limitation to cairo is your imagination. \end_layout \begin_layout Standard Dig in and see what you can learn. \end_layout \begin_layout Section Basics \end_layout \begin_layout Standard The first example with cairo will be some simple drawing. It will create a surface and draw a line saving to a image file. When working with cairo it must be remembered that the \shape italic cairo \shape default module is needed and must be imported. \end_layout \begin_layout LyX-Code import cairo \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code WIDTH, HEIGHT = 400, 400 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Setup Cairo \end_layout \begin_layout LyX-Code surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT) \end_layout \begin_layout LyX-Code context = cairo.Context(surface) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Set thickness of brush \end_layout \begin_layout LyX-Code context.set_line_width(15) \end_layout \begin_layout LyX-Code # Draw Vertical Line \end_layout \begin_layout LyX-Code context.move_to(200, 150) \end_layout \begin_layout LyX-Code context.line_to(200, 250) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Draw horizontal line \end_layout \begin_layout LyX-Code context.move_to(150, 200) \end_layout \begin_layout LyX-Code context.line_to(250, 200) \end_layout \begin_layout LyX-Code context.stroke() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Output a PNG file \end_layout \begin_layout LyX-Code surface.write_to_png("cairo-draw-line1.png") \end_layout \begin_layout Standard This example creates an ImageSurface with a width and height of 400. The ImageSurface is set to use the cairo.FORMAT_ARG32 (See below for details). The context is what actually keeps track of everything that is done to the surface and is used to draw. It is used to control how the drawing operations are used. \end_layout \begin_layout Standard With a context set it is now possible to draw or perform other actions. First thing that is done is to set the line width to 15 using the \emph on context.set_line_width(15) \emph default \begin_inset Index status collapsed \begin_layout Plain Layout cairo!set \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset line \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset width \end_layout \end_inset method. The \emph on set_line \emph default method sets the width of a line for a context. \end_layout \begin_layout Standard Next, using the contexts move_to \begin_inset Index status collapsed \begin_layout Plain Layout cairo!move \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset to \end_layout \end_inset method moves the position of the brush to the position specified; which in the line is x position 200 and y position 150. X coordinates \begin_inset Index status collapsed \begin_layout Plain Layout cairo!coordinates \end_layout \end_inset are measured from the left most part of the surface. Y coordinates are measured from the top most part of the surface. \end_layout \begin_layout Standard Using the contexts \emph on line_to \emph default method will draw a line from the current position, that was specified with the move_to method, to the new position specified with the line_to method. To display what has been drawn with the \emph on line_to \emph default method the contexts \emph on stroke \emph default method must be called. Once \emph on context.stroke() \emph default is called then the lines are actually applied to the surface. \end_layout \begin_layout Standard See figure \begin_inset CommandInset ref LatexCommand ref reference "fig:Cairo - Two Striaght Lines" \end_inset to see what the output should look like. \end_layout \begin_layout Standard \begin_inset Float figure wide false sideways false status open \begin_layout Plain Layout \begin_inset Graphics filename images/cairo/cairo-draw-line1.png scale 50 \end_inset \end_layout \begin_layout Plain Layout \begin_inset Caption \begin_layout Plain Layout Two Straight Lines \end_layout \end_inset \begin_inset CommandInset label LatexCommand label name "fig:Cairo - Two Striaght Lines" \end_inset \end_layout \end_inset \end_layout \begin_layout Subsection Cairo Surface Format \end_layout \begin_layout Standard There are four surface options available \begin_inset Foot status collapsed \begin_layout Plain Layout The list of formats available are taken from the cairo website and can be found at: \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.cairographics.org/manual/cairo-Image-Surfaces.html \end_layout \end_inset \end_layout \end_inset and they are: \end_layout \begin_layout List \labelwidthstring 00.00.0000 cairo.FORMAT_ARGB32 - each pixel is a 32-bit quantity, with alpha in the upper 8 bits, then red, then green, then blue. The 32-bit quantities are stored native-endian. Pre-multiplied alpha is used. (That is, 50% transparent red is 0x80800000, not 0x80ff0000.) \end_layout \begin_layout List \labelwidthstring 00.00.0000 cairo.FORMAT_RGB24 - each pixel is a 32-bit quantity, with the upper 8 bits unused. Red, Green, and Blue are stored in the remaining 24 bits in that order. \end_layout \begin_layout List \labelwidthstring 00.00.0000 cairo.FORMAT_A8 - each pixel is a 8-bit quantity holding an alpha value. \end_layout \begin_layout List \labelwidthstring 00.00.0000 cairo.FORMAT_A1 - each pixel is a 1-bit quantity holding an alpha value. Pixels are packed together into 32-bit quantities. The ordering of the bits matches the endianess of the platform. On a big-endian machine, the first pixel is in the uppermost bit, on a little-endian machine the first pixel is in the least-significant bit. \end_layout \begin_layout Standard In most cases cairo.FORMAT_ARGB32 or cairo.FORMAT_RGB24 will be used. \end_layout \begin_layout Subsection Cairo Surfaces \end_layout \begin_layout Standard \begin_inset Note Note status open \begin_layout Plain Layout Fill this section out a little with some commentary and a very very small code snippet. \end_layout \end_inset \end_layout \begin_layout List \labelwidthstring 00.00.0000 cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT) - Use to render to memory buffers. \end_layout \begin_layout List \labelwidthstring 00.00.0000 cairo.PDFSurface("drawings.pdf", WIDTH, HEIGHT) - Renders to the specified PDF file. \end_layout \begin_layout List \labelwidthstring 00.00.0000 cairo.PSSurface("drawings.ps", WIDTH, HEIGHT) - Renders to the specified Postscrip t file. \end_layout \begin_layout List \labelwidthstring 00.00.0000 cairo.SVGSurface("drawings.svg", WIDTH, HEIGHT) - Renders to the specified SVG file. \end_layout \begin_layout Section Drawing Context \end_layout \begin_layout Standard As discussed above, a context is what allows the programmer to use the cairo surface. This section will discover different uses of the context class by making use of several different examples. For a list of the context methods used in this section please skip ahead to \begin_inset CommandInset ref LatexCommand vref reference "sub:Cairo Context Methods" \end_inset . \end_layout \begin_layout Subsection Paths: Lines, Curves, Arcs \end_layout \begin_layout Standard To start off this section lets take a look at line drawing again, but using it to draw more than two straight lines. This example will use the \emph on line_to \emph default method to create a rectangle and triangle. It will also use a new method, \emph on arc \emph default , to create a circle. Along with with these two methods, the color of the context will be set using the \emph on set_source_rgb \emph default value. These methods set points that are then used to create a path. \end_layout \begin_layout Standard This example starts by calling the \emph on main \emph default function. Inside the main function it creates a cairo ImageSurface with an alpha RGB format and a width and height of 400. It then creates a context from this surface. The ImageSurface renders to a memory buffer and not an image. To save to an image the surface must call the \emph on write_to_png \emph default method that is available to all surface types. \end_layout \begin_layout Standard Next it sets the line with of the context to 15. Immediately after this it calls the draw_rectangle, draw_triangle, and draw_circle functions. These are functions that are defined in this example and are not cairo builtin methods. While cairo contexts do have a builtin method to create rectangles, this example is doing it manually just to show how to use the line_to method in different ways. \end_layout \begin_layout Standard Cairo Context Basics \end_layout \begin_layout LyX-Code #!/usr/bin/env python \end_layout \begin_layout LyX-Code import cairo \end_layout \begin_layout LyX-Code import math \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def draw_rectangle(context=None): \end_layout \begin_layout LyX-Code x1, y1 = 25, 150 # top left corner \end_layout \begin_layout LyX-Code x2, y2 = 25, 250 # bottom left corner \end_layout \begin_layout LyX-Code x3, y3 = 125, 250 # bottom right corner \end_layout \begin_layout LyX-Code x4, y4 = 125, 150 # top right corner \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code context.set_source_rgb(1.0, 0.0, 0.0) # red \end_layout \begin_layout LyX-Code context.move_to(x1, y1) \end_layout \begin_layout LyX-Code context.line_to(x2, y2) \end_layout \begin_layout LyX-Code context.line_to(x3, y3) \end_layout \begin_layout LyX-Code context.line_to(x4, y4) \end_layout \begin_layout LyX-Code context.close_path() \end_layout \begin_layout LyX-Code context.stroke() \end_layout \begin_layout Standard The draw_rectangle function starts off by defining four corners that will make up the rectangle. \end_layout \begin_layout Standard These four x and four y coordinates create the four corners of the rectangle. Top left, bottom left, bottom right and the top right corners. To draw a rectangle the function first uses the \emph on move_to(x1, y1) \emph default method on the context to move the starting position to the first corner. Then it uses the \emph on line_to(x2, y2) \emph default method to create a line from the first corner to the second. Then it again uses the \emph on line_to \emph default method with x3 and y3 to create a line from the second corner to the third corner. And last with the \emph on line_to \emph default method it creates a line from the third to the fourth corner. \end_layout \begin_layout Standard Now if you follow that lines that were created, you will notice only the left side, bottom, and right side where drawn, but all four corners were used. The line_to method could be used again to draw a line from x4 and y4 to x1 and y1, but instead the \emph on close_path \begin_inset Index status collapsed \begin_layout Plain Layout cairo!Context!close \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset path \end_layout \end_inset \emph default method is used. The close path method will draw a line from the current position to the first position (since the last time the \emph on stroke \emph default method was called). \end_layout \begin_layout Standard Also in the draw_rectangle function the context is set to draw these lines in red using the set_source_rgb(red, green, blue) \begin_inset Index status collapsed \begin_layout Plain Layout cairo!Context!set \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset source \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset rgb \end_layout \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout cairo!color \end_layout \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout cairo!Context!ontext!color \end_layout \end_inset method. This method takes 3 variables each with a value between 0.0 and 1.0, with 0.0 being none of that color and 1.0 being a solid color. The lower the value, the higher the opacity. \end_layout \begin_layout LyX-Code def draw_triangle(context=None): \end_layout \begin_layout LyX-Code context.set_source_rgb(0.0, 1.0, 0.0) # green \end_layout \begin_layout LyX-Code context.move_to(275, 175) \end_layout \begin_layout LyX-Code context.line_to(375, 375) \end_layout \begin_layout LyX-Code context.rel_line_to(-200, 0) \end_layout \begin_layout LyX-Code context.close_path() \end_layout \begin_layout LyX-Code context.stroke() \end_layout \begin_layout Standard The draw_triangle method is similar to the draw_rectangle function in that it also uses the move_to, line_to and close_path methods. But it also uses the \emph on rel_line_to \emph default \begin_inset Index status collapsed \begin_layout Plain Layout cairo!Context!rel \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset line \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset to \end_layout \end_inset method; this method stand for relative_line_to, and moves to a new position based on the current location instead of using the absolute value of the surfaces width and height. \end_layout \begin_layout Standard Like in the rectangle function, the triangle function sets the color \begin_inset Index status collapsed \begin_layout Plain Layout cairo!Context!color \end_layout \end_inset (to green) \end_layout \begin_layout Standard Next it starts by moving the starting coordinate to x coordinate 275 and y coordinate 175. Then draws a line to x 375 and y 375. \end_layout \begin_layout Standard After this instead of drawing based on absolute coordinates of the surface width and height, it uses the \emph on rel_line_to \emph default method to draw from x 375 and 375. It uses -200 x which moves from 375 to 175 and moves 0 from y. This means there is a line drawn from (375, 375) to (175, 375). \end_layout \begin_layout Standard Finally it closes the path and uses the \emph on stroke \emph default method to apply the lines to the surface. \end_layout \begin_layout LyX-Code def draw_circle(context=None): \end_layout \begin_layout LyX-Code width, height = 100, 100 \end_layout \begin_layout LyX-Code radius = min(width, height) \end_layout \begin_layout LyX-Code context.set_source_rgb(0.0, 0.0, 1.0) # blue \end_layout \begin_layout LyX-Code context.arc(275, 100, radius / 2.0 - 20, 0, 2 * math.pi) \end_layout \begin_layout LyX-Code context.stroke() \end_layout \begin_layout Standard The draw_circle function introduces the a new method; \emph on arc \emph default \begin_inset Index status collapsed \begin_layout Plain Layout cairo!Context!arc \end_layout \end_inset . \end_layout \begin_layout Standard The start_angle and stop_angle are specified in radians. If you do not know how to work with radians take a look at section \begin_inset CommandInset ref LatexCommand vref reference "sub:Radians and Degrees" \end_inset . Here the start angle is set to 0. The stop_angle is set to 2 * math.pi, which is 360 degrees. This arc therefore forms a full circle. \end_layout \begin_layout Standard Other parts of the arc method is the x and y coordinate positions for the center of the arc. After the x and y coordinates come the radius of the arc. \end_layout \begin_layout LyX-Code def draw_curve(context=None): \end_layout \begin_layout LyX-Code context.set_source_rgb(0.5, 0.0, 0.3) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code context.move_to(20, 20) \end_layout \begin_layout LyX-Code context.curve_to (60, 100, 100, 20, 140, 100) \end_layout \begin_layout LyX-Code context.stroke() \end_layout \begin_layout Standard The draw_curve function is used to draw a cubic Bézier spline from the current position to x3 and y3, using x1, x2, y1, y2 as control points. If no current position is set, x1 and y1 are used as the starting position. This is accomplished using the curve_to method. The curve_to method is defined as context.curve_to(x1, y1, x2, y2, x3, y3). \end_layout \begin_layout LyX-Code def main(): \end_layout \begin_layout LyX-Code surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 400, 400) \end_layout \begin_layout LyX-Code context = cairo.Context(surface) \end_layout \begin_layout LyX-Code context.set_line_width(15) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code draw_rectangle(context) \end_layout \begin_layout LyX-Code draw_triangle(context) \end_layout \begin_layout LyX-Code draw_circle(context) \end_layout \begin_layout LyX-Code draw_curve(context) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code surface.write_to_png("cairo-basics.png") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if __name__ == "__main__": \end_layout \begin_layout LyX-Code main() \end_layout \begin_layout Subsubsection Radians and Degrees \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sub:Radians and Degrees" \end_inset \end_layout \begin_layout Standard If you do not know how to work with radians you are in luck, as it is very simple. \end_layout \begin_layout LyX-Code radians=degree*(math.pi/180) \end_layout \begin_layout Standard If you want to know what the degrees of a radian is that is simple as well. Switch the degree with the radian and divide 180 by PI. \end_layout \begin_layout LyX-Code degree=radians*(180/math.pi) \end_layout \begin_layout Subsection Text \end_layout \begin_layout Standard Drawing text is with cairo is the same as drawing a line or an arc but using some specific functions for text. Start off like any other cairo application setting the type of surface and setup a context. \end_layout \begin_layout LyX-Code import cairo \end_layout \begin_layout LyX-Code text = "Hello to the Great Text." \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 800, 75) \end_layout \begin_layout LyX-Code context = cairo.Context(surface) \end_layout \begin_layout LyX-Code context.set_source_rgb(0.0, 0.0, 0.0) # set to black \end_layout \begin_layout Standard What is then needed is to set the type of font and its size. Here a Monospace font is set with a normal slant and is set to be bold (see section \begin_inset CommandInset ref LatexCommand vref reference "sub:Cairo - Font Styles" \end_inset for more styles). The size of the font is set to 50. \end_layout \begin_layout LyX-Code context.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, \end_layout \begin_layout LyX-Code cairo.FONT_WEIGHT_BOLD) \end_layout \begin_layout LyX-Code context.set_font_size(50) \end_layout \begin_layout Standard Using the context that was created it is possible to retrieve information on the text that is being used with the text_extents method. \end_layout \begin_layout LyX-Code x_bearing, y_bearing, width, height = context.text_extents(text)[:4] \end_layout \begin_layout Standard Last is to move the context to the location that it should be drawn. Here the text is set to draw a X coordinate 5 and at a Y coordinate that is is the height of the text. To apply the text the show_text method is now called. This method adds text to the cairo context. To show the text the stroke method is called. To finish off it is saved to a file called cairo-draw-text1.png. \end_layout \begin_layout LyX-Code context.move_to(5, height) \end_layout \begin_layout LyX-Code context.show_text(text) \end_layout \begin_layout LyX-Code context.stroke() \end_layout \begin_layout LyX-Code surface.write_to_png("cairo-draw-text1.png") \end_layout \begin_layout Subsubsection Font Styles \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sub:Cairo - Font Styles" \end_inset \end_layout \begin_layout Standard There are more than two types of font face styles available with cairo; there are five. \end_layout \begin_layout Itemize cairo.FONT_SLANT_ITALIC \end_layout \begin_layout Itemize cairo.FONT_SLANT_NORMAL \end_layout \begin_layout Itemize cairo.FONT_SLANT_OBLIQUE \end_layout \begin_layout Itemize cairo.FONT_WEIGHT_BOLD \end_layout \begin_layout Itemize cairo.FONT_WEIGHT_NORMAL \end_layout \begin_layout Subsection Antialias \end_layout \begin_layout Standard First lets define antialias so there is no confusion. \end_layout \begin_layout Description Antialias: Is the technique of minimizing the distortion artifacts created while drawing. \end_layout \begin_layout Standard But what does this mean? Basically nothing if a straight line is being drawn. However if a curve or arc is being drawn it will look distorted or jagged, not very smooth at all. However with antialiasing turned on it will look smooth by setting the color correctly around the edges. The best way to understand this is to view an image. Take a look at figure \begin_inset CommandInset ref LatexCommand ref reference "fig:Cairo - Antialias Example" \end_inset and see if you can tell the difference. \end_layout \begin_layout Standard \begin_inset Float figure wide false sideways false status open \begin_layout Plain Layout \begin_inset Graphics filename images/cairo/cairo-antialias.png \end_inset \end_layout \begin_layout Plain Layout \begin_inset Caption \begin_layout Plain Layout Antialias Example - As can be seen the circle on the left uses the default cairo antialias while the circle on the left turns antialias off. As can be seen when antialias is turned off the curves become jagged/distorted. \end_layout \end_inset \begin_inset CommandInset label LatexCommand label name "fig:Cairo - Antialias Example" \end_inset \end_layout \end_inset \end_layout \begin_layout Standard Now the question is why would you want to turn off antialiasing? I cannot think of to many reasons, but one that I can think of is for the program DeVeDe. It is a GUI application that uses a few command line applications to create DVDs from video files. \end_layout \begin_layout Standard One of the programs that DeVeDe uses is dvdauthor. One of the functions of dvdauthor is to create dvd menus. And one part of the menu system is not able to handle more than four colors in an image including the alpha channel. With antialiasing turned on it will output images with many colors, because to make a curve look smooth it uses different shades of the color being used. However if antialias is set to none the image created with cairo will only have the colors specified and will be able to be used with dvdauthor. \end_layout \begin_layout Subsubsection Changing Antialias \end_layout \begin_layout Standard To change the default antialias the contexts set_antialias method is used. \end_layout \begin_layout LyX-Code context.set_antialias(Antialias Type) \end_layout \begin_layout Standard To find out what the current setting is just use the context get_antialias() method. \end_layout \begin_layout Standard The example below sets up a normal cairo surface and context. It then draws two circles. The first circles draws with the default antialias, which is cairo.ANTIALIAS_DEF AULT, and the second circle is drawn with antialias turned off. \end_layout \begin_layout LyX-Code import cairo, math \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def draw_circle(context, xc, yc): \end_layout \begin_layout LyX-Code radius = 150 \end_layout \begin_layout LyX-Code context.set_source_rgb(0.0, 0.0, 1.0) \end_layout \begin_layout LyX-Code context.arc(xc, yc, radius / 2.0 - 20, 0, 2 * math.pi) \end_layout \begin_layout LyX-Code context.stroke() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if __name__ == "__main__": \end_layout \begin_layout LyX-Code surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 300, 200) \end_layout \begin_layout LyX-Code context = cairo.Context(surface) \end_layout \begin_layout LyX-Code context.set_line_width(20) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code draw_circle(context, 75, 100) \end_layout \begin_layout LyX-Code context.set_antialias(cairo.ANTIALIAS_NONE) \end_layout \begin_layout LyX-Code draw_circle(context, 225, 100) \end_layout \begin_layout LyX-Code surface.write_to_png("cairo-antialias.png") \end_layout \begin_layout Standard To turn off antialias, the context set_antialias method must be given the cairo.ANTIALIAS_NONE type. To see what this looks like take a look at figure \begin_inset CommandInset ref LatexCommand ref reference "fig:Cairo - Antialias Example" \end_inset . \end_layout \begin_layout Subsubsection Antialias Types \end_layout \begin_layout Standard The four options available for antialias are: \end_layout \begin_layout Itemize cairo.ANTIALIAS_DEFAULT \end_layout \begin_layout Itemize cairo.ANTIALIAS_GRAY \end_layout \begin_layout Itemize cairo.ANTIALIAS_SUBPIXEL \end_layout \begin_layout Itemize cairo.ANTIALIAS_NONE \end_layout \begin_layout Subsection Context Methods \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sub:Cairo Context Methods" \end_inset \end_layout \begin_layout Description set_source_rgb(R,G,B) This allows setting the color value of the context \end_layout \begin_layout Description rel_curve_to(x1,y1,x2,y2,x3,y3) Create a curve instead of a straight line from the current position to x3 and y3, using x1/y1 and x2/y2 as control point. Where x1, x2, x3, y1, y2, y3 are relative to the current position. \end_layout \begin_layout Description curve_to(x1,y1,x2,y2,x3,y4) Create a curve instead of a straight line from the current position to x3 and y3, using x1/y1 and x2/y2 as control point \end_layout \begin_layout Description rel_line_to(x,y) Draw a line relative to the current position with an offset of x and of y \end_layout \begin_layout Description line_to(x,y) Draw a line from the current position to the new position \end_layout \begin_layout Description rel_mov_to(x,y) Move the position relative to the current position \end_layout \begin_layout Description move_to(x,y) Move by an absolute position \end_layout \begin_layout Description set_font_size(size) set the size of the font \end_layout \begin_layout Description arc Draw an arc \end_layout \begin_layout Description fill Color the path that as been set with rectangle or line_to with the color that has been set \end_layout \begin_layout Description rectangle(x1,y1,x2,y2) Draw a rectangle \end_layout \begin_layout Description set_antialias(type) Set the the type of antialias that is to be used \end_layout \begin_layout Description close_path Draw a line from the starting position since the last time stroke was called from the current position, thus closing the path \end_layout \begin_layout Section Cairo and PyGTK \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sec:Cairo - Cairo and PyGTK" \end_inset \end_layout \begin_layout Standard \begin_inset Float figure wide false sideways false status collapsed \begin_layout Plain Layout \begin_inset Graphics filename images/cairo/cairo-gtk-screenshot.png scale 70 \end_inset \end_layout \begin_layout Plain Layout \begin_inset Caption \begin_layout Plain Layout Custom PyGTK widget with Cairo \end_layout \end_inset \end_layout \begin_layout Plain Layout \begin_inset CommandInset label LatexCommand label name "fig:Cairo - PyGTK Cairo Custom Widget" \end_inset \end_layout \end_inset \end_layout \begin_layout Standard Cairo can be used with PyGTK by creating a custom widget. The custom widget discussed here will extend the gtk.DrawingArea class and override \begin_inset Foot status collapsed \begin_layout Plain Layout Take a look at \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.sicem.biz/personal/lgs/docs/docs/gobject-python/gobject-tutorial.html \end_layout \end_inset for a tutorial on creating custom properties and signals. Overriding signals is also covered. \end_layout \end_inset the expose_event \begin_inset Index status collapsed \begin_layout Plain Layout signals!expose \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset event \end_layout \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout expose \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset event \end_layout \end_inset signal callback method; \emph on do_expose_event \emph default \begin_inset Index status collapsed \begin_layout Plain Layout do \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset expose \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset event \end_layout \end_inset . \end_layout \begin_layout LyX-Code class CairoGtkOverride(gtk.DrawingArea): \end_layout \begin_layout LyX-Code __gsignals__ = {"expose_event": "override" } \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def __init__(self): \end_layout \begin_layout LyX-Code gtk.DrawingArea.__init__(self) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def do_expose_event(self, event): \end_layout \begin_layout LyX-Code context = self.window.cairo_create() \end_layout \begin_layout LyX-Code context.rectangle(event.area.x, event.area.y, \end_layout \begin_layout LyX-Code event.area.width, event.area.height) \end_layout \begin_layout LyX-Code context.clip() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.draw(context, *self.window.get_size()) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def draw(self, context, width, height): \end_layout \begin_layout LyX-Code context.set_source_rgb(0.5, 0.0, 0.0) \end_layout \begin_layout LyX-Code context.rectangle(0, 0, width, height) \end_layout \begin_layout LyX-Code context.fill() \end_layout \begin_layout Standard To properly override a signal in PyGTK set the class variable __gsignals__ to override the expose_event signal. In the __init__ method the class initiates its base class. \end_layout \begin_layout Standard The \emph on do_expose_event \emph default method is the callback for the expose_event signal. It sets up a cairo context, creates a rectangle to the size of the widget. It uses the event to retrieve the size that is needed; \emph on event.area.x \emph default and \emph on event.area.y \emph default are the starting x and y coordinates while \emph on event.area.width \emph default and \emph on event.area.height \emph default are the width and height of the widget. Then the widget is set to only draw to the size of the rectangle using \emph on context.clip() \emph default . The last part is to call the classes \emph on draw \emph default method on every expose event. \end_layout \begin_layout Standard The draw method takes as arguments a cairo context and a width and height of the widget. The draw method is where you can use cairo just as if it were not with PyGTK. The draw method in CairoGtkOverride draws a red rectangle. \end_layout \begin_layout Standard Now that a custom widget class has been created it can be extend as much as is wanted and the draw method overwritten to draw what is desired. \end_layout \begin_layout LyX-Code class Circle(CairoGtkOverride): \end_layout \begin_layout LyX-Code def draw(self, context, width, height): \end_layout \begin_layout LyX-Code context.set_source_rgb(1.0, 0.0, 0.0) \end_layout \begin_layout LyX-Code radius = min(width, height) \end_layout \begin_layout LyX-Code context.arc(width / 2.0, height / 2.0, \end_layout \begin_layout LyX-Code radius / 2.0 - 20, 0, 2 * math.pi) \end_layout \begin_layout LyX-Code context.stroke() \end_layout \begin_layout Standard The above code extend the gtk custom widget class that was created further up and draws a circle instead of a red rectangle. \end_layout \begin_layout Standard To run the code just add these widgets to your PyGTK application the same way you would any other widget. \end_layout \begin_layout LyX-Code if __name__ == "__main__": \end_layout \begin_layout LyX-Code win = gtk.Window() \end_layout \begin_layout LyX-Code win.connect("delete-event", gtk.main_quit) \end_layout \begin_layout LyX-Code vbox = gtk.VBox() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code override_widget = CairoGtkOverride() \end_layout \begin_layout LyX-Code circle_widget = Circle() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code vbox.pack_start(override_widget, True, True, 0) \end_layout \begin_layout LyX-Code vbox.pack_start(circle_widget, True, True, 0) \end_layout \begin_layout LyX-Code win.add(vbox) \end_layout \begin_layout LyX-Code win.show_all() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Section Summary \end_layout \begin_layout Standard For more examples on PyGTK and cairo you can take a look at the following resources: \end_layout \begin_layout Itemize \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://blog.eikke.com/index.php/ikke/2007/02/17/python_cairo_xshape_and_clocks \end_layout \end_inset \end_layout \begin_layout Itemize \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://ralph-glass.homepage.t-online.de/clock/readme.html \end_layout \end_inset \end_layout \begin_layout Itemize \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://ralph-glass.homepage.t-online.de/shogi/readme.html \end_layout \end_inset \end_layout \begin_layout Itemize \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.cairographics.org/pycairo/resources/ \end_layout \end_inset \end_layout \begin_layout Itemize \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.tortall.net/mu/wiki/CairoTutorial \end_layout \end_inset \end_layout \begin_layout Itemize \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.tortall.net/mu/wiki/PyGTKCairoTutorial \end_layout \end_inset \end_layout \begin_layout Itemize \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.pygtk.org/articles/cairo-pygtk-widgets/cairo-pygtk-widgets.htm \end_layout \end_inset \end_layout \begin_layout Itemize \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.pygtk.org/articles/cairo-pygtk-widgets/cairo-pygtk-widgets2.htm \end_layout \end_inset \end_layout \begin_layout Standard Remember, if you want to see what is available in your cairo install, use dir(cairo) from within python to see what is available. \end_layout \begin_layout LyX-Code import cairo \end_layout \begin_layout LyX-Code dir(cairo) \end_layout \begin_layout Chapter Printing \end_layout \begin_layout Standard \begin_inset Box Frameless position "t" hor_pos "c" has_inner_box 1 inner_pos "t" use_parbox 0 width "100col%" special "none" height "1in" height_special "totalheight" status open \begin_layout Plain Layout \begin_inset CommandInset include LatexCommand input filename "chapter-heading.lyx" \end_inset \end_layout \end_inset \end_layout \begin_layout Standard A requirement for printing with PyGTK is cairo so it will be helpful to read the chapter on cairo first. However it is only necessary if you wish to know what is going on. If all you want is quick and easy printing than this chapter by itself should suffice. \end_layout \begin_layout Standard Cairo is not only used for drawing pretty pictures. It can be used with PyGTK to print documents or whatever it is you wish to print. \end_layout \begin_layout Section Print Example \end_layout \begin_layout Standard This chapter will provide a simple python class that takes as arguments: \end_layout \begin_layout Itemize action - The action to be performed (see section \begin_inset CommandInset ref LatexCommand vref reference "sub:Cario - Print Actions" \end_inset ) \end_layout \begin_layout Itemize data - print the provided string \end_layout \begin_layout Itemize filename - open a text file to be printed \end_layout \begin_layout Standard To use the PrintExample class all you have to do is create an instance specifyin g some data to print and the type of print action that is to be taken. For example lets say that the text \begin_inset Quotes eld \end_inset This text is Printed \begin_inset Quotes erd \end_inset is to be printed with a print dialog being opened to the user, then the following code would be used. \end_layout \begin_layout LyX-Code printer = PrintExample(gtk.PRINT_OPERATION_ACTION_PRINT_DIALOG, \end_layout \begin_layout LyX-Code \begin_inset Quotes eld \end_inset This text is Printed \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout Standard Inside the __init__ method of the PrintExample class the paper size(see section \begin_inset CommandInset ref LatexCommand vref reference "sub:Cario - Paper Sizes" \end_inset ) is set, page setup information is created, and a print operation is initiated. \end_layout \begin_layout LyX-Code class PrintExample: \end_layout \begin_layout LyX-Code def __init__(self, action=None, data=None, filename=None): \end_layout \begin_layout LyX-Code self.text = data \end_layout \begin_layout LyX-Code self.layout = None \end_layout \begin_layout LyX-Code self.font_size=12 \end_layout \begin_layout LyX-Code self.lines_per_page=0 \end_layout \begin_layout LyX-Code if action==None: \end_layout \begin_layout LyX-Code action = gtk.PRINT_OPERATION_ACTION_PREVIEW \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code paper_size = gtk.PaperSize(gtk.PAPER_NAME_A4) \end_layout \begin_layout LyX-Code setup = gtk.PageSetup() \end_layout \begin_layout LyX-Code setup.set_paper_size(paper_size) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code print_ = gtk.PrintOperation() \end_layout \begin_layout LyX-Code print_.set_default_page_setup(setup) \end_layout \begin_layout LyX-Code print_.set_unit(gtk.UNIT_MM) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code print_.connect("begin_print", self.begin_print) \end_layout \begin_layout LyX-Code print_.connect("draw_page", self.draw_page) \end_layout \begin_layout LyX-Code if action == gtk.PRINT_OPERATION_ACTION_EXPORT: \end_layout \begin_layout LyX-Code print_.set_export_filename(filename) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code response = print_.run(action) \end_layout \begin_layout Standard So first off in the __init__ method a few instance variables are created. \end_layout \begin_layout Description self.text: Is used to hold the data that is to be printed \end_layout \begin_layout Description self.layout: Is used to hold an pango layout instance \end_layout \begin_layout Description self.font_size: Is used to hold the font size that will be use with the layout with a pango.FontDescription instance \end_layout \begin_layout Description self.lines_per_page: Is used to store how many lines are available per page \end_layout \begin_layout Standard Next it checks to see what action as been set. If there is no action it will set as default to show a print preview. \end_layout \begin_layout LyX-Code action = gtk.PRINT_OPERATION_ACTION_PREVIEW \end_layout \begin_layout Standard Next an instance of the gtk.PaperSize class is created with a paper type of gtk.PAPER_NAME_A4 and is assigned to the variable \emph on paper_size \emph default . After this an instance of gtk.PageSetup is created and has a page size set by the just created instance of gtk.PaperSize \emph on paper_size \emph default . \end_layout \begin_layout Standard The print operation instance is assigned to the variable print_ using the gtk.PrintOperation class. It uses the print setup created above and sets the unit size to millimeters. \end_layout \begin_layout LyX-Code print_.set_default_page_setup(setup) \end_layout \begin_layout LyX-Code print_.set_unit(gtk.UNIT_MM) \end_layout \begin_layout Standard It then connects the signals needed to print to their methods in the PrintExampl e class. The needed signals are \emph on begin_print \emph default and \emph on draw_page \emph default . The begin_print signal calls a method that sets up the needed information for the print operation. The \emph on draw_page \emph default signal calls a method that uses the the information from the \emph on begin_print \emph default method to print each individual page. \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code print_.connect("begin_print", self.begin_print) \end_layout \begin_layout LyX-Code print_.connect("draw_page", self.draw_page) \end_layout \begin_layout Standard Lastly, if the print action is to export it also sets the filename that it is to be exported. \end_layout \begin_layout Standard As stated above the begin_print method is called with the begin_print signal and will setup the information that is needed to print using the draw_page method. \end_layout \begin_layout LyX-Code def begin_print(self, operation, context): \end_layout \begin_layout LyX-Code width = context.get_width() \end_layout \begin_layout LyX-Code height = context.get_height() \end_layout \begin_layout LyX-Code self.layout = context.create_pango_layout() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.layout.set_font_description( \end_layout \begin_layout LyX-Code pango.FontDescription("Sans " + str(self.font_size)) ) \end_layout \begin_layout LyX-Code self.layout.set_width(int(width*pango.SCALE)) \end_layout \begin_layout LyX-Code self.layout.set_text(self.text) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code num_lines = self.layout.get_line_count() \end_layout \begin_layout LyX-Code self.lines_per_page = math.floor( \end_layout \begin_layout LyX-Code context.get_height() / (self.font_size/2) ) \end_layout \begin_layout LyX-Code pages = ( int(math.ceil( float(num_lines) / \end_layout \begin_layout LyX-Code float(self.lines_per_page) ) ) ) \end_layout \begin_layout LyX-Code operation.set_n_pages(pages) \end_layout \begin_layout Standard The begin_print method has the arguments \emph on operation \emph default and \emph on context \emph default . The operation argument will be used to set the number of pages. The context is used to get the information needed and create a pango layout. Pango is the part of gtk that is used for fonts and is needed for setting the font type, setting the width of the page and setting the text. \end_layout \begin_layout Standard The the first two lines retrieve the width and the height of the of the context argument (which is a cairo context). It then creates a pango instance using the context.create_pango_layout() method and assigns this to the class instance variable self.layout from this point out obviously become a pango.Layout instance. \end_layout \begin_layout Standard The next part now uses self.layout to set the font type to Sans 12. The self.font_size is set as a class instance variable in the __init__ method so that it can be used from both the begin_print and draw_page methods. It sets the self.layout with to the cairo \emph on context \emph default width multiplied by the pango.SCALE constant (1024). After this the text of the pango layout is then set to the text that is held in the variable self.text; which was set in the __init__ method. \end_layout \begin_layout Standard The number of lines in the whole document is retrieved with by calling self.layou t.get_line_count(). The number of lines per page is calculated using the context height and dividing by the font size. The font size is divided by two so the lines are not spaced to far apart \begin_inset Foot status collapsed \begin_layout Plain Layout There is a different way to do this but I found this the easiest way to start off with. \end_layout \end_inset \begin_inset Note Note status collapsed \begin_layout Plain Layout TODO: Add the second way to do the begin_print and draw_page methods using 1024(pango.SCALE) :) \end_layout \end_inset . \end_layout \begin_layout Standard The number pages is calculated by dividing the number of lines in the whole document by the number of lines per page. It then sets the number pages by calling the operation.set_n_pages method. \end_layout \begin_layout Standard The draw_page method is called directly after the begin_print method. It uses the information that was stored in class instance variables and in the operation argument to print each page. It also has the argument page_number. This holds the current page number that is being printed. Remember that the draw_page method is not called once, it is called once for each page that is to be printed. \end_layout \begin_layout LyX-Code def draw_page (self, operation, context, page_number): \end_layout \begin_layout LyX-Code cr = context.get_cairo_context() \end_layout \begin_layout LyX-Code cr.set_source_rgb(0, 0, 0) \end_layout \begin_layout LyX-Code start_line = page_number * self.lines_per_page \end_layout \begin_layout LyX-Code if page_number + 1 != operation.props.n_pages: \end_layout \begin_layout LyX-Code end_line = start_line + self.lines_per_page \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code end_line = self.layout.get_line_count() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code cr.move_to(0, 0) \end_layout \begin_layout LyX-Code iter = self.layout.get_iter() \end_layout \begin_layout LyX-Code i=0 \end_layout \begin_layout LyX-Code while 1: \end_layout \begin_layout LyX-Code if i > start_line: \end_layout \begin_layout LyX-Code line = iter.get_line() \end_layout \begin_layout LyX-Code cr.rel_move_to(0, self.font_size/2) \end_layout \begin_layout LyX-Code cr.show_layout_line(line) \end_layout \begin_layout LyX-Code i += 1 \end_layout \begin_layout LyX-Code if not (i < end_line and iter.next_line()): \end_layout \begin_layout LyX-Code break \end_layout \begin_layout Standard First off the draw_page method creates a cairo context by calling context.get_cai ro_context(). The context is assigned to cr. It then sets the color of the text to black using cr.set_source_rgb(0, 0, 0). After this the starting line for the current page to print is calculated by multiplying the current page by the number of lines per page. \end_layout \begin_layout Standard It then calculates the last line that is on the page. If it is not the last page of the document the last line is the start line plus the lines per page. If it is the last page to be printed the end line is the line count of the whole document. \end_layout \begin_layout LyX-Code if page_number + 1 != operation.props.n_pages: \end_layout \begin_layout LyX-Code end_line = start_line + self.lines_per_page \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code end_line = self.layout.get_line_count() \end_layout \begin_layout Standard With this information the method is now able to draw the text using cairo. The context is set to the upper most left part of the page using \emph on cr.move_to(0, 0) \emph default . \end_layout \begin_layout Standard It creates an iter of the layout that is used to iterate through each line of the document that is left. A while loop is used to move through the lines. Each time through the while loop the variable i is incremented. Once I is greater than the start line, that was calculated for this page, the line is retrieved using \emph on iter.get_line() \emph default . The context is moved relative to its current position by the font size divided by two. Then the text is drawn to the context using the cr.show_layout_line method. \end_layout \begin_layout Standard Once the variable is as incremented to a greater value then the end line, or there are no more lines in the iter to iterate through, break is called ending the while loop and exiting the draw_page method. \end_layout \begin_layout Section Print Actions \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sub:Cario - Print Actions" \end_inset \end_layout \begin_layout Standard There are several print actions that can be used with printing. \end_layout \begin_layout Description gtk.PRINT_OPERATION_ACTION_PREVIEW Show the print preview \end_layout \begin_layout Description gtk.PRINT_OPERATION_ACTION_EXPORT Export to a file. This requires the "export-filename" property to be set \end_layout \begin_layout Description gtk.PRINT_OPERATION_ACTION_PRINT_DIALOG Show the print dialog \end_layout \begin_layout Description gtk.PRINT_OPERATION_ACTION_PRINT Start printing immediately without showing the print dialog. Based on the current print settings. \end_layout \begin_layout Section Paper Sizes \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sub:Cario - Paper Sizes" \end_inset \end_layout \begin_layout Standard There are several different predefined paper sizes that can be used with PyGTK printing. These are listed below. There is also the possibility to use a custom paper size, but this is not discussed here. \end_layout \begin_layout Description gtk.PAPER_NAME_A3 Name for the A3 paper size. \end_layout \begin_layout Description gtk.PAPER_NAME_A4 Name for the A4 paper size. \end_layout \begin_layout Description gtk.PAPER_NAME_A5 Name for the A5 paper size. \end_layout \begin_layout Description gtk.PAPER_NAME_B5 Name for the B5 paper size. \end_layout \begin_layout Description gtk.PAPER_NAME_LETTER Name for the Letter paper size. \end_layout \begin_layout Description gtk.PAPER_NAME_EXECUTIVE Name for the Executive paper size. \end_layout \begin_layout Description gtk.PAPER_NAME_LEGAL for the Legal paper size. \end_layout \begin_layout Section Summary \end_layout \begin_layout Standard In summary, printing using cairo sucks but at least it is not to bad. \end_layout \begin_layout Chapter Gnome Desktop Integration \end_layout \begin_layout Standard \begin_inset Box Frameless position "t" hor_pos "c" has_inner_box 1 inner_pos "t" use_parbox 0 width "100col%" special "none" height "1in" height_special "totalheight" status open \begin_layout Plain Layout \begin_inset CommandInset include LatexCommand input filename "chapter-heading.lyx" \end_inset \end_layout \end_inset \end_layout \begin_layout Standard \begin_inset Branch SecondEdition status open \begin_layout Section Storing Passwords \end_layout \begin_layout Standard This section is not yet written :) \end_layout \begin_layout Standard keyring \end_layout \end_inset \end_layout \begin_layout Section GConfig \end_layout \begin_layout Standard Save your applications configuration file using GConfig. This example is based off the gconfig-basic-app.py file that comes with the pygtk source code, I have changed it into something I find easier to understand. \end_layout \begin_layout LyX-Code import gconf, gobject, gtk \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code class GConfigExample: \end_layout \begin_layout LyX-Code def __init__(self): \end_layout \begin_layout LyX-Code client = gconf.client_get_default() \end_layout \begin_layout LyX-Code client.add_dir ("/apps/pygtk-book-gconf-example-app", \end_layout \begin_layout LyX-Code gconf.CLIENT_PRELOAD_NONE) \end_layout \begin_layout Standard Before even creating the gtk window, get the default gconf client, then tell the gconf client that we are interested in the given directory. This means the gconf client will receive notification of changes to this directory, and will also cache keys under this directory. To avoid getting a copy of the whole gconf database do not add \begin_inset Quotes eld \end_inset / \begin_inset Quotes erd \end_inset as that would specify the entire database. Also gconf.CLIENT_PRELOAD_NONE is used to avoid loading all config keys on startup. If the application reads all the config keys on startup, then preloading the cache may make sense, otherwise preload none is the way to go. \end_layout \begin_layout Standard After setting up the initial gconf code the gtk window is created. \end_layout \begin_layout LyX-Code self.window = gtk.Window() \end_layout \begin_layout LyX-Code self.window.set_title( \begin_inset Quotes eld \end_inset GConfig Example \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code vbox = gtk.VBox(False, 5) \end_layout \begin_layout LyX-Code self.window.add(vbox) \end_layout \begin_layout LyX-Code \end_layout \begin_layout Standard Next, the program will have eight labels that will show the database directory path as well as the value that is being stored. The method create_configurable_widget is used to create, display, and hook up the labels to be updated on changes to the gconf database. \end_layout \begin_layout LyX-Code config = self.create_configurable_widget(client, \end_layout \begin_layout LyX-Code "/apps/pygtk-book-gconf-example-app/foo") \end_layout \begin_layout LyX-Code vbox.pack_start(config, True, True) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code config = self.create_configurable_widget(client, \end_layout \begin_layout LyX-Code "/apps/pygtk-book-gconf-example-app/bar") \end_layout \begin_layout LyX-Code vbox.pack_start(config, True, True) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code config = self.create_configurable_widget(client, \end_layout \begin_layout LyX-Code "/apps/pygtk-book-gconf-example-app/baz") \end_layout \begin_layout LyX-Code vbox.pack_start (config, True, True) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code config = self.create_configurable_widget(client, \end_layout \begin_layout LyX-Code "/apps/pygtk-book-gconf-example-app/blah") \end_layout \begin_layout LyX-Code vbox.pack_start(config, True, True) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.window.connect( \begin_inset Quotes eld \end_inset delete_event \begin_inset Quotes erd \end_inset , lambda wid, we: gtk.main_quit()) \end_layout \begin_layout LyX-Code \end_layout \begin_layout Standard Here we use the set_data method on the applications main window, setting the key to \begin_inset Quotes eld \end_inset client \begin_inset Quotes erd \end_inset and the value to the gconf object that was created abouve; \emph on client \emph default . As well a preferences button is created and added to the window. The preferences button will open a preference dialog that will edit the gconfig entries directly and does not interact at all with the GConfigExample class that shows reading from gconf. \end_layout \begin_layout LyX-Code self.window.set_data ( \begin_inset Quotes eld \end_inset client \begin_inset Quotes erd \end_inset , client) \end_layout \begin_layout LyX-Code prefs_button = gtk.Button ("Preferences") \end_layout \begin_layout LyX-Code vbox.pack_end (prefs_button, False, False) \end_layout \begin_layout LyX-Code prefs_button.connect ( \begin_inset Quotes eld \end_inset clicked \begin_inset Quotes erd \end_inset , self.prefs_button_clicked_callback) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.window.show_all() \end_layout \begin_layout Standard Once the widget monitoring notification that was created in the create_configura ble_widget method is destroyed, the notification callback is removed. \end_layout \begin_layout LyX-Code def configurable_widget_destroy_callback(self, widget): \end_layout \begin_layout LyX-Code client = widget.get_data( \begin_inset Quotes eld \end_inset client \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code notify_id = widget.get_data( \begin_inset Quotes eld \end_inset notify_id \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if notify_id: \end_layout \begin_layout LyX-Code client.notify_remove (notify_id) \end_layout \begin_layout LyX-Code \end_layout \begin_layout Standard Here there is a notification callback for the value label widgets that monitor the current value of a gconf key, when a gconf value is changed so is the label within the program. Note that the \emph on value \emph default can be None (unset) or it can have the wrong type. The program needs to check to make sure it can survive \emph on gconftool --break-key \emph default . \end_layout \begin_layout LyX-Code def configurable_widget_config_notify(self, client, cnxn_id, entry, label): \end_layout \begin_layout LyX-Code if not entry.value: \end_layout \begin_layout LyX-Code label.set_text( \begin_inset Quotes eld \end_inset \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code elif entry.value.type == gconf.VALUE_STRING: \end_layout \begin_layout LyX-Code label.set_text( entry.value.to_string() ) \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code label.set_text( \begin_inset Quotes eld \end_inset !type error! \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code \end_layout \begin_layout Standard This is the create_configurable_widget method that creates the labels that are displayed. Each gconf database directory will have a label to show the location as well as one label to show the value. \end_layout \begin_layout LyX-Code def create_configurable_widget(self, client, config_key): \end_layout \begin_layout LyX-Code hbox = gtk.HBox(True) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code key_label = gtk.Label(config_key + ": ") \end_layout \begin_layout LyX-Code label = gtk.Label ( \begin_inset Quotes eld \end_inset \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code hbox.pack_start(key_label) \end_layout \begin_layout LyX-Code hbox.pack_start(label) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code s = client.get_string(config_key) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if s: \end_layout \begin_layout LyX-Code label.set_text(s) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code notify_id = client.notify_add(config_key, self.configurable_widget_config_noti fy, label) \end_layout \begin_layout Standard It should be noted here that notify_id will be 0 if there is an error, so that is handled in the destroy callback. \end_layout \begin_layout LyX-Code label.set_data( \begin_inset Quotes eld \end_inset notify_id \begin_inset Quotes erd \end_inset , notify_id) \end_layout \begin_layout LyX-Code label.set_data( \begin_inset Quotes eld \end_inset client \begin_inset Quotes erd \end_inset , client) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code label.connect( \begin_inset Quotes eld \end_inset destroy \begin_inset Quotes erd \end_inset , self.configurable_widget_destroy_callback) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code return hbox \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def prefs_button_clicked_callback(self, widget): \end_layout \begin_layout LyX-Code client = self.window.get_data( \begin_inset Quotes eld \end_inset client \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code prefs_dialog = EditConfigValues(client) \end_layout \begin_layout Standard Next is the code for the preference dialog. the code will be in the EditConfigValues class. It is important to know that the preference dialog will never directly edit any values in the main window, it will only edit values in the gconf database. This is to test that the program works correctly as sometimes the values will be edited using gconf-editor instead of the applications preference window. \end_layout \begin_layout LyX-Code class EditConfigValues: \end_layout \begin_layout LyX-Code def __init__(self, client): \end_layout \begin_layout LyX-Code self.dialog = gtk.Dialog ("GConfig Example Preferences", None, 0, (gtk.STOCK_CLOSE, gtk.RESPONSE_ACCEPT)) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.dialog.connect('response', lambda wid,ev: wid.destroy ()) \end_layout \begin_layout LyX-Code self.dialog.set_default_response (gtk.RESPONSE_ACCEPT) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code vbox = gtk.VBox(False, 5) \end_layout \begin_layout Standard Create four labels and four text entries that are used to display the gconf location as well as the current value in an entry area, this is accomplished using the create_config_entry method. \end_layout \begin_layout LyX-Code self.dialog.vbox.pack_start(vbox) \end_layout \begin_layout LyX-Code entry = self.create_config_entry(client, \end_layout \begin_layout LyX-Code "/apps/pygtk-book-gconf-example-app/foo", True) \end_layout \begin_layout LyX-Code vbox.pack_start (entry, False, False) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code entry = self.create_config_entry(client, \end_layout \begin_layout LyX-Code "/apps/pygtk-book-gconf-example-app/bar") \end_layout \begin_layout LyX-Code vbox.pack_start (entry, False, False) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code entry = self.create_config_entry (client, \end_layout \begin_layout LyX-Code "/apps/pygtk-book-gconf-example-app/baz") \end_layout \begin_layout LyX-Code vbox.pack_start (entry, False, False) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code entry = self.create_config_entry (client, \end_layout \begin_layout LyX-Code "/apps/pygtk-book-gconf-example-app/blah") \end_layout \begin_layout LyX-Code vbox.pack_start (entry, False, False) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.dialog.show_all() \end_layout \begin_layout Standard The config_entry_commit method does as its names says and commits changes to the gconf database. If the \emph on text \emph default string is zero-length it is unset, otherwise it is set. \end_layout \begin_layout LyX-Code def config_entry_commit(self, entry, *args): \end_layout \begin_layout LyX-Code client = entry.get_data( \begin_inset Quotes eld \end_inset client \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code text = entry.get_chars(0, -1) \end_layout \begin_layout LyX-Code key = entry.get_data ('key') \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if text: \end_layout \begin_layout LyX-Code client.set_string(key, text) \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code client.unset(key) \end_layout \begin_layout LyX-Code \end_layout \begin_layout Standard The create_config_entry method takes as arguments the gconf client, the config key that is to be created, as well as whether the text entry has focus. This method creates a label that shows the config key and a text entry that shows the value. Editing the text entry changes the value of the gconf value. \end_layout \begin_layout LyX-Code def create_config_entry(self, client, config_key, focus=False): \end_layout \begin_layout LyX-Code hbox = gtk.HBox(False, 5) \end_layout \begin_layout LyX-Code label = gtk.Label(config_key) \end_layout \begin_layout LyX-Code entry = gtk.Entry() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code hbox.pack_start(label, False, False, 0) \end_layout \begin_layout LyX-Code hbox.pack_end(entry, False, False, 0) \end_layout \begin_layout LyX-Code \end_layout \begin_layout Standard Calling client.get_string(config_key) will print an error via the default error handler if the key is not set to a string. \end_layout \begin_layout LyX-Code s = client.get_string(config_key) \end_layout \begin_layout LyX-Code if s: \end_layout \begin_layout LyX-Code entry.set_text(s) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code entry.set_data( \begin_inset Quotes eld \end_inset client \begin_inset Quotes erd \end_inset , client) \end_layout \begin_layout LyX-Code entry.set_data( \begin_inset Quotes eld \end_inset key \begin_inset Quotes erd \end_inset , config_key) \end_layout \begin_layout Standard The changes will be commited if the user moves focus away from the text entry they are in, or if they hit enter; Changes are not commited on the \emph on changed \emph default signal as that would mean every new character entered would be sent, instead it waits for the user to finish first. Finally if the gconf client key is not writable the text entry is set to not writtable. \end_layout \begin_layout LyX-Code entry.connect( \begin_inset Quotes eld \end_inset focus_out_event \begin_inset Quotes erd \end_inset , self.config_entry_commit) \end_layout \begin_layout LyX-Code entry.connect ( \begin_inset Quotes eld \end_inset activate \begin_inset Quotes erd \end_inset , self.config_entry_commit) \end_layout \begin_layout LyX-Code entry.set_sensitive( client.key_is_writable(config_key) ) \end_layout \begin_layout LyX-Code if focus: \end_layout \begin_layout LyX-Code entry.grab_focus() \end_layout \begin_layout LyX-Code return hbox \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if __name__ == "__main__": \end_layout \begin_layout LyX-Code GConfigExample() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Section PyGobject \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sec:PyGObject" \end_inset \end_layout \begin_layout Standard I am not going to cover very much in this section because that would be a lot, maybe in a later verion. For now this section will cover one useful function. For more information check out its documentation at \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://pygtk.org/docs/pygobject/index.html \end_layout \end_inset . \end_layout \begin_layout Description gobject.timeout_add(interval,callback) is a function that will call the function specified in the callback as often as is specified by the interval until the callback function returns False. \end_layout \begin_layout List \labelwidthstring 00.00.0000 Interval The number of seconds between calls. Eg. 1 for one second, 100 for 100 seconds. That is pretty simple \end_layout \begin_layout List \labelwidthstring 00.00.0000 Callback The function that will be called at each interval. \end_layout \begin_layout Standard I find this is a useful function to use when I want to periodically check to see if a long running process has finished. Another good example is lets say you have a music player with a progress bar, once a second while a song is playing you would want to update the progress bar. To do this you could setup a \emph on gobject.timeout_add \emph default to call an update function that checks the position of the currently playing song and update the progress bar with that information. \end_layout \begin_layout Standard \begin_inset Branch SecondEdition status open \begin_layout Section Gnome Virtual Filesystems \end_layout \begin_layout Standard This section is not yet written :) \end_layout \begin_layout Standard The new GVFS/GIO stuff \end_layout \end_inset \end_layout \begin_layout Section Gnome Menus (.desktop files) \end_layout \begin_layout Standard If an application is to be added to the main menu it will need an appname.desktop \begin_inset Index status collapsed \begin_layout Plain Layout desktop \end_layout \end_inset file with details about the application. The .desktop file will hold various information about the application including the name, how to execute it, tool tip comment, icon, category and more. \end_layout \begin_layout Subsection Keys \end_layout \begin_layout Standard The way that a .desktop file holds information is with keys \begin_inset Index status collapsed \begin_layout Plain Layout desktop!keys \end_layout \end_inset . There are several keys and a few of them are required. The required keys are: \end_layout \begin_layout Itemize Type \begin_inset Index status collapsed \begin_layout Plain Layout desktop!Type \end_layout \end_inset - Application, Link, Directory \end_layout \begin_layout Itemize Name \begin_inset Index status collapsed \begin_layout Plain Layout desktop!Name \end_layout \end_inset - The name of the application and what will show up in the menu \end_layout \begin_layout Itemize Exec \begin_inset Index status collapsed \begin_layout Plain Layout desktop!Exec \end_layout \end_inset - The program to execute with arguments \end_layout \begin_layout Itemize URL \begin_inset Index status collapsed \begin_layout Plain Layout desktop!URL \end_layout \end_inset - Only required if the entry is a Link Type \end_layout \begin_layout Standard There are several other keys besides the required ones. To see what is available visit the .desktop files specification web page \begin_inset Foot status collapsed \begin_layout Plain Layout For more information on keys that can be used with .desktop files please visit: \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s05.html \end_layout \end_inset \end_layout \end_inset . \end_layout \begin_layout Standard .desktop Example \end_layout \begin_layout LyX-Code [Desktop Entry] \end_layout \begin_layout LyX-Code Version=1.0 \end_layout \begin_layout LyX-Code Encoding=UTF-8 \end_layout \begin_layout LyX-Code Name=Hello World \end_layout \begin_layout LyX-Code GenericName=Display Hello World \end_layout \begin_layout LyX-Code Comment=This is my first PyGTK application \end_layout \begin_layout LyX-Code X-MultipleArgs=false \end_layout \begin_layout LyX-Code Type=Application \end_layout \begin_layout LyX-Code TryExec=helloworld \end_layout \begin_layout LyX-Code Exec=helloworld \end_layout \begin_layout LyX-Code Categories=Utility \end_layout \begin_layout LyX-Code Icon=helloworld \end_layout \begin_layout Standard Save this example as helloworld.desktop. When it is viewed with a file manger it will show up as \begin_inset Quotes eld \end_inset Hello World \begin_inset Quotes erd \end_inset because that is what the Name key is set to. \end_layout \begin_layout Standard This Example sets up a .desktop with a version of 1.0. The encoding type is UTF-8. The GenericName is a generic name to describe the application. It is assigned to the Application type. It will try to execute helloworld. The category is Utility. The utility category means that it will be placed in the Accessories category in the menu. The comment is the tooltip for the that will be displayed on hovering over it. And last the icon is set to helloworld. \end_layout \begin_layout Standard When using Icons it must be set to the absolute path or be installed in a location that it is able to be found. This helloworld icon is a image with the name helloworld.png and can be found on the books website. Supported icon image types are png, xpm and svg. \end_layout \begin_layout Subsection Category Information \end_layout \begin_layout Standard Included in the keys that can be used with a .desktop file is the category key. The category is the category that the Application, Link, or Directory will be included under. If for example we have an Application and it is in the Office category; then when the main menu is opened and the office subcategory is opened the application will show up there. \end_layout \begin_layout Standard Here is a list of the default categories. More categories can be found on the menu specification web page \begin_inset Foot status collapsed \begin_layout Plain Layout If you would like more information on categories please visit: \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://standards.freedesktop.org/menu-spec/menu-spec-1.0.html \end_layout \end_inset \end_layout \end_inset . \end_layout \begin_layout Itemize AudioVideo - A multimedia (audio/video) application \end_layout \begin_layout Itemize Audio - An audio application Desktop entry must include AudioVideo as well \end_layout \begin_layout Itemize Video - A video application Desktop entry must include AudioVideo as well \end_layout \begin_layout Itemize Development - An application for development \end_layout \begin_layout Itemize Education - Educational software \end_layout \begin_layout Itemize Game - A game \end_layout \begin_layout Itemize Graphics - Graphical application \end_layout \begin_layout Itemize Network - Network application such as a web browser \end_layout \begin_layout Itemize Office - An office type application \end_layout \begin_layout Itemize Settings - Settings applications Entries may appear in a separate menu or as part of a "Control Center" \end_layout \begin_layout Itemize System - System application, "System Tools" such as say a log viewer or network monitor \end_layout \begin_layout Itemize Utility - Small utility application, "Accessories" \end_layout \begin_layout Subsection Installing and Using .desktop files \end_layout \begin_layout Standard Creating a .desktop file without installing \begin_inset Index status collapsed \begin_layout Plain Layout desktop!installing \end_layout \end_inset is pointless. It must be installed to be used. This section is going to use a small sample PyGTK application with a .desktop file to show how they work together. Then a small shell script will be created to install or uninstall the applicati on, application data, and related .desktop file. \end_layout \begin_layout Standard First lets create a small python program that is the main file to create the GUI and a second python file that will only have one function that returns a small message. These two files are used to show how it can be installed and set the path in the main python file to the correct install location of the supporting python modules that are included in the application \begin_inset Foot status collapsed \begin_layout Plain Layout A better way would probably be to install all the files to the library directory including the main python file. Then install a shell script to the binary directory that looks for and launches the directory. This way it does not need to append the to the system path the location of the applications python modules. \end_layout \end_inset . \end_layout \begin_layout LyX-Code #!/usr/bin/env python \end_layout \begin_layout LyX-Code import sys \end_layout \begin_layout LyX-Code sys.path.append("/usr/local/lib/helloworld") \end_layout \begin_layout LyX-Code import gtk \end_layout \begin_layout LyX-Code import helloworld_message \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if __name__ == '__main__': \end_layout \begin_layout LyX-Code win = gtk.Window() \end_layout \begin_layout LyX-Code win.connect("delete_event", lambda w,e: gtk.main_quit()) \end_layout \begin_layout LyX-Code label = gtk.Label(helloworld_message.message()) \end_layout \begin_layout LyX-Code win.add(label) \end_layout \begin_layout LyX-Code win.show_all() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Standard At the very top of this example sys is import and the location /usr/local/lib/he lloworld is append to the system path. The reason this is done is because this is where all the applications modules will be installed. If it does not append this directory then importing the helloworld_message module will fail. \end_layout \begin_layout Standard The helloworld_message.py file only contains one function and is only two lines long. \end_layout \begin_layout LyX-Code def message(): \end_layout \begin_layout LyX-Code return ".desktop example program" \end_layout \begin_layout Standard Now that there is a working application and a desktop file that was created above it is time to install everything. For the purposes of installing the helloworld.desktop, helloworld.py, and helloworld_message.py files a bash shell script will be used. \end_layout \begin_layout Standard The shell script will take one argument that may be either --install or --uninstall. Anything other then that will display how to use this shell script. This script has been kept very simple so that it will be easy to understand. \end_layout \begin_layout Standard To start off lets cover the beginning of the script. \end_layout \begin_layout LyX-Code #!/bin/bash \end_layout \begin_layout LyX-Code # Get script directory path. \end_layout \begin_layout LyX-Code scriptdir="`dirname ${0}`" \end_layout \begin_layout LyX-Code DESTDIR="${DESTDIR:-}" \end_layout \begin_layout Standard These first few lines set the shell script to be run by bash and set the variables \begin_inset Quotes eld \end_inset scriptdir \begin_inset Quotes erd \end_inset and \begin_inset Quotes eld \end_inset DESTDIR \begin_inset Quotes erd \end_inset . \end_layout \begin_layout Standard Next is the installation function. This function will install the main python file as a binary and the supporting python modules and data files. \end_layout \begin_layout LyX-Code install_program() # arg1=bindir, arg2=datadir, arg3=pkglibdir, \end_layout \begin_layout LyX-Code # arg4=pkgdatadir, arg5=pkgdocdir. \end_layout \begin_layout LyX-Code { \end_layout \begin_layout LyX-Code echo ${DESTDIR} \end_layout \begin_layout LyX-Code # Install binary data - /usr/local/bin/helloworld \end_layout \begin_layout LyX-Code install -m 755 -d "${DESTDIR}${1}" \end_layout \begin_layout LyX-Code install -m 755 "${scriptdir}/helloworld.py" "${DESTDIR}${1}/helloworld" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Install package library - /usr/local/lib/helloworld \end_layout \begin_layout LyX-Code install -m 755 -d "${DESTDIR}${3}" \end_layout \begin_layout LyX-Code install "${scriptdir}"/helloworld_*.py "${DESTDIR}${3}/" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Install package data /usr/local/share/helloworld \end_layout \begin_layout LyX-Code #install -m 755 -d "${DESTDIR}${4}" \end_layout \begin_layout LyX-Code #install -m 644 "${scriptdir}/helloworld.png" "${DESTDIR}${4}/" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Install data directory - /usr/local/share/pixmaps \end_layout \begin_layout LyX-Code install -m 755 -d "${DESTDIR}${2}/pixmaps" \end_layout \begin_layout LyX-Code install -m 644 "${scriptdir}/helloworld.png" "${DESTDIR}${2}/pixmaps/" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # /usr/local/share/applications \end_layout \begin_layout LyX-Code install -m 755 -d "${DESTDIR}${2}/applications" \end_layout \begin_layout LyX-Code install -m 644 "${scriptdir}/helloworld.desktop" \backslash \end_layout \begin_layout LyX-Code "${DESTDIR}${2}/applications/" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code echo "Finished Install" \end_layout \begin_layout LyX-Code } \end_layout \begin_layout Standard This function takes five arguments that specifiy where the binary, data, library, package data, and documentation are to be installed. It installs the helloworld.py file to /usr/local/bin/helloworld so it may be run by executing helloworld. It then install all python files that start with \begin_inset Quotes eld \end_inset helloworld_ \begin_inset Quotes erd \end_inset to the /usr/local/lib/helloworld directory. If there were any data files they would be installed to /usr/local/share/hellow orld directory, but since there were none those lines are commented out(they are only using the helloworld.png file as an example). \end_layout \begin_layout Standard The helloworld.png file is installed to the /usr/local/share/pixmaps directory, making it usable as an icon from the helloworld.desktop file. And at the very last, helloworld.desktop is installed to the /usr/local/share/ap plications directory. Once this is completed the the helloworld application should show up in the menu (Applications -> Accessories -> Hello World). \end_layout \begin_layout Standard The next and last function in the install script is used to uninstall the helloworld application and is much smaller then the install function. \end_layout \begin_layout LyX-Code uninstall_program() # arg1=bindir, arg2=datadir, arg3=pkglibdir, \end_layout \begin_layout LyX-Code # arg4=pkgdatadir, arg5=pkgdocdir. \end_layout \begin_layout LyX-Code { \end_layout \begin_layout LyX-Code rm -f "${DESTDIR}${1}/helloworld" \end_layout \begin_layout LyX-Code rm -f "${DESTDIR}${1}/helloworld.py" \end_layout \begin_layout LyX-Code rm -rf "${DESTDIR}${3}" \end_layout \begin_layout LyX-Code rm -rf "${DESTDIR}${4}" \end_layout \begin_layout LyX-Code rm -rf "${DESTDIR}${5}" \end_layout \begin_layout LyX-Code rm -f "${DESTDIR}${2}/pixmaps/helloworld.png" \end_layout \begin_layout LyX-Code rm -f "${DESTDIR}${2}/applications/helloworld.desktop" \end_layout \begin_layout LyX-Code echo "Finished Uninstall" \end_layout \begin_layout LyX-Code } \end_layout \begin_layout Standard The uninstall function deletes all the files that were installed and all the directories that were created by the install function. This is very simple and there is no more to say about it. \end_layout \begin_layout Standard The last part is to read the arguments given to the shell script and call the right function. \end_layout \begin_layout LyX-Code # First arg to the script \end_layout \begin_layout LyX-Code action=$1 \end_layout \begin_layout LyX-Code if test "$action" = --install \end_layout \begin_layout LyX-Code then \end_layout \begin_layout LyX-Code echo "install selected" \end_layout \begin_layout LyX-Code install_program "/usr/local/bin" \backslash \end_layout \begin_layout LyX-Code "/usr/local/share" \backslash \end_layout \begin_layout LyX-Code "/usr/local/lib/helloworld" \backslash \end_layout \begin_layout LyX-Code "/usr/local/share/helloworld" \backslash \end_layout \begin_layout LyX-Code "/usr/local/share/doc/helloworld" \end_layout \begin_layout LyX-Code elif test "$action" = --uninstall \end_layout \begin_layout LyX-Code then \end_layout \begin_layout LyX-Code echo "uninstall selected" \end_layout \begin_layout LyX-Code uninstall_program "/usr/local/bin" \backslash \end_layout \begin_layout LyX-Code "/usr/local/share" \backslash \end_layout \begin_layout LyX-Code "/usr/local/lib/helloworld" \backslash \end_layout \begin_layout LyX-Code "/usr/local/share/helloworld" \backslash \end_layout \begin_layout LyX-Code "/usr/local/share/doc/helloworld" \end_layout \begin_layout LyX-Code else \end_layout \begin_layout LyX-Code echo "" \end_layout \begin_layout LyX-Code echo "Usage:" \end_layout \begin_layout LyX-Code echo " --install - Use this argument to install" \end_layout \begin_layout LyX-Code echo " --uninstall - Use this argument to uninstall" \end_layout \begin_layout LyX-Code echo "" \end_layout \begin_layout LyX-Code fi \end_layout \begin_layout Standard This part of the install script reads the first argument to it and assigns it to the variable action. Then action is tested to see if it should install, uninstall, or display the accepted arguments. \end_layout \begin_layout Standard That is all to creating a .desktop file for use with an application. \end_layout \begin_layout Standard \begin_inset Branch SecondEdition status open \begin_layout Section Mimetypes \end_layout \begin_layout Standard This section is not yet written :) \end_layout \begin_layout Standard mimetypes and adding mimetypes \end_layout \end_inset \end_layout \begin_layout Standard \begin_inset Branch SecondEdition status open \begin_layout Section CD and DVD Burning \end_layout \begin_layout Standard This section is not yet written :) \end_layout \begin_layout Standard nautilus-burn \end_layout \end_inset \end_layout \begin_layout Standard \end_layout \begin_layout Chapter Audio and Video Playback - GStreamer \end_layout \begin_layout Standard \begin_inset Box Frameless position "t" hor_pos "c" has_inner_box 1 inner_pos "t" use_parbox 0 width "100col%" special "none" height "1in" height_special "totalheight" status open \begin_layout Plain Layout \begin_inset CommandInset include LatexCommand input filename "chapter-heading.lyx" \end_inset \end_layout \end_inset \end_layout \begin_layout Section Introduction \end_layout \begin_layout Standard GStreamer is a multimedia framework that can be used from the simple to the more advanced. The possibilities range from playing a simple audio file or video file to creating an advanced audio/video editor. \end_layout \begin_layout Standard When you are finished reading this chapter you will be able to use a high level playbin factory element to play audio and video, detect missing codecs and automatically install them, discover the file information about your audio or video files and apply all these to your very own PyGTK program. \end_layout \begin_layout Standard This chapter does not cover any advanced topics but it does show you how to very quickly add the ability to play audio or video to your own program. \end_layout \begin_layout Standard Enjoy the journey. \end_layout \begin_layout Section The Beginnings \end_layout \begin_layout Subsection Playbin \end_layout \begin_layout Standard The playbin \begin_inset Index status collapsed \begin_layout Plain Layout playbin \end_layout \end_inset element is a very high level, automatic video/audio player. It will automatically detect your multimedia file type and take the correct actions for it to be played. All that needs to be done to use it is to supply the playbin with a location of a multimedia file and set the state of it to play. \end_layout \begin_layout Standard \series bold playbin Features: \end_layout \begin_layout Itemize Audio and video output ( \begin_inset Quotes eld \end_inset audio-sink \begin_inset Quotes erd \end_inset and \begin_inset Quotes eld \end_inset video-sink \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout Itemize Error Handling \end_layout \begin_layout Itemize EOS handling(end of stream) \end_layout \begin_layout Itemize State handling \end_layout \begin_layout Itemize Seeking \end_layout \begin_layout Itemize Buffers network sources \end_layout \begin_layout Itemize Visualization for audio supported \end_layout \begin_layout Itemize Subtitle support ( \begin_inset Quotes eld \end_inset suburi \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout Standard \series bold Playbin element: \end_layout \begin_layout Standard What is needed in every GStreamer application is an element to play your media with. In the case of this chapter all that we are going to use is the \begin_inset Quotes eld \end_inset playbin \begin_inset Quotes erd \end_inset factory. You create this using the gst.element_make_factory(factory, element_name) \begin_inset Index status collapsed \begin_layout Plain Layout GStreamer!element \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset make \begin_inset ERT status collapsed \begin_layout Plain Layout \backslash _ \end_layout \end_inset factory \end_layout \end_inset method like so: \end_layout \begin_layout LyX-Code player_name = gst.element_make_factory( \begin_inset Quotes eld \end_inset playbin \begin_inset Quotes erd \end_inset , \begin_inset Quotes eld \end_inset YourElementName \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout Standard \series bold Set location of the multimedia file: \end_layout \begin_layout Standard Setting the location of the file is done with the player_name.set_property( \begin_inset Quotes eld \end_inset uri \begin_inset Quotes erd \end_inset , \begin_inset Quotes eld \end_inset location \begin_inset Quotes erd \end_inset ) like so: \end_layout \begin_layout LyX-Code player_name.set_property( \begin_inset Quotes eld \end_inset uri \begin_inset Quotes erd \end_inset , \begin_inset Quotes eld \end_inset file:///home/peter/myvideo.avi \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout Standard \series bold Set state of the multimedia file: \end_layout \begin_layout Standard Some states that the multimedia file may be set to include: \end_layout \begin_layout Itemize gst.STATE_PLAYING -- Used to start playing \end_layout \begin_layout Itemize gst.STATE_PAUSED -- Used to pause file \end_layout \begin_layout Itemize gst.STATE_NULL -- Used to stop file \end_layout \begin_layout Standard To set the state of the \begin_inset Quotes eld \end_inset player_name \begin_inset Quotes erd \end_inset just created above to play the file you would set_state(state) method like so: \end_layout \begin_layout LyX-Code player_name.set_state(gst.STATE_PLAYING) \end_layout \begin_layout Standard To set the file to be paused you would: \end_layout \begin_layout LyX-Code player_name.set_state(gst.STATE_PAUSED) \end_layout \begin_layout Standard To altogether stop the file that is playing you would set the state like so: \end_layout \begin_layout LyX-Code player_name.set_state(gst.STATE_NULL) \end_layout \begin_layout Subsection Bus - watching for GStreamer signals \end_layout \begin_layout Standard The GStreamer \begin_inset Index status collapsed \begin_layout Plain Layout GStreamer!bus \end_layout \end_inset bus \begin_inset Index status collapsed \begin_layout Plain Layout bus \end_layout \end_inset is what allows for receiving signals from GStreamer. It is important because it will allow your program to detect things such as errors or the end of the audio or video stream. \end_layout \begin_layout Standard When the end of stream is detected it is the programs responsibility to set the state back to gst.STATE_NULL. Otherwise if you try to load in another file or play the same file again it will not play because the state is already set to gst.STATE_PLAYING. \end_layout \begin_layout Standard The bus is not difficult to use and it will only add a few more lines to the program and one extra function to handle the messages. \end_layout \begin_layout Standard So if we have created a player bin using the gst.element_make_factory method and have called it \emph on player_name \emph default then we can create a bus and watch it like so: \end_layout \begin_layout LyX-Code bus = player_name.get_bus() \end_layout \begin_layout LyX-Code bus.add_signal_watch() \end_layout \begin_layout LyX-Code bus.connect( \begin_inset Quotes eld \end_inset message \begin_inset Quotes erd \end_inset , on_message) \end_layout \begin_layout Standard This creates a bus from the player_name playbin, adds a signal watcher, and connects the bus to send signals to the function on_message when messages are detected. \end_layout \begin_layout Standard The function message can detect whatever type of message that GStreamer has but in the examples in this chapter it will focus on errors and detecting when the end of stream has occurred so that the program will reset the state to gst.STATE_NULL. \end_layout \begin_layout Standard An example message function looks like this: \end_layout \begin_layout LyX-Code def on_message(self, bus, message): \end_layout \begin_layout LyX-Code # Detect end of stream and set state to to NULL \end_layout \begin_layout LyX-Code if message.type == gst.MESSAGE_EOS: \end_layout \begin_layout LyX-Code self.player_name.set_state(gst.STATE_NULL) \end_layout \begin_layout LyX-Code elif message.type == gst.MESSAGE_ERROR: \end_layout \begin_layout LyX-Code self.player_name.set_state(gst.STATE_NULL) \end_layout \begin_layout LyX-Code (err, debug) = message.parse_error() \end_layout \begin_layout LyX-Code print \begin_inset Quotes eld \end_inset Error: %s \begin_inset Quotes erd \end_inset % err, debug \end_layout \begin_layout Section Playing Audio \end_layout \begin_layout Standard Playing audio with PyGST is a very simple matter that only requires a few lines of code to get the audio playing. \end_layout \begin_layout Standard As the was just covered we will use the gst.element_make_factory function and set it up with a PyGTK GUI. But besides that we will create a false video sink using the gst.element_make_fa ctory function so that if the multimedia file is a video, only the audio portion is played. This is because we are using the high level \begin_inset Quotes eld \end_inset playbin \begin_inset Quotes erd \end_inset element which will automatically play everything. So if all you want played is the audio, the video must be redirected. \end_layout \begin_layout Standard Create a multimedia playbin to play the audio and redirect all video to a fake video sink that is added to the multimedia pipeline: \end_layout \begin_layout LyX-Code # Create the player_name sink \end_layout \begin_layout LyX-Code player_name = gst.element_make_factory( \begin_inset Quotes eld \end_inset playbin \begin_inset Quotes erd \end_inset , \begin_inset Quotes eld \end_inset Multimedia Player \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code # Create the fake video sink \end_layout \begin_layout LyX-Code fake_video_sink = gst.element_make_factory( \begin_inset Quotes eld \end_inset fakesink \begin_inset Quotes erd \end_inset , \begin_inset Quotes eld \end_inset Fake sink for Videos \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code #Add the fake video sink to the player \end_layout \begin_layout LyX-Code player_name.set_property( \begin_inset Quotes eld \end_inset videosink \begin_inset Quotes erd \end_inset , fake_video_sink) \end_layout \begin_layout Standard If a fake video sink is not created and a video file is played ,it will pop up a window with the video playing in it. This will really subtract from the professional feel of your application. \end_layout \begin_layout Standard Now what is needed is to add the audio source using the player_name.set_property method and set the state to playing. This is just like what is discussed earlier and is now shown below: \end_layout \begin_layout LyX-Code player_name.set_property( \begin_inset Quotes eld \end_inset uri \begin_inset Quotes erd \end_inset , \begin_inset Quotes eld \end_inset file:///home/peter/mymusic.mp3 \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code player_name.set_state(gst.PLAYING) \end_layout \begin_layout Standard It really is that simple. \end_layout \begin_layout Standard For a full example of how to hook up audio and video to a PyGTK application please review the example at the end of the chapter. \end_layout \begin_layout Section Playing Video \end_layout \begin_layout Standard Playing audio is very simple and is much like playing audio except that with playing video there is no fake video sink created to hide the video. \end_layout \begin_layout Standard To play video a playbin must be created using the gst.element_make_factory function. Then set the location of the video file with the newly created playbin and then set the playbin state to gst.PLAYING. \end_layout \begin_layout LyX-Code # Create the player_name sink \end_layout \begin_layout LyX-Code player_name = gst.element_make_factory( \begin_inset Quotes eld \end_inset playbin \begin_inset Quotes erd \end_inset , \begin_inset Quotes eld \end_inset Multimedia Player \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code # Set the location of the video file \end_layout \begin_layout LyX-Code player_name.set_property( \begin_inset Quotes eld \end_inset uri \begin_inset Quotes erd \end_inset , \begin_inset Quotes eld \end_inset file:///home/peter/myvideo.avi \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code # Start playing the video. \end_layout \begin_layout LyX-Code player_name.set_state(gst.PLAYING) \end_layout \begin_layout Standard It really is much shorter to play a video then it is an audio file. But remember that you should also hook up a bus, as shown in the section \begin_inset Quotes eld \end_inset bus -- watching for GStreamer signals \begin_inset Quotes erd \end_inset , to your video to catch messages. However there is a problem with this code. \end_layout \begin_layout Standard If you play a video with this code the video will open up in its own window. If the video opening in its own window is good enough for your program so be it; however I believe that for most programs the video will be better suited in a widget inside of the application. \end_layout \begin_layout Subsection \series bold Play the Video in you Application \end_layout \begin_layout Standard To play a video file in your own application you use a gtk.DrawingArea widget to play the video. You create a gtk.DrawingArea and sync it with the video using the bus that has been created to watch for GStreamer messages. \end_layout \begin_layout Standard Create your gtk.DrawingArea like so: \end_layout \begin_layout LyX-Code self.videowidget = gtk.DrawingArea() \end_layout \begin_layout LyX-Code self.videowidget.set_size_request(400, 250) \end_layout \begin_layout Standard Then add this widget to your PyGTK window. \end_layout \begin_layout Standard Now you sync the video to your videowidget using the bus. If your bus name is \emph on bus \emph default you would enable sync messages and connect it to a function with the following code: \end_layout \begin_layout LyX-Code bus.enable_sync_message_emission() \end_layout \begin_layout LyX-Code bus.connect( \begin_inset Quotes eld \end_inset sync-message::element \begin_inset Quotes erd \end_inset , self.on_sync_message) \end_layout \begin_layout Standard This code enables the sync message and then connects any signals to be forwarded to the \emph on self.on_sync_message \emph default function. The on_sync_message function will hook the video up to the gtk.DrawingArea widget that has been created to show the video. \end_layout \begin_layout Standard Here is an example function showing how to play a video. \end_layout \begin_layout LyX-Code def on_sync_message(self, bus, message): \end_layout \begin_layout LyX-Code if message.structure is None: \end_layout \begin_layout LyX-Code return False \end_layout \begin_layout LyX-Code if message.structure.get_name() == \begin_inset Quotes eld \end_inset prepare-xwindow-id \begin_inset Quotes erd \end_inset : \end_layout \begin_layout LyX-Code if sys.platform == \begin_inset Quotes eld \end_inset win32 \begin_inset Quotes erd \end_inset : \end_layout \begin_layout LyX-Code win_id = self.videowidget.window.handle \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code win_id = self.videowidget.window.xid \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code assert win_id \end_layout \begin_layout LyX-Code imagesink = message.src \end_layout \begin_layout LyX-Code imagesink.set_property( \begin_inset Quotes eld \end_inset force-aspect-ratio \begin_inset Quotes erd \end_inset , True) \end_layout \begin_layout LyX-Code imagesink.set_xwindow_id(win_id) \end_layout \begin_layout Standard Now when the state of your playbin element is set to play using gst.PLAYING, the video will be played inside of your application instead of opening up in its own window. \end_layout \begin_layout Standard For a full example of how to hook up audio and video to a PyGTK application please review the example at the end of the chapter. \end_layout \begin_layout Subsection Play Video Example \end_layout \begin_layout Standard This example will be referred to in following sections and when adding things such as seeking and will be expanded upon in the file example at the end of the chapter. \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "Simple Video Player Example" \end_inset \end_layout \begin_layout LyX-Code #!/usr/bin/env python \end_layout \begin_layout LyX-Code import pygst \end_layout \begin_layout LyX-Code pygst.require("0.10") \end_layout \begin_layout LyX-Code import gst, pygtk, gtk \end_layout \begin_layout LyX-Code import sys \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code class Main(object): \end_layout \begin_layout LyX-Code def __init__(self): \end_layout \begin_layout LyX-Code self.multimedia_file="" \end_layout \begin_layout LyX-Code # Create the GUI \end_layout \begin_layout LyX-Code self.win = gtk.Window() \end_layout \begin_layout LyX-Code self.win.set_title("Play Video Example") \end_layout \begin_layout LyX-Code self.win.connect("delete_event", \end_layout \begin_layout LyX-Code lambda w,e: gtk.main_quit()) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code vbox = gtk.VBox(False, 0) \end_layout \begin_layout LyX-Code hbox = gtk.HBox(False, 0) \end_layout \begin_layout LyX-Code self.load_file = \end_layout \begin_layout LyX-Code gtk.FileChooserButton("Choose Audio File") \end_layout \begin_layout LyX-Code self.play_button = \end_layout \begin_layout LyX-Code gtk.Button("Play", gtk.STOCK_MEDIA_PLAY) \end_layout \begin_layout LyX-Code self.pause_button = \end_layout \begin_layout LyX-Code gtk.Button("Pause", gtk.STOCK_MEDIA_PAUSE) \end_layout \begin_layout LyX-Code self.stop_button = \end_layout \begin_layout LyX-Code gtk.Button("Stop", gtk.STOCK_MEDIA_STOP) \end_layout \begin_layout LyX-Code self.videowidget = gtk.DrawingArea() \end_layout \begin_layout LyX-Code # You want to expand the video widget or \end_layout \begin_layout LyX-Code # else you cannot see it \end_layout \begin_layout LyX-Code self.videowidget.set_size_request(400, 250) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.load_file.connect("selection-changed", \end_layout \begin_layout LyX-Code self.on_file_selected) \end_layout \begin_layout LyX-Code self.play_button.connect("clicked", self.on_play_clicked) \end_layout \begin_layout LyX-Code self.pause_button.connect("clicked", self.on_pause_clicked) \end_layout \begin_layout LyX-Code self.stop_button.connect("clicked", self.on_stop_clicked) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code hbox.pack_start(self.play_button, False, True, 0) \end_layout \begin_layout LyX-Code hbox.pack_start(self.pause_button, False, True, 0) \end_layout \begin_layout LyX-Code hbox.pack_start(self.stop_button, False, True, 0) \end_layout \begin_layout LyX-Code vbox.pack_start(self.load_file, False, True, 0) \end_layout \begin_layout LyX-Code vbox.pack_start(self.videowidget, True, True, 0) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code vbox.pack_start(hbox, False, True, 0) \end_layout \begin_layout LyX-Code self.win.add(vbox) \end_layout \begin_layout LyX-Code self.win.show_all() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Setup GStreamer \end_layout \begin_layout LyX-Code self.player = gst.element_factory_make( \end_layout \begin_layout LyX-Code "playbin", "MultimediaPlayer") \end_layout \begin_layout LyX-Code bus = self.player.get_bus() \end_layout \begin_layout LyX-Code bus.add_signal_watch() \end_layout \begin_layout LyX-Code bus.enable_sync_message_emission() \end_layout \begin_layout LyX-Code #used to get messages that GStreamer emits \end_layout \begin_layout LyX-Code bus.connect("message", self.on_message) \end_layout \begin_layout LyX-Code #used for connecting video to your application \end_layout \begin_layout LyX-Code bus.connect("sync-message::element", \end_layout \begin_layout LyX-Code self.on_sync_message) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_file_selected(self, widget): \end_layout \begin_layout LyX-Code self.multimedia_file = self.load_file.get_filename() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_play_clicked(self, widget): \end_layout \begin_layout LyX-Code self.player.set_property('uri', \end_layout \begin_layout LyX-Code "file://" + self.multimedia_file) \end_layout \begin_layout LyX-Code self.player.set_state(gst.STATE_PLAYING) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_pause_clicked(self, widget): \end_layout \begin_layout LyX-Code self.player.set_state(gst.STATE_PAUSED) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_stop_clicked(self, widget): \end_layout \begin_layout LyX-Code self.player.set_state(gst.STATE_NULL) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_message(self, bus, message): \end_layout \begin_layout LyX-Code if message.type == gst.MESSAGE_EOS: \end_layout \begin_layout LyX-Code # End of Stream \end_layout \begin_layout LyX-Code self.player.set_state(gst.STATE_NULL) \end_layout \begin_layout LyX-Code elif message.type == gst.MESSAGE_ERROR: \end_layout \begin_layout LyX-Code self.player.set_state(gst.STATE_NULL) \end_layout \begin_layout LyX-Code (err, debug) = message.parse_error() \end_layout \begin_layout LyX-Code print "Error: %s" % err, debug \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_sync_message(self, bus, message): \end_layout \begin_layout LyX-Code if message.structure is None: \end_layout \begin_layout LyX-Code return False \end_layout \begin_layout LyX-Code if message.structure.get_name() == "prepare-xwindow-id": \end_layout \begin_layout LyX-Code if sys.platform == \begin_inset Quotes eld \end_inset win32 \begin_inset Quotes erd \end_inset : \end_layout \begin_layout LyX-Code win_id = self.videowidget.window.handle \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code win_id = self.videowidget.window.xid \end_layout \begin_layout LyX-Code assert win_id \end_layout \begin_layout LyX-Code imagesink = message.src \end_layout \begin_layout LyX-Code imagesink.set_property("force-aspect-ratio", True) \end_layout \begin_layout LyX-Code imagesink.set_xwindow_id(win_id) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if __name__ == "__main__": \end_layout \begin_layout LyX-Code Main() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Section Multimedia Info \end_layout \begin_layout Standard Now what I should mention here is that this code is more or less the unmodified example that comes with the PyGST source code. Copyright (C) 2006 Andy Wingo, LGPL Version 2. \end_layout \begin_layout Standard Lets say that you are going to play a video and you want to know some informatio n about it. Maybe you want to know what the video width and height is to set a proper size on your video widget. Or maybe you want to know the length of the video. Well this information is very easy to find out using GStreamer. \end_layout \begin_layout Standard First you will have to import the GStreamer discoverer like so: \end_layout \begin_layout LyX-Code from gst.extend import discoverer \end_layout \begin_layout Standard Now that you have the discoverer imported you can access information about the video file with only a few lines of code. \end_layout \begin_layout Standard We create a discover function that will be the main work area that hooks everything together. \end_layout \begin_layout Standard The discover functions includes an in-line function that is connected to using a gobject main loop since this is a command line example. If this code is used in a PyGTK GUI it will will run fine without the gobject main loop because it is already running in the applications GTK main loop. \end_layout \begin_layout Standard If the the file is discovered to be a multimedia file it is then sent to the succeed function where it prints out information about the file. \end_layout \begin_layout Standard If it fails and is not recognized as a multimedia file then it prints out an error message and exits the gobject main loop. \end_layout \begin_layout LyX-Code def discover(path): \end_layout \begin_layout LyX-Code def discovered(d, is_media): \end_layout \begin_layout LyX-Code if is_media: \end_layout \begin_layout LyX-Code succeed(d) \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code print \begin_inset Quotes eld \end_inset error: %r does not appear to be a media file \begin_inset Quotes erd \end_inset % path \end_layout \begin_layout LyX-Code # Exit the gobject main loop \end_layout \begin_layout LyX-Code # Remove this in a pygtk program. \end_layout \begin_layout LyX-Code sys.exit(1) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code d = discoverer.Discoverer(path) \end_layout \begin_layout LyX-Code # Connect discovered to the inline function discovered. \end_layout \begin_layout LyX-Code d.connect( \begin_inset Quotes eld \end_inset discovered \begin_inset Quotes erd \end_inset , discovered) \end_layout \begin_layout LyX-Code d.discover() \end_layout \begin_layout LyX-Code # comment out the gobject.MainLoop.run() in a pygtk program. \end_layout \begin_layout LyX-Code gobject.MainLoop().run() \end_layout \begin_layout Standard The succeed method is called from the discover function when the file is detected as a multimedia file. It can be used to print out or save information about the video or audio file. \end_layout \begin_layout Standard Data available for video files include: \end_layout \begin_layout Itemize is_video \end_layout \begin_layout Itemize video_length \end_layout \begin_layout Itemize fps - videorate.num / videorate.denom \end_layout \begin_layout Itemize videocaps \end_layout \begin_layout Itemize videowidth \end_layout \begin_layout Itemize videoheight \end_layout \begin_layout Standard Data available for audio files include: \end_layout \begin_layout Itemize is_audio \end_layout \begin_layout Itemize audiocaps \end_layout \begin_layout Itemize audiofloat \end_layout \begin_layout Itemize audiorate \end_layout \begin_layout Itemize audiowidth \end_layout \begin_layout Itemize audiodepth \end_layout \begin_layout Itemize audiolength \end_layout \begin_layout Itemize audiochannels \end_layout \begin_layout LyX-Code def succeed(d): \end_layout \begin_layout LyX-Code print( \begin_inset Quotes eld \end_inset media type \begin_inset Quotes erd \end_inset , d.mimetype) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code print( \begin_inset Quotes eld \end_inset has video \begin_inset Quotes erd \end_inset , d.is_video) \end_layout \begin_layout LyX-Code if d.is_video: \end_layout \begin_layout LyX-Code print( \begin_inset Quotes eld \end_inset video length (ms) \begin_inset Quotes erd \end_inset , d.videolength / gst.MSECOND) \end_layout \begin_layout LyX-Code print( \begin_inset Quotes eld \end_inset framerate (fps) \begin_inset Quotes erd \end_inset , \begin_inset Quotes eld \end_inset %s/%s \begin_inset Quotes erd \end_inset % (d.videorate.num, d.videorate.denom)) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code print( \begin_inset Quotes eld \end_inset has audio \begin_inset Quotes erd \end_inset , d.is_audio) \end_layout \begin_layout LyX-Code if d.is_audio: \end_layout \begin_layout LyX-Code print( \begin_inset Quotes eld \end_inset audio caps \begin_inset Quotes erd \end_inset , d.audiocaps) \end_layout \begin_layout LyX-Code print( \begin_inset Quotes eld \end_inset audio format \begin_inset Quotes erd \end_inset , d.audiofloat and \begin_inset Quotes eld \end_inset floating-point \begin_inset Quotes erd \end_inset or \begin_inset Quotes eld \end_inset integer \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code print( \begin_inset Quotes eld \end_inset audio length (ms) \begin_inset Quotes erd \end_inset , d.audiolength / gst.MSECOND) \end_layout \begin_layout LyX-Code # Exit gobject main loop. \end_layout \begin_layout LyX-Code sys.exit(0) \end_layout \begin_layout Standard All that is left is to run the discover file with a path to a multimedia file specified. To read in the file location and do the proper handling of it you could use the following code: \end_layout \begin_layout LyX-Code if __name__ == \begin_inset Quotes eld \end_inset __main__ \begin_inset Quotes erd \end_inset : \end_layout \begin_layout LyX-Code if len(sys.argv) != 2: \end_layout \begin_layout LyX-Code print >> sys.stderr, \begin_inset Quotes eld \end_inset usage: script_name.py PATH-TO-MEDIA-FILE \begin_inset Quotes erd \end_inset \end_layout \begin_layout LyX-Code sys.exit(1) \end_layout \begin_layout LyX-Code path = sys.argv.pop() \end_layout \begin_layout LyX-Code if not os.path.isfile(path): \end_layout \begin_layout LyX-Code print >> sys.stderr, \begin_inset Quotes eld \end_inset error: file %r does not exist \begin_inset Quotes erd \end_inset % path \end_layout \begin_layout LyX-Code print >> sys.stderr, \begin_inset Quotes eld \end_inset usage: gst-discover PATH-TO-MEDIA-FILE \begin_inset Quotes erd \end_inset \end_layout \begin_layout LyX-Code sys.exit(1) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code discover(path) \end_layout \begin_layout Standard For a full example of how to retrieve information from an audio or video file from a PyGTK application, please review MediaInfo class in the example at the end of the chapter. Also you can check out the PyGST examples on this books website. \end_layout \begin_layout Section Codec Buddy - Auto install multimedia Codecs \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sec: Gst - Codec Buddy" \end_inset \end_layout \begin_layout Standard Tested with Ubuntu 8.10 \end_layout \begin_layout Standard Taking advantage of the gst.pbutils allows a program to automatically install available codecs or provide the user of the program a choice of actions to take. \end_layout \begin_layout LyX-Code #!/usr/bin/python \end_layout \begin_layout LyX-Code import pygst, gst, pygtk, gtk \end_layout \begin_layout LyX-Code pygst.require("0.10") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code class InstallMissingCodecExample(object): \end_layout \begin_layout LyX-Code def __init__(self): \end_layout \begin_layout LyX-Code # Gtk Gui \end_layout \begin_layout LyX-Code self.win = gtk.Window() \end_layout \begin_layout LyX-Code self.win.set_title("Install Missing Codec Example") \end_layout \begin_layout LyX-Code self.win.connect("delete_event", lambda w,e: gtk.main_quit()) \end_layout \begin_layout LyX-Code self.load_file = gtk.FileChooserButton("Choose Audio File") \end_layout \begin_layout LyX-Code self.load_file.connect("selection-changed", self.on_file_selected) \end_layout \begin_layout LyX-Code self.win.add(self.load_file) \end_layout \begin_layout LyX-Code self.win.show_all() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Setup GStreamer \end_layout \begin_layout LyX-Code self.player = gst.element_factory_make("playbin", \end_layout \begin_layout LyX-Code "MultimediaPlayer") \end_layout \begin_layout LyX-Code bus = self.player.get_bus() \end_layout \begin_layout LyX-Code bus.add_signal_watch() \end_layout \begin_layout LyX-Code bus.connect("message", self.on_message) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_file_selected(self, widget): \end_layout \begin_layout LyX-Code print "Selected: ", self.load_file.get_filename() \end_layout \begin_layout LyX-Code multimedia_file = self.load_file.get_filename() \end_layout \begin_layout LyX-Code self.player.set_property('uri', "file://" + multimedia_file) \end_layout \begin_layout LyX-Code self.play() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def play(self): \end_layout \begin_layout LyX-Code self.player.set_state(gst.STATE_PLAYING) \end_layout \begin_layout LyX-Code # Codec Buddy Methods \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_message(self, bus, message): \end_layout \begin_layout LyX-Code import gst \end_layout \begin_layout LyX-Code if message.type == gst.MESSAGE_ERROR: \end_layout \begin_layout LyX-Code self.player.set_state(gst.STATE_NULL) \end_layout \begin_layout LyX-Code (err, debug) = message.parse_error() \end_layout \begin_layout LyX-Code print "Error: %s" % err, debug \end_layout \begin_layout LyX-Code elif message.type == gst.MESSAGE_EOS: \end_layout \begin_layout LyX-Code # End of Stream \end_layout \begin_layout LyX-Code self.player.set_state(gst.STATE_NULL) \end_layout \begin_layout LyX-Code elif message.type == gst.MESSAGE_ELEMENT: \end_layout \begin_layout LyX-Code """ CodicBuddy Stuff """ \end_layout \begin_layout LyX-Code st = message.structure \end_layout \begin_layout LyX-Code if st and st.get_name().startswith('missing-'): \end_layout \begin_layout LyX-Code self.player.set_state(gst.STATE_NULL) \end_layout \begin_layout LyX-Code if gst.pygst_version >= (0, 10, 10): \end_layout \begin_layout LyX-Code import gst.pbutils \end_layout \begin_layout LyX-Code detail = gst.pbutils.missing_plugin_message_get_installer_detail(message ) context = gst.pbutils.InstallPluginsContext() \end_layout \begin_layout LyX-Code gst.pbutils.install_plugins_async([detail], \end_layout \begin_layout LyX-Code context, self.install_plugin) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def install_plugin(self, result): \end_layout \begin_layout LyX-Code if result == gst.pbutils.INSTALL_PLUGINS_SUCCESS: \end_layout \begin_layout LyX-Code gst.update_registry() \end_layout \begin_layout LyX-Code self.play() \end_layout \begin_layout LyX-Code return \end_layout \begin_layout LyX-Code if result == gst.pbutils.INSTALL_PLUGINS_USER_ABORT: \end_layout \begin_layout LyX-Code dialog = gtk.MessageDialog(parent=None, \end_layout \begin_layout LyX-Code flags=gtk.DIALOG_MODAL, type=gtk.MESSAGE_INFO, \end_layout \begin_layout LyX-Code buttons=gtk.BUTTONS_OK, message_format= \end_layout \begin_layout LyX-Code "Plugin installation aborted.") \end_layout \begin_layout LyX-Code dialog.run() \end_layout \begin_layout LyX-Code dialog.hide() \end_layout \begin_layout LyX-Code return \end_layout \begin_layout LyX-Code error.show("Error", "failed to install plugins: %s" % \end_layout \begin_layout LyX-Code str(result)) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if __name__ == "__main__": \end_layout \begin_layout LyX-Code InstallMissingCodecExample() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Section Seeking - Basic Position Seeking \end_layout \begin_layout Standard Seeking allows multimedia software to display the position in the audio or video stream and also may allow the user to skip to a different section of the media file they are watching or listening to. \end_layout \begin_layout Subsection Displaying the Current Position \end_layout \begin_layout Standard When playing a media file it may be a good idea to display the current position relative to the duration of the file as a courtesy to the user. \end_layout \begin_layout Standard Displaying the duration of the file and current time position will require adding two methods to the play video ( \begin_inset CommandInset ref LatexCommand vref reference "Simple Video Player Example" \end_inset ) example found earlier in the chapter. \end_layout \begin_layout Standard In the __init__ method of the Main class the variables time_format, duration and is_playing are added. \end_layout \begin_layout LyX-Code self.time_format = gst.Format(gst.FORMAT_TIME) \end_layout \begin_layout LyX-Code self.duration = None \end_layout \begin_layout LyX-Code self.is_playing = False \end_layout \begin_layout Standard The time_format will be used when seeking the the duration of the media file and seeking the current position of the file. The duration will be a string to display the length of the media file. the is_playing variable will be used to let the methods that are going to soon be added know if the player is playing or not. \end_layout \begin_layout Standard The is_playing variable must be set to False anytime the file is not playing. This includes the end of stream message in the on_message method and when the pause and stop buttons are clicked. \end_layout \begin_layout Standard Further down in the __init__ method a label called time_label is added and that is it for the GUI changes to display the time. \end_layout \begin_layout LyX-Code self.time_label = gtk.Label( \begin_inset Quotes eld \end_inset 00:00 / 00:00 \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout Standard Going through the different methods, besides setting is_playing to False in the on_stop_clicked and on_pause_clicked methods, in the on_play_clicked it must be set to True. But after setting it to playing by clicking the play button the application must be able to update the GUI with the new current position every second. \end_layout \begin_layout Standard To update the GUI every second the on_play_clicked button adds the following: \end_layout \begin_layout LyX-Code timer = gobject.timeout_add(1000, self.update_time_label) \end_layout \begin_layout Standard This will create a timer that is executed every one second calling the method update_time_label. It will execute every one second as long a update_time_label returns true. \end_layout \begin_layout Standard Skipping down to below the on_sync_message method there is the new function update_time_label. \end_layout \begin_layout LyX-Code def update_time_label(self): \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code Update the time_label to display the current location \end_layout \begin_layout LyX-Code in the media file as well as update the seek bar \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code if self.is_playing == False: \end_layout \begin_layout LyX-Code print "return false" \end_layout \begin_layout LyX-Code return False \end_layout \begin_layout LyX-Code print "update_time_label" \end_layout \begin_layout LyX-Code if self.duration == None: \end_layout \begin_layout LyX-Code try: \end_layout \begin_layout LyX-Code self.length = self.player.query_duration(self.time_format, None)[0] \end_layout \begin_layout LyX-Code self.duration = self.convert_time(self.length) \end_layout \begin_layout LyX-Code except: \end_layout \begin_layout LyX-Code self.duration = None \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if self.duration != None: \end_layout \begin_layout LyX-Code self.current_position = self.player.query_position(self.time_format, None)[0] \end_layout \begin_layout LyX-Code current_position_formated = self.convert_time(self.current_position) \end_layout \begin_layout LyX-Code self.time_label.set_text(current_position_formated + "/" + self.duration) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Update the seek bar \end_layout \begin_layout LyX-Code # gtk.Adjustment(value=0, lower=0, upper=0, step_incr=0, page_incr=0, page_size=0) \end_layout \begin_layout LyX-Code percent = (float(self.current_position)/float(self.length))*100.0 \end_layout \begin_layout LyX-Code adjustment = gtk.Adjustment(percent, 0.00, 100.0, 0.1, 1.0, 1.0) self.seeker.s et_adjustment(adjustment) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code return True \end_layout \begin_layout Standard If the \emph on is_playing \emph default variable is set to False the \emph on update_time_label \emph default method will return False. This method starts to be called when the play button is called and is called every one second until it returns False. \end_layout \begin_layout Standard If the duration of the file has not yet been set it will be set here. The duration is found in nanoseconds and is converted to a string by passing it into the convert_time method. The duration variable will be reset to None each time a new media file is added to be played. If the duration is not None then it never set again unless a new file is loaded. \end_layout \begin_layout Standard After duration of the file is found the \emph on current_position \emph default variable is calculated every time the \emph on update_time_label \emph default is called. The current position is found the same was as the duration except that the \emph on query_position \emph default function is used. Then the time in nanoseconds is converted to a person understandable string. \end_layout \begin_layout Standard Once the duration and the current position is found it is displayed to the user by setting the text of the time_label like so: \end_layout \begin_layout LyX-Code self.time_label.set_text(current_position_formated + \begin_inset Quotes eld \end_inset / \begin_inset Quotes erd \end_inset + self.duration) \end_layout \begin_layout Standard As was just discussed above, the convert_time function is used to convert the time of the media file from nanoseconds to human readable string. This code is adapted from a tutorial \begin_inset Foot status collapsed \begin_layout Plain Layout This is licensed under the LGPL Version 3 and can be found at: \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://pygstdocs.berlios.de/pygst-tutorial/seeking.html \end_layout \end_inset \end_layout \end_inset found on the PyGST documentation site. It takes the time in nanoseconds and converts it to human readable string in the format of HH::MM::SS and then returns it. \end_layout \begin_layout LyX-Code def convert_time(self, time=None): \end_layout \begin_layout LyX-Code # convert_ns function from: \end_layout \begin_layout LyX-Code # http://pygstdocs.berlios.de/pygst-tutorial/seeking.html \end_layout \begin_layout LyX-Code # LGPL Version 3 - Copyright: Jens Persson \end_layout \begin_layout LyX-Code if time==None: \end_layout \begin_layout LyX-Code return None \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code hours = 0 \end_layout \begin_layout LyX-Code minutes = 0 \end_layout \begin_layout LyX-Code seconds = 0 \end_layout \begin_layout LyX-Code time_string = "" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code time = time / 1000000000 # gst.NSECOND \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if time >= 3600: \end_layout \begin_layout LyX-Code hours = time / 3600 \end_layout \begin_layout LyX-Code time = time - (hours * 3600) \end_layout \begin_layout LyX-Code if time >= 60: \end_layout \begin_layout LyX-Code minutes = time / 60 \end_layout \begin_layout LyX-Code time = time - (minutes * 60) \end_layout \begin_layout LyX-Code #remaining time is seconds \end_layout \begin_layout LyX-Code seconds = time \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code time_string = time_string + str(hours).zfill(2) + ":" + \end_layout \begin_layout LyX-Code str(minutes).zfill(2) + ":" + str(seconds).zfill(2) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code #return time in Hours:Minutes:Seconds format \end_layout \begin_layout LyX-Code return time_string \end_layout \begin_layout Standard If the time passed in is None it immediately returns None. The method divides the passed in time by 1 000 000 000 and then proceeds to calculate the hours, minutes, and seconds; creating a nice string HH:MM:SS to view by a human. \end_layout \begin_layout Standard When the time_string has been completed it is returned to be used by the user interface. \end_layout \begin_layout Subsection Seeking a New Position \end_layout \begin_layout Standard Like displaying the position and duration, seeking a new position in a media file will use the methods convert_time as well as update_time_label, but it will also use a horizontal scaler to that it will let the user slide to a new position. \begin_inset Note Note status open \begin_layout Plain Layout TODO: Sometime fix the code so the scaler updates as the video is playing. In other words the scaler should do more then just allow the user to seek a new position. \end_layout \end_inset \end_layout \begin_layout Standard To allow a user to update the position of the media file a horizontal scaler needs to be added. To use a scaler you must create an adjustment first. \end_layout \begin_layout LyX-Code self.adjustment = gtk.Adjustment(0.0, 0.00, 100.0, 0.1, 1.0, 1.0) \end_layout \begin_layout Standard This creates a new adjustment with a starting value of 0, lower limit of 0.00, upper limit of 100.0, and step increment of 0.1, page increment of 1.0, and page size of 1.0. The adjustment is used with the the horizontal scaler that is to be created to control the place in the media file. \end_layout \begin_layout LyX-Code self.seeker = gtk.HScale(self.adjustment) \end_layout \begin_layout LyX-Code self.seeker.set_draw_value(False) \end_layout \begin_layout LyX-Code self.seeker.set_update_policy(gtk.UPDATE_DISCONTINUOUS) \end_layout \begin_layout Standard The first line creates the seeker and the set_draw_value(False) line keeps the format-value signal from being admitted and the value of the current position is not displayed. \end_layout \begin_layout Standard On the third line the seeker is set to update in a discontinuous way. What this means is that it will only be updated when a button-release-event signal is emitted. \end_layout \begin_layout Standard The new method being added for seeking is seeker_button_release_event and the signals is connected like this: \end_layout \begin_layout LyX-Code self.seeker.connect( \begin_inset Quotes eld \end_inset button-release-event \begin_inset Quotes erd \end_inset , self.seeker_button_release_event) \end_layout \begin_layout Standard When the scaler button is released this method is called and the media file is set to a new position. \end_layout \begin_layout LyX-Code def seeker_button_release_event(self, widget, event): \end_layout \begin_layout LyX-Code print "seeker_button_release_event" \end_layout \begin_layout LyX-Code value = widget.get_value() \end_layout \begin_layout LyX-Code if self.is_playing == True: \end_layout \begin_layout LyX-Code duration = self.player.query_duration(self.time_format, None)[0] \end_layout \begin_layout LyX-Code time = value * (duration / 100) \end_layout \begin_layout LyX-Code print self.convert_time(time) \end_layout \begin_layout LyX-Code self.player.seek_simple(self.time_format, gst.SEEK_FLAG_FLUSH, time) \end_layout \begin_layout Standard When the self.seeker is released, its current position is retrieved and the position to reposition the media file is calculated. \end_layout \begin_layout LyX-Code time = value * (duration / 100) \end_layout \begin_layout Standard After the new position is determined, the media file is set to it using the seek_simple function. The seek_simple function takes a GStreamer time format, a seek flag, and the new time \begin_inset Foot status open \begin_layout Plain Layout \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/html/GstElement. html \end_layout \end_inset \end_layout \end_inset , returning True if it succeeds. \end_layout \begin_layout Section Volume Control \end_layout \begin_layout Standard Adding the option to control the volume of audio or video from individual programs is accomplished using the gtk.VolumeButton. \end_layout \begin_layout Standard A volume button is created like any other widget in PyGTK. \end_layout \begin_layout LyX-Code volume_button = gtk.VolumeButton() \end_layout \begin_layout Standard Then hook the volume button up to the value-changed signal with a method to control the volume. \end_layout \begin_layout LyX-Code volume_button.connect( \begin_inset Quotes eld \end_inset value-changed \begin_inset Quotes erd \end_inset , self.on_volume_changed) \end_layout \begin_layout Standard The last piece to complete is to create the method that is being used to increase and decrease the volume. \end_layout \begin_layout LyX-Code def on_file_selected(self, widget, value=0.5) \end_layout \begin_layout LyX-Code self.player.set_property( \begin_inset Quotes eld \end_inset volume \begin_inset Quotes erd \end_inset , float(value)) \end_layout \begin_layout LyX-Code return True \end_layout \begin_layout Standard What this method does is to control the volume and increase by the percent raised on the volume slider. The default value is 0.5 if it is not specified. \end_layout \begin_layout Subsection Volume Control Example \end_layout \begin_layout LyX-Code class Main(object): \end_layout \begin_layout LyX-Code def __init__(self): \end_layout \begin_layout LyX-Code self.win = gtk.Window() \end_layout \begin_layout LyX-Code self.win.set_title("Volume Control Example") \end_layout \begin_layout LyX-Code self.win.set_default_size(200, -1) \end_layout \begin_layout LyX-Code self.win.connect("delete_event", lambda w,e: gtk.main_quit()) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code hbox = gtk.HBox(False, 0) \end_layout \begin_layout LyX-Code self.load_file = gtk.FileChooserButton("Choose Audio File") \backslash \end_layout \begin_layout LyX-Code self.load_file.connect("selection-changed", self.on_file_selected) \end_layout \begin_layout LyX-Code volume_button = gtk.VolumeButton() \end_layout \begin_layout LyX-Code volume_button.connect("value-changed", self.on_volume_changed) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code hbox.pack_start(self.load_file, True, True, 0) \end_layout \begin_layout LyX-Code hbox.pack_start(volume_button, False, True, 0) \end_layout \begin_layout LyX-Code self.win.add(hbox) \end_layout \begin_layout LyX-Code self.win.show_all() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.player = gst.element_factory_make("playbin", "MultimediaPlayer") \end_layout \begin_layout LyX-Code bus = self.player.get_bus() \end_layout \begin_layout LyX-Code bus.add_signal_watch() \end_layout \begin_layout LyX-Code bus.enable_sync_message_emission() \end_layout \begin_layout LyX-Code bus.connect("message", self.on_message) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_file_selected(self, widget): \end_layout \begin_layout LyX-Code self.player.set_property("uri", "file://" + self.load_file.get_filename()) \end_layout \begin_layout LyX-Code self.player.set_state(gst.STATE_PLAYING) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_volume_changed(self, widget, value=10): \end_layout \begin_layout LyX-Code self.player.set_property("volume", float(value)) \end_layout \begin_layout LyX-Code return True \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_message(self, bus, message): \end_layout \begin_layout LyX-Code if message.type == gst.MESSAGE_EOS: \end_layout \begin_layout LyX-Code self.player.set_state(gst.STATE_NULL) \end_layout \begin_layout LyX-Code elif message.type == gst.MESSAGE_ERROR: \end_layout \begin_layout LyX-Code self.player.set_state(gst.STATE_NULL) \end_layout \begin_layout LyX-Code (err, debug) = message.parse_error() \end_layout \begin_layout LyX-Code print "Error: %s" % err, debug \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if __name__ == "__main__": \end_layout \begin_layout LyX-Code Main() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Section Example \end_layout \begin_layout Standard The purpose of this section is to provide a full media example that includes a user interface written with PyGTK. The application will be able to play, pause, or stop audio and video. Also the program will be able to discover information about the media file such as its length, width and height, and audio format using the MediaInfo class. \end_layout \begin_layout Standard Using the information found with the MediaInfo class the user interface will be able to resize its video display to match the width and height of the video. \end_layout \begin_layout Standard The GstPlayer class will wrap the GStreamer functions to make it easy to separate the multimedia and user interface functionality. The user interface will use a separate class called VideoWidget to display the video in also to make it easier to reuse. \end_layout \begin_layout Subsection MediaInfo Class \end_layout \begin_layout Standard The MediaInfo class is very similar to the media info section covered earlier in the chapter. What the MediaInfo class does here is to create an easy to use wrapper around the GStreamer discoverer functions, providing accessor methods to the data. \end_layout \begin_layout Standard Though there are many different pieces of information that can be discovered about a media file, for this example it will be kept short. For more information on the different variables that can be used to access the information please refer to the \shape italic multimedia info \shape default section earlier in this chapter. \end_layout \begin_layout Standard The only information that is of interest at the moment is if the media file is audio or video. If it is a video file, the width and the video height is of interest. \begin_inset space \space{} \end_inset If seeking is involved then the length of the file is also of interest, but this is not covered in this example. \end_layout \begin_layout LyX-Code class MediaInfo: \end_layout \begin_layout LyX-Code def __init__(self, path): \end_layout \begin_layout LyX-Code def discovered(d, is_media): \end_layout \begin_layout LyX-Code if is_media: \end_layout \begin_layout LyX-Code self.succeed(d) \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code self.fail(path) \end_layout \begin_layout LyX-Code self.__finished = False \end_layout \begin_layout LyX-Code self.__is_media=False \end_layout \begin_layout LyX-Code self.__video_width = 0 \end_layout \begin_layout LyX-Code self.__video_height = 0 \end_layout \begin_layout LyX-Code self.__is_video = False \end_layout \begin_layout LyX-Code self.__is_audio = False \end_layout \begin_layout LyX-Code self.__video_length = 0.0 \end_layout \begin_layout LyX-Code self.__frame_rate = \begin_inset Quotes eld \end_inset \begin_inset Quotes erd \end_inset \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.__is_fullscreen = False \end_layout \begin_layout LyX-Code print \begin_inset Quotes eld \end_inset path: \begin_inset Quotes eld \end_inset , path \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code d = discoverer.Discoverer(path) \end_layout \begin_layout LyX-Code #print help(d.discover) \end_layout \begin_layout LyX-Code d.connect( \begin_inset Quotes eld \end_inset discovered \begin_inset Quotes erd \end_inset , discovered) \end_layout \begin_layout LyX-Code d.discover() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def fail(self, path): \end_layout \begin_layout LyX-Code print \begin_inset Quotes eld \end_inset error: %r does not appear to be a media file \begin_inset Quotes erd \end_inset % path \end_layout \begin_layout LyX-Code self.__is_media = False \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def succeed(self, d): \end_layout \begin_layout LyX-Code print \begin_inset Quotes eld \end_inset File discover success \begin_inset Quotes erd \end_inset \end_layout \begin_layout LyX-Code self.__is_media = True \end_layout \begin_layout LyX-Code self.__mimetype = d.mimetype \end_layout \begin_layout LyX-Code self.__is_video = d.is_video \end_layout \begin_layout LyX-Code if self.__is_video: \end_layout \begin_layout LyX-Code self.__video_width = d.videowidth \end_layout \begin_layout LyX-Code self.__video_height = d.videoheight \end_layout \begin_layout LyX-Code # Retrieve the video length in minute \end_layout \begin_layout LyX-Code self.__video_length = ((d.videolength / gst.MSECOND) / 1000) / 60 \end_layout \begin_layout LyX-Code self.__frame_rate = \begin_inset Quotes eld \end_inset %s/%s \begin_inset Quotes erd \end_inset % (d.videorate.num, d.videorate.denom) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.__finished = True \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def poll(self): \end_layout \begin_layout LyX-Code return self.__finished \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def is_media(self): \end_layout \begin_layout LyX-Code return self.__is_media \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def is_video(self): \end_layout \begin_layout LyX-Code return self.__is_video \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def is_audio(self): \end_layout \begin_layout LyX-Code return self.__is_audio \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def get_width(self): \end_layout \begin_layout LyX-Code return self.__video_width \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def get_height(self): \end_layout \begin_layout LyX-Code return self.__video_height \end_layout \begin_layout Standard The MediaInfo class starts off by creating an inline function called discovered which is called by connecting the \shape italic discovered \shape default signal, near the end of the init function, to the \shape italic discovered \shape default function. The init function also creates sever class instance variables that is used to store information about the media file. \end_layout \begin_layout Standard If the file loaded is detected as being a media file it is sent to the method \shape italic succeed \shape default . If the file is not a media file the fail method is called, an error message is printed to the console, and the class variable self.__is_media is set to false indicating the file is not a media file. \end_layout \begin_layout Standard If the succeed method is called it will set the class variable self.__is_media to true. It will then set the mime type using the variable self.__mimetype. It will check to see if the media file is an audio file or a video file. It will set self.__is_video to true if it is a video file. \end_layout \begin_layout Standard The height and width of a video file is stored respectively in the variables self.__video_height and self.__video_width. \end_layout \begin_layout Standard Then there are a few methods to retrieve these variables that are of interest to the programmer. The methods return the variables that are obviously in their names. get_height() returns self.__video_height and so on with the other methods. \end_layout \begin_layout Standard The only other method that is included is the \shape italic poll \shape default method. Since the discovery of media information is not instantaneous, if you attempt to use any of the get methods to retrieve information such as the height or width of a video, it may return the initialization values of the variables which is zero. \end_layout \begin_layout Standard The \shape italic poll \shape default method will return True when the \shape italic succeed \shape default method has finished, indicating that all the variables have been assigned. \end_layout \begin_layout Standard If the poll method returns False you must wait to use the information provided by the MediaInfo class. \end_layout \begin_layout Subsection GstPlayer Class \end_layout \begin_layout Standard The GstPlayer class is used to control the GStreamer instance, watch the bus, handle GStreamer errors and messages, and sync the GStreamer video to video widget created with the VideoWidget class below. \end_layout \begin_layout LyX-Code class GstPlayer(object): \end_layout \begin_layout LyX-Code def __init__(self, videowidget): \end_layout \begin_layout LyX-Code # Setup GStreamer \end_layout \begin_layout LyX-Code self.videowidget = videowidget \end_layout \begin_layout LyX-Code self.player = gst.element_factory_make( \begin_inset Quotes eld \end_inset playbin \begin_inset Quotes erd \end_inset , \begin_inset Quotes eld \end_inset MultimediaPlayer \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code bus = self.player.get_bus() \end_layout \begin_layout LyX-Code bus.add_signal_watch() \end_layout \begin_layout LyX-Code bus.enable_sync_message_emission() \end_layout \begin_layout LyX-Code #used to get messages that gstreamer emits \end_layout \begin_layout LyX-Code bus.connect( \begin_inset Quotes eld \end_inset message \begin_inset Quotes erd \end_inset , self.on_message) \end_layout \begin_layout LyX-Code #used for connecting video to your application \end_layout \begin_layout LyX-Code bus.connect( \begin_inset Quotes eld \end_inset sync-message::element \begin_inset Quotes erd \end_inset , self.on_sync_message) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def set_location(self, location): \end_layout \begin_layout LyX-Code self.player.set_property( \begin_inset Quotes eld \end_inset uri \begin_inset Quotes erd \end_inset , \begin_inset Quotes eld \end_inset file:// \begin_inset Quotes erd \end_inset + location) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def play(self): \end_layout \begin_layout LyX-Code print \begin_inset Quotes eld \end_inset playing \begin_inset Quotes erd \end_inset \end_layout \begin_layout LyX-Code self.player.set_state(gst.STATE_PLAYING) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def pause(self): \end_layout \begin_layout LyX-Code print \begin_inset Quotes eld \end_inset paused \begin_inset Quotes erd \end_inset \end_layout \begin_layout LyX-Code self.player.set_state(gst.STATE_PAUSED) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def stop(self): \end_layout \begin_layout LyX-Code print \begin_inset Quotes eld \end_inset stoped \begin_inset Quotes erd \end_inset \end_layout \begin_layout LyX-Code self.player.set_state(gst.STATE_NULL) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_message(self, bus, message): \end_layout \begin_layout LyX-Code if message.type == gst.MESSAGE_EOS: # End of Stream \end_layout \begin_layout LyX-Code self.player.set_state(gst.STATE_NULL) \end_layout \begin_layout LyX-Code elif message.type == gst.MESSAGE_ERROR: \end_layout \begin_layout LyX-Code self.player.set_state(gst.STATE_NULL) \end_layout \begin_layout LyX-Code (err, debug) = message.parse_error() \end_layout \begin_layout LyX-Code print \begin_inset Quotes eld \end_inset Error: %s \begin_inset Quotes erd \end_inset % err, debug \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_sync_message(self, bus, message): \end_layout \begin_layout LyX-Code if message.structure is None: \end_layout \begin_layout LyX-Code return False \end_layout \begin_layout LyX-Code if message.structure.get_name() == \begin_inset Quotes eld \end_inset prepare-xwindow-id \begin_inset Quotes erd \end_inset : \end_layout \begin_layout LyX-Code self.videowidget.set_sink(message.src) \end_layout \begin_layout LyX-Code message.src.set_property( \begin_inset Quotes eld \end_inset force-aspect-ratio \begin_inset Quotes erd \end_inset , True) \end_layout \begin_layout Standard The GstPlayer class starts off with a video widget being passed in. This video widget is used to sync the GStreamer video with. After this the GStreamer pipeline is created using gst.element_factory_make using the \shape italic playbin \shape default element and with the identifier MultimediaPlayer. \end_layout \begin_layout LyX-Code self.player = gst.element_factory_make( \begin_inset Quotes eld \end_inset playbin \begin_inset Quotes erd \end_inset , \begin_inset Quotes eld \end_inset MultimediaPlayer \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout Standard Next a bus is created to be used with the pipeline and signal watching is added. Signals are connected to the self._on_message method. \end_layout \begin_layout LyX-Code bus = self.player.get_bus() \end_layout \begin_layout LyX-Code bus.add_signal_watch() \end_layout \begin_layout LyX-Code bus.connect( \begin_inset Quotes eld \end_inset message \begin_inset Quotes erd \end_inset , self.on_message) \end_layout \begin_layout Standard After this is used the enable_sync_message_emission() method on the bus to enable the player to sync the GStreamer video with the video widget that is being used. If this is not done, a separate window will be opened that is outside of the running application to play the video in. The sync emissions signals are directed to the self.on_sync_message method. \end_layout \begin_layout LyX-Code bus.enable_sync_message_emission() \end_layout \begin_layout LyX-Code bus.connect( \begin_inset Quotes eld \end_inset sync-message::element \begin_inset Quotes erd \end_inset , self.on_sync_message) \end_layout \begin_layout Standard The on_sync_message method uses the video widget that has been passed in during the initialization of GstPlayer to attach the video to the application. This is a little hocus pocus that I am not really sure of what is happening. It seems to be communicating somewhat with the underlying X Windows about the window id. \end_layout \begin_layout Standard Then the set_sink method that is in video widget class is set to the message src. The set_sink method is used for convenience. The VideoWidget class that is discussed in the next section. \end_layout \begin_layout Standard The very last part is to set force the aspect ratio of the video. \end_layout \begin_layout LyX-Code def on_sync_message(self, bus, message): \end_layout \begin_layout LyX-Code if message.structure is None: \end_layout \begin_layout LyX-Code return False \end_layout \begin_layout LyX-Code if message.structure.get_name() == \begin_inset Quotes eld \end_inset prepare-xwindow-id \begin_inset Quotes erd \end_inset : \end_layout \begin_layout LyX-Code self.videowidget.set_sink(message.src) \end_layout \begin_layout LyX-Code message.src.set_property( \begin_inset Quotes eld \end_inset force-aspect-ratio \begin_inset Quotes erd \end_inset , True) \end_layout \begin_layout Standard The rest of what is covered by the GstPlayer class is very self explanatory. A \shape italic play \shape default , \shape italic stop \shape default , and \shape italic pause \shape default method to play, pause, or stop the media file. And there is also a method call \shape italic set_location \shape default that sets the location of the media file that is to be played. \end_layout \begin_layout Subsection VideoWidget \end_layout \begin_layout Standard The VideoWidget class is a subclass of gtk.DrawingArea. It is created to ease the use of displaying videos inside of applications and is used in conjunction with the user interface and GstPlayer class. \end_layout \begin_layout LyX-Code class VideoWidget(gtk.DrawingArea): \end_layout \begin_layout LyX-Code \begin_inset Quotes eld \end_inset \begin_inset Quotes erd \end_inset \begin_inset Quotes erd \end_inset \end_layout \begin_layout LyX-Code Extend gtk.DrawingArea to create our own video widget. \end_layout \begin_layout LyX-Code \begin_inset Quotes eld \end_inset \begin_inset Quotes erd \end_inset \begin_inset Quotes erd \end_inset \end_layout \begin_layout LyX-Code def __init__(self): \end_layout \begin_layout LyX-Code gtk.DrawingArea.__init__(self) \end_layout \begin_layout LyX-Code self.imagesink = None \end_layout \begin_layout LyX-Code self.unset_flags(gtk.DOUBLE_BUFFERED) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def do_expose_event(self, event): \end_layout \begin_layout LyX-Code if self.imagesink: \end_layout \begin_layout LyX-Code self.imagesink.expose() \end_layout \begin_layout LyX-Code return False \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code return True \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def set_sink(self, sink): \end_layout \begin_layout LyX-Code if sys.platform == \begin_inset Quotes eld \end_inset win32 \begin_inset Quotes erd \end_inset : \end_layout \begin_layout LyX-Code win_id = self.window.handle \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code win_id = self.window.xid \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code assert win_id \end_layout \begin_layout LyX-Code self.imagesink = sink \end_layout \begin_layout LyX-Code self.imagesink.set_xwindow_id(win_id) \end_layout \begin_layout Standard VideoWidget starts off with the initialization of the DrawingArea parent class. It then proceeds to set the imagesink to none. Last in the initialization is to unset the double buffering. \end_layout \begin_layout Standard The set_sink method asserts that the window xid is available and then proceeds to set the imagesink to the sink that is passed in. The sink is passed in from the GstPlayer class. Then while still in the set_sink method the imagesink sets the id of the xwindow to self.window.xid. \end_layout \begin_layout Standard With the VideoWidget class now completed it is simple to use to display videos. To use it with a PyGTK application you would just initialize in your PyGTK GUI code like so: \end_layout \begin_layout LyX-Code videowidget = VideoWidget() \end_layout \begin_layout Subsection User Interface \end_layout \begin_layout Standard A good user interface is required to make the ability to play media files useful. The user interface will allow the user to interact with the program, open audio or video files, and if the file is a video then resize it to fullscreen. \end_layout \begin_layout LyX-Code class Main(object): \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code The Main class is the Gui. It creates an instance of the \end_layout \begin_layout LyX-Code GstPlayer class and the FileInfo class. It is what the user \end_layout \begin_layout LyX-Code interacts with and controls what happens. \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code def __init__(self): \end_layout \begin_layout LyX-Code #Store the location of the multimedia file \end_layout \begin_layout LyX-Code self.multimedia_file = None \end_layout \begin_layout LyX-Code # To be used with the FileInfo Class \end_layout \begin_layout LyX-Code self.file_info = None \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Create the GUI \end_layout \begin_layout LyX-Code self.win = gtk.Window() \end_layout \begin_layout LyX-Code self.win.set_title("Play Video Example 2") \end_layout \begin_layout LyX-Code self.win.connect("delete_event", lambda w,e: \end_layout \begin_layout LyX-Code gtk.main_quit()) \end_layout \begin_layout LyX-Code vbox = gtk.VBox(False, 0) \end_layout \begin_layout LyX-Code self.control_box = gtk.HBox(False, 0) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Control Buttons \end_layout \begin_layout LyX-Code self.load_file_button = gtk.FileChooserButton( \end_layout \begin_layout LyX-Code "Choose Audio File") \end_layout \begin_layout LyX-Code self.play_button = gtk.Button("Play", \end_layout \begin_layout LyX-Code gtk.STOCK_MEDIA_PLAY) \end_layout \begin_layout LyX-Code self.pause_button = gtk.Button("Pause", \end_layout \begin_layout LyX-Code gtk.STOCK_MEDIA_PAUSE) \end_layout \begin_layout LyX-Code self.stop_button = gtk.Button("Stop", gtk.STOCK_MEDIA_STOP) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Video Widget Stuff \end_layout \begin_layout LyX-Code self.videowidget = VideoWidget() \end_layout \begin_layout LyX-Code self.videowidget.set_size_request(400, 250) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Signals and Callbacks \end_layout \begin_layout LyX-Code self.load_file_button.connect("selection-changed", \end_layout \begin_layout LyX-Code self.on_file_selected) \end_layout \begin_layout LyX-Code self.play_button.connect("clicked", self.on_play_clicked) \end_layout \begin_layout LyX-Code self.pause_button.connect("clicked", self.on_pause_clicked) \end_layout \begin_layout LyX-Code self.stop_button.connect("clicked", self.on_stop_clicked) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Fullscreen stuff \end_layout \begin_layout LyX-Code self.win.connect("key-press-event", \end_layout \begin_layout LyX-Code self.on_win_key_press_event) \end_layout \begin_layout LyX-Code self.win.connect("window-state-event", \end_layout \begin_layout LyX-Code self.on_window_state_event) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.control_box.pack_start(self.play_button, \end_layout \begin_layout LyX-Code False, True, 0) \end_layout \begin_layout LyX-Code self.control_box.pack_start(self.pause_button, \end_layout \begin_layout LyX-Code False, True, 0) \end_layout \begin_layout LyX-Code self.control_box.pack_start(self.stop_button, \end_layout \begin_layout LyX-Code False, True, 0) \end_layout \begin_layout LyX-Code vbox.pack_start(self.load_file_button, False, True, 0) \end_layout \begin_layout LyX-Code vbox.pack_start(self.videowidget, True, True, 0) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # You want to expand the video widget or else you \end_layout \begin_layout LyX-Code #cannot see it \end_layout \begin_layout LyX-Code vbox.pack_start(self.control_box, False, True, 0) \end_layout \begin_layout LyX-Code self.win.add(vbox) \end_layout \begin_layout LyX-Code self.win.show_all() \end_layout \begin_layout LyX-Code self.gst_player = GstPlayer(self.videowidget) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def fullscreen_mode(self): \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code Called from the on_win_key_press_event method. If the \end_layout \begin_layout LyX-Code program is in fullscreen this method will unfullscreen \end_layout \begin_layout LyX-Code it. If the program is not in fullscreen it will set it \end_layout \begin_layout LyX-Code to fullscreen. This method will also hide the controls \end_layout \begin_layout LyX-Code while in fullscreen mode. \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code if self.__is_fullscreen: \end_layout \begin_layout LyX-Code self.win.unfullscreen() \end_layout \begin_layout LyX-Code self.control_box.show() \end_layout \begin_layout LyX-Code self.load_file_button.show() \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code self.control_box.hide() \end_layout \begin_layout LyX-Code self.load_file_button.hide() \end_layout \begin_layout LyX-Code self.win.fullscreen() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_win_key_press_event(self, widget, event): \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code Handle any key press event on the main window. \end_layout \begin_layout LyX-Code This method is being used to detect when the ESC key \end_layout \begin_layout LyX-Code is being pressed in fullscreen to take the \end_layout \begin_layout LyX-Code window out of fullscreen \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code key = gtk.gdk.keyval_name(event.keyval) \end_layout \begin_layout LyX-Code if key == "Escape" or key == "f": \end_layout \begin_layout LyX-Code self.fullscreen_mode() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_window_state_event(self, widget, event): \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code Detect window state events to determine whether in \end_layout \begin_layout LyX-Code fullscreen or not in fullscreen \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code self.__is_fullscreen = bool(event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN) \end_layout \begin_layout LyX-Code print "Is fullscreen: ", self.__is_fullscreen \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_file_selected(self, widget): \end_layout \begin_layout LyX-Code print "Selected: ", self.load_file_button.get_filename() \end_layout \begin_layout LyX-Code self.multimedia_file = self.load_file_button.get_filename() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Do not call method from here immediately. \end_layout \begin_layout LyX-Code # FileInfo.poll() will return false when it is ready. \end_layout \begin_layout LyX-Code # Usually a second or two. \end_layout \begin_layout LyX-Code self.file_info = MediaInfo(self.multimedia_file) \end_layout \begin_layout LyX-Code self.gst_player.set_location( \end_layout \begin_layout LyX-Code self.multimedia_file ) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_play_clicked(self, widget): \end_layout \begin_layout LyX-Code print "play clicked" \end_layout \begin_layout LyX-Code print "Video (width, height): ", \end_layout \begin_layout LyX-Code self.file_info.get_width(), \end_layout \begin_layout LyX-Code self.file_info.get_height() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.videowidget.set_size_request( \end_layout \begin_layout LyX-Code self.file_info.get_width(), \end_layout \begin_layout LyX-Code self.file_info.get_height() ) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.gst_player.play() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_pause_clicked(self, widget): \end_layout \begin_layout LyX-Code print "pause clicked" \end_layout \begin_layout LyX-Code self.gst_player.pause() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_stop_clicked(self, widget): \end_layout \begin_layout LyX-Code print "stop clicked" \end_layout \begin_layout LyX-Code self.gst_player.stop() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if __name__ == "__main__": \end_layout \begin_layout LyX-Code Main() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Standard The Main class starts off by initializing a few variables and creating the required user interface code. \end_layout \begin_layout Standard The multimedia_file is set to None and will store the location of the media file that is to be played. \end_layout \begin_layout Standard The file_info variable is set to None and will be later used to initialize the MediaInfo class. \end_layout \begin_layout Standard After declaring the first class instance variables the Main class creates the user interface, sets the title to \begin_inset Quotes eld \end_inset Play Video Example 2 \begin_inset Quotes erd \end_inset , and adds a few buttons to play, pause, and stop the video. It also adds a gtk.FileChooserButton video to select the media files that will be played. \end_layout \begin_layout Standard Next it creates a video widget using the VideoWidget class that was described above. \end_layout \begin_layout Standard At the very bottom of the of the __init__ method the class variable self.gst_play er is initialized as an instance of the GstPlayer class using the self.videowidge t instance that was created. \end_layout \begin_layout LyX-Code self.gst_player = GstPlayer(self.videowidget) \end_layout \begin_layout Standard On the clicked signal is emitted from the play, pause, or stop button; then the methods on_play_clicked, on_pause_clicked, and on_stop_clicked are called respective to their buttons. \end_layout \begin_layout Standard When a file is selected with the load_file_button the on_file_selected method is called. \end_layout \begin_layout Standard In the section of the code commented as full screen stuff it will connect key-press-event signals and window-state-event signals to the on_win_key_press_ event and on_window_state_event methods. The on_win_key_press_event method will detect if the key pressed is the \begin_inset Quotes eld \end_inset F \begin_inset Quotes erd \end_inset or \begin_inset Quotes eld \end_inset Esc \begin_inset Quotes erd \end_inset key and if so call the fullscreen_mode() method. If it is fullscreen it will unfullscreen the video. If it is not in full screen it will set it to fullscreen. \end_layout \begin_layout Standard The on_window_state_event detects changes in the window state. All that it is used for is to set the variable self.__is_fullscreen to True or false. This variable is used in the method fullscreen_mode() to either hide the control buttons (play, pause, stop, load_file) or show them. If the variable is set to False it will set these widgets to be displayed and unfullscreen the video widget. If the self.__full_screen is True it will hide all the control widgets and set the video widget to fullscreen with self.win.fullscreen(). \end_layout \begin_layout Standard Next is the on_file_selected method. This method is called when the a file selected from the gtk.FileChooserButton load_file_button. It stores the location of the file in the variable self.multimedia_file. After the file location has been stored it is used to create an instance of the MediaInfo class. \end_layout \begin_layout LyX-Code self.file_info = MediaInfo(self.multimedia_file) \end_layout \begin_layout Standard And finally in the on_file_selected method the location of the media file is loaded into the the GstPlayer instance self.gst_player. \end_layout \begin_layout LyX-Code self.gst_player.set_location(self.multimedia_file) \end_layout \begin_layout Standard The on_play_clicked method will use the MediaInfo instance self.file_info to get the width and height of the video and set the size of the self.videowdige t to the correct dimensions to display the video. \end_layout \begin_layout Standard After this it will set the video playing by calling the play method in the GstPlayer class: \end_layout \begin_layout LyX-Code self.gst_player.play() \end_layout \begin_layout Standard As with the on_play_clicked method the on_pause_clicked and on_stop_clicked methods will use call the pause and stop methods from the GstPlayer class. \end_layout \begin_layout LyX-Code self.gst_player.pause() \end_layout \begin_layout LyX-Code self.gst_player.stop() \end_layout \begin_layout Standard The only thing that is left to do is to make sure the Main class is called and this is accomplished at the bottom of the source code, which detects if this is file is the main file being run and enters the GTK mainloop. \end_layout \begin_layout LyX-Code if __name__ == \begin_inset Quotes eld \end_inset __main \begin_inset Quotes erd \end_inset : \end_layout \begin_layout LyX-Code Main() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Section Summary \end_layout \begin_layout Standard Basically GStreamer is a very powerful framework with many options available. Even though this chapter only covered a small portion of what is available, as a programmer you should now be able to add audio and video play back to your application. \end_layout \begin_layout Standard As well as seek the position of the media file and display the current location and length of the file to the user. \end_layout \begin_layout Standard For more information using GStreamer visit the following sites: \end_layout \begin_layout Enumerate \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://pygstdocs.berlios.de/ \end_layout \end_inset Contains tutorials and documentation on python GStreamer \end_layout \begin_layout Enumerate The main C documentation. If you do not know C this may not be of use, but it may, anyway \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://gstreamer.freedesktop.org/data/doc/gstreamer/head/manual/html/index.html \end_layout \end_inset \end_layout \begin_layout Enumerate Check out all the examples that come with the PyGST source at \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://webcvs.freedesktop.org/gstreamer/gst-python/examples/ \end_layout \end_inset \end_layout \begin_layout Enumerate And of course check out the examples that come with this books website \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.majorsilence.com/rubbish/pygtk-book/ \end_layout \end_inset \end_layout \begin_layout Chapter Dbus Interprocess Communication \end_layout \begin_layout Standard \begin_inset Box Frameless position "t" hor_pos "c" has_inner_box 1 inner_pos "t" use_parbox 0 width "100col%" special "none" height "1in" height_special "totalheight" status open \begin_layout Plain Layout \begin_inset CommandInset include LatexCommand input filename "chapter-heading.lyx" \end_inset \end_layout \end_inset \end_layout \begin_layout Section Introduction \end_layout \begin_layout Standard DBus is used for interprocess communication between applications. Simply put this means that applications can retrieve information from one another by accessing special methods that are provided by DBus. \end_layout \begin_layout Description ObjectPath \begin_inset CommandInset label LatexCommand label name "des:ObjectPath-An-application" \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout DBus!ObjectPath \end_layout \end_inset An application may export an object to represent itself or different parts of itself. For example Rhythmbox exports an object representing the Play List and an object representing the current playing song. Eg. /org/gnome/Rhythmbox/Player \end_layout \begin_layout Description BusName \begin_inset CommandInset label LatexCommand label name "des:BusName-The-name" \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout DBus!BusName \end_layout \end_inset The name of the application as exposed through DBus. Eg. org.gnome.Rhythmbox \end_layout \begin_layout Description Interface \begin_inset CommandInset label LatexCommand label name "des:Interface-Is-used" \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout DBus!Interface \end_layout \end_inset Is used to access methods through DBus. Eg. org.gnome.Rhythmbox \end_layout \begin_layout Standard This chapters purpose is to show how to control other applications with DBus and how to add DBus to your PyGTK applications so that you can expose functionality of your applications to others. \end_layout \begin_layout Section Controlling Applications \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sec:DBus - Controlling Applications" \end_inset \end_layout \begin_layout Standard First off is going to be an example of how to use dbus to communicate with another application. This example will communicate with the rhythmbox music player. The reason for using rhythmbox is because it is it is rather ubiquitous in the gnome distro world. \end_layout \begin_layout LyX-Code #!/usr/bin/env python \end_layout \begin_layout LyX-Code import os, gobject, dbus \end_layout \begin_layout LyX-Code from dbus.mainloop.glib import DBusGMainLoop \end_layout \begin_layout LyX-Code import gtk \end_layout \begin_layout Standard The above code imports the needed code to work with this example. What is needed to work with DBus is the \emph on dbus \emph default module and \emph on DBusGMainLoop \emph default . The dbus module is used for the common dbus interactions while DBusGMainLoop is used to work with gobject main loops, which PyGTK uses. \end_layout \begin_layout Standard Here the class DBusExample is created with the __init__ method setting up the dbus. \end_layout \begin_layout LyX-Code class DBusExample(object): \end_layout \begin_layout LyX-Code def __init__(self): \end_layout \begin_layout LyX-Code # Do before session or system bus is created. \end_layout \begin_layout LyX-Code dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) \end_layout \begin_layout LyX-Code self.bus = dbus.SessionBus() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.proxy_object = self.bus.get_object('org.gnome.Rhythmbox', \end_layout \begin_layout LyX-Code '/org/gnome/Rhythmbox/Player') \end_layout \begin_layout LyX-Code self.player = dbus.Interface(self.proxy_object, \end_layout \begin_layout LyX-Code 'org.gnome.Rhythmbox.Player') \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.bus.add_signal_receiver(self.on_song_changed, \end_layout \begin_layout LyX-Code dbus_interface="org.gnome.Rhythmbox.Player", \end_layout \begin_layout LyX-Code signal_name="playingUriChanged") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.init_gui() \end_layout \begin_layout LyX-Code self.list_available_commands() \end_layout \begin_layout Standard To begin with a DBus SessionBus is created. This allows connecting to other applications. If this example were connecting to a system process it would use a SystemBus. Once the bus is created, a proxy object is assigned to self.proxy_object using the self.bus.get_object method. The get_object method takes as arguments the applications Bus Name( \begin_inset CommandInset ref LatexCommand pageref reference "des:BusName-The-name" \end_inset ) and Object Path( \begin_inset CommandInset ref LatexCommand pageref reference "des:ObjectPath-An-application" \end_inset ). \end_layout \begin_layout Standard After the proxy_object has been created it is used to create an interface to the available methods. The interface self.player is created using the dbus.Interface class. It is initlized with the self.proxy_object and using the org.gnome.Rhythmbox.Player interface. This interface provides for methods to control and retrieve information on the currently playing song. \end_layout \begin_layout Standard Below this a signal handler is created on the bus to catch the playingUriChanged Signal from the interface org.gnome.Rhythmbox.Player and call the on_song_changed method. \end_layout \begin_layout Standard Lastly in the __init__ method the init_gui method is called. The init_gui method is rather insignificant as it creates a small gui with a few buttons. However the callback methods for those buttons use the self.player interface to control rhythmbox. \end_layout \begin_layout LyX-Code def init_gui(self): \end_layout \begin_layout LyX-Code win = gtk.Window() \end_layout \begin_layout LyX-Code win.connect("delete_event", lambda w,e:gtk.main_quit()) \end_layout \begin_layout LyX-Code vbox = gtk.VBox() \end_layout \begin_layout LyX-Code hbox = gtk.HBox() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.output = gtk.Label("") \end_layout \begin_layout LyX-Code vbox.pack_start(self.output, False, True, 0) \end_layout \begin_layout LyX-Code mute=gtk.Button("Mute") \end_layout \begin_layout LyX-Code play_pause=gtk.Button("Play/Pause") \end_layout \begin_layout LyX-Code previous=gtk.Button("Previous") \end_layout \begin_layout LyX-Code next=gtk.Button("Next") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code mute.connect("clicked", self.on_mute_clicked) \end_layout \begin_layout LyX-Code play_pause.connect("clicked", self.on_play_pause_clicked) \end_layout \begin_layout LyX-Code previous.connect("clicked", self.on_previous_clicked) \end_layout \begin_layout LyX-Code next.connect("clicked", self.on_next_clicked) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code hbox.pack_start(mute, False, True, 0) \end_layout \begin_layout LyX-Code hbox.pack_start(play_pause, False, True, 0) \end_layout \begin_layout LyX-Code hbox.pack_start(previous, False, True, 0) \end_layout \begin_layout LyX-Code hbox.pack_start(next, False, True, 0) \end_layout \begin_layout LyX-Code vbox.pack_start(hbox, False, True, 0) \end_layout \begin_layout LyX-Code win.add(vbox) \end_layout \begin_layout LyX-Code win.show_all() \end_layout \begin_layout Standard The init_gui method above creates a small user interface with a play/pause, mute, previous and next button to control rhythmbox. It also as a label that is used by the on_song_changed callback method, that was specified in the __init__ method, to display the path and name of the current playing song. \end_layout \begin_layout LyX-Code def on_mute_clicked(self, widget): \end_layout \begin_layout LyX-Code if self.player.getMute(): \end_layout \begin_layout LyX-Code self.player.setMute(False) \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code self.player.setMute(True) \end_layout \begin_layout Standard The on_mute_clicked method checks to see if rhythmbox is muted, if it is it will unmute it. If it is not muted it will set it to mute. This is accomplished using the self.player interface with the setMute method, which takes a boolean argument. \end_layout \begin_layout LyX-Code def on_play_pause_clicked(self, widget): \end_layout \begin_layout LyX-Code if self.player.getPlaying(): \end_layout \begin_layout LyX-Code self.player.playPause(False) \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code self.player.playPause(True) \end_layout \begin_layout Standard The on_play_pause_clicked method will set rhythmbox to play if it is paused and pause it if it is playing. This is accomplished using the self.player interface with the playPause method, which takes a boolean argument. \end_layout \begin_layout LyX-Code def on_previous_clicked(self, widget): \end_layout \begin_layout LyX-Code self.player.previous() \end_layout \begin_layout Standard The on_previous_clicked method will set rhythmbox to play the previous played song. This is accomplished using the self.player interface with the previous method. \end_layout \begin_layout LyX-Code def on_next_clicked(self, widget): \end_layout \begin_layout LyX-Code self.player.next() \end_layout \begin_layout Standard The on_next_clicked method will set rhythmbox to play the next song. This is accomplished using the self.player interface with the next method. \end_layout \begin_layout LyX-Code def on_song_changed(self, data): \end_layout \begin_layout LyX-Code path, filename = os.path.split(self.player.getPlayingUri()) \end_layout \begin_layout LyX-Code self.output.set_text("Path: " + path + " \backslash nFilename: " + filename) \end_layout \begin_layout Standard The on_song_changed method is called when the playingUriChanged signal is emitted. It retrieves the current songs current uri, splitting it into a path and file name, and displays it using a gtk label. It should also be pointed out that instead of using the getPlayUri() method, the data argument could be used as it is the uri of the current song as well. \end_layout \begin_layout Standard And last lets not forget the small amount of code to run this example \end_layout \begin_layout LyX-Code if __name__ == "__main__": \end_layout \begin_layout LyX-Code app = DBusExample() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Section Adding DBus to your Applications \end_layout \begin_layout Standard Controlling other applications using DBus is one thing but it is not enough if you application needs to allow others to control it. To let other applications have access to your program requires explosing methods of sub class of dbus.service.Object. \end_layout \begin_layout Subsection Creating a DBus Service \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sub:DBus - Creating a DBus Service" \end_inset \end_layout \begin_layout Standard To start off a few modlues need to be imported. The import ones are the DBus ones. \end_layout \begin_layout LyX-Code #!/usr/bin/env python \end_layout \begin_layout LyX-Code import os, gobject, dbus, dbus.service \end_layout \begin_layout LyX-Code from dbus.mainloop.glibimport DBusGMainLoop \end_layout \begin_layout LyX-Code import gtk \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code output_label = None \end_layout \begin_layout Standard So of the above modules dbus, dbus.service and DBusGMainLoop are what are important for allowing other applications to connect to his one. After the imports there is the output_label which will be used as a global to create a gtk.Label to display messages that are received through DBus. \end_layout \begin_layout Standard After this DBusObject class is created; it can be named whatever you want as long as it subclasses dbus.service.Object. As you will see it is not necessary to create __init__ method with this class as the parent classes can be used. \end_layout \begin_layout LyX-Code class DBusObject(dbus.service.Object): \end_layout \begin_layout LyX-Code # Display and message to gtk label and return message to caller \end_layout \begin_layout LyX-Code @dbus.service.method('com.majorsilence.MessageInterface', \end_layout \begin_layout LyX-Code in_signature='', out_signature='s') \end_layout \begin_layout LyX-Code def display_welcome_message(self): \end_layout \begin_layout LyX-Code global output_label \end_layout \begin_layout LyX-Code output_label.set_text("Welcome to dbus.") \end_layout \begin_layout LyX-Code return "Welcome to dbus." \end_layout \begin_layout Standard To expose methods for the @dbus.service.method decorator is used, specifying the DBus Interface that the method will available on and the methods in (arguments) and out (return value) signatures. Here the interface is specified as com.majorsilence.MessageInterface, so any application calling this method would have to use com.majorsilence.MessageInt erface. After the decorator declare the method as normal. The method name is the same name that will be exposed. \end_layout \begin_layout Standard So what we end up with here is a method called display_welcome_message that returns a string, s meaning it is a dbus.String type (see \begin_inset CommandInset ref LatexCommand vref reference "sec:Dbus - Types" \end_inset ). As can be seen it sets the label to \begin_inset Quotes eld \end_inset Welcome to dbus \begin_inset Quotes erd \end_inset and returns the same message to the calling program. \end_layout \begin_layout Standard Moving on to the next method, it takes a string as an argument emits a signal and completion and returns nothing. \end_layout \begin_layout LyX-Code # Set gtk label to the message that is passed \end_layout \begin_layout LyX-Code @dbus.service.method(dbus_interface='com.majorsilence.MessageInterface', in_signat ure='s', out_signature='') \end_layout \begin_layout LyX-Code def set_message(self, s): \end_layout \begin_layout LyX-Code global output_label \end_layout \begin_layout LyX-Code if not isinstance(s, dbus.String): \end_layout \begin_layout LyX-Code print "not string" \end_layout \begin_layout LyX-Code return \end_layout \begin_layout LyX-Code output_label.set_text(s) \end_layout \begin_layout LyX-Code #emit signal \end_layout \begin_layout LyX-Code self.message_signal() \end_layout \begin_layout Standard As before and like all exposed DBus methods the @dbus.service.method decorator is used. This method has the same DBus Interface as the first method, com.majorsilence.Mes sageInterface, and an in_signature of s meaning a dbus.String (see \begin_inset CommandInset ref LatexCommand vref reference "sec:Dbus - Types" \end_inset ). \end_layout \begin_layout Standard The method is set_message, it takes as an argument a string. It checks to make sure it was passed a string, if it was it will set the label to the string that was passed in. The interesting thing about this method compared to the first one is that it emits a signal on completion. It does this by calling the self.message_signal() method as its last act. \end_layout \begin_layout Standard The self.message_signal is the method that is described next. It to uses a dbus decorator, but instead of using the @dbus.service.method decorator, it uses the @dbus.service.signal decorator. What this means is that when this method is called it will emit a signal that can be caught using the add_signal_receiver method that was described in \begin_inset CommandInset ref LatexCommand vref reference "sec:DBus - Controlling Applications" \end_inset . \end_layout \begin_layout LyX-Code @dbus.service.signal('com.majorsilence.MessageInterface') \end_layout \begin_layout LyX-Code def message_signal(self): \end_layout \begin_layout LyX-Code return \end_layout \begin_layout Standard As can be seen the message_signal method uses the @dbus.service.signal decorator and specifies the com.majorsilence.MessageInterface. If it is to include data with its signal it should also have a out_signature specifying the correct type. \end_layout \begin_layout Standard All that is left is the the main() function that is used to setup a very small PyGTK GUI and create the neccesary DBus initiation. \end_layout \begin_layout LyX-Code def main(): \end_layout \begin_layout LyX-Code # Create GTK Gui \end_layout \begin_layout LyX-Code global output_label \end_layout \begin_layout LyX-Code win = gtk.Window() \end_layout \begin_layout LyX-Code win.connect("delete_event", lambda w,e:gtk.main_quit()) \end_layout \begin_layout LyX-Code output_label = gtk.Label("This message will change through using dbus.") \end_layout \begin_layout LyX-Code win.add(output_label) \end_layout \begin_layout LyX-Code win.show_all() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Start DBus Service \end_layout \begin_layout LyX-Code dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) \end_layout \begin_layout LyX-Code session_bus = dbus.SessionBus() \end_layout \begin_layout LyX-Code name = dbus.service.BusName("com.majorsilence.MessageService", session_bus) \end_layout \begin_layout LyX-Code object = DBusObject(session_bus, "/TestObject") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Standard The important part of the code starts after the # Start DBus Service comment. These four lines of code are what makes available dbus and makes it possible to expose method of the application to any other DBus capable program. First DBus must be set to use the glib gobject main loop (the same that PyGTK uses), without this it will not work. Next it creates a session bus that allows applications to connect to a bus. After this it uses the session bus to create a bus name using the dbus.service.Bu sName class. It takes as arguements the session bus that was created and the interface com.majorsilence.MessageService. \end_layout \begin_layout Standard Finally the object is create calling the DBusObject class that we have created, using the session bus that we have created and using the /TestObject object path. \end_layout \begin_layout LyX-Code if __name__ == "__main__": \end_layout \begin_layout LyX-Code main() \end_layout \begin_layout Standard Of course do not forget to call the main function that runs the the example PyGTK DBus service application. \end_layout \begin_layout Subsection Connecting to your DBus Service \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sub:DBus - Connecting to your DBus Service" \end_inset \end_layout \begin_layout Standard Controlling your own application through DBus is very similiar to how the first example controlled Rhythmbox. This is a small application that will call the two exposed methods from \begin_inset CommandInset ref LatexCommand ref reference "sub:DBus - Creating a DBus Service" \end_inset and handle the signal that is emitted. \end_layout \begin_layout LyX-Code #!/usr/bin/env python \end_layout \begin_layout LyX-Code import os, gobject,dbus \end_layout \begin_layout LyX-Code from dbus.mainloop.glib import DBusGMainLoop \end_layout \begin_layout LyX-Code import gtk \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code class DBusClient(object): \end_layout \begin_layout LyX-Code def __init__(self): \end_layout \begin_layout LyX-Code # Do before session or system bus is created. \end_layout \begin_layout LyX-Code dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) \end_layout \begin_layout LyX-Code self.bus = dbus.SessionBus() \end_layout \begin_layout LyX-Code self.proxy = self.bus.get_object('com.majorsilence.MessageService', \end_layout \begin_layout LyX-Code '/TestObject') \end_layout \begin_layout LyX-Code self.control_interface = dbus.Interface(self.proxy, \end_layout \begin_layout LyX-Code 'com.majorsilence.MessageInterface') \end_layout \begin_layout LyX-Code self.bus.add_signal_receiver(self.on_message_recieved, \end_layout \begin_layout LyX-Code dbus_interface="com.majorsilence.MessageInterface", \end_layout \begin_layout LyX-Code signal_name="message_signal") \end_layout \begin_layout Standard As can be seen, connect to the bus name com.majorsilence.MessageSerive using the objec path /TestObject. Next the interface is created using self.proxy and the interface com.majorsilence. MessageInterface. Finally the signal message_signal is handled by connecting it to the self.on_mes sage_recieved method when it is emitted from the com.majorsilence.MessageInterface interface. \end_layout \begin_layout LyX-Code win = gtk.Window() \end_layout \begin_layout LyX-Code win.connect("delete_event", lambda w,e:gtk.main_quit()) \end_layout \begin_layout LyX-Code vbox = gtk.VBox() \end_layout \begin_layout LyX-Code hbox = gtk.HBox() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.text_message=gtk.Entry() \end_layout \begin_layout LyX-Code set_message=gtk.Button("Set Message") \end_layout \begin_layout LyX-Code display_message=gtk.Button("Display Welcome Message") \end_layout \begin_layout LyX-Code set_message.connect("clicked", self.on_set_message_clicked) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code display_message.connect("clicked", \end_layout \begin_layout LyX-Code self.on_display_message_clicked) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code hbox.pack_start(set_message, False, True, 0) \end_layout \begin_layout LyX-Code hbox.pack_start(display_message, False, True, 0) \end_layout \begin_layout LyX-Code vbox.pack_start(self.text_message, False, True, 0) \end_layout \begin_layout LyX-Code vbox.pack_start(hbox, False, True, 0) \end_layout \begin_layout LyX-Code win.add(vbox) \end_layout \begin_layout LyX-Code win.show_all() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_message_recieved(self): \end_layout \begin_layout LyX-Code print "message_signal caught" \end_layout \begin_layout Standard When the signal is emitted it does nothing prints a message to the console. \begin_inset Note Note status collapsed \begin_layout Plain Layout Probably want to change this to displaying in a gtk.label, Can always do this later. \end_layout \end_inset \end_layout \begin_layout LyX-Code def on_set_message_clicked(self, widget): \end_layout \begin_layout LyX-Code message = self.text_message.get_text() \end_layout \begin_layout LyX-Code self.control_interface.set_message(message) \end_layout \begin_layout Standard When the set message button is clicked it grabs the text from the text entry and uses the self.control_interface to set the label in the serve appliction to whatever text was typed in. \end_layout \begin_layout LyX-Code def on_display_message_clicked(self, widget): \end_layout \begin_layout LyX-Code print self.control_interface.display_welcome_message() \end_layout \begin_layout Standard When the display message button is clicked it calls the exposed method display_w elcome_message() which is a method with a predfined message that is displayed to the DBus service applications label. \end_layout \begin_layout LyX-Code if __name__ == "__main__": \end_layout \begin_layout LyX-Code app = DBusClient() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Standard The code to to actually run the example. \end_layout \begin_layout Section Finding Exposed Methods \end_layout \begin_layout Standard \begin_inset Index status collapsed \begin_layout Plain Layout DBus!Finding Exposed Methods \end_layout \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout DBus!Introspection \end_layout \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout DBus!List all Exposed Methods, Properties, and Signals \end_layout \end_inset \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sec:DBus - Finding Exposed Methods" \end_inset \end_layout \begin_layout Standard Now you are asking yourself \begin_inset Quotes eld \end_inset it is all good and well that I can access functionalty throught DBus, but how do I find what is available? \begin_inset Quotes erd \end_inset . Well this is actionally fairly simple and is accomplished using introspection. Basically form is \end_layout \begin_layout LyX-Code your_interface.Introspect(dbus_interface='org.freedesktop.DBus.Introspectable') \end_layout \begin_layout LyX-Code def list_available_commands(self): \end_layout \begin_layout Standard Here is an example using rhythmbox. It lists all the available methods, signals and properties of the interface that is used. It is printed as xml as that is the form that DBus uses. \end_layout \begin_layout LyX-Code import gobject, dbus \end_layout \begin_layout LyX-Code from dbus.mainloop.glib import DBusGMainLoop \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) \end_layout \begin_layout LyX-Code bus = dbus.SessionBus() \end_layout \begin_layout LyX-Code proxy_object = bus.get_object('org.gnome.Rhythmbox', \end_layout \begin_layout LyX-Code '/org/gnome/Rhythmbox/Player') \end_layout \begin_layout LyX-Code player = dbus.Interface(proxy_object, \end_layout \begin_layout LyX-Code 'org.gnome.Rhythmbox.Player') \end_layout \begin_layout LyX-Code print player.Introspect(dbus_interface= \end_layout \begin_layout LyX-Code 'org.freedesktop.DBus.Introspectable') \end_layout \begin_layout Standard That is all that is to it, very simple, very easy. \end_layout \begin_layout Section Types \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sec:Dbus - Types" \end_inset \end_layout \begin_layout Standard When using introspection a print out of the xml will be displayed, for instance a piece of it may look like this. \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout Standard What this small piece means is that there is a method that is available called \emph on playPause \emph default . It takes one argument. Its type is b meaning it is a boolean. The direction is in, meaning it recieves input, if the direction is out is returns a value. \end_layout \begin_layout Standard It is important to know what the different types are so here is a list. \end_layout \begin_layout List \labelwidthstring 00.00.0000 b dbus.Boolean, bool \end_layout \begin_layout List \labelwidthstring 00.00.0000 d dbus.Double, float \end_layout \begin_layout List \labelwidthstring 00.00.0000 g dbus.Signature \end_layout \begin_layout List \labelwidthstring 00.00.0000 i dbus.Int32, int \end_layout \begin_layout List \labelwidthstring 00.00.0000 n dbus.Int16 \end_layout \begin_layout List \labelwidthstring 00.00.0000 o dbus.ObjectPath \end_layout \begin_layout List \labelwidthstring 00.00.0000 q dbus.UInt16 \end_layout \begin_layout List \labelwidthstring 00.00.0000 s dbus.String, dbus.UTF8String, str, unicode \end_layout \begin_layout List \labelwidthstring 00.00.0000 t dbus.UInt64 \end_layout \begin_layout List \labelwidthstring 00.00.0000 u dbus.UInt32 \end_layout \begin_layout List \labelwidthstring 00.00.0000 x dbus.Int64, long \end_layout \begin_layout List \labelwidthstring 00.00.0000 y dbus.Byte \end_layout \begin_layout Standard DBus also supports for container types. \end_layout \begin_layout List \labelwidthstring 00.00.0000 ax dbus.Array, list - a is an array and the x is the type that is used. X here means it is an array of dbus.UInt64/long \end_layout \begin_layout List \labelwidthstring 00.00.0000 ay dbus.ByteArray, str - Is a more effienct array \end_layout \begin_layout List \labelwidthstring 00.00.0000 (types) dbus.Struct, tuple - The signature of is either None or a string representing the contents of the struct. The signature '(iis)' would be used for two integers and a string. \end_layout \begin_layout List \labelwidthstring 00.00.0000 a{xy} dbus.Dictionary, dict - a is the key and y is the value. So a{si} would be a dictionary with strings for keys and integers for values. \end_layout \begin_layout List \labelwidthstring 00.00.0000 v variants \end_layout \begin_layout Section Summary \end_layout \begin_layout Standard Although you are probably not a DBus expert from this chapter, it should have given you a good enough understanding to start accessing other application s and add some basic support to your own application. What you need to do is experiment a little and make sure that you fully understand DBus and maybe read up on it a little more. \end_layout \begin_layout Standard Some other resources that you may want to check out are: \end_layout \begin_layout Itemize \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html \end_layout \end_inset \end_layout \begin_layout Itemize \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://dbus.freedesktop.org/doc/dbus-python/ \end_layout \end_inset \end_layout \begin_layout Itemize \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://dbus.freedesktop.org/doc/dbus-python/api/index.html \end_layout \end_inset \end_layout \begin_layout Itemize \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www-128.ibm.com/developerworks/linux/library/l-dbus.html \end_layout \end_inset \end_layout \begin_layout Itemize \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.madsoft.org/2008/06/10/interfacing-banshee-10-with-dbus-and-python/ \end_layout \end_inset \end_layout \begin_layout Itemize \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://mumble.sourceforge.net/DBus \end_layout \end_inset \end_layout \begin_layout Chapter Clutter \end_layout \begin_layout Standard \begin_inset Box Frameless position "t" hor_pos "c" has_inner_box 1 inner_pos "t" use_parbox 0 width "100col%" special "none" height "1in" height_special "totalheight" status open \begin_layout Plain Layout \begin_inset CommandInset include LatexCommand input filename "chapter-heading.lyx" \end_inset \end_layout \end_inset \end_layout \begin_layout Section Introduction \end_layout \begin_layout Standard Note: This chapter is about pyclutter 1.0 and no longer contains information on older versions. \end_layout \begin_layout Standard The best way to describe clutter is to quote its home page which states: \end_layout \begin_layout Standard Clutter is an open source software library for creating fast, visually rich and animated graphical user interfaces. \end_layout \begin_layout Standard Clutter can be integrated with many Linux technologies including GStreamer, Cairo a GTK+. It also is portable and runs on Windows and OSX which allows for cross platform goodness. \end_layout \begin_layout Standard But how is clutter used? This is actually very simple. Instead of creating a gtk.Window as with using PyGTK, with clutter a clutter.Stag e is created. And instead of using widgets, Actors are used. This is actually rather neat. We have Stages on which to do our work and Actors that perform. \end_layout \begin_layout Standard Some base Actors included with clutter are: \end_layout \begin_layout Itemize Texture \end_layout \begin_layout Itemize CloneTexture \end_layout \begin_layout Itemize Text - For all things text. Replaces Label and Entry \end_layout \begin_layout Itemize Rectangle - For creating Rectangles \end_layout \begin_layout Itemize Label - displaying Labels (deprecated as of 1.0) \end_layout \begin_layout Itemize Entry - For entering text (deprecated as of 1.0) \end_layout \begin_layout Standard A stage is created by using the clutter.Stage object like so: \end_layout \begin_layout LyX-Code stage = clutter.Stage() \end_layout \begin_layout Standard The size of stage can be set: \end_layout \begin_layout LyX-Code stage.set_size(800, 400) \end_layout \begin_layout Standard The title of the stage can be set using the stage.set_title() method: \end_layout \begin_layout LyX-Code stage.set_title( \begin_inset Quotes eld \end_inset Hey Hey, My First Clutter App \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout Standard To make sure that a clutter application is shut down properly make sure to add \end_layout \begin_layout LyX-Code stage.connect('destroy', clutter.main_quit) \end_layout \begin_layout Section Colors \end_layout \begin_layout Standard The colour of a stage can be set using set_color() method and using the clutter.color_from_string() \begin_inset Foot status collapsed \begin_layout Plain Layout Formally clutter.color_parse(...) \end_layout \end_inset method. The clutter parse method can take several different colour inputs including the colours as Text or RGB Notation. \end_layout \begin_layout Standard The colour of a stage can be set: \end_layout \begin_layout LyX-Code stage.set_color(clutter.color_from_string( \begin_inset Quotes eld \end_inset red \begin_inset Quotes erd \end_inset )) \end_layout \begin_layout LyX-Code stage.set_color(clutter.Color(255, 0, 0)) \end_layout \begin_layout Standard Colours can be applied to more then just stages, they may also be applied to all the Actors that will be shown in the next section. \end_layout \begin_layout Section User Input \end_layout \begin_layout Subsection Keyboard \end_layout \begin_layout Standard It is very easy to catch user input. For catching keyboard events the stage must connect the \begin_inset Quotes eld \end_inset key-press-event \begin_inset Quotes erd \end_inset to a handler. \end_layout \begin_layout LyX-Code # key-press-event is for the keyboard \end_layout \begin_layout LyX-Code stage.connect( \begin_inset Quotes eld \end_inset key-press-event \begin_inset Quotes erd \end_inset , on_key_press_event) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_key_press_event(self, stage, event): \end_layout \begin_layout LyX-Code # event has the following: \end_layout \begin_layout LyX-Code # event.hardware_keycode \end_layout \begin_layout LyX-Code # event.keyval - ascii (or unicode) value of the key \end_layout \begin_layout LyX-Code # event.modifier_state \end_layout \begin_layout LyX-Code # event.put \end_layout \begin_layout LyX-Code # event.source \end_layout \begin_layout LyX-Code # event.time \end_layout \begin_layout LyX-Code # event.type \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code print \begin_inset Quotes eld \end_inset keyval \begin_inset Quotes eld \end_inset , event.keyval \end_layout \begin_layout LyX-Code try: \end_layout \begin_layout LyX-Code print \begin_inset Quotes eld \end_inset key pressed \begin_inset Quotes eld \end_inset , chr(event.keyval) \end_layout \begin_layout LyX-Code except ValueError: \end_layout \begin_layout LyX-Code print \begin_inset Quotes eld \end_inset Key pressed not recognized ascii character. \begin_inset Quotes erd \end_inset \end_layout \begin_layout LyX-Code print \begin_inset Quotes eld \end_inset Returning from key pressed function \begin_inset Quotes erd \end_inset \end_layout \begin_layout LyX-Code return \end_layout \begin_layout Standard What is probably going to be the most useful when handling a key press event is the character pressed. The event.keval can be converted using the builtin function \emph on chr \emph default . For example in ascii 97 is a. So if we press a and have the following code \end_layout \begin_layout LyX-Code print chr(event.keyval) \end_layout \begin_layout Standard an \begin_inset Quotes eld \end_inset a \begin_inset Quotes erd \end_inset will be printed to the screen. \end_layout \begin_layout Subsection Mouse \end_layout \begin_layout Standard Catching input from a mouse is also very easy and all that needs to be done is to handle the \begin_inset Quotes eld \end_inset button-press-event \begin_inset Quotes erd \end_inset . The handler function takes two arguments \begin_inset Quotes eld \end_inset stage \begin_inset Quotes erd \end_inset and \begin_inset Quotes eld \end_inset event \begin_inset Quotes erd \end_inset and is handled like so: \end_layout \begin_layout LyX-Code stage.connect( \begin_inset Quotes eld \end_inset button-press-event \begin_inset Quotes erd \end_inset , on_button_press_event) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_button_press_event (stage, event): \end_layout \begin_layout LyX-Code #mouse button \end_layout \begin_layout LyX-Code #event.button - 1 left click \end_layout \begin_layout LyX-Code # - 3 right click \end_layout \begin_layout LyX-Code # - 2 left and right clicked same time \end_layout \begin_layout LyX-Code # \end_layout \begin_layout LyX-Code # event.x - X Coordinate of button press \end_layout \begin_layout LyX-Code # event.y - Y Coordinate of button press \end_layout \begin_layout LyX-Code # \end_layout \begin_layout LyX-Code print \begin_inset Quotes eld \end_inset mouse button %d pressed at (%d, %d) \begin_inset Quotes erd \end_inset % (event.button, event.x, event.y) \end_layout \begin_layout Section Actors \end_layout \begin_layout Subsection Text \begin_inset CommandInset label LatexCommand label name "sub:clutter - Actor - Text" \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout clutter!Actor!Text \end_layout \end_inset \end_layout \begin_layout LyX-Code class clutter.Text(clutter.Actor) \end_layout \begin_layout LyX-Code get_text() \end_layout \begin_layout LyX-Code set_text(text) - Sets the text in the entry \end_layout \begin_layout LyX-Code set_editable(Boolean) \end_layout \begin_layout LyX-Code set_reactive(Boolean) \end_layout \begin_layout LyX-Code set_position(xPos, yPos) \end_layout \begin_layout LyX-Code ... \end_layout \begin_layout LyX-Code \end_layout \begin_layout Standard You can set text in it using the set_text(text) method and get text using the get_text() method. If you want to use the Text actor as a label you would set it to \emph on set_editable(False) \emph default . Here is an example with a Text actor being created and added to a stage. You will have to click on the Text actor to be able to type in it, just like a normal gtk or windows text field. \end_layout \begin_layout LyX-Code class EntryExample: \end_layout \begin_layout LyX-Code def __init__(self): \end_layout \begin_layout LyX-Code self.stage = clutter.Stage() \end_layout \begin_layout LyX-Code self.stage.set_size(400, 400) \end_layout \begin_layout LyX-Code self.stage.set_color(clutter.color_from_string("red")) \end_layout \begin_layout LyX-Code self.text = clutter.Text() \end_layout \begin_layout LyX-Code self.text.set_text("Text Entry") \end_layout \begin_layout LyX-Code self.text.set_color(clutter.color_parse("green")) \end_layout \begin_layout LyX-Code self.text.set_size(150, 50) \end_layout \begin_layout LyX-Code self.text.set_position(200, 200) \end_layout \begin_layout LyX-Code self.text.set_reactive(True) \end_layout \begin_layout LyX-Code self.text.set_editable(True) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.text.connect("button-press-event", \end_layout \begin_layout LyX-Code self.on_mouse_press_event) \end_layout \begin_layout LyX-Code self.text.connect("key-press-event", \end_layout \begin_layout LyX-Code self.on_key_press_event) \end_layout \begin_layout LyX-Code self.stage.connect('destroy', \end_layout \begin_layout LyX-Code clutter.main_quit) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.stage.add(self.text) \end_layout \begin_layout LyX-Code self.stage.show_all() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_mouse_press_event(self, actor, event): \end_layout \begin_layout LyX-Code self.stage.set_key_focus(self.text) \end_layout \begin_layout LyX-Code return False \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_key_press_event(self, actor, event): \end_layout \begin_layout LyX-Code print "Text Actor is: ", actor.get_text() \end_layout \begin_layout LyX-Code print "Key pressed is: ", unichr(event.keyval) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if __name__ == "__main__": \end_layout \begin_layout LyX-Code app = EntryExample() \end_layout \begin_layout LyX-Code clutter.main() \end_layout \begin_layout LyX-Code \end_layout \begin_layout Standard As can be seen in the code above the stage color is set to red, the text color in the Text actor is set to green. The text actor is set to have width of 150 and a height of 50. The position of the Text actor on the stage is set to x 200 y 200. The actor is also set to be editable and reactive. Simple enough. \end_layout \begin_layout Standard Three call back functions are added. One callback for when a mouse button is pressed over the Text actor. One callback event for when a key is pressed on the keyboard while the focus is in the Text actor. Finally the last callback event is for the stage destroy signal which is connected above to \emph on clutter.main_quit \emph default to insure the program exits properly. \end_layout \begin_layout Subsection Rectangles \end_layout \begin_layout LyX-Code class clutter.Rectangle(clutter.Actor): \end_layout \begin_layout LyX-Code clutter.Rectangle(color=None) \end_layout \begin_layout LyX-Code get_color() \end_layout \begin_layout LyX-Code set_color(color) \end_layout \begin_layout LyX-Code get_border_color() \end_layout \begin_layout LyX-Code set_border_color(color) \end_layout \begin_layout LyX-Code get_border_width() \end_layout \begin_layout LyX-Code set_border_width(width) \end_layout \begin_layout Standard Creating rectangles does not entail much. Basically you just call the clutter.Rectangle class and you are done. Of course you will probably want to do more to it then that and add it to a stage. As can be seen in the class outline of a clutter.Rectangle above there is not much to work with in and of themselves making them easy to work with. \end_layout \begin_layout Standard Setting a border width on a Rectangle will increase its size bye the border size multiplied by 2. So if your rectangle is set to 200 wide and you add a border of 20 you end up with a rectangle that is 240 wide. \end_layout \begin_layout Subsection Textures \end_layout \begin_layout LyX-Code class clutter.Texture(Actor) \end_layout \begin_layout LyX-Code set_area_from_rgb_data(data, has_alpha, x, y, width, height, rowstride, bpp, flags, error) \end_layout \begin_layout LyX-Code - Updates a sub-region of the pixel data in a Texture. \end_layout \begin_layout LyX-Code set_from_rgb_data(data, has_alpha, width, height, rowstride, bpp, flags, error) \end_layout \begin_layout LyX-Code - Sets the Texture from RBG data. \end_layout \begin_layout LyX-Code set_from_yuv_data(data, width, height, flags, error) \end_layout \begin_layout LyX-Code - Sets the Texture from a YUV image data. \end_layout \begin_layout LyX-Code set_from_file(filename, error) - obvious \end_layout \begin_layout LyX-Code set_filter_quality(filter_quality) \end_layout \begin_layout LyX-Code ... \end_layout \begin_layout Standard Loading an image file and setting it up as a texture is very simple. \end_layout \begin_layout LyX-Code purple_flower = clutter.Texture(filename= \begin_inset Quotes erd \end_inset flower.jpg \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout Standard Look at that, only one line of code and we have a texture that is ready to be used in our pyclutter program. Now all that needs to be done is add the purple_flower to the stage. \end_layout \begin_layout LyX-Code import clutter \end_layout \begin_layout LyX-Code stage = clutter.Stage() \end_layout \begin_layout LyX-Code stage.set_size(400, 400) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code purple_flower = clutter.Texture(filename="flower.jpg") \end_layout \begin_layout LyX-Code (width, height) = purple_flower.get_size() \end_layout \begin_layout LyX-Code stage.add(purple_flower) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code stage.show_all() \end_layout \begin_layout LyX-Code stage.connect('destroy', clutter.main_quit) \end_layout \begin_layout LyX-Code clutter.main() \end_layout \begin_layout Subsubsection Cloning a textue \end_layout \begin_layout LyX-Code class clutter.CloneTexture(Actor) \end_layout \begin_layout LyX-Code get_parent_texture() \end_layout \begin_layout LyX-Code set_parent_texture(Texture) \end_layout \begin_layout Standard Cloning a texture is as easy to create as the original texture. \end_layout \begin_layout LyX-Code # Create the original Texture \end_layout \begin_layout LyX-Code purple_flower = clutter.Texture(filename= \begin_inset Quotes erd \end_inset flower.jpg \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Create the cloned Texture \end_layout \begin_layout LyX-Code clone = clutter.CloneTexture(purple_flower) \end_layout \begin_layout Standard And it is that simple. Now the actual work starts with making the texture do what it is supposed to be doing. It should probably be properly placed, maybe have some behaviours (this is discussed later), setting up time lines. \end_layout \begin_layout Standard Lets make the Texture and the cloned texture show up on the stage now. \end_layout \begin_layout LyX-Code clone.set_position(200, 200) \end_layout \begin_layout LyX-Code stage.add(purple_flower, clone) \end_layout \begin_layout LyX-Code stage.show_all() \end_layout \begin_layout LyX-Code stage.connect('destroy', clutter.main_quit) \end_layout \begin_layout Standard Now the the original texture is in the upper most left corner of the stage and the clone is displayed at coordinates x 200 and y 200. \end_layout \begin_layout LyX-Code import clutter \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def create_texture(fName): \end_layout \begin_layout LyX-Code image = clutter.Texture(filename=fName) \end_layout \begin_layout LyX-Code (width, height) = image.get_size() \end_layout \begin_layout LyX-Code return image \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code stage = clutter.Stage() \end_layout \begin_layout LyX-Code stage.set_size(400, 400) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Create the original Texture from a picture of a flower \end_layout \begin_layout LyX-Code purple_flower = create_texture("flower.jpg") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Create a clone of the origial Texture cloned_flower = clutter.CloneTexture(purp le_flower) \end_layout \begin_layout LyX-Code cloned_flower.set_position(200, 200) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code stage.add(purple_flower, cloned_flower) stage.show_all() stage.connect('destroy', clutter.main_quit) \end_layout \begin_layout LyX-Code clutter.main() \end_layout \begin_layout Subsection Labels \end_layout \begin_layout Standard Labels have been deprecated and as of pyclutter 1.0 you they should not be used. Instead the Text actor should be used and will be demonstrated here. To see more about the Text actor please see \begin_inset CommandInset ref LatexCommand vref reference "sub:clutter - Actor - Text" \end_inset . \end_layout \begin_layout LyX-Code class clutter.Text \end_layout \begin_layout LyX-Code set_text(text) \end_layout \begin_layout LyX-Code set_editable(Boolean) \end_layout \begin_layout LyX-Code set_color(color) \end_layout \begin_layout LyX-Code .... \end_layout \begin_layout LyX-Code \end_layout \begin_layout Standard So here is the example of use Text as a label. Bascially all that is being done is using the method \emph on set_editable(False) \emph default to make sure users cannot change the text. \end_layout \begin_layout LyX-Code import clutter \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code stage = clutter.Stage() \end_layout \begin_layout LyX-Code stage.set_size(400, 400) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code label = clutter.Text() \end_layout \begin_layout LyX-Code label.set_editable(False) \end_layout \begin_layout LyX-Code label.set_text("Clutter Label Text") \end_layout \begin_layout LyX-Code label.set_color(clutter.color_from_string("brown")) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # If no position is given it defaults to the upper most left corner. \end_layout \begin_layout LyX-Code stage.add(label) \end_layout \begin_layout LyX-Code stage.show_all() \end_layout \begin_layout LyX-Code stage.connect('destroy', clutter.main_quit) \end_layout \begin_layout LyX-Code clutter.main() \end_layout \begin_layout LyX-Code \end_layout \begin_layout Section Animations \end_layout \begin_layout Subsection Timelines \begin_inset CommandInset label LatexCommand label name "sub:clutter - Timelines" \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout clutter!Timeline \end_layout \end_inset \end_layout \begin_layout LyX-Code class clutter.Timeline: \end_layout \begin_layout LyX-Code __init__(duration) \end_layout \begin_layout LyX-Code fps - Frames per second \end_layout \begin_layout LyX-Code num_frames - The total number of frames \end_layout \begin_layout LyX-Code duration - The duration of the timeline, in milliseconds \end_layout \begin_layout LyX-Code def get_duration() \end_layout \begin_layout LyX-Code def set_duration(msecs) \end_layout \begin_layout LyX-Code def get_direction() - retrieves the direction of the timeline, either forward or backward. \end_layout \begin_layout LyX-Code def set_direction() \end_layout \begin_layout LyX-Code def get_loop() \end_layout \begin_layout LyX-Code def set_loop(True or False) - Set the timeline to loop \end_layout \begin_layout LyX-Code def get_progress() \end_layout \begin_layout LyX-Code def start() - Start the timeline \end_layout \begin_layout LyX-Code def pause() - Pause the timeline \end_layout \begin_layout LyX-Code def stop() \end_layout \begin_layout LyX-Code def rewind() \end_layout \begin_layout Standard To use a time line you will want to add it to a clutter actor. The timeline is setup and then the animation effect that it is to be applied to it. \end_layout \begin_layout Standard So lets create a time line with a duration of 3000(3 seconds). \end_layout \begin_layout LyX-Code timeline = clutter.Timeline(duration=3000) \end_layout \begin_layout Standard Next we will set the timeline to loop using the set_loop() method. This sets the timeline to loop once it has finished each run through. \end_layout \begin_layout LyX-Code timeline.set_loop(True) \end_layout \begin_layout LyX-Code \end_layout \begin_layout Subsection Alpha \begin_inset CommandInset label LatexCommand label name "sub:clutter - Alpha" \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout clutter!Alpha \end_layout \end_inset \end_layout \begin_layout Standard \begin_inset Note Note status collapsed \begin_layout Plain Layout TODO: Add information about the different things that can be down with alpha \end_layout \begin_layout Plain Layout from \end_layout \begin_layout Plain Layout http://clutter-project.org/docs/clutter/0.6/clutter-animation-behaviours.html \end_layout \end_inset \end_layout \begin_layout LyX-Code class clutter.Alpha(goject.GObject) \end_layout \begin_layout LyX-Code clutter.Alpha(timeline, func, data) \end_layout \begin_layout LyX-Code def get_alpha() \end_layout \begin_layout LyX-Code def get_timeline() \end_layout \begin_layout LyX-Code def set_func(func) \end_layout \begin_layout LyX-Code def set_timeline(timeline) \end_layout \begin_layout Standard Next if you want to apply an animation you will want to setup the effect that is going to happen to your chosen object. So what is going to happen now is to create a clutter.Alpha object. Then create a clutter.BehaviourOpacity object using our just created alpha and apply this action to our rectangle. Then start the timeline running which will start the animation. \end_layout \begin_layout LyX-Code alpha = clutter.Alpha(timeline, clutter.EASE_IN_OUT_BOUNCE) \end_layout \begin_layout LyX-Code behaviour = clutter.BehaviourOpacity(0xdd, 0, alpha) \end_layout \begin_layout LyX-Code behaviour.apply(rect) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # start the timeline running, thus starting the animation \end_layout \begin_layout LyX-Code timeline.start() \end_layout \begin_layout Standard The timeline can be setup anywhere and then started at any time using the timeline.start() method and stopped with the timeline.stop() method. \end_layout \begin_layout Standard If we put all this together we get a working application that is only a few lines long. \end_layout \begin_layout Standard What is needed to be known about clutter.Alpha is that it is a function of time not pixel form of alpha. \end_layout \begin_layout Standard There are many predefined Clutter.Alpha functions that can be used with the clutter.Alpha class to effect the behaviour of the timeline. You will just have to experiment with them to see what suits your needs. \end_layout \begin_layout Itemize clutter.EASE_IN_OUT_BOUNCE \end_layout \begin_layout Itemize clutter.EASE_IN_BACK \end_layout \begin_layout Itemize clutter.EASE_IN_BOUNCE \end_layout \begin_layout Itemize clutter.EASE_IN_CIRC \end_layout \begin_layout Itemize clutter.EASE_IN_CUBIC \end_layout \begin_layout Itemize clutter.EASE_IN_ELASTIC \end_layout \begin_layout Itemize clutter.EASE_IN_EXPO \end_layout \begin_layout Itemize clutter.EASE_IN_OUT_BACK \end_layout \begin_layout Itemize clutter.EASE_IN_OUT_CIRC \end_layout \begin_layout Itemize clutter.EASE_IN_OUT_CUBIC \end_layout \begin_layout Itemize clutter.EASE_IN_OUT_ELASTIC \end_layout \begin_layout Itemize clutter.EASE_IN_OUT_EXPO \end_layout \begin_layout Itemize clutter.EASE_IN_OUT_QUAD \end_layout \begin_layout Itemize clutter.EASE_IN_OUT_QUART \end_layout \begin_layout Itemize clutter.EASE_IN_OUT_QUINT \end_layout \begin_layout Itemize clutter.EASE_IN_OUT_SINE \end_layout \begin_layout Itemize clutter.EASE_IN_QUAD \end_layout \begin_layout Itemize clutter.EASE_IN_QUART \end_layout \begin_layout Itemize clutter.EASE_IN_QUINT \end_layout \begin_layout Itemize clutter.EASE_IN_SINE \end_layout \begin_layout Itemize clutter.EASE_OUT_BACK \end_layout \begin_layout Itemize clutter.EASE_OUT_BOUNCE \end_layout \begin_layout Itemize clutter.EASE_OUT_CIRC \end_layout \begin_layout Itemize clutter.EASE_OUT_CUBIC \end_layout \begin_layout Itemize clutter.EASE_OUT_ELASTIC \end_layout \begin_layout Itemize clutter.EASE_OUT_EXPO \end_layout \begin_layout Itemize clutter.EASE_OUT_QUAD \end_layout \begin_layout Itemize clutter.EASE_OUT_QUART \end_layout \begin_layout Itemize clutter.EASE_OUT_QUINT \end_layout \begin_layout Itemize clutter.EASE_OUT_SINE \end_layout \begin_layout Subsection BehaviourOpacity \begin_inset CommandInset label LatexCommand label name "sub:clutter - BehaviourOpacity" \end_inset \begin_inset Index status open \begin_layout Plain Layout clutter!BehaviourOpacity \end_layout \end_inset \end_layout \begin_layout Standard Please see the section \begin_inset CommandInset ref LatexCommand vref reference "sub:clutter - Alpha" \end_inset before reading this section. Using Behaviour Opacity \end_layout \begin_layout LyX-Code import clutter \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code class Blinker: \end_layout \begin_layout LyX-Code def __init__(self): \end_layout \begin_layout LyX-Code self.stage = clutter.Stage() \end_layout \begin_layout LyX-Code self.stage.set_color(clutter.color_from_string( \begin_inset Quotes eld \end_inset red \begin_inset Quotes erd \end_inset )) \end_layout \begin_layout LyX-Code self.stage.set_size(400, 400) \end_layout \begin_layout LyX-Code self.stage.set_title( \begin_inset Quotes eld \end_inset My Blinking (BehaviourOpacity) Rectangle Example \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.rect = clutter.Rectangle() \end_layout \begin_layout LyX-Code self.rect.set_color(clutter.color_from_string( \begin_inset Quotes eld \end_inset green \begin_inset Quotes erd \end_inset )) \end_layout \begin_layout LyX-Code self.rect.set_size(200, 200) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code rect_xpos = self.stage.get_width() / 4 \end_layout \begin_layout LyX-Code rect_ypos = self.stage.get_height() / 4 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.rect.set_position(rect_xpos, rect_ypos) \end_layout \begin_layout LyX-Code self.timeline = clutter.Timeline(duration=3000) \end_layout \begin_layout LyX-Code self.timeline.set_loop(True) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code alpha = clutter.Alpha(self.timeline, clutter.EASE_IN_OUT_SINE) \end_layout \begin_layout LyX-Code self.behaviour = clutter.BehaviourOpacity(alpha=alpha , opacity_start=0xdd , opacity_end=0) \end_layout \begin_layout LyX-Code self.behaviour.apply(self.rect) \end_layout \begin_layout LyX-Code self.timeline.start() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.stage.add(self.rect) \end_layout \begin_layout LyX-Code self.stage.show_all() \end_layout \begin_layout LyX-Code self.stage.connect('destroy', clutter.main_quit) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if __name__ == \begin_inset Quotes eld \end_inset __main__ \begin_inset Quotes erd \end_inset : \end_layout \begin_layout LyX-Code app = Blinker() \end_layout \begin_layout LyX-Code clutter.main() \end_layout \begin_layout Subsection BehaviourRotate \begin_inset CommandInset label LatexCommand label name "sub:clutter - BehaviourRotate" \end_inset \begin_inset Index status open \begin_layout Plain Layout clutter!BehaviourRotate \end_layout \end_inset \end_layout \begin_layout LyX-Code class clutter.BehaviourRotate(Behaviour) \end_layout \begin_layout LyX-Code clutter.BehaviourRotate(alpha(optional), angle_end, angle_start) \end_layout \begin_layout LyX-Code def get_axis() \end_layout \begin_layout LyX-Code def set_axis(axis) \end_layout \begin_layout LyX-Code -clutter.Z_AXIS \end_layout \begin_layout LyX-Code -clutter.Y_AXIS \end_layout \begin_layout LyX-Code -clutter.X_AXIS \end_layout \begin_layout LyX-Code get_bounds(angle_start, angle_end) \end_layout \begin_layout LyX-Code set_bounds(angle_start, angle_end) \end_layout \begin_layout LyX-Code get_center(x, y, z) \end_layout \begin_layout LyX-Code set_center(x, y, z) \end_layout \begin_layout LyX-Code get_direction() \end_layout \begin_layout LyX-Code set_direction(direction) \end_layout \begin_layout LyX-Code ... \end_layout \begin_layout Standard The clutter.BehaviourRotate class allows you to set a rotate behaviour on an a chosen actor. \end_layout \begin_layout Standard The example that is to follow will create a Rectangle and add it to a stage and then we will create a time line. The timeline will control a BehaviourRotate that will affect the Rectangle (though any Actor will do). \end_layout \begin_layout Standard At this point I will assume you know how to create a rectangle and add it to a stage, so from this point out I will just focus on the Behaviours. \end_layout \begin_layout Standard So we have to create a timeline that will control the behaviour. \end_layout \begin_layout LyX-Code timeline = clutter.Timeline(duration=3000) \end_layout \begin_layout LyX-Code timeline.set_loop(True) \end_layout \begin_layout LyX-Code alpha = clutter.Alpha(timeline, clutter.EASE_IN_OUT_SINE) \end_layout \begin_layout Standard Now that the timeline that is going to be used with the behaviour has been created we can create the behaviour itself. You will should notice that the axis is set to clutter.Z_AXIS, also available are clutter.X_AXIS and clutter.Y_AXIS. \end_layout \begin_layout LyX-Code rotate_behaviour = clutter.BehaviourRotate(axis=clutter.Z_AXIS, angle_start=0.0, angle_end=359.0) \end_layout \begin_layout LyX-Code rotate_behaviour.set_alpha(alpha) \end_layout \begin_layout LyX-Code rotate_behaviour.apply(rect) \end_layout \begin_layout Standard So a instance of BehaviourRotate is created, rotating on the z axis, using the alpa timeline created above and this is applied to the rectangle instance rect. \end_layout \begin_layout LyX-Code import clutter \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code stage = clutter.Stage() \end_layout \begin_layout LyX-Code stage.set_size(400, 400) \end_layout \begin_layout LyX-Code rect = clutter.Rectangle() \end_layout \begin_layout LyX-Code rect.set_color(clutter.color_from_string("red")) \end_layout \begin_layout LyX-Code rect.set_size(100, 100) rect.set_position(150, 150) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code timeline = clutter.Timeline(duration=3000) \end_layout \begin_layout LyX-Code timeline.set_loop(True) \end_layout \begin_layout LyX-Code alpha = clutter.Alpha(timeline, clutter.EASE_IN_OUT_SINE) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code rotate_behaviour = clutter.BehaviourRotate( \end_layout \begin_layout LyX-Code axis=clutter.Z_AXIS, angle_start=0.0, angle_end=359.0) \end_layout \begin_layout LyX-Code rotate_behaviour.set_alpha(alpha) \end_layout \begin_layout LyX-Code rotate_behaviour.apply(rect) \end_layout \begin_layout LyX-Code timeline.start() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code stage.add(rect) \end_layout \begin_layout LyX-Code stage.show_all() \end_layout \begin_layout LyX-Code stage.connect('destroy', clutter.main_quit) \end_layout \begin_layout LyX-Code clutter.main() \end_layout \begin_layout Subsection BehaviourScale - Not Finished \begin_inset CommandInset label LatexCommand label name "sub:clutter-BehaviourScale" \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout clutter!BehaviourScale \end_layout \end_inset \end_layout \begin_layout LyX-Code class clutter.BehaviourScale(Behaviour) \end_layout \begin_layout LyX-Code clutter.BehaviourScale(x_scale_start, y_scale_start, x_scale_end, y_scale_end , alpha(optional) ) \end_layout \begin_layout LyX-Code def get_bounds() \end_layout \begin_layout LyX-Code def set_bounds(x_scale_begin, y_scale_begin, x_scale_end, y_scale_end) \end_layout \begin_layout Subsection BehaviourDepth \begin_inset CommandInset label LatexCommand label name "sub:clutter - BehaviourDepth" \end_inset \begin_inset Index status open \begin_layout Plain Layout clutter-BehaviourDepth \end_layout \end_inset \end_layout \begin_layout LyX-Code class clutter.BehaviourDepth \end_layout \begin_layout LyX-Code clutter.BehaviourDepth(depth_start, depth_end) \end_layout \begin_layout LyX-Code def set_bounds(depth_start, depth_end) \end_layout \begin_layout LyX-Code def get_bounds(depth_start, depth_end) \end_layout \begin_layout Standard BehaviourDepth works like the other behaviours that have been discussed above. You create your behaviour specifying your desired action and attach it to a timeline. \end_layout \begin_layout Standard With BehaviourDepth the only options (and required) are the start depth and end depth. Play around with them a little to get your desired result. \end_layout \begin_layout LyX-Code timeline = clutter.Timeline(duration=6000) \end_layout \begin_layout LyX-Code timeline.set_loop(True) \end_layout \begin_layout LyX-Code alpha = clutter.Alpha(timeline, clutter.EASE_IN_OUT_SINE) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code rotate_behaviour = clutter.BehaviourDepth(0, 250) \end_layout \begin_layout LyX-Code rotate_behaviour.set_alpha(alpha) \end_layout \begin_layout LyX-Code rotate_behaviour.apply(rect) \end_layout \begin_layout Standard All you have to do to start the required behaviour is start the time line and watch the results. \end_layout \begin_layout LyX-Code import clutter \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code stage = clutter.Stage() \end_layout \begin_layout LyX-Code stage.set_size(400, 400) \end_layout \begin_layout LyX-Code rect = clutter.Rectangle() \end_layout \begin_layout LyX-Code rect.set_color(clutter.color_from_string("red")) \end_layout \begin_layout LyX-Code rect.set_size(100, 100) rect.set_position(150, 150) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code timeline = clutter.Timeline(duration=6000) \end_layout \begin_layout LyX-Code timeline.set_loop(True) alpha = clutter.Alpha( \end_layout \begin_layout LyX-Code timeline, clutter.EASE_OUT_BOUNCE) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code rotate_behaviour = clutter.BehaviourDepth(0, 250) \end_layout \begin_layout LyX-Code rotate_behaviour.set_alpha(alpha) \end_layout \begin_layout LyX-Code rotate_behaviour.apply(rect) \end_layout \begin_layout LyX-Code timeline.start() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code stage.add(rect) \end_layout \begin_layout LyX-Code stage.show_all() \end_layout \begin_layout LyX-Code stage.connect('destroy', clutter.main_quit) \end_layout \begin_layout LyX-Code clutter.main() \end_layout \begin_layout Section Groups and Positioning \begin_inset CommandInset label LatexCommand label name "sec:clutter - Groups-and-Positioning" \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout clutter!Group \end_layout \end_inset \end_layout \begin_layout Standard Groups allow the programmer to group together many actors. Instead of the Actor references the colors and position of the stage, they reference off of the group that they are in. Groups also allow for relative positioning, as in the positions of Actors are placed relative to their parent Group. \end_layout \begin_layout Standard For example if you have a Rectangle and it is inside Group when you set the position of the rectangle to (100, 100), it is relative to the position of the group. So in the following snippet of code, the Rectangle is is being set to (100, 100) in the Group but relative to the stage it is set to (200, 200) \end_layout \begin_layout LyX-Code group = clutter.Group() \end_layout \begin_layout LyX-Code group.set_position(100, 100) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code rect = clutter.Rectangle() \end_layout \begin_layout LyX-Code rect.set_size(100, 100) \end_layout \begin_layout LyX-Code rect.set_position(100, 100) \end_layout \begin_layout LyX-Code group.add(rect) \end_layout \begin_layout Standard \begin_inset Branch SecondEdition status open \begin_layout Section Embed clutter in PyGTK - Not Finished \end_layout \begin_layout LyX-Code import clutter \end_layout \begin_layout LyX-Code from clutter import cluttergtk \end_layout \begin_layout LyX-Code You may also want to take a look at these two files for more examples of using pygtk and clutter together. \end_layout \begin_layout LyX-Code https://fedorahosted.org/fedora-tour/browser/frontend1/FedoraTourGtk.py \end_layout \begin_layout LyX-Code https://fedorahosted.org/fedora-tour/browser/frontend1/splash_gtk.py \end_layout \end_inset \end_layout \begin_layout Section Summary \end_layout \begin_layout Standard For more information about clutter you can visit its web site at: \end_layout \begin_layout Standard \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.clutter-project.org \end_layout \end_inset \end_layout \begin_layout Standard For more information on the pyclutter api you can download the pyclutter source from the clutter project site and view the documentation or load the documentation in a python console using: \end_layout \begin_layout LyX-Code import clutter \end_layout \begin_layout LyX-Code help(clutter) \end_layout \begin_layout Standard Also for each section of pyclutter covered in this chapter full source examples are on the books website. You are encouraged to download and inspect these and expand upon them to further learn how to best use clutter for your own projects. The address for these downloads is \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.majorsilence.com/rubbish/pygtk-book/examples/ \end_layout \end_inset . \end_layout \begin_layout Chapter Embedded Web Browsers \end_layout \begin_layout Standard \begin_inset Box Frameless position "t" hor_pos "c" has_inner_box 1 inner_pos "t" use_parbox 0 width "100col%" special "none" height "1in" height_special "totalheight" status open \begin_layout Plain Layout \begin_inset CommandInset include LatexCommand input filename "chapter-heading.lyx" \end_inset \end_layout \end_inset \end_layout \begin_layout Section Introduction \end_layout \begin_layout Standard This chapter is going to explore how a PyGTK application can have the web embedded into them. It will cover using the firefox engine gecko, webkit, and Internet Explorer. \end_layout \begin_layout Section gtkmozembed \end_layout \begin_layout Standard Using gtkmozemebed to embed mozilla firefox inside a PyGTK application is the easiest of the options in this chapter. A browser is initialized and added to the PyGTK window. \end_layout \begin_layout Standard What is needed to start off with is to import the needed python modules. \end_layout \begin_layout LyX-Code import gtk \end_layout \begin_layout LyX-Code import gtkmozembed \end_layout \begin_layout Standard Along with the browser the application will probably want some buttons to control how the web pages. Included will be a back, forward, refresh, stop, go, and home button. Also there will be a address bar. These will be connect to methods to control their actions. \end_layout \begin_layout Standard Web Browser PyGTK Init Method \end_layout \begin_layout LyX-Code class ExampleBrowser(object): \end_layout \begin_layout LyX-Code def __init__(self): \end_layout \begin_layout LyX-Code data = \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code Hello PyGTK using MozEmbed to embed a web browser. \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code win = gtk.Window() \end_layout \begin_layout LyX-Code win.set_size_request(800, 600) \end_layout \begin_layout LyX-Code win.connect("delete_event", lambda w,e: gtk.main_quit()) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code vbox = gtk.VBox(False, 0) \end_layout \begin_layout LyX-Code control_box = gtk.HBox(False, 0) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code back = gtk.Button("Back") \end_layout \begin_layout LyX-Code forward = gtk.Button("Forward") \end_layout \begin_layout LyX-Code refresh = gtk.Button("Refresh") \end_layout \begin_layout LyX-Code stop = gtk.Button("Stop") \end_layout \begin_layout LyX-Code home = gtk.Button("Home") \end_layout \begin_layout LyX-Code # no limit on address length \end_layout \begin_layout LyX-Code self.address = gtk.Entry(max=0) \end_layout \begin_layout LyX-Code go = gtk.Button("Go") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code control_box.pack_start(back, True, True, 2) \end_layout \begin_layout LyX-Code control_box.pack_start(forward, True, True, 2) \end_layout \begin_layout LyX-Code control_box.pack_start(refresh, True, True, 2) \end_layout \begin_layout LyX-Code control_box.pack_start(stop, True, True, 2) \end_layout \begin_layout LyX-Code control_box.pack_start(home, True, True, 2) \end_layout \begin_layout LyX-Code control_box.pack_start(self.address, True, True, 2) \end_layout \begin_layout LyX-Code control_box.pack_start(go, True, True, 2) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code back.connect("clicked", self.on_back_clicked, None) \end_layout \begin_layout LyX-Code forward.connect("clicked", self.on_forward_clicked, None) \end_layout \begin_layout LyX-Code refresh.connect("clicked", self.on_refresh_clicked, None) \end_layout \begin_layout LyX-Code stop.connect("clicked", self.on_stop_clicked, None) \end_layout \begin_layout LyX-Code home.connect("clicked", self.on_home_clicked, data) \end_layout \begin_layout LyX-Code self.address.connect("key_press_event", self.on_address_keypress) \end_layout \begin_layout LyX-Code go.connect("clicked", self.on_go_clicked, None) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code vbox.pack_start(control_box, False, True, 2) self.browser = gtkmozembed.MozEmbe d() \end_layout \begin_layout LyX-Code #gtkmozembed.set_profile_path("/tmp", "foobar") \end_layout \begin_layout LyX-Code vbox.add(self.browser) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code win.add(vbox) \end_layout \begin_layout LyX-Code win.show_all() \end_layout \begin_layout LyX-Code ## self.browser.load_url('http://www.pygtk.org') \end_layout \begin_layout LyX-Code self.browser.render_data(data, long(len(data)), 'file:///', 'text/html') \end_layout \begin_layout LyX-Code # Load file from file system \end_layout \begin_layout LyX-Code #self.browser.load_url('file:///path/to/file/name.html') \end_layout \begin_layout Standard This code creates a PyGTK window and creates a control box, adds some buttons to the control box and then adds it to the window. \end_layout \begin_layout Standard The import part to see though is the gtkmozembed part. \end_layout \begin_layout LyX-Code self.browser = gtkmozembed.MozEmbed() \end_layout \begin_layout LyX-Code self.browser.render_data(data, long(len(data)), 'file:///', 'text/html') \end_layout \begin_layout Standard This small piece of code initializes the web browser and leaves the an instance with the name self.browser. It then displays the message, \begin_inset Quotes eld \end_inset PyGTK using MozEmbed to embed a web browser \begin_inset Quotes erd \end_inset . \end_layout \begin_layout Standard With newly created browser it is now possible to access all the methods that are available such as going forward, backward, and entering addresses. \end_layout \begin_layout Standard Mozilla Callback Methods \end_layout \begin_layout LyX-Code def on_back_clicked(self, widget=None, data=None): \end_layout \begin_layout LyX-Code print "Back button clicked." \end_layout \begin_layout LyX-Code if self.browser.can_go_back(): \end_layout \begin_layout LyX-Code self.browser.go_back() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_forward_clicked(self, widget=None, data=None): \end_layout \begin_layout LyX-Code print "Forward button clicked." \end_layout \begin_layout LyX-Code if self.browser.can_go_forward(): \end_layout \begin_layout LyX-Code self.browser.go_forward() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_refresh_clicked(self, widget=None, data=None): \end_layout \begin_layout LyX-Code print "Refresh button clicked." \end_layout \begin_layout LyX-Code self.browser.reload(gtkmozembed.FLAG_RELOADNORMAL) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_stop_clicked(self, widget=None, data=None): \end_layout \begin_layout LyX-Code print "Stop Button Clicked." \end_layout \begin_layout LyX-Code self.browser.stop_load() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_home_clicked(self, widget=None, data=None): \end_layout \begin_layout LyX-Code print "Home Button clicked." \end_layout \begin_layout LyX-Code print "Back button only works on actual pages and not render_data" \end_layout \begin_layout LyX-Code self.browser.render_data(data, long(len(data)), 'file:///', 'text/html') \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_go_clicked(self, widget=None, data=None): \end_layout \begin_layout LyX-Code print "Go Button Clicked." \end_layout \begin_layout LyX-Code self.browser.load_url(self.address.get_text()) \end_layout \begin_layout Standard These methods should be self explanatory. For example there is the on_back_clicked method which checks if the browser is able to go back to a previous page with: \end_layout \begin_layout LyX-Code self.browser.can_go_back() \end_layout \begin_layout Standard If it passes this check, meaning there is a page to go back to, it goes back to the previous page with the following code: \end_layout \begin_layout LyX-Code self.browser.go_back() \end_layout \begin_layout Standard As can be seen with each of the signaled methods, they are just like the on_back_clicked methods. They are very easy to use and not much else to that. \end_layout \begin_layout Subsection Running a PyGTK Mozembed Application \end_layout \begin_layout Standard This section is very important if the gtkmozembed application is \shape italic seg faulting \shape default . Depending on which operating system mozembed is being used on it may very well give a \shape italic segmentation fault \shape default . This can be very frustrating to deal with if the reason is not known. However since I have run into this problem several times myself on a couple of systems I can give a few hints. \end_layout \begin_layout Standard Basically the problem is that some mozilla libraries that are needed cannot be found by the application, so to run the app it needs to be started with a shell script instead of just running the python script. \end_layout \begin_layout Subsubsection Ubuntu Gutsy \end_layout \begin_layout Standard To use gtkmozembed on Ubuntu Gutsy, the path variables LD_LIBRARY_PATH and MOZILLA_FIVE_HOME must be set. So what should done is create a shell script. For example, create with a shell script with the name mozilla_embed_start.sh and put the following code inside: \end_layout \begin_layout Standard gtkmozembed Run Script (Gutsy) \end_layout \begin_layout LyX-Code #!/bin/bash \end_layout \begin_layout LyX-Code export LD_LIBRARY_PATH=/usr/lib/firefox \end_layout \begin_layout LyX-Code export LD_MOZILLA_FIVE_HOME=/usr/lib/firefox \end_layout \begin_layout LyX-Code python your_gtkmozembed_application.py \end_layout \begin_layout Standard Now run this code in the same directory as \shape italic your_gtkmozembed_application.py \shape default file and it should run with no segmentation fault. \end_layout \begin_layout Subsubsection Ubuntu Feisty, Edgy, and Dapper \end_layout \begin_layout Standard On Ubuntu Feisty, Edgy, and Dapper the path variable LD_LIBRARY_PATH must be set to the firefox library directory. \end_layout \begin_layout Standard A shell script must also be created just like for Ubuntu Gutsy. \end_layout \begin_layout Standard gtkmozembed Script (Feisty, Edgy, Dapper) \end_layout \begin_layout LyX-Code #!/bin/bash \end_layout \begin_layout LyX-Code export LD_LIBRARY_PATH=/usr/lib/firefox \end_layout \begin_layout LyX-Code python your_gtkmozembed_application.py \end_layout \begin_layout Standard Now run this shell script in the same directory as your gtkmozembed application and it should now be running without any segmentation faults. \end_layout \begin_layout Subsubsection Other Distributions \end_layout \begin_layout Standard If you are running a different distribution of Linux or maybe a BSD the variables that need to be set may be different or the path to the firefox libraries may be different. \end_layout \begin_layout Standard From here you are on your own. I suggest you try one of the Ubuntu solutions as one of them will probably work as long as you put in the correct firefox library path. \end_layout \begin_layout Standard \begin_inset Branch SecondEdition status open \begin_layout Section webkit \end_layout \begin_layout Standard This section is not yet written :) \end_layout \begin_layout Standard http://live.gnome.org/PyWebKitGtk \end_layout \begin_layout Standard http://code.google.com/p/pywebkitgtk/ \end_layout \end_inset \end_layout \begin_layout Section Internet Explorer \end_layout \begin_layout Standard Using Internet Explorer with PyGTK is an interesting exercise that took me quite of bit of web searching before finding how to do this. Because this is not something that I need often I will be using a somewhat modified sample found on a mailing list \begin_inset Foot status open \begin_layout Plain Layout Mailing list found at at: \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.mail-archive.com/comtypes-users@lists.sourceforge.net/msg00084.html \end_layout \end_inset and a direct link to the code found is: \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.mail-archive.com/comtypes-users@lists.sourceforge.net/msg00084/ie_in_gtk.p y \end_layout \end_inset \end_layout \end_inset . \end_layout \begin_layout Standard Just to be clear this is for Microsoft Windows only. In fact the only reason I am writing this here is so I do not forget myself. I once spent several hours creating a custom application for a specific purpose that had the ability to preview output html internally for ease of use. Then only to find out that gtkmozembed is not for windows. But I needed it to run on windows and linux. So here is how I got Internet Explorer to work on windows with PyGTK. \end_layout \begin_layout Standard To start, the comtypes package will need to be installed and can be found at: \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://sourceforge.net/projects/comtypes/ \end_layout \end_inset . Also pywin32 should be installed. \end_layout \begin_layout Standard Once this has been installed Internet explorer can be taken advantage of. First start off by importing the required modules and creating the required ctypes variables. \end_layout \begin_layout LyX-Code import win32con \end_layout \begin_layout LyX-Code from ctypes import * \end_layout \begin_layout LyX-Code from ctypes.wintypes import * \end_layout \begin_layout LyX-Code from comtypes import IUnknown \end_layout \begin_layout LyX-Code from comtypes.automation import IDispatch, VARIANT \end_layout \begin_layout LyX-Code from comtypes.client import wrap \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code kernel32 = windll.kernel32 \end_layout \begin_layout LyX-Code user32 = windll.user32 \end_layout \begin_layout LyX-Code atl = windll.atl \end_layout \begin_layout Standard Of course PyGTK will need to be imported like any other GTK application. \end_layout \begin_layout LyX-Code import pygtk \end_layout \begin_layout LyX-Code pygtk.require( \begin_inset Quotes eld \end_inset 2.0 \begin_inset Quotes erd \end_inset ) \end_layout \begin_layout LyX-Code import gtk \end_layout \begin_layout Standard Now a class will be created call GUI with an __init__ method with the following code to setup the window. This will basically just be like using the gtkmozembed window. \end_layout \begin_layout LyX-Code self.home_url = "http://www.majorsilence.com/" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.win = gtk.Window(gtk.WINDOW_TOPLEVEL) \end_layout \begin_layout LyX-Code self.win.set_title("Example Webbrowser that works on Linux and Windows") self.win.co nnect("destroy", gtk.main_quit) \end_layout \begin_layout LyX-Code self.win.set_size_request(750, 550) \end_layout \begin_layout LyX-Code self.win.realize() \end_layout \begin_layout LyX-Code # Create a VBox to house the address bar and the IE control. \end_layout \begin_layout LyX-Code self.main_vbox = gtk.VBox() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code control_box = gtk.HBox(False, 0) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code back = gtk.Button("Back") \end_layout \begin_layout LyX-Code forward = gtk.Button("Forward") \end_layout \begin_layout LyX-Code refresh = gtk.Button("Refresh") \end_layout \begin_layout LyX-Code stop = gtk.Button("Stop") \end_layout \begin_layout LyX-Code home = gtk.Button("Home") \end_layout \begin_layout LyX-Code self.address = gtk.Entry(max=0) \end_layout \begin_layout LyX-Code go = gtk.Button("Go") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code control_box.pack_start(back, True, True, 2) \end_layout \begin_layout LyX-Code control_box.pack_start(forward, True, True, 2) \end_layout \begin_layout LyX-Code control_box.pack_start(refresh, True, True, 2) \end_layout \begin_layout LyX-Code control_box.pack_start(stop, True, True, 2) \end_layout \begin_layout LyX-Code control_box.pack_start(home, True, True, 2) \end_layout \begin_layout LyX-Code control_box.pack_start(self.address, True, True, 2) \end_layout \begin_layout LyX-Code control_box.pack_start(go, True, True, 2) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code back.connect("clicked", self.on_backward_clicked, None) \end_layout \begin_layout LyX-Code forward.connect("clicked", self.on_forward_clicked, None) \end_layout \begin_layout LyX-Code refresh.connect("clicked", self.on_refresh_clicked, None) \end_layout \begin_layout LyX-Code stop.connect("clicked", self.on_stop_clicked, None) \end_layout \begin_layout LyX-Code home.connect("clicked", self.on_home_clicked, None) \end_layout \begin_layout LyX-Code self.address.connect("key_press_event", self.on_address_keypress) \end_layout \begin_layout LyX-Code go.connect("clicked", self.on_go_clicked, None) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.main_vbox.pack_start(control_box, False, True, 2) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.win.add(self.main_vbox) \end_layout \begin_layout LyX-Code self.win.show_all() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Initialize all the Internet Explorer things \end_layout \begin_layout LyX-Code self.init_ie() \end_layout \begin_layout Standard As can be seen with this example, the initialization function most creates a nice window to view web pages in. This is to be used with the rest of the code. \end_layout \begin_layout Standard At the very end of the initialization is found the method call \shape italic self.init_ie() \shape default , this is what sets up all the Internet Explorer stuff. I will be very honest here and say I am not sure how it all works since I do not really care to much about Windows programming, but I know that it does work. \end_layout \begin_layout Standard So to take a look at the init_ie method what is found is the following: \end_layout \begin_layout LyX-Code def init_ie(self): \end_layout \begin_layout LyX-Code # Create a DrawingArea to host IE and add it to the hbox. \end_layout \begin_layout LyX-Code self.container = gtk.DrawingArea() \end_layout \begin_layout LyX-Code self.main_vbox.add(self.container) \end_layout \begin_layout LyX-Code self.container.show() \end_layout \begin_layout LyX-Code # Make the container accept the focus and pass it to the control; \end_layout \begin_layout LyX-Code # this makes the Tab key pass focus to IE correctly. \end_layout \begin_layout LyX-Code self.container.set_property("can-focus", True) \end_layout \begin_layout LyX-Code self.container.connect("focus", self.on_container_focus) \end_layout \begin_layout LyX-Code # Resize the AtlAxWin window with its container. \end_layout \begin_layout LyX-Code self.container.connect("size-allocate", self.on_container_size) \end_layout \begin_layout LyX-Code # Create an instance of IE via AtlAxWin. \end_layout \begin_layout LyX-Code atl.AtlAxWinInit() \end_layout \begin_layout LyX-Code hInstance = kernel32.GetModuleHandleA(None) \end_layout \begin_layout LyX-Code parentHwnd = self.container.window.handle \end_layout \begin_layout LyX-Code self.atlAxWinHwnd = user32.CreateWindowExA(0, "AtlAxWin", self.home_url, \end_layout \begin_layout LyX-Code win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_HSCROLL | \end_layout \begin_layout LyX-Code win32con.WS_VSCROLL, 0, 0, 100, 100, parentHwnd, None, hInstance, 0) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Get the IWebBrowser2 interface for the IE control. \end_layout \begin_layout LyX-Code pBrowserUnk = POINTER(IUnknown)() \end_layout \begin_layout LyX-Code atl.AtlAxGetControl(self.atlAxWinHwnd, byref(pBrowserUnk)) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # the wrap call querys for the default interface \end_layout \begin_layout LyX-Code self.browser = wrap(pBrowserUnk) \end_layout \begin_layout LyX-Code # Create a Gtk window that refers to the native AtlAxWin window. \end_layout \begin_layout LyX-Code self.gtkAtlAxWin = gtk.gdk.window_foreign_new(long(self.atlAxWinHwnd)) \end_layout \begin_layout LyX-Code # By default, clicking a GTK widget doesn't grab the focus away from \end_layout \begin_layout LyX-Code # a native Win32 control. \end_layout \begin_layout LyX-Code self.address.connect("button-press-event", self.on_widget_click) \end_layout \begin_layout Standard All I can say about this is that it works. If you can figure it out good for you. Now lets focus on some of the methods that are needed to work with Internet Explorer that are connected to in the init_ie method. \end_layout \begin_layout Standard Here is the on_widget_clicked method: \end_layout \begin_layout LyX-Code def on_widget_click(self, widget, data): \end_layout \begin_layout LyX-Code control self.win.window.focus() \end_layout \begin_layout Standard This method is used with Internet Explorer because on Windows, by default a GTK application does not grab control from native win32 api. \end_layout \begin_layout Standard Next is the on_container_size method. \end_layout \begin_layout LyX-Code def on_container_size(self, widget, sizeAlloc): \end_layout \begin_layout LyX-Code self.gtkAtlAxWin.move_resize(0, 0, sizeAlloc.width, sizeAlloc.height) \end_layout \begin_layout Standard This is used to make sure the gtk.Drawing container is properly sized \begin_inset Foot status collapsed \begin_layout Plain Layout Well as far as I can tell. \end_layout \end_inset . \end_layout \begin_layout Standard The last special method is on_container_focus. \end_layout \begin_layout LyX-Code def on_container_focus(self, widget, data): \end_layout \begin_layout LyX-Code rect = RECT() \end_layout \begin_layout LyX-Code user32.GetWindowRect(self.atlAxWinHwnd, byref(rect)) \end_layout \begin_layout LyX-Code ieHwnd = user32.WindowFromPoint(POINT(rect.left, rect.top)) \end_layout \begin_layout LyX-Code user32.SetFocus(ieHwnd) \end_layout \begin_layout Standard Apparently this method is used to pass the focus to Internet Explorer by passing the handle of the Internet Explorer control. \end_layout \begin_layout Standard And now are the backward, forward, stop, refresh, and home buttons. \end_layout \begin_layout LyX-Code def on_backward_clicked(self, widget=None, data=None): \end_layout \begin_layout LyX-Code try: \end_layout \begin_layout LyX-Code self.browser.GoBack() \end_layout \begin_layout LyX-Code except: \end_layout \begin_layout LyX-Code pass # No page to go back to \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_forward_clicked(self, widget=None, data=None): \end_layout \begin_layout LyX-Code try: \end_layout \begin_layout LyX-Code self.browser.GoForward() \end_layout \begin_layout LyX-Code except: \end_layout \begin_layout LyX-Code pass \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_refresh_clicked(self, widget=None, data=None): \end_layout \begin_layout LyX-Code self.browser.Refresh() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_stop_clicked(self, widget=None, data=None): \end_layout \begin_layout LyX-Code self.browser.Stop() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_home_clicked(self, widget=None, data=None): \end_layout \begin_layout LyX-Code #self.browser.GoHome() \end_layout \begin_layout Standard When using the GoBack and GoForward methods they must be used with error handling or they will will crash the program. The Refresh method is used to refresh, the Stop method is used to stop ad GoHome is used to go to the browsers home page. \end_layout \begin_layout Standard The on_go_clicked method takes the address entered in the address bar and loads that page. \end_layout \begin_layout LyX-Code def on_go_clicked(self, widget=None, data=None): \end_layout \begin_layout LyX-Code v = byref(VARIANT()) \end_layout \begin_layout LyX-Code self.browser.Navigate(self.address.get_text(), v, v, v, v) \end_layout \begin_layout Standard Loading the page that is in the address uses the Navigate method, pass in the address, then a variant to the rest \begin_inset Foot status collapsed \begin_layout Plain Layout I really have no idea what this does. \end_layout \end_inset . \end_layout \begin_layout Standard And finally the on_address_keypress method. This is the last method to be used with the Internet Explorer example. All this method does is watch for the Enter to be pressed and then calls the on_go_clicked method. \end_layout \begin_layout Section Mozilla and IE Example \end_layout \begin_layout Standard This section will show an example of how to use Internet Explorer or Mozilla as the engine depending on the operating system that is being used. \end_layout \begin_layout Standard Mozilla and Internet Explorer \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code Embedding IE in pygtk via AtlAxWin and ctypes. \end_layout \begin_layout LyX-Code """ \end_layout \begin_layout LyX-Code # needs the comtypes package from http://sourceforge.net/projects/comtypes/ \end_layout \begin_layout LyX-Code import sys \end_layout \begin_layout LyX-Code import pygtk \end_layout \begin_layout LyX-Code pygtk.require("2.0") \end_layout \begin_layout LyX-Code import gtk \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if sys.platform=="win32": \end_layout \begin_layout LyX-Code import win32con \end_layout \begin_layout LyX-Code from ctypes import * \end_layout \begin_layout LyX-Code from ctypes.wintypes \end_layout \begin_layout LyX-Code import * from comtypes \end_layout \begin_layout LyX-Code import IUnknown from comtypes.automation \end_layout \begin_layout LyX-Code import IDispatch, VARIANT from comtypes.client \end_layout \begin_layout LyX-Code import wrap \end_layout \begin_layout LyX-Code kernel32 = windll.kernel32 \end_layout \begin_layout LyX-Code user32 = windll.user32 \end_layout \begin_layout LyX-Code atl = windll.atl \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code import gtkmozembed \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code class GUI: \end_layout \begin_layout LyX-Code def __init__(self): \end_layout \begin_layout LyX-Code self.home_url = "http://www.majorsilence.com/" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.win = gtk.Window(gtk.WINDOW_TOPLEVEL) \end_layout \begin_layout LyX-Code self.win.set_title("Example Web browser that works on Linux and Windows") \end_layout \begin_layout LyX-Code self.win.connect("destroy", gtk.main_quit) self.win.set_size_request(750, 550) \end_layout \begin_layout LyX-Code self.win.realize() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.main_vbox = gtk.VBox() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code control_box = gtk.HBox(False, 0) \end_layout \begin_layout LyX-Code back = gtk.Button("Back") \end_layout \begin_layout LyX-Code forward = gtk.Button("Forward") \end_layout \begin_layout LyX-Code refresh = gtk.Button("Refresh") \end_layout \begin_layout LyX-Code stop = gtk.Button("Stop") \end_layout \begin_layout LyX-Code home = gtk.Button("Home") \end_layout \begin_layout LyX-Code self.address = gtk.Entry(max=0) # no limit on address length \end_layout \begin_layout LyX-Code go = gtk.Button("Go") \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code control_box.pack_start(back, True, True, 2) \end_layout \begin_layout LyX-Code control_box.pack_start(forward, True, True, 2) \end_layout \begin_layout LyX-Code control_box.pack_start(refresh, True, True, 2) \end_layout \begin_layout LyX-Code control_box.pack_start(stop, True, True, 2) \end_layout \begin_layout LyX-Code control_box.pack_start(home, True, True, 2) \end_layout \begin_layout LyX-Code control_box.pack_start(self.address, True, True, 2) \end_layout \begin_layout LyX-Code control_box.pack_start(go, True, True, 2) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code back.connect("clicked", self.on_backward_clicked, None) \end_layout \begin_layout LyX-Code forward.connect("clicked", self.on_forward_clicked, None) \end_layout \begin_layout LyX-Code refresh.connect("clicked", self.on_refresh_clicked, None) \end_layout \begin_layout LyX-Code stop.connect("clicked", self.on_stop_clicked, None) \end_layout \begin_layout LyX-Code home.connect("clicked", self.on_home_clicked, None) \end_layout \begin_layout LyX-Code self.address.connect("key_press_event", self.on_address_keypress) \end_layout \begin_layout LyX-Code go.connect("clicked", self.on_go_clicked, None) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.main_vbox.pack_start(control_box, False, True, 2) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.win.add(self.main_vbox) \end_layout \begin_layout LyX-Code self.win.show_all() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if sys.platform=="win32": \end_layout \begin_layout LyX-Code self.init_ie() \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code self.init_mozilla() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def init_ie(self): \end_layout \begin_layout LyX-Code # Create a DrawingArea to host IE and add it to the hbox. \end_layout \begin_layout LyX-Code self.container = gtk.DrawingArea() \end_layout \begin_layout LyX-Code self.main_vbox.add(self.container) \end_layout \begin_layout LyX-Code self.container.show() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Make the container accept the focus and pass it to the control; \end_layout \begin_layout LyX-Code # this makes the Tab key pass focus to IE correctly. \end_layout \begin_layout LyX-Code self.container.set_property("can-focus", True) \end_layout \begin_layout LyX-Code self.container.connect("focus", self.on_container_focus) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Resize the AtlAxWin window with its container. \end_layout \begin_layout LyX-Code self.container.connect("size-allocate", self.on_container_size) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Create an instance of IE via AtlAxWin. \end_layout \begin_layout LyX-Code atl.AtlAxWinInit() \end_layout \begin_layout LyX-Code hInstance = kernel32.GetModuleHandleA(None) \end_layout \begin_layout LyX-Code parentHwnd = self.container.window.handle \end_layout \begin_layout LyX-Code self.atlAxWinHwnd = user32.CreateWindowExA(0, "AtlAxWin", self.home_url, \end_layout \begin_layout LyX-Code win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_HSCROLL | \end_layout \begin_layout LyX-Code win32con.WS_VSCROLL, 0, 0, 100, 100, parentHwnd, None, hInstance, 0) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Get the IWebBrowser2 interface for the IE control. \end_layout \begin_layout LyX-Code pBrowserUnk = POINTER(IUnknown)() \end_layout \begin_layout LyX-Code atl.AtlAxGetControl(self.atlAxWinHwnd, byref(pBrowserUnk)) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # the wrap call queries for the default interface \end_layout \begin_layout LyX-Code self.browser = wrap(pBrowserUnk) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # Create a Gtk window that refers to the native AtlAxWin window. \end_layout \begin_layout LyX-Code self.gtkAtlAxWin = gtk.gdk.window_foreign_new(long(self.atlAxWinHwnd)) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code # By default, clicking a GTK widget doesn't grab the focus away from \end_layout \begin_layout LyX-Code # a native Win32 control. \end_layout \begin_layout LyX-Code self.address.connect("button-press-event", self.on_widget_click) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def init_mozilla(self): \end_layout \begin_layout LyX-Code self.browser = gtkmozembed.MozEmbed() \end_layout \begin_layout LyX-Code self.main_vbox.add(self.browser) \end_layout \begin_layout LyX-Code self.browser.load_url(self.home_url) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_backward_clicked(self, widget=None, data=None): \end_layout \begin_layout LyX-Code if sys.platform=="win32": \end_layout \begin_layout LyX-Code try: \end_layout \begin_layout LyX-Code self.browser.GoBack() \end_layout \begin_layout LyX-Code except: \end_layout \begin_layout LyX-Code pass # No page to go back to \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code if self.browser.can_go_back(): \end_layout \begin_layout LyX-Code self.browser.go_back() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_forward_clicked(self, widget=None, data=None): \end_layout \begin_layout LyX-Code if sys.platform=="win32": \end_layout \begin_layout LyX-Code try: \end_layout \begin_layout LyX-Code self.browser.GoForward() \end_layout \begin_layout LyX-Code except: \end_layout \begin_layout LyX-Code pass \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code if self.browser.can_go_forward(): \end_layout \begin_layout LyX-Code self.browser.go_forward() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_refresh_clicked(self, widget=None, data=None): \end_layout \begin_layout LyX-Code if sys.platform=="win32": \end_layout \begin_layout LyX-Code self.browser.Refresh() \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code self.browser.reload(gtkmozembed.FLAG_RELOADNORMAL) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_stop_clicked(self, widget=None, data=None): \end_layout \begin_layout LyX-Code if sys.platform=="win32": \end_layout \begin_layout LyX-Code self.browser.Stop() \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code self.browser.stop_load() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_home_clicked(self, widget=None, data=None): \end_layout \begin_layout LyX-Code if sys.platform=="win32": \end_layout \begin_layout LyX-Code # To go to Internet explorer's default home page use: \end_layout \begin_layout LyX-Code #self.browser.GoHome() \end_layout \begin_layout LyX-Code v = byref(VARIANT()) \end_layout \begin_layout LyX-Code self.browser.Navigate(self.home_url, v, v, v, v) \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code self.browser.load_url(self.home_url) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_go_clicked(self, widget=None, data=None): \end_layout \begin_layout LyX-Code if sys.platform=="win32": \end_layout \begin_layout LyX-Code v = byref(VARIANT()) \end_layout \begin_layout LyX-Code self.browser.Navigate(self.address.get_text(), v, v, v, v) \end_layout \begin_layout LyX-Code #print dir(self.browser) \end_layout \begin_layout LyX-Code else: \end_layout \begin_layout LyX-Code self.browser.load_url(self.address.get_text()) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_address_keypress(self, widget, event): \end_layout \begin_layout LyX-Code if gtk.gdk.keyval_name(event.keyval) == "Return": \end_layout \begin_layout LyX-Code print "Key press: Return" \end_layout \begin_layout LyX-Code self.on_go_clicked(None) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_widget_click(self, widget, data): \end_layout \begin_layout LyX-Code # used on win32 platform because by default a gtk application does \end_layout \begin_layout LyX-Code # not grab control from native win32 control \end_layout \begin_layout LyX-Code self.win.window.focus() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_container_size(self, widget, sizeAlloc): \end_layout \begin_layout LyX-Code self.gtkAtlAxWin.move_resize(0, 0, sizeAlloc.width, sizeAlloc.height) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_container_focus(self, widget, data): \end_layout \begin_layout LyX-Code # Used on win32 with Internet Explorer \end_layout \begin_layout LyX-Code # Pass the focus to IE. First get the HWND of the IE control; this \end_layout \begin_layout LyX-Code # is a bit of a hack but I couldn't make IWebBrowser2._get_HWND work. \end_layout \begin_layout LyX-Code rect = RECT() \end_layout \begin_layout LyX-Code user32.GetWindowRect(self.atlAxWinHwnd, byref(rect)) \end_layout \begin_layout LyX-Code ieHwnd = user32.WindowFromPoint(POINT(rect.left, rect.top)) \end_layout \begin_layout LyX-Code user32.SetFocus(ieHwnd) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if "__name__" == "__main__": \end_layout \begin_layout LyX-Code gui = GUI() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Chapter Internationalization \end_layout \begin_layout Standard \begin_inset Box Frameless position "t" hor_pos "c" has_inner_box 1 inner_pos "t" use_parbox 0 width "100col%" special "none" height "1in" height_special "totalheight" status open \begin_layout Plain Layout \begin_inset CommandInset include LatexCommand input filename "chapter-heading.lyx" \end_inset \end_layout \end_inset \end_layout \begin_layout Standard Must install intltool package on linux systems to provide the tools and scripts that are needed to extract the needed information from the python scripts and the programs glade files. \end_layout \begin_layout Itemize gettext.bindtextdomain(domain, localedir) - Bind the text to main to the locale directory that is specified. Where the binary .mo files are looked for. \end_layout \begin_layout Itemize gettext.textdomain(domain) - Sets the current global domain to the domain argument. If domain is none then the current global domain is returned. \end_layout \begin_layout Itemize gettext.translation(domain, localedir, languages, class, fallback, codeset) - Set the domain and the locale directory. All this chapter will be interested in is the first two arguments domain and localedir. \end_layout \begin_layout Itemize gettext.install(domain, localedir, unicode, codeset, names) - Install the function _() in the python builtin namespace so that it may be used easily from any python module within a program. \end_layout \begin_layout Section Python/PyGTK Translation \begin_inset CommandInset label LatexCommand label name "sec:Python/PyGTK-Translation" \end_inset \end_layout \begin_layout Standard To start off here is a very small program that has been setup for localization. \end_layout \begin_layout LyX-Code import pygtk, gtk \end_layout \begin_layout LyX-Code pygtk.require("2.0") \end_layout \begin_layout LyX-Code import locale, gettext \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code APP="translation-example" \end_layout \begin_layout LyX-Code DIR="po" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code locale.setlocale(locale.LC_ALL, '') \end_layout \begin_layout LyX-Code gettext.bindtextdomain(APP, DIR) \end_layout \begin_layout LyX-Code gettext.textdomain(APP) \end_layout \begin_layout LyX-Code lang = gettext.translation(APP, DIR) \end_layout \begin_layout LyX-Code _ = lang.gettext \end_layout \begin_layout LyX-Code gettext.install(APP, DIR) \end_layout \begin_layout Standard To start off the variable APP is set to \begin_inset Quotes eld \end_inset translation-example \begin_inset Quotes erd \end_inset and is used to set the domain for the translation. \end_layout \begin_layout LyX-Code class TranslationExample(object): \end_layout \begin_layout LyX-Code def __init__(self): \end_layout \begin_layout LyX-Code self.label_1 = gtk.Label( _("Hello World!") ) \end_layout \begin_layout LyX-Code label_2 = gtk.Label( _("Still in the HBox") ) \end_layout \begin_layout LyX-Code button = gtk.Button( _("Click Me") ) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code button.connect("clicked", self.on_button_clicked, \end_layout \begin_layout LyX-Code _("Anything can go here") ) \end_layout \begin_layout LyX-Code vbox = gtk.VBox() \end_layout \begin_layout LyX-Code vbox.pack_start(self.label_1) vbox.pack_start(label_2) \end_layout \begin_layout LyX-Code vbox.pack_start(button) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code win = gtk.Window() \end_layout \begin_layout LyX-Code win.connect("destroy", lambda wid: gtk.main_quit()) \end_layout \begin_layout LyX-Code win.add(vbox) \end_layout \begin_layout LyX-Code win.show_all() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def on_button_clicked(self, widget, data=None): \end_layout \begin_layout LyX-Code self.label_1.set_text( _("Hello ") + str(data) ) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if __name__ == "__main__": \end_layout \begin_layout LyX-Code TranslationExample() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Standard For more indepth coverage of gettext visit \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://docs.python.org/library/gettext.html \end_layout \end_inset . To download the tools from windows get them from the gnu site \begin_inset CommandInset href LatexCommand href target "ftp://ftp.gnu.org/gnu/gettext/gettext-tools-0.13.1.bin.woe32.zip" \end_inset . \end_layout \begin_layout Standard Now use the gettext command tool to extract the needed strings from all the python files and create the translation-example.pot file. \end_layout \begin_layout LyX-Code gettext --language=Python --keyword=_ --keyword=N_ \end_layout \begin_layout LyX-Code --output=translation-example.pot translation-example.py \end_layout \begin_layout Standard Now for each language that will be available for the application a .po file must be created. So if Canadian English is the language is to be used: \end_layout \begin_layout LyX-Code msginit --input=translation-example.pot --locale=en_CA \end_layout \begin_layout Standard Will output a en_CA.po file. American English would be: \end_layout \begin_layout LyX-Code msginit --input=translation-example.pot --locale=en_US \end_layout \begin_layout Standard Will output an en_US.po file. German would be: \end_layout \begin_layout LyX-Code msginit --input=translation-example.pot --locale=de_DE \end_layout \begin_layout Standard This of course would output de.po. \end_layout \begin_layout Standard Finally the .po files must be edited and the localized language put into their proper places. Just make sure that when the .po files are created that the \emph on charset \emph default is set to \emph on utf-8 \emph default . \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "Translations - PO translations example" \end_inset \end_layout \begin_layout LyX-Code # SOME DESCRIPTIVE TITLE. \end_layout \begin_layout LyX-Code # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER \end_layout \begin_layout LyX-Code # This file is distributed under the same license as the PACKAGE package. \end_layout \begin_layout LyX-Code # FIRST AUTHOR , YEAR. \end_layout \begin_layout LyX-Code # \end_layout \begin_layout LyX-Code #, fuzzy \end_layout \begin_layout LyX-Code msgid "" \end_layout \begin_layout LyX-Code msgstr "" \end_layout \begin_layout LyX-Code "Project-Id-Version: PACKAGE VERSION \backslash n" \end_layout \begin_layout LyX-Code "Report-Msgid-Bugs-To: \backslash n" \end_layout \begin_layout LyX-Code "POT-Creation-Date: 2009-02-17 16:01-0330 \backslash n" \end_layout \begin_layout LyX-Code "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE \backslash n" \end_layout \begin_layout LyX-Code "Last-Translator: FULL NAME \backslash n" \end_layout \begin_layout LyX-Code "Language-Team: LANGUAGE \backslash n" \end_layout \begin_layout LyX-Code "MIME-Version: 1.0 \backslash n" \end_layout \begin_layout LyX-Code "Content-Type: text/plain; charset=utf-8 \backslash n" \end_layout \begin_layout LyX-Code "Content-Transfer-Encoding: 8bit \backslash n" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code #: translation-example.py:20 \end_layout \begin_layout LyX-Code msgid "Hello " \end_layout \begin_layout LyX-Code msgstr "" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code #: translation-example.py:23 \end_layout \begin_layout LyX-Code msgid "Hello World!" \end_layout \begin_layout LyX-Code msgstr "" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code #: translation-example.py:24 \end_layout \begin_layout LyX-Code msgid "Still in the HBox" \end_layout \begin_layout LyX-Code msgstr "" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code #: translation-example.py:25 \end_layout \begin_layout LyX-Code msgid "Click Me" \end_layout \begin_layout LyX-Code msgstr "" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code #: translation-example.py:29 \end_layout \begin_layout LyX-Code msgid "Anything can go here" \end_layout \begin_layout LyX-Code msgstr "" \end_layout \begin_layout Standard Now what you need to do is edit the .po files so that the empty msgstr have the translated text. So what this means is that: \end_layout \begin_layout LyX-Code #: translation-example.py:20 \end_layout \begin_layout LyX-Code msgid "Hello " \end_layout \begin_layout LyX-Code msgstr "" \end_layout \begin_layout Standard Would become in German: \end_layout \begin_layout LyX-Code #: translation-example.py:20 \end_layout \begin_layout LyX-Code msgid "Hello " \end_layout \begin_layout LyX-Code msgstr "Guten Tag" \end_layout \begin_layout Standard Once all the strings are translated then the .po file must be converted into a binary .mo file and placed in its proper folder. So the en_CA.po file would be converted into translation-example.mo and placed in the folder ./po/en_CA/LC_MESSAGES/. \end_layout \begin_layout LyX-Code msgfmt --output-file=translation-example.mo en_CA.po \end_layout \begin_layout Standard Now copy the translation-example.mo file to the folder ./po/en_CA/LC_MESSAGES/. To test the the translated copy do the following: \end_layout \begin_layout LyX-Code LANG=lang python myapp.py \end_layout \begin_layout Standard So to test translation-example.py with german that would become: \end_layout \begin_layout LyX-Code LANG=de_DE.UTF-8 python translation-example.py \end_layout \begin_layout Standard It should be noted that on some systems that the .UTF-8 part is not needed. \end_layout \begin_layout Section gtk.glade Translation \begin_inset CommandInset label LatexCommand label name "sec:gtk.glade-Translation" \end_inset \end_layout \begin_layout Standard Translating a project that makes use of a glade file is easy. It just takes a few extra commands to extract the needed text strings. To start off here is an example program that makes use of the translation-examp le.glade file (See Figure \begin_inset CommandInset ref LatexCommand ref reference "fig:Translations - Glade Translation Project" \end_inset ). \end_layout \begin_layout Standard \begin_inset Float figure wide false sideways false status open \begin_layout Plain Layout \begin_inset Graphics filename images/translations/translations-example.png scale 40 \end_inset \end_layout \begin_layout Plain Layout \begin_inset Caption \begin_layout Plain Layout Glade Translation Project \end_layout \end_inset \begin_inset CommandInset label LatexCommand label name "fig:Translations - Glade Translation Project" \end_inset \end_layout \begin_layout Plain Layout \end_layout \end_inset \end_layout \begin_layout LyX-Code import pygtk \end_layout \begin_layout LyX-Code pygtk.require("2.0") \end_layout \begin_layout LyX-Code import gtk, gtk.glade \end_layout \begin_layout LyX-Code import locale, gettext \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code APP="translation-example" \end_layout \begin_layout LyX-Code DIR="po-glade" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code locale.setlocale(locale.LC_ALL, '') \end_layout \begin_layout LyX-Code gettext.bindtextdomain(APP, DIR) \end_layout \begin_layout LyX-Code gettext.textdomain(APP) \end_layout \begin_layout LyX-Code lang = gettext.translation(APP, DIR) \end_layout \begin_layout LyX-Code _ = lang.gettext \end_layout \begin_layout LyX-Code gettext.install(APP, DIR) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code class TranslationExample(object): \end_layout \begin_layout LyX-Code def on_button_clicked(self, widget, data=None): \end_layout \begin_layout LyX-Code self.label_1.set_text( _("Hello ") + str(data) ) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def __init__(self): \end_layout \begin_layout LyX-Code self.gladefile = gtk.glade.XML("translation-example.glade") \end_layout \begin_layout LyX-Code gtk.glade.bindtextdomain(APP, DIR) \end_layout \begin_layout LyX-Code self.gladefile.signal_autoconnect(self) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.main_window = self.gladefile.get_widget("window1") \end_layout \begin_layout LyX-Code self.main_window.connect("delete_event", lambda wid, we: gtk.main_quit()) \end_layout \begin_layout LyX-Code self.main_window.show_all() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if __name__ == "__main__": \end_layout \begin_layout LyX-Code TranslationExample() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout Standard Create a translation-example.glade.h file by running intltool-extract on translati on-example.glade. This is needed to extract the strings to translate with the gettext command line tool. \end_layout \begin_layout LyX-Code intltool-extract --type=gettext/glade translation-example.glade \end_layout \begin_layout Standard Now use the xgettext command tool to extract the needed strings from all the python files as well as the translation-example.glade.h header file that was created and create the translation-example.pot file. \end_layout \begin_layout LyX-Code xgettext --language=Python --keyword=_ --keyword=N_ \end_layout \begin_layout LyX-Code --output=translation-example.pot translation-example.py \end_layout \begin_layout LyX-Code translation-example.glade.h \end_layout \begin_layout Standard Now for each language that will be available for the application a .po file must be created. So if Canadian English is the language is to be used: \end_layout \begin_layout LyX-Code msginit --input=translation-example.pot --locale=en_CA \end_layout \begin_layout Standard Will output a en_CA.po file. American English would be: \end_layout \begin_layout LyX-Code msginit --input=translation-example.pot --locale=en_US \end_layout \begin_layout Standard Will output an en_US.po file. German would be: \end_layout \begin_layout LyX-Code msginit --input=translation-example.pot --locale=de_DE \end_layout \begin_layout Standard This of course would put de.po. \end_layout \begin_layout Standard Finally the .po files must be edited and the localized language put into their proper places. To do this please refer back to \begin_inset CommandInset ref LatexCommand vref reference "Translations - PO translations example" \end_inset as it shows you how to use the \emph on msgfmt \emph default command and proper way to do the translations. \end_layout \begin_layout Section gtk.Builder Translation \begin_inset CommandInset label LatexCommand label name "sec:gtk.Builder-Translation" \end_inset \end_layout \begin_layout Standard Translating a project that makes use of a gtk.Builder file is easy. It just takes a few extra commands to extract the needed text strings. To start off here is an example program that makes use of the translation-examp le.glade file (See Figure \begin_inset CommandInset ref LatexCommand ref reference "fig:Translations - Glade Translation Project" \end_inset ). First this file must be translated to a gtk.Builder file using the gtk-builder-c onvert (See section \begin_inset CommandInset ref LatexCommand vref reference "sec: Gtk Builder Convert" \end_inset ) script. \end_layout \begin_layout LyX-Code import pygtk \end_layout \begin_layout LyX-Code pygtk.require("2.0") \end_layout \begin_layout LyX-Code import gtk \end_layout \begin_layout LyX-Code import locale, gettext \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code APP="translation-example" \end_layout \begin_layout LyX-Code DIR="po-glade" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code locale.setlocale(locale.LC_ALL, '') \end_layout \begin_layout LyX-Code # This is needed to make gtk.Builder work by specifying the \end_layout \begin_layout LyX-Code # translations directory \end_layout \begin_layout LyX-Code locale.bindtextdomain(APP, DIR) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code gettext.bindtextdomain(APP, DIR) \end_layout \begin_layout LyX-Code gettext.textdomain(APP) \end_layout \begin_layout LyX-Code lang = gettext.translation(APP, DIR) \end_layout \begin_layout LyX-Code _ = lang.gettext gettext.install(APP, DIR) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code class TranslationExample(object): \end_layout \begin_layout LyX-Code def on_button_clicked(self, widget, data=None): \end_layout \begin_layout LyX-Code self.label_1.set_text( _("Hello ") + str(data) ) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def __init__(self): \end_layout \begin_layout LyX-Code self.gladefile = gtk.Builder() \end_layout \begin_layout LyX-Code self.gladefile.add_from_file("translation-example.xml") \end_layout \begin_layout LyX-Code self.gladefile.set_translation_domain(APP) \end_layout \begin_layout LyX-Code self.gladefile.connect_signals(self) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.main_window = self.gladefile.get_object("window1") \end_layout \begin_layout LyX-Code self.main_window.connect("delete_event", lambda wid, we: gtk.main_quit()) \end_layout \begin_layout LyX-Code self.main_window.show_all() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if __name__ == "__main__": \end_layout \begin_layout LyX-Code TranslationExample() \end_layout \begin_layout LyX-Code gtk.main() \end_layout \begin_layout LyX-Code \end_layout \begin_layout Standard Translating a gtk.Builder xml file uses the exact same commands as translating a glade file, however .glade is replaced with .xml for the file that is being used. So create a translation-example.xml.h file by running intltool-extract on translation-example.xml. This is needed to extract the strings to translate with the gettext command line tool. \end_layout \begin_layout LyX-Code intltool-extract --type=gettext/glade translation-example.xml \end_layout \begin_layout Standard Now use the xgettext command tool to extract the needed strings from all the python files as well as the translation-example.glade.h header file that was created and create the translation-example.pot file. \end_layout \begin_layout LyX-Code xgettext --language=Python --keyword=_ --keyword=N_ \end_layout \begin_layout LyX-Code --output=translation-example.pot translation-example.py \end_layout \begin_layout LyX-Code translation-example.xml.h \end_layout \begin_layout Standard Now for each language that will be available for the application a .po file must be created. So if Canadian English is the language is to be used: \end_layout \begin_layout LyX-Code msginit --input=translation-example.pot --locale=en_CA \end_layout \begin_layout Standard Will output a en_CA.po file. American English would be: \end_layout \begin_layout LyX-Code msginit --input=translation-example.pot --locale=en_US \end_layout \begin_layout Standard Will output an en_US.po file. German would be: \end_layout \begin_layout LyX-Code msginit --input=translation-example.pot --locale=de_DE \end_layout \begin_layout Standard This of course would put de.po. \end_layout \begin_layout Standard Finally the .po files must be edited and the localized language put into their proper places. To do this please refer back to \begin_inset CommandInset ref LatexCommand vref reference "Translations - PO translations example" \end_inset as it shows you how to use the \emph on msgfmt \emph default command and proper way to do the translations. \end_layout \begin_layout Section Testing Translations \begin_inset CommandInset label LatexCommand label name "sec:Testing-Translations" \end_inset \end_layout \begin_layout Standard To make sure that the translation is working properly it should be tested. This section will go into a bit more detail on setting this up. \end_layout \begin_layout Standard First the language suppport files that the application has been translated into must be installed on the operating system. This section assumes ubuntu is the test system and the examples are geared toward it. \end_layout \begin_layout Standard So lets assume the test system is ubuntu and German is the language that is to be tested. The easiest way to make sure that German language support is installed is to install the \emph on language-support-de \emph default package. This package will install all the german translation packages for the test system. If you wish you do not need to install this meta package, but can hunt down all the individual packages for german support. \end_layout \begin_layout Standard Now make sure that the .mo files, in this case translation-example.mo, are copied to each of their respective language folders; Eg ./po/en_CA/LC_MESSAGES/. To test the the translated copy do the following: \end_layout \begin_layout LyX-Code LANG=lang python myapp.py \end_layout \begin_layout Standard So to test translation-example.py with german that would become: \end_layout \begin_layout LyX-Code LANG=de_DE.UTF-8 python translation-example.py \end_layout \begin_layout Standard It should be noted that on some systems that the .UTF-8 part is not needed. \end_layout \begin_layout Subsection Testing on Win32/Win64 \end_layout \begin_layout Standard From the command line: \end_layout \begin_layout LyX-Code SET Lang=de_DE \end_layout \begin_layout LyX-Code myapp.py \end_layout \begin_layout Standard Another problem on Windows with gtkbuilder is that that it will not be translate d in a pygtk application. You have to force it using ctypes \begin_inset Foot status collapsed \begin_layout Plain Layout For more information see \begin_inset CommandInset href LatexCommand href target "https://bugzilla.gnome.org/show_bug.cgi?id=574520" \end_inset \end_layout \end_inset . At least at the time of writting (pygtk 2.16 with gtk 2.16 and 2.18) \end_layout \begin_layout Standard After this line of code \end_layout \begin_layout LyX-Code gettext.install(APP,localedir=DIR) \end_layout \begin_layout Standard You will then try something like this: \end_layout \begin_layout LyX-Code try: \end_layout \begin_layout LyX-Code libintl = ctypes.cdll.LoadLibrary("C: \backslash \backslash GTK \backslash \backslash gtk-2.16.6 \backslash \backslash bin \backslash \backslash intl.dll") \end_layout \begin_layout LyX-Code libintl.bindtextdomain(APP, DIR) \end_layout \begin_layout LyX-Code except: \end_layout \begin_layout LyX-Code print "Error Loading translations into gtk.builder files" \end_layout \begin_layout Section Translation Cheatsheet \begin_inset CommandInset label LatexCommand label name "sec:Translation-Cheatsheet" \end_inset \end_layout \begin_layout Standard Small quick cheetsheet of the commands that are needed to translate. \end_layout \begin_layout LyX-Code intltool-extract --type=gettext/glade translation-example.glade \end_layout \begin_layout LyX-Code \end_layout \begin_layout Standard Extract from both glade/builder and python scripts \end_layout \begin_layout LyX-Code xgettext --language=Python --keyword=_ --keyword=N_ \end_layout \begin_layout LyX-Code --output=translation-example.pot translation-example.py \end_layout \begin_layout LyX-Code translation-example.glade.h \end_layout \begin_layout Standard Canadian English \end_layout \begin_layout LyX-Code msginit --input=translation-example.pot --locale=en_CA \end_layout \begin_layout Standard American English \end_layout \begin_layout LyX-Code msginit --input=translation-example.pot --locale=en_US \end_layout \begin_layout Standard German \end_layout \begin_layout LyX-Code msginit --input=translation-example.pot --locale=de_DE \end_layout \begin_layout LyX-Code \end_layout \begin_layout Standard Change charset in each .po file to \begin_inset Quotes eld \end_inset charset=UTF-8 \begin_inset Quotes erd \end_inset and put in each translation string. Create binary .mo files for each .po file and place them in their proper ./po/LANG/LC_MESSAGES/ folder. \end_layout \begin_layout LyX-Code msgfmt --output-file=translation-example.mo en_CA.po \end_layout \begin_layout LyX-Code msgfmt --output-file=translation-example.mo en_US.po \end_layout \begin_layout LyX-Code msgfmt --output-file=translation-example.mo de_DE.po \end_layout \begin_layout Standard Test each language the application using each language that it has been translated into. \end_layout \begin_layout LyX-Code LANG=en_CA.UTF-8 python translation-example.py \end_layout \begin_layout LyX-Code LANG=en_US.UTF-8 python translation-example.py \end_layout \begin_layout LyX-Code LANG=de_DE.UTF-8 python translation-example.py \end_layout \begin_layout Section Locale Lists \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sec:Translations - Locale Lists" \end_inset \begin_inset Index status collapsed \begin_layout Plain Layout Locale \end_layout \end_inset \end_layout \begin_layout Standard \begin_inset Note Note status open \begin_layout Plain Layout List of supported locales can be found at /usr/share/i18n/SUPPORTED \end_layout \end_inset \end_layout \begin_layout Standard To be able to use and test any of these locale languages the language support packages for your linux distrubtion must be installed. On ubuntu these start with \emph on language-support \emph default and can be found using the synaptic package manager. So for german it would be \emph on language-support-de \emph default . \end_layout \begin_layout Standard Here is a short list of locales \begin_inset Foot status collapsed \begin_layout Plain Layout On my ubuntu system there is a very nice list at /usr/share/i18n/SUPPORTED. This is a big list that does not include long form of the location of the locale. \end_layout \end_inset that can be translated to. \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Plain Layout \backslash begin{multicols}{2} \end_layout \end_inset \end_layout \begin_layout Description en_US English, United States of America \end_layout \begin_layout Description en_CA English, Canada \end_layout \begin_layout Description en_AU English, Australian \end_layout \begin_layout Description en_GB English, Great Britain/United Kingdom \end_layout \begin_layout Description es_MX Spanish, Mexico \end_layout \begin_layout Description es_ES, Spanish, Spain \end_layout \begin_layout Description de_DE Germany, German \end_layout \begin_layout Description fr_FR French, France \end_layout \begin_layout Description fr_CA French, Canadian \end_layout \begin_layout Description it_IT Italian, Italy \end_layout \begin_layout Description ru_RU Russian, Russia \end_layout \begin_layout Description pt_BR Portuguese, Brazil \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Plain Layout \backslash end{multicols} \end_layout \end_inset \end_layout \begin_layout Section Summary \begin_inset CommandInset label LatexCommand label name "sec:Translations - Summary" \end_inset \end_layout \begin_layout Standard For more information on this topic please see these sites: \end_layout \begin_layout Itemize \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://docs.python.org/library/gettext.html \end_layout \end_inset \end_layout \begin_layout Itemize \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.learningpython.com/2006/12/03/translating-your-pythonpygtk-application/ \end_layout \end_inset \end_layout \begin_layout Itemize \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://faq.pygtk.org/index.py?req=show&file=faq22.002.htp \end_layout \end_inset \end_layout \begin_layout Chapter IronPython and Gtk-Sharp \end_layout \begin_layout Section Introduction \end_layout \begin_layout Standard The purpose of this chapter is to introduce using Gtk with IronPython. It will include a few short examples covering: \end_layout \begin_layout Itemize Layouts with Gtk.VBox and Gtk.HBox \end_layout \begin_layout Itemize Gtk.Buttons \end_layout \begin_layout Itemize Gtk.Entry \end_layout \begin_layout Itemize Widget Events (Callbacks) \end_layout \begin_layout Itemize Gtk.MessageDialog \end_layout \begin_layout Itemize Gtk.Label \end_layout \begin_layout Itemize Gtk.CheckButton \end_layout \begin_layout Itemize Gtk.RadioButton \end_layout \begin_layout Itemize Gtk.ComboBox \end_layout \begin_layout Itemize Gtk.Statusbar \end_layout \begin_layout Itemize Gtk.StatusIcon \end_layout \begin_layout Section Example 1 \end_layout \begin_layout Standard Example 1 shows the basics of using: \end_layout \begin_layout Itemize Layouts with Gtk.VBox \end_layout \begin_layout Itemize Gtk.Buttons \end_layout \begin_layout Itemize Gtk.Entry \end_layout \begin_layout Itemize Widget Events (Callbacks) \end_layout \begin_layout Itemize Message Dialogs \end_layout \begin_layout Standard To use Gtk Sharp from IronPython first you need to import the clr and add a reference to the gtk-sharp. Once this is finished you can import Gtk. The example below creates one window, adds a Gtk.Entry and Gtk.Button. The button has one event which is the self.HelloWorld function. The self.HelloWorld function displays a MessageDialog that will change the gtk.Entry default value to \begin_inset Quotes eld \end_inset Hello World! \begin_inset Quotes erd \end_inset if Yes is clicked. A Gtk.VBox is created and added to the window. This vbox is used to pack the self.textentry1 and button vertically. You can also use a Gtk.HBox instead or a combination of Gtk.VBox and Gtk.HBox. \end_layout \begin_layout Standard Gtk.Application.Init() must be called before using Gtk and Gtk.Application.Run() starts the Gtk main event loop. The window has the DeleteEvent attached to call the self.DeleteEvent function. The self.DeleteEvent function alls Gtk.Application.Quite() which exits the application. \end_layout \begin_layout LyX-Code import clr \end_layout \begin_layout LyX-Code clr.AddReference('gtk-sharp') \end_layout \begin_layout LyX-Code import Gtk \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code class GtkExample(object): \end_layout \begin_layout LyX-Code def __init__(self): \end_layout \begin_layout LyX-Code Gtk.Application.Init() \end_layout \begin_layout LyX-Code self.window = Gtk.Window("Hello World") \end_layout \begin_layout LyX-Code self.window.DeleteEvent += self.DeleteEvent \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code vbox = Gtk.VBox() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code button = Gtk.Button("Show Message") \end_layout \begin_layout LyX-Code button.Clicked += self.HelloWorld \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.textentry1 = Gtk.Entry("Default Text") \end_layout \begin_layout LyX-Code vbox.PackStart(self.textentry1) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code vbox.PackStart(button) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code self.window.Add(vbox) \end_layout \begin_layout LyX-Code self.window.ShowAll() \end_layout \begin_layout LyX-Code Gtk.Application.Run() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def DeleteEvent(self, widget, event): \end_layout \begin_layout LyX-Code Gtk.Application.Quit() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code def HelloWorld(self, widget, event): \end_layout \begin_layout LyX-Code m = Gtk.MessageDialog(None, Gtk.DialogFlags.Modal, Gtk.MessageType.Info, \backslash \end_layout \begin_layout LyX-Code Gtk.ButtonsType.YesNo, False, 'Change the text entry to "Hello World?"') \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code result = m.Run() \end_layout \begin_layout LyX-Code m.Destroy() \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if result == int(Gtk.ResponseType.Yes): \end_layout \begin_layout LyX-Code self.textentry1.Text = "Hello World!" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code if __name__ == "__main__": \end_layout \begin_layout LyX-Code GtkExample() \end_layout \begin_layout Section Summary \end_layout \begin_layout Standard At this point you should be able to create a basic Gtk application using IronPython and be able to extrapolate based on the c# gtk documation how to use more features from within IronPython. \end_layout \begin_layout Standard \begin_inset Branch SecondEdition status open \begin_layout Chapter Telepathy and Empathy \end_layout \begin_layout Standard This chapter is not yet written :) \end_layout \begin_layout Standard For more information see the following links: \end_layout \begin_layout Standard http://telepathy.freedesktop.org/wiki/ \end_layout \begin_layout Standard http://people.collabora.co.uk/~danni/telepathy-book/ \end_layout \begin_layout Chapter Geoclue \end_layout \begin_layout Standard This chapter is not yet written :) \end_layout \begin_layout Standard http://www.freedesktop.org/wiki/Software/GeoClue \end_layout \begin_layout Standard http://folks.o-hand.com/~jku/geoclue-docs/rn02.html \end_layout \begin_layout Standard http://blog.pierlux.com/projects/libchamplain/en/ \end_layout \begin_layout Chapter Custom Widgets \end_layout \begin_layout Standard This chapter is not yet written :) \end_layout \end_inset \end_layout \begin_layout Standard \end_layout \begin_layout Chapter \start_of_appendix Book Text Licenses \end_layout \begin_layout Standard See \begin_inset CommandInset href LatexCommand href target "http://creativecommons.org/licenses/by-sa/3.0/legalcode" \end_inset \end_layout \begin_layout Standard License \end_layout \begin_layout Standard THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. \end_layout \begin_layout Standard BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. \end_layout \begin_layout Standard 1. Definitions \end_layout \begin_layout Standard 1. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License. 2. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined below) for the purposes of this License. 3. "Creative Commons Compatible License" means a license that is listed at http://creativecommons.org/compatiblelicenses that has been approved by Creative Commons as being essentially equivalent to this License, including, at a minimum, because that license: (i) contains terms that have the same purpose, meaning and effect as the License Elements of this License; and, (ii) explicitly permits the relicensing of adaptations of works made available under that license under this License or a Creative Commons jurisdiction license with the same License Elements as this License. 4. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership. 5. "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike. 6. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License. 7. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast. 8. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematograp hy; a work of drawing, painting, architecture, sculpture, engraving or lithograp hy; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work. 9. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. 10. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images. 11. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium. \end_layout \begin_layout Standard 2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. \end_layout \begin_layout Standard 3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: \end_layout \begin_layout Standard 1. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; 2. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified."; 3. to Distribute and Publicly Perform the Work including as incorporated in Collections; and, 4. to Distribute and Publicly Perform Adaptations. 5. \end_layout \begin_layout Standard For the avoidance of doubt: 1. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; 2. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and, 3. Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License. \end_layout \begin_layout Standard The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technicall y necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved. \end_layout \begin_layout Standard 4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: \end_layout \begin_layout Standard 1. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(c), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(c), as requested. 2. You may Distribute or Publicly Perform an Adaptation only under the terms of: (i) this License; (ii) a later version of this License with the same License Elements as this License; (iii) a Creative Commons jurisdiction license (either this or a later license version) that contains the same License Elements as this License (e.g., Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible License. If you license the Adaptation under one of the licenses mentioned in (iv), you must comply with the terms of that license. If you license the Adaptation under the terms of any of the licenses mentioned in (i), (ii) or (iii) (the "Applicable License"), you must comply with the terms of the Applicable License generally and the following provisions: (I) You must include a copy of, or the URI for, the Applicable License with every copy of each Adaptation You Distribute or Publicly Perform; (II) You may not offer or impose any terms on the Adaptation that restrict the terms of the Applicable License or the ability of the recipient of the Adaptation to exercise the rights granted to that recipient under the terms of the Applicable License; (III) You must keep intact all notices that refer to the Applicable License and to the disclaimer of warranties with every copy of the Work as included in the Adaptation You Distribute or Publicly Perform; (IV) when You Distribute or Publicly Perform the Adaptatio n, You may not impose any effective technological measures on the Adaptation that restrict the ability of a recipient of the Adaptation from You to exercise the rights granted to that recipient under the terms of the Applicable License. This Section 4(b) applies to the Adaptation as incorporated in a Collection, but this does not require the Collection apart from the Adaptation itself to be made subject to the terms of the Applicable License. 3. If You Distribute, or Publicly Perform the Work or any Adaptations or Collectio ns, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Ssection 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. 4. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise. \end_layout \begin_layout Standard 5. Representations, Warranties and Disclaimer \end_layout \begin_layout Standard UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. \end_layout \begin_layout Standard 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL , PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. \end_layout \begin_layout Standard 7. Termination \end_layout \begin_layout Standard 1. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. 2. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. \end_layout \begin_layout Standard 8. Miscellaneous \end_layout \begin_layout Standard 1. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. 2. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and condition s as the license granted to You under this License. 3. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. 4. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. 5. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. 6. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the correspon ding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law. \end_layout \begin_layout Chapter Source Code Lisence \end_layout \begin_layout Standard GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 \end_layout \begin_layout Standard Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. \end_layout \begin_layout Standard This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. \end_layout \begin_layout Standard 0. Additional Definitions. \end_layout \begin_layout Standard As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. \end_layout \begin_layout Standard "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. \end_layout \begin_layout Standard An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. \end_layout \begin_layout Standard A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". \end_layout \begin_layout Standard The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. \end_layout \begin_layout Standard The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. \end_layout \begin_layout Standard 1. Exception to Section 3 of the GNU GPL. \end_layout \begin_layout Standard You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. \end_layout \begin_layout Standard 2. Conveying Modified Versions. \end_layout \begin_layout Standard If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: \end_layout \begin_layout Standard a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or \end_layout \begin_layout Standard b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. \end_layout \begin_layout Standard 3. Object Code Incorporating Material from Library Header Files. \end_layout \begin_layout Standard The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: \end_layout \begin_layout Standard a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. \end_layout \begin_layout Standard b) Accompany the object code with a copy of the GNU GPL and this license document. \end_layout \begin_layout Standard 4. Combined Works. \end_layout \begin_layout Standard You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: \end_layout \begin_layout Standard a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. \end_layout \begin_layout Standard b) Accompany the Combined Work with a copy of the GNU GPL and this license document. \end_layout \begin_layout Standard c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. \end_layout \begin_layout Standard d) Do one of the following: \end_layout \begin_layout Standard 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. \end_layout \begin_layout Standard 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. \end_layout \begin_layout Standard e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) \end_layout \begin_layout Standard 5. Combined Libraries. \end_layout \begin_layout Standard You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: \end_layout \begin_layout Standard a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. \end_layout \begin_layout Standard b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. \end_layout \begin_layout Standard 6. Revised Versions of the GNU Lesser General Public License. \end_layout \begin_layout Standard The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. \end_layout \begin_layout Standard Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundatio n. \end_layout \begin_layout Standard If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorizatio n for you to choose that version for the Library. \end_layout \begin_layout Chapter PyGTK and Windows \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sec:Appendix PyGTK and Windows" \end_inset \end_layout \begin_layout Section Install GTK and Glade \end_layout \begin_layout Standard First thing you need before you start using PyGTK on Windows is to have GTK+ installed. \end_layout \begin_layout Standard Go to the gtk web site ( \begin_inset Flex URL status open \begin_layout Plain Layout http://www.gtk.org \end_layout \end_inset ) download section and download the GTK bundle for windows. Then you will need to set your path to include the location of gtk. \end_layout \begin_layout Itemize Control Panel -> System -> Advanced tab -> Environment Variables -> Select \begin_inset Quotes eld \end_inset Path \begin_inset Quotes erd \end_inset \end_layout \begin_layout Standard Now for the path select it for your current user or your system wide path and add the \begin_inset Quotes eld \end_inset bin \begin_inset Quotes erd \end_inset location from where you put your GTK bundle. \end_layout \begin_layout Section Install PyGTK \end_layout \begin_layout Standard Go to the PyGTK website ( \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.pygtk.org \end_layout \end_inset ) download page. At the top of the page are the downloads for Windows. You will need all three and need to install them in this order: \end_layout \begin_layout Itemize PyGObject \end_layout \begin_layout Itemize PyCairo \end_layout \begin_layout Itemize PyGTK \end_layout \begin_layout Standard You may also want to download the GTK+ Preference Tool. You should be able to find it at \begin_inset CommandInset href LatexCommand href target "http://sourceforge.net/projects/gtk-win/files/" \end_inset . This tool will allow you to set the GTK theme on your Windows user account. At this point you should have a PyGTK development environment on your computer. \end_layout \begin_layout Standard If you want you can also install Win32 extensions for python from \begin_inset CommandInset href LatexCommand href target "http://sourceforge.net/projects/pywin32/files/" \end_inset . \end_layout \begin_layout Section Icons Not Displaying \end_layout \begin_layout Standard Some win32 distributions of GTK+ have icons set to not display. This is a configuration option in the gtkrc theme files. One option to fix this and make sure that icons display for buttons and all other widgets is to place the following code in the main PyGTK file of your application. \end_layout \begin_layout LyX-Code if sys.platform=="win32": \end_layout \begin_layout LyX-Code gtk.settings_get_default().set_long_property("gtk-button-images", True, "main") \end_layout \begin_layout Standard The other option is to do the following: \end_layout \begin_layout Enumerate Open the C: \backslash GTK \backslash share \backslash themes \backslash MS-Windows \backslash gtk-2.0 \backslash gtkrc file \end_layout \begin_layout Enumerate And change the line \begin_inset Quotes eld \end_inset gtk-button-images = 0 \begin_inset Quotes erd \end_inset to \begin_inset Quotes eld \end_inset gtk-button-images = 1 \begin_inset Quotes erd \end_inset \end_layout \begin_layout Chapter Stock Icons \end_layout \begin_layout Standard \begin_inset CommandInset label LatexCommand label name "sec:Appendix Stock Icons" \end_inset \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Plain Layout \backslash begin{multicols}{2} \end_layout \end_inset \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_ABOUT Available in GTK+ 2.6 and above. \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_ADD \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_APPLY \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_BOLD \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_CANCEL \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_CDROM \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_CLEAR \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_CLOSE \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_COLOR_PICKER Available in GTK+ 2.2 and above. \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_CONVERT \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_CONNECT Available in GTK+ 2.6 and above. \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_COPY \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_CUT \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_DELETE \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_DIALOG_AUTHENTICATION Available in GTK+ 2.4 and above. \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_DIALOG_ERROR \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_DIALOG_INFO \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_DIALOG_QUESTION \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_DIALOG_WARNING \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_DIRECTORY Available in GTK+ 2.6 and above. \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_DISCONNECT Available in GTK+ 2.6 and above. \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_DND \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_DND_MULTIPLE \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_EDIT Available in GTK+ 2.6 and above. \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_EXECUTE \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_FILE Available in GTK+ 2.6 and above. \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_FIND \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_FIND_AND_REPLACE \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_FLOPPY \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_FULLSCREEN Available in GTK+ 2.8 and above. \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_GOTO_BOTTOM \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_GOTO_FIRST \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_GOTO_LAST \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_GOTO_TOP \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_GO_BACK \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_GO_DOWN \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_GO_FORWARD \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_GO_UP \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_HARDDISK Available in GTK+ 2.4 and above \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_HELP \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_HOME \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_INDENT Available in GTK+ 2.4 and above. \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_INDEX \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_INFO Available in GTK+ 2.8 and above. \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_ITALIC \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_JUMP_TO \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_JUSTIFY_CENTER \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_JUSTIFY_FILL \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_JUSTIFY_LEFT \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_JUSTIFY_RIGHT \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_LEAVE_FULLSCREEN Available in GTK+ 2.8 and above. \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_MEDIA_FORWARD Available in GTK+ 2.6 and above. \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_MEDIA_NEXT Available in GTK+ 2.6 and above. \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_MEDIA_PAUSE Available in GTK+ 2.6 and above. \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_MEDIA_PLAY RTL version is Available in GTK+ 2.6 and above. \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_MEDIA_PREVIOUS Available in GTK+ 2.6 and above. \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_MEDIA_RECORD Available in GTK+ 2.6 and above. \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_MEDIA_REWIND Available in GTK+ 2.6 and above. \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_MEDIA_STOP Available in GTK+ 2.6 and above. \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_MISSING_IMAGE \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_NETWORK Available in GTK+ 2.4 and above. \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_NEW \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_NO \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_OK \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_OPEN \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_PASTE \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_PREFERENCES \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_PRINT \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_PRINT_PREVIEW \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_PROPERTIES \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_QUIT \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_REDO \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_REFRESH \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_REMOVE \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_REVERT_TO_SAVED \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_SAVE \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_SAVE_AS \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_SELECT_COLOR \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_SELECT_FONT \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_SORT_ASCENDING \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_SORT_DESCENDING \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_SPELL_CHECK \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_STOP \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_STRIKETHROUGH \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_UNDELETE \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_UNDERLINE \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_UNDO \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_UNINDENT Available in GTK+ 2.4 and above. \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_YES \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_ZOOM_100 \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_ZOOM_FIT \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_ZOOM_IN \end_layout \begin_layout List \labelwidthstring 00.00.0000 gtk.STOCK_ZOOM_OUT \end_layout \begin_layout Standard \begin_inset ERT status open \begin_layout Plain Layout \backslash end{multicols} \end_layout \end_inset \end_layout \begin_layout Bibliography \begin_inset CommandInset bibitem LatexCommand bibitem key "key-1" \end_inset 2006-03-02, John Finlay, Version 2.5, PyGTK 2.0 Tutorial, \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://pygtk.org/pygtk2tutorial/ \end_layout \end_inset \end_layout \begin_layout Bibliography \begin_inset CommandInset bibitem LatexCommand bibitem key "key-7" \end_inset 2007-07-31, \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.cairographics.org/pycairo/tutorial/ \end_layout \end_inset \end_layout \begin_layout Bibliography \begin_inset CommandInset bibitem LatexCommand bibitem key "key-3" \end_inset Accessed 2008-08-31,Michael Urman, \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.tortall.net/mu/wiki/CairoTutorial \end_layout \end_inset \end_layout \begin_layout Bibliography \begin_inset CommandInset bibitem LatexCommand bibitem key "key-4" \end_inset Accessed 2008-08-31,Michael Urman, \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.tortall.net/mu/wiki/PyGTKCairoTutorial \end_layout \end_inset \end_layout \begin_layout Bibliography \begin_inset CommandInset bibitem LatexCommand bibitem key "key-5" \end_inset Accessed 2008-08-31, Lawrence Oluyede, \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://pygtk.org/articles/cairo-pygtk-widgets/cairo-pygtk-widgets.htm \end_layout \end_inset \end_layout \begin_layout Bibliography \begin_inset CommandInset bibitem LatexCommand bibitem key "key-6" \end_inset \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://pygtk.org/articles/cairo-pygtk-widgets/cairo-pygtk-widgets2.htm \end_layout \end_inset \end_layout \begin_layout Bibliography \begin_inset CommandInset bibitem LatexCommand bibitem key "key-9" \end_inset \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.mono-project.com/FAQ:_Gtk#Should_I_hook_up_to_events.2C_or_override_meth ods_to_create_a_custom_widget.3F \end_layout \end_inset \end_layout \begin_layout Bibliography \begin_inset CommandInset bibitem LatexCommand bibitem key "key-10" \end_inset 2008-04-16, \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://blog.sontek.net/2008/04/16/printing-in-gtk/ \end_layout \end_inset \end_layout \begin_layout Bibliography \begin_inset CommandInset bibitem LatexCommand bibitem key "key-11" \end_inset 2008-04-15, John Anderson, PyGTK 2.0 Reference Manual \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://pygtk.org/docs/pygtk/ \end_layout \end_inset \end_layout \begin_layout Bibliography \begin_inset CommandInset bibitem LatexCommand bibitem key "key-16" \end_inset Accessed 2008-08-31, Cairo 1.6.5 API Reference Manual \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.cairographics.org/manual/cairo-Image-Surfaces.html \end_layout \end_inset \end_layout \begin_layout Bibliography \begin_inset CommandInset bibitem LatexCommand bibitem key "key-19" \end_inset \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s05.html \end_layout \end_inset \end_layout \begin_layout Bibliography \begin_inset CommandInset bibitem LatexCommand bibitem key "key-20" \end_inset \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://standards.freedesktop.org/menu-spec/menu-spec-1.0.html \end_layout \end_inset \end_layout \begin_layout Bibliography \begin_inset CommandInset bibitem LatexCommand bibitem key "key-21" \end_inset \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://pygstdocs.berlios.de/pygst-tutorial/seeking.html \end_layout \end_inset \end_layout \begin_layout Bibliography \begin_inset CommandInset bibitem LatexCommand bibitem key "key-22" \end_inset \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/html/GstElement. html \end_layout \end_inset \end_layout \begin_layout Bibliography \begin_inset CommandInset bibitem LatexCommand bibitem key "key-23" \end_inset 2008-01-23, Thomas Heller, Internet Explorer with PyGTK, \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.mail-archive.com/comtypes-users@lists.sourceforge.net/msg00084.html \end_layout \end_inset \end_layout \begin_layout Bibliography \begin_inset CommandInset bibitem LatexCommand bibitem key "key-27" \end_inset 2007-09-23, Giuliani Vito Ivan, \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://zeta-puppis.com/2007/09/23/an-introduction-to-pyclutter-part-one/ \end_layout \end_inset \end_layout \begin_layout Bibliography \begin_inset CommandInset bibitem LatexCommand bibitem key "key-2" \end_inset Accessed 2009-02-18, gettext - Multilingual internationalization services, \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://docs.python.org/library/gettext.html \end_layout \end_inset \end_layout \begin_layout Bibliography \begin_inset CommandInset bibitem LatexCommand bibitem key "key-3" \end_inset Accessed 2009-02-18, learningpython.com, \begin_inset Flex URL status collapsed \begin_layout Plain Layout http://www.learningpython.com/2006/12/03/translating-your-pythonpygtk-application/ \end_layout \end_inset \end_layout \begin_layout Standard \begin_inset CommandInset index_print LatexCommand printindex \end_inset \end_layout \end_body \end_document