I’m a person who has spent an unreasonable amount of time making minute changes to the appearance of my bar in qtile. Despite the very nice selection of widgets availabe by default with qtile, it was only a matter of time before I decided I wanted to experiment with making my own custom widget. Fortunately, if you can do a bit of python this is quite an approachable undertaking.
The dream widget I was lacking was a little live crikcet score ticker type thing; something which would scroll along on my bar showing me the score in live cricket matches. I’m sure this will interest very few people but I learnt some good stuff along the way.
As far as I can tell from looking at the code for some of the built-in widgets and this very helpful guide, a basic hello world widget would look something like this.
from libqtile.widget import base class HelloWorld(base._TextBox): def __init__(self, **config): super().__init__("", **config) self.text = 'Hello world!'
Qtile provides a selection of base widget classes to do various useful things. This example uses the simple
_TextBox which displays the content of the
Hello World Version 2
Very nice, but for the cricket widget we’ll need something which lets us update the text. The answer is
ThreadPoolText. This class lets you periodically update the text by overridding its
poll method with a function that returns the updated text.
This example updates itself with hello world in a different language every five seconds.
import random class HolaMundo(base.ThreadPoolText): defaults = [ ("update_interval", 5, "Update interval for the widget"), ] def __init__(self, **config): super().__init__("", **config) self.add_defaults(HolaMundo.defaults) def poll(self): messages = [ 'Hola mundo', 'Ciao mondo', 'مرحبا بالعالم', 'Saluton Mondo', 'Sawubona Mhlaba' ] return random.choice(messages)
Along with the whole poll thing, this example also introduces
defaults. This is a list of tuples which define parameters that the user can configure. Here we’ve added
update_interval which defines how often the widget is updated.
For getting the cricket scores we’ll be using a handy rss feed from cricinfo so no brute force scraping will be required, just my beloved feedparser. This doesn’t involve anything directly widget related, just massaging the rss feed into something that would look useful on your bar. Anyway, this is what the finished item looked like.
from libqtile.widget import base import feedparser class CricketScores(base.ThreadPoolText): defaults = [ ("update_interval", 60, "Update interval for the cricket scores widget"), ("teams",,"Teams to display scores for"), ("separator"," \U0001F3CF ","Text to place between scores"), ("no_scores_string","","Text to show when there are no scores to show"), ] def __init__(self, **config): super().__init__("", **config) self.add_defaults(CricketScores.defaults) def get_scores(self): # parse rss feed and get # live matches from title field feed = feedparser.parse("http://static.cricinfo.com/rss/livescores.xml") scores =  for match in feed.entries: # filter live matches if "*" in match.title: scores.append(match.title) # remove scores not involving chosen teams filtered_scores =  for score in scores: for team in self.teams: if team in score: filtered_scores.append(score) # if no scores, show no_scores_string if len(filtered_scores) == 0: return self.no_scores_string else: # form pretty string with separators final_scores = "" for score in filtered_scores: if score != filtered_scores[-1]: final_scores += score + self.separator else: final_scores += score return final_scores def poll(self): return get_scores()
Actaully Using It In Your Config
Assuming you have a
my_widget.py file in the same directory as your
config.py, you simply have to import it and add it your list of widgets.
from my_widget import LovelyWidget screens = [ Screen( top = bar.Bar( widgets = [ LovelyWidget(), widget.GroupBox(), ] ) ) ]
Happy widget writing.