Qt 窗口管理详解:QMainWindow、QDialog 与 QWidget

  • 更清晰的原理图解与布局剖析
  • 完整可运行项目代码(文本编辑器 + 查找对话框 + 自定义仪表盘)
  • 丰富控件组合与高级用法(QSplitter、QTabWidget、QToolBox 等)
  • Qt Designer + 代码混合开发最佳实践
  • 信号槽、属性系统、动态布局实战
  • 自动化测试用例(Qt Test)
  • 性能优化与常见陷阱
  • 真实场景扩展:半导体测试机控制面板(带采集、阈值设置、实时图表)

1. 引言

Qt 的窗口体系是 GUI 开发的基石:

  • QMainWindow:应用程序主框架(菜单、工具栏、状态栏、停靠区)
  • QDialog:临时交互窗口(模态/非模态)
  • QWidget:所有控件的根基,可独立成窗或嵌入

2026 年主流趋势:

  • Qt 6 全面取代 Qt 5(Qt 5.15 LTS 已停止官方支持)
  • QML + C++ 混合开发(QQuickWidget / QQmlEngine)
  • 高 DPI / 跨平台适配(QHighDpiScaling)
  • 现代布局(QGridLayout、QFormLayout、QSplitter)

本文目标:让你从零掌握窗口开发,并能独立完成复杂桌面应用(如半导体测试机控制界面)。

2. QMainWindow:高级窗口类

2.1 基本结构与布局原理

QMainWindow 采用固定层级布局(不可改变顺序):

┌───────────────────────────────────────┐
│              QMenuBar                 │  ← 菜单栏(顶部,高度固定)
├───────────────────────────────────────┤
│         QToolBar(可多个、可停靠)     │  ← 工具栏(可浮动、隐藏)
├───────────────────────────────────────┤
│                                       │
│          Central Widget               │  ← 中心区域(唯一,必须设置)
│         (占据最大空间,可伸缩)       │
│                                       │
├───────────────────────────────────────┤
│      QDockWidget(可多个、浮动)       │  ← 停靠窗口(左右上下)
└───────────────────────────────────────┘
│              QStatusBar               │  ← 状态栏(底部)
└───────────────────────────────────────┘

关键 API(Qt 6 常用):

setMenuBar(QMenuBar*);               // 自定义菜单栏
addToolBar(QToolBar*);               // 添加工具栏
setCentralWidget(QWidget*);          // 设置中心部件(必须)
addDockWidget(Qt::DockWidgetArea, QDockWidget*);  // 添加停靠窗口
setStatusBar(QStatusBar*);           // 自定义状态栏

2.2 菜单栏(QMenuBar + QAction)

现代写法(带图标、快捷键、禁用/勾选)

QMenu* fileMenu = menuBar()->addMenu(tr("&File"));

QAction* newAct = new QAction(QIcon(":/icons/new.png"), tr("&New"), this);
newAct->setShortcut(QKeySequence::New);
newAct->setStatusTip(tr("Create a new file"));
connect(newAct, &QAction::triggered, this, &MainWindow::newFile);
fileMenu->addAction(newAct);

// 分隔线 + 退出
fileMenu->addSeparator();
fileMenu->addAction(tr("E&xit"), this, &QMainWindow::close, QKeySequence::Quit);

动态菜单(最近文件列表):

QMenu* recentMenu = fileMenu->addMenu(tr("Recent Files"));
for (const QString& file : recentFiles) {
    QAction* act = recentMenu->addAction(file);
    connect(act, &QAction::triggered, this, [=]{ openFile(file); });
}

2.3 工具栏(QToolBar)

QToolBar* fileToolBar = addToolBar(tr("File"));
fileToolBar->setMovable(true);
fileToolBar->setIconSize(QSize(32, 32));  // 高 DPI 适配

fileToolBar->addAction(newAct);
fileToolBar->addAction(saveAct);

// 嵌入自定义控件
QComboBox* zoomCombo = new QComboBox;
zoomCombo->addItems({"50%", "100%", "150%", "200%"});
fileToolBar->addWidget(zoomCombo);

高级:多工具栏 + 浮动分组

QToolBar* editToolBar = addToolBar(tr("Edit"));
editToolBar->addAction(cutAct);
editToolBar->addAction(copyAct);
editToolBar->addAction(pasteAct);

