Qt 窗口管理详解:QMainWindow、QDialog 与 QWidget
accept();// 或 close() 保持非模态accept();在 MainWindow 中使用});QMainWindow 完整布局与高级用法QDialog 模态/非模态 + 自定义对话框QWidget 自定义绘制与布局综合项目开发、测试、优化进阶方向Qt Quick / QML 替换 QWidget(更现代、流畅)
·
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 采集
更多推荐



所有评论(0)