[WIP | Guide] Create New System Menu Widgets

Please Note that this guide is based on a fresh install and any reference to line numbers are based on unmodded files and its your responsibility to find the correct place to make the modifications

As of 4th March, i finally worked out how to make custom system menu widgets and thought, we need some documentation to get a better understanding of how to do it. please do not discuss any thing that you have ideas with on here and keep it strictly to documenting the creation of System Widgets. for discussing ideas etc, please do that in this post

System Widgets, time to make a custom one, sounds easy, well its not so much if you dont know what to do, thats where this guide helps. You will need to use terminal alot to start, stop disable and enable the pt-sys-oled service throughout this and if your using VSCode, you cannot run as sudo so you will need to retry saves all the time as sudo and enter your password every time

Setting up the menu and files
First a modification to Meny.py is required and a new file placed in

Step 1 | Create a test file called test.py

  • open terminal and enter cd /usr/lib/pt-sys-oled/components/widgets/sys_info
  • create a new file with sudo touch test_code.py
  • chmod the file to prefent annoying saving sudo chmod 666 test_code.py

disclaimer: i am only telling you to chmod filels that you create only and have not tested nor will test on other files to prevent any issues arising so chmod other files at your own risk

Step 2 | Setting up the menu entry

  • open /usr/lib/pt-sys-oled/components/Meny.py
  • Line 7 | put a comma after ethernet and make a new line and enter test_code
  • Copy line 93 to 102
  • Make a new line and paste
  • in the new menu item change “cpu” to test and “cpu_load” to “test_code”
  • Save the file

Step 3 | Write some test code
The structure of widgets are rather specific and its REALLY easy to make a mistake causing frustrations so lets start with the basics before you go full steam ahead

basic imports

from components.widgets.common.functions import title_text, draw_text
from components.widgets.common.base_widgets import BaseSnapshot


  • title_text - displays text at the top of the screen aligned to the center (optional)
  • draw_text - prints text to the screen, what ever you want
  • BaseSnapShot - Required for class

NOTE: please ignore the yellow underlined code, its just pylance that cannot resolve the error (missing imports) they are not missing and the error can be ignored

Basic Class Setup

class Hotspot(BaseSnapshot):
    def __init__(self, width, height, mode, interval, **data):
        super(Hotspot, self).__init__(width, height, interval, Hotspot.render)

    def render(draw, width, height):


This is the bare basic you need to get things working,
def init is where you can put your variables etc
def render - This is where you put the code to display things to the screen

Display a title and custom text
in def render enter something like this

        title_text(draw, 0, width, 'test_code.py')
        draw_text(draw, xy=(0,13), text='Hello pi-top[4]')

This will print test_code.py at the top of the screen aligned in the center and 13 pixels under it, will print Hello pi-top[4] to the left of the screen

still more to do

The full code

from components.widgets.common.functions import title_text, draw_text
from components.widgets.common.base_widgets import BaseSnapshot

class Hotspot(BaseSnapshot):
    def __init__(self, width, height, mode, interval, **data):
        super(Hotspot, self).__init__(width, height, interval, Hotspot.render)

    def render(draw, width, height):
        title_text(draw, 0, width, 'test_code.py')
        draw_text(draw, xy=(0,13), text='Hello pi-top[4]')

Restart the service
in terminal you need to enter the following

  • sudo systemctl stop pt-sys-oled.service
  • sudo systemctl start pt-sys-oled.service

if nothing went wrong then the screen should come back on with no issues. if not you need to fix any mistakes and then do the following

  • sudo systemctl disable pt-sys-oled.service
  • sudo systemctl stop pt-sys-oled.service
  • sudo systemctl enable pt-sys-oled.service
  • sudo systemctl start pt-sys-oled.service

if all is good press the up or down button till you get to the new custom widget

This is the very basics you need to make a custom widget for the pi-top system menu. I will explain more on what you can import and how to use them.

What can you do with this?
its up to your imagination, I have personally been able to display GPS data from the GPIO UART as a system widget so you can do anything you want, use sensors, get system information, maybe even get email notifications if your up to it :laughing:


Imports | what you can import
You can import anything you need but there are some basic imports to help you out

If you have a look at any of the available widgets, you will see that there are imports from the following:

  • components.widgets.common.functions
  • components.widgets.common.base_widgets
  • components.widgets.common.image_component
  • components.widgets.common.values

these are built in for the system menu to make things easier like display text on the screen and placing text on the screen etc

Mainly this is functions that you can use for displaying text on the screen. the fuctions available to import are:

  • get_image_file_path
  • draw_text
  • right_text
  • title_text
  • align_to_middle

So far , if you have gone though my guide above you will have used draw_text and title_text but here is more on the usage

This gets the file path of an image located in

one note about adding files to this location, chmod 666 the files you place in there to prevent issues

If you want to use an alternative file path, do not use this function

draw_text(draw, xy, text, fill=1, font=none, anchor=none, spacing=0, align=“left”, features=None, font_size=12)

