Welcome to Teemu’s Blog

Picture of me.

Hi, my name is Teemu Harju. I'm a 27-year-old engineer from Espoo, Finland and this is site is my contribution to the world wide web. I'll write here about all kinds of stuff that interests me. Python coding is my passion. I'm also interested in different kinds of web technologies.

Coding for Nokia 770 using Python - Part 2

Ok, the hugely popular (a minor overstatement) “Coding for Nokia 770 using Python” tutorial continues. ;) The code for part 2 example is already 180 lines, but have no fear, I’ve included the example file for download and will also go through the code and try to explain what it does. Again the “tutorial” is about developing an user interface and the actual application doesn’t do anything useful. It is meant to be more of an example than actual application. Example shows how to create buttons, panels, text area, simple dialogs and a menu using pygtk and hildon.

Since everyone loves images, I’ve included an image of the application that the example creates. To read more, click the link below the image. The example code can be downloaded from here.

UITest part 2

I’ll explain here some parts of the example code, that can be seen on the end of this post with lines numbered. First of all lets start with the 1st line, in case someone is wondering what it is. It is there only to tell what application should be executed when this script is run in a shell. So you’ll only have to type “./uitest.py” to start the application from xterm.

Then something about the actual application. First we create a class for the application called “UITest”. This is done on line 9. I’m not going further to the theory of object oriented programming, but let’s say that this is done to give a structure to the application that is easier to follow when application grows in size.

Classes always contain a constructor, that in our case is found starting from line 14. This is called each time an instance of the class (object) is created. UITest object is created on the line 179. Inside the constructor, the UITest application is created. For this you use hildon.App() and hildon.AppView() functions. Few quotes from the maemo.org tutorial help to understand what these are.

The HildonApp is the base of any Hildon application. It does not replace the standard GTK+ main window but rather encapsulates it. It handles such things as application views and menus so it is required for every maemo application.

HildonAppView is an application view, which can be compared to a window in a normal window manager desktop. It is managed by managed by the HildonApp which is it’s parent widget. HildonAppView usually fills the whole screen and one HildonApp can contain several HildonAppViews, which are considered being different “windows” of an application.

Each view has its own menu and toolbar, both of which can be configured to meet the needs of a particular view. For example calendar application might have a Day view, Month view and Add appointment view, which all are their own HildonAppViews and open at the same time.

Inside the appview you can start adding elements. It can only contain one element, so you have to add a sort of an container element to it, that of course then can contain multiple elements. In this example we use a horizontal pane that divides the application in two parts. Pane is created on line 36, by creating a HPaned object.

In this pane we will add TextView and VButtonBox objects. In a textview you can type and VButtonBox is used to easily lay out the buttons. This is done on lines 44 and 45, by calling the functions that create and set some properties for both.

To get for example buttons to do something when they are clicked, you need to attach signals to them. An example of this can be seen on line 135 and 136 where two buttons will be connected to a signal called “clicked”. You just define what function will be called when this “clicked” signal from the button is received. The functions in the example are self.about_callback and self.destroy.

To learn how to create a drop down menu for your application, look at the self.create_menu function at the line 59. The appview that was created in the constructor will be given as a parameter for this function, since it is needed to be able to create the menu for this view. Menu is created on the line 51, by calling this function.

Ok, this post is growing to be quite long already and I don’t want to bore you people. Look at the example code and read the the comment lines to help you understand what is done and why. Please post comments if you have something to ask or would do things differently. To find out what ready functions pygtk already supports, I recommend checking PyGTK 2.0 Reference Manual. This was all this time. Let’s hope that for the next part I’m able to create an application that actually does something. ;)

