Evaluating Qt

Published at 13:18 on 14 May 2026

I had high hopes for PySide6, the dominant Python binding to the Qt framework. From what I had read, it was clearly the most popular way to do GUI programming in Python, well-supported by an active dev team, and a little checking up revealed this impression was accurate.

All went well until it came time to do some I/O-and-compute-intensive background work. For some reason, the Qt dev team didn’t stop at GUI development; they went on to develop a whole universe that encompasses more than just graphical user interfaces. As an example, instead of using standard Python thread pools, all documentation on worker threads in Qt focuses on using QThreadPool. I was hoping to find some explanation on why things are done this way, and why doing it the standard Python way was or was not a good idea, but I had no luck. Not willing to run risks, QThreadPool it was.

Thankfully, it wasn’t bad. It’s just a typical thread pool that one submits jobs too. The problem happened when my jobs were done and wanted to return some results for the UI thread to display.

In normal UI frameworks, that is simplicity itself. There is a call to submit code for execution in the front-end event loop, you submit a closure containing the result and how to process it, exit the background job, and you are done.

Qt is not normal. It has these quirky relics of the early/mid 90’s attempts to manage concurrency called signals and slots and it expects you to use them everywhere. That includes worker jobs that need to notify the UI event loop of something.

It turns out that signals and slots are seriously broken by design. They don’t work unless both sender and receiver objects remain active until the signal is received by the slot. Any departure from this requirement and things will randomly fail. Sometimes they fail visibly with an exception (often an oddball one out of left field that doesn’t directly signify the cause of the issue). Sometimes signals will just get silently dropped on the floor.

So, no problem, just keep the sender active. But I’m sending the signal to indicate that I am done! Why should I remain active? My work is done, it is time to exit. It is an onerous requirement, one that I don’t have to bother with in any other framework of which I am aware. In wxPython, Swing, and Avalonia, I just submit the code to do the front-end processing to the event loop and exit. In Qt, I have to wait around for the front end to message me back that it has received my results and I may now exit.

And signals themselves are bizarre and un-Pythonic. You create them by defining class variables of type Signal, but you never use them as such. Instead you create instances of that class and Qt does some backhanded, behind-the-scenes hackery to transmute those class variables into instance variables of type SignalInstance. Just one more sharp edge for the programmer to cut themselves on.

It gets worse. It turns out that this is but a particular instance of Qt in Python being for the most part a thin veneer over a library written in C++. There is little or no communication between how C++ manages memory and how Python manages it. Create a control, add it to a parent control, but don’t store it in a global variable or as an instance variable in a long-lived Python object, and Python might garbage-collect it even though Qt still has a C++ pointer to the same memory region. Qt programmers have to follow all sorts of arbitrary rules to avoid memory management issues.

Use Qt and you will be painfully aware of memory management all the time. This cuts to the prime reason I want to use a language like Python, which features automatic memory management, in the first place! Forcing this sort of experience onto the programmer is positively asinine.

This is not a minor nitpick, as it gets in the way of how I like to program. I like to be dynamic and reactive. Just create UI elements on the fly and display them. The logic that manages and displays them will (or, rather, should) hold references to them, keeping them from being garbage collected, until they get closed or removed. Then they vanish, in memory as well as on screen. The number of elements naturally expands and contracts according to the needs of the user. The only constraint is the amount of hardware memory, and possibly quotas on same imposed by the system. Larger systems with more memory can naturally be taken advantage of, as needed. Simple, logical, effective. And needlessly painful to accomplish in Qt.

And this seems to be an only-in-Qt thing. In wxPython, for example, the dev team thought about this, and explicitly coded their glue to the C++ world to avoid these issues. (It’s not very hard to do.)

Qt seems to be the answer to the question: “I want the worst of both worlds. Is there any way I can program in a language as slow as Python and still have the memory-management headaches of C++ to deal with?”

To hell with Qt.

This Is Not a Pandemic

Published at 15:29 on 13 May 2026

