Creating a UI using Qt Designer - robotique-udes/rover_udes GitHub Wiki

Installing Qt Designer

The easiest way to install Qt Designer is to install PySide6 from pip using pip install pyside6, then you can launch it using pyside6-designer.

Creating a UI: learning from example

Launch Qt Designer. Select a new Main Window. (If you need to create a Widget for rqt, select Widget instead. For this tutorial, we will create a standalone Main Window. Note that most screenshots were taken while creating a Widget instead of a MainWindow. This is not really relevant, and everything should work as expected.) Always start by adding a Grid Layout (QGridLayout) to your Widget or MainWindow. Use drag-and-drop to do so.

image

This will be your main container.

Add an element. Drag-and-drop a Label (QLabel) within the boundaries of the grid Layout. Select the Label you've just added. The rightmost part of the screen represents the properties of your selected component. One of the most important of these properties is the objectName. This is the name that you will use to access this element from within your code.

image

You can explore the properties for the objects you add. Here, we can see that there are properties to configure the font of the text that will be displayed by our label.

image

The properties are grouped by the class hierarchy. QObject is always at the top, with the objectName property. Then, you usually have QWidget, and after that it depends on the element you selected. In case of the Label, we can change the text that is displayed using the text property in the QLabel section.

image

We will now add a second element to our grid and learn how to position them within the grid. Drag a Double Spin Box (QDoubleSpinBox) and hover above the right side of the grid. A blue line will appear, this is the place where your new element will be inserted if you drop it now.

image

Drop it there. Set its objectName property to x_spinbox.

Add a QLabel in between the first label ("Position") and the newly inserted QSpinBox, and set it's text to "x". You can also change the text of a QLabel by double-clicking on it. Then, add two more QLabel and two more QDoubleSpinBox underneath the first row. Set the text of the labels to "y" and "z". Set the objectName property of the QDoubleSpinBox to y_spinbox and z_spinbox.

image

Select each QDoubleSpinBox, and check the property readOnly in the QAbstractSpinBox section (QAbstractSpinBox::readOnly).

image

(The image shows QSpinBox instead of QDoubleSpinBox, but the rest should still be valid as both are very similar elements.)

You can resize elements in the grid so that they take more than one spot. Select the "Position" label, and drag the bottom blue dot down one spot in the grid, than do it again.

image

image

This is too spaced out, we want it to be grouped at the top. Add a Vertical Spacer (QSpacerItem) under the "z" label (red annotation).

image

Expand it left and right (blue annotation). Notice the properties of the spacer (green annotation). First, you can see that a Spacer is not a QObject. You can also see a property called sizeType. QWidget also has a similar property, called sizePolicy. These properties define what the element will do to take up the space. By default, spacers are "Expanding": they will take the most space that they can in the direction that they act upon. This property accepts a fixed set of values. If you click on it, you will see a drop-down appear, letting you choose a value. Many properties are like that, and you will always get a dropdown menu like this so that you can know the list of available values. If you want to know what each value meens, you can refer to the Qt documentation. For the sizeType here, see the doc on QSizePolicy. For instance, if you wanted to put a specific amount of space between two elements, you could choose "Fixed". For now, we will stick to "Expanding".

We will now learn a way to fix mistakes or changes of ideas in placing an element. Imagine that I added two Push Button (QPushButton) like that:

image

Then, I decide that I want them next to each other instead of one above the another, and aligned right. I can simply replace it using drag-and-drop:

image

By moving the buttons, I removed the last element of a row, which meens that there is now an empty row in the layout, with no element in it. You can see it because there is a bolder red line than usual where the empty row lies:

image