This is one of the main functions that you will use, as its used to display text on the OLED screen and is easy to use

  • draw - nothing to do just make sure you have it in
  • xy - use a tuple for for x and y coordinates eg. (10,5)
  • text - This is what you want to display on the screen, use a string or variable if you wish or even fstrings. it can use special character code such as \n for new line or \t for tab space
  • fill - 1 will display text as white and 0 will display text as black. default is 1 so if you want white text you can leave it out
  • font - to use a default font, you dont need to do anything, if you want to specify a font (has to be installed, then use font=“Fira Code”
  • anchor - To be honest, I have no idea what its for, maybe @pi-topMIKE can help us out with that
  • spacing - adds additional pixel spacing between new lines, if using multiple lines text strings
  • features - another one i have no idea what its for, another one for @pi-topMIKE to help us with
  • font_size - rather simple, changes the size of the font, default is 12

the only thing for this one that you will be importing is:-

  • BaseSnapshot
    its important that this is imported or things just will not work

I need to do more testing to get a better understanding of this so have nothing to add at this time

Values it a way of formatting text, placing them on the screen, useful if you want to keep text placement similar to other widgets

values that can me imported are:-

  • default_margin_x
  • default_margin_y
  • right_text_default_margin
  • common_first_line_y
  • common_second_line_y
  • common_third_line_y

you can use these for x and y coordinates with draw_text to place text on the screen

3rd party or system imports
As mention you can import any library you need, just a little side note that they have to be installed as sudo for system wide access to actually work


I am reserving this for more further content

I will be providing more information so that you can do more and understand making more complex system menu’s. Hopefully when complete, we can get a compiled knolege base to explain everything so that its easier to find for those that wish to explore this territory :slight_smile:


I am reserving this for even more further content

Apologies for the walls of text here, its something i cannot avoid

No worries. I sometimes ramble and make a wall too haha. I’m on call today so in my down time, I’ll be playing around with this. I’ll report my findings here as well.

Alright, I have some additional findings…

First, because this is a system daemon service, if you don’t get it working the first time, be sure to check /var/logs/daemon.log for errors found in the script and what is happening in the service world. This will become important in a sec…

Second, if you use sudo nano /... be sure that when you copy and paste, the results are the same. I was racking my head for a few minutes over here trying to figure out why I couldn’t get the clock widget running. Upon inspecting the daemon log, I found that it wasn’t being set up with init properly. Upon inspecting what I copied, it appears that if what your copying goes off screen in nano, the text that should be copied becomes a “$”

Kind of annoying, but if you use nano, or you need to debug, those are some suggestions.

On the other hand, perfect write up! I think this is exactly what everyone was looking for. Now, what we need to do is figure out how to modify these files automatically and create a user directory in the /home/user path so that it more simply integrates what is happening here into the menu.py files. Kind of like a module manager. :grin: or, if not a module manager, then an api that you can call to create/edit/or delete a specific script from a specific location, and it would add the widget on the backend and keep track of where the widget is in the Menu.py

Also, @CAProjects found a typo in the guide if you are ok with me pointing it out. Near the top in step 2, you wrote Menu.py as Meny.pi :grimacing:

Thanks for the guide! Love it!

PS Just started using sudo mu-editor ... instead of sudo nano ...

Have you tried VSCode yet it’s rather powerful and has extensions for helping with Python stuff too and debugging code etc, does not help with testing the system stuff as you can’t run it.

There is more info to share on this so will add it in the reserved posts, things about the refresh rate of the widget etc, no need to while loops to loop the code, variables, etc.

As for the typos, I don’t mind, tough to get things perfect first time.

I learned a ton doing a system menu widget for my gps code and knew there was people needing something like this, was a case of understanding how it all works to be able to do something like this

1 Like

Oh yeah, I love VSCode. I use it a bunch. I’m just refraining from using it when it’s a simple text file, particularly because of the long load times and because I don’t want to close my project I’m currently working on to speed up the load times haha

I’m currently working on updating the other widgets to display better and have created a post on it. Just working on it right now, at least until it gets updated on the pi-top end of things. I’ll probably create a pull request on the github as well so it’s easy as pi (lol see what i did here :rofl:) to just implement the fixes.

Yeah. I was also thinking of maybe making a github repository and working with you and the other guys on creating the module manager or api call to add widgets. What are your thoughts on how we could implement it?

1 Like


I have included a link to this page until we have a rework design completed :slight_smile:


Awesome! Thanks @pi-topMIKE! :slight_smile:

Hello! Looks like there may have been an update which involved a sort of menu handler since this was published. Still looking at the /usr/lib/pt-sys-oled/components/Meny.py however the front and apparent loading of the modules appear totally different.

I -heavily- used your cpu info app to add some utility, and am quite thankful for your comprehensive write-up. If possible,I was wondering if you (CAProjects) maybe could take another look and see if it’s just a simple amendment?

I’ll have a look at it between Thursday and Saturday and see what’s different or changed.

Could you also post a photo of what your seing?

Figured out what we were looking at. There’s just a new menu structure and the PageManager.py looks like its now handling the new menu items. The structure added several layers of behaviors, including the screen saver. I feel silly because it makes more sense now.