Making a simple notepad UI in QT Designer

QT is a popular cross-language and cross-platform library for creating Graphical User Interfaces for personal computers and alike. It has theming and styling support, support for keyboard interaction and all common hotkeys, and most importantly, wide adoption and a large amount of tools to make working with it easier.

Today I will tell you about QT Designer, a graphical tool for creating QT-based interfaces.

Video version Link to heading

Preparation Link to heading

Installing QT Designer Link to heading

Windows Link to heading

Download and install QT executable found in official webside or install third party package like pyqt5 or pyqt6 using pip from command line that also includes it.

Then, you need to check installation path, find designer.exe file here and launch it.

Linux Link to heading

Install qt development tools package, it has different name based on distribution you are using

sudo apt install qttools5-dev-tools # QT5, Ubuntu/Mint
sudo apt install qttools6-dev-tools # QT6, Ubuntu/Mint

sudo dnf install qt5-qttools-libs-designer # QT5, Fedora
sudo dnf install qt5-qttools-libs-designer # QT6, Fedora

sudo pacman -S qt5-tools #QT5, Arch
sudo pacman -S qt6-tools #QT6, Arch

nix-shell -p libsForQt5.qt5.qttools # QT5, Nix
nix-shell -p qt6.qttools # QT6, Nix

After that, run designer command which will open Qt Designer, or make a desktop file as shortcut to launch it graphically.

Learning interface Link to heading

Here is the main window of QT designer. For simplicity, I highlighted the important regions on the screenshot.

Screenshot of window interface with parts highlighted - 1 is empty fake window, 2 is list of objects at that window, 3 is table with properties, 4 is combined part of interface that has signal/slot editor chosen on picture, 4 is a list of all possible widgets, 6 is icon buttons, 7 is menu bar.

  1. Main Workspace
  2. Object inspector
  3. Property Editor
  4. Signal Editor (combined with two other)
  5. Widget Box
  6. Buttons Bar
  7. Menu Bar

Creating a basic text editor interface Link to heading

So, we will create a simple interface for notepad, or text editor app. We will need three types of widgets. Various buttons are PushButton widget, Actual space to edit in is implemented using TextEdit wigdet, while Search bar is LineEdit instead.

Drag and drop Link to heading

One of things that are gotten completely wrong in a lot of simmilar tutorials, is how they ask you to manually drag and drop widgets from widget box (5) into your interface (1) and position them how you want. If we try doing so, it works, but…

Smaller window cuts off part of interface. And bigger window just adds a lot of empty space.

As you can see, it doesn’t scale well and may waste a lot of useful space for no reason at all.

Using layouts Link to heading

So, instead of that we use layouts, that are like containers that automatically arrange and resize widgets based on simple rules.

The most basic ones are horizontal and vertical layouts:

Vertical layout (QVBoxLayout) arranges widgets one after another vertically, while horizontal (QHBoxLayout) - horizontally.

And the one we will use is grid layout:

Grid Layout (QGridLayout) allows to add widgets both horizontally and vertically.

So we place our widgets on grid how we want…

Grid Layout with our widgets, but text edit takes up only one column.

And see that it doesn’t look quite like text editor yet. To fix this, we hold left mouse button on sides of text edit widget, and resize it several times to make it take all width of our layout.

Grid Layout with our widgets, and with text edit taking up correct amount of space.

And also you can add a horizontal spacer widget to separate buttons and search bar.

Added horizontal spacer makes it easier to tell it’s a different parts of interface.

Then we press right button outside of layout, and choose Lay out in context menu, selecting any of layouts mentioned above to transform the window itself into a layout.

Context menu showing a lot of options, Lay out is chosen, and inside of it 'Lay Out in a grid' option chosen, that you can also call by pressing Ctrl + 5. Grid layout now takes up all of window!

Customizing widget properties Link to heading

Changing text on a button is pretty easy, double click on it and input your text.

But we need a lot more than that, so we will select a widgets either directly or using Object inspector (2), then go to object properties (3) to edit ones we need.

Name ID in editor Description
Object Name objectName Identifier of object used in code, so ensure it’s easy to understand
Text text Text on buttons and other objects. For text edit and line edit it will place preexisting “real” text, not the placeholder text you usually need.
Placeholder Text placeholderText Grayed out hint inside of line edit and text edit widgets
Tooltip toolTip Text that shows up if you hover cursor over button, write a bigger description here

Result will looks like this:

Notepad window, buttons are named New file, Open and Save, Search bar has placeholder text 'Search...' with tooltip for new file button saying 'Create new file, erasing current text if it's not saved'.

You should also add accessible name and description to widgets you added. This is a bare minimum of what you can do to ensure your software is accessible to more users.

