Next: Extending adesklets, Previous: Using adesklets, Up: Top
As previously stated (See About adesklets.), adesklets is basically an interactive Imlib2 console, with one or two aditionnal features:
So let's start a typical interactive session to see what happens. Under X, open a terminal, and type:
adesklets :
As in last section (See 'Using adesklets as a command-line graphic
editor'.), you will get a prompt. Now, enter the command
'images_info
'. You should get on stdout:
2 images id 0 width 1 height 1 alpha 1 filename (null) id 1 width 1 height 1 alpha 1 filename (null) command 0 ok: images_info
This tells you that you have two images: image 0 and image 1, each 1x1 pixels. Those are the only images you can never destroy nor do any operations you want with (resize them independently, flip them diagonaly, etc.) Why is that?
But where is this window? Well, right know,
it is only 1x1 pixels, and it is not shown1 on the screen.
So let's resize it to 100x100 pixels: type 'window_resize
100 100
'. Now, if you re-enter the command
'images_info
', you will get:
2 images id 0 width 100 height 100 alpha 1 filename (null) id 1 width 100 height 100 alpha 1 filename (null) command 2 ok: images_info
The foreground and background images have
been resized to 100x100 pixels. Moreover, the background image
has been updated to contain a new image of the background. Now,
let's make this window visible. Type two commands:
'window_reset managed
' and then
'window_show
'.
The first command tell adesklets to let your window manager “manage” your window (the default is not to); it means the window gets decorated, and you can interactively change its visibility2. The second command shows our 100x100 window. So far, it's nothing too impressive–just a black 100x100 square with a titlebar and fixed-size borders.
As mentioned above, only image 0 (the
foreground) is displayed by default. Now, type:
'window_set_transparency 1
'. Wow! We see the root
window below! With “window transparency” on, the
image 1 (background) is first copied onto the window, then image
0 (foreground) which is purely transparent black, is blended onto
it3.
It is time to say a word about colors.
Remember, adesklets is an Imlib2 console. Thus, as with
Imlib24, colors are always true 32 colors, RGBA
encoded, with eight bits (0 to 255) per channel, including an
alpha channel for transparency. Palette conversion and such will
automatically take place if your screen depth is less than that,
so you do not need to bother with that. If you type:
'context_get_color
', you will get:
command 0 ok: context color 255 255 255 255
Imlib2 is a kind of state machine; as such,
it comes with a “context” that remembers a series of
attributes it will use in its future operations. For instance, we
now know the color it will use as long as we do not modify it is
opaque pure white (red=255, blue=255, green=255, alpha=255). If
we type: 'context_get_image
', it returns:
command 0 ok: context image 0
Which means all window operations we perform
are made directly on the forgeground image. So, let's draw a
filled semi-transparent magenta rectangle on the foreground.
Commands could be: 'context_set_color 255 0 0 200
'
and 'image_fill_rectangle 25 25 50 50
'. Neat, isn't
it?
If you are used to Imlib2 programming, you
will have noticed by now those last few commands look quite
familiar. This is pretty normal; most of Imlib2 C functions are
presented as corresponding 'commands' in adesklets. This way,
adesklets' command 'image_fill_rectangle
' follows
the same semantic as Imlib2's
'imlib_image_fill_rectangle()
' function, the command
'context_set_color
' functions just like
'imlib_context_set_color()
', etc. The only two
significant (and systematic) differences are that whenever you
would use an 'Imlib_Something
' object in C, you use
an integer ID with adesklets, and on the semantic of the
'imlib_free_something()
' functions that need to be
given an ID instead of freeing the context selected object of
this type.
This is pretty easy to illustrate. Let's load the “Vera” font included with adesklets at a 20pt size, set it as default font, and write in pure opaque white “Hello” diagonally on the foreground image from the top left corner before unloading the font. It would look like:
6 >>> load_font Vera/20 command 6 ok: new font 0 7 >>> context_set_font 0 command 7 ok: context_set_font 0 8 >>> context_set_direction text_to_angle command 8 ok: context_set_direction text_to_angle 9 >>> context_set_angle 45 command 9 ok: context_set_angle 45 10 >>> context_set_color 255 255 255 255 command 10 ok: context_set_color 255 255 255 255 11 >>> text_draw 0 0 Hello command 11 ok: text_draw 0 0 Hello 12 >>> free_font 0 command 12 ok: free_font 0
If you look at your imlib2 documentation, you
will immediatly notice the two differences we just talked about:
all references to the font Vera/20 are made through an integer ID
(0 here), and the 'free_font
' command, unlike the
corresponding 'imlib_free_font()
' function, is
called with the font ID as an argument instead of the context
font.
Now let's say you want to save you resulting
image; not a problem! Let us type: 'save_image
out.png
', and the foreground is saved in the file
“out.png” in the current working directory5.
Now, just for the sake of it, let us exit adesklets, and start
another interactive session to see if we can reload this image.
Just type in the “quit
” command or press
^D (Control D: end of file). Then your new session should look
something like:
0 >>> window_resize 100 100 command 0 ok: window_resize 100 100 1 >>> window_reset managed command 1 ok: window_reset managed 2 >>> window_set_transparency 1 command 2 ok: window_set_transparency 1 3 >>> load_image out.png command 3 ok: new image 2 4 >>> blend_image_onto_image 2 0 0 0 100 100 0 0 100 100 command 4 ok: blend_image_onto_image 2 0 0 0 100 100 0 0 100 100 5 >>> window_show command 5 ok: window_show 6 >>> free_image 2 command 6 ok: free_image 2
Of course, we wanted to visualize the result;
this is why we emitted commands 'window_reset
',
'window_set_transparency
' and
'window_show
'; they are not otherwise useful. The
command 'blend_image_onto_image
' comes straight from
Imlib2 once again; it takes the newly created image 2 from the
third command and blends it onto the context image (image 0,
foreground by default), for its coordinates (0,0) to 100x100
pixels farther, and puts it on the rectangle starting at (0,0) of
size 100x100 pixels of image 0. It is finally good to note that,
with adesklets, there is always an image in Imlib2 context:
whenever you try to free the current image, the image context is
always reset to the foreground image.
OK, so it is almost the end of this primer... From there you should play around with adesklets. It is pretty straightforward to use if you are already familiar with Imlib2. If you are not, your best resource is certainly having Imlib2 documentation not far away. :-)
Some last tips:
help
' command.history
' command to output your
current session to a file6.pause
' command at the end of the script to
freeze the interpreter.Here is an example:
#!/usr/bin/env adesklets -f # Make the window 'managed' # window_reset managed # Resize the window to 100x100 pixels # window_resize 100 100 # Show the window, then freeze for 10 seconds before exiting # window_show pause 10
You will just have to make this script executable and run it. As usual, output from the commands will get printed to stdout.
play
command. This
two-arguments command gives you a way to replay any sequence of
commands from history, using the command number of the first
and last command as parameters.When you are ready for real work7, you might want to use a real scripted language instead of the raw adesklets interpreter we used in the primer; you never know when just having variables could come in handy.
At the time of this writing (02 April 2005), the Python package is included 8, and the Perl 5 binding is under way.
A really useful feature of the adesklets interpreter is its ability to produce
a full trace of its session (commands, events and special
messages), either on stdeerr
or as a log file,
independently of what is driving it. To have access to this
feature, one must:
--enable-debug
switch to
configure
.ADESKLETS_LOG
variable in the
environment if the session's output should to be stored in
files instead of printed to stderr
9. It should be an absolute filename
prefix which the interpreter, inheriting from the environment,
can use to create complete file names to write to.echo
command to set easy to find log points.
From Python, things are not very different than from the
interpreter. Commands have been wrapped into Python functions,
and an Events_handler
class has been made public to
handle asynchronous feedback from the interpreter. Let's have a
look at the test/test.py script from
the source tarball of version 0.4.7:
""" test.py - S.Fourmanoit <syfou@users.sourceforge.net>, 2004 Small, non-exhaustive adesklets test script: - Resize adesklets window to 100x100 pixels - Put it under wm's control - Set it to be pseudo-transparent - Map it on screen - Then wait until the user exits, generating an alam event every 10 seconds, and catching motion_notify events as they occur To try it: - Install adesklets with python support enabled (default) - Run python test.py from this directory """ import adesklets class My_Events(adesklets.Events_handler): def __init__(self): adesklets.Events_handler.__init__(self) def __del__(self): adesklets.Events_handler.__del__(self) def ready(self): adesklets.window_resize(100,100) adesklets.window_reset(adesklets.WINDOW_MANAGED) adesklets.window_set_transparency(True) adesklets.window_show() def quit(self): print 'Quitting...' def alarm(self): print 'Alarm. Next in 10 seconds.' return 10 def motion_notify(self, delayed, x, y): print 'Motion notify:', x, y, delayed My_Events().pause()That's it! Twenty-six lines of Python code, and we have a perfectly functionnal desklet. I think things are pretty self-explanatory; have a look at the Python online help for
adesklets
, adesklets.commands
and
adesklets.Events_handler
for a good, complete
explanation (See Python
package documentation.) . All we do here is:
adesklets
package: this
automatically instantiates a child adesklets process and sets up all
communications. When the package initialization returns without
raising any exception, it means the interpreter is up and ready
to accept commands.adesklets.Events_handler
and redefine
the methods invoked for the events we are interested in (all
other events are not just ignored, they are not
generated).All adesklets objects (fonts, images, color ranges, polygons, etc), are stored in stacks (one stack per object type). Python's ID's are nothing more than the initial position of given objects inside the relevant stack, and therefore will become invalid if an object of the same type below them gets freed.
For instance, if we start with a clean state, all we have in the stack of images is the window's foreground (ID 0) and background (ID 1). When someone creates two other images from Python like so:
im_1 = adesklets.create_image(10,10) im_2 = adesklets.create_image(10,10)
We get im_1==1
, and
im_2==2
. Whenever someones does:
adesklets.free_image(im_1)
The stack starts collapsing, and what was
once image 3 (im_1
) is now image 2, and
im_2
is now an invalid reference. This clearly means
that invoking:
adesklets.free_image(im_2)
would generate an image out of
range
error message.
adesklets now includes an
indirect mode of execution and textual variables (non-recursive
textual replacements, to be precise) as well. This means desklets
writers have what is needed to create precisely-timed animations.
You can find an easy-to-follow example in test/fading.py. A somewhat more involved use of
these features is also made in the yab
desklet. To
realize an animation from Python, one should:
adesklets.start_recording()
function, emitting an
undetermined number of adesklets
commands, then toggling back into normal mode (direct
execution) with adesklets.stop_recording()
. Of
course, no command is ever evaluated while the interpreter is
in indirect mode; commands are only stored in the history for
future replay.adesklets.play_set_abort_on_events()
function. The
high-level adesklets.Events_handler::set_events()
method can also be used to dynamicaly change what events are
caught. See test/test_events.py for
an example.adesklets.set()
function,
very similar to its Bourne shell homonym.adesklets.play()
function.Here are a few additional remarks:
time_gate
command. This works very simply: every
time a macro replays, adesklets records its starting time, and
whenever a time_gate
command is encountered during
the replay, it waits for the number of seconds given as an
argument to time_gate
to run out. For instance,
specifing time_gate 0.1
in a macro will make any
subsequent commands within the macro not execute before the
macro has run for at least a tenth of second.set
). If no matching variable is found, the
sequence is simply discarded.play
command are always atomic for the driving desklet, as are any
other non-macro commands. Simply put, it means no event will
ever get generated while a macro is replayed; they will all get
issued right after the play
command returns.set_abort_on_events
flags or variable
every time you want to call a macro, only if something needs to
be changed since the last call.adesklets.Events_handler::set_events()
is to stop
the catching of specific events during macro playback, so the
macro can be interrupted only in specific circumstances. Of
course, all events generated before the call to the method will
not be lost but queued, and appropriate event methods will be
called later, provided the handlers are set back once the macro
playback completes.adesklets.Events_handler::block()
and
adesklets.Events_handler::unblock()
around all
animation-related code if you have an object of a child class
of adesklets.Events_handler
instantiated.Let us just mention here that from adesklets 0.4.0, the
adesklets.utils
module now includes an optional
ConfigFile
class that can be easily reused by
desklets authors to add an easily extendable configuration
facility to their code11 (See Python
package documentation.) . The class also handles
internationalization automatically by default, setting the
charsets according to users' needs. Charset usage can of course
be determined or changed at any time using the
adesklets.get_charset()
and
adesklets.set_charset()
functions. The
adesklets.utils.ConfigFile
class also has a
charset
attribute one can examine to determine the
user's preference. When using this class, one should note that an
'ASCII' charset is considered the default, and will not toggle
any conversion. This way, users on platforms not supporting iconv
will always be able to use adesklets
without internationalization support.
Finally, let us mention you may want to take a look at the weather desklet source code from the SourceForge project web site
for a more substantial piece of Python code using adesklets. There are also a few other test desklets under test/ that may give you some ideas. You could also have a look at pydoc's autogenerated adesklets package help, included with this document (See Python package documentation.). Whenever you are happy enough with your desklet, feel free to share it with the rest of us (this would be much appreciated)–package it (See Packaging GNU Makefile for desklets.), and then submit it (See Submitting a desklet.).
[1] “Mapped” in X Window lingo
[2] An “unmanaged” window is very useful for scripted desklets: it looks like it is part of the root window as it can be made to never go on top, and to stay mapped when workspaces are switched.
[3] Well...Not quite. Actually, there is a buffer involved for performance, but the operation is logically equivalent to this description.
[4] Keep your Imlib2 documentation nearby; it is very handy! See Imlib2 documentation.
[5] For this to work, you need to have your Imlib2 installation properly configured for using libpng, or you could receive a “no loader for file format” error.
[6] For this to work, you need to have GNU history when building adesklets.
[7] :-)
[8] adesklets was written to be easily used from a variety of interpreted languages; future plans includes wrappers for Perl and Ruby–do not hesitate to propose your help if you want to speed things up!
[9] Printing to stderr
will
break most bindings, and should therefore be used mainly when
invoking the interpreter directly from the console.
[10] This last step is not mandatory; the
script may very well continue, but it should be written so that
it supports signal interruptions. See
help(adesklets.Events_handler)
for further
explanations
[11] You can look at any non-contributed configurable desklet for example usage.