# Learning about qtile widgets with cricket

Apr 3, 2023
qtile python

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.

### Hello World?!

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 `self.text` property.

### 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.

### Cricket Scores

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.