Acessible Name acessibleName Name of object that screen reader software reads to vision impaired users
Acessible Description acessibleDescription Something like a tooltip for screen reader software users

Tab order Link to heading

Correct tab order, on the other hand, allows to use Tab button to focus next (or previous, with Shift+Tab) interactive widget using only keyboard, which is also useful to screen reader users as well as other people who need to navigate your app from keyboard.

Edit Tab order button highlighted.

Select Edit Tab Order mode in button bar (6), and check if buttons are numbered in left to right, up to bottom order, if they are not, press on all of them in that order.

Window with widgets numbered from 1 to 5.

You can also use tab order list for the same purpose.

Widget right clicked and “Tab order list” context menu item selected.

Tab order list window with ordered list of widget

Setting up actions Link to heading

Next we will add actions, which are acting as both entries in menu bar and as actions you can add shortcuts to.

  1. Double click on Type here in empty menu bar of your window, and type category name.
  2. Press again and choose Type here inside dropdown menu, this time typing an action name. You can also add separators here.
  3. Select new action and edit shortcut entry in property editor, adding shortcuts

Process of following above steps.

For our simple editor I recommend this list of shortcuts and actions

Category > Name Shortcut
File > New Ctrl+N
File > Open… Ctrl+O
File > Save Ctrl+S
File > Save as… Ctrl+Shift+S
File > Quit Ctrl+Q
Edit > Search Ctrl+F

Signals Link to heading

At last, we should add signals. Signals allow us to connect different windows and/or actions to make that change of state of one affects the other. In our case, we want our buttons to be connected to respective actions, and our Search action to focus our Search Bar.

We could use a button from button bar (2) to enter signal editing mode, but it does not allow connecting actions, so we will use Signal Editor (4) to add new signal and select:

  1. Button by objectName as Sender
  2. clicked() as Signal
  3. Action by objectName as Receiver
  4. trigger() as Slot

Table of Signal editor with newFileButton and actionNew_file as example.

For Search action we need to use triggered() and setFocus() instead

Same example but with searchBar and action_search added.

Do that for all buttons, and we are basically done with QT designer, with only thing left to do is to save the project as ui file and put it in project folder.

Connecting interface to PyQT5 code Link to heading

After finishing all of that, we will still need to put this interface into our app. We will use PyQT5, a Python language library for QT as an example.

Load interface from code Link to heading

So to load an interface we have a two different ways. One is to convert a ui file which we can save from QT Designer into python code, using uic command line tool or generating it from QT Designer itself.

pyuic5 interface_filename.ui > interface_filename.py
from PyQt5.QtWidgets import QMainWindow, QApplication
from interface_filename import Ui_MainWindow

class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__() #initialize main 
        
        # Load and apply interface to main window
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()

The other one is to use uic as library and load a UI file directly, converting at runtime. Second methods adds slight overhead to your application and requires shipping ui file with it, but allows you to quickly test changes to .ui file just by rerunning the code.

from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5 import uic

class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__() #initialize main 
        
        # Load .ui file from path and apply to main window
        uic.loadUi("interface_filename.ui",self)

app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()

There are two differences between two that affect our project:

  • In first method, objects are called as self.ui.objectName since we imported them as object called ui, while in second method we use just self.objectName
  • If you use second method, it weirdly ignores central widgets’ margin settings, which makes it have no space between buttons and edges of window, so we need our manually added grid layout to have margin instead, using Property Editor (3) (You can also set margin properties to 0 in central widget so there is no discrepancy between how your interface is imported)

Table of properties with layoutLeftMargin and simmilar properites for other sides set to 10.

Connect to functions Link to heading

After we do that, we need to add some functions that make our interface actually function. Here I will not go into details about how these functions work, since this is mostly a qt designer video, so our focus is to connect them to our widgets afterwards.

QT designer did some work for us, allowing us to simply call widgets from code just as their names (in case of Python, self.objectName for uic library method, or self.ui.objectName if we used first method), so we add code that connects action triggered signals to functions in __init__ of our MainWindow object.

self.ui.actionNew_file.triggered.connect(self.create_new_file)
# or
self.actionNew_file.triggered.connect(self.create_new_file)

For buttons we would use clicked instead of triggered, but we already connected them with signals so it’s not needed

The exception here is search field, we use returnPressed to trigger searching function.

self.ui.searchBar.returnPressed.connect(self.search_text)
#or
self.searchBar.returnPressed.connect(self.search_text)
Info
QT designer spared us the need to manually assign shortcuts for actions and write twice as many code for connecting the shortcuts.

Conclusion Link to heading

You can find full project files here - https://codeberg.org/system-maid/cute-writer

I may also make a follow-up tutorial for more complex interfaces, or PyQT5 code, but for now, goodbye, I hope to see you again later.