Written by Bo Thorsen
Creating custom widgets is not a hard problem, but there are a few things you should consider. Here I present a simple list of what you should at least consider doing. I won’t handle the case where you build a widget that is just a container with children, because this is not a “real” custom widget. This is about creating a completely new widget that is not included in the standard list of widgets.
This blog is meant as a check-list you can go to, when you create a new widget. Just run through the items below and you are well on your way to creating a good widget that will serve you well in many different circumstances. What you won’t see here is a deep and thorough discussion of how to do each of them. But I will give you a bunch of hints and tips on how to do a properly good job when you are tasked with creating a custom widget.
The list of things you need to consider are the following:
- Sizing for layouts
- Size policy
- Focus policy
- QStyle support
- Input handling
- Signals and slots
- Designer plugin
This is perhaps a surprisingly long list. And there will be some of those you won’t need to handle.
If you don’t have a lot of experience with creating custom widgets, you should go through the items below methodically. As you gain experience, this simple list will be enough. And, of course, later you won’t need it at all.
Make custom widget development easier for yourself by saving this check-list and using it whenever you start a new widget.
Sizing for layouts / size policy
One of the things you should always do is handle sizing. This is not needed, if you don’t use layout managers, because then the sizing of this widget will be handled from the outside. But in my almost 21 years as a Qt developer, I have so far not seen a single case where it was a good idea not to use the layouts.
The one thing you must do is to give it a size hint. Just override the sizeHint() method and return the proper size for this widget. The trick is to figure out what the proper size is. In some cases this will rely on font size, or the contents. In those cases, you need to notify the layout system that the size hint has changed, if the content or font changes. You do this by calling updateGeometry().
For a few widgets, the height depends on how wide the widget is set, or the width depends on the height. In these cases, override the heightForWidth or widthForHeight methods. Those are pretty rare, though.
It is the size policy that tells the layout managers how to use the size information from the widget. These size policies are the one thing that most other window layout systems miss, and it’s the key to fully using the Qt systems power. You need to set those correctly. In an upcoming blog entry, I will go into detail about the layouts and the size policies, so I will only briefly mention those here. In most cases you simply call setSizePolicy() with the policies for vertical and horizontal. These can be overwritten by the users of this widget class, but it’s important that you set reasonable defaults. Find the proper size policies in the documentation. It’s a good tip to look at the value, because the flags are to me a very good hint at what the size policies really do – in many cases better than the description text. Finally, remember that many widgets have different size policies for vertical and horizontal.
The focus policy is quite simple, as it’s only about keyboard input. There are basically only two that makes sense: No focus or strong focus.
Once you have focus, the keyboard events will go to this widget.
Note that you can get keyboard events even if this has a no focus policy, if the widget has children with focus, who doesn’t accept the keyboard events. But this is admittedly a rare thing to do. Usually, if you set no focus, then you don’t add keyboard handlers.
Unless you are creating an invisible container widget, you are of course going to need some painting. Implement the paintEvent() method. This is only mentioned for completeness, and I won’t go into details with it.
There are basically two things you need to do on the widget side, if you’re using a QStyle. Those are to implement the size hint and paint event by just calling the style.
In both cases, you need a style option. Initialize that and call the proper method on the current style. I usually create a private method on the d-pointer that sets up the fields of the style option. Remember that the base QStyleOption has an initFrom() method that sets up the standard QWidget fields.
Sometimes you create a custom widget that looks to the user like it’s a standard Qt widget – a QComboBox like widget, for example. The reason for doing this is to modify the default behaviour in a way you can’t do with the available API on the widget. In this case, you can reuse the styling code to handle size and painting by calling the style with the proper option object. And then you implement the input handlers to change the widget behaviour.
However, in most cases, the custom widgets require custom style code. The way to do this is usually to inherit QProxyStyle and add the code to handle your widget(s) here and call the standard code for all other widgets.
It’s not a clear cut decision whether or not to support styles in custom widgets. In some cases, the sizes and painting are going to be the same no matter what the system style is, and in that case it makes no sense at all to move the size and paint code to the style. But if you do have to implement different styles for your widget, then you should go with the style code.
It’s also a decision that can quite easily be changed later, as the code is fairly similar, whether or not it’s placed in the class itself or in a style class.
There are a lot of input types in Qt:
- Mouse wheel
- Touch screens
- Pen tablets (Wacom boards for example)
- Custom hardware for non standard input
All of those can have an effect on your widget, if you want them to. The most common thing is to implement just the keyboard and mouse, because for most widgets the standard conversion from touch or pen tablet to mouse is fine. But if you need the additional information from the pen strength/size etc or have to handle multi touch, then you need to implement support for these as well.
A lot of custom hardware is usually mapped to actually work with keyboard events, which saves you from handling those in some weird fashion. If this is the case, you can see the key codes that you receive from the hardware and react to those.
Handling keyboard is important even for widgets that doesn’t allow text input. For example, a button will normally allow space or enter to work as a mouse press, and radio buttons will allow keys up or down to switch the current setting. The key (no pun intended) is to try and help the user by allowing him or her to do what feels natural.
You also need to remember that input handling adds states that the painting (or the size) needs to handle. For example, a normal button has these flags that can be true or not: Pressed, focus, enabled and mouse hover. Combining those leads to a bunch of independent states that all need to be drawn differently. (None of those will affect the size of a button, but setting the text on it will.)
When you get a specification from a graphics artist on how the widget should look, one of the first things you need to do is to sit down and go through the widget states and make sure they are all covered by the spec. Because they never are. So you need a round trip to the graphics people to get a complete spec, and you might as well do the rest of the code while you wait for the updated spec.
The Qt properties serve a number of purposes:
- Tell the widget users what the properties that modify widget contents are.
- Export the property to QML code. Sure, you can’t just show a QWidget in QML, but they are also QObjects where you can manipulate the properties. It’s not a good reason in this particular blog entry, but normally it is.
- If you implement a designer plugin, the properties are available for manipulation in designer for this widget.
- Works with Qt introspection so you can list the properties and manipulate them dynamically (which is what designer does).
Of those, the first is always valid. And for this reason I tend to use Q_PROPERTY for all properties on my object classes. You should at least make the value(s) of the widget a property, but there might be others as well – headers, suffix, etc.
The only thing special (when speaking about code) to widgets about properties is if you add a designer plugin. In all other aspects, the properties have the same advantages as for any QObject subclass.
Signals and slots
Proper use of signals and slots is one of my biggest pet peeves in Qt development. If you watched my QObject Deep Dive at the Qt World Summit 2017, you will know this is an area I care a lot about.
To me, signals and slots are one of the ways you can help yourself avoid the dreaded spaghetti code. This is done by creating black boxes that do not know anything about the outside world. The Qt signal/slot system is just an implementation of the visitor pattern, where some of the work is automatically done for you.
For example, the button knows whether or not it’s been clicked. It will tell you when it’s clicked by emitting a signal. And it has no clue about what the consequence of a click is. Most developers instantly see that it’s wrong for the button to handle a click directly, by calling some function when it happens. But the same developers do not follow this for other widgets, so suddenly the changes in widget state results in a direct call somewhere.
The proper way to think about signals and slots is that it’s the API for setting and listening to an object. And two objects are connected from signal to slot by a third object that owns both of them. That owner object is the one that knows what the consequence of, for example, the click is.
When you consider which slots to offer, it’s mostly simple setter methods, when we’re talking widgets. Another usual type is to perform the action of the widget – click the button or trigger the action. For controller objects it’s not this simple, because there are a lot of slots that are connected to the action signals from the widgets. But for widgets it’s often the case that our slots are setters or actions. Most properties will have a slot, unless they are read only. There are also some methods you allow to be slots, simply because you connect them to other objects, but again this isn’t common for widgets. Except in the case where a widget has children, and in that case you usually use private slots or lambda slots to connect to the children signals.
For signals, you should communicate changes in states for your widget. It’s not all states that should be emitted as signals, for example a press by a mouse isn’t communicated, the release isn’t, but the aggregated event “clicked” is. You should also add a myPropertyChanged signal for each property.
For a longer discussion of this topic, you can watch the video with my talk from Qt WS 2017.
The purpose of a designer plugin is to allow the UI/UX people and the developers to manipulate your custom widgets in Qt designer. Or in the embedded designer in Qt Creator.
When you install the plugin, you get your widget in the list of available widgets, and all the custom properties are available to set up. Also, the widget will be properly painted by the actual paint event in your widget. And you can also make it look like the proper QStyle, if you have a custom style as well.
The alternative to doing this is to use the designer promotion, where you just tell designer that you have a widget and it’s a subclass of QWidget (or QFrame, or whatever you actually have). In this case you only see the base class properties. You can in theory set the properties with dynamic properties, but please don’t. In this case, set up your custom widget in code where it is created.
Creating a designer often seems like a good idea. However, it does present you with some deployment problems, especially for large teams. Because you almost force the entire set of developers to build and install the plugin before they can use the designer. It’s not necessary for compilation, though, it’s only if you do any work in designer itself that you need it. Unfortunately designer plugins are notoriously difficult to compile, and I have seen endless questions about the plugins from the teams we have worked with. The problem is that you have to build it with exactly the same compiler tool chain as designer was built with, and you have to do it in release mode. Unless your Qt is built in debug, then your plugin needs to be built in debug mode as well. So you can’t just always use the same compiler as you build the application with, if you use the system Qt or a downloaded Qt version. It’s easier, though, if all developers work with a custom built Qt. But these days it’s rare to see that.
I haven’t mentioned QML items here, since it’s about widgets. But I actually apply a lot of the same rules when creating custom items. I always have a top level Item that encapsulates the item contents. On the top level item I place the public properties, signals and functions. And I handle size, painting, input events etc. Most of the things in the list are valid for QML as well, although you have to map the concept to how QML works.
I’ll leave you with one final tip: Don’t place any logic in the widget, other than the logic necessary for the widget itself (paint states, input handling…). Logic that handles anything outside of the widget states needs to go somewhere else. Emit signals and react to those outside of the widget instead.