It is scary that hantavirus is transmitting from human to human, and not via intermediate rodent hosts as is typical. But it is not a new development. The strain detected occurs in South America, where it transmits from human to human from time to time. Such it has been for a long time, and it hasn’t turned into a global (or even a regional) pandemic. The cruise began in Argentina, so it is obvious how its passengers originally became exposed. It spread like it did because a cruise ship concentrates a lot of people into a small space.

When the experts claim this is not going to turn into a pandemic, they are correct.

Evaluating Avalonia

Published at 09:49 on 13 May 2026

The suckiness that is the Java build environment finally got to me. That corrupt JAR file (the second time this has happened to me) was the final straw. I should not have to debug common build tools and the files they make. They should just work. The only bugs I should routinely battle with are the ones I just wrote in my own code. That is quite enough work, thank you very much.

So, despite Java in many ways being the best way to write a portable graphical application for the desktop, those JCA’s just are too much. The final straw had broken the camel’s back.

I had sort of been biased against Avalonia since learning that it focuses on pixel-for-pixel consistency between platforms (something I believe is a big mistake for desktop application programming). Then again, ASP.NET Core is so much better than any Python alternative web framework and Avalonia uses a similar code-behind model. Maybe the pixel-for-pixel compatibility doesn’t result in something that bad after all?

Executive summary: The rendering is better than I thought, but it doesn’t matter. There are enough other reasons not to use Avalonia.

File dialogs. In Swing (Java), Qt, and wxWidgets, one can customize these by adding extra controls. In Avalonia, you get the standard file dialog and that is it, no provision for customization. Want to ask for more parameters? Pop up a second dialog. The problem is, that is often a losing user experience; many load and save operations just naturally lend themselves to a need for a little extra information. Better to handle it all in one step, unless one is asking for a lot of extra information (and one usually is not).

File filters in dialogs. In Java and wxWidgets, you filter files by extension. In Qt, you filter by extension or MIME type. In Avalonia, you filter by extension on Windows and Linux. Linux also offers the option of filtering by MIME type. On the Mac, neither of the above work; you must filter on this wonky thing called an “Apple uniform identifier type.” Hello? The whole point of a cross-platform framework is to smooth this sort of stuff out! I shouldn’t have to deal with any weird Apple-specific stuff like this at all; it should just be MIME types and/or extensions everywhere. So stupid and annoying.

Data types in the clipboard. On most frameworks (Java Swing and AWT, Qt, wxWidgets) these are normalized from system-to-system. On Avalonia, the story is similar to file frameworks: one way for Linux and Windows, another way (those “Apple uniform identifier types” again) on Macs.

