Qt Embedded Programming Techniques

1. Overview of Qt Embedded Development

Qt/Embedded Linux (now known as Qt for Device Creation) is a version of Qt optimized for embedded systems, featuring the following characteristics:

  • No need for the X Window System, using its own window system QWS (Qt Window System)
  • Customizable compilation, removing unnecessary modules
  • Supports multiple processor architectures (ARM, MIPS, PowerPC, etc.)
  • Provides a complete Qt API, compatible with the desktop version

1.1 Basic Environment Configuration

# Configure Qt for Embedded Linux
./configure -embedded <arch> -qt-gfx-linuxfb -no-largefile -no-opengl \
            -qt-mouse-<mouse_driver> -qt-kbd-<keyboard_driver>

# Example: ARM architecture configuration
./configure -embedded arm -xplatform qws/linux-arm-g++ -qt-gfx-linuxfb \
            -qt-mouse-tslib -qt-kbd-tty

2. Embedded Graphics System

2.1 Using Linux Framebuffer

// main.cpp
#include <QApplication>
#include <QLabel>

int main(int argc, char *argv[])
{
    // Specify to use Linux framebuffer
    QApplication app(argc, argv, QApplication::GuiServer);
    
    QLabel label("Hello Embedded Qt!", 0);
    label.setAlignment(Qt::AlignCenter);
    label.resize(240, 320);  // Typical embedded device resolution
    label.show();
    
    return app.exec();
}

2.2 Touchscreen Support (Using tslib)

// Initialize touchscreen
#include <tslib.h>

void initTouchScreen()
{
    struct tsdev *ts = ts_setup(NULL, 0);
    if (!ts) {
        qWarning("Cannot initialize touchscreen");
        return;
    }
    
    // Calibrate touchscreen (usually performed on first run)
    if (access("/etc/pointercal", F_OK) != 0) {
        QProcess::execute("ts_calibrate");
    }
    
    // Set Qt environment variable
    qputenv("QWS_MOUSE_PROTO", "Tslib:/dev/input/event0");
}

3. Resource Optimization Techniques

3.1 Using Resource Files

// Compile-time resource file
// resources.qrc
<RCC>
    <qresource prefix="/">
        <file>images/logo.png</file>
        <file>translations/app_zh.qm</file>
    </qresource>
</RCC>

// Use resources in code
QPixmap pix(":/images/logo.png");

3.2 Static Compilation

# Static compile Qt library
./configure -static -release -no-exceptions -no-stl ...

# Static link application
qmake -config static

4. Embedded Input Methods

4.1 Virtual Keyboard Implementation

// virtualkeyboard.h
#include <QWidget>
#include <QSignalMapper>

class VirtualKeyboard : public QWidget
{
    Q_OBJECT
    
public:
    VirtualKeyboard(QWidget *parent = nullptr);
    
signals:
    void keyPressed(const QString &text);
    
private:
    QSignalMapper *mapper;
    void createLayout();
};

// virtualkeyboard.cpp
VirtualKeyboard::VirtualKeyboard(QWidget *parent)
    : QWidget(parent), mapper(new QSignalMapper(this))
{
    createLayout();
    connect(mapper, SIGNAL(mappedString(const QString&)), 
            this, SIGNAL(keyPressed(const QString&)));
}

void VirtualKeyboard::createLayout()
{
    QGridLayout *layout = new QGridLayout(this);
    
    const QStringList keys = {
        "1", "2", "3", "4", "5", "6", "7", "8", "9", "0",
        "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P",
        "A", "S", "D", "F", "G", "H", "J", "K", "L", ";",
        "Z", "X", "C", "V", "B", "N", "M", ",", ".", "/"
    };
    
    for (int i = 0; i < keys.size(); ++i) {
        QPushButton *btn = new QPushButton(keys[i]);
        btn->setFixedSize(30, 30);
        connect(btn, SIGNAL(clicked()), mapper, SLOT(map()));
        mapper->setMapping(btn, keys[i]);
        layout->addWidget(btn, i / 10, i % 10);
    }
    
    setLayout(layout);
}

5. Integration of Embedded Device Features

5.1 Power Management

#include <QPowerSource>
#include <QBatteryInfo>

class PowerMonitor : public QObject
{
    Q_OBJECT
    
public:
    PowerMonitor(QObject *parent = nullptr) : QObject(parent) {
        QBatteryInfo battery;
        connect(&battery, SIGNAL(remainingCapacityChanged(int,int)),
                this, SLOT(updateBatteryStatus(int,int)));
        
        QPowerSource *ac = new QPowerSource(QPowerSource::AC, this);
        connect(ac, SIGNAL(availabilityChanged(bool)),
                this, SLOT(onACStatusChanged(bool)));
    }
    
public slots:
    void updateBatteryStatus(int remaining, int max) {
        int percent = (remaining * 100) / max;
        qDebug() << "Battery:" << percent << "%";
        
        if (percent < 10) {
            emit lowBatteryWarning();
        }
    }
    
    void onACStatusChanged(bool available) {
        qDebug() << "AC power:" << (available ? "connected" : "disconnected");
    }
    
signals:
    void lowBatteryWarning();
};

5.2 Backlight Control

// Control LCD backlight brightness
void setBacklightBrightness(int percent)
{
    QFile file("/sys/class/backlight/backlight/brightness");
    if (file.open(QIODevice::WriteOnly)) {
        int value = (percent * 255) / 100;
        file.write(QByteArray::number(value));
        file.close();
    }
}

6. Embedded Deployment

6.1 Creating Root Filesystem

# Example deployment script
#!/bin/sh

QT_DIR=/opt/qt-embedded
APP_DIR=/opt/myapp

