Creating budgie applets

These are some notes from a recent conversation I had about how best to start developing new applets. Hopefully this is useful for an initial primer.

when developing applets its probably best to treat it as a whole app

  • develop it separately to run within a container on a window such as
    a Gtk Grid or Box. Then when ready to make the applet it is easy
    enough to take that container and add it to the equivalent of a window - the Budgie Popover.

Examples should be consulted - lots of python & vala based examples on budgie-extras

The simplest is budgie-rotationlock

  • this just responds to a click.

Another is
that shows a Budgie Popover when clicking on the applet in the panel.

Essential API doc for reference - &

budgie applets must either be placed globally in
/usr/lib/budgie-desktop/plugins or locally

All applets have a .plugin file and a “module file” - referenced in
the .plugin file. With those two things it will appear in
budgie-desktop-settings - panel applets.

Note - you have to restart the panel for changes to take effect i.e.
budgie-panel --replace &

We also have applet templates for you to get going with:


Do you have a C example? I want to create applets to the wider Budgie community (i.e. Ubuntu, Solus and beyond) but I’m not really willing to learn Vala (which won’t serve me outside of GNOME programming while both Python and C synergise with my studies) and Python isn’t really accepted by the Solus devs (and I really want to get better used to C).

Our budgie-indicator-applet is written in C if that helps

Alternatively just take a vala template applet, compile it and look at the generated C code.

Applets in Ruby - is it possible? Just curious… Ruby also supports GTK3

Sure. You’ll need to create the budgie bindings I am guessing using the gir1.2-budgie-desktop-1.0 introspection library

Quick google … dunno if this is helpful

I have never used ruby so cannot really help further.

Sad :frowning:
I thought there was prebuilt bindings, I dunno how to make some.
Any examples? Thanks

As I said previously … i dont know anything about ruby.

Ok, how were the Python bindings generated? I could do something similar using that…
Also the latest gir_ffi is

That you will need to look through the upstream meson build files.

You will also need bindings for libpeas. Again you will need to research this. Similar gir bindings exist for libpeas.

Alright thx David! I will…

I had a few questions about making Budgie applets, if someone has the time to answer. They mostly pertain to Budgie instead of just programming in general, so I hope its okay.

  1. I see there has been an effort in Vala applets to not hard code the path to /usr/bin/, using Config.PACKAGE_BINDIR. Is there something comparable for python, or should I just spawn external commands without a path and hope for the best?

  2. I notice threads/timeout_add that start under an applet just keep running even after the applet is removed. I am assuming this is due to them being actually “owned” by budgie-panel, so they now live and die with the panel? I see your desktop apps spawned by the applets (i.e. WeatherShow) use dconf dumps to detect whether or not the applet is still on the panel. Is something like this advisable for the panel applets as well? In theory, I could add and remove my clock applet 10 times and now have 10 of the threads running.

  3. Somewhat pertaining to #2, my clock applet creates a temp file. If it is added and removed multiple times in the same session, there will now be multiple timeout_add processes running, and all reading/writing the same file. Is it better to base the temporary file name on something else, like uuid to avoid conflicts?

Thanks all for your help so far.

1 Like

Excellent questions

  1. On the first one, that’s some magic David introduced. We should use full paths for security reasons, but which that is could depend on the distro it is installed on. David might add a few words on how to do the trick in python.
  2. Timeout_add() runs until return false is set. A while ago, we created a signal to send to the applet if the applet is removed from the panel. You will find this e.g. in wallpaperswitcher_runner.vala-line 54-76. You can use this signal to set the return value to false to end the story in a clean way (or any other action to exit). Older applets btw might still have the polling dump - mechanism you mention.
  3. Should be fixed if you prevent multiple threads or loops by using [2].

Make sure, if you are using temp files, to include your current user name in the file’s name, to prevent conflicts if there are multiple users.

1 Like

Thank you for the fast reply! With #1, I keep hitting dead ends with all my searches - they just keep turning up ways to find the path of the python interpreter. Or external commands like “which”, and that defeats the purpose…

Looking at wallpaperswitcher_runner, it looks like that is the direction to go in. Seems pretty straightforward, so hopefully it shouldn’t be too tricky to convert to python. Something I would never have thought of (had no idea what the new_with_path was for before), so thanks for the pointer!

And yes, I did use the user name in the temp file. I will admit, I borrowed that from what you did with ClockWorks, so again, thank you! (though David did tweak my line for me there, oops)

Again, I appreciate your time.

1 Like

My plan for python applets is to use the same trick as for vala I.e. use configure_file to write to a file … in this case a .py file.

The .py output file will just have variables that are equal to the prefix, libdir, bindir etc.

The main python module will just use a normal import statement and thus the variables can be used in the code.

Anyway, that’s the theory … just haven’t had the time to do that for the python based applets in extras. Need to find more hours in the day.



Okay, thanks. That actually explains a lot. If I am understanding you both correctly, the reason I can’t find something similar in python is because BIN_DIR isn’t even available for Vala until you tell meson generate it.

So the wallpaperswitcher_runner signal works really well to do what I needed. I did notice though that it didn’t work right if I did it from the __init__ method (seems it couldn’t yet find its own uuid in the panel settings). I am guessing that it is because its uuid isn’t actually added to that list of panel applets until it after it “returns” the new budgie applet? Running “watch applet” from idle_add does resolve this though, so I am hoping that is an appropriate fix.

The only other issue is that I had temporary print statements in my applet to watch to make sure everything was working. I noticed that all the signals/callbacks I connected for the Gio.Settings also stayed active even after the applet was removed. This I didn’t know. For example, first time I removed the applet, it printed the “closing applet” message once. If i add and remove again, it printed twice, and so on. Same if I changed things in the Dconf Editor after I removed it from the panel. It would print the “changing settings” messages still. Again, I’m guessing this is because those signals belong to the budgie-panel? Either way, your fix came in handy here because it allowed me to disconnect those signals before it exits the timeout_add.

I think I am on the right track with it now but always willing to hear if I got things wrong Thanks again for your help!