2.4 状态栏(QStatusBar)

// 临时消息(5秒消失)
statusBar()->showMessage(tr("File saved"), 5000);

// 永久部件(行/列信息)
QLabel* positionLabel = new QLabel(tr("Line: 1  Col: 1"));
statusBar()->addPermanentWidget(positionLabel);

// 进度条
QProgressBar* progress = new QProgressBar;
progress->setRange(0, 100);
statusBar()->addWidget(progress);

2.5 中心部件(setCentralWidget)

QTextEdit* editor = new QTextEdit(this);
setCentralWidget(editor);

// 更复杂布局:QTabWidget + QSplitter
QTabWidget* tabs = new QTabWidget;
QSplitter* splitter = new QSplitter(Qt::Horizontal);
splitter->addWidget(new QTreeView);   // 项目树
splitter->addWidget(editor);          // 编辑器
tabs->addTab(splitter, "Main");
setCentralWidget(tabs);

2.6 可停靠窗口(QDockWidget)

QDockWidget* projectDock = new QDockWidget(tr("Project"), this);
projectDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
QTreeView* tree = new QTreeView(projectDock);
projectDock->setWidget(tree);
addDockWidget(Qt::LeftDockWidgetArea, projectDock);

// 控制可见性
projectDock->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);

多停靠区

QDockWidget* outputDock = new QDockWidget(tr("Output"), this);
addDockWidget(Qt::BottomDockWidgetArea, outputDock);
tabifyDockWidget(projectDock, outputDock);  // 标签页化

2.7 完整示例:现代文本编辑器主窗口(Qt 6 风格)

mainwindow.h

#pragma once
#include <QMainWindow>
#include <QTextEdit>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void newFile();
    void openFile();
    void saveFile();
    void updateCursorPosition();
    void showFindDialog();

private:
    Ui::MainWindow *ui;
    QTextEdit *editor;
};

mainwindow.cpp(关键部分)

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFileDialog>
#include <QMessageBox>
#include <QDockWidget>
#include <QLabel>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    editor = new QTextEdit(this);
    setCentralWidget(editor);

    // 菜单
    QMenu* fileMenu = menuBar()->addMenu(tr("&File"));
    fileMenu->addAction(tr("&New"), this, &MainWindow::newFile, QKeySequence::New);
    fileMenu->addAction(tr("&Open..."), this, &MainWindow::openFile, QKeySequence::Open);
    fileMenu->addAction(tr("&Save"), this, &MainWindow::saveFile, QKeySequence::Save);

    // 工具栏
    QToolBar* toolBar = addToolBar(tr("File"));
    toolBar->addAction(tr("New"), this, &MainWindow::newFile);
    toolBar->addAction(tr("Open"), this, &MainWindow::openFile);

    // 状态栏
    statusBar()->showMessage(tr("Ready"));

    // 光标位置显示
    QLabel* posLabel = new QLabel(tr("Ln 1, Col 1"));
    statusBar()->addPermanentWidget(posLabel);
    connect(editor, &QTextEdit::cursorPositionChanged, this, [=]{
        QTextCursor cur = editor->textCursor();
        posLabel->setText(tr("Ln %1, Col %2").arg(cur.blockNumber()+1).arg(cur.columnNumber()));
    });

    // 查找对话框
    QAction* findAct = new QAction(tr("&Find..."), this);
    findAct->setShortcut(QKeySequence::Find);
    connect(findAct, &QAction::triggered, this, &MainWindow::showFindDialog);
    menuBar()->addMenu(tr("&Edit"))->addAction(findAct);
}

void MainWindow::showFindDialog()
{
    // 查找对话框(模态)
    // 实现见下文 FindDialog
}

3. QDialog:对话框窗口类

3.1 模态 vs 非模态
  • 模态exec() → 阻塞主窗口,直到关闭,返回 Accepted / Rejected
  • 非模态show() → 不阻塞,可设 WA_DeleteOnClose 自动销毁
3.2 自定义对话框(推荐 Qt Designer)

finddialog.h

#pragma once
#include <QDialog>

namespace Ui { class FindDialog; }

