Recent Posts

New desk

2 min read; 9 GFI

I moved into my current place back in August 2019, bringing with me a fairly small desk. Originally, many years back, I'd had a pretty big office with lots of office space but, upon moving up to Scotland back in 2016, I needed to go with something a lot smaller.

Which was perfectly fine. Me and that desk wrote a lot of code. Me and that desk transitioned from one job to another, and then another. Me and that desk made it through the pandemic.

It's been a good desk.

But it was small. I'm in a place where I could spread out a fair bit again, but I also kept putting it off and putting it off.

Earlier this year I decided that it was high time I actually upgraded; I also promised the desk to someone else who I know will get a lot of good use out of it; so finally earlier this week I put in the order for something bigger and fancier.

Yesterday, with the help of a very good friend (thanks Mariëlle!), I got the desk built and roughly in place, and then today I've been putting the desktop back together and adding extra bits.

My new desk

It's so nice to have more space to spread out, it's also nice to have a black desk again! (the last one was white because... reasons) But what's really exciting is that it transforms into a standing desk at the touch of a button.

I've been trying it in the standing configuration today and, while I doubt I could do a whole day of coding with it like that, I'm already really liking it as a way of breaking up the time at the keyboard. On the days I work from home, or the days of my own where I get sucked into a personal project, I can absolutely see me swapping between the two states.

All that's left now is to get used to it. The screens are ever so slightly further away, the height is ever so slightly different (although I can adjust it, of course, but what I've done is adjust the sitting position to a nicer one and that's going to take some getting used to), my iPad and Stream Deck are in just a slightly different location, etc, etc... So I'm sure there'll be a few days of sitting here and making small tweaks to the spot where things live.

Before I know it I'll be used to it. It'll be "my spot".

Dark Waters

1 min read; 10 GFI

More than ever I'm listening to music while I work. Despite having grown up in the 80s, and so having a good vinyl collection, and then having got into buying all the CDs in the 90s and 00s, these days I have managed to embrace the "just stream all the things" approach. After a long time using Spotify I finally ended up migrating to Apple Music.

One of the things I do find Apple Music does well is the whole "you seem to like this, have you tried this?" thing; which on occasion has resulted in a pretty neat rabbit hole of discovery.

Earlier this year this happened with female-fronted Dutch goth/rock type bands. I don't even remember how that happened, but it's a thing that happened and I gleefully dived right in. I came away with a few names I didn't know before, but one album has really stuck in my head and, now that we're just over 1/2 way through the year, I feel it's the one that gets played more than anything else.

Delain Dark Waters