XAML. This is the declarative, layout-describing language that one codes behind. Unfortunately, unlike Razor templates in ASP.NET (which are powerful and have access to basically all C# control structures), XAML is pretty weak-sauce. It’s a garden-variety XML schema, with little or no provision for imperative coding. This is a pretty big problem, as cross-platform GUI programming often necessitates imperative coding. Consider pull-down menus; the Mac organizes them quite differently than do Windows or Linux. So a user-friendly app will include conditional logic and adapt itself accordingly. This is not exactly easy in XAML. You have to use some brand-new (and poorly-documented) features, some of which have some truly obnoxious misfeatures. The end result ends up being a lot more verbose and repetitive than it needs to be.

MVVM. Avalonia rams the model-view-view-model paradigm down your throat. It’s possible to resist this, but you have to work at it. MVVM was an effort to manage complexity in very large and complex programs. Not all programs are very large and complex (and fewer programs should be), so not all programs need MVVM. Even the inventor of MVVM openly admits this. Worse, the flavour of MVVM that Avalonia pushes is cartoonishly rigid, formulaic, and doctrinaire. Every view gets its own separate view model, even though it often makes more sense to share a view model amongst cooperating views. Even worse yet, by pushing something that makes sense for the largest programs, one pushes the mindset that computer programs should by default be large and complex. This mindset is positively harmful, as it leads to feature bloat.

The documentation. XAML is all standard controls classes under the hood, so one doesn’t have to use it. One can just use those controls classes directly, in C#. Then one gets the full suite of comprehensive imperative programming features C# has. Unfortunately, the documentation on how to do this is extremely incomplete. It’s mostly focused on how to do things in XAML. What documentation there is tends to be something of a mess. Complex classes, with many dozens of properties and methods, and nothing in alphabetical order. Why? Just why? There is simply no excuse for this lack of organization.

The choice of what programming language to use is often dictated by frameworks and libraries, and not the core features of the language itself. So it is here. Everything other than Avalonia in C# is generally worse than Avalonia. So C# simply doesn’t make sense for this project.

It is the exact converse of the situation with back-end Web programming. This time, it is Python that has clearly the best framework.

Why? Culture. Group identification. Avalonia was written by people that came out of the .NET world and wanted to make .NET applications more portable. It was strongly patterned after Windows frameworks like WPF. User-friendly GUI programs have never precisely been Microsoft’s strongest suit; Windows is rightly regarded as inferior to the Mac when it comes to user friendliness. It just turns out that that externally-visible awkwardness is there in no small part due to internal awkwardness bubbling up to the surface. And because the Windows universe is really big, most of those in it are astoundingly ignorant of what other universes have done.

When it came to Web servers, Microsoft had been left behind. They had to do a good job on ASP.NET, else nobody would switch over from the various open source frameworks that had already dominated the landscape. When it comes to the desktop, the exact opposite was the case. Complacency tends to produce inferior outcomes.

That said, I still might use Avalonia someday. If, that is, I ever have occasion to develop another smartphone app. I would want it to be portable, and the main alternatives to Avalonia for such things are Electron and Qt. The former is Javascript-based, and the way Javascript so badly botched modules and imports is reason enough to eschew the language. Qt is, well, Qt, and I will get into the specifics of that particular flavour of awfulness soon.

Portable GUI Frameworks

Published at 22:01 on 11 May 2026

I’ve been experimenting with them, because I have a need to rewrite a tool I use from Kotlin into some other programming language. Not because of any real defects in Kotlin (despite its proximity to the Java world and its associated JCA’s, this is not a show-stopper).

No, the problem is that I need to read HEIC files, and there is no good, easy way to do this in Kotlin, because there are simply no good, open-source, comprehensive, general-purpose imaging processing libraries available for the JVM. There are limited libraries that do not support HEIC nor the large size of the HEIC images I wish to read (such as the built-in stuff in the standard Java class library). There are esoteric libraries like ImageJ that support all sorts of oddball image formats used in the microscopy and health care fields, but not HEIC. There is no shortage of libraries that are abandonware and haven’t had an update in a decade or more.

By contrast, C# has Magick.NET and Python has Pillow, both of which do what I want. So I have been looking into GUI frameworks for those languages. Lets just say there are a lot of bad ones and leave it at that for now. I will post some details on the badness later (some of it is pretty mind-blowingly bad).

Suffice it to say that at this point I am reasonably sure wxPython will suit my needs, and it took a disgustingly long period of trial-and-error to get to that conclusion.

The Ebook Hard Sell

Published at 09:00 on 23 April 2026

This, I think, is how the capitalists are going to force more widespread adoption of ebooks (which are otherwise a questionable value proposition):

They will, in other words, deliberately under-produce (or, better yet, not produce at all) real paper books, books that give one the full right of ownership. Then, when customers understandably shy away from buying artificially scarce items at artificially inflated prices, announce that “the market has spoken” and there is no longer significant “customer interest” in traditional paper books.

The Trump-Vatican Spat

Published at 20:05 on 17 April 2026

Trump is ending up on the wrong side of the Catholic Church hierarchy. Pope Leo XIV, the first American pope, has issued multiple thinly-veiled criticisms of the Trump Regime’s policies, and Trump is increasingly unhappy (and vocally so) about it.

In the last 50 years, the track record of regimes that ended up prominently on the Vatican’s wrong side is, let us say, not precisely great. Most famously, of course, is the Soviet regime and its system of satellite states in eastern and central Europe. It all started with the emergence of Solidarność within about a year of the new Pope’s return visit to his homeland.

Pope John Paul II (and the Catholic Church in general) are cast in the popular view as figures of the Right for their role in undermining a nominally socialist system, and for their traditionalist views on the role and rights of women, the family, and homosexuality. It’s not so simple. Yes, those views exist, no doubt about it.

But Catholic social teaching has also long been critical of unbridled free-market capitalism. This goes way back. Literally back to Jesus Christ. The Gospels are full of messages of sympathy for the poor, the foreigner, and the less fortunate in general. That same “conservative” John Paul II wrote the following as a young cleric:

Necessarily demands brotherhood from people, without which the communal possession of goods seems impossible. This brotherhood is nothing else but the Christian idea of social love. Thus, in this way, the ideal of social love as the zenith of Christian ethical teaching converges with the communist ideal at its peak, when it comes to the realization of this ideal in the area of possessing material goods by people. Because of that when choosing between ideals, the Church must choose the communist ideal over the principle of private property.

And later, as pope:

Catholic social doctrine is not a surrogate for capitalism. In fact, although decisively condemning “socialism,” the church, since Leo XIII’s Rerum Novarum, has always distanced itself from capitalistic ideology, holding it responsible for grave social injustices. In Quadragesimo Anno Pius XI, for his part, used clear and strong words to stigmatize the international imperialism of money.

The latter quote, by the way, was spoken in Soviet-occupied Latvia in 1993. Even when criticizing the Soviet empire, John Paul II was careful to distance himself from capitalist orthodoxy.

I am old enough that I was alive and becoming increasingly politically aware when that was happening, and remember how incensed talking heads in the US media generally were when the Pope had the temerity to criticize capitalism. It was one of the things that made me realize the mass media were not nearly so free and impartial as I was told they were. But I digress.

It is not just the brutality of left-wing authoritarianism that John Paul II tangled with, either. He, and local bishops and cardinals, also played key roles in undermining both the Pinochet and Marcos dictatorships. Cardinal Jaime Lachica Sin is in fact widely regarded as one of the leaders of the revolution that toppled the Marcos dictatorship.

Of particular importance is that all these regimes fell, and in relatively short order (it didn’t take multiple decades).

My point here is not to gloss over or deny the conservative aspects of Catholic teaching. Those exist and are very real. My point is that Catholic teaching is more than just that. It aligns neatly with neither the political Right nor the political Left in the modern context. It is the result of a two-millennia old faith tradition, and an organization that sees itself as the custodian of that tradition, and not a creature of current sentiments.

I would also have to say that the Catholic Church’s less than stellar record in dealing with 20th century fascism (and remorse over the same) probably has something to do with it. It started with the Holy See signing the Lateran Treaty with the Mussolini regime. That particular treaty resolved a lot of ambiguity about the role of the pope in modern Europe (it created the Vatican City as an independent microstate), and is widely regarded as one of Mussolini’s few good accomplishments. Because of how well collaborating with the fascist regime in Italy turned out, the Vatican then chose to cut a deal with the fascist regime in Germany. The icing on the cake was Vatican’s complicity in setting up the ratlines that helped many Nazi war criminals escape justice.

It is thus reasonable to suspect that guilt for past misdeeds is part of the current willingness to criticize authoritarianism. Ultimately, though, that is a moot point. However that willingness emerged, it is there, it is deep-seated, and it has a past track record of success.

In this light, I suggest that the current spat between the Trump Regime and the Catholic Church is probably a good sign that the days of the former are in all likelihood limited.

Bypassing Cloudflare to Scrape a Web Site

Published at 21:49 on 15 April 2026

It’s supposed to be really hard, and Cloudflare does indeed to a very good of detecting (and banning) web scrapers. But, it turns out that this is one of those things that, while indeed very difficult to do in the general case, is actually quite simple in a lot of specific cases.

The main trick is to use Playwright (with the playwright_stealth addon) to control a browser, and to use that browser to scrape the web. You could theoretically use some other browser automating tool like Selenium to do this, but the problem with Selenium is that it basically advertises itself with every request (and there is no way to turn this off), and that triggers Cloudflare in short order.

The second trick is to mimic a human user as faithfully as possible. Generally, that means going slow, i.e. no faster than a human would navigate a web page, and inserting randomness into the process so it looks like a human and not an automaton is controlling the browser. This is where things break down for abusive scrapers like AI companies; they can’t take it slow, because if they do, it will literally take millennia to get as much data as they want. But I am not an AI company, and only need to scrape a modest amount of data, so taking it slow is good enough (I just let it run, and the results get collected eventually, not as fast as they might otherwise have been, but it still beats manual cutting and pasting).

The reason I mention this is that, if you try searching on how to do this, the results tend to be pretty useless. They are dominated by either a) techniques that no longer work and which will trigger Cloudflare almost instantly, or b) commercial scraping services interested in taking your money. The latter may actually be a useful service if you want to scrape a lot, but I am not in that category.