class FindDialog : public QDialog
{
    Q_OBJECT

public:
    explicit FindDialog(QWidget *parent = nullptr);
    ~FindDialog();

signals:
    void findNext(const QString &text, bool caseSensitive);
    void findPrevious(const QString &text, bool caseSensitive);

private slots:
    void on_findNextButton_clicked();
    void on_findPreviousButton_clicked();

private:
    Ui::FindDialog *ui;
};

finddialog.cpp

#include "finddialog.h"
#include "ui_finddialog.h"

FindDialog::FindDialog(QWidget *parent)
    : QDialog(parent), ui(new Ui::FindDialog)
{
    ui->setupUi(this);
    setWindowTitle(tr("Find"));
    setFixedSize(sizeHint());
}

FindDialog::~FindDialog()
{
    delete ui;
}

void FindDialog::on_findNextButton_clicked()
{
    emit findNext(ui->findLineEdit->text(), ui->caseSensitiveCheck->isChecked());
    accept(); // 或 close() 保持非模态
}

void FindDialog::on_findPreviousButton_clicked()
{
    emit findPrevious(ui->findLineEdit->text(), ui->caseSensitiveCheck->isChecked());
    accept();
}

在 MainWindow 中使用

void MainWindow::showFindDialog()
{
    FindDialog dialog(this);
    connect(&dialog, &FindDialog::findNext, this, [=](const QString& text, bool cs){
        editor->find(text, cs ? QTextDocument::FindCaseSensitively : QTextDocument::FindFlags());
    });
    dialog.exec();
}

4. 综合示例:完整文本编辑器(Qt 6 现代版)

项目结构

TextEditor/
├── main.cpp
├── mainwindow.h/cpp/ui
├── finddialog.h/cpp/ui
├── resources.qrc
└── icons/ (new.png, open.png, save.png, find.png)

main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    MainWindow window;
    window.resize(1000, 700);
    window.show();
    return app.exec();
}

完整 mainwindow.cpp(含查找、统计、图标)

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "finddialog.h"
#include <QFileDialog>
#include <QMessageBox>
#include <QDockWidget>
#include <QLabel>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setWindowTitle(tr("Text Editor"));

    editor = new QTextEdit(this);
    setCentralWidget(editor);

    // 菜单
    QMenu* fileMenu = menuBar()->addMenu(tr("&File"));
    fileMenu->addAction(QIcon(":/icons/new.png"), tr("&New"), this, &MainWindow::newFile, QKeySequence::New);
    fileMenu->addAction(QIcon(":/icons/open.png"), tr("&Open..."), this, &MainWindow::openFile, QKeySequence::Open);
    fileMenu->addAction(QIcon(":/icons/save.png"), tr("&Save"), this, &MainWindow::saveFile, QKeySequence::Save);

    QMenu* editMenu = menuBar()->addMenu(tr("&Edit"));
    editMenu->addAction(QIcon(":/icons/find.png"), tr("&Find..."), this, &MainWindow::showFindDialog, QKeySequence::Find);

    // 工具栏
    QToolBar* toolBar = addToolBar(tr("File"));
    toolBar->addAction(QIcon(":/icons/new.png"), tr("New"), this, &MainWindow::newFile);
    toolBar->addAction(QIcon(":/icons/open.png"), tr("Open"), this, &MainWindow::openFile);
    toolBar->addAction(QIcon(":/icons/save.png"), tr("Save"), this, &MainWindow::saveFile);

    // 状态栏
    statusBar()->showMessage(tr("Ready"));

    // 统计停靠窗口
    QDockWidget* statsDock = new QDockWidget(tr("Statistics"), this);
    addDockWidget(Qt::RightDockWidgetArea, statsDock);
    statsLabel = new QLabel(tr("Words: 0  Characters: 0"));
    statsDock->setWidget(statsLabel);

    connect(editor, &QTextEdit::textChanged, this, &MainWindow::updateStats);
}

void MainWindow::updateStats()
{
    QString text = editor->toPlainText();
    int chars = text.length();
    int words = text.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts).size();
    statsLabel->setText(tr("Words: %1  Characters: %2").arg(words).arg(chars));
}

5. 自动化测试用例(Qt Test 全面扩展)

mainwindow_test.cpp

#include <QtTest>
#include "mainwindow.h"
#include "finddialog.h"

