After spending way too much time digging through inadequate documentation and fighting obscure errors, I figured I'd write something on my experience with writing custom widget plugins for Qt Designer, why I decided to do it, and how to debug them when things go wrong. Including Windows and Linux specific issues.
I haven't tried building this on a Mac. I imagine the experience would be similar to Linux, although there appears to be some additional steps required.
What is Qt Designer?
Qt Designer is the official UI design tool for Qt. It is used to author .ui files that describe the UI and layout of a widget using XML. UI files can be used to generate C++ code and then compiled into the application, or it can be loaded at runtime.
Qt Designer is available as both a standalone application, and integrated into Qt Creator IDE.
If used properly this system can help you separate logic and design, allowing designers to focus on UI design, and programmers to focus on the underlying logic. Of course, this rarely works out perfectly, and you will occasionally have to modify some code with your UI changes. Still, there are benefits to UI files: Prototyping, fast iteration times, tweaking, WYSIWYG design, etc.
Whether this is useful or not depends on the project, and the people working on it.
Why make a Qt Designer plugin?
While Qt Designer is not the best UI designer application I've used, it works reasonably well with the standard Qt widgets. The keyword here is standard Qt widgets; if you've ever worked on a larger project using Qt, you've almost certainly ended up making or using custom widgets. Custom widgets are great, and can vastly improve the user experience over generic widgets. The only problem is that Qt Designer doesn't know how to use them.
A common workaround is to use the base class—typically QWidget—and "promote" it in the designer. While this will compile and run just fine, you immediately lose much of what makes Qt Designer useful: it's no longer WYSIWYG. Your placeholder widget won't look correct, and won't behave like your custom widget; you need to compile and run the program to see it in action. Not only that, if your custom widget has custom properties, you can't set them from within Qt Designer.
For me, this is such a huge drawback that in the past, I've often ended up moving UI into code entirely. I assumed—incorrectly—that getting Qt Designer to work with custom widgets was a lot of work. Turns out it's actually pretty simple! Even better, the plugin code is pretty easy to maintain. In fact, I've barely touched the plugin code for my widgets since I first wrote it, even though the widgets themselves have been updated plenty of times since.
The biggest issue turned out to be loading the plugin, and—somewhat surprisingly for Qt—the documentation around that.
Writing a Qt Designer plugin
Qt does provide a pretty comprehensive example for how to write a Qt Designer plugin for a single widget. If you want to build a collection of widgets, like I've done here, you'll want to take a look at the QDesignerCustomWidgetCollectionInterface class.
The API documentation provides enough of an example to get you started. In short, you'll need to implement QDesignerCustomWidgetInterface for each widget you wish to expose as a QDesigner plugin, then you need to implement QDesignerCustomWidgetCollectionInterface to create a collection. The result will be one library with all your plugins bundled into it. Code-wise that's it really. It's not a lot of boiler-plate; most of the code is just telling Qt Designer which widgets should be bundled and how they should be presented.
The tricky part is getting Qt Designer to load your plugin. This part of the manual tells you to place the compiled library in bin/plugins/designer subdirectory of your Qt installation. But, what if you don't want or can't install your plugin in the install directory?
Gaps in the documentation
When I first attempted to write a plugin, my collection of widgets were built using QMake, and a series of .pro files. There is plenty of official HOWTOs for setting this up, and the example mentioned previously will guide you through the essential parts, assuming you're using QMake. The example and most other documentation available fails to mention some of the configuration steps QMake itself is doing behind the scenes, which is quite frustrating when you're not using QMake. Additionally, documentation on how to debug a plugin that is not working properly—along with common issues for certain platforms—is a bit scattered, and can be hard to find. If it exists at all.
Building your plugin using CMake
CMake ships with Qt modules, so building Qt programs using CMake in general works quite well. I'm not going to go through how CMake works, or how to set up a typical Qt project; there are plenty of examples out there (including my own, which also includes sip bindings for Python/PyQt5). For Qt plugins there are not as many examples and there are a few gotchas to be aware of:
Typically, a Qt project would depend on the Core, Widgets, and maybe Gui components of Qt. Since we're building a designer plugin, we'll also need to depend on the Designer component:
We also need to tell CMake where the header files are for the Designer component. If we don't, compilation will fail with a somewhat confusing "Undefined interface" error.
The rest of the setup should be similar to a normal Qt project, which I won't go through here.
A fully functional example is available on my github.
If you are using a different build system, there are a few other things you need to consider (see below).
Debugging your plugin
There is a tiny section containing some tips for debugging your plugin, but it's a bit vague and, at the time of writing makes references to QMake specific solutions.
So, if your plugin works and Qt Designer finds it, great! It'll show up in the widget list on the left, under the name and category you've specified in your plugin.
If your plugin does not work, there is... Nothing. Was there a problem loading the plugin? Did Qt Designer even find it? By default you'll get zero error messages and zero output.
Obtaining debug output
Fortunately, Qt Designer can provide debug information, if you enable it and know where to look:
If you're running Qt Designer directly you might get some hints by checking the dialog under Help->About Plugins... If the plugins were found but couldn't be loaded, this might provide a clue. If you're using Qt Creator, this might not help much, as designer plugins do not appear to show up in the list, even if they have been successfully loaded.
Set the environment variable
QT_DEBUG_PLUGINS to a non-zero value before launching Qt Designer or Qt Creator to get additional information. A pretty huge gotcha here realizing that these messages are printed using Qt's logging facilities.
On Linux (and probably Mac), this goes to the standard output, so make sure you launch Qt Designer from a terminal.
On Windows, they're sent to the debugger. In other words, you won't see anything at all unless you specifically look at the debug log stream. If you don't have a debugger attached, you can use DebugView.
Qt Designer does not mention the plugin
OK, so neither the plugin dialog, nor the debug output even mentions your plugin. This very likely means it cannot find it. Or more accurately, it's not in its search path. Qt will look for plugins in a number of places. If you don't want to install your plugin in the Qt installation directory, and if you don't want to build a custom version of Qt Designer with additional search paths, the best way is to simply set the
QT_PLUGIN_PATH environment variable before you launch Qt Designer. The linked documentation mentions this, but it fails to mention what it expects that directory to contain. A Qt plugin path should point to a directory with subdirectories, one for each plugin type. For designer plugins, this directory is called "designer". So if you have a plugin called myplugin.dll, or myplugin.so, you can place that file in e.g. "my_qt_plugins/designer/" and point
QT_PLUGIN_PATH to "my_qt_plugins".
Qt Designer complains about mixing debug and release
One platform specific issue you can run into under Windows is this warning:
The plugin 'C:/my_qt_plugins/designer/myplugin.dll' uses incompatible Qt library. (Cannot mix debug and release libraries.)
If you're not using QMake, this warning is very misleading.
It has nothing to do with the library being built in release or debug. At least not in the traditional sense; this warning is derived from json metadata embedded in the plugin. A section of this metadata is user specific and can be pretty useful if you are loading custom plugins in your application using QPluginLoader. This metadata is also printed by Qt Designer if you set
QT_DEBUG_PLUGINS as mentioned above.
As it turns out, there is a "debug" key embedded in this metadata that is set during compile. If you use QMake, this is set to false if you build release, and true if you build debug. However, this is specific to QMake. If you build your plugin using another build system that isn't Qt-aware, you'll have to set it yourself.
CMake comes with Qt modules and should do this for you.
As far as I can tell, there is no documentation for this, and I ended up having to check the source code responsible for creating the metadata. In short, if
QT_NO_DEBUG is not set when building release, Qt will think that it's a debug library and refuse to load.
This flag has other effects, such as removing
See Debugging Macros for more information.
In short, while the warning does have merit, it's entirely possible to build your library in release and still get the warning. Similarly, if your version of Qt Designer was built in debug and you accidentally build a release version of your plugin with the debug key set, things may break.
You'll also need to build your plugin so it's compatible with Qt Designer. If you're running Windows, and you're not building Qt Designer yourself, it's probably a 32bit release build.
The downloaded version of Qt Creator and Qt Designer on Windows can be built with different compilers, and won't load plugins that have not been built using the same compiler.
If Qt Designer is 32bit, make sure your plugin is 32bit too. If it's 64bit, make sure your plugin is 64bit. You also need to make sure the compiler used for building your plugin produces compatible output with the compiler used to build Qt Designer. Depending on the situation and how your plugin is used and distributed, it might be worth building your own version of Qt Designer or Qt Creator.