That all of this is even necessary to write about is yet another data point in the overall enshittification of the Internet, which by my reckoning peaked in its usefulness circa 2010 and has been heading downhill ever since. The services I now have to use devious techniques to scrape used to be easily scrapable; actually, in many cases, they had free API’s designed to work well with another computer and didn’t need to be scraped at all.

And yes, the abusive scrapers are as much (or more) to blame here as are the sites removing functionality. I have had to implement anti-scraping measures on a site I host, because AI companies were ignoring robots.txt and scraping the daylights out of it. It was so bad that I was getting server crashes from the abuse.

Downtown Vancouver Compass Directions: A Proposal

Published at 08:51 on 11 April 2026

If you live in Vancouver, you know why this is needed without any explanation: the streets in Downtown are canted at approximately a 45˚ angle from the cardinal compass points, as a result of the desire to better align them with the shoreline of Vancouver Harbour.

This of course makes giving directions awkward: “Head north on Granville Street.” But Granville Street doesn’t run either north or south. What does that mean? This proposal offers a concise solutions.

Define “north” to mean “towards Vancouver Harbour” and “west” to mean “towards Stanley Park” and it all sorts out. Plus, this makes sense. Vancouver Harbour is in the same direction as the North Shore, which has “north” as part of its name. Correspondingly, the neighbourhood that borders Stanley Park is known as the West End.

