Editing PyMaemo/Using Python in Maemo

Warning: You are not logged in. Your IP address will be recorded in this page's edit history.

Warning: This page is 38 kilobytes long; some browsers may have problems editing pages approaching or longer than 32kb. Please consider breaking the page into smaller sections.

The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then save the changes below to finish undoing the edit.
Latest revision Your text
Line 1: Line 1:
 +
=Using Python in Maemo=
 +
Last Updated: Wed, 02 May 2007
Last Updated: Wed, 02 May 2007
Line 5: Line 7:
Author: Daniel d'Andrada T. de Carvalho <daniel.[last name] at indt.org.br>
Author: Daniel d'Andrada T. de Carvalho <daniel.[last name] at indt.org.br>
-
{{ambox|notice|
+
<b style="color: red">This document is outdated and will not work for Maemo 5 (fremantle)</b>
-
text=This document is outdated and will not work for [[Open development/Maemo roadmap/Fremantle|Maemo 5 (fremantle)]]}}
+
==Introduction==
==Introduction==
Line 17: Line 18:
* Learn the basics of the Maemo platform<br/>For more information, see the ''Overview of the Maemo Platform'' section in ''Maemo 3.0 Tutorial''<ref name="maemo3tutorial">[http://www.maemo.org/platform/docs/howtos/Maemo_tutorial_bora.html Maemo 3.0 Tutorial]</ref>
* Learn the basics of the Maemo platform<br/>For more information, see the ''Overview of the Maemo Platform'' section in ''Maemo 3.0 Tutorial''<ref name="maemo3tutorial">[http://www.maemo.org/platform/docs/howtos/Maemo_tutorial_bora.html Maemo 3.0 Tutorial]</ref>
 +
* Set up the Maemo development environment<br/>The Maemo development environment is where you run your Maemo applications. For more information, see ''Setting Up and Testing Development Environment'' section in ''Maemo 3.0 Tutorial''<ref name="maemo3tutorial" />. The tutorial assumes that you are developing from Scratchbox instead of directly using a Maemo device (such as a Nokia 770).
* Set up the Maemo development environment<br/>The Maemo development environment is where you run your Maemo applications. For more information, see ''Setting Up and Testing Development Environment'' section in ''Maemo 3.0 Tutorial''<ref name="maemo3tutorial" />. The tutorial assumes that you are developing from Scratchbox instead of directly using a Maemo device (such as a Nokia 770).
Line 39: Line 41:
distutils, pdb, pydoc, BaseHTTPServer, SimpleHTTPServer, SimpleXMLRPCServer, CGIHTTPServer, cgi, cgitb, DocXMLRPCServeri, robotparser, smtpd, compile, encodings.cp*, encodings.mac*, doctest, unittest, config, symtable, tabnanny, timeit, trace and hotshot.
distutils, pdb, pydoc, BaseHTTPServer, SimpleHTTPServer, SimpleXMLRPCServer, CGIHTTPServer, cgi, cgitb, DocXMLRPCServeri, robotparser, smtpd, compile, encodings.cp*, encodings.mac*, doctest, unittest, config, symtable, tabnanny, timeit, trace and hotshot.
-
The <code>import</code> command will import <code>.pyo</code> files even if the interpreted is called without the <code>-O</code> or <code>-OO</code> option. This is a difference from the standard Python behavior.
+
The <var>import</var> command will import <var>.pyo</var> files even if the interpreted is called without the <var>-O</var> or <var>-OO</var> option. This is a difference from the standard Python behavior.
==Installing Python==
==Installing Python==
Line 47: Line 49:
===On Maemo SDK (scratchbox)===
===On Maemo SDK (scratchbox)===
-
Follow the instructions on http://pymaemo.garage.maemo.org/installation.html.
+
Follow the instructions on http://pymaemo.garage.maemo.org/sdk_installation.html.
===On Maemo devices===
===On Maemo devices===
Line 61: Line 63:
Create a <var>hello_world_1.py</var> file with the following content.
Create a <var>hello_world_1.py</var> file with the following content.
-
<source lang="python">
+
<nowiki>
-
#!/usr/bin/env python2.5
+
#!/usr/bin/env python2.5
-
import gtk
+
import gtk
 +
 +
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
 +
 +
label = gtk.Label("Hello World!")
 +
window.add(label)
 +
 +
label.show()
 +
window.show()
 +
 +
gtk.main()</nowiki>
-
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+
In the Scratchbox console, first make the file executable by running the <var>chmod +x ./hello_world_1.py</var> command, and then run the example by typing <var>run-standalone.sh ./hello_world_1.py</var>. Figure 1 illustrates the results of the run command:
-
label = gtk.Label("Hello World!")
+
<br/>
-
window.add(label)
+
[[Image:hello_world_1_small.jpg|frame|border|center|middle|alt=Plain PyGTK "Hello World!" application|Plain PyGTK "Hello World!" application]]
-
 
+
<br/>
-
label.show()
+
-
window.show()
+
-
 
+
-
gtk.main()
+
-
</source>
+
-
 
+
-
In the Scratchbox console, first make the file executable by running the <code>chmod +x ./hello_world_1.py</code> command, and then run the example by typing <code>run-standalone.sh ./hello_world_1.py</code>. Figure 1 illustrates the results of the run command:
+
-
 
+
-
[[Image:hello_world_1_small.jpg|frame|center|alt=Screenshot of "Hello World!" application|Figure 1: Plain PyGTK "Hello World!" application]]
+
You can see that the plain PyGTK code already "works" on Maemo. However, do not use plain PyGTK code or rely on the assumption that any PyGTK code "works" on Maemo. In this tiny example program, the obvious sign of misfit in the Maemo environment is that its borders (skin graphic area) are broken (look at the top corners).
You can see that the plain PyGTK code already "works" on Maemo. However, do not use plain PyGTK code or rely on the assumption that any PyGTK code "works" on Maemo. In this tiny example program, the obvious sign of misfit in the Maemo environment is that its borders (skin graphic area) are broken (look at the top corners).
-
The reason for using the <code>run-standalone.sh</code> command to execute the application is that it adds the Hildon theming. Figure 2 illustrates how the application looks if run without the <code>run-standalone.sh</code> command:
+
The reason for using the <var>run-standalone.sh</var> command to execute the application is that it adds the Hildon theming. Figure 2 illustrates how the application looks if run without the <var>run-standalone.sh</var> command:
-
[[Image:hello_world_1_no_theme_small.jpg|frame|center|alt=Screenshot of unthemed application|Figure 2: Application without the Hildon theme]]
+
<br/>
 +
[[Image:hello_world_1_no_theme_small.jpg|frame|border|center|middle|alt=Application without the Hildon theme|Application without the Hildon theme]]
 +
<br/>
-
{{ambox|
+
<div class="note"><div class="label">Note</div><div class="content"> The <var>run-standalone.sh</var> command is only available if you run applications from the Scratchbox console. </div></div>
-
text=The <code>run-standalone.sh</code> command is only available if you run applications from the Scratchbox console.}}
+
===HildonWindow Class===
===HildonWindow Class===
-
The <code>HildonWindow</code> class overloads the <code>GtkWindow</code> class, providing the Hildon theme (look and feel) for top level windows. In order to use the facilities provided by the Hildon framework (and to integrate cleanly in it), the application must use a <code>HildonWindow</code> instead of a <code>GtkWindow</code>. With a <code>HildonWindow</code> class the application has, for example, access to Hildon menus and toolbars.
+
The <var>HildonWindow</var> class overloads the <var>GtkWindow</var> class, providing the Hildon theme (look and feel) for top level windows. In order to use the facilities provided by the Hildon framework (and to integrate cleanly in it), the application must use a <var>HildonWindow</var> instead of a <var>GtkWindow</var>. With a <var>HildonWindow</var> class the application has, for example, access to Hildon menus and toolbars.
-
The required code change is simple: replace the <code>GtkWindow</code> with <code>HildonWindow</code> and import the <code>hildon</code> module. The following example illustrates the required changes (also downloadable [http://pymaemo.garage.maemo.org/documentation/pymaemo_tutorial/examples/hello_world_1.py here]):
+
The required code change is simple: replace the <var>GtkWindow</var> with <var>HildonWindow</var> and import the <var>hildon</var> module. The following example illustrates the required changes (also downloadable [http://pymaemo.garage.maemo.org/documentation/pymaemo_tutorial/examples/hello_world_1.py here]):
-
<source lang="python">
+
<nowiki>
-
#!/usr/bin/env python2.5
+
#!/usr/bin/env python2.5
 +
 +
import gtk
 +
import hildon
 +
 +
window = hildon.Window()
 +
window.connect("destroy", gtk.main_quit)
 +
label = gtk.Label("Hello World!")
 +
window.add(label)
 +
 +
label.show()
 +
window.show()
 +
 +
gtk.main()</nowiki>
-
import gtk
+
<br/>
-
import hildon
+
[[Image:hello_world_3_small.jpg|frame|border|center|middle|alt=Hildon-compliant "Hello World!" application|Hildon-compliant "Hello World!" application]]
 +
<br/>
-
window = hildon.Window()
+
Note how the borders are now drawn in the right way, since the program is using the <var>HildonWindow</var> class.
-
window.connect("destroy", gtk.main_quit)
+
-
label = gtk.Label("Hello World!")
+
-
window.add(label)
+
-
 
+
-
label.show()
+
-
window.show()
+
-
 
+
-
gtk.main()
+
-
</source>
+
-
 
+
-
[[Image:hello_world_3_small.jpg|frame|center|alt=Screenshot of Hildon-compliant "Hello World!" application|Figure 3: Hildon-compliant "Hello World!" application]]
+
-
 
+
-
Note how the borders are now drawn in the right way, since the program is using the <code>HildonWindow</code> class.
+
===HildonProgram Class===
===HildonProgram Class===
-
The <code>HildonProgram</code> class is a programmer commodity used to apply program-wide settings to all Hildon windows used by the application (for example, this allows you to have a common menu and toolbar on all windows). In addition, <code>HildonProgram</code> also manages other program-wide issues, such as hibernating.
+
The <var>HildonProgram</var> class is a programmer commodity used to apply program-wide settings to all Hildon windows used by the application (for example, this allows you to have a common menu and toolbar on all windows). In addition, <var>HildonProgram</var> also manages other program-wide issues, such as hibernating.
-
 
+
-
The example "Hello World" application has no use for a <code>HildonProgram</code> object, but it is added to the following example (also downloadable [http://pymaemo.garage.maemo.org/documentation/pymaemo_tutorial/examples/hello_world_2.py here]), simply to illustrate how a regular Hildon application is structured.
+
-
 
+
-
<source lang="python">
+
-
#!/usr/bin/env python2.5
+
-
import gtk
+
-
import hildon
+
-
 
+
-
class HelloWorldApp(hildon.Program):
+
-
  def __init__(self):
+
-
    hildon.Program.__init__(self)
+
-
 
+
-
    self.window = hildon.Window()
+
-
    self.window.connect("destroy", gtk.main_quit)
+
-
    self.add_window(self.window)
+
-
 
+
-
    label = gtk.Label("Hello World!")
+
-
    self.window.add(label)
+
-
    label.show()
+
-
  def run(self):
+
The example "Hello World" application has no use for a <var>HildonProgram</var> object, but it is added to the following example (also downloadable [http://pymaemo.garage.maemo.org/documentation/pymaemo_tutorial/examples/hello_world_2.py here]), simply to illustrate how a regular Hildon application is structured.
-
    self.window.show_all()
+
-
    gtk.main()
+
-
app = HelloWorldApp()
+
<nowiki>
-
app.run()
+
#!/usr/bin/env python2.5
-
</source>
+
import gtk
 +
import hildon
 +
 +
class HelloWorldApp(hildon.Program):
 +
  def __init__(self):
 +
    hildon.Program.__init__(self)
 +
 +
    self.window = hildon.Window()
 +
    self.window.connect("destroy", gtk.main_quit)
 +
    self.add_window(self.window)
 +
 +
    label = gtk.Label("Hello World!")
 +
    self.window.add(label)
 +
    label.show()
 +
 +
  def run(self):
 +
    self.window.show_all()
 +
    gtk.main()
 +
 +
app = HelloWorldApp()
 +
app.run()</nowiki>
When running the application, the result is the same as shown in Figure 3.
When running the application, the result is the same as shown in Figure 3.
Line 161: Line 165:
To install and run gPodder:
To install and run gPodder:
-
# Download the source package [gpodder-0.8.0.tar.gz here] or directly from the project's page, <code>http://gpodder.berlios.de/</code>.
+
# Download the source package [gpodder-0.8.0.tar.gz here] or directly from the project's page, <var>http://gpodder.berlios.de/</var>.
-
# Extract the <code>gpodder-0.8.0.tar.gz</code> file. The <code>gpodder-0.8.0</code> directory will be created.
+
# Explode the <var>gpodder-0.8.0.tar.gz</var> file. The <var>gpodder-0.8.0</var> directory will be created.
-
# In the <code>gpodder-0.8.0</code> directory, use the Scratchbox console to run the following commands: <code>python2.5 setup.py install</code> and <code>run-standalone.sh gpodder</code>
+
# In the <var>gpodder-0.8.0</var> directory, use the Scratchbox console to run the following commands: <var>python2.5 setup.py install</var> and <var>run-standalone.sh gpodder</var>
The Scratchbox console can display some GLib warnings, but you can ignore them. Figure 4 illustrates the screen you see after running the commands:
The Scratchbox console can display some GLib warnings, but you can ignore them. Figure 4 illustrates the screen you see after running the commands:
-
[[Image:gpodder_1_small.jpg|frame|center|alt=Screenshot of gPodder running on Maemo without any modifications|Figure 4: gPodder running on Maemo without any modifications]]
+
<br/>
 +
[[Image:gpodder_1_small.jpg|frame|border|center|middle|alt=gPodder running on Maemo without any modifications|gPodder running on Maemo without any modifications]]
 +
<br/>
This means that gPodder 0.8 works on Maemo without any modifications. However, it is not a Maemo application yet, and the steps in the following sections are required to make it fit cleanly in the Maemo environment.
This means that gPodder 0.8 works on Maemo without any modifications. However, it is not a Maemo application yet, and the steps in the following sections are required to make it fit cleanly in the Maemo environment.
Line 175: Line 181:
This section gives a brief overview of gPodder's source code. The purpose of the overview is to make it easier to understand the code changes implemented in the following sections.
This section gives a brief overview of gPodder's source code. The purpose of the overview is to make it easier to understand the code changes implemented in the following sections.
-
Most of the code changes are made in the <code>gpodder-0.8.0/src/gpodder/gpodder.py</code> file. It contains the following classes:
+
Most of the code changes are made in the <var>gpodder-0.8.0/src/gpodder/gpodder.py</var> file. It contains the following classes:
-
<source lang="python">
+
-
class Gpodder(SimpleGladeApp)
+
class Gpodder(SimpleGladeApp)
-
class Gpodderchannel(SimpleGladeApp)
+
class Gpodderchannel(SimpleGladeApp)
-
class Gpodderproperties(SimpleGladeApp)
+
class Gpodderproperties(SimpleGladeApp)
-
class Gpodderepisode(SimpleGladeApp)
+
class Gpodderepisode(SimpleGladeApp)
-
class Gpoddersync(SimpleGladeApp)
+
class Gpoddersync(SimpleGladeApp)
-
class Gpodderopmllister(SimpleGladeApp)
+
class Gpodderopmllister(SimpleGladeApp)
-
</source>
+
-
Open the <code>gpodder-0.8.0/data/gpodder.glade</code> file using the ''[http://glade.gnome.org/ Glade User Interface Designer]'' (another option is ''[http://gazpacho.sicem.biz/ Gazpacho]''). You can see that there is one class for each <code>GtkWindow</code> defined in it, as shown in Figure 5:
+
Open the <var>gpodder-0.8.0/data/gpodder.glade</var> file using the ''[http://glade.gnome.org/ Glade User Interface Designer]'' (another option is ''[http://gazpacho.sicem.biz/ Gazpacho]''). You can see that there is one class for each <var>GtkWindow</var> defined in it, as shown in Figure 5:
-
[[Image:gpodder_glade_1.png|frame|center|alt=Screenshot of Glade showing a list of windows defined in gPodder's glade file|Figure 5: Windows defined in gPodder's glade file]]
+
<br/>
 +
[[Image:gpodder_glade_1.png|frame|border|center|middle|alt=Windows defined in gPodder's glade file|Windows defined in gPodder's glade file]]
 +
<br/>
-
In addition to loading the window description from <code>gpodder.glade</code>, the <code>SimpleGladeApp</code> class also makes all window elements directly accessible from <code>self</code>, ignoring the element's hierarchy. Figure 6 illustrates part of the element's hierarchy for the gPodder window, and how to access the elements from inside a <code>Gpodder</code> method.
+
In addition to loading the window description from <var>gpodder.glade</var>, the <var>SimpleGladeApp</var> class also makes all window elements directly accessible from <var>self</var>, ignoring the element's hierarchy. Figure 6 illustrates part of the element's hierarchy for the gPodder window, and how to access the elements from inside a <var>Gpodder</var> method.
-
[[Image:gpodder_glade_2.png|frame|center|alt=Screenshot of widget tree for the gPodder window|Figure 6: Widget tree for the gPodder window]]
+
{| class="widget-code"
 +
|
 +
<br/>
 +
[[Image:gpodder_glade_2.png|frame|border|center|middle|alt=Widget tree for the gPodder window|Widget tree for the gPodder window]]
 +
<br/>
-
<source lang="python">
+
|
 +
  self.vMain
  self.vMain
  self.mainMenu
  self.mainMenu
Line 206: Line 218:
  self.scrollAvailable
  self.scrollAvailable
  self.treeAvailable
  self.treeAvailable
-
</source>
+
|}
===Changing to HildonProgram and HildonWindow===
===Changing to HildonProgram and HildonWindow===
-
The first code change in the porting exercise is to make gPodder use <code>HildonProgram</code> and <code>HildonWindow</code> classes instead of the <code>GtkWindow</code> class.
+
The first code change in the porting exercise is to make gPodder use <var>HildonProgram</var> and <var>HildonWindow</var> classes instead of the <var>GtkWindow</var> class.
-
Start by modifying the <code>gpodder.py</code> file (in the <code>gpodder-0.8.0/src/gpodder</code> directory). Since you want to use Hildon elements, you have to import its module. The following example illustrates the import, with ''++'' indicating new lines:
+
Start by modifying the <var>gpodder.py</var> file (in the <var>gpodder-0.8.0/src/gpodder</var> directory). Since you want to use Hildon elements, you have to import its module. The following example illustrates the import:
-
<pre>
+
-
61 |from libipodsync import gPodder_iPodSync
+
61 |from libipodsync import gPodder_iPodSync
-
62 |from libipodsync import ipod_supported
+
62 |from libipodsync import ipod_supported
-
63 |
+
63 |
-
++ |import hildon
+
++ |import hildon
-
++ |
+
++ |
-
64 |# for isDebugging:
+
64 |# for isDebugging:
-
65 |import libgpodder
+
65 |import libgpodder
-
</pre>
+
-
Second, add a <code>HildonProgram</code> (<code>self.app</code>) and a <code>HildonWindow</code> (<code>self.window</code>). The following example illustrates the added objects:
+
Second, add a <var>HildonProgram</var> (<var>self.app</var>) and a <var>HildonWindow</var> (<var>self.window</var>). The following example illustrates the added objects:
-
<pre>
+
-
115 |        if libgpodder.isDebugging():
+
115 |        if libgpodder.isDebugging():
-
116 |            print "A new %s has been created" % self.__class__.__name__
+
116 |            print "A new %s has been created" % self.__class__.__name__
-
117 |
+
117 |
-
++ |        self.app = hildon.Program()
+
  ++ |        self.app = hildon.Program()
-
++ |
+
  ++ |
-
++ |        self.window = hildon.Window()
+
  ++ |        self.window = hildon.Window()
-
++ |        self.window.set_title(self.gPodder.get_title())
+
  ++ |        self.window.set_title(self.gPodder.get_title())
-
++ |        self.app.add_window(self.window)
+
  ++ |        self.app.add_window(self.window)
-
++ |
+
  ++ |
-
++ |        self.vMain.reparent(self.window)
+
  ++ |        self.vMain.reparent(self.window)
-
++ |        self.gPodder.destroy()
+
  ++ |        self.gPodder.destroy()
-
++ |
+
  ++ |
-
++ |        self.window.show_all()
+
  ++ |        self.window.show_all()
-
++ |
+
  ++ |
-
118 |        #self.gPodder.set_title( self.gPodder.get_title())
+
118 |        #self.gPodder.set_title( self.gPodder.get_title())
-
119 |        #self.statusLabel.set_text( "Welcome to gPodder! Suggestions? Mail to: thp@perli.net")
+
119 |        #self.statusLabel.set_text( "Welcome to gPodder! Suggestions? Mail to: thp@perli.net")
-
120 |        # set up the rendering of the comboAvailable combobox
+
120 |        # set up the rendering of the comboAvailable combobox
-
</pre>
+
-
The <code>gPodder</code> class (<code>self</code>) has its <code>close_gpodder</code> method connected to the <code>destroy</code> signal from the original <code>gPodder</code> Gtk window. This means that you have to remove the connection from <code>gPodder</code> and put it in the new <code>Hildonwindow</code> (<code>self.window</code>).
+
The <var>gPodder</var> class (<var>self</var>) has its <var>close_gpodder</var> method connected to the <var>destroy</var> signal from the original <var>gPodder</var> Gtk window. This means that you have to remove the connection from <var>gPodder</var> and put it in the new <var>Hildonwindow</var> (<var>self.window</var>).
-
To remove the signal connection from the original <code>gPodder</code> Gtk window, open the <code>gpodder.glade</code> file (in the <code>gpodder-0.8.0/data</code> directory) and remove the connection, as shown in Figure 7.
+
To remove the signal connection from the original <var>gPodder</var> Gtk window, open the <var>gpodder.glade</var> file (in the <var>gpodder-0.8.0/data</var> directory) and remove the connection, as shown in Figure 7.
-
[[Image:gpodder_glade_3.png|frame|center|alt=destroy signal for gPodder window|Figure 7: destroy signal for gPodder window]]
+
<br/>
 +
[[Image:gpodder_glade_3.png|frame|border|center|middle|alt=destroy signal for gPodder window|destroy signal for gPodder window]]
 +
<br/>
-
The following example illustrates how you connect <code>Gpodder.close_gpodder</code> to the new <code>HildonProgram</code> (<code>self.app</code>):
+
The following example illustrates how you connect <var>Gpodder.close_gpodder</var> to the new <var>HildonProgram</var> (<var>self.app</var>):
-
<pre>
+
-
119 |
+
119 |
-
120 |        self.window = hildon.Window()
+
120 |        self.window = hildon.Window()
-
121 |        self.window.set_title(self.gPodder.get_title())
+
121 |        self.window.set_title(self.gPodder.get_title())
-
++ |        self.window.connect("destroy", self.close_gpodder)
+
  ++ |        self.window.connect("destroy", self.close_gpodder)
-
122 |        self.app.add_window(self.window)
+
122 |        self.app.add_window(self.window)
-
123 |
+
123 |
-
124 |        self.vMain.reparent(self.window)
+
124 |        self.vMain.reparent(self.window)
-
</pre>
+
-
The change from <code>GtkWindow</code> to <code>HildonProgram</code>/<code>HildonWindow</code> is now complete. Figure 8 illustrates the results if you run gPodder again.
+
The change from <var>GtkWindow</var> to <var>HildonProgram</var>/<var>HildonWindow</var> is now complete. Figure 8 illustrates the results if you run gPodder again.
-
{{ambox|
+
<div class="note"><div class="label">Note</div><div class="content"> Don't forget to run <var>python2.5 setup.py install</var> before lauching gPodder again, otherwise you will still be using the old, unmodified, version. </div></div>
-
text=Don't forget to run <code>python2.5 setup.py install</code> before lauching gPodder again, otherwise you will still be using the old, unmodified, version.}}
+
-
[[Image:gpodder_2_small.jpg|frame|border|center|middle|alt=Screenshot of gPodder using HildonProgram and HildonWindow|Figure 8: gPodder using <code>HildonProgram</code> and <code>HildonWindow</code>]]
+
<br/>
 +
[[Image:gpodder_2_small.jpg|frame|border|center|middle|alt=gPodder using HildonProgram and HildonWindow|gPodder using HildonProgram and HildonWindow]]
 +
<br/>
Note that the window fits in the screen without any broken borders, just as the "Hello World" application did.
Note that the window fits in the screen without any broken borders, just as the "Hello World" application did.
Line 275: Line 287:
===Changing to HildonWindow menu bar===
===Changing to HildonWindow menu bar===
-
This section describes how you make gPodder use Hildon's title area as its menu bar, instead of using its own GTK+ menu (a <code>GtkMenuBar</code> object).
+
This section describes how you make gPodder use Hildon's title area as its menu bar, instead of using its own GTK+ menu (a <var>GTKMenuBar</var> object).
-
In the <code>gpodder.glade</code> file, you can see that the <code>gPodder</code> window has a menu bar (a <code>GtkMenuBar</code> object) called <code>mainMenu</code>. You must move all its children (<code>menuPodcasts</code>, <code>menuChannels</code> and <code>menuHelp</code>) to the <code>HildonWindow</code>'s menu and then destroy the empty <code>mainMenu</code> menu.
+
In the <var>gpodder.glade</var> file, you can see that the <var>gPodder</var> window has a menu bar (a <var>GTKMenuBar</var> object) called <var>mainMenu</var>. You must move all its children (<var>menuPodcasts</var>, <var>menuChannels</var> and <var>menuHelp</var>) to the <var>HildonWindow</var>'s menu and then destroy the empty <var>mainMenu</var> menu.
-
To achieve this, add the following lines to the <code>gpodder.py</code> file:
+
To achieve this, add the following lines to the <var>gpodder.py</var> file:
-
<pre>
+
-
125 |        self.vMain.reparent(self.window)
+
125 |        self.vMain.reparent(self.window)
-
126 |        self.gPodder.destroy()
+
126 |        self.gPodder.destroy()
-
127 |
+
127 |
-
++ |        menu = gtk.Menu()
+
  ++ |        menu = gtk.Menu()
-
++ |        for child in self.mainMenu.get_children():
+
  ++ |        for child in self.mainMenu.get_children():
-
++ |            child.reparent(menu)
+
  ++ |            child.reparent(menu)
-
++ |        self.window.set_menu(menu)
+
  ++ |        self.window.set_menu(menu)
-
++ |
+
  ++ |
-
++ |        self.mainMenu.destroy()
+
  ++ |        self.mainMenu.destroy()
-
++ |
+
  ++ |
-
128 |        self.window.show_all()
+
128 |        self.window.show_all()
-
129 |
+
129 |
-
130 |        #self.gPodder.set_title( self.gPodder.get_title())
+
130 |        #self.gPodder.set_title( self.gPodder.get_title())
-
</pre>
+
Figure 9 illustrates the resulting menu:
Figure 9 illustrates the resulting menu:
-
[[Image:gpodder_3_small.jpg|frame|center|alt=Screenshot of gPodder using HildonWindow's menu bar|Figure 9: gPodder using HildonWindow's menu bar]]
+
<br/>
 +
[[Image:gpodder_3_small.jpg|frame|border|center|middle|alt=gPodder using HildonWindow's menu bar|gPodder using HildonWindow's menu bar]]
 +
<br/>
===Using Hildon widgets===
===Using Hildon widgets===
-
Hildon has a set of widgets for common operations, such as a color selection dialog, file chooser dialog and a time picker. Most of them provide the same functionality (or extension) as the existing GTK+ widgets. For example, <code>HildonFileChooserDialog</code> has the same purpose as <code>GtkFileChooserDialog</code>.
+
Hildon has a set of widgets for common operations, such as a color selection dialog, file chooser dialog and a time picker. Most of them provide the same functionality (or extension) as the existing GTK+ widgets. For example, <var>HildonFileChooserDialog</var> has the same purpose as <var>GtkFileChooserDialog</var>.
Replace the GTK+ widgets with the Hildon ones whenever possible, since the Hildon widgets were designed to obey Maemo's restrictions and peculiarities.
Replace the GTK+ widgets with the Hildon ones whenever possible, since the Hildon widgets were designed to obey Maemo's restrictions and peculiarities.
Line 309: Line 322:
gPodder uses a GTK+ file chooser dialog when users export their channel lists. It doesn't fit well into Maemo's environment as you can see from Figure 10.
gPodder uses a GTK+ file chooser dialog when users export their channel lists. It doesn't fit well into Maemo's environment as you can see from Figure 10.
-
[[Image:gpodder_4_small.jpg|frame|center|alt=Screenshot of gPodder using GTK's file chooser dialog|Figure 10: gPodder using GTK's file chooser dialog]]
+
<br/>
 +
[[Image:gpodder_4_small.jpg|frame|border|center|middle|alt=gPodder using GTK's file chooser dialog|gPodder using GTK's file chooser dialog]]
 +
<br/>
-
Make it use a <code>HildonFileChooserDialog</code> instead. The following example illustrates the code changes needed in <code>gpodder-0.8.0/src/gpodder/gpodder.py</code>:
+
Make it use a <var>HildonFileChooserDialog</var> instead. The following example illustrates the code changes needed in <var>gpodder-0.8.0/src/gpodder/gpodder.py</var><nowiki>: </nowiki>
-
<pre>
+
-
579    |        if len( self.channels) == 0:
+
579    |        if len( self.channels) == 0:
-
580    |          self.showMessage( _("Your channel list is empty. Nothing to export."))
+
580    |          self.showMessage( _("Your channel list is empty. Nothing to export."))
-
581    |          return
+
581    |          return
-
582 -- |        dlg = gtk.FileChooserDialog( title=_("Export to OPML"), parent = None,[...]
+
582 -- |        dlg = gtk.FileChooserDialog( title=_("Export to OPML"), parent = None,[...]
-
583 -- |        dlg.add_button( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
+
583 -- |        dlg.add_button( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
-
584 -- |        dlg.add_button( gtk.STOCK_SAVE, gtk.RESPONSE_OK)
+
584 -- |        dlg.add_button( gtk.STOCK_SAVE, gtk.RESPONSE_OK)
-
    ++ |        dlg = hildon.FileChooserDialog(self.window, gtk.FILE_CHOOSER_ACTION_SAVE);
+
    ++ |        dlg = hildon.FileChooserDialog(self.window, gtk.FILE_CHOOSER_ACTION_SAVE);
-
585    |        response = dlg.run()
+
585    |        response = dlg.run()
-
586    |        if response == gtk.RESPONSE_OK:
+
586    |        if response == gtk.RESPONSE_OK:
-
587    |            foutname = dlg.get_filename()
+
587    |            foutname = dlg.get_filename()
-
</pre>
+
-
Figure 11 illustrates the results when you select ''Menu > Channels > Export List'':
+
Figure 11 illustrates the results when you select ''Menu > Channels > Export List''<nowiki>:</nowiki>
-
[[Image:gpodder_5_small.jpg|frame|center|alt=Screenshot of gPodder using Hildon's file chooser dialog|Figure 11: gPodder using Hildon's file chooser dialog]]
+
<br/>
 +
[[Image:gpodder_5_small.jpg|frame|border|center|middle|alt=gPodder using Hildon's file chooser dialog|gPodder using Hildon's file chooser dialog]]
 +
<br/>
===Adjusting the Glade interface to fit into a small screen===
===Adjusting the Glade interface to fit into a small screen===
Line 334: Line 350:
The following change to the interface layout is necessary to make it fit into the relatively small Maemo screen:
The following change to the interface layout is necessary to make it fit into the relatively small Maemo screen:
-
* Main window (<code>gPodder</code>) Make the tab names shorter. Replace “''Downloaded Podcasts''” with “''Downloaded''” and “''Available Podcasts''” with “''Podcasts''” to make the tab names stay within screen boundaries. Figure 12 shows the tab names before (left) and after (right) that change.
+
* Main window (<var>gPodder</var>) Make the tab names shorter. Replace “''Downloaded Podcasts''” with “''Downloaded''” and “''Available Podcasts''” with “''Podcasts''” to make the tab names stay within screen boundaries. Figure 12 shows the tab names before (left) and after (right) that change.
-
[[Image:gpodder_6_small.jpg|frame|center|alt=Screenshot of gPodder with shortened tab names|Figure 12: gPodder with shortened tab names]]
+
<br/>
 +
[[Image:gpodder_6_small.jpg|frame|border|center|middle|alt=gPodder with shortened tab names|gPodder with shortened tab names]]
 +
<br/>
===Using hardware keys===
===Using hardware keys===
Line 349: Line 367:
* Window state changes, to know whether the window is already in full screen mode.
* Window state changes, to know whether the window is already in full screen mode.
-
To make gPodder respond correctly when the user presses the <var>Full screen</var> hardware key you have to make the following changes to <code>gpodder-0.8.0/src/gpodder/gpodder.py</code>:
+
To make gPodder respond correctly when the user presses the <var>Full screen</var> hardware key you have to make the following changes to <var>gpodder-0.8.0/src/gpodder/gpodder.py</var><nowiki>: </nowiki>
1. Connect to the signals and set a variable to monitor whether the application is in full screen mode.
1. Connect to the signals and set a variable to monitor whether the application is in full screen mode.
-
<pre>
+
-
120 |        self.window = hildon.Window()
+
120 |        self.window = hildon.Window()
-
121 |        self.window.set_title(self.gPodder.get_title())
+
121 |        self.window.set_title(self.gPodder.get_title())
-
122 |        self.window.connect("destroy", self.close_gpodder)
+
122 |        self.window.connect("destroy", self.close_gpodder)
-
++ |        self.window.connect("key-press-event", self.on_key_press)
+
  ++ |        self.window.connect("key-press-event", self.on_key_press)
-
++ |        self.window.connect("window-state-event", self.on_window_state_change)
+
  ++ |        self.window.connect("window-state-event", self.on_window_state_change)
-
++ |        self.window_in_fullscreen = False #The window isn't in full screen mode initially.
+
  ++ |        self.window_in_fullscreen = False #The window isn't in full screen mode initially.
-
123 |        self.app.add_window(self.window)
+
123 |        self.app.add_window(self.window)
-
124 |
+
124 |
-
125 |        self.vMain.reparent(self.window)
+
125 |        self.vMain.reparent(self.window)
-
</pre>
+
 
2. Implement the callback to monitor window state changes.
2. Implement the callback to monitor window state changes.
-
<pre>
+
-
575 |            self.showMessage( _("Could not delete channel.\nProbably no channel is selected."))
+
575 |            self.showMessage( _("Could not delete channel.\nProbably no channel is selected."))
-
576 |    #-- Gpodder.on_itemRemoveChannel_activate }
+
576 |    #-- Gpodder.on_itemRemoveChannel_activate }
-
577 |
+
577 |
-
++ |    def on_window_state_change(self, widget, event, *args):
+
  ++ |    def on_window_state_change(self, widget, event, *args):
-
++ |        if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
+
  ++ |        if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
-
++ |            self.window_in_fullscreen = True
+
  ++ |            self.window_in_fullscreen = True
-
++ |        else:
+
  ++ |        else:
-
++ |            self.window_in_fullscreen = False
+
  ++ |            self.window_in_fullscreen = False
-
++ |
+
  ++ |
-
578 |    #-- Gpodder.on_itemExportChannels_activate {
+
578 |    #-- Gpodder.on_itemExportChannels_activate {
-
579 |    def on_itemExportChannels_activate(self, widget, *args):
+
579 |    def on_itemExportChannels_activate(self, widget, *args):
-
580 |        if libgpodder.isDebugging():
+
580 |        if libgpodder.isDebugging():
-
</pre>
+
3. Implement the callback to monitor key presses.
3. Implement the callback to monitor key presses.
-
<pre>
+
-
581 |        else:
+
581 |        else:
-
582 |            self.window_in_fullscreen = False
+
582 |            self.window_in_fullscreen = False
-
583 |
+
583 |
-
++ |    def on_key_press(self, widget, event, *args):
+
  ++ |    def on_key_press(self, widget, event, *args):
-
++ |        if event.keyval == gtk.keysyms.F6:
+
  ++ |        if event.keyval == gtk.keysyms.F6:
-
++ |            # The "Full screen" hardware key has been pressed
+
  ++ |            # The "Full screen" hardware key has been pressed
-
++ |            if self.window_in_fullscreen:
+
  ++ |            if self.window_in_fullscreen:
-
++ |                self.window.unfullscreen ()
+
  ++ |                self.window.unfullscreen ()
-
++ |            else:
+
  ++ |            else:
-
++ |                self.window.fullscreen ()
+
  ++ |                self.window.fullscreen ()
-
++ |
+
  ++ |
-
584 |    #-- Gpodder.on_itemExportChannels_activate {
+
584 |    #-- Gpodder.on_itemExportChannels_activate {
-
585 |    def on_itemExportChannels_activate(self, widget, *args):
+
585 |    def on_itemExportChannels_activate(self, widget, *args):
-
586 |        if libgpodder.isDebugging():
+
586 |        if libgpodder.isDebugging():
-
</pre>
+
-
{{ambox|
+
<div class="frame note"><div class="label">Note</div><div class="content">The ''Full screen'' hardware key maps to the F6 key on the SDK.</div></div>
-
text=The ''Full screen'' hardware key maps to the F6 key on the SDK.}}
+
Figure 13 illustrates gPodder in full screen mode.
Figure 13 illustrates gPodder in full screen mode.
-
[[Image:gpodder_7_small.jpg|frame|center|alt=Screenshot of gPodder in full screen mode|Figure 13: gPodder in full screen mode]]
+
<br/>
 +
[[Image:gpodder_7_small.jpg|frame|border|center|middle|alt=gPodder in full screen mode|gPodder in full screen mode]]
 +
<br/>
==Introduction to LibOSSO==
==Introduction to LibOSSO==
-
 
-
{{main|Documentation/Maemo 5 Developer Guide/Application Development/LibOSSO library}}
 
LibOSSO is the basic library containing required and helpful functions for Maemo applications. One of LibOSSO's main features is RPC (Remote Procedure Calls) services (as it "wraps" D-Bus <ref name="dbus">[http://www.freedesktop.org/wiki/Software/dbus D-Bus]</ref>). In addition, LibOSSO provides access to low-level hardware functions, such as turning on (or keeping on) the display, autosaving, state saving and system time.
LibOSSO is the basic library containing required and helpful functions for Maemo applications. One of LibOSSO's main features is RPC (Remote Procedure Calls) services (as it "wraps" D-Bus <ref name="dbus">[http://www.freedesktop.org/wiki/Software/dbus D-Bus]</ref>). In addition, LibOSSO provides access to low-level hardware functions, such as turning on (or keeping on) the display, autosaving, state saving and system time.
Line 415: Line 430:
All Maemo-compliant applications must use LibOSSO to respond coherently to system changes and signals, such as the battery low, pre-shutdown and state saving signals.
All Maemo-compliant applications must use LibOSSO to respond coherently to system changes and signals, such as the battery low, pre-shutdown and state saving signals.
-
This section describes how remote procedure calls are implemented using LibOSSO. The example is divided in two different sample applications: <code>osso_test_sender.py</code> and <code>osso_test_receiver.py</code>.
+
This section describes how remote procedure calls are implemented using LibOSSO. The example is divided in two different sample applications: <var>osso_test_sender.py</var> and <var>osso_test_receiver.py</var>.
The sender sample application sends an RPC message when a button is clicked. D-Bus interprets the message and sends the RPC to the receiver sample application, which displays a system notification stating that it has received the RPC.
The sender sample application sends an RPC message when a button is clicked. D-Bus interprets the message and sends the RPC to the receiver sample application, which displays a system notification stating that it has received the RPC.
Line 423: Line 438:
The sender is simply a button occupying the whole window. When you click it, an RPC is sent to the receiver sample application.
The sender is simply a button occupying the whole window. When you click it, an RPC is sent to the receiver sample application.
-
Create a <code>osso_test_sender.py</code> file with the following content (or download it [http://pymaemo.garage.maemo.org/documentation/pymaemo_tutorial/examples/osso_test_sender.py here]).
+
Create a <var>osso_test_sender.py</var> file with the following content (or download it [http://pymaemo.garage.maemo.org/documentation/pymaemo_tutorial/examples/osso_test_sender.py here]).
-
 
+
-
<source lang="python">
+
-
#!/usr/bin/python2.5
+
-
import osso
+
-
import hildon
+
-
import gtk
+
-
 
+
-
def send_rpc(widget, osso_c):
+
-
    osso_rpc = osso.Rpc(osso_c)
+
-
    osso_rpc.rpc_run("spam.eggs.osso_test_receiver",
+
-
        "/spam/eggs/osso_test_receiver",
+
-
        "spam.eggs.osso_test_receiver", "do_something")
+
-
    print "RPC sent"
+
-
osso_c = osso.Context("osso_test_sender", "0.0.1", False)
+
<nowiki>
-
window = hildon.Window()
+
#!/usr/bin/python2.5
-
window.connect("destroy", gtk.main_quit)
+
import osso
-
send_button = gtk.Button("Send RPC")
+
import hildon
-
window.add(send_button)
+
import gtk
-
send_button.connect("clicked", send_rpc, osso_c)
+
-
window.show_all()
+
def send_rpc(widget, osso_c):
-
gtk.main()
+
    osso_rpc = osso.Rpc(osso_c)
-
</source>
+
    osso_rpc.rpc_run("spam.eggs.osso_test_receiver",
 +
        "/spam/eggs/osso_test_receiver",
 +
        "spam.eggs.osso_test_receiver", "do_something")
 +
    print "RPC sent"
 +
 +
osso_c = osso.Context("osso_test_sender", "0.0.1", False)
 +
window = hildon.Window()
 +
window.connect("destroy", gtk.main_quit)
 +
send_button = gtk.Button("Send RPC")
 +
window.add(send_button)
 +
send_button.connect("clicked", send_rpc, osso_c)
 +
window.show_all()
 +
gtk.main()</nowiki>
===Receiver sample application===
===Receiver sample application===
Line 452: Line 466:
The receiver is an application that has no GUI (no window), it runs unseen in the background waiting for an RPC. When it receives one, it uses LibOSSO to display an information banner notifying the user that an RPC has been received.
The receiver is an application that has no GUI (no window), it runs unseen in the background waiting for an RPC. When it receives one, it uses LibOSSO to display an information banner notifying the user that an RPC has been received.
-
Create a <var>osso_test_receiver.py</var> file with the following content (or download it [http://pymaemo.garage.maemo.org/documentation/pymaemo_tutorial/examples/osso_test_receiver.py here]). '''Don't forget to set this file as executable, otherwise D-Bus will not be able to start it. You can do this using: <code>chmod +x osso_test_receiver.py</code>'''
+
Create a <var>osso_test_receiver.py</var> file with the following content (or download it [http://pymaemo.garage.maemo.org/documentation/pymaemo_tutorial/examples/osso_test_receiver.py here]). '''Don't forget to set this file as executable, otherwise DBUS will not be able to start it. You can do this using: chmod +x osso_test_receiver.py'''
-
<source lang="python">
+
<nowiki>
-
#!/usr/bin/python2.5
+
#!/usr/bin/python2.5
-
import osso
+
import osso
-
import gtk
+
import gtk
 +
 +
def callback_func(interface, method, arguments, user_data):
 +
    print "RPC received"
 +
    osso_c = user_data
 +
    osso_sysnote = osso.SystemNote(osso_c)
 +
    osso_sysnote.system_note_infoprint("osso_test_receiver: Received an RPC to %s." % method)
 +
 +
osso_c = osso.Context("osso_test_receiver", "0.0.1", False)
 +
print "osso_test_receiver started"
 +
osso_rpc = osso.Rpc(osso_c)
 +
osso_rpc.set_rpc_callback("spam.eggs.osso_test_receiver",
 +
    "/spam/eggs/osso_test_receiver",
 +
    "spam.eggs.osso_test_receiver", callback_func, osso_c)
 +
gtk.main()</nowiki>
-
def callback_func(interface, method, arguments, user_data):
+
The receiver also must register itself as a D-Bus service. Create a <var>osso_test_receiver.service</var> file with the following content.
-
    print "RPC received"
+
-
    osso_c = user_data
+
-
    osso_sysnote = osso.SystemNote(osso_c)
+
-
    osso_sysnote.system_note_infoprint("osso_test_receiver: Received an RPC to %s." % method)
+
-
osso_c = osso.Context("osso_test_receiver", "0.0.1", False)
+
-
print "osso_test_receiver started"
+
[D-BUS Service]
-
osso_rpc = osso.Rpc(osso_c)
+
Name=spam.eggs.osso_test_receiver
-
osso_rpc.set_rpc_callback("spam.eggs.osso_test_receiver",
+
Exec={SOME_DIRECTORY_HIERARCHY}/osso_test_receiver.py
-
    "/spam/eggs/osso_test_receiver",
+
-
    "spam.eggs.osso_test_receiver", callback_func, osso_c)
+
-
gtk.main()
+
-
</source>
+
-
The receiver also must register itself as a D-Bus service. Create a <code>osso_test_receiver.service</code> file with the following content.
+
Replace <var>{SOME_DIRECTORY_HIERARCHY}</var> with the directory where <var>osso_test_receiver.py</var> is located.
-
<pre>
+
Add the <var>osso_test_receiver.service</var> file to <var>/usr/share/dbus-1/services</var>.
-
[D-BUS Service]
+
-
Name=spam.eggs.osso_test_receiver
+
-
Exec={SOME_DIRECTORY_HIERARCHY}/osso_test_receiver.py
+
-
</pre>
+
-
 
+
-
Replace <code>{SOME_DIRECTORY_HIERARCHY}</code> with the directory where <code>osso_test_receiver.py</code> is located.
+
-
 
+
-
Add the <code>osso_test_receiver.service</code> file to <code>/usr/share/dbus-1/services</code>.
+
===Running the sample applications===
===Running the sample applications===
-
First make sure that D-Bus recognises the new D-Bus service ( <code>spam.eggs.osso_test_receiver</code>). To do this, restart your Maemo environment (which, in turn, will restart D-Bus) by using the following command on the Scratchbox console:
+
First make sure that D-Bus recognises the new D-Bus service ( <var>spam.eggs.osso_test_receiver</var>). To do this, restart your Maemo environment (which, in turn, will restart D-Bus) by using the following command on the Scratchbox console:
  [sbox-TARGET_NAME: ~] > af-sb-init.sh restart
  [sbox-TARGET_NAME: ~] > af-sb-init.sh restart
-
Then run <code>osso_test_sender.py</code> with the following command (assuming that it is in your home directory):
+
Then run <var>osso_test_sender.py</var> with the following command (assuming that it is in your home directory):
  [sbox-TARGET_NAME: ~] > run-standalone.sh ./osso_test_sender.py
  [sbox-TARGET_NAME: ~] > run-standalone.sh ./osso_test_sender.py
-
Figure 14 illustrates what now happens every time you click the ''Send RPC'' button.
+
Figure 12 illustrates what now happens every time you click the ''Send RPC'' button.
-
[[Image:libosso_tutorial_small.jpg|frame|center|alt=Screenshot of LibOSSO sample application|Figure 14: LibOSSO sample application]]
+
<br/>
 +
[[Image:libosso_tutorial_small.jpg|frame|border|center|middle|alt=LibOSSO sample application|LibOSSO sample application]]
 +
<br/>
Note that you do not have to manually run <var>osso_test_receiver.py</var>, as D-Bus does it automatically. Due to its extreme simplicity, the once instantiated <var>osso_test_receiver.py</var> continues running in background until you manually kill it from the Scratchbox console.
Note that you do not have to manually run <var>osso_test_receiver.py</var>, as D-Bus does it automatically. Due to its extreme simplicity, the once instantiated <var>osso_test_receiver.py</var> continues running in background until you manually kill it from the Scratchbox console.
 +
==Distributing Python applications==
==Distributing Python applications==
Line 508: Line 523:
===Distributing a "Hello PyMaemo!" application===
===Distributing a "Hello PyMaemo!" application===
-
This section describes the process of creating a Maemo package by showing how to package a simple "hello world" style application. Create a <code>hello-pymaemo</code> file (without the "<code>.py</code>" suffix) with the following content.
+
This section describes the process of creating a Maemo package by showing how to package a simple "hello world" style application. Create a <var>hello-pymaemo</var> file (without the "<var>.py</var>" suffix) with the following content.
-
<source lang="python">
+
<nowiki>
-
#!/usr/bin/env python2.5
+
#!/usr/bin/env python2.5
 +
 +
import gtk
 +
import hildon
 +
 +
window = hildon.Window ()
 +
window.set_title ("Hello Maemo!")
 +
window.connect("destroy", gtk.main_quit)
 +
 +
button = gtk.Button ("Hello Python for Maemo!")
 +
window.add (button)
 +
 +
window.show_all ()
 +
gtk.main ()</nowiki>
-
import gtk
+
Make the file executable by running the <var>chmod +x hello-pymaemo</var> command. This ensures that you can run the script in the same way as a regular binary application.
-
import hildon
+
-
 
+
-
window = hildon.Window ()
+
-
window.set_title ("Hello Maemo!")
+
-
window.connect("destroy", gtk.main_quit)
+
-
 
+
-
button = gtk.Button ("Hello Python for Maemo!")
+
-
window.add (button)
+
-
 
+
-
window.show_all ()
+
-
gtk.main ()
+
-
</source>
+
-
 
+
-
Make the file executable by running the <code>chmod +x hello-pymaemo</code> command. This ensures that you can run the script in the same way as a regular binary application.
+
====Desktop integration====
====Desktop integration====
-
{{main|Desktop file format}}
+
You must have an icon for the application. The icon is shown in the menu entry and in the task navigator bar. The icon must be a 26x26 pixels PNG image with a transparent background, such as the [http://www.maemo.org/platform/docs/python-bora/images/pymaemo_bora/hello_icon_26x26.png  example icon] shown in Figure 13:
-
 
+
-
You must have an icon for the application. The icon is shown in the menu entry and in the task navigator bar. The icon must be a 26×26 pixels PNG image with a transparent background, such as the [http://www.maemo.org/platform/docs/python-bora/images/pymaemo_bora/hello_icon_26x26.png  example icon] shown in Figure 15:
+
-
[[Image:hello_icon_26x26.jpg|frame|center|alt=Hello PyMaemo icon|Figure 15: Hello PyMaemo icon]]
+
<br/>
 +
[[Image:hello_icon_26x26.jpg|frame|border|center|middle|alt=Hello PyMaemo icon|Hello PyMaemo icon]]
 +
<br/>
-
Name the icon <code>hello_icon_26x26.png</code>.
+
Name the icon <var>hello_icon_26x26.png</var>.
-
The menu entry is a <code>.desktop</code> file with the following content for the application:
+
The menu entry is a <var>.desktop</var> file with the following content for the application:
-
<pre>
+
-
[Desktop Entry]
+
[Desktop Entry]
-
Version=1.0.0
+
Version=1.0.0
-
Encoding=UTF-8
+
Encoding=UTF-8
-
Name=Hello PyMaemo!
+
Name=Hello PyMaemo!
-
Exec=/usr/bin/hello-pymaemo
+
Exec=/usr/bin/hello-pymaemo
-
Icon=hello_icon_26x26
+
Icon=hello_icon_26x26
-
Type=Application
+
Type=Application
-
</pre>
+
-
{{ambox|
+
<div class="frame note"><div class="label">Note</div><div class="content"> Be very careful when writing a desktop file, since the system is very sensitive to typographical errors and misplaced whitespaces in it. A faulty desktop file will simply fail to show its menu entry without yielding any errors. </div></div>
-
text=Be very careful when writing a desktop file, since the system is very sensitive to typographical errors and misplaced whitespaces in it. A faulty desktop file will simply fail to show its menu entry without yielding any errors.}}
+
-
{| class="wikitable"
+
{| class="real-table"
-
|+ Table 1. <code>.desktop</code> file fields
+
|+ Table 1. <var>.desktop</var> file fields
|-
|-
! Field name
! Field name
! Description
! Description
|-
|-
-
| <code>Version</code>
+
|
 +
<var>Version</var>
| Application version
| Application version
|-
|-
-
| <code>Encoding</code>
+
|
-
|Character encoding. Must always be <code>UTF8</code>.
+
<var>Encoding</var>
 +
|
 +
Character encoding. Must always be <var>UTF8</var>.
|-
|-
-
| <code>Name</code>
+
|
 +
<var>Name</var>
| Application's name
| Application's name
|-
|-
-
| <code>Exec</code>
+
|
 +
<var>Exec</var>
| File to be executed when the application is called
| File to be executed when the application is called
|-
|-
-
| <code>Icon</code>
+
|
-
| Application's icon. Only the name of the file '''without''' its suffix (<code>.png</code>).
+
<var>Icon</var>
 +
|
 +
Application's icon. Only the name of the file '''without''' its suffix (<var>.png</var>).
|-
|-
-
| <code>Type</code>
+
|
-
| "<code>Application</code>"since it is an entry for an application
+
<var>Type</var>
 +
|
 +
"<var>Application</var>"since it is an entry for an application
|}
|}
====Copying files to their locations====
====Copying files to their locations====
-
Use Python Distribution Utilities ("Distutils") to copy the files to their proper locations. Create a <code>setup.py</code> file with the following content.
+
Use Python Distribution Utilities ("Distutils") to copy the files to their proper locations. Create a <var>setup.py</var> file with the following content.
-
<source lang="python">
+
-
from distutils.core import setup
+
from distutils.core import setup
 +
 +
setup(name='hello-pymaemo',
 +
        version='1.0.0',
 +
        scripts=['hello-pymaemo'],
 +
        data_files = [
 +
                    ('share/pixmaps',            ['hello_icon_26x26.png']),
 +
                    ('share/applications/hildon', ['hello-pymaemo.desktop']),
 +
                    ]
 +
      )
-
setup(name='hello-pymaemo',
+
In the Scratchbox console, issue the <var>python2.5 setup.py install</var> command to achieve the following result:
-
      version='1.0.0',
+
-
      scripts=['hello-pymaemo'],
+
-
      data_files = [
+
-
                    ('share/pixmaps',            ['hello_icon_26x26.png']),
+
-
                    ('share/applications/hildon', ['hello-pymaemo.desktop']),
+
-
                    ]
+
-
      )
+
-
</source>
+
-
In the Scratchbox console, issue the <code>python2.5 setup.py install</code> command to achieve the following result:
+
* <var>hello-pymaemo</var> is copied to <var>/usr/bin</var>
 +
* <var>hello_icon_26x26.png</var> is copied to <var>/usr/share/pixmaps</var>
 +
* <var>hello-pymaemo.desktop</var> is copied to <var>/usr/share/applications/hildon</var>
-
* <code>hello-pymaemo</code> is copied to <code>/usr/bin</code>
+
After you have run the command, the application is actually installed in your system (in Scratchbox, you have to run <var>af-sb-init.sh restart</var> before calling your application from the menu). You can access it from the ''Extras'' menu.
-
* <code>hello_icon_26x26.png</code> is copied to <code>/usr/share/pixmaps</code>
+
-
* <code>hello-pymaemo.desktop</code> is copied to <code>/usr/share/applications/hildon</code>
+
-
 
+
-
After you have run the command, the application is actually installed in your system (in Scratchbox, you have to run <code>af-sb-init.sh restart</code> before calling your application from the menu). You can access it from the ''Extras'' menu.
+
The problem with this approach is that if you want to remove the application, you have to manually delete every file from its corresponding directory, which is not practical. It is also not practical to distribute an application that way, since every user has to manually run the command (or a script that does it).
The problem with this approach is that if you want to remove the application, you have to manually delete every file from its corresponding directory, which is not practical. It is also not practical to distribute an application that way, since every user has to manually run the command (or a script that does it).
Line 612: Line 632:
====Creating a Debian package====
====Creating a Debian package====
-
{{main|Packaging}}
+
When creating a Debian package, the first step is to put all files (the hello-pymaemo script, and the png, desktop and service files) in an empty directory called <var>hello-pymaemo-1.0.0</var>. The directory name must follow the <var><package-name>-<app-version></var> convention. This means that the package you are creating for the hello world application is called <var>hello-pymaemo</var>.
-
 
+
-
When creating a Debian package, the first step is to put all files (the hello-pymaemo script, and the png, desktop and service files) in an empty directory called <code>hello-pymaemo-1.0.0</code>. The directory name must follow the <code><package-name>-<app-version></code> convention. This means that the package you are creating for the hello world application is called <code>hello-pymaemo</code>.
+
'''Note:''' there is a module that allows deb creation directly from setup.py [http://home.cfl.rr.com/genecash/nokia/making_packages.html here].
'''Note:''' there is a module that allows deb creation directly from setup.py [http://home.cfl.rr.com/genecash/nokia/making_packages.html here].
-
As Debian packages use makefiles (<code>Makefile</code>) instead of Python Distutils (<code>setup.py</code>), you have to write a <code>Makefile</code> to act as an interface between the Debian package system and the <code>setup.py</code>. The file is very simple; it merely issues commands to <code>setup.py</code> according to make's target. If you have no knowledge of make files, see Chapters 1 and 2 in ''GNU Make Manual''<ref name="make">[http://www.gnu.org/software/make/manual GNU Make Manual]</ref>.
+
As Debian packages use makefiles (<var>Makefile</var>) instead of Python Distutils (<var>setup.py</var>), you have to write a <var>Makefile</var> to act as an interface between the Debian package system and the <var>setup.py</var>. The file is very simple; it merely issues commands to <var>setup.py</var> according to make's target. If you have no knowledge of make files, see Chapters 1 and 2 in ''GNU Make Manual''<ref name="make">[http://www.gnu.org/software/make/manual GNU Make Manual]</ref>.
Create a <var>Makefile</var> file with the following content (indentation with tabulators):
Create a <var>Makefile</var> file with the following content (indentation with tabulators):
-
<source lang="make">
+
-
all:
+
all:
-
python2.5 setup.py build
+
python2.5 setup.py build
-
clean:
+
clean:
-
python2.5 setup.py clean --all
+
python2.5 setup.py clean --all
-
install:
+
install:
-
python2.5 setup.py install --root $(DESTDIR)
+
python2.5 setup.py install --root $(DESTDIR)
-
</source>
+
-
In Scratchbox console (inside the <code>hello-pymaemo-1.0.0</code> directory), enter the following command:
+
In Scratchbox console (inside the <var>hello-pymaemo-1.0.0</var> directory), enter the following command:
  [sbox-TARGET_NAME: ~/hello-pymaemo-1.0.0] > dh_make -e your.email@somewhere.com
  [sbox-TARGET_NAME: ~/hello-pymaemo-1.0.0] > dh_make -e your.email@somewhere.com
Line 637: Line 654:
The system displays the following output:
The system displays the following output:
-
<pre>
+
-
Type of package: single binary, multiple binary, library, or kernel module?  [s/m/l/k] s
+
Type of package: single binary, multiple binary, library, or kernel module?  [s/m/l/k] s
-
Maintainer name : unknown
+
Maintainer name : unknown
-
Email-Address  : your.email@somewhere.com
+
Email-Address  : your.email@somewhere.com
-
Date            : Thu, 18 May 2006 13:58:04 -0300
+
Date            : Thu, 18 May 2006 13:58:04 -0300
-
Package Name    : hello-pymaemo
+
Package Name    : hello-pymaemo
-
Version        : 1.0.0
+
Version        : 1.0.0
-
Type of Package : Single
+
Type of Package : Single
-
Hit <enter> to confirm:
+
Hit <enter> to confirm:
-
Done. Please edit the files in the debian/ subdirectory now.
+
Done. Please edit the files in the debian/ subdirectory now.
-
You should also check that the hello-pymaemo Makefiles install into $DESTDIR and not in / .
+
You should also check that the hello-pymaemo Makefiles install into $DESTDIR and not in / .
-
</pre>
+
-
Choose "single binary" as package type. In case the "<code>--root $(DESTDIR)</code>" part of the makefile is not clear to you, the last sentece in the output is meant to clarify the situation.
+
Choose "single binary" as package type. In case the "<var>--root $(DESTDIR)</var>" part of the makefile is not clear to you, the last sentece in the output is meant to clarify the situation.
-
The <code>dh_make</code> command creates a <code>debian</code> subdirectory containing multiple configuration text files, most of which are templates that can be removed, since the application does not use them. In addition, the command makes a copy of the original directory, calling it <code>hello-pymaemo-1.0.0.orig</code>.
+
The <var>dh_make</var> command creates a <var>debian</var> subdirectory containing multiple configuration text files, most of which are templates that can be removed, since the application does not use them. In addition, the command makes a copy of the original directory, calling it <var>hello-pymaemo-1.0.0.orig</var>.
-
Table 2 lists the files needed in <code>hello-pymaemo-1.0.0/debian</code> (others can be removed):
+
Table 2 lists the files needed in <var>hello-pymaemo-1.0.0/debian</var> (others can be removed):
-
{| class="wikitable"
+
{| class="real-table"
-
|+ Table 2. Required files for the example application package
+
|+ Table 2. Needed files for the example application package
|-
|-
-
! File in <code>./debian</code>
+
! File in <var>./debian</var>
! Description
! Description
|-
|-
-
| <code>changelog</code>
+
| changelog
| Application's change log
| Application's change log
|-
|-
-
| <code>compat</code>
+
| compat
| Debian helper compatibily version. Leave it as it is.
| Debian helper compatibily version. Leave it as it is.
|-
|-
-
| <code>control</code>
+
| control
| Describes the packages to be made. For more information, see the paragraphs below the table.
| Describes the packages to be made. For more information, see the paragraphs below the table.
|-
|-
-
| <code>copyright</code>
+
| copyright
| Copyright text. Fill in the blanks.
| Copyright text. Fill in the blanks.
|-
|-
-
| <code>rules</code>
+
| rules
| A makefile containing the rules to build all kinds of packages (such as source and binary). For more information, see the paragraphs below the table.
| A makefile containing the rules to build all kinds of packages (such as source and binary). For more information, see the paragraphs below the table.
|}
|}
-
The key files in <code>./debian</code> are <code>control</code> and <code>rules</code>. They contain a generic template showing what they must look like. In <code>control</code>, you must simply fill in the blanks and, in <code>rules</code>, you essentially need to remove unwanted and unnecessary code.
+
The key files in <var>./debian</var> are <var>control</var> and <var>rules</var>. They contain a generic template showing what they must look like. In <var>control</var>, you must simply fill in the blanks and, in <var>rules</var>, you essentially need to remove unwanted and unnecessary code.
-
The following example illustrates what the <code>control</code> file for the example application must contain:
+
The following example illustrates what the <var>control</var> file for the example application must contain:
-
<pre>
+
-
Source: hello-pymaemo
+
Source: hello-pymaemo
-
Section: user/other
+
Section: user/other
-
Priority: optional
+
Priority: optional
-
Maintainer: My Name <your.email@somewhere.com>
+
Maintainer: My Name <your.email@somewhere.com>
-
Build-Depends: debhelper (>= 4.0.0), python2.5-dev
+
Build-Depends: debhelper (>= 4.0.0), python2.5-dev
-
Standards-Version: 3.6.0
+
Standards-Version: 3.6.0
 +
 +
Package: hello-pymaemo
 +
Architecture: all
 +
Depends: python2.5, python2.5-hildon, python2.5-gtk2
 +
Description: A simple "Hello Python for Maemo!"
 +
  A very simple application consisting of a single button, containing the
 +
  text "Hello Python for Maemo!".
 +
XB-Maemo-Icon-26:
 +
  iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABmJLR0QA/wD/AP+g
 +
  vaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH1gURDQoYya0JlwAAAU9J
 +
  REFUSMftlL1KA0EUhb/NZl/ggnHQxsJUxt5CUucVJCCkDfgyKdIGG5/A0s5HEBtJ
 +
  EdDAQGBgmw0YJmMzgXXYza5CtNkDW9zZw5z7c+ZCgwb/Ai3i9sVl/Bq8RIs4LRK1
 +
  gJDsKvJyNXmJMuYTsMoY1zpgozaABdYArQNPZQ1kfyGU7SpqVwxzAMwABWhgpIwp
 +
  4vWBB+AUWAI3ypjnfEXtPU4bLKx9vErTeCeiRSYF+fTn1j5dp2myE9EiU+DSi3wX
 +
  ymeqRQAmZ3EcA5E/fgO6BULT8zhOcrwXoJdrXRa2Lgps2y2odAUcBUIXQdz78YyC
 +
  SldAp8b7+bXrIv91qjZBietqCc2DjbAt4b2WxJkyZljVujlwp0U0cPxuLcAIuC+4
 +
  dKxFlsDJarvdAGP/b6hFnDImYs+uG3hbO2AB3Jbsur63tQM+fFx3bzZocEB8AdV2
 +
  gJBZgKTwAAAAAElFTkSuQmCC
-
Package: hello-pymaemo
+
The <var>XB-Maemo-Icon-26</var> field contains the application icon file (in this case, <var>hello_icon_26x26.png</var>) encoded in base64. This is the icon that is shown in the Application Installer, next to the package name. To do this encoding in Linux, you can use either <var>uuencode</var> or <var> openssl</var> (there can be more suitable applications). Maemos's Scratchbox rootstrap is delivered with <var>uuencode</var>. Do not forget to put a white space at the beginning of each line containing the icon-encoded text. The white space serves as indentation. The same rule stands for the long package description (<var> A very simple application[...]</var>).
-
Architecture: all
+
-
Depends: python2.5, python2.5-hildon, python2.5-gtk2
+
-
Description: A simple "Hello Python for Maemo!"
+
-
A very simple application consisting of a single button, containing the
+
-
text "Hello Python for Maemo!".
+
-
XB-Maemo-Icon-26:
+
-
iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABmJLR0QA/wD/AP+g
+
-
vaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH1gURDQoYya0JlwAAAU9J
+
-
REFUSMftlL1KA0EUhb/NZl/ggnHQxsJUxt5CUucVJCCkDfgyKdIGG5/A0s5HEBtJ
+
-
EdDAQGBgmw0YJmMzgXXYza5CtNkDW9zZw5z7c+ZCgwb/Ai3i9sVl/Bq8RIs4LRK1
+
-
gJDsKvJyNXmJMuYTsMoY1zpgozaABdYArQNPZQ1kfyGU7SpqVwxzAMwABWhgpIwp
+
-
4vWBB+AUWAI3ypjnfEXtPU4bLKx9vErTeCeiRSYF+fTn1j5dp2myE9EiU+DSi3wX
+
-
ymeqRQAmZ3EcA5E/fgO6BULT8zhOcrwXoJdrXRa2Lgps2y2odAUcBUIXQdz78YyC
+
-
SldAp8b7+bXrIv91qjZBietqCc2DjbAt4b2WxJkyZljVujlwp0U0cPxuLcAIuC+4
+
-
dKxFlsDJarvdAGP/b6hFnDImYs+uG3hbO2AB3Jbsur63tQM+fFx3bzZocEB8AdV2
+
-
gJBZgKTwAAAAAElFTkSuQmCC
+
-
</pre>
+
-
The <code>XB-Maemo-Icon-26</code> field contains the application icon file (in this case, <code>hello_icon_26x26.png</code>) encoded in base64. This is the icon that is shown in the Application Installer, next to the package name. To do this encoding in Linux, you can use either <code>uuencode</code> or <code>openssl</code> (there can be more suitable applications). Maemos's Scratchbox rootstrap is delivered with <code>uuencode</code>. Do not forget to put a white space at the beginning of each line containing the icon-encoded text. The white space serves as indentation. The same rule stands for the long package description (<code> A very simple application[...]</code>).
+
The Application Installer only shows packages in the <var>user</var> section. Thus, your <var>Section:</var> field in the <var>control </var> file must have the <var>Section: user/<SUBSECTION></var> syntax, where <var><SUBSECTION></var> is arbitrary.
-
The Application Installer only shows packages in the <code>user</code> section. Thus, your <code>Section:</code> field in the <code>control</code> file must have the <code>Section: user/<SUBSECTION></code> syntax, where <code><SUBSECTION></code> is arbitrary.
+
The following example illustrates the <var>rules</var> file for the example application:
-
The following example illustrates the <code>rules</code> file for the example application:
+
<nowiki>
 +
#!/usr/bin/make -f
 +
# -*- makefile -*-
 +
# Sample debian/rules that uses debhelper.
 +
# GNU copyright 1997 to 1999 by Joey Hess.
 +
 +
# Uncomment this to turn on verbose mode.
 +
#export DH_VERBOSE=1
 +
 +
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
 +
  INSTALL_PROGRAM += -s
 +
endif
 +
 +
configure: configure-stamp
 +
 +
configure-stamp:
 +
dh_testdir
 +
  # Add here commands to configure the package.
 +
 +
touch configure-stamp
 +
build: build-stamp
 +
 +
build-stamp: configure-stamp
 +
dh_testdir
 +
  # Add here commands to compile the package.
 +
$(MAKE)
 +
 +
touch build-stamp
 +
 +
clean:
 +
dh_testdir
 +
dh_testroot
 +
rm -f build-stamp configure-stamp
 +
 +
# Add here commands to clean up after the build process.
 +
-$(MAKE) clean
 +
 +
dh_clean
 +
 +
install: build
 +
dh_testdir
 +
dh_testroot
 +
dh_clean -k
 +
dh_installdirs
 +
 +
# Add here commands to install the package into debian/hello-pymaemo.
 +
$(MAKE) install DESTDIR=$(CURDIR)/debian/hello-pymaemo
 +
 +
# Build architecture-independent files here.
 +
 +
binary-indep: build install
 +
dh_testdir
 +
dh_testroot
 +
dh_installchangelogs
 +
dh_fixperms
 +
dh_installdeb
 +
dh_gencontrol
 +
dh_md5sums
 +
dh_builddeb
 +
 +
binary-arch: build install
 +
 +
binary: binary-indep binary-arch
 +
.PHONY: build clean binary-indep binary-arch binary install configure
 +
</nowiki>
-
<source lang="make">
+
The main point is that the <var>binary-arch</var> target was emptied and the <var>binary-indep</var> filled, since the application being 100% Python means that it is 100% architecture-independent.
-
#!/usr/bin/make -f
+
-
# -*- makefile -*-
+
-
# Sample debian/rules that uses debhelper.
+
-
# GNU copyright 1997 to 1999 by Joey Hess.
+
-
 
+
-
# Uncomment this to turn on verbose mode.
+
-
#export DH_VERBOSE=1
+
-
 
+
-
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
+
-
INSTALL_PROGRAM += -s
+
-
endif
+
-
 
+
-
configure: configure-stamp
+
-
 
+
-
configure-stamp:
+
-
dh_testdir
+
-
# Add here commands to configure the package.
+
-
 
+
-
touch configure-stamp
+
-
build: build-stamp
+
-
 
+
-
build-stamp: configure-stamp
+
-
dh_testdir
+
-
# Add here commands to compile the package.
+
-
$(MAKE)
+
-
 
+
-
touch build-stamp
+
-
 
+
-
clean:
+
-
dh_testdir
+
-
dh_testroot
+
-
rm -f build-stamp configure-stamp
+
-
 
+
-
# Add here commands to clean up after the build process.
+
-
-$(MAKE) clean
+
-
 
+
-
dh_clean
+
-
 
+
-
install: build
+
-
dh_testdir
+
-
dh_testroot
+
-
dh_clean -k
+
-
dh_installdirs
+
-
 
+
-
# Add here commands to install the package into debian/hello-pymaemo.
+
-
$(MAKE) install DESTDIR=$(CURDIR)/debian/hello-pymaemo
+
-
 
+
-
# Build architecture-independent files here.
+
-
 
+
-
binary-indep: build install
+
-
dh_testdir
+
-
dh_testroot
+
-
dh_installchangelogs
+
-
dh_fixperms
+
-
dh_installdeb
+
-
dh_gencontrol
+
-
dh_md5sums
+
-
dh_builddeb
+
-
 
+
-
binary-arch: build install
+
-
 
+
-
binary: binary-indep binary-arch
+
-
.PHONY: build clean binary-indep binary-arch binary install configure
+
-
</source>
+
-
 
+
-
The main point is that the <code>binary-arch</code> target was emptied and the <code>binary-indep</code> filled, since the application being 100% Python means that it is 100% architecture-independent.
+
All the preliminary steps are now done, and you can build the Debian package itself with the following command:
All the preliminary steps are now done, and you can build the Debian package itself with the following command:
Line 787: Line 802:
  [sbox-TARGET_NAME: ~/hello-pymaemo-1.0.0] > dpkg-buildpackage -rfakeroot
  [sbox-TARGET_NAME: ~/hello-pymaemo-1.0.0] > dpkg-buildpackage -rfakeroot
-
The system displays some output, including a couple of warnings near the end of it (about <code>XB-Maemo-Icon-26</code.) but that is normal. The parent directory now has a <code>hello-pymaemo_1.0.0-1_all.deb</code> file - your Debian package. This is the file that is distributed to Maemo devices and installed using the Application Installer.
+
The system displays some output, including a couple of warnings near the end of it (about XB-Maemo-Icon-26) but that is normal. The parent directory now has a <var> hello-pymaemo_1.0.0-1_all.deb</var> file - your Debian package. This is the file that is distributed to Maemo devices and installed using the Application Installer.
You can install your package in scratchbox using the following command:
You can install your package in scratchbox using the following command:
Line 799: Line 814:
Figure 14 shows our hello-world application properly installed and running.
Figure 14 shows our hello-world application properly installed and running.
-
[[Image:packaging_1_small.jpg|frame|center|alt=Screenshot of the "Hello World!" properly installed|Figure 14: The "Hello World!" properly installed]]
+
<br/>
 +
[[Image:packaging_1_small.jpg|frame|border|center|middle|alt=The "Hello World!" properly installed.|The "Hello World!" properly installed.]]
 +
<br/>
All files shown in this section can be downloaded [http://pymaemo.garage.maemo.org/documentation/pymaemo_tutorial/examples/hello-pymaemo_1.0.0-1.tar.gz here] and the resulting debian package [http://pymaemo.garage.maemo.org/documentation/pymaemo_tutorial/examples/hello-pymaemo_1.0.0-1_all.deb here].
All files shown in this section can be downloaded [http://pymaemo.garage.maemo.org/documentation/pymaemo_tutorial/examples/hello-pymaemo_1.0.0-1.tar.gz here] and the resulting debian package [http://pymaemo.garage.maemo.org/documentation/pymaemo_tutorial/examples/hello-pymaemo_1.0.0-1_all.deb here].
Line 808: Line 825:
<references/>
<references/>
-
 
-
[[Category:Python]]
 
-
[[Category:Diablo]]
 

Learn more about Contributing to the wiki.


Please note that all contributions to maemo.org wiki may be edited, altered, or removed by other contributors. If you do not want your writing to be edited mercilessly, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource (see maemo.org wiki:Copyrights for details). Do not submit copyrighted work without permission!


Cancel | Editing help (opens in new window)

Templates used on this page: