10.2  "Hello World!" and More

by Martin Grimme

10.2.1  Introduction

"Hello World!" is traditionally the first program which you write when learning a new language. The "Hello World!" for gDesklets is remarkably easy. Therefore, we will extend it a bit. This tutorial will let you take a peek into advanced things like configuration dialogs, action handlers, event objects, and inline scripting to give you a quick overview.

10.2.2  Ready, Set, Go!

You don't need much for creating desklets. Just make sure that you have these things ready:

  • the gDesklets platform
  • this book
  • your favorite text editor (the author recommends gedit for writing desklets)

Now, open your text editor and let's start the show!

10.2.3  Hello World!

The first "Hello World!" desklet will be easy. It will just open a window saying "Hello World!" to the user.

Copy the following lines into your editor and save the file as hello.display.

<?xml version="1.0" encoding="UTF-8"?>

<display>

  <label value="Hello World!"/>

</display>
  

Run and enjoy your first self-made desklet. Now, wasn't that easy?

It's a bit tiny, indeed. But we can change the font size to make the text more readable. How about a font which is three centimeters high?

<?xml version="1.0" encoding="UTF-8"?>

<display>

  <label value="Hello World!" font="Sans 3cm"/>

</display>
  

Much better! Why don't we change the label color as well?

<?xml version="1.0" encoding="UTF-8"?>

<display>

  <label value="Hello World!" font="Sans 3cm" color="red"/>

</display>
  

10.2.4  Make it Configurable

Not everybody likes red labels. It would be better to let the user change the color. This can be achieved with the preferences system.

First, we have to give our label an ID, so that we can call it by its name later. This is done with the id property. The ID can be any alphanumeric string as long as its a unique ID within the .display file. Just call it "mylabel" for now.

<label id="mylabel" value="Hello World!" font="Sans 3cm" color="red"/>
  

Now that we have given a name to the label, we can bind its color (or any other property, too) to a preferences dialog.

The preferences dialog is defined within the <prefs> tag. We need a color selector bound to the label:

<color label="Text Color:" bind="Dsp.mylabel.color"/>
  

As you can see, the color selector has a label text, with which it appears in the dialog. The interesting part is the bind property, however. With bind, you can bind any readable and writable object to a configuration element. In our "Hello World!" example, this is the color property of the label. To avoid namespace clutter, all display elements sit within the Dsp namespace. That's why we need the "Dsp." prefix before "mylabel".

Here is our working configurable "Hello World!" desklet:

<?xml version="1.0" encoding="UTF-8"?>

<display>

  <label id="mylabel" value="Hello World!" font="Sans 3cm" color="red"/>

  <prefs>

    <color label="Text Color:" bind="Dsp.mylabel.color"/>

  </prefs>

</display>
  

The label color is fully configurable now. Restart the desklet or gDesklets and you will see that the configuration has been saved across sessions. Making a desklet configurable couldn't be easier!

For an excercise, you can try to make the label font and the text configurable as well. That way you can already get used to using this book as a reference book.

10.2.5  Adding Life

Until now, our "Hello World!" was pretty static and boring. We are going to change this now. Let's use some scripting to make it alive.

First of all, we add an image and let it appear behind the label. To make it appear behind the label, set it before it in the .display file. That way the label will be in front.

<image uri="/usr/share/pixmaps/gdesklets.png"/>
<label id="mylabel" value="Hello World!" font="Sans 3cm" color="red"/>
  

Every display element provides hooks, where you can put action handlers in. These hooks are properties as well, and accept Python scripts. To make the image react on entering with the mouse pointer, we have to setup the on-enter handler.

<image uri="/usr/share/pixmaps/gdesklets.png" on-enter="print 'entering'"/>
  

Instead of the simple print statement, we could also modify properties. For example, change the size of the image.

Before you start adding an ID to the image as well, keep in mind that action handlers let you refer to the element where the action occurred using the self reference. Changing the image size on entering the image is thus as simple as this, without the need for an ID:

<image uri="/usr/share/pixmaps/gdesklets.png" on-enter="self.scale = 2"/>
  

The on-leave handler gets activated when the mouse leaves the element again. In that case, we want to reset the image to its original size again. Here's the full code so far:

<?xml version="1.0" encoding="UTF-8"?>

