12. Building Complex UIs with QMainWindow

QMainWindow is a QWidget subclass with built-in support for toolbars, dock widgets, menu bars, and status bars. It is useful as a starting point for building applications that have a central area and those elements.

[TODO: MAIN WINDOW LAYOUT IMAGE]

It handles layout and docking automatically, saving you from manual QWidget management.

12.1 Setting Up the Central Widget

A central widget ca be any of the standard widgets, typically a QTextEdit, a QTableView, or a QGraphicsView. It is set with setCentralWidget().

An icon of a clipboard-list1

You are building a simple note-taking application. It needs to support rich text editing (bold, italic, different font sizes, etc.), and you anticipate more features in the future, such as menus, toolbars, and side panels. You decide to use QMainWindow as the main window and place a QTextEdit as its central widget.

To use QMainWindow in your application:

  1. Create a class that inherits from QMainWindow and name it Editor.

  2. Inside the Editor class’s __init__() method, instantiate a QTextEdit widget. This will serve as the primary area for viewing and editing notes.

  3. Call self.setCentralWidget() on the QMainWindow instance, passing the QTextEdit object. This places the text editor in the window’s central area, automatically handling resizing and layout.

When you run the application, you see a window filled with the editable text area. You can type, select, copy, and paste text. The context menu and both scrollbars are present but text formatting (bold, italic, font size) is not available.

12.2 Adding a Status Bar

The QStatusBar class is a horizontal bar at the bottom of a QMainWindow, used for displaying status information. It supports three types of messages:

  • Temporary - briefly shown over normal messages but not permanent ones. Used for notifications, tool tip explanations, or menu help text.

  • Normal - displayed persistently on the left side. Used for dynamic information like cursor position, document length, or similar.

  • Permanent - always visible on the right side. Used for indicators such as Caps Lock status or character encoding.

An icon of a clipboard-list1

Your note-taking application users have requested live feedback at the bottom of the window: the current cursor line and column, the total character count, and (when text is selected) a note showing how many characters are selected.

To add a status bar to the main window:

  1. Create QLabel widgets for the information you want to display persistently. In our example, we use one label for cursor position and another for total character count.

  2. Access the status bar with self.statusBar() and add the labels using addWidget() for normal messages and addPermanentWidget() for permanent ones.

  3. Connect QTextEdit signals to update the information:

    • textChanged() to refresh cursor position and character count.
    • selectionChanged() to show the selected character count temporarily.

In the example, the permanent label always shows the total character count, while the normal label shows the cursor position. When text is selected, we briefly display a temporary message showing the selection size (e.g., “42 characters selected”). The temporary message hides the normal label for a couple of seconds but leaves the permanent character count visible.

12.3 Creating Menus and Actions

Menus are constructed using QMenuBar as the top-level container, with QMenu objects for each dropdown group, and QAction objects for the individual commands or items. QActions can be reused across menus, toolbars, and keyboard shortcuts.

[TODO: MENUBAR DIAGRAM]

An icon of a clipboard-list1

Your application users expect standard application menus: a File menu with an Exit command and a Help menu with an About dialog that shows application information and version.

To add drop-down menus to your main window:

  1. Access the main window’s menu bar with QMainWindow.menuBar(). Add individual menus using QMenuBar.addMenu() with a title (e.g., ‘&File’). Ampersands enable keyboard shortcuts.

  2. For each menu item, create a QAction object. Set its text, a shortcut with setShortcut(), and connect its triggered() signal to a slot (e.g., for quitting or showing a dialog). Set the main window as the action parent to ensure it stays in scope for the application lifetime.

  3. Add the action to the menu using QMenu.addAction().

Menu-level keyboard accelerators are enabled by prefixing letters with an ampersand (&) in the menu title (e.g., “&File” allows Alt+F to open it). For action-level shortcuts (e.g., Ctrl+Q for Exit), use setShortcut().

When you run the application, the menu bar appears at the top. Selecting “Exit” closes the app, and “About” displays a simple dialog.

12.4 Adding Toolbars

QToolBar provides a panel for quick-access controls. Users can drag a toolbar to different dock areas, float them as separate windows, or customize their position. You can reuse the same QAction objects for both menus and toolbars.

An icon of a clipboard-list1

You are enhancing the note-taking application with a toolbar. You decide to place the Exit and About commands on it.

To use a toolbar in your application:

  1. Create the toolbar using QMainWindow.addToolBar().

  2. Add existing QAction objects (reused from menus), custom QWidgets, or separators as needed.

  3. Optionally, add icons to the actions with setIcon(). Icons display in both menus and toolbars. Customize the toolbar’s appearance with setToolButtonStyle().

When you run the application, the toolbar appears at the top. Hovering shows tooltips (inherited from action text), and clicking performs the same functions as the menu. Users can right-click the toolbar area to toggle visibility or drag it to reposition.

12.5 Using Dock Widgets

QDockWidget enables the creation of panels that can be docked within a QMainWindow or floated as independent windows. It is used for tools that users may want to rearrange or hide to customise their workflow.

Your text editor users want quick access to common formatting tools (bold, italic, and font size selection) without cluttering the main menu or toolbar. You provide these controls in a dockable widget that can be positioned on the left or right and floated if needed.

To use a dock widget in your application:

  1. Instantiate a QDockWidget and configure its properties, such as title or allowed docking areas.

  2. Create child widgets and arrange them in a layout within a container QWidget, then set this container as the dock’s content using setWidget().

  3. Connect signals from the child widgets to slots that apply formatting via QTextCharFormat and mergeCurrentCharFormat(). To reflect the current text format in the dock (e.g., when moving the cursor), connect QTextEdit.cursorPositionChanged() to update the widgets’ states.

12.6 Completing the Editor