Qt Development Notes

Published 2024-08-06

Updated 2025-09-22

A collection of Qt development notes about touch keyboard integration, multithreaded data access, QWidget update crashes, qmake organization, localization UI, modal responsiveness, QPixmap handling, and translation workflow.

Written by - kok-s0s

Qt

Qt

C++

This article collects several Qt development notes from real project work and GPT-assisted troubleshooting.

Waking the Windows Touch Keyboard from Qt

The goal is to show the Windows touch keyboard when a Qt input widget gains focus.

Implementation Idea

On Windows, the touch keyboard can be invoked by starting the system keyboard process or interacting with the related Windows shell behavior.

In a Qt application, the practical approach is:

  1. detect focus on input controls such as QLineEdit or QTextEdit;
  2. install an event filter for those controls;
  3. when focus enters, call the platform-specific keyboard launcher;
  4. keep this logic wrapped so non-Windows platforms are not affected.

Code-Level Shape

The logic should be hidden behind a small helper class instead of being scattered across widgets.

Typical responsibilities:

Usage

The UI layer should only need a simple call, such as registering a widget or a group of widgets with the helper.

This keeps business widgets independent from Windows-specific details.

Summary

The key point is not how to launch the keyboard once. The key point is isolating platform-specific behavior behind a small boundary so the rest of the Qt code remains portable.

Crashes Caused by Multithreaded Data Access

When multiple threads operate on the same data, crashes often come from concurrent reads and writes without synchronization.

Why It Crashes

Common causes:

Correct Handling

First, check whether data is accessed across threads.

If it is, protect shared data consistently. Use std::mutex, QMutex, QReadWriteLock, or another synchronization primitive that matches the access pattern.

Keep the locked region as small as possible:

{
  std::lock_guard<std::mutex> lock(mutex);
  localCopy = sharedData;
}

// Work on localCopy outside the lock.

If the pattern appears repeatedly, wrap the shared state in a reusable structure so callers cannot forget locking.

Standard Process

When a similar crash appears:

  1. confirm whether cross-thread access exists;
  2. confirm which thread owns the object;
  3. check whether signals are direct or queued;
  4. protect shared data or move mutation to the owning thread;
  5. add logs around object lifetime and thread IDs.

The short version: shared mutable data must have a clear owner or a clear lock.

QWidget::update() Crash

A QWidget::update() crash may appear only on certain platforms, especially when UI behavior differs between Windows and Linux.

Platform Differences

On Windows, some widgets tolerate certain update orders or repaint timing more leniently.

On Linux, especially with X11 or Wayland, repainting may depend more directly on the underlying paint stack. Hidden lifetime bugs, invalid widget states, or model/view inconsistencies can surface as crashes around update().

Memory Layout Differences

Undefined behavior may not crash on every platform. A dangling pointer or invalid object access may survive on Windows but fail on Linux because memory layout, allocator behavior, and paint timing differ.

Fix Strategy

For cross-platform safety:

If a crash happens after a signal is triggered, inspect both the sender lifetime and receiver lifetime.

Making qmake as Clear as CMake

qmake can be organized cleanly if the project is split into reusable .pri files.

Use .pri Files for Modules

This is similar to using add_subdirectory() or module-level CMake files.

# main.pro
include(src/core/core.pri)
include(src/widgets/widgets.pri)
include(src/network/network.pri)

Each .pri file owns the sources, headers, resources, and include paths for one module.

Use Variables for Common Configuration

Common include paths, defines, and compiler flags can be stored in variables to avoid duplication.

Set Compiler Options Clearly

Separate MSVC and GCC/Clang options so platform differences stay explicit.

Manage Resources Uniformly

Keep resources close to the modules that use them, or collect them in a clear top-level resource file.

Generate .pro Files When Needed

For large projects, generation scripts can reduce manual maintenance, similar to generating CMakeLists.txt fragments.

Consider qmake + CMake Hybrid Migration

If the project is old and cannot migrate immediately, keep qmake working while preparing a CMake structure in parallel.

Keeping a Good UI Experience in Multilingual Qt Apps

Multilingual UI often breaks layouts because translated text can be much longer than the original.

Use QLayout

Avoid fixed positions. Let layouts calculate widget positions and sizes automatically.

Let QLabel Wrap

For labels that may become long after translation:

label->setWordWrap(true);

Adapt Fonts for Different Languages

Different languages may need different font families or sizes. Avoid assuming one font works well for every locale.

Ellipsize Long Text

For text that must stay in one line, use font metrics to elide it:

QString text = fontMetrics.elidedText(originalText, Qt::ElideRight, width);

Reserve Space

Use setMinimumWidth() or layout stretch factors where translated strings need extra room.

Allow Resizing

Dialogs and panels should be resizable when content length varies significantly between languages.

Let Buttons Adapt to Text

Buttons should have enough horizontal padding and should not rely on fixed widths that only fit one language.

Adapt QSS

QSS rules may need language-aware font or spacing adjustments.

The summary: localization is not only translation. It is also layout engineering.

Keeping the UI Responsive with Popups

When a popup is shown during long-running work, the UI can easily become unresponsive.

Method 1: QProgressDialog

Use QProgressDialog and set it modal when the user must wait for the current operation.

Method 2: Disable the Main Window

setEnabled(false) can prevent users from triggering conflicting operations while a task is running. Remember to restore it in all exit paths.

Method 3: Local QEventLoop

A local event loop can keep the UI processing events while waiting, but it must be used carefully because it can introduce reentrancy problems.

Method 4: QThread

Move long-running work to a background thread and use signals to update progress on the UI thread.

The preferred approach for real long-running work is usually a worker thread plus queued UI updates.

Keeping the Interface Responsive

The purpose of responsiveness handling is to prevent the main thread from being blocked by long-running operations.

Common problems:

Better alternatives:

QPixmap Image Processing

QPixmap is closely tied to the GUI system and should generally be used on the GUI thread.

To avoid problems:

Translation Workflow

Basic Flow

Qt’s translation flow usually includes:

  1. mark strings with tr();
  2. extract source text into .ts files;
  3. translate strings with Qt Linguist or another translation tool;
  4. compile .ts files into .qm files;
  5. load the correct .qm file at runtime through QTranslator.

Efficient Updates and Iteration

Avoid manually maintaining .ts files when possible. Use tools such as lupdate and lrelease in scripts or CI.

Automate the translation flow:

lupdate project.pro
lrelease project.pro

For dynamic language switching, load the target translator, install it on the application, and refresh visible UI text.

Translation platforms can help when multiple people participate in translation.

Track translation entries in version control so changes are reviewable.

If generated .ts files should not include location metadata, configure the extraction command accordingly.

Pitfalls to Avoid

Summary

Good Qt localization needs both tooling and UI design. Translation files solve the text problem, but layouts, fonts, and runtime refresh behavior determine whether the final experience feels reliable.