Python:
  1. #!/var/lib/install/usr/bin/python2.4
  2.  
  3. # Imports gtk and hildon libraries so we can create
  4. # the user interface using those.
  5. import gtk
  6. import hildon
  7.  
  8. # Creates a class called UITest
  9. class UITest:
  10.  
  11.   # This is the constructor. It is run each time
  12.   # the UITest object is created, ie. when this
  13.   # application is started.
  14.   def __init__(self):
  15.     # This is the main window of the application.
  16.     self.app = hildon.App()
  17.  
  18.     # This is a view in the main window.
  19.     # There can be multiple of these.
  20.     self.appview = hildon.AppView("View One")
  21.  
  22.     # Set the view and titles to the application
  23.     self.app.set_title("UITest")
  24.     self.app.set_two_part_title(True)
  25.     self.app.set_appview(self.appview)
  26.  
  27.     # This connects the X-mark in the top right
  28.     # corner to the function "self.destroy"
  29.     # that closes the application.
  30.     self.appview.connect("destroy", self.destroy)
  31.  
  32.     # You can only add one widget inside one view.
  33.     # This one widget then can of course contain
  34.     # multiple widgets. We use a pane that divides
  35.     # the view into two parts.
  36.     self.hpane = gtk.HPaned()
  37.  
  38.     # Sets the separator in the pane to 450
  39.     # pixels.
  40.     self.hpane.set_position(450)
  41.  
  42.     # Now we add a TextView and a ButtonBox
  43.     # to the pane.
  44.     self.hpane.add1(self.create_textview())
  45.     self.hpane.add2(self.create_buttons())
  46.  
  47.     # This adds the pane to the view.
  48.     gtk.Container.add(self.appview, self.hpane)
  49.  
  50.     # Creates and shows the menu.
  51.     self.create_menu(self.appview)
  52.  
  53.     # Shows the pane and the app itself.
  54.     self.hpane.show()
  55.     self.app.show()
  56.  
  57.   # This is our own fuction that we can use to create
  58.   # a menu for our application.
  59.   def create_menu(self, appview):
  60.     # Gets this views menu so we can add stuff
  61.     # to it.
  62.     main_menu = hildon.AppView.get_menu(appview)
  63.  
  64.     # This menu will be used as a submenu.
  65.     menu_help = gtk.Menu()
  66.  
  67.     # Creates items for the menu.
  68.     item_help = gtk.MenuItem('Help')
  69.     item_about = gtk.MenuItem('About')
  70.     item_separator = gtk.SeparatorMenuItem()
  71.     item_close = gtk.MenuItem('Close')
  72.  
  73.     # Connects signals "activate" to the menu items
  74.     # so that something actually happens when they
  75.     # are activated.
  76.     item_about.connect("activate", self.about_callback)
  77.     item_close.connect("activate", self.destroy)
  78.  
  79.     # Adds the items to the menus.
  80.     gtk.Menu.append(main_menu, item_help)
  81.     gtk.Menu.append(menu_help, item_about)
  82.     gtk.Menu.append(main_menu, item_separator)
  83.     gtk.Menu.append(main_menu, item_close)
  84.  
  85.     # Adds the submenu to the main_menu.
  86.     gtk.MenuItem.set_submenu(item_help, menu_help)
  87.  
  88.     # Shows all menu widgets.
  89.     gtk.Widget.show_all(main_menu)
  90.  
  91.   # Creates a TextView that can be used for typing
  92.   # or editing text in it.
  93.   def create_textview(self):
  94.     # Create new TextView
  95.     textview = gtk.TextView()
  96.  
  97.     # Set some properties for it.
  98.     textview.set_editable(True)
  99.     textview.set_cursor_visible(True)
  100.     textview.set_wrap_mode(gtk.WRAP_WORD)
  101.     textview.set_justification(gtk.JUSTIFY_CENTER)
  102.  
  103.     # Get the textbuffer that contains the text
  104.     # in the textview.
  105.     textbuffer = textview.get_buffer()
  106.  
  107.     # Set the text that shows inside the textview.
  108.     textbuffer.set_text('You can write here someting...')
  109.  
  110.     # Show the textview.
  111.     textview.show()
  112.  
  113.     # Return the created textview.
  114.     return textview
  115.  
  116.   # Creates a ButtonBox widget that can be used to easily
  117.   # lay out buttons.
  118.   def create_buttons(self):
  119.     # Creates a vertical ButtonBox where buttons are
  120.     # laid on top of each other.
  121.     button_box = gtk.VButtonBox()
  122.  
  123.     # Set some properties for the ButtonBox
  124.     button_box.set_layout(gtk.BUTTONBOX_SPREAD)
  125.     button_box.set_border_width(10)
  126.     button_box.set_spacing(20)
  127.  
  128.     # Create two buttons that will be added to the
  129.     # ButtonBox.
  130.     about_button = gtk.Button('About')
  131.     close_button = gtk.Button(None, gtk.STOCK_QUIT)
  132.  
  133.     # Connect signals to the buttons so that something
  134.     # actually happens when they are pressed.
  135.     about_button.connect("clicked", self.about_callback)
  136.     close_button.connect("clicked", self.destroy)
  137.  
  138.     # Add both buttons to the ButtonBox.
  139.     button_box.add(about_button)
  140.     button_box.add(close_button)
  141.  
  142.     # Show buttons and the ButtonBox.
  143.     gtk.Widget.show_all(button_box)
  144.  
  145.     # Return created ButtonBox.
  146.     return button_box
  147.  
  148.   # This is a callback function that is called when about
  149.   # menuitem or button are clicked. It shows information
  150.   # about the application using AboutDialog.
  151.   def about_callback(self, widget, data=None):
  152.     # Create the about dialog.
  153.     dialog = gtk.AboutDialog()
  154.  
  155.     # Set the properties for it.
  156.     dialog.set_name('UITest')
  157.     dialog.set_version('0.0.1')
  158.     dialog.set_comments('Describe your app here...')
  159.     dialog.set_website('http://www.teemuharju.net')
  160.  
  161.     # Show the dialog.
  162.     dialog.show()
  163.  
  164.   # This is also a callback function. This is called
  165.   # when you want to close the application.
  166.   def destroy(self, widget, data=None):
  167.     gtk.main_quit()
  168.  
  169.   # This is the main function that will start the
  170.   # application. Note that the __init__ function
  171.   # is always called before this one.
  172.   def main(self):
  173.     gtk.main()
  174.  
  175. # This creates the UITest class, ie. runs the __init__
  176. # function and then calls the main function to start
  177. # the application.
  178. if __name__ == "__main__":
  179.   uitest = UITest()
  180.   uitest.main()