# Create target directory structure
mkdir -p $APP_DIR/{bin,lib,plugins}

# Copy application
cp myapp $APP_DIR/bin/

# Copy Qt libraries
cp $QT_DIR/lib/libQtCore.so.4 $APP_DIR/lib/
cp $QT_DIR/lib/libQtGui.so.4 $APP_DIR/lib/

# Copy plugins
cp -r $QT_DIR/plugins/{imageformats,sqldrivers} $APP_DIR/plugins/

# Create startup script
echo "#!/bin/sh\nexport QTDIR=$QT_DIR\nexport LD_LIBRARY_PATH=$APP_DIR/lib\nexport QT_PLUGIN_PATH=$APP_DIR/plugins\n$APP_DIR/bin/myapp -qws" > $APP_DIR/start_app.sh

chmod +x $APP_DIR/start_app.sh

6.2 Cross-Compilation Configuration

# myapp.pro cross-compilation configuration example

TARGET = myapp

# Specify cross-compiler
QMAKE_CC = arm-linux-gcc
QMAKE_CXX = arm-linux-g++
QMAKE_LINK = arm-linux-g++

# Embedded platform configuration
QT += core gui
QT_CONFIG -= opengl

# Optimization options
QMAKE_CFLAGS_RELEASE += -Os
QMAKE_CXXFLAGS_RELEASE += -Os

# Reduce executable size
QMAKE_LFLAGS += -Wl,-s

SOURCES += main.cpp \
           # Other source files

HEADERS += # Header files

7. Qtopia Application Example

Qtopia is an embedded application framework built on Qt/Embedded.

7.1 Simple Qtopia Application

// qtopiaapp.cpp
#include <QtopiaApplication>
#include <QMainWindow>
#include <QSoftMenuBar>

class MyApp : public QMainWindow
{
    Q_OBJECT
    
public:
    MyApp(QWidget *parent = nullptr) : QMainWindow(parent) {
        // Set Qtopia application window properties
        setWindowTitle(tr("My Qtopia App"));
        setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
        
        // Add soft menu
        QSoftMenuBar::setLabel(this, Qt::Key_Select, tr("Select"));
        QSoftMenuBar::addMenuTo(this, tr("Options"));
        
        // Main interface content
        QLabel *label = new QLabel(tr("Welcome to Qtopia!"), this);
        label->setAlignment(Qt::AlignCenter);
        setCentralWidget(label);
    }
};

QTOPIA_ADD_APPLICATION("myapp", MyApp)
QTOPIA_MAIN

8. Performance Optimization Techniques

8.1 Reducing Memory Usage

// Load images using shared memory
QPixmap loadSharedPixmap(const QString &path)
{
    static QHash<QString, QPixmap> pixmapCache;
    
    if (!pixmapCache.contains(path)) {
        QPixmap pix;
        if (!pix.load(path)) {
            qWarning() << "Failed to load image:" << path;
            return QPixmap();
        }
        
        // Convert to the same format as the screen to reduce runtime conversion
        pix = pix.convertToFormat(QImage::Format_RGB16);
        pixmapCache.insert(path, pix);
    }
    
    return pixmapCache.value(path);
}

8.2 Optimizing Drawing Performance

// Optimized drawing for custom widgets
void CustomWidget::paintEvent(QPaintEvent *)
{
    // 1. Use local drawing to avoid full-screen redraw
    QPainter painter(this);
    painter.setClipRect(dirtyRect);
    
    // 2. Use pre-generated resources
    static QPixmap cachedBackground = generateBackground();
    painter.drawPixmap(0, 0, cachedBackground);
    
    // 3. Disable anti-aliasing
    painter.setRenderHint(QPainter::Antialiasing, false);
    
    // 4. Use fast drawing methods
    painter.drawRects(rects.data(), rects.size());
}

9. Embedded Debugging Techniques

9.1 Remote Debugging

// Use QtRemoteObjects for remote debugging
// Run on target device:
// myapp -qws -remote-debug 1234

// Connect on development machine:
void connectToRemoteDevice()
{
    QRemoteObjectHost srcNode(QUrl("tcp://192.168.1.100:1234"));
    
    // Get remote object
    RemoteDebugInterface *debug = srcNode.acquire<RemoteDebugInterface>();
    
    if (debug) {
        connect(debug, &RemoteDebugInterface::logMessage, 
                [](const QString &msg) {
                    qDebug() << "Remote Log:" << msg;
                });
    }
}

9.2 Memory Monitoring

#include <QMemoryInfo>

void checkMemoryUsage()
{
    QMemoryInfo memory;
    qDebug() << "Memory usage:"
             << "\nTotal:" << memory.total() / 1024 << "KB"
             << "\nUsed:" << memory.used() / 1024 << "KB"
             << "\nFree:" << memory.free() / 1024 << "KB"
             << "\nApplication:" << memory.processUsed() / 1024 << "KB";
    
    if (memory.free() < (2 * 1024 * 1024)) { // Less than 2MB
        emit lowMemoryWarning();
    }
}

Conclusion

Qt embedded development requires special attention to the following aspects:

  1. Resource Optimization: Streamlining the Qt library, static compilation, optimizing memory usage
  2. Input Methods: Adapting to touchscreen and small keyboard input
  3. Device Integration: Power management, backlight control, and other hardware features
  4. Performance Tuning: Optimizing drawing performance, reducing CPU and memory usage
  5. Deployment Strategies: Creating a compact filesystem, cross-compilation configuration

Qt for Device Creation provides a complete toolchain and API, enabling developers to create feature-rich and efficient applications for embedded devices. By effectively utilizing Qt’s cross-platform features and embedded optimization capabilities, the complexity of embedded development can be significantly reduced.

Leave a Comment