This will also happen if you remove the last element of a column, not just a row. To remove this, select the QGridLayout in the top-right objects browser. (You can also select it in the editor, but some elements like layout are hard to select in the editor because they don't have a big surface to click on. Then, use right-click on the QGridLayout in the objects browser, and in the "Page layout" section ("Mise en page" in the image), select "Simplify the grid layout" ("Simplifier le layout de grille" in the image).

image

You can see that the red line is no longer bolder:

image

Set the text of the two buttons. Add a Horizontal Spacer to push them to the right. Set their objectName property to random_button and clear_button.

image

Now, we want to test the UI. We can preview it by hitting Ctrl-R, or by going to Form > Preview... (Formulaire > Prévisualisation... in the image):

image

You can try changing the values in the spin box, and clicking the buttons. Clicking them won't do anything yet, but they will make a clicked animation (become bolder as you click them).

image

Now, try to shrink the window by dragging one of its corners:

image

We see that our elements don't get resized with the window. This is because our main Grid Layout is only placed on the window, and not bound to it.

To fix this, we need to turn the main window itself into a Grid Layout. Close the preview window. First, select the main window in the top-right objects browser, or by clicking on it in the editor (in that case, use the top-right objects browser to validate that you selected the right thing). Then, press the small grid logo in the top menu bar.

image

Now, the grid adjusted itself to the window.

image

Use the preview option again, and shrink the window. It works now!

image

The window can't be reduced in size to anything smaller than the minimum size to fix every element on it. This is based on the minimum size of each element in the window. the minimum size of an element can be changed in its properties.

Now, we want to add a "Random 2" button with most properties that are the same to the "Random" button. Inserting a new button and settings its properties one by one would work, but if we changed a lot of properties, it can be tedious, and we might need to do many back-and-forths between the "Random 2" and the "Random" buttons, to go look at the values and come back to set them in the new button. Instead, we can just copy the "Random" button. Try it. Select the "Random" button, and it Ctrl-C, then Ctrl-V. You will get an error message saying that there is no place to put the new element. This is because your window is in grid mode. You need to put it back to free mode in a similar way that we used to put it in grid mode. Select the main window, and choose the icon with an "interdiction" logo:

image

image

Now, we can copy-paste without worries. Set its objectName property to random_2_button. When we are done, we put it back in grid mode:

image

This is one of the main reasons to use a main grid layout as a container for everything else, instead of relying on the "natural" grid layout of the main window. If we used the "grid layout" of the main window only, we would loose the relative positionning of all of our elements every time we'd want to copy-paste. This way, we don't.

It is often nicer to show the application name in the top bar, instead of "Form". To change this, we need to set the windowTitle property of the MainWindow (if you are making a Widget instead, to use in rqt for instance, you need to set the windowTitle on the top-level QWidget). Set the windowTitle property of the main window to My Demo App. Set the objectName property of the main window to MyDemoApp. Note that they are different: they don't need to be the same, but objectName can never include any space.

image

We now have a UI, congratulations! We will see how to use it from Python here.

Overview of the available elements in Qt Designer

image

Layouts and spacers

  • Grid layouts are the most versatile, but you can also use vertical layouts (one column), horizontal layouts (one row), or form layouts (two columns).
  • We saw spacers in the example above, use them to put blank space between elements or to force their positionning.

Buttons

  • Push Button (QPushButton) -> Standard button
  • Tool Button (QToolButton) -> Small button used for a kind of "more options" button. By default, shows "...".
  • Radio Button (QRadioButton) -> May can be grouped together: selecting one will deselect the others.
  • Check Box (QCheckBox) -> Classic checkbox. Can be configured to have three states, the third one showing a dash "-".
  • Command Link Button -> No idea what this is, use the documentation.
  • Dialog Button Box -> A pair of Cancel and OK buttons, mostly useful when creating a dialog window (like a confirmation popup, for instance). Buttons are preconfigured so that clicking "OK" will return a success from the dialog, while clicking "Cancel" will return a failure. We can then use this value to decide what we do. A standard QPushButton can be added and configured on a QDialog too. See the documentation for more information.

Item views

  • Used to display data using the Model View Controller idiom. Google or use the documentation for more information.

Item Widgets

  • Similar to the view, but much simpler to use for simple cases, albeit more limited in functionnalities. They don't need a model: they store the data themselves.

image

Containers

  • Group Box (QGroupBox) -> Can be used to create a group of related elements with a title. Placing many QRadioButton in the same Group Box will link them together.
  • Scroll Area (QScrollArea) -> Allow to create a scrollable area. Some other widgets are already scrollable.
  • Tool Box, Tab Widget, Stacked Widget -> Allow to have multiple pages, organized in different ways.
  • Tool Box (QToolBox) -> Pages are stacked, can be manually changed.
  • Tab Widget (QTabWidget) -> Pages are in tabs, can be manually changed.
  • Stacked Widget (QStackedWidget) -> Paged are on top of each other, cannot be manually changed.
  • Frame (QFrame) -> Draw a box around its content.
  • Widget (QWidget) -> Mostly useful using the promotion system and custom widgets (see Custom widgets and promotion).
  • MDI Area -> No idea, use the doc
  • Dock Widget (QDockWidget) -> Allows the user to snap elements to the center, bottom, top, left or right of the window, and also allows them to stack them in the same space.
  • QAxWidget -> No idea

Input Widgets

  • Combo Box (QComboBox) -> Shows a list of choice, with a drop-down. You can add to the list of choices in code, not sure if you can directly in Qt Designer.
  • Font Combo Box (QFontComboBox) -> Never used. Might automatically offer the user a choice of fonts available on the system?
  • Line Edit (QLineEdit) -> Editable text that fits on one line. (A readOnly property is available to use it for display only, which is also available for many other input elements).
  • Text Edit (QTextEdit) -> Editable text on multiple lines. Can be scrolled if its content doesn't fit in the display area. Can display HTML and other stuff like that, see the doc.
  • Plain Text Edit (QPlainTextEdit) -> Like QTextEdit, but doens't display HTML or anything.
  • Spin Box (QSpinBox) -> Can enter or display integers. There are arrow to raise or lower tha value by a configurable interval (can be different between up and down directions). These arrows can be hidden (both or only one). Max and min values can be configured. Scrolling when we hover on this element does the same thing a the arrows. Can also be edited directly by typing the new value on the keyboard.
  • Double Spin Box (QDoubleSpinBox) -> Same as the QSpinBox, but used for floating point numbers. The number of shown decimal places can be configured.
  • Time Edit, Date Edit, Date/Time Edit -> Never used, self explanatory.
  • Horizontal/Vertical Scroll Bar -> Self explanatory.
  • Horizontal/Vertical Slider, Dial -> Works similarly to a spin box (scroll aware, configurable step, stores a value as an integer). The value cannot be read directly or edited directly.
  • Key Sequence Edit -> Never used, allows to configure a keyboard shortcut.

image

Display

  • Label -> Display text, read-only. Text can be changed in code if needed.
  • Text Browser -> Similar to QTextEdit, read-only.
  • Calendar Widget, LCD Number -> Never used, self explanatory.
  • Progress Bar -> Very nice. The display can be configured (X/TOTAL, %, etc.). Also has a mode "progressing" in which the bars shows that work is being done, even though we can't really count the progress being made to display real progress.
  • Horizontal/Vertical Line -> For decoration. Self explanatory.
  • Graphics View, OpenGL Widget, QtQuick Widget, QWebEngineView -> Complex to use, used for advanced display purposes.

Custom Widgets

  • It is possible to create Qt Designer plugins to show custom widgets here. Never used. Usually, it is easier to use the promotion system to use custom Widgets in Qt Designer.

Custom widgets and promotion

Imagine that I want to create a special QPushButton with special functionnalities. In my code, I will subclass the QPushButton class (by inheriting from it). But how can I use my custom button MySpecialPushButton in Qt Designer?

The solution is to use the promotion system. This allows you to place a standard widget in Qt Designer, but tell it to replace it with your custom class in the real code. In our case, we can place a standard QPushButton in Qt Designer, but when we use our UI in code later on, it will be autmatically transformed (promoted) into a MySpecialPushButton.

Place a standard PushButton. In our case, we will reuse the example from above, and promote the "Random" button

Using our UI from code

There are two ways to use the UI generated with Qt Designer in code. One of them only works in Python, while the second one works in both C++ and Python. This tutorial will only show how to do it in Python. For C++, use the Qt documentation.

Compile the .ui file using uic (Recommended)

This is the recommended way to use your UI, as it has many advantages over the other one. The main advantage is that you will get autocompletion from your IDE for the components of you UI. It is more complicated to setup, though, so the second method might be useful for quick prototyping. This will show you how to set it up using VSCode.

Automatic compilation in VSCode (Recommended)

  1. Install the Qt for Python extension by Shuang Wu.
  2. In the Qt for Python settings, look for the option Qt for Python > Uic: Options. It is recommended to change the setting in the "Workspace" or "Folder" tab of the settings.
  3. Change the second entry to point to the path where you want the compiled file to be placed. Make sure to add this path to your .gitignore. Now, every time you modify and save your UI file, it will be automatically recompiled.

The manual way

You can compile your UI manually using the uic tool from Qt. In Python, invoke it as pyside6-uic <path to your UI file> > ui_<name of your UI file>.py. This will create a file called ui_<name of your UI file>.py in the current directory. Make sure to replace <path to your UI file> with the actual path, and <name of your UI file> with the actual name of your file, without the .ui extension. You can choose the output path and the output file name, but this tutorial assumes that is is called ui_<name of your UI file>.py for consistency with the use of the Qt for Python extension default naming scheme.

Dynamic loading (Not recommended)

In Python, Qt can dynamically load you UI file at runtime. This has the advantage of not needing to recompile your UI every time you change it, but you lose the autocompletion for the elements in your UI file. This is a bad tradeoff, especially if you setup the Qt for Python extension, in which case you won't need to recompile your UI file anyway, as it will be done automatically for you by the extension. But this can be useful for quick prototyping. In order to do this, you don't need to compile your UI file. In the next section, just follow the instructions for dynamic loading.

Creating a Python code for our example UI

Create a folder, and place your UI file there. Name it demo_app.ui. #TODO provide ui file

Open this folder in VSCode. Make sure your are setup for Python development. Create a file called main.py.

image

We will now compile the UI file. Use either the manual instructions, or if you have the Qt for Python extension, right-click que UI file in the file explorer, and select Compile Qt UI File. This should create a file called ui_demo_file.py in the same directory.

In main.py, import QApplication and QMainWindow from PySide6.QtWidgets.

from PySide6.QtWidgets import QApplication, QMainWindow

Create a class MainWindow that inherits from QMainWindow. In the __init__ method, call the super __init__. This will call the parent (QMainWindow) __init__.

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

Create a main function. In it, create a QApplication, and a MainWindow. Call show() on the MainWindow, then exec() on the QApplication. Call your main function.

def main():
    app = QApplication()
    window = MainWindow()
    window.show()
    app.exec()


if __name__ == "__main__":
    main()

Run your script. This will show an empty window:

image

Now, we will load our compiled UI. At the top of the file, import Ui_MyDemoApp from ui_demo_app:

from ui_demo_app import Ui_MyDemoApp

In MainWindow.__init__, add the following lines:

self.ui = Ui_MyDemoApp()
# The following line can replace the preceding line to load the UI dynamically
# self.ui = self.loadUi("demo_app.ui")
self.ui.setupUi(self)

Run your application. This is great, but nothing works yet.

TODO connect elements and everything.

Not (yet?) covered topics

  • Signals and slot in Qt Designer. Qt Designer can be used to connect slots and signals directly, using the Slots and Signals editor tab in the bottom-right.
  • QMenuBar and QAction. QMenuBar and actions can be created and configured in Qt Designer (like a "File" menu with "Open and "Close" actions, etc.)
  • Resources explorer. Used to set custom icons and images where applicable (on buttons or actions, for instance).
  • Tab order. It is possible to configure the order of fields and buttons when the Tab key is pressed to make the GUI keyboard-friendly, directly in Qt Designer.
  • Keyboard shortcuts. It is possible to configure keyboard shortcuts for buttons, actions and other interactive elements to make the GUI keyboard-friendly, directly in Qt Designer.
  • QSplitter. It is possible to add a Splitter between two widgets, which allows the user to drag the separation between them to select the relative space that each one takes (both vertical and horizontal are available). Google for QSplitter in Qt Designer.
  • QValidator. It is possible to add a validator to many input elements to restrict the values that can be entered in those fields.
  • Using nested UI files. It is possible to place a simple QWidget in your UI file, and setup another specific UI file on top of it in code, using the same method as used in this tutorial, but on this specific widget. This can be used for QDialog and other popups, or to better separate different parts of a UI. Google for it.