28 Responses to “Coding for Nokia 770 using Python - Part 2”

  1. Antonio Says:

    Hello,

    First of all, thank you very much for your interesting tutorials :-)

    Just a question:

    when I execute “run-standalone.sh ./uitest.py” this error occurs: “/usr/bin/run-standalone.sh: 16: ./uitest.py: not found”.
    The strange thing is that the error occurs only with the second uitest.py, the one from the first tutorial works fine. Can you help me?

    Thanks

  2. Teemu Says:

    Hi Antonio,

    Are you sure you run that command in the same directory there the uitest.py file is? By typing “pwd” you can see the directory where you currently are and by typing “ls” you can see the list of files in that directory.

  3. Antonio Says:

    Sure, file is in the same directory. In fact, if I replace uitest.py (2) with uitest.py (1) it works well. Also, if I execute “cat uitest.py” it displays the code on the screen.

    So I think that something is missing into code…I don’t know…

  4. morical Says:

    Hello, it’s good to see tutorial continuing! I’m still having problems installing pymaemo, fakeroot doesn’t work even after reinstalling scratchbox.. I think first time installing scracthbox I did some part without being logged in as superuser, Do you think that could have left some kind of mark in my linux which doesn’t allow all actions for user to run even after reinstallation? I have really ran out of options as I keep trying to get it work.

    Handling of GTK and Hildon seems very straightforward and easy to use, as seen from that example. It only feeds my hunger as I have to wait so long to get my hands in it.

  5. Teemu Says:

    Hi Antonio,

    I just downloaded the script from the site and it worked fine for me. What if you try to start it without the run-standalone.sh part… just type “./uitest.py”. This has to be done on the device, not through SSH connection.

  6. Teemu Says:

    Hi morical,

    It might be something like that. You can remove the scratchbox by just removing the /scratchbox directory. At least I’ve done that couple of times. Also if you have Ubuntu, you can migtht want to check this blog. That way you can be sure that the SDK is properly installed.

  7. Zack Says:

    You do an excellent job of explaining Teemu! Great job!

    -Zack

  8. Teemu Says:

    Thanks Zack… I would like to explain even little more, but I think that these posts would grow to be too large. I sort of lear myself also while I try to explain something to others.

  9. Antonio Says:

    Hi Teemu,

    Also writing the command line “./uitest.py” it says: file not found…it’s very strange, because the file exists…I’m sure about it.

    Anyway, later I’ll download the script from the site and try again. Maybe, some errors have occured during the file manipulation. I’ll let you know.

    Thank you

  10. Teemu Says:

    Hi Antonio,

    Yeah… that is really strange. Lets hope it works after re-downloading.

  11. Antonio Says:

    Nothing to do…

    The only difference with the other uitest.py version is that the first version was written with nano editor directly into the device through ssh, while this one is downloaded from your site, then copied to the device…very strange!!!

    OK, I’ll try to rewrite the code of uitest.py with nano editor.

    Anyway, thank you very much.

  12. Teemu Says:

    Hi again Antonio :)

    Very strange… the current uitest.py on the site has also been written with nano on the device. I can’t figure out any other things you could try. :(

    I just recently noticed that if you use the tablets browser to download a text file like a python script, it forces the file extension to .html. How strange/annoying is that? God knows what else it does to the file.

  13. Antonio Says:

    Well, I can’t download the python script from the tablet browser beacuse it doesn’t save it, but just open it in the web page. So I’ve downloaded your script to a PC, renamed it as uitest.zip, then uplaoded it to a web server. From the tablet browser I’ve downloaded uitest.zip and then renamed it again uitest.py (yes, I haven’t a USB cable at the moment here :-) ).

    Maybe the renaming of the file causes the error.

  14. Antonio Says:

    Well, I’m rewriting it with nano and it seems working…

  15. Teemu Says:

    Good to hear. :) As I’ve said earlier… you learn as you type. ;)

  16. Antonio Says:

    Sure…and it’s more funny!!! ;-)

    Ok, it works great.

    Thanks again and…waiting for the next tutorial… :-)

  17. Martin N. Says:

    Hi, I just tried the script and made my first adjustments - well, it’s great fun, thank you very much!
    But at the moment I’m facing a problem: I couldn’t find any way to get the program back after minimization, since it doesn’t appear in the program list on the left side of the screen. Do you have an idea how to solve this?

    Thanks
    Martin

  18. Teemu Says:

    Hi Martin,

    That is something that I’ve noticed also. I need to check how it could be done. It is sort of the only thing that is missing for the applications to be completely “Hildonized”. I don’t know whether it is a missing feature from pymaemo or if there is something that is missing from the scripts. I’ll definetly post about it as soon as I get a solution.

  19. Martin N. Says:

    The solution could be to launch the file with a .desktop file. I have tried this for a while now, but without success. Perhaps I’m lucky tomorrow :)

  20. Martin N. Says:

    Hi Teemu, I’ve found a solution in the Maemo SDK Tutorial: http://www.maemo.org/platform/docs/tutorials/Maemo_tutorial.html#app-to-task-navigator-menu

    Based on this I created the following two files for your example:

    uitest.desktop:

    [Desktop Entry]
    Encoding=UTF-8
    Version=0.0.1
    Type=Application
    Name=UITest
    Exec=/home/user/python/uitest.py
    X-Osso-Service=example.uitest
    Icon=qgn_list_gene_default_app

    example.uitest.service:

    [D-BUS Service]
    Name=example.uitest
    Exec=/home/user/python/uitest.py

    …and created those links:

    ln -s /home/user/python/uitest.desktop /var/lib/install/etc/others-menu/extra_applications/uitest.desktop

    ln -s /home/user/python/example.uitest.service /var/lib/install/usr/lib/dbus-1.0/services/example.uitest.service

    Actually you don’t need the desktop file to have an icon in the task list, but I think it makes the hildonization complete. ;)

  21. Teemu Says:

    Nice.. big thanks Martin. I’ll make a post about it so people don’t miss this.

  22. Teemu’s Blog » Blog Archive » Coding for Nokia 770 using Python - Misc Notes Says:

    […] Also big thanks to Martin N for giving a nice example on how to make the python script fully hildonized by adding it to the start menu and also how to get the icon to the task list while your app is running. Here’s what he wrote… Hi Teemu, I’ve found a solution in the Maemo SDK Tutorial: […]

  23. Comix Says:

    VEEERY NICE TUTORIAL! :)
    I’ve started to use python instead of C++ for my maemo applications once I saw this tutorial!

    Thanks again! :-D

  24. Teemu Says:

    Hi Comix,

    Thanks, nice to hear that my tutorial has been useful. I also like coding Python much better that writing in C/C++. I like to do things simple. ;) And Python is much more easily approachable than C-programming.

  25. vk Says:

    Looks nice.

    One question, though. Given that the 770 is very limited in memory, how large are Hildon programs written in Python? Could you think of running more than one of them simultaneously?

  26. Teemu Says:

    Hi vk,

    The actual Python scripts does not take practically any space on the device. So installing the applications doesn’t take much space from the devices internal memory. Of course the Python runtime package is quite large.

    When the Python app is started, the interpreter uses the runtime memory, but I can’t tell how much. I haven’t tested running multiple Python apps in the same time. But I’d guess that it shouldn’t be much more than with other apps. Depends probably of the Python runtime implementation. Does anyone have better information?

  27. pointer Says:

    why don’t continue this tutorial? i really benefir a lot. thank you.

  28. Mark M Says:

    In answer to the problem experienced by Antonio where he could see a file, but not execute it. I have seen similar problems on Debian/Ubuntu Linux when trying to execute a script that contain Windows line ending (CR/LF) instead of Linux style one (LF).

    A decent file copy program (WinSCP) will convert Windows to Unix as it copies if you instruct it to copy as text. If you copy as binary, you will get this problem.

    Also explains why using Nano to recreate the file sorted the problem out.

    Sorry I did not find this blog last year, but maybe it will help someone in future.

Leave a Reply