Creating a ROX application

This tutorial will show you how to create simple ROX applications using Python. Of course, you can write ROX applications in any language you choose, but python is probably the easiest. Before starting, you will need ROX-Filer, and ROX-Lib (2.0 or later).

http://rox.sourceforge.net/screens/tutor.png

If you find the python code confusing, you may want to read the httpPython Tutorial first.

Making the application directory

ROX applications should be supplied as self-contained Application Directories. This makes them very easy to install and manage, even for users without root access. Creating a basic application is simple:

  1. Choose a name for your application and create a directory with that name.
  2. Create a new file inside it called `AppRun' (not `apprun' or `APPRUN'!). This file will be run when a user clicks on the application directory. For this tutorial, it should initially contain the following:
    #!/usr/bin/env python
    import findrox; findrox.version(1, 9, 2)
    import rox
     
    rox.info('Hello World!')
  3. Make the AppRun file executable (using the filer's Permissions feature).
  4. Open ROX-Lib2's Help subdirectory (choose 'Help' from the filer's menu) and copy the findrox.py file into your new directory.

Now go up to the directory containing your application and refresh the display. Your directory should have a different icon -- click on it! If all went well, you'll get a nice message window with a pretty icon.

The code in detail

Before we go on, let's take a look at the program so far:

#!/usr/bin/env python
import findrox; findrox.version(1, 9, 2)
import rox

The first line tells the system that this is a Python program. The second loads the 'findrox.py' file you copied in, which locates ROX-Lib2 and adds it to the path. You only have to import findrox once in your program (not in every file). The third line loads the main module from ROX-Lib2 (this must be done after findrox is loaded).
rox.info('Hello World!')

This uses a function from the 'rox' module imported above to display a message in a box. There are several functions available for displaying dialogs, including alert (an error box), croak (a fatal error box), and confirm (allowing the user to cancel).

Windows

As well as dialog boxes, we can create normal windows too (replace the last line with these lines):

window = rox.Window()
window.show_all()
 
rox.mainloop()

The rox.Window class is like a regular GtkWindow, except that it increments a counter each time you create one and decrements it each time a window is destroyed. This allows your application to terminate automatically when all its windows are closed.

Initially the window is hidden. Widgets aren't actually displayed on the screen until you show them. `show_all' will also show any widgets which we put inside the window, which will be handy later...

The last line enters the main loop, causing our program to wait for events (such as the window being closed, buttons being pressed, etc) and handle these events by calling any signal handlers we may have set up (see below).

When the window is closed, it calls rox.toplevel_unref() to decrease the number-of-windows counter. Since it was 1 before, it will now reach zero and the rox.mainloop() function returns. Having reached the end of the code, the process will quit. If you want to continue running even when no windows are open, you can call rox.toplevel_ref and rox.toplevel_unref manually to prevent your program quitting too soon.

Extending the program

We'll need to use the gtk module now. You should import this via the rox module to make sure you get the correct version (not the gtk+-1.2 version). The module is simply called 'g', since you'll be typing it a lot!

We'll set the title for the window and give it a border. Then, we'll add a vertical packing box and put some things in it -- put this code just before the `window.show_all()':

from rox import g
 
window.set_title('My 1st App!')
window.set_border_width(10)
 
vbox = g.VBox(spacing = 4)
window.add(vbox)
 
button = g.Button('Click me!')
count = g.Label('0')
 
vbox.pack_start(g.Label('This is a button:'))
vbox.pack_start(button)
vbox.pack_start(count)

Structure of the window

Why do we need a packing box?

A window can only contain one widget directly. If you added two it wouldn't know how to lay them out, so we use a special VBox widget for that. A VBox stacks any widgets you put in it vertically (you could use an HBox instead to put them side-by-side).
By default, GTK tends to crowd widgets close together, so we use `set_border_width(10)' and `spacing = 4' to spread things out a bit.

Signals

Widgets (such as windows, buttons, etc) emit `signals' when things happen to them. For example, a button emits the `clicked' signal when the user clicks on it. By `connecting' some code to the signal, we can react to signals in any way we want.

Put this just after the previous code:

def inc_counter(b):
        number = int(count.get())
        count.set_text(str(number + 1))
 
button.connect('clicked', inc_counter)

Whenever the button is clicked, the `inc_counter' function is called. This function gets the text in the label (0 to begin with) and converts it to an integer. It then adds one to it, converts back to a string and puts the result back in the label.

Although the code we've looked at here is very simple, it's easy to extend it to more complicated problems. In particular, you can keep putting more widgets inside other widgets. For example, you could pack_start an HBox into the VBox and put buttons inside the HBox to get them along the bottom of the window.

Futher reading

Now we're going to look at other aspects of creating an application. If you want to know more about using GTK, you'll want to read the httpGTK Tutorial. The code is in C rather than python, but you can usually work out how to convert it.

Eg, this (first line is general, second is a specific example):

gtk_<sometype>_<some_method>(GTK_<SOMETYPE>(object), arg1, arg2, ...);
gtk_window_set_title(GTK_WINDOW(window), "The title");

becomes this:

object.some_method(arg1, arg2, ...)
window.set_title("The title")

The ROX applications Archive, Memo and Edit are written in python, if you want some more complicated examples. Feel free to copy bits for your own (GPL) programs.

Packaging and distributing

Before you can give your program to other people, you'll want to add a few things. For a start, you don't want the standard `Application Directory' icon! Use a graphics package (such as The GIMP) to create an icon up to about 42 by 42 pixels. Save it in PNG format as .DirIcon within the application directory. You'll probably have to refresh the filer's display to see it. You can also set the icon using the filer's 'Set Icon...' feature; make sure you select 'Copy image into directory'.

More importantly, you'll need to write some documentation. For this, create a subdirectory called Help inside the application. When you bring up a menu over the application in ROX-Filer and choose Help, this directory will be opened.

You can organise the files in here as you please, but some helpful things to include are Changes (What's new?), COPYING (What's the license?) and a README with some general information including:

  • The author and homepage of the application.
  • A summary of what the application does.
  • Enough instructions that someone can do something useful with it.
  • A place to report bugs.

A full manual would be nice too! Click on the Help button in ROX-Filer's toolbar for an example.

Finally, you should create an AppInfo.xml file with some details about your program. Here is a sample file:

<?xml version="1.0"?>
<AppInfo>
  <Summary>A little counter</Summary>
  <About>
    <Purpose>Counter</Purpose>
 
    <Version>2.0.0 (12-Jul-2002)</Version>
    <Authors>Thomas Leonard</Authors>
    <License>GNU General Public License</License>
    <Homepage>http://rox.sourceforge.net</Homepage>
 
  </About>
</AppInfo>

ROX-Filer will show the Summary text in a tooltip over your application, and the fields in the About section in the Info box. See the ROX-Filer Manual for more details about this file.

To create a package for distribution, drag the application directory to Archive to create a .tgz archive. Other people should be able to simply extract this archive anywhere and run the application.

If you've got extra python files in your program, you'll find that there are some '.pyc' files lying around -- delete them before archiving to make the archive smaller.