Creating a ROX applet

This tutorial will show you how to create a simple ROX applet (a program that runs inside the panel). The applet will be a clock, so we'll also look at timed events.

You should have read the first tutorial.

Start with a normal application

It's good to be able to run applets in their own window too, and its very similar, so we'll start by creating a stand-alone clock application.
Create the application directory as before, and start with this code:

#!/usr/bin/env python
import findrox; findrox.version(1, 9, 8)
import rox
from rox import g
import time
time_display = g.Label('')
def update_time():
main = rox.Window()

OK, that's pretty straighforward. A label in a window. To get the time to update we need to add a timeout. This takes the minimum time between calls (in ms) and a function to call. Add this just before entering the mainloop:

g.timeout_add(1000, update_time)

If you run this, you'll see that the time only updates once. update_time() needs to return True to get called again:

def update_time():
        return True

Making it an applet

Copy AppRun as AppletRun. Instead of creating a rox.Window, create a rox.applet.Applet, passing the first command-line argument:

import sys
from rox import applet
main = applet.Applet(sys.argv[1])

Make sure AppletRun is executable. And that's it!

When you click on the program, it runs it in a window using AppRun, but if you drag it to a panel then ROX-Filer runs AppletRun with the ID of a socket on the panel.

For a more complicated task you would put most of the code in a separate file (eg and get AppRun and AppletRun to import that, to save duplicating code.

Applet sizing

People often want to make applets that scale to the current size of the panel. The problems people often have with this are:

  • The panel grows and grows...
  • ...or shrinks and shrinks...
  • ...or the user can't change the size anymore from the Options box.

These are caused by the applet trying to tell the filer what size the applet wants to be. The filer then sets/limits the panel size as requested, which often causes the applet to try to change its size for the new panel, etc.

Here's how to do it correctly. This explanation assumes a horizontal panel. For vertical panels, reverse width and height...

The trick is: set your vertical size request to a small and fixed value. 8 is good. Set the horizontal width to the desired size.

The sequence then looks like this:

  1. The panel asks the applet for its desired size.
  2. The applet responds 50×8 (want to be 50 pixels wide and as high as the panel).
  3. The panel sends the applet a size-allocate with its new size of 50×100 (the requested width × the actual height).

* Do not request the applet's actual height. This value is the minimum height for the applet. Stick to 8 (or whatever the actual minimum is).
* Do not request a height of -1. GTK will then use the widget's default natural size, which for images is the same situation as the previous point.

Here is a Demo Applet that both scales with the Panel and also adjusts to the orientation of the Panel (Horizontal or Vertical).

#!/usr/bin/env python
import findrox
import rox, sys
from rox import applet, g
# This XPM ripped EVILly from the GTK tutorials:
xpm_data = [
        "16 13 3 1",
        "       c None",
        ".      c #000000000000",
        "X      c #FFFFFFFFFFFF",
        "   ......       ",
        "   .XXX.X.      ",
        "   .XXX.XX.     ",
        "   .XXX.XXX.    ",
        "   .XXX.....    ",
        "   .XXXXXXX.    ",
        "   .XXXXXXX.    ",
        "   .XXXXXXX.    ",
        "   .XXXXXXX.    ",
        "   .XXXXXXX.    ",
        "   .XXXXXXX.    ",
        "   .XXXXXXX.    ",
        "   .........    "]
class DemoApplet(applet.Applet):
    A Demo Applet that displays a GtkImage and scales along with the Panel
    and adjusts according to the orientation of the Panel.
    def __init__(self):
        applet.Applet.__init__(self, sys.argv[1])
        self.vertical = self.get_panel_orientation() in ('Left', 'Right')
        if self.vertical:
            self.set_size_request(8, -1)
            self.set_size_request(-1, 8)
        self.image = g.Image()
        self.pixbuf = g.gdk.pixbuf_new_from_xpm_data(xpm_data)
        self.size = 0
        self.connect('size-allocate', self.event_callback)
    def event_callback(self, widget, rectangle):
        Get the new size and resize the pixbuf,
        but only if the size is different and is valid.
        This also assumes you want square widgets.
        side = self.get_panel_orientation()
        if self.vertical:
            size = rectangle[2]
            size = rectangle[3]
        if size != self.size:
    def resize_image(self, size):
        """Create a scaled version of the pixmap, and set image to that."""
        scaled_pixbuf = self.pixbuf.scale_simple(size, size,
        self.size = size
    def get_panel_orientation(self):
        "Return the panel orientation ('Top', 'Bottom', 'Left', 'Right')"
        pos = self.socket.property_get('_ROX_PANEL_MENU_POS', 'STRING', False)
        if pos:
            return pos[2].split(',')[0]
        return 'Bottom'
main = DemoApplet()