Pedants upset by the fact that these terms do not align with their traditional meaning can be placated by referring to them as “grid north,” “grid west,” etc.

This is something I have always done in my own mind, in fact. But I keep running into pedants who bring up that 45˚ cant. Now I have some rhetorical ammunition at the ready.

Another Javascript Problem: Libraries

Published at 08:26 on 5 April 2026

One of the nice things about C# and Python is that both come with a robust, comprehensive standard library. Java, too: although its standard library tends to be awkward and clunky to use, it is still comprehensive and well documented.

Javascript? Not so much. As part of its heritage of originally being a relatively small, client-side language for embedded scripting, its built-in library is not nearly so comprehensive. One must instead go to npm for many things that are supported out of the box in many other programming languages. Thankfully, as a result of Javascript’s popularity, npm is very comprehensive.

Unfortunately, the quality of the libraries published there (and the quality of their documentation) is, let me just say, erratic. Plus, there’s just so many of them, and it is not always obvious which libraries are better-supported and higher-quality, often resulting a fair amount of trial and error before one finds a suitable solution. Then there is the module and import hell I have written of before; sometimes a given module is not available in the module and import configuration you are currently using, causing you to have to deal with all sorts of uniquely Javascript headaches that no decent programming language has.

In short, if you write an application in Javascript, you’re not going to be able to find reasonable, well-documented default library solutions as often as you would were you using a better-designed, better-planned programming language. Finding and using the right libraries is going to be a source of recurring headaches.