This keeps creeping back to the top of things I'm playing; sometimes when I'm in "bang on the keyboard lots" coding mode (you know the sort of coding mode: where you don't have to think too much because you have a good plan for what you're doing, but you've got a lot of tapping away to do), plus also often while I'm in the car.

I won't be the least bit surprised to find that this ends up being my most-played album this year. According to last.fm it's at the top of the albums I've played (in places where I have scrobbling set up) in the last 365 days.

My last 365 days of albums on last.fm

The switch has been made

2 min read; 11 GFI

Well, it didn't take as long as I expected it to. Just yesterday morning I was giving Pelican a look over as a possible engine for generating my blog, having wanted to move away from Jekyll for a while now. Having tried it and liked what I saw to start with, I wrote about how I liked it and wondered how long it would take me to make the switch.

By the evening I was making a proper effort to get the switchover started, and just a wee while earlier, before writing this post, the switch was made!

The process of making the switch was roughly this (and keep in mind I'm coming from using Jekyll):

  1. Made a branch in the repo to work in.
  2. Removed all of the Jekyll-oriented files.
  3. Decided to set up Pelican and related tools in a virtual environment, managed using pipenv.
  4. Ran pelican-quickstart to kick things off and give me a framework to start with.
  5. Renamed the old _posts directory to content.
  6. Kept tweaking the hell out of the Pelican config file until it started to look "just so" (this is a process that has been ongoing, and doubtless will keep happening for some time to come).
  7. Tried out a few themes and settled on Flex; while not exactly what I wanted, it was close enough to help keep me motivated (while rolling my own theme from scratch would seem fun, I just know it would mean the work would never get done, or at least finished).
  8. Did a mass tidy up of all the tags in all the posts; something I'd never really paid too much attention to as the Jekyll-based blog never actually allowed for following tags.
  9. Went though all the posts and removed double quotes from a lot of the titles in the frontmatter (something Jekyll seems to have stripped, but which Pelican doesn't).
  10. Tweaked the FILE_METADATA to ensure that the slugs for the URLs came from the filenames -- by default Pelican seems to slugify the title of a post and this meant that some of the URLs were changing.

All in all I probably spent 6 or 7 hours on making the move; a lot of that involving reading up on how to configure Pelican and researching themes. The rest of it was a lot of repetitive work to fix or tidy things.

The most important aspect of this was keeping the post URLs the same all the way back to the first post; as best as I can tell I've managed that.

So far I'm pleased with the result. I'm going to live with the look/theme for a wee while and see how it sits for me. I'm sure I'll tweak it a bit as time goes on, but at the moment I'm comfortable with how it looks.

Considering Pelican

2 min read; 10 GFI

Since getting my blog editing environment set up on the "new" machine a couple of days back I've been thinking some more about moving away from Jekyll. Jekyll itself has served me well since I started this blog back in 2015, but I was reminded again when installing it on the Mac Mini that it's Ruby-based and I have very little understanding of how to get a good Ruby experience on macOS1.

Having mentioned on Mastodon that I was thinking about finally looking at moving my blog management/generation to something new, and specifically something Python-based and ideally some sort of site generator, I got a few suggestions.

One that looks promising so far is Pelican. At first glance it seems to tick a few boxes for me:

  • Python-based (so easy for me to grok in terms of installing, and also more chance of being hackable).
  • Uses Markdown (curiously as an alternative, to reStructuredText, which looks to be the default).
  • Does article-based stuff as well as page-based stuff.
  • Lots of themes, and themes are Jinja2-based (I'm pretty familiar with Jinja thanks to my Django days and also using the library when kicking off ng2web).
  • RSS feed generation.
  • Syntax-highlighted code blocks.

While I'm not quite ready to dive in and make the move just yet (I am on a "muck about at home" holiday this week, but I've got enough planned without losing a day to rebooting my blog), I did do a quick experiment to see if Pelican would work for me.

Key to this is can I keep the URLs for all the posts the same? If I can't that's a non-starter.

Things got off to a good start with an easy install:

pipx install "pelican[markdown]"

I then used the pelican-quickstart to kick off a test site, copied in my existing Markdown files, dived into the docs and found how to configure the generated URLs and... yeah, within like 10 minutes I had a very rough version of my blog up and going.

It looked like garbage, the theme really wasn't to my taste at all, but the core of the blog was working.

I've nuked it all for now but a more considered evaluation is now on my TODO list. Things I'll need to drive into properly are:

  • Find a base theme that's to my taste.
  • Get Disqus working with it so that any old comments remain in place.
  • Get my image/attachment layout back in place.
  • Go through and tidy up all the tagging (which has been a mess with this blog because I never did get round to getting Jekyll to actually use tags).
  • Figure out the best way to do the publishing to GitHub pages.
  • Likely a bunch of other stuff I've not thought about yet.

But, yeah, for a brief "over first coffee of the day" tinker to see if I like it... I like!

Let's see how long it takes me to actually get around to making the switch. ;-)


  1. When setting this up a couple of days back, I had to pin some packages for the blog to older versions because of Ruby version issues; I'm sure that Ruby has virtual environment solutions akin to Python, but diving into that just for one tool... nah. 

A new GitHub profile README

2 min read; 9 GFI

My new GitHub banner

Ever since GitHub introduced the profile README1 I've had a massively low-effort one in place. I made the repo, quickly wrote the file, and then sort of forgot about it. Well, I didn't so much forget as just keep looking at it and thinking "I should do something better with that one day".

Thing is, while there are lots of fancy approaches out there, and lots of neat generator tools and the like... they just weren't for me.

Then yesterday, over my second morning coffee, after getting my blog environment up and going again, I had an idea. It could be cool to use Textual's screenshot facility to make something terminal-themed! I mean, while it's not all I am these days, so much of what I'm doing right now is aimed at the terminal.

So... what to do? Then I thought it could be cool to knock up some sort of login screen type thing; with a banner. One visit to an online large terminal text generator site later, I had some banner text. All that was left was to write a simple Textual application to create the "screen".

The main layout is simple enough:

def compose(self) -> ComposeResult:
    yield Label(NAME, classes="banner")
    yield Label(PRATTLE)
    yield Label("github.com/davep login: [reverse] [/]")

where NAME contains the banner and PRATTLE contains the "login message". With some Textual CSS sprinkled over it to give the exact layout and colour I wanted, all that was left was to make the snapshot. This was easy enough too.

While the whole thing isn't fully documented just yet, Textual does have a great tool for automatically running an application and interacting with it; that meant I could easily write a function to load up my app and save the screenshot:

async def make_banner() -> None:
    async with GitHubBannerApp().run_test() as pilot:
        pilot.app.save_screenshot("davep.svg")

Of course, that needs running async, but that's simple enough:

if __name__ == "__main__":
    asyncio.run(make_banner())

Throw in a Makefile so I don't forget what I'm supposed to run:

.PHONY: all
all:
    pipenv run python make_banner.py

and that's it! Job done!

From here onward I guess I could have some real fun with this. It would be simple enough I guess to modify the code so that it changes what's displayed over time; perhaps show a "last login" value that relates to recently activity or something; any number of things; and then run it in a cron job and update the repository.

For now though... I'll stick with keeping things nice and simple.


  1. It was actually kind of annoying when they introduced it because the repo it uses is named after your user name. I already had a davep repo: it was a private repo where I was slowly working on a (now abandoned, I'll start it again some day I'm sure) ground-up rewrite of my davep.org website. 

Catching up

1 min read; 7 GFI

So... erm... yeah... I did it again. I looked away for a moment and somehow almost 7 months passed without a post! It's so easily done too isn't it? While, when I revived this blog last year, I didn't make a point of intending to write lots and often, I had hope that I'd manage something at least once a week; perhaps at least once a month.

Ahh well.

There's been two main reasons why it's been quiet around here. The first is that my (now not so) new job keeps me busy (in a good way). It involves a reasonable amount of trekking into town and back (which I don't mind on the whole), and once I'm home in the evening I'm generally (but not always) done with the keyboard and desk.

The second reason, which is probably the dafter one, is that a bit earlier this year I finally upgraded my desktop setup from the 2019 Intel MacBook Pro I was using to a recently-released M2Pro Mac Mini (and what an upgrade!). How this plays into blogging being even more quiet is... I needed to set up Jekyll again, and I'd forgotten how I got it running in the first place, so I kept putting off getting it going, and...

Well, this morning, I sat down with coffee, grepped the history on my previous machine, and got it running in like 5 minutes (of course).

So, here I am, back adding another blog post. I'm writing this as much to test that the setup works as anything else.

But also, this time, I'm going to try and make a promise to myself: I'm going to try and write more. I can and should write about anything. I can and should write short things as well as long things. I can and should remember that it's not about writing things that are going to be super important or anything like that, it's about just getting stuff down and creating and recording.

Note of course I said "try" and make a promise.

We'll see. ;-)

So you're looking for a wee bit of Textual help...

(Modified: 2026-04-28 09:34:45 UTC+01:00)
11 min read; 10 GFI

Introduction

Patience, Highlander. You have done well. But it'll take time. You are generations being born and dying. You are at one with all living things. Each man's thoughts and dreams are yours to know. You have power beyond imagination. Use it well, my friend. Don't lose your head.

Juan Sánchez Villalobos Ramírez, Chief metallurgist to King Charles V of Spain

As of the time of writing, I'm a couple or so days off having been with Textualize for 3 months. It's been fun, and educational, and every bit as engaging as I'd hoped, and more. One thing I hadn't quite prepared for though, but which I really love, is how so many other people are learning Textual along with me.

Even in those three months the library has changed and expanded quite a lot, and it continues to do so. Meanwhile, more people are turning up and using the framework; you can see this online in social media, blogs and of course in the ever-growing list of projects on GitHub which depend on Textual.

This inevitably means there's a lot of people getting to grips with a new tool, and one that is still a bit of a moving target. This in turn means lots of people are coming to us to get help.

As I've watched this happen I've noticed a few patterns emerging. Some of these good or neutral, some... let's just say not really beneficial to those seeking the help, or to those trying to provide the help. So I wanted to write a little bit about the different ways you can get help with Textual and your Textual-based projects, and to also try and encourage people to take the most helpful and positive approach to getting that help.

Now, before I go on, I want to make something very clear: I'm writing this as an individual. This is my own personal view, and my own advice from me to anyone who wishes to take it. It's not Textual (the project) or Textualize (the company) policy, rules or guidelines. This is just some ageing hacker's take on how best to go about asking for help, informed by years of asking for and also providing help in email, on Usenet, on forums, etc.

Or, put another way: if what you read in here seems sensible to you, I figure we'll likely have already hit it off over on GitHub or in the Discord server. ;-)

Where to go for help

At this point this is almost a bit of an FAQ itself, so I thought I'd address it here: where's the best place to ask for help about Textual, and what's the difference between GitHub Issues, Discussions and our Discord server?

I'd suggest thinking of them like this:

Discord

You have a question, or need help with something, and perhaps you could do with a reply as soon as possible. But, and this is the really important part, it doesn't matter if you don't get a response. If you're in this situation then the Discord server is possibly a good place to start. If you're lucky someone will be hanging about who can help out.

I can't speak for anyone else, but keep this in mind: when I look in on Discord I tend not to go scrolling back much to see if anything has been missed. If something catches my eye, I'll try and reply, but if it doesn't... well, it's mostly an instant chat thing so I don't dive too deeply back in time.

ℹ️ Note

As a slight aside here: sometimes people will pop up in Discord, ask a question about something that turns out looking like a bug, and that's the last we hear of it. Please, please, please, if this happens, the most helpful thing you can do is go raise an issue for us. It'll help us to keep track of problems, it'll help get your problem fixed, it'll mean everyone benefits.

My own advice would be to treat Discord as an ephemeral resource. It happens in the moment but fades away pretty quickly. It's like knocking on a friend's door to see if they're in. If they're not in, you might leave them a note, which is sort of like going to...

GitHub

On the other hand, if you have a question or need some help or something where you want to stand a good chance of the Textual developers (amongst others) seeing it and responding, I'd recommend that GitHub is the place to go. Dropping something into the discussions there, or leaving an issue, ensures it'll get seen. It won't get lost.

As for which you should use -- a discussion or an issue -- I'd suggest this: if you need help with something, or you want to check your understanding of something, or you just want to be sure something is a problem before taking it further, a discussion might be the best thing. On the other hand, if you've got a clear bug or feature request on your hands, an issue makes a lot of sense.

Don't worry if you're not sure which camp your question or whatever falls into though; go with what you think is right. There's no harm done either way (I may move an issue to a discussion first before replying, if it's really just a request for help -- but that's mostly so everyone can benefit from finding it in the right place later on down the line).

The dos and don'ts of getting help

Now on to the fun part. This is where I get a bit preachy. Ish. Kinda. A little bit. Again, please remember, this isn't a set of rules, this isn't a set of official guidelines, this is just a bunch of "if you want my advice, and I know you didn't ask but you've read this far so you actually sort of did, don't say I didn't warn you!" waffle.

This isn't going to be an exhaustive collection, far from it. But I feel these are some important highlights.

Do...

When looking for help, in any of the locations mentioned above, I'd totally encourage:

Be clear and detailed

Too much detail is almost always way better than not enough. "My program didn't run", often even with some of the code supplied, is so much harder to help than "I ran this code I'm posting here, and I expected this particular outcome, and I expected it because I'd read this particular thing in the docs and had comprehended it to mean this, but instead the outcome was this exception here, and I'm a bit stuck -- can someone offer some pointers?"

The former approach means there often ends up having to be a back and forth which can last a long time, and which can sometimes be frustrating for the person asking. Manage frustration: be clear, tell us everything you can.

Say what resources you've used already

If you've read the portions of the documentation that relate to what you're trying to do, it's going to be really helpful if you say so. If you don't, it might be assumed you haven't and you may end up being pointed at them.

So, please, if you've checked the documentation, looked in the FAQ, done a search of past issues or discussions or perhaps even done a search on the Discord server... please say so.

Be polite

This one can go a long way when looking for help. Look, I get it, programming is bloody frustrating at times. We've all rage-quit some code at some point, I'm sure. It's likely going to be your moment of greatest frustration when you go looking for help. But if you turn up looking for help acting all grumpy and stuff it's not going to come over well. Folk are less likely to be motivated to lend a hand to someone who seems rather annoyed.

If you throw in a please and thank-you here and there that makes it all the better.

Fully consider the replies

You could find yourself getting a reply that you're sure won't help at all. That's fair. But be sure to fully consider it first. Perhaps you missed the obvious along the way and this is 100% the course correction you'd unknowingly come looking for in the first place. Sure, the person replying might have totally misunderstood what was being asked, or might be giving a wrong answer (it me! I've totally done that and will again!), but even then a reply along the lines of "I'm not sure that's what I'm looking for, because..." gets everyone to the solution faster than "lol nah".

Entertain what might seem like odd questions

Aye, I get it, being asked questions when you're looking for an answer can be a bit frustrating. But if you find yourself on the receiving end of a small series of questions about your question, keep this in mind: Textual is still rather new and still developing and it's possible that what you're trying to do isn't the correct way to do that thing. To the person looking to help you it may seem to them you have an XY problem.

Entertaining those questions might just get you to the real solution to your problem.

Allow for language differences

You don't need me to tell you that a project such as Textual has a global audience. With that rather obvious fact comes the other fact that we don't all share the same first language. So, please, as much as possible, try and allow for that. If someone is trying to help you out, and they make it clear they're struggling to follow you, keep this in mind.

Acknowledge the answer

I suppose this is a variation on "be polite" (really, a thanks can go a long way), but there's more to this than a friendly acknowledgement. If someone has gone to the trouble of offering some help, it's helpful to everyone who comes after you to acknowledge if it worked or not. That way a future help-seeker will know if the answer they're reading stands a chance of being the right one.

Accept that Textual is zero-point software (right now)

Of course the aim is to have every release of Textual be stable and useful, but things will break. So, please, do keep in mind things like:

  • Textual likely doesn't have your feature of choice just yet.
  • We might accidentally break something (perhaps pinning Textual and testing each release is a good plan here?).
  • We might deliberately break something because we've decided to take a particular feature or way of doing things in a better direction.

Of course it can be a bit frustrating at times, but overall the aim is to have the best framework possible in the long run.

Don't...

Okay, now for a bit of old-hacker finger-wagging. Here's a few things I'd personally discourage:

Lack patience

Sure, it can be annoying. You're in your flow, you've got a neat idea for a thing you want to build, you're stuck on one particular thing and you really need help right now! Thing is, that's unlikely to happen. Badgering individuals, or a whole resource, to reply right now, or complaining that it's been $TIME_PERIOD since you asked and nobody has replied... that's just going to make people less likely to reply.

Unnecessarily tag individuals

This one often goes hand in hand with the "lack patience" thing: Be it asking on Discord, or in GitHub issues, discussions or even PRs, unnecessarily tagging individuals is a bit rude. Speaking for myself and only myself: I love helping folk with Textual. If I could help everyone all the time the moment they have a problem, I would. But it doesn't work like that. There's any number of reasons I might not be responding to a particular request, including but not limited to (here I'm talking personally because I don't want to speak for anyone else, but I'm sure I'm not alone here):

  • I have a job. Sure, my job is (in part) Textual, but there's more to it than that particular issue. I might be doing other stuff.
  • I have my own projects to work on too. I like coding for fun as well (or writing preaching old dude blog posts like this I guess, but you get the idea).
  • I actually have other interests outside of work hours so I might actually be out doing a 10k in the local glen, or battling headcrabs in VR, or something.
  • Housework. :-/

You get the idea though. So while I'm off having a well-rounded life, it's not good to get unnecessarily intrusive alerts to something that either a) doesn't actually directly involve me or b) could wait.

Seek personal support

Again, I'm going to speak totally for myself here, but I also feel the general case is polite for all: there's a lot of good support resources available already; sending DMs on Discord or Twitter or in the Fediverse, looking for direct personal support, isn't really the best way to get help. Using the public/collective resources is absolutely the best way to get that help. Why's it a bad idea to dive into DMs? Here's some reasons I think it's not a good idea:

  • It's a variation on "unnecessarily tagging individuals".
  • You're short-changing yourself when it comes to getting help. If you ask somewhere more public you're asking a much bigger audience, who collectively have more time, more knowledge and more experience than a single individual.
  • Following on from that, any answers can be (politely) fact-checked or enhanced by that audience, resulting in a better chance of getting the best help possible.
  • The next seeker-of-help gets to miss out on your question and the answer. If asked and answered in public, it's a record that can help someone else in the future.
Doubt your ability or skill level

I suppose this should really be phrased as a do rather than a don't, as here I want to encourage something positive. A few times I've helped people out who have been very apologetic about their questions being "noob" questions, or about how they're fairly new to Python, or programming in general. Really, please, don't feel the need to apologise and don't be ashamed of where you're at.

If you've asked something that's obviously answered in the documentation, that's not a problem; you'll likely get pointed at the docs and it's what happens next that's the key bit. If the attitude is "oh, cool, that's exactly what I needed to be reading, thanks!" that's a really positive thing. The only time it's a problem is when there's a real reluctance to use the available resources. We've all seen that person somewhere at some point, right? ;-)

Not knowing things is totally cool.

Conclusion

So, that's my waffle over. As I said at the start: this is my own personal thoughts on how to get help with Textual, both as someone whose job it is to work on Textual and help people with Textual, and also as a FOSS advocate and supporter who can normally be found helping Textual users when he's not "on the clock" too.

What I've written here isn't exhaustive. Neither is it novel. Plenty has been written on the general subject in the past, and I'm sure more will be written on the subject in the future. I do, however, feel that these are the most common things I notice. I'd say those dos and don'ts cover 90% of "can I get some help?" interactions; perhaps closer to 99%.

Finally, and I think this is the most important thing to remember, the next time you are battling some issue while working with Textual: don't lose your head!

ℹ️ Note

This advice blog post was first hosted on the Textual devlog.

OIDIA

2 min read; 10 GFI

Another little thing that's up on PyPI now, which is the final bit of fallout from the Textual dogfooding sessions, is a little project I'm calling OIDIA.

The application is a streak tracker. I'm quite the fan of streak trackers. I've used a few over the years, both to help keep me motivated and honest, and also to help me track that I've avoided unhelpful things too. Now, most of the apps I've used, and use now, tend to have reminders and counts and stats and are all about "DO NOT BREAK THE STREAK OR ELSE" and that's mostly fine, but...

To keep things simple and to purely concentrate on how to build Textual apps, I made this a "non-judgement" streak tracker. It's designed to be really simple: you add a streak, you bump up/down the number of times you did (or didn't do) the thing related to that streak, for each day, and that's it.

No totals. No stats. No reminders and bugging. No judgement.

Here it is in action:

When I started it, I wasn't quite sure how I wanted to store the data. Throwing it in a SQLite database held some appeal, but that also felt like a lot of faff for something so simple. Also, I wanted to make the data as easy to get at, to use elsewhere, and to hack on, as possible. So in the end I went with a simple JSON file.

On macOS and GNU/Linux streaks.json lives in ~/.local/share/oidia, on Windows it'll be in... I'm not sure off the top of my head actually; it'll be in whatever directory the handy xdg library has chosen. and because it's JSON that means that something like this:

OIDIA in action

ends up looking like this:

[
    {
        "title": "Hack some Python",
        "days": {
            "2022-12-02": 1,
            "2022-12-03": 1,
            "2022-12-04": 1,
            "2022-12-05": 1,
            "2022-12-06": 1,
            "2022-12-07": 1,
            "2022-12-08": 1,
            "2022-12-01": 1,
            "2022-11-30": 1,
            "2022-11-29": 1,
            "2022-11-28": 1
        }
    },
    {
        "title": "Brush my teeth",
        "days": {
            "2022-12-02": 2,
            "2022-12-03": 2,
            "2022-12-04": 2,
            "2022-12-05": 2,
            "2022-12-06": 2,
            "2022-12-07": 2,
            "2022-12-08": 1,
            "2022-12-01": 2,
            "2022-11-30": 2,
            "2022-11-29": 2,
            "2022-11-28": 2
        }
    },
    {
        "title": "Walk",
        "days": {
            "2022-12-02": 1,
            "2022-12-03": 1,
            "2022-12-04": 1,
            "2022-12-05": 1,
            "2022-12-06": 1,
            "2022-12-07": 1,
            "2022-12-08": 1,
            "2022-12-01": 1,
            "2022-11-30": 1,
            "2022-11-29": 1,
            "2022-11-28": 1
        }
    },
    {
        "title": "Run 5k",
        "days": {
            "2022-12-03": 2,
            "2022-12-05": 1,
            "2022-11-30": 1,
            "2022-11-28": 2
        }
    },
    {
        "title": "Run 10k",
        "days": {
            "2022-12-03": 1,
            "2022-11-28": 1
        }
    }
]

Of course, it remains to be seen how well that actually scales; possibly not so well over a long period of time, but this was written more as another way to explore Textual than anything else. Even then, it would be pretty trivial to update to something better for holding the data.

If this seems like your thing (and I will be supporting it and onward developing it) you can find it over on PyPI, which means it can be installed with pip or the ever-handy pipx:

pipx install oidia

Be the Keymaster!

(Modified: 2026-04-28 10:21:31 UTC+01:00)
3 min read; 10 GFI

That didn't go to plan

So... yeah... the dogfooding... When I wrote my previous post I had wanted to try and do a post towards the end of each week, highlighting what I'd done on the "dogfooding" front. Life kinda had other plans. Not in a terrible way, but it turns out that getting both flu and Covid jabs (AKA "jags" as they tend to say in my adopted home) on the same day doesn't really agree with me too well.

I have been working, but there's been some odd moments in the past week and a bit and, last week, once I got to the end, I was glad for it to end. So no blog post happened.

Anyway...

What have I been up to?

While mostly sat feeling sorry for myself on my sofa, I have been coding. Rather than list all the different things here in detail, I'll quickly mention them with links to where to find them and play with them if you want:

FivePyFive

While my Textual 5x5 puzzle is one of the examples in the Textual repo, I wanted to make it more widely available so people can download it with pip or pipx. See over on PyPI and see if you can solve it. ;-)

textual-qrcode

I wanted to put together a very small example of how someone may put together a third party widget library, and in doing so selected what I thought was going to be a mostly-useless example: a wrapper around a text-based QR code generator website. Weirdly I've had a couple of people express a need for QR codes in the terminal since publishing that!

A Textual QR Code

PISpy

PISpy is a very simple terminal-based client for the PyPI API. Mostly it provides a hypertext interface to Python package details, letting you look up a package and then follow its dependency links. It's very simple at the moment, but I think more fun things can be done with this.

OIDIA

I'm a big fan of the use of streak-tracking in one form or another. Personally I use a streak-tracking app for keeping tabs of all sorts of good (and bad) habits, and as a heavy user of all things Apple I make a lot of use of the Fitness rings, etc. So I got to thinking it might be fun to do a really simple, no shaming, no counting, just recording, streak app for the Terminal. OIDIA is the result.

As of the time of writing I only finished the first version of this yesterday evening, so there are plenty of rough edges; but having got it to a point where it performed the basic tasks I wanted from it, that seemed like a good time to publish.

Expect to see this getting more updates and polish.

Wait, what about this Keymaster thing?

Ahh, yes, about that... So one of the handy things I'm finding about Textual is its key binding system. The more I build Textual apps, the more I appreciate the bindings, how they can be associated with specific widgets, the use of actions (which can be used from other places too), etc.

But... (there's always a "but" right -- I mean, there'd be no blog post to be had here otherwise).

The terminal doesn't have access to all the key combinations you may want to use, and also, because some keys can't necessarily be "typed", at least not easily (think about it: there's no F1 character, you have to type F1), many keys and key combinations need to be bound with specific names.

So there's two problems here: how do I discover what keys even turn up in my application, and when they do, what should I call them when I pass them to Binding?

That felt like a "well Dave just build an app for it!" problem. So I did:

If you're building apps with Textual and you want to discover what keys turn up from your terminal and are available to your application, you can:

pipx install textual-keys

and then just run textual-keys and start mashing the keyboard to find out.

There's a good chance that this app, or at least a version of it, will make it into Textual itself (very likely as one of the devtools). But for now it's just an easy install away.

I think there's a call to be made here too: have you built anything to help speed up how you work with Textual, or just make the development experience "just so"? If so, do let folk know, and come yell about it on the #show-and-tell channel in the Discord server.

ℹ️ Note

This personal development blog post was first hosted on the Textual devlog.

New Things On PyPI

(Modified: 2026-04-28 11:12:56 UTC+01:00)
4 min read; 9 GFI

An update

So, it's fast approaching 2 months now since I started the new thing and it's been a busy time. I've had to adjust to a quite a few new things, not least of which has been a longer and more involved commute. I'm actually mostly enjoying it too. While having to contend with buses isn't the best thing to be doing with my day, I do have a very fond spot for Edinburgh and it's nice to be in there most days of the week.

Part of the fallout from the new job has been that, in the last couple of weeks, I've thrown some more stuff up on PyPI. This comes about as part of a bit of a dog-fooding campaign we're on at the moment. While they have been, and will continue to be, mentioned on the Textualize blog, I thought I'd give a brief mention of them here on my own blog too given they are, essentially, personal projects.

gridinfo

This is one I'd like to tweak some more and improve on if possible. It is, in essence, a Python-coded terminal tool that does more or less the same as slstats.el. It started out as a rather silly quick hack, designed to do something different with rich-pixels.

Here's the finished version (as of the time of writing) being put through its paces:

Download from here, or install and play with it with a quick pipx install gridinfo.

unbored

The next experiment with Textual was to write a terminal-based client for the Bored-API. My initial plan for this was to just have a button or two that the user could mash on and they'd get an activity suggestion dropped into the middle of the terminal; but really that seemed a bit boring. Then I realised that it'd be a bit more silly and a bit more fun if I did it as a sort of TODO app. Bored? Run it up and use one of the activities you'd generated before. Don't like any of them? Ignore them and generate some more! Feeling bad that you've got such a backlog of reasons to not be bored? Delete a bunch!

And so on.

Here's a short video of it in action:

Download from here, or install and play with it with a quick pipx install unbored.

textual-qrcode

This one... this one I'm going to blame on the brain fog that followed flu and Covid jabs that happened the day before (and which are still kicking my arse 4 days later). Monday morning, at my desk, and I'm wondering what to next write to experiment with Textual, and I realised it would be interesting to write something that would show off that it's easy to make a third party widget library.

And... yeah, I don't know why, but I remembered qrencode.el and so textual-qrcode was born!

The most useless Textual widget yet

I think the most amusing part about this is that I did it in full knowledge that it would be useless; the idea being it would be a daft way of showing off how you could build a widget library as an add-on for Textual. But... more than one person actually ended up saying "yeah hold up there this could actually be handy!"

If you're one of those people... you'll find it here.

FivePyFive

While I was on a roll putting stuff up on PyPI, I also decided to tweak up my Textual-based 5x5 and throw that up too. This was my first app built with Textual, initially written before I'd even spoken to Will about the position here. At one point I even did a version in Lisp.

It's since gone on to become one of the example apps in Textual itself but I felt it deserved being made available to the world via an easy(ish) install. So, if you fancy trying to crack the puzzle in your terminal, just do a quick:

pipx install fivepyfive

and click away.

You can find it over here.

PISpy

Finally... for this week anyway, is a tool I've called PISpy. It's designed as a simple terminal client for looking up package information on PyPI. As of right now it's pretty straightforward, but I'd like to add more to it over time. Here's an example of it in action:

One small wrinkle with publishing it to PyPI was the fact that, once I'd chosen the name, I checked that it hadn't been used on PyPI (it hadn't) but when it came to publishing the package it got rejected because the name was too similar to another package! I don't know which, it wouldn't say, but that was a problem. So in the end the published name ended up having to be slightly different from the actual tool's name.

See over here for the package, and you can install it with a:

pipx install pispy-client

and then just run pispy in the terminal.

Conclusion

It's been a fun couple of weeks coming up with stuff to help exercise Textual, and there's more to come. Personally I've found the process really helpful in that it's help me learn more about the framework and also figure out my own approach to working with it. Each thing I've built so far has been a small step in evolution on from what I did in the previous thing. I doubt I've arrived at a plateau of understanding just yet.