class WindowTest : public QObject
{
    Q_OBJECT

private slots:
    void test_open_file()
    {
        MainWindow w;
        w.show();
        QVERIFY(QTest::qWaitForWindowActive(&w));

        QString file = QFINDTESTDATA("test.txt");
        w.openFile(file);
        QVERIFY(!w.editor->toPlainText().isEmpty());
    }

    void test_find_dialog_signal()
    {
        FindDialog dialog(nullptr);
        QSignalSpy nextSpy(&dialog, &FindDialog::findNext);
        dialog.ui->findLineEdit->setText("Qt");
        QTest::mouseClick(dialog.ui->findNextButton, Qt::LeftButton);
        QCOMPARE(nextSpy.count(), 1);
        QCOMPARE(nextSpy.takeFirst().at(0).toString(), "Qt");
    }

    void test_stats_update()
    {
        MainWindow w;
        w.editor->setPlainText("Hello world\nThis is Qt");
        w.updateStats();
        QVERIFY(w.statsLabel->text().contains("Words: 4"));
        QVERIFY(w.statsLabel->text().contains("Characters: 24"));
    }
};

QTEST_MAIN(WindowTest)
#include "mainwindow_test.moc"

6. 真实场景:半导体测试机控制面板

应用扩展

  • QMainWindow:主界面(菜单:测试、配置、视图、帮助)
  • QDockWidget:实时采集列表、参数设置、日志
  • QDialog:阈值设置对话框、测试配置向导
  • QWidget:自定义电压/电流仪表盘(paintEvent 绘制)

仪表盘示例

class VoltageGauge : public QWidget
{
    Q_OBJECT
    Q_PROPERTY(double value READ value WRITE setValue NOTIFY valueChanged)

public:
    VoltageGauge(QWidget *parent = nullptr) : QWidget(parent), m_value(0.0) {}

    double value() const { return m_value; }
    void setValue(double v) { if (qFuzzyCompare(m_value, v)) return; m_value = v; update(); emit valueChanged(v); }

signals:
    void valueChanged(double);

protected:
    void paintEvent(QPaintEvent *) override
    {
        QPainter p(this);
        p.setRenderHint(QPainter::Antialiasing);

        // 圆形背景
        p.setBrush(Qt::black);
        p.drawEllipse(rect().adjusted(10,10,-10,-10));

        // 指针
        double angle = (m_value / 5.0) * 270 - 135;  // 0~5V → -135°~135°
        p.save();
        p.translate(width()/2, height()/2);
        p.rotate(angle);
        p.setPen(QPen(Qt::red, 4));
        p.drawLine(0, 0, 0, -80);
        p.restore();
    }

private:
    double m_value;
};

总结

通过本文,你已掌握:

  • QMainWindow 完整布局与高级用法
  • QDialog 模态/非模态 + 自定义对话框
  • QWidget 自定义绘制与布局
  • 综合项目开发、测试、优化

进阶方向

  • Qt Quick / QML 替换 QWidget(更现代、流畅)
  • QStateMachine 管理复杂状态机
  • QML + C++ 混合开发(QQuickWidget)
  • 集成 Qt Charts 实时曲线显示

Qt 混合开发示例:QML + C++(完整实战指南)

Qt 的混合开发模式(C++ 后端 + QML 前端)是 2025–2026 年主流做法,尤其适合:

  • 需要高性能计算 / 硬件访问的场景(C++ 负责逻辑、数据处理、线程)
  • 追求现代、流畅、动态 UI 的场景(QML 负责界面、动画、状态管理)

本文提供 从零到完整项目 的混合开发示例,包含:

  • 项目结构
  • C++ 后端(数据模型、业务逻辑、线程)
  • QML 前端(UI、ListView、动画、信号绑定)
  • 双向通信(C++ → QML、QML → C++)
  • 完整可运行代码
  • 部署与调试技巧

项目场景:半导体测试机实时监控面板

功能

  • C++ 后端:模拟采集电压/电流/温度(QThreadPool 并发)
  • QML 前端:实时曲线(ChartView)、设备列表(ListView)、控制按钮
  • 双向通信:C++ 推送采集数据 → QML 更新图表;QML 点击“开始测试” → C++ 启动采集

1. 项目结构(推荐)