<display>

  <image uri="/usr/share/pixmaps/gdesklets.png"
         on-enter="self.scale = 2"
         on-leave="self.scale = 1"/>
  <label id="mylabel" value="Hello World!" font="Sans 3cm" color="red"/>

  <prefs>

    <color label="Text Color:" bind="Dsp.mylabel.color"/>

  </prefs>

</display>
  

10.2.6  More Fancy Stuff

The last challenge in our "Hello World!" tutorial will be to make the image draggable with the mouse pointer. Does this sound difficult? Don't worry, it's not complicated at all.

At first, we have to be aware that there are two states now:

  • State 1: the image is not being dragged
  • State 2: the image is being dragged

It is important to keep track of the current state. Initially we are in state 1. As soon as the user presses the mouse button, we enter state 2 and remain in that state until she releases the button again. Then we enter state 1 again. If we're in state 2, we have to watch the pointer movements and move the image to the current pointer position.

To keep track of the state, we need some variable. This will inevitably lead us to inline scripting. Inline scripts are Python scripts which can directly interact with the display elements. Action handlers were your first encounter with inline scripts already. But you can also put scripts inside the <script> tag, like this:

<script>

  # this is some inline script

  a = 1
  b = 2
  print a + b

  # we can also modify properties of display elements here
  Dsp.mylabel.value = "Hallo Welt!"

</script>
  

Most times, scripts require us to use characters, which are reserved by XML, such as "<", ">", or "&". They can be entered by using the common XML entities instead, e.g. "&lt;", "&gt;", or "&amp;". While this is neccessary for action handler scripts, we can use CDATA sections in the <script> tag to avoid it. A CDATA section is a section which the XML parser doesn't parse. Therefore, there is no need for escaping reserved characters.

Any text within "<![CDATA[" and "]]>" is ignored by the XML parser. It is always safe to enclose inline scripts in a CDATA section, like this:

<script><![CDATA[

  ...

]]></script>
  

Back to our problem. The current state can be remembered by using a script variable:

<script><![CDATA[

  # the initial state
  state = 1

]]></script>
  

The on-press handler is responsible for taking actions when the user presses the mouse button. The on-release handler is called on releasing the button. Therefore, we use these two action handlers for setting the current state:

<?xml version="1.0" encoding="UTF-8"?>

<display on-press="state = 2"
         on-release="state = 1">

  <image uri="/usr/share/pixmaps/gdesklets.png"
         on-enter="self.scale = 2"
         on-leave="self.scale = 1"/>
  <label id="mylabel" value="Hello World!" font="Sans 3cm" color="red"/>

  <prefs>

    <color label="Text Color:" bind="Dsp.mylabel.color"/>

  </prefs>


  <script><![CDATA[

    # the initial state
    state = 1

  ]]></script>

</display>
  

The last action handler needed for this tutorial is the on-motion handler. It gets called whenever the mouse is being moved. Since we want to capture motion events on the whole display, we put this handler into the <display> tag.

We're going to call a move() function in the action handler. This function, of course, should only be used if we're currently in state 2:

<display on-press="state = 2"
         on-release="state = 1"
         on-motion="if (state == 2): move(self.event.x, self.event.y)">
  

The self.event object is an event object of the action handler. Every action handler provides an event object with information about the action. In this example the current position of the mouse pointer can read from the x and y properties. Please note that the event object is only available within the action handler.

For the last part, the move() function has to be implemented. This function takes the current pointer position and modifies the x and y properties of the image in order to move it. This time, we really need to give the image an ID.

The complete code now looks like this:

<?xml version="1.0" encoding="UTF-8"?>

<display on-press="state = 2"
         on-release="state = 1"
         on-motion="if (state == 2): move(self.event.x, self.event.y)">

  <image id="img" uri="/usr/share/pixmaps/gdesklets.png"
         on-enter="self.scale = 2"
         on-leave="self.scale = 1"/>
  <label id="mylabel" value="Hello World!" font="Sans 3cm" color="red"/>

  <prefs>

    <color label="Text Color:" bind="Dsp.mylabel.color"/>

  </prefs>


  <script><![CDATA[

    # the initial state
    state = 1

    def move(x, y):

        Dsp.img.x = x
        Dsp.img.y = y

  ]]></script>

</display>
  

Now that you've finished this tutorial, you should have got a good overview of most concept of the gDesklets platform. However, this tutorial covered many, but not all parts of the platform. There's still lots to be explored!