I don't think I've mentioned it before on this blog, but some time back I
decided it would be fun to use Textual to
write a Mandelbrot explorer (simple Mandelbrot explorers have been another
one of my favourite known problem to try an unknown
thing problems). Doing it in the
terminal seemed like a fun little hack. I started off with creating
textual-canvas and then built
textual-mandelbrot on top
of that.
Not too long back I added a "command palette" to
Textual
(I'd prefer to call it a minibuffer, but I get that that's not fashionable
these days), but so far I've not used it in any of my own projects; earlier
today I thought it could be fun to add it to textual-mandelbrot.
Most of the commands I've added are trivial and really better covered by
(and are covered by) keystrokes, but it was a good test and a way to show
off how to create a command provider.
Having started this I can see some more useful things to add: for example it
might be interesting to add a facility where you can bookmark a specific
location, zoom level, iteration value, etc, and revisit later. The command
palette would feel like a great way to pull back those bookmarks.
What I really liked though was how easy this was to do. The code to make
the commands
available
is pretty trivial and, I believe, easy to follow. Although I do say so
myself I think I managed to design a very accessible API for this.
There's more I'd like to add to that (the Textual command palette itself, I
mean), of course; this was just the start. Support for commands that accept
and prompt for arguments would be a neat and obvious enhancement (especially
if done in a way that's reminiscent of how commands could be defined in
CLIM -- I
remember really liking how you
could create self-documenting and self-completing commands in that).
Since quickly hacking together textual-query-sandbox a few days
back, I've made a bunch of small
changes here and there. While most have been cosmetic and playing with some
ideas, some have also been internal improvements that should make the tool
work better.
The most prominent change is one I pondered in the previous post, where I
thought it might be interesting to have a small collection of playgrounds
grounded together with a TabbedContent. So as of now the tool still has
the original playground which had an emphasis on nested containers:
There's now a playground with an emphasis on selecting widgets within
containers1:
There's also now a playground that has an emphasis on pulling out widgets
based on ID and classes:
The other change you will notice from the original post is the DOM tree
shown in the bottom right corner. Note that that isn't there to show your
query result (that's the bottom left panel), it's there to help picture how
the DOM in the current playground hangs together, and will hopefully help in
picturing the structure for when you write a query.
I sense there's still a lot of fun things I could add to this, and I'm still
keen on the idea of having the playgrounds "soft coded" in some way, so
people can make their own and load them up.
Another thing I want to try and work on is making the display as useful as
possible. While I think it's actually pretty neat and clear, there's not a
lot of space2 available to show the playground and the results. Finding
a good balance is an interesting problem.
For a number of reasons this is turning into a really enjoyable tinker
project.
This is, of course, slightly nonsensical wording. Containers are
widgets in Textual. Pretty much everything you see in your terminal is a
widget, even a Screen is a widget. ↩
A lot of this of course hinges on how big someone's terminal is. I
tend to run a fairly high resolutions with the smallest font I find
readable so my terminal windows are often pretty "big"; other people
tend to have something much smaller in terms of cell with/height. ↩
Sometimes I can have an idea for a Textual widget, library or application on
my ideas list for weeks, months even, before I get around to it -- mostly
just due to not having the clear time to make a run at getting it going --
and then other times an idea can pop into my head and it has to be created
there and then. Has to be!
This happened yesterday evening.
While the tool I built is something I'd thought of before (back around
November last year I think) it hadn't even made it to my "list of stuff I
should make" that I keep in Apple Reminders; not sure why really. But then
yesterday evening a question cropped up on the Textual Discord
server that related to the subject and I was
reminded of it.
The subject being: Textual DOM
queries. I like to think that
DOM queries in Textual are pretty easy to do, and well-explained in the
docs, but it's fair to admit that they need a bit of practice first, just
like any powerful tool. So I was reminded that I'd wanted to write a sandbox
application, that would have a practice DOM inside it, an input field to
type in a query, and a way of displaying the results.
In this very first version (which was really quickly put together -- it
was something like 15 minutes to write the main code and then probably 45
minutes tweaking styles, adding all the admin stuff to allow deployment to
PyPi and writing the
README) there's an Input,
a display of a group of nested containers with different IDs and classes,
and then a Pretty widget
at the bottom to show the
query
result.
If you think this looks like it might be useful to you, it can be installed
using either pip or (ideally) pipx:
$pipxinstalltextual-query-sandbox
and then you can run it with:
$tqs
At which point load up the Textual query docs, type queries into the input
field, hit enter and see what gets highlighted and which widgets end up in
the result set at the bottom of the screen.
Like I say: this was a quick hack yesterday evening, I think there's a lot
more can go into this. For one thing I think a more interesting practice DOM
would be a good idea, with a good mix of widgets; another thing could be
having a collection of different DOM playgrounds that can be switched
between (a TabbedContent of different playgrounds could be fun here); this
could even be taken further such that the user can create their own
playground DOM to practice against.
Eventually it would be neat if this could be turned into a library that can
be included in a Textual application, as a development-time debug tool, so
that on-the-fly test queries can be made.
For now though, it's started, it's under way, and I think the current
version probably covers 90% of the use cases for something like this; making
for a really quick and easy tool to double-check how to query something.
Late on last year I wrote about a
bunch of new things that I'd added to PyPi, things mostly kicked off by an
early dog-fooding session we had at textual
HQ.
Since then I've been slowly doing my best to keep the applications up to
date with Textual.
As much as possible we try and not make breaking changes with the framework,
but at the same time it is still 0.x software and there's still new ways of
doing things being designed so there's going to be the odd break in approach
now and again.
Unbored, my kind of silly
self-populating TODO list application, has been sitting atop Textual 0.20.x
for a while now and earlier today I checked how it was getting in with
0.32.0 and... actually surprisingly okay. Not perfect, there were a couple
of things that had suffered from bitrot, but it wasn't crashing.
The main thing I needed to change was the ability to focus a couple of
containers (they didn't used to receive focus by default, now they do so I
had to tell them not to again), and that was about it.
While the application itself is a bit silly, and likely of no real use to
anyone, I feel it's a pretty good barometer application, helping me check
what the experience is like when it comes to maintaining a Textual
application and the needs to keep on top of changes to Textual.
It goes without saying, I hope, that really you should pin the Textual
dependency for your applications, and upgrade in a controlled and tested
way; for this though it's less crucial and is a good test of the state of
the ecosystem, and on the remote chance that anyone is using it, it'll be
helpful to me if it does break and they yell.
Given that for a good chunk of this year I've been a bit lax about writing
here, there's a couple or so coding projects I've not written about (well,
not on here anyway -- I have spoken lots about them over on
Fosstodon). One such project is
textual-canvas.
As the name might suggest, it's a "canvas" for Textual applications, which
provides a pretty basic interface for drawing pixels, lines and circles --
and of course any other shape you are able to build up from those basics.
Posted on 2023-07-05 17:56 +0100 in Meta
• Tagged with
Python, Blogging
• 2 min read
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):
Made a branch in the repo to work in.
Removed all of the Jekyll-oriented files.
Decided to set up Pelican and related tools in a virtual environment,
managed using pipenv.
Ran pelican-quickstart to kick things off and give me a framework to
start with.
Renamed the old _posts directory to content.
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).
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).
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.
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).
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.
Posted on 2023-07-04 08:32 +0100 in Meta
• Tagged with
Python, Blogging
• 3 min read
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.
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:
$pipxinstall"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 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.
;-)
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. ↩
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".
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.
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:allall:pipenvrunpythonmake_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.
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. ↩
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:
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:
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 busses 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 (you can read some
background to this over on the company
blog).
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!
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!"
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:
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:
$pipxinstallpispy-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.