Programming

Flicker-Free Custom Painting in Qt 3

I wrote a small Qt 3 widget that draws a graph, and it flickered whenever another window crossed it. The first version painted the background and graph directly onto the widget. The user could occasionally see the exciting interval between those operations.

The obvious cure is to draw a complete frame off-screen and copy it in one operation.

void Graph::paintEvent(QPaintEvent *event)
{
    QPixmap frame(size());
    frame.fill(colorGroup().base());

    QPainter p(&frame);
    drawGrid(&p);
    drawValues(&p);
    p.end();

    bitBlt(this, event->rect().topLeft(), &frame, event->rect());
}

The pixmap receives the background and every graph element before the screen changes, so the widget never displays a half-painted frame. For a frequently updated widget, I keep a cached pixmap and rebuild it only when the size, palette, or data changes.

Calling update() schedules a paint event and allows Qt to combine several dirty regions. Calling repaint() paints immediately. I use update() unless the application genuinely requires synchronous drawing; repeated immediate paints are an efficient way to make smooth animation less smooth.

Another option is the WNoAutoErase widget flag, which prevents Qt from erasing the background before paintEvent(). It can remove one source of flicker, but then the paint code must cover every exposed pixel. Missing one produces attractive garbage from whichever window was there before.

Double buffering costs memory proportional to the widget area, and redrawing an elaborate pixmap for every tiny exposure wastes time. Respect the event’s rectangle and cache expensive static parts.

The result is not clever: compose, then copy. That is precisely why I like it. Rendering bugs are much easier to diagnose when the screen sees only finished frames.

Threads in Userspace and the Kernel

A program I was debugging had six POSIX threads, ps showed one process, and /proc showed several task directories. This produced the inevitable question: are Linux threads implemented in userspace or in the kernel? The unhelpful but accurate answer is both.

The pthread library provides the interface the application sees: pthread_create, mutexes, condition variables, thread-local state, and POSIX rules. With NPTL, creating a thread eventually uses the kernel’s clone() facility to create another schedulable task that shares selected resources with its siblings.

The kernel does not need a completely different object named “thread.” It schedules tasks. Flags passed to clone() determine whether tasks share an address space, file descriptor table, signal handlers, and other state. Tasks in the same thread group form what userspace presents as one process.

This is visible under procfs:

ls /proc/$(pidof myprogram)/task

Each entry is a task ID. The main thread’s task ID is also the process ID normally shown by tools. Updated ps can display either the process-oriented view or individual threads, depending on its options. Neither view is lying; they answer different questions.

Synchronization also crosses the boundary selectively. An uncontended NPTL mutex is normally handled with atomic operations in userspace. Entering the kernel for every lock and unlock would be expensive. When a thread cannot acquire the lock, a futex operation lets it sleep in the kernel; an unlock can wake a waiter when necessary.

This “fast userspace, kernel on contention” pattern explains why futex means fast userspace mutex. It does not mean the kernel knows nothing about waiting threads. The kernel supplies the queueing and scheduling that userspace cannot safely improvise.

Signal handling is where abstractions become less tidy. Some signals target the process or thread group, while others target a particular task. The library and kernel cooperate to provide POSIX behaviour. Code should use pthread signal operations instead of guessing that numeric task IDs are interchangeable with old Linux process IDs.

The same warning applies to debugging. If one thread blocks, inspect the task view; if the question concerns resource ownership, remember that file descriptors and memory may be shared. Killing a random task because it looks like a child process is not thread debugging. It is experimental program termination.

I find the model easier once I stop asking whether a thread is “really” a process. In Linux it is a schedulable task with a particular set of shared resources, grouped so userspace can provide process and pthread semantics. That is a mechanism, not a philosophical position, and it matches what the tools are showing.

Qt Layouts Before Pixel Pushing

I fixed a dialog today that looked fine in English and ridiculous in German. The original code positioned every Qt widget with coordinates, so a longer label pushed straight through the line edit beside it. Apparently translations had failed to respect our geometry. Very inconsiderate of them.

The obvious fix was not to adjust the numbers. It was to remove them.

In Qt 3, layouts already know how to negotiate widget sizes. A QVBoxLayout stacks rows, a QHBoxLayout arranges each row, and QGridLayout handles label-and-field forms without pretending every label has the same width.

QGridLayout *layout = new QGridLayout(this, 2, 2, 8, 6);
layout->addWidget(new QLabel(tr("Server name:"), this), 0, 0);
layout->addWidget(serverEdit, 0, 1);
layout->addWidget(new QLabel(tr("User:"), this), 1, 0);
layout->addWidget(userEdit, 1, 1);
layout->setColStretch(1, 1);

The margins and spacing remain explicit, but the widgets provide size hints and the second column absorbs extra room. The dialog now survives longer translations, a different font, and a user resizing it.

One small trap is mixing layouts with manual setGeometry() calls on the same children. The layout will win, usually just after the hand-tuned version looked correct on the developer’s machine.

I still use fixed sizes when the thing really is fixed, such as a small colour swatch. For forms and text, coordinates are merely tomorrow’s bug written as two integers. Let the layout do the dull arithmetic. It has more patience than I do.