SemiMonitor/
├── CMakeLists.txt               # 现代 CMake 构建(推荐)
├── main.cpp                     # 入口 + QML 引擎注册
├── DataCollector.h/cpp          # 数据采集后端(C++)
├── DeviceModel.h/cpp            # QAbstractListModel(桥接 QList → QML)
├── resources.qrc                # QML 文件 + 图标
├── qml/
│   ├── main.qml                 # 主界面
│   ├── DeviceDelegate.qml       # ListView 委托
│   └── VoltageChart.qml         # 实时曲线组件
└── CMakeLists.txt

2. CMakeLists.txt(现代构建方式)

cmake_minimum_required(VERSION 3.16)
project(SemiMonitor LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

find_package(Qt6 6.5 REQUIRED COMPONENTS Quick Core Concurrent Charts)

qt_standard_project_setup()

qt_add_executable(SemiMonitor
    main.cpp
    DataCollector.cpp
    DeviceModel.cpp
)

qt_add_qml_module(SemiMonitor
    URI SemiMonitor
    VERSION 1.0
    QML_FILES
        qml/main.qml
        qml/DeviceDelegate.qml
        qml/VoltageChart.qml
    RESOURCES
        resources.qrc
)

target_link_libraries(SemiMonitor PRIVATE
    Qt6::Quick
    Qt6::Core
    Qt6::Concurrent
    Qt6::Charts
)

install(TARGETS SemiMonitor
    BUNDLE DESTINATION .
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

3. C++ 后端核心代码

DataCollector.h(采集 + 数据推送)

#pragma once
#include <QObject>
#include <QList>
#include <QMap>
#include <QThreadPool>
#include <QRunnable>
#include <QTimer>

struct DeviceData {
    QString id;
    double voltage = 0.0;
    double current = 0.0;
    double temperature = 0.0;
    bool isAbnormal = false;
    QDateTime lastUpdate;
};

class DataCollector : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QList<QVariantMap> devices READ devices NOTIFY devicesChanged)

public:
    explicit DataCollector(QObject *parent = nullptr);
    ~DataCollector();

    Q_INVOKABLE void startCollection(int intervalMs = 1000);
    Q_INVOKABLE void stopCollection();

    QList<QVariantMap> devices() const;

signals:
    void devicesChanged();

private slots:
    void simulateData();

private:
    QTimer *m_timer = nullptr;
    QThreadPool m_pool;
    QList<DeviceData> m_devices;
    mutable QMutex m_mutex;
};

DataCollector.cpp

#include "DataCollector.h"
#include <QRandomGenerator>
#include <QDateTime>

DataCollector::DataCollector(QObject *parent) : QObject(parent)
{
    // 初始化 8 个模拟设备
    for (int i = 1; i <= 8; ++i) {
        m_devices.append({QString("DEV-%1").arg(i)});
    }

    m_timer = new QTimer(this);
    connect(m_timer, &QTimer::timeout, this, &DataCollector::simulateData);
}

DataCollector::~DataCollector()
{
    stopCollection();
}

void DataCollector::startCollection(int intervalMs)
{
    m_timer->start(intervalMs);
}

void DataCollector::stopCollection()
{
    m_timer->stop();
}

QList<QVariantMap> DataCollector::devices() const
{
    QMutexLocker lock(&m_mutex);
    QList<QVariantMap> result;
    for (const auto &d : m_devices) {
        QVariantMap m;
        m["id"] = d.id;
        m["voltage"] = d.voltage;
        m["current"] = d.current;
        m["temperature"] = d.temperature;
        m["abnormal"] = d.isAbnormal;
        m["lastUpdate"] = d.lastUpdate.toString(Qt::ISODate);
        result << m;
    }
    return result;
}

void DataCollector::simulateData()
{
    QMutexLocker lock(&m_mutex);
    auto rand = QRandomGenerator::global();

    bool changed = false;
    for (auto &d : m_devices) {
        double oldV = d.voltage;
        d.voltage = 3.3 + rand->bounded(-0.5, 0.5);
        d.current = 0.3 + rand->bounded(0.0, 0.4);
        d.temperature = 25.0 + rand->bounded(0.0, 90.0);
        d.isAbnormal = (d.voltage < 3.0 || d.voltage > 3.6 ||
                        d.current > 0.5 || d.temperature > 85.0);
        d.lastUpdate = QDateTime::currentDateTime();

        if (!qFuzzyCompare(oldV, d.voltage)) changed = true;
    }

    if (changed) emit devicesChanged();
}

4. QML 前端(main.qml)

import QtQuick
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Layouts
import QtCharts
import SemiMonitor 1.0

Window {
    width: 1200
    height: 800
    visible: true
    title: "半导体测试机实时监控"

    DataCollector {
        id: collector
        Component.onCompleted: collector.startCollection(800)  // 每 800ms 更新一次
    }

    ColumnLayout {
        anchors.fill: parent
        spacing: 10

        // 顶部控制栏
        RowLayout {
            Layout.fillWidth: true
            height: 60

            Button {
                text: "开始采集"
                onClicked: collector.startCollection(500)
            }
            Button {
                text: "停止采集"
                onClicked: collector.stopCollection()
            }
            Item { Layout.fillWidth: true }
        }

        // 主内容区:SplitView
        SplitView {
            Layout.fillWidth: true
            Layout.fillHeight: true
            orientation: Qt.Horizontal

            // 左侧:设备列表
            ListView {
                SplitView.preferredWidth: 300
                SplitView.minimumWidth: 200
                model: collector.devices
                delegate: DeviceDelegate { }
            }

            // 右侧:实时曲线(ChartView)
            ChartView {
                id: chartView
                SplitView.fillWidth: true
                title: "电压实时曲线"
                antialiasing: true

                ValueAxis {
                    id: axisX
                    min: 0
                    max: 60
                    titleText: "时间 (s)"
                }

                ValueAxis {
                    id: axisY
                    min: 2.0
                    max: 5.0
                    titleText: "电压 (V)"
                }

                LineSeries {
                    id: voltageSeries
                    axisX: axisX
                    axisY: axisY
                    name: "电压"
                }

                // 定时更新曲线(示例:只显示最新 60 秒)
                Timer {
                    interval: 1000; running: true; repeat: true
                    onTriggered: {
                        if (voltageSeries.count > 60) {
                            voltageSeries.remove(0);
                        }
                        voltageSeries.append(voltageSeries.count, collector.devices[0].voltage);
                    }
                }
            }
        }
    }
}

DeviceDelegate.qml(ListView 项)

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

ItemDelegate {
    width: ListView.view.width
    height: 80

    background: Rectangle {
        color: model.abnormal ? "#FFCCCC" : "white"
        border.color: "gray"
    }

    RowLayout {
        anchors.fill: parent
        anchors.margins: 10
        spacing: 15

        Rectangle {
            width: 20; height: 20
            radius: 10
            color: model.abnormal ? "red" : "green"
        }

        ColumnLayout {
            Layout.fillWidth: true
            spacing: 5

            Text { text: model.id; font.bold: true }
            Text { text: "电压: " + model.voltage.toFixed(2) + " V" }
            Text { text: "电流: " + model.current.toFixed(2) + " A" }
            Text { text: "温度: " + model.temperature.toFixed(1) + " °C" }
        }

        Text {
            text: model.lastUpdate
            font.pointSize: 8
            color: "gray"
        }
    }
}

5. 注册与启动(main.cpp)

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "DataCollector.h"
#include "DeviceModel.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    qmlRegisterType<DataCollector>("SemiMonitor", 1, 0, "DataCollector");
    qmlRegisterType<DeviceModel>("SemiMonitor", 1, 0, "DeviceModel");

    QQmlApplicationEngine engine;
    engine.load(QUrl("qrc:/qml/main.qml"));

    return app.exec();
}

6. 编译与运行(CMake)

mkdir build && cd build
cmake ..
cmake --build . --config Release
./SemiMonitor

运行效果

  • 左侧设备列表实时刷新(8 个模拟设备)
  • 右侧电压曲线动态绘制
  • 点击按钮可控制采集频率

7. 常见问题与优化

  • QML 性能:大数据量 ListView 使用 ListView + delegate + model 缓存
  • C++ → QML 推送:使用 emit devicesChanged() 通知 QML 更新
  • 线程安全:采集数据使用 QMutex 保护
  • 高频更新:建议 500–1000ms 刷新一次,避免 UI 卡顿
  • 真实硬件:替换 simulateData() 为串口/Modbus/PCIe 采集
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