qt基本用法

一、Qt 基础概念

1. 什么是 Qt

Qt 是一个 C++ 框架,主要用来写桌面程序、嵌入式程序、移动端程序。核心能力:

  • GUI:图形界面(按钮、窗口、菜单)
  • 网络:TCP/UDP/HTTP
  • 数据库:SQLite/MySQL
  • 多媒体:音频/视频
  • 多线程:线程、线程池
  • 串口通信:QSerialPort
  • XML/JSON 解析
  • 跨平台:Windows/Linux/Mac/Android/iOS 一套代码

2. Qt 的模块

复制代码
Qt Core      ← 核心(信号槽、容器、文件、线程、定时器)
Qt GUI       ← 图形界面基础(窗口、事件、字体、图片)
Qt Widgets   ← 传统控件(按钮、输入框、表格)
Qt QML       ← QML 引擎
Qt Quick     ← QML 现代界面
Qt Network   ← 网络(TCP/UDP/HTTP)
Qt SQL       ← 数据库
Qt SerialPort← 串口通信
Qt Multimedia← 音视频
Qt Charts    ← 图表
Qt 3D        ← 3D渲染
Qt WebEngine ← 内嵌浏览器

3. pro 文件(项目文件)

复制代码
QT       += core gui widgets       # 用到的Qt模块
TARGET = MyApp                     # 生成的exe名字
TEMPLATE = app                     # 项目类型:app=可执行程序 lib=库 subdirs=多子项目

SOURCES += main.cpp mainwindow.cpp     # cpp文件
HEADERS += mainwindow.h                # h文件
FORMS   += mainwindow.ui               # ui文件(Designer设计的界面)
RESOURCES += qml/qml.qrc               # 资源文件(QML、图片等)
LIBS    += -L$$PWD/ -lMyLib            # 链接外部库
INCLUDEPATH += $$PWD/include            # 头文件搜索路径

# 平台判断
win32 { RC_FILE += app_version.rc }     # Windows资源文件
linux { LIBS += -lpthread }             # Linux链接线程库

# 编译器判断
msvc { QMAKE_CXXFLAGS += /utf-8 }      # MSVC编码设置
mingw { QMAKE_CXXFLAGS += -std=c++11 }  # MinGW标准

# 条件编译
CONFIG += c++11                         # 启用C++11
CONFIG += c++17                         # 启用C++17
CONFIG += debug_and_release             # 同时生成debug和release
DEFINES += MY_FLAG                      # 定义预处理宏

4. qmake 变量

复制代码
$$PWD          # 当前pro文件所在目录
$$OUT_PWD      # 构建输出目录
$$[QT_INSTALL_LIBS]   # Qt库安装路径
$$basename(HEADER)     # 取文件名
$$dirname(HEADER)      # 取目录名

5. 前向声明(Forward Declaration)

复制代码
// mainwindow.h 里
class QQuickWidget;    // 只声明,不include
class TrdpQmlBridge;

class MainWindow : public QMainWindow {
    QQuickWidget *mQuickWidget;    // 只用指针,不需要知道完整定义
    TrdpQmlBridge *mBridge;
};

为什么用前向声明 :减少头文件依赖,加快编译。只要用指针或引用,就不需要 #include


6. 头文件保护

复制代码
// 方式1:传统方式
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
// ... 类定义
#endif

// 方式2:简洁方式(推荐)
#pragma once
// ... 类定义

作用:防止头文件被重复包含。


二、QObject 核心机制

7. QObject 父子关系

复制代码
QObject *parent = new QObject();
QObject *child1 = new QObject(parent);   // child1的父对象是parent
QObject *child2 = new QObject(parent);   // child2的父对象是parent

delete parent;
// parent被删除时,child1和child2自动被删除
// 不需要手动delete子对象

内存管理:父对象析构时自动析构所有子对象。这是 Qt 的垃圾回收机制。


8. 对象树

复制代码
QApplication                    ← 全局唯一
  └── MainWindow                ← 主窗口
        ├── QQuickWidget        ← QML容器
        │     └── QQmlEngine    ← QML引擎
        ├── TrdpQmlBridge       ← 桥接对象
        │     └── TrdpServiceHost  ← 服务管理
        │           └── TrdpWorker  ← 工作线程对象
        └── LogManager          ← 日志管理器

9. objectName

复制代码
QObject *obj = new QObject();
obj->setObjectName("myButton");       // 设置名字
QString name = obj->objectName();     // 获取名字 "myButton"

用途:调试时标识对象,QML 里也可以通过 objectName 查找对象。


10. findChild / findChildren

复制代码
// 在子对象树中按名字查找
QPushButton *btn = parent->findChild<QPushButton*>("myButton");

// 查找所有子对象
QList<QPushButton*> allButtons = parent->findChildren<QPushButton*>();

11. connect 的第五个参数(连接类型)

复制代码
connect(sender, &Sender::signal,
        receiver, &Receiver::slot,
        Qt::AutoConnection);    // 默认值
连接类型 意义
Qt::AutoConnection 同线程直接调用,不同线程排队调用(默认)
Qt::DirectConnection 无论是否同线程,都在发送方线程直接调用
Qt::QueuedConnection 无论是否同线程,都在接收方线程排队调用
Qt::BlockingQueuedConnection 排队调用,发送方阻塞等待接收方执行完
Qt::UniqueConnection 防止重复连接,和其他类型组合使用
复制代码
// 防止重复连接
connect(btn, &QPushButton::clicked, this, &MyClass::onClicked, Qt::UniqueConnection);
// 多次调用这条connect,只会连接一次

12. 信号槽返回值

复制代码
// 信号没有返回值(void)
signals:
    void dataReady(QByteArray data);

// 槽可以有返回值,但QueuedConnection下拿不到
// DirectConnection可以
connect(sender, &Sender::request, receiver, &Receiver::process, Qt::DirectConnection);

13. 一个信号连多个槽

复制代码
connect(timer, &QTimer::timeout, this, &MyClass::updateUI);      // 槽1
connect(timer, &QTimer::timeout, this, &MyClass::checkStatus);   // 槽2
// 两个槽都会被调用,顺序不确定

14. 多个信号连一个槽

复制代码
connect(btn1, &QPushButton::clicked, this, &MyClass::onAnyButtonClicked);
connect(btn2, &QPushButton::clicked, this, &MyClass::onAnyButtonClicked);
// 任意一个按钮点击都会调用同一个槽

15. 信号连信号

复制代码
connect(btn, &QPushButton::clicked, this, &MyClass::clicked);
// 按钮的clicked → 转发为MyClass的clicked信号
// 相当于中继/转发

三、控件(Widgets)

16. 基础控件

复制代码
// 按钮
QPushButton *btn = new QPushButton("确定");
QPushButton *btn2 = new QPushButton(QIcon("icon.png"), "带图标的按钮");
connect(btn, &QPushButton::clicked, this, &MyClass::onClicked);

// 标签
QLabel *label = new QLabel("文字");
label->setAlignment(Qt::AlignCenter);         // 居中
label->setWordWrap(true);                      // 自动换行
label->setPixmap(QPixmap("image.png"));        // 显示图片

// 单行输入框
QLineEdit *edit = new QLineEdit();
edit->setPlaceholderText("请输入IP地址");       // 占位文字
edit->setEchoMode(QLineEdit::Password);        // 密码模式
edit->setValidator(new QIntValidator(0, 65535)); // 限制只能输入0-65535
edit->setMaxLength(15);                         // 最大长度
QString text = edit->text();                    // 获取输入内容

// 多行文本框
QPlainTextEdit *plainEdit = new QPlainTextEdit();
plainEdit->setReadOnly(true);                   // 只读
plainEdit->appendPlainText("追加一行");         // 追加文字
plainEdit->clear();                             // 清空
int lines = plainEdit->blockCount();            // 行数

// QTextEdit:富文本(支持HTML)
QTextEdit *richEdit = new QTextEdit();
richEdit->setHtml("<b>粗体</b> <font color='red'>红色</font>");

// 下拉框
QComboBox *combo = new QComboBox();
combo->addItem("选项1");
combo->addItem("选项2", QVariant(100));   // 带用户数据
combo->addItems({"A", "B", "C"});         // 批量添加
QString current = combo->currentText();    // 当前文字
QVariant data = combo->currentData();     // 当前用户数据

// 狂选框
QCheckBox *check = new CheckBox("同意协议");
check->setChecked(true);                    // 设置选中
bool isChecked = check->isChecked();       // 是否选中

// 单选按钮
QRadioButton *radio1 = new QRadioButton("选项A");
QRadioButton *radio2 = new QRadioButton("选项B");
QButtonGroup *group = new QButtonGroup(this);
group->addButton(radio1, 1);    // id=1
group->addButton(radio2, 2);    // id=2

// 数字输入框
QSpinBox *spin = new QSpinBox();
spin->setRange(0, 100);          // 范围
spin->setValue(50);              // 当前值
spin->setSingleStep(5);         // 步长

QDoubleSpinBox *doubleSpin = new QDoubleSpinBox();
doubleSpin->setDecimals(2);     // 小数位数

// 滑块
QSlider *slider = new QSlider(Qt::Horizontal);
slider->setRange(0, 100);
slider->setValue(50);

// 进度条
QProgressBar *progress = new QProgressBar();
progress->setRange(0, 100);
progress->setValue(75);          // 75%

// 列表
QListWidget *listWidget = new QListWidget();
listWidget->addItem("项目1");
listWidget->addItem("项目2");
// 取出选中项
QListWidgetItem *item = listWidget->currentItem();
QString text = item->text();

// 表格
QTableWidget *table = new QTableWidget(10, 3);   // 10行3列
table->setHorizontalHeaderLabels({"姓名", "年龄", "城市"});
table->setItem(0, 0, new QTableWidgetItem("张三"));

// 树
QTreeWidget *tree = new QTreeWidget();
tree->setHeaderLabels({"名称", "值"});
QTreeWidgetItem *root = new QTreeWidgetItem(tree, {"根节点", ""});
QTreeWidgetItem *child = new QTreeWidgetItem(root, {"子节点", "123"});

// 分割器(可拖拽调整大小)
QSplitter *splitter = new QSplitter(Qt::Horizontal);
splitter->addWidget(leftWidget);
splitter->addWidget(rightWidget);

// 选项卡
QTabWidget *tabs = new QTabWidget();
tabs->addTab(page1, "第一页");
tabs->addTab(page2, "第二页");

// 工具栏
QToolBar *toolbar = addToolBar("工具栏");
toolbar->addAction("新建", this, &MyClass::onNew);
toolbar->addAction(QIcon("open.png"), "打开", this, &MyClass::onOpen);

// 状态栏
statusBar()->showMessage("就绪", 3000);   // 显示3秒

// 菜单栏
QMenu *fileMenu = menuBar()->addMenu("文件(&F)");  // &F 表示快捷键 Alt+F
fileMenu->addAction("新建(&N)", this, &MyClass::onNew, QKeySequence::New);
fileMenu->addAction("打开(&O)", this, &MyClass::onOpen, QKeySequence::Open);
fileMenu->addSeparator();    // 分隔线
fileMenu->addAction("退出(&X)", this, &QWidget::close);

17. 对话框

复制代码
// 消息框
QMessageBox::information(this, "提示", "操作成功");
QMessageBox::warning(this, "警告", "磁盘空间不足");
QMessageBox::critical(this, "错误", "连接失败");
int ret = QMessageBox::question(this, "确认", "确定要退出吗?",
                                 QMessageBox::Yes | QMessageBox::No);
if (ret == QMessageBox::Yes) { /* 退出 */ }

// 文件选择对话框
QString path = QFileDialog::getOpenFileName(this, "选择文件", "",
    "文本文件 (*.txt);;所有文件 (*.*)");
QString dir = QFileDialog::getExistingDirectory(this, "选择目录");
QString savePath = QFileDialog::getSaveFileName(this, "保存文件");

// 输入对话框
QString text = QInputDialog::getText(this, "输入", "请输入姓名:");
int num = QInputDialog::getInt(this, "输入", "请输入数量:", 10, 0, 100);
double value = QInputDialog::getDouble(this, "输入", "请输入价格:", 9.99, 0, 1000, 2);
QString item = QInputDialog::getItem(this, "选择", "请选择:", {"A", "B", "C"});

// 颜色选择对话框
QColor color = QColorDialog::getColor(Qt::white, this, "选择颜色");

// 字体选择对话框
QFont font = QFontDialog::getFont(nullptr, this, "选择字体");

// 进度对话框
QProgressDialog progress("正在处理...", "取消", 0, 100, this);
progress.setWindowModality(Qt::WindowModal);   // 模态,阻塞父窗口
for (int i = 0; i <= 100; i++) {
    progress.setValue(i);
    if (progress.wasCanceled()) break;
    QThread::msleep(50);
}

18. 自定义对话框

复制代码
class MyDialog : public QDialog {
    Q_OBJECT
public:
    MyDialog(QWidget *parent = nullptr) : QDialog(parent) {
        QVBoxLayout *layout = new QVBoxLayout(this);
        QLineEdit *edit = new QLineEdit();
        QPushButton *okBtn = new QPushButton("确定");
        QPushButton *cancelBtn = new QPushButton("取消");

        layout->addWidget(edit);
        layout->addWidget(okBtn);
        layout->addWidget(cancelBtn);

        connect(okBtn, &QPushButton::clicked, this, &QDialog::accept);   // 结果=Accepted
        connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject); // 结果=Rejected
    }
};

// 使用
MyDialog dlg;
if (dlg.exec() == QDialog::Accepted) {
    qDebug() << "用户点了确定";
}

19. 布局详解

复制代码
// 边距
layout->setContentsMargins(10, 10, 10, 10);   // 左、上、右、下
layout->setSpacing(8);                         // 控件之间的间距

// 弹簧
layout->addStretch();          // 添加弹性空间
layout->insertStretch(0);     // 在位置0插入弹性空间

// 对齐
layout->addWidget(btn, 0, Qt::AlignRight);   // 右对齐

// 拉伸因子
QHBoxLayout *hLayout = new QHBoxLayout();
hLayout->addWidget(leftWidget,  1);   // 占1份
hLayout->addWidget(rightWidget, 2);   // 占2份(右边是左边的两倍宽)

// QFormLayout:表单布局(标签+输入框)
QFormLayout *form = new QFormLayout();
form->addRow("IP地址:", ipEdit);
form->addRow("端口号:", portEdit);
form->addRow("密码:", passwordEdit);

// QStackedLayout:叠放布局(同一时间只显示一个)
QStackedLayout *stack = new QStackedLayout();
stack->addWidget(page1);   // 索引0
stack->addWidget(page2);   // 索引1
stack->setCurrentIndex(1); // 显示page2

20. QSS 样式表(类似 CSS)

复制代码
// 全局样式
qApp->setStyleSheet("QPushButton { background: #333; color: white; border-radius: 5px; }");

// 单个控件样式
btn->setStyleSheet("background-color: red; color: white; padding: 10px;");

// 选择器
/*
QPushButton#myBtn { }        id选择器
QPushButton:hover { }        悬停状态
QPushButton:pressed { }      按下状态
QPushButton:checked { }      选中状态(ToggleButton)
QLineEdit:focus { }          获得焦点状态
QComboBox::drop-down { }     子控件选择器
*/

常见属性

复制代码
background-color: #333;        /* 背景色 */
color: white;                  /* 文字颜色 */
border: 1px solid #666;        /* 边框 */
border-radius: 5px;            /* 圆角 */
padding: 10px;                 /* 内边距 */
margin: 5px;                   /* 外边距 */
font-size: 14px;               /* 字号 */
font-weight: bold;             /* 粗体 */
font-family: "Microsoft YaHei"; /* 字体 */

21. UI 文件(Designer)

复制代码
// 使用UI文件
#include "ui_mainwindow.h"

class MainWindow : public QMainWindow {
    Ui::MainWindow *ui;
};

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);           // 加载UI文件定义的界面
    // UI里的控件通过 ui->控件名 访问
    ui->pushButton->setText("点击我");
    connect(ui->lineEdit, &QLineEdit::textChanged, this, &MyClass::onTextChanged);
}

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

22. 自定义控件

复制代码
// 继承已有控件
class MyButton : public QPushButton {
    Q_OBJECT
public:
    MyButton(const QString &text, QWidget *parent = nullptr)
        : QPushButton(text, parent)
    {
        setStyleSheet("background: #1f7a63; color: white; border-radius: 10px;");
    }
protected:
    void paintEvent(QPaintEvent *event) override {
        QPushButton::paintEvent(event);   // 先画默认样式
        // 再画自己的东西
        QPainter painter(this);
        painter.setPen(Qt::red);
        painter.drawText(rect(), Qt::AlignCenter, "自定义");
    }
};

// 继承QWidget完全自定义
class MyChart : public QWidget {
    Q_OBJECT
public:
    explicit MyChart(QWidget *parent = nullptr) : QWidget(parent) {}
protected:
    void paintEvent(QPaintEvent *event) override {
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);  // 抗锯齿
        painter.setPen(QPen(Qt::blue, 2));
        painter.drawLine(0, 0, width(), height());      // 画线
        painter.setBrush(Qt::red);
        painter.drawEllipse(50, 50, 30, 30);            // 画圆
    }
};

23. QPainter 绘图

复制代码
void MyWidget::paintEvent(QPaintEvent *) {
    QPainter painter(this);

    // 设置画笔(线条)
    QPen pen(Qt::red, 2, Qt::SolidLine);   // 红色、2像素、实线
    painter.setPen(pen);

    // 设置画刷(填充)
    QBrush brush(Qt::blue, Qt::SolidPattern);
    painter.setBrush(brush);

    // 画各种形状
    painter.drawLine(10, 10, 100, 100);              // 线
    painter.drawRect(10, 10, 80, 60);                // 矩形
    painter.drawEllipse(10, 10, 80, 60);             // 椭圆
    painter.drawRoundedRect(10, 10, 80, 60, 10, 10); // 圆角矩形
    painter.drawText(10, 30, "Hello");               // 文字
    painter.drawImage(10, 10, QImage("logo.png"));   // 图片

    // 坐标变换
    painter.translate(100, 100);    // 平移原点
    painter.rotate(45);             // 旋转45度
    painter.scale(2, 2);            // 放大2倍
}

24. QPainterPath(复杂路径)

复制代码
QPainterPath path;
path.moveTo(0, 0);           // 起点
path.lineTo(100, 0);         // 画线到
path.lineTo(100, 100);
path.lineTo(0, 100);
path.closeSubpath();         // 闭合

path.addEllipse(50, 50, 40, 40);   // 加一个圆

painter.drawPath(path);
painter.fillPath(path, QBrush(Qt::blue));

四、事件系统

25. 事件循环

复制代码
int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    // a.exec() 就是事件循环
    // 无限循环,不断从系统消息队列取事件,分发给对应的对象
    return a.exec();
}

事件处理流程

复制代码
用户操作(点击/按键/鼠标移动)
    ↓
操作系统产生消息
    ↓
QApplication::exec() 取出消息
    ↓
QApplication::notify() 分发
    ↓
QWidget::event() 接收
    ↓
具体处理函数(mousePressEvent/keyPressEvent)

26. 重写事件处理函数

复制代码
class MyWidget : public QWidget {
protected:
    // 鼠标按下
    void mousePressEvent(QMouseEvent *event) override {
        QPoint pos = event->pos();             // 鼠标位置(相对控件)
        QPoint globalPos = event->globalPos(); // 全局位置
        Qt::MouseButton btn = event->button(); // 哪个按钮

        if (btn == Qt::LeftButton) {
            qDebug() << "左键按下在" << pos;
        }
    }

    // 鼠标移动
    void mouseMoveEvent(QMouseEvent *event) override {
        // 需要先调用 setMouseTracking(true) 才能持续接收
        qDebug() << "鼠标移动到" << event->pos();
    }

    // 鼠标释放
    void mouseReleaseEvent(QMouseEvent *event) override {
        qDebug() << "鼠标释放";
    }

    // 鼠标双击
    void mouseDoubleClickEvent(QMouseEvent *event) override {
        qDebug() << "双击";
    }

    // 滚轮
    void wheelEvent(QWheelEvent *event) override {
        int delta = event->angleDelta().y();   // 正值=上滚,负值=下滚
        qDebug() << "滚轮:" << delta;
    }

    // 键盘按下
    void keyPressEvent(QKeyEvent *event) override {
        int key = event->key();                // 按键代码
        QString text = event->text();          // 按键字符
        Qt::KeyboardModifiers mods = event->modifiers(); // 修饰键

        if (key == Qt::Key_Escape) {
            close();
        }
        if (mods & Qt::ControlModifier && key == Qt::Key_S) {
            save();    // Ctrl+S
        }
    }

    // 键盘释放
    void keyReleaseEvent(QKeyEvent *event) override {}

    // 窗口大小改变
    void resizeEvent(QResizeEvent *event) override {
        QSize oldSize = event->oldSize();
        QSize newSize = event->size();
        qDebug() << "大小从" << oldSize << "变到" << newSize;
    }

    // 窗口关闭
    void closeEvent(QCloseEvent *event) override {
        int ret = QMessageBox::question(this, "确认", "确定退出?");
        if (ret == QMessageBox::Yes) {
            event->accept();    // 允许关闭
        } else {
            event->ignore();    // 取消关闭
        }
    }

    // 窗口显示/隐藏
    void showEvent(QShowEvent *event) override {
        qDebug() << "窗口显示了";
    }

    void hideEvent(QHideEvent *event) override {
        qDebug() << "窗口隐藏了";
    }

    // 焦点变化
    void focusInEvent(QFocusEvent *event) override {
        qDebug() << "获得焦点";
    }

    void focusOutEvent(QFocusEvent *event) override {
        qDebug() << "失去焦点";
    }

    // 拖放
    void dragEnterEvent(QDragEnterEvent *event) override {
        if (event->mimeData()->hasUrls()) {
            event->acceptProposedAction();    // 接受拖放
        }
    }

    void dropEvent(QDropEvent *event) override {
        QList<QUrl> urls = event->mimeData()->urls();
        for (const QUrl &url : urls) {
            qDebug() << "拖入文件:" << url.toLocalFile();
        }
    }

    // 绘图(最重要)
    void paintEvent(QPaintEvent *event) override {
        QPainter painter(this);
        // ... 绘图代码
    }
};

27. 定时器事件(另一种定时器方式)

复制代码
class MyWidget : public QWidget {
    int mTimerId;

    void showEvent(QShowEvent *) override {
        mTimerId = startTimer(1000);   // 每1000ms触发一次
    }

    void timerEvent(QTimerEvent *event) override {
        if (event->timerId() == mTimerId) {
            qDebug() << "定时触发";
        }
    }

    void hideEvent(QHideEvent *) override {
        killTimer(mTimerId);   // 停止定时器
    }
};

和 QTimer 的区别

QTimer timerEvent
使用方式 对象+信号槽 重写虚函数
适合场景 通用 同一对象需要多个定时器时
灵活性 高(可连接lambda) 低(只能用timerId区分)

五、文件和IO

28. QFile 详细

复制代码
// 读文件
QFile file("data.txt");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
    qDebug() << "打开失败:" << file.errorString();
    return;
}
QByteArray data = file.readAll();     // 读全部
// 或逐行读
while (!file.atEnd()) {
    QByteArray line = file.readLine();
    qDebug() << line.trimmed();
}
file.close();

// 写文件
QFile file("output.txt");
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
    file.write("Hello\n");
    file.write("World\n");
    file.close();
}

// 追加写入
QFile file("log.txt");
if (file.open(QIODevice::Append | QIODevice::Text)) {
    file.write("追加的内容\n");
    file.close();
}

// 二进制读写
QFile file("data.bin");
file.open(QIODevice::WriteOnly);
QDataStream out(&file);
out << (qint32)42 << (double)3.14 << QString("hello");
file.close();

29. QTextStream / QDataStream

复制代码
// QTextStream:文本读写,自动处理编码
QFile file("data.txt");
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream out(&file);
out.setCodec("UTF-8");          // 设置编码
out << "你好" << Qt::endl;
out << "速度:" << 120.5 << "km/h" << Qt::endl;
file.close();

// 读取
file.open(QIODevice::ReadOnly | QIODevice::Text);
QTextStream in(&file);
while (!in.atEnd()) {
    QString line = in.readLine();
}
file.close();

// QDataStream:二进制读写,跨平台字节序
QFile file("data.bin");
file.open(QIODevice::WriteOnly);
QDataStream out(&file);
out.setVersion(QDataStream::Qt_5_15);   // 指定版本,保证兼容性
out << (quint16)12345 << QString("test");
file.close();

30. QDir 详细

复制代码
QDir dir;
dir.exists();                           // 当前目录是否存在
dir.mkpath("config/logs");              // 递归创建目录
dir.mkdir("newFolder");                 // 创建单层目录
dir.remove("file.txt");                 // 删除文件
dir.rename("old.txt", "new.txt");       // 重命名
dir.cd("subfolder");                    // 进入子目录
dir.cdUp();                             // 返回上层目录
dir.absolutePath();                     // 绝对路径
dir.entryList();                        // 所有文件和目录名
dir.entryList(QDir::Files);             // 只要文件
dir.entryList(QDir::Dirs);              // 只要目录
dir.entryList({"*.cpp", "*.h"});        // 过滤后缀
dir.entryInfoList();                    // 返回 QFileInfoList

// 递归遍历
QDirIterator it("src", {"*.cpp"}, QDir::Files, QDirIterator::Subdirectories);
while (it.hasNext()) {
    qDebug() << it.next();   // 输出所有子目录下的cpp文件
}

31. QFileInfo

复制代码
QFileInfo info("/path/to/file.txt");
info.exists();           // true
info.fileName();         // "file.txt"
info.baseName();         // "file"   (不含后缀)
info.suffix();           // "txt"    (后缀)
info.completeSuffix();   // 后缀(如 "tar.gz" 返回 "tar.gz")
info.absolutePath();     // "/path/to"
info.absoluteFilePath(); // "/path/to/file.txt"
info.size();             // 文件大小(字节)
info.birthTime();        // 创建时间
info.lastModified();     // 最后修改时间
info.isDir();            // false
info.isFile();           // true
info.isExecutable();     // 是否可执行
info.isReadable();       // 是否可读
info.isWritable();       // 是否可写

32. QTemporaryFile / QTemporaryDir

复制代码
// 临时文件(程序退出自动删除)
QTemporaryFile file;
if (file.open()) {
    file.write("临时数据");
    QString path = file.fileName();   // 临时文件路径
}
// file析构时自动删除

// 指定前缀和后缀
QTemporaryFile file("report_XXXXXX.xlsx");   // XXXXXX会被替换为随机字符

33. QStandardPaths(标准路径)

复制代码
QString home = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
// C:/Users/用户名

QString desktop = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
// C:/Users/用户名/Desktop

QString appData = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
// C:/Users/用户名/AppData/Roaming/应用名

QString temp = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
// 临时目录

QString doc = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
// C:/Users/用户名/Documents

六、网络

34. QTcpSocket / QTcpServer

复制代码
// TCP服务端
class Server : public QObject {
    QTcpServer *mServer;
public:
    Server() {
        mServer = new QTcpServer(this);
        connect(mServer, &QTcpServer::newConnection, this, &Server::onNewConnection);
        mServer->listen(QHostAddress::Any, 8080);
    }

    void onNewConnection() {
        QTcpSocket *socket = mServer->nextPendingConnection();
        connect(socket, &QTcpSocket::readyRead, this, [socket]() {
            QByteArray data = socket->readAll();
            socket->write("收到: " + data);
        });
        connect(socket, &QTcpSocket::disconnected, socket, &QTcpSocket::deleteLater);
    }
};

// TCP客户端
QTcpSocket *socket = new QTcpSocket(this);
socket->connectToHost("192.168.0.1", 8080);
connect(socket, &QTcpSocket::connected, this, []() {
    qDebug() << "连接成功";
});
connect(socket, &QTcpSocket::readyRead, this, [socket]() {
    QByteArray data = socket->readAll();
    qDebug() << "收到:" << data;
});
socket->write("Hello Server");

35. QUdpSocket

复制代码
QUdpSocket *socket = new QUdpSocket(this);
socket->bind(QHostAddress::Any, 17224);    // 绑定端口

connect(socket, &QUdpSocket::readyRead, this, [socket]() {
    while (socket->hasPendingDatagrams()) {
        QByteArray data(socket->pendingDatagramSize(), 0);
        QHostAddress sender;
        quint16 senderPort;
        socket->readDatagram(data.data(), data.size(), &sender, &senderPort);
        qDebug() << "收到" << sender.toString() << senderPort << data;
    }
});

// 发送
QByteArray data = "Hello";
socket->writeDatagram(data, QHostAddress("239.255.0.130"), 17224);

// 加入组播组
socket->joinMulticastGroup(QHostAddress("239.255.0.130"));

36. QNetworkAccessManager(HTTP)

复制代码
QNetworkAccessManager *manager = new QNetworkAccessManager(this);

// GET请求
QNetworkReply *reply = manager->get(QNetworkRequest(QUrl("https://api.example.com/data")));
connect(reply, &QNetworkReply::finished, this, [reply]() {
    QByteArray data = reply->readAll();
    qDebug() << "响应:" << data;
    reply->deleteLater();
});

// POST请求
QNetworkRequest request(QUrl("https://api.example.com/login"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QByteArray body = R"({"user":"admin","pass":"123"})";
QNetworkReply *reply = manager->post(request, body);

// 下载文件
QNetworkReply *reply = manager->get(QNetworkRequest(QUrl("https://example.com/file.zip")));
QFile *file = new QFile("file.zip");
file->open(QIODevice::WriteOnly);
connect(reply, &QNetworkReply::readyRead, this, [reply, file]() {
    file->write(reply->readAll());
});
connect(reply, &QNetworkReply::finished, this, [reply, file]() {
    file->close();
    file->deleteLater();
    reply->deleteLater();
});

七、数据库

37. SQLite

复制代码
// 打开数据库
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("app.db");
if (!db.open()) {
    qDebug() << "打开失败:" << db.lastError().text();
}

// 执行SQL
QSqlQuery query;
query.exec("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)");

// 插入
query.prepare("INSERT INTO users (name, age) VALUES (?, ?)");
query.addBindValue("张三");
query.addBindValue(25);
query.exec();

// 或用exec直接执行
query.exec("INSERT INTO users (name, age) VALUES ('李四', 30)");

// 查询
query.exec("SELECT id, name, age FROM users");
while (query.next()) {
    int id = query.value(0).toInt();
    QString name = query.value(1).toString();
    int age = query.value(2).toInt();
    qDebug() << id << name << age;
}

// 参数化查询(防SQL注入)
query.prepare("SELECT * FROM users WHERE name = :name AND age > :age");
query.bindValue(":name", "张三");
query.bindValue(":age", 20);
query.exec();

38. QSqlTableModel(表格模型)

复制代码
QSqlTableModel *model = new QSqlTableModel(this, db);
model->setTable("users");
model->select();   // 加载数据

// 绑定到表格视图
tableView->setModel(model);

// 修改后自动提交到数据库
model->setEditStrategy(QSqlTableModel::OnFieldChange);

// 过滤
model->setFilter("age > 20");
model->setSort(1, Qt::DescendingOrder);   // 按第1列降序

八、多线程深入

39. QThreadPool + QRunnable

复制代码
class MyTask : public QRunnable {
    void run() override {
        // 在线程池里执行
        qDebug() << "任务执行中" << QThread::currentThread();
    }
};

// 使用
MyTask *task = new MyTask();
task->setAutoDelete(true);   // 执行完自动删除
QThreadPool::globalInstance()->start(task);

// 设置线程池最大线程数
QThreadPool::globalInstance()->setMaxThreadCount(4);

和 QThread 的区别

QThread QThreadPool + QRunnable
线程数量 手动创建,一个任务一个线程 线程池自动管理,复用线程
适合场景 长期运行的任务(TRDP轮询) 大量短期任务(批量处理)

40. QtConcurrent(简洁的并行计算)

复制代码
#include <QtConcurrent>

// 在另一个线程执行函数
QFuture<void> future = QtConrun::run([]() {
    // 耗时计算
    int sum = 0;
    for (int i = 0; i < 1000000; i++) sum += i;
    qDebug() << "结果:" << sum;
});

// 并行处理列表
QList<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
QFuture<void> future = QtConcurrent::map(numbers, [](int &n) {
    n = n * n;   // 每个元素平方
});

// 并行映射(有返回值)
QStringList files = {"a.txt", "b.txt", "c.txt"};
QFuture<QString> future = QtConcurrent::mapped(files, [](const QString &file) {
    QFile f(file);
    f.open(QIODevice::ReadOnly);
    return f.readAll();
});

// 等待结果
future.waitForFinished();
QList<QString> results = future.results();

41. QFuture / QFutureWatcher

复制代码
QFutureWatcher<void> *watcher = new QFutureWatcher<void>(this);

// 监听进度
connect(watcher, &QFutureWatcher<void::progressValueChanged, this, [watcher](int value) {
    progressBar->setValue(value);
});

// 监听完成
connect(watcher, &QFutureWatcher<void>::finished, this, []() {
    qDebug() << "全部完成";
});

// 启动并行任务
QFuture<void> future = QtConcurrent::run([]() {
    // 耗时操作
});
watcher->setFuture(future);

42. 线程间通信方式汇总

复制代码
// 方式1:信号槽(最常用)
connect(sender, &Sender::signal, receiver, &Receiver::slot, Qt::QueuedConnection);

// 方式2:invokeMethod
QMetaObject::invokeMethod(worker, "doWork", Qt::QueuedConnection);

// 方式3:自定义事件
QCoreApplication::postEvent(receiver, new MyEvent());

// 方式4:共享内存
QSharedMemory sharedMem("MySharedMemory");
sharedMem.create(1024);   // 创建1024字节共享内存
sharedMem.lock();
memcpy(sharedMem.data(), "hello", 5);
sharedMem.unlock();

九、QML 深入

43. QML 基本类型

复制代码
property int count: 10
property real price: 9.99       // 浮点数
property string name: "hello"
property bool visible: true
property url link: "https://example.com"
property color bg: "#333333"
property date birthday: "2026-05-27"
property var anything: null     // 万能类型
property var list: [1, 2, 3]    // 数组
property var obj: ({a: 1, b: 2}) // 对象

44. 锚点布局(Anchors)

复制代码
Rectangle {
    id: parent
    width: 400; height: 300

    Rectangle {
        id: child
        width: 100; height: 50

        // 锚点布局
        anchors.centerIn: parent          // 居中
        anchors.top: parent.top           // 顶部对齐
        anchors.left: parent.left         // 左边对齐
        anchors.right: parent.right       // 右边对齐
        anchors.bottom: parent.bottom     // 底部对齐
        anchors.fill: parent              // 填满父容器(等同于上面四个全设)
        anchors.margins: 10               // 四周边距10
        anchors.topMargin: 20             // 只设置上边距
        anchors.horizontalCenter: parent.horizontalCenter  // 水平居中
        anchors.verticalCenter: parent.verticalCenter      // 垂直居中

        // 相对其他控件定位
        anchors.top: otherItem.bottom     // 我的顶部 = 另一个的底部(放在它下面)
        anchors.left: otherItem.right     // 我的左边 = 另一个的右边(放在它右边)
        anchors.topMargin: 8              // 间距8
    }
}

45. 动画(Animation)

复制代码
// 属性动画
Rectangle {
    id: rect
    width: 100; height: 100; color: "red"

    // 点击后宽度变200,带动画
    MouseArea {
        anchors.fill: parent
        onClicked: rect.width = 200
    }

    Behavior on width {
        NumberAnimation { duration: 300; easing.type: Easing.InOutQuad }
    }
}

// 显式动画
NumberAnimation {
    id: anim
    target: rect
    property: "x"
    from: 0; to: 200
    duration: 500
    easing.type: Easing.OutBounce
}
// 触发
onClicked: anim.start()

// 顺序动画
SequentialAnimation {
    NumberAnimation { target: rect; property: "x"; to: 200; duration: 300 }
    NumberAnimation { target: rect; property: "y"; to: 200; duration: 300 }
    ColorAnimation { target: rect; property: "color"; to: "blue"; duration: 200 }
}

// 并行动画
ParallelAnimation {
    NumberAnimation { target: rect; property: "x"; to: 200; duration: 300 }
    NumberAnimation { target: rect; property: "y"; to: 200; duration: 300 }
}

// 循环动画
SequentialAnimation {
    loops: Animation.Infinite
    NumberAnimation { target: rect; property: "opacity"; to: 0; duration: 500 }
    NumberAnimation { target: rect; property: "opacity"; to: 1; duration: 500 }
}

46. 状态(States)和过渡(Transitions)

复制代码
Rectangle {
    id: rect
    width: 100; height: 100; color: "red"

    states: [
        State {
            name: "expanded"
            PropertyChanges { target: rect; width: 300; color: "blue" }
        },
        State {
            name: "normal"
            PropertyChanges { target: rect; width: 100; color: "red" }
        }
    ]

    transitions: Transition {
        NumberAnimation { properties: "width"; duration: 300 }
        ColorAnimation { duration: 300 }
    }

    MouseArea {
        anchors.fill: parent
        onClicked: rect.state = rect.state === "expanded" ? "normal" : "expanded"
    }
}

47. 列表视图(ListView)

复制代码
ListView {
    width: 200; height: 300
    model: myModel              // 数据源

    delegate: Rectangle {       // 每一项怎么显示
        width: parent.width
        height: 40
        color: index % 2 === 0 ? "#222" : "#333"

        Text {
            text: model.display    // model.xxx 取数据
            color: "white"
            anchors.centerIn: parent
        }

        MouseArea {
            anchors.fill: parent
            onClicked: console.log("点击了第" + index + "项")
        }
    }

    highlight: Rectangle { color: "#1f7a63" }   // 选中项高亮
    currentIndex: 0                               // 默认选中第0项
}

48. Repeater(简单列表)

复制代码
Column {
    Repeater {
        model: ["IP地址", "端口", "协议"]
        delegate: Text {
            text: modelData      // modelData 取当前项
            color: "white"
        }
    }
}

49. Timer(QML定时器)

复制代码
Timer {
    id: timer
    interval: 1000       // 1000ms
    repeat: true         // 重复触发
    running: false       // 初始不运行

    onTriggered: {
        console.log("每秒触发一次")
    }
}

// 启动
onClicked: timer.start()

// 停止
onClicked: timer.stop()

// 一次性定时器
Timer {
    interval: 2000
    running: true
    onTriggered: console.log("2秒后执行一次")
}

50. Connections 监听任意对象信号

复制代码
Connections {
    target: someObject                // 监听谁
    enabled: root.visible             // 只在可见时监听

    function onDataReady(data) {      // on + 信号名(首字母大写)
        console.log(data)
    }

    function onErrorOccurred(msg) {
        errorLabel.text = msg
    }
}

51. QML 里调用 JavaScript

复制代码
// 内联函数
function calculate(a, b) {
    return a + b
}

// 调用
onClicked: {
    var result = calculate(3, 5)
    console.log(result)    // 8
}

// 外部JS文件
// utils.js
function formatTime(date) {
    return Qt.formatDateTime(date, "hh:mm:ss")
}

// QML里引用
import "utils.js" as Utils
onClicked: console.log(Utils.formatTime(new Date()))

52. Qt Quick Controls 2 控件

复制代码
import QtQuick.Controls 2.12

Button { text: "按钮" }
ToolButton { icon.source: "qrc:/icon.png" }
RoundButton { text: "+" }

TextField { placeholderText: "输入文字" }
TextArea { text: "多行文本" }
SpinBox { from: 0; to: 100; value: 50 }
Slider { from: 0; to: 100; value: 50 }
Switch { checked: true }
CheckBox { text: "选项"; checked: false }
RadioButton { text: "选项A"; checked: true }
ComboBox { model: ["A", "B", "C"] }
ProgressBar { value: 0.5 }
BusyIndicator { running: true }    // 转圈加载指示器

Menu {
    title: "文件"
    Action { text: "新建"; onTriggered: console.log("新建") }
    Action { text: "打开"; onTriggered: console.log("打开") }
    MenuSeparator {}
    Action { text: "退出"; onTriggered: Qt.quit() }
}

Dialog {
    title: "确认"
    standardButtons: Dialog.Ok | Dialog.Cancel
    Label { text: "确定要退出吗?" }
    onAccepted: Qt.quit()
}

SwipeView {
    currentIndex: 0
    Page { Label { text: "第一页" } }
    Page { Label { text: "第二页" } }
}

Drawer {
    edge: Qt.LeftEdge
    width: 200; height: parent.height
    Label { text: "侧边栏" }
}

十、高级特性

53. 插件系统

复制代码
// 定义接口
class IPlugin {
public:
    virtual ~IPlugin() {}
    virtual QString name() = 0;
    virtual void execute() = 0;
};
Q_DECLARE_INTERFACE(IPlugin, "com.myapp.IPlugin/1.0")

// 实现插件
class MyPlugin : public QObject, public IPlugin {
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "com.myapp.IPlugin/1.0")
    Q_INTERFACES(IPlugin)
public:
    QString name() override { return "MyPlugin"; }
    void execute() override { qDebug() << "插件执行"; }
};

// 加载插件
QDir dir("plugins");
for (const QString &fileName : dir.entryList({"*.dll", "*.so"})) {
    QPluginLoader loader(dir.absoluteFilePath(fileName));
    QObject *plugin = loader.instance();
    if (plugin) {
        IPlugin *iface = qobject_cast<IPlugin*>(plugin);
        if (iface) {
            iface->execute();
        }
    }
}

54. 国际化(i18n)

复制代码
// C++代码里
QLabel *label = new QLabel(tr("你好"));    // tr() 标记需要翻译的字符串
qDebug() << tr("当前版本: %1").arg(version);

// QML代码里
Label { text: qsTr("你好") }
Label { text: qsTr("共 %1 条记录").arg(count) }

// 加载翻译文件
QTranslator translator;
if (translator.load("app_zh_CN.qm")) {
    qApp->installTranslator(&translator);
}

// 工具流程
// 1. lupdate 提取所有 tr() 字符串 → 生成 .ts 文件
// 2. Linguist 翻译 .ts 文件
// 3. lrelease 编译 .ts → .qm 文件

55. QLoggingCategory(日志分类)

复制代码
// 定义分类
Q_LOGGING_CATEGORY(networkLog, "app.network")
Q_LOGGING_CATEGORY(uiLog, "app.ui")

// 使用
qCDebug(networkLog) << "网络连接成功";
qCWarning(uiLog) << "界面异常";

// 运行时设置哪些分类输出
QLoggingCategory::setFilterRules("app.network.debug=true\napp.ui.debug=false");

56. Q_ENUM / Q_FLAG(枚举注册)

复制代码
class MyClass : public QObject {
    Q_OBJECT
public:
    enum Status { Idle, Running, Error };
    Q_ENUM(Status)    // 注册到元对象系统

    enum Flags { Read = 1, Write = 2, Execute = 4 };
    Q_DECLARE_FLAGS(Permissions, Flags)
    Q_FLAG(Permissions)
};

// 现在可以
MyClass::Status s = MyClass::Running;
QMetaEnum metaEnum = QMetaEnum::fromType<MyClass::Status>();
metaEnum.valueToKey(s);        // "Running"
metaEnum.keyToValue("Error");  // 2

57. QCommandLineParser(命令行参数)

复制代码
QCommandLineParser parser;
parser.setApplicationDescription("TRDP工作台");
parser.addHelpOption();
parser.addVersionOption();

QCommandLineOption configOpt("c", "配置文件", "文件名", "config.ini");
parser.addOption(configOpt);
parser.process(app);

QString configFile = parser.value(configOpt);   // "config.ini"
// 命令行: TrdpModel.exe -c config-beijing.ini

58. QProcess(启动外部程序)

复制代码
QProcess *process = new QProcess(this);

// 启动并等待完成
process->start("cmd", {"/c", "dir"});
process->waitForFinished();
QString output = process->readAllStandardOutput();
QString error = process->readAllStandardError();

// 异步启动
connect(process, &QProcess::readyReadStandardOutput, this, [process]() {
    qDebug() << process->readAllStandardOutput();
});
connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
        this, [](int exitCode, QProcess::ExitStatus status) {
    qDebug() << "退出码:" << exitCode;
});
process->start("python", {"script.py"});

// 启动并忘记(打开文件/URL)
QProcess::startDetached("notepad.exe", {"data.txt"});
QDesktopServices::openUrl(QUrl("https://example.com"));

59. QSerialPort(串口通信)

复制代码
QSerialPort *serial = new QSerialPort(this);
serial->setPortName("COM3");
serial->setBaudRate(QSerialPort::Baud115200);
serial->setDataBits(QSerialPort::Data8);
serial->setParity(QSerialPort::NoParity);
serial->setStopBits(QSerialPort::OneStop);
serial->setFlowControl(QSerialPort::NoFlowControl);

if (serial->open(QIODevice::ReadWrite)) {
    connect(serial, &QSerialPort::readyRead, this, [serial]() {
        QByteArray data = serial->readAll();
        qDebug() << "收到:" << data.toHex();
    });

    serial->write(QByteArray::fromHex("01 02 03"));
}

// 列出可用串口
for (const QSerialPortInfo &info : QSerialPortInfo::availablePorts()) {
    qDebug() << info.portName() << info.description();
}

60. QTimer 单次和精密定时

复制代码
// 单次定时器
QTimer::singleShot(1000, this, &MyClass::doSomething);

// 单次定时器(lambda)
QTimer::singleShot(1000, []() { qDebug() << "1秒后执行"; });

// 精密定时器
QTimer *timer = new QTimer(this);
timer->setTimerType(Qt::PreciseTimer);   // 精确到毫秒(默认是CoarseTimer,精度约±5%)
timer->setInterval(10);                  // 10ms
timer->start();

// 精度对比
// Qt::PreciseTimer   精确到毫秒级,CPU占用高
// Qt::CoarseTimer    精度约±5%,省电(默认)
// Qt::VeryCoarseTimer 精度约±5%,最小间隔1秒

61. QElapsedTimer(高精度计时)

复制代码
QElapsedTimer timer;
timer.start();

// 做一些事情
for (int i = 0; i < 1000000; i++) { /* ... */ }

qint64 elapsed = timer.elapsed();          // 毫秒
qint64 nanoSec = timer.nsecsElapsed();     // 纳秒
qDebug() << "耗时:" << elapsed << "ms";

62. QSharedPointer / QWeakPointer / QScopedPointer

复制代码
// QSharedPointer:引用计数智能指针
QSharedPointer<MyClass> p1(new MyClass());
QSharedPointer<MyClass> p2 = p1;    // 引用计数=2
p1.clear();                          // 引用计数=1
p2.clear();                          // 引用计数=0,自动delete

// QWeakPointer:弱引用,不影响引用计数
QWeakPointer<MyClass> weak = p1;
QSharedPointer<MyClass> locked = weak.toStrongRef();   // 尝试升级为强引用
if (locked) { /* 还活着 */ }

// QScopedPointer:作用域指针,离开作用域自动释放
{
    QScopedPointer<QFile> file(new QFile("data.txt"));
    file->open(QIODevice::ReadOnly);
    // 离开作用域自动 delete
}

// QScopedArrayPointer:数组版本
QScopedArrayPointer<int> arr(new int[100]);

63. QVariant(万能类型)

复制代码
QVariant v;
v = 42;                    // 存int
v = "hello";               // 存字符串
v = QList<QVariant>{1, 2}; // 存列表
v = QVariantMap{{"a", 1}}; // 存字典

v.toInt();        // 42
v.toString();     // "hello"
v.type();         // QVariant::Int / QVariant::String
v.typeName();     // "int" / "QString"
v.canConvert<int>();  // 能不能转成int

// 和 QVariantMap 配合使用
QVariantMap map;
map["name"] = "张三";
map["age"] = 25;
map["scores"] = QVariantList{90, 85, 92};

64. QJsonDocument / QJsonObject / QJsonArray

复制代码
// 解析JSON
QJsonDocument doc = QJsonDocument::fromJson(jsonData);
QJsonObject obj = doc.object();
QString name = obj["name"].toString();
int age = obj["age"].toInt();
QJsonArray scores = obj["scores"].toArray();

// 构建JSON
QJsonObject obj;
obj["name"] = "张三";
obj["age"] = 25;
QJsonArray arr;
arr.append(90);
arr.append(85);
obj["scores"] = arr;

QJsonDocument doc(obj);
QByteArray json = doc.toJson(QJsonDocument::Indented);   // 格式化输出
// {"age": 25, "name": "张三", "scores": [90, 85]}

65. QXmlStreamReader / QXmlStreamWriter

复制代码
// 读XML
QXmlStreamReader xml(xmlData);
while (!xml.atEnd()) {
    xml.readNext();
    if (xml.isStartElement()) {
        if (xml.name() == "item") {
            QString id = xml.attributes().value("id").toString();
            qDebug() << "item id=" << id;
        }
    }
    if (xml.isCharacters() && !xml.isWhitespace()) {
        qDebug() << "文本:" << xml.text();
    }
}

// 写XML
QXmlStreamWriter xml(&output);
xml.setAutoFormatting(true);
xml.writeStartDocument();
xml.writeStartElement("root");
xml.writeStartElement("item");
xml.writeAttribute("id", "1");
xml.writeTextElement("name", "张三");
xml.writeEndElement();   // item
xml.writeEndElement();   // root
xml.writeEndDocument();

66. QRegularExpression(正则表达式)

复制代码
QRegularExpression re(R"(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})");
// 匹配IP地址

QRegularExpressionMatch match = re.match("服务器IP是192.168.0.103");
if (match.hasMatch()) {
    QString ip = match.captured(0);   // "192.168.0.103"
}

// 全局匹配(找所有)
QRegularExpression re2(R"(\d+)");
QString text = "共123条,已处理45条";
QRegularExpressionMatchIterator it = re2.globalMatch(text);
while (it.hasNext()) {
    QRegularExpressionMatch m = it.next();
    qDebug() << m.captured(0);   // "123" "45"
}

// 替换
QString result = text.replace(re2, "XX");
// "共XX条,已处理XX条"

// 捕获组
QRegularExpression re3(R"((\w+)@(\w+)\.(\w+))");
QRegularExpressionMatch m = re3.match("user@example.com");
m.captured(0);   // "user@example.com"  整个匹配
m.captured(1);   // "user"              第1个括号
m.captured(2);   // "example"           第2个括号
m.captured(3);   // "com"               第3个括号

67. QHostAddress / QNetworkInterface

复制代码
// 解析IP地址
QHostAddress addr("192.168.0.1");
quint32 ipv4 = addr.toIPv4Address();   // 3232235521

// 列出所有网卡和IP
for (const QNetworkInterface &iface : QNetworkInterface::allInterfaces()) {
    qDebug() << "网卡:" << iface.humanReadableName();
    for (const QNetworkAddressEntry &entry : iface.addressEntries()) {
        qDebug() << "  IP:" << entry.ip().toString();
        qDebug() << "  子网掩码:" << entry.netmask().toString();
    }
}

十一、调试和工具

68. qDebug / qInfo / qWarning / qCritical / qFatal

复制代码
qDebug() << "调试信息";          // 默认输出到控制台
qInfo() << "普通信息";
qWarning() << "警告";
qCritical() << "严重错误";
qFatal() << "致命错误";          // 输出后程序终止

// 自定义格式
qSetMessagePattern("[%{type}] %{file}:%{line} - %{message}");
// 输出: [Debug] main.cpp:10 - 调试信息

69. 自定义消息处理器

复制代码
void myMessageHandler(QtMsgType type, const QMessageLogContext &ctx, const QString &msg) {
    QFile file("log.txt");
    file.open(QIODevice::Append | QIODevice::Text);
    QTextStream out(&file);
    out << QDateTime::currentDateTime().toString("hh:mm:ss.zzz") << " ";
    out << msg << "\n";
}

int main(int argc, char *argv[]) {
    qInstallMessageHandler(myMessageHandler);   // 安装自定义处理器
    // 之后所有 qDebug/qWarning 等都走这个函数
}

70. 断言和测试

复制代码
// 断言
Q_ASSERT(x > 0);                          // 失败→崩溃+打印位置
Q_ASSERT_X(list.size() > 0, "func", "空列表");  // 失败→崩溃+自定义消息

// 单元测试
#include <QtTest/QTest>

class TestMyClass : public QObject {
    Q_OBJECT
private slots:
    void testAdd() {
        QCOMPARE(1 + 1, 2);               // 相等
        QVERIFY(2 > 1);                   // 为真
        QVERIFY2(list.size() > 0, "列表为空"); // 为真+失败消息
        QEXPECT_FAIL("", "已知bug", Continue);  // 预期失败
        QCOMPARE(actual, expected);
    }
    void testInit() {
        // 每个slot是一个测试用例
    }
};

QTEST_MAIN(TestMyClass)                   // 生成main函数
#include "test.moc"

71. 性能分析

复制代码
// 方法1:QElapsedTimer
QElapsedTimer timer;
timer.start();
// ... 要测的代码
qDebug() << "耗时:" << timer.elapsed() << "ms";

// 方法2:QScopedProfiler(Qt6)
QScopedProfiler p("MyOperation");
// ... 要测的代码
// 析构时自动输出耗时

// 方法3:QLatin1String vs QStringLiteral
// QLatin1String:编译期,零拷贝,适合ASCII比较
if (str == QLatin1String("config")) { /* 快 */ }
// QStringLiteral:编译期创建QString,避免运行时转换
if (str == QStringLiteral("config")) { /* 也快 */ }
// 直接写 "config":运行时从const char*转换,慢
if (str == "config") { /* 慢 */ }

十二、Qt 的坑和最佳实践

72. 常见坑

复制代码
// 坑1:信号槽连接后对象被删除
MyClass *obj = new MyClass();
connect(sender, &Sender::signal, obj, &MyClass::slot);
delete obj;
// obj被删除后,连接自动断开,不会崩溃
// 但如果用lambda捕获了裸指针,可能会崩溃
connect(sender, &Sender::signal, this, [obj]() {
    obj->doSomething();   // 危险:obj可能已被删除
});
// 解决:用 QPointer 或 weak_ptr

// 坑2:QueuedConnection下参数生命周期
void send() {
    QByteArray data(1000, 'x');
    emit dataReady(data);
    // data在这里被销毁
    // 如果是QueuedConnection,Qt会自动拷贝data,安全
    // 如果是DirectConnection,不涉及拷贝,也安全
}

// 坑3:Q_OBJECT漏加
// 不加Q_OBJECT:
// - 信号槽连接失败(运行时)
// - dynamic_cast 失败
// - qobject_cast 返回nullptr

// 坑4:事件循环阻塞
void MyWidget::onButtonClick() {
    QThread::sleep(5);    // 界面卡死5秒!
}
// 解决:耗时操作放到子线程

// 坑5:QThread对象本身在主线程
QThread *thread = new QThread();
// thread的所有槽函数在主线程执行(除非moveToThread)
// thread->start()只是启动了run(),thread对象还在主线程

73. 最佳实践

复制代码
// 1. 用智能指针管理内存
QScopedPointer<QFile> file(new QFile("data.txt"));

// 2. 用QPointer跟踪QObject生命周期
QPointer<MyClass> safePtr = obj;
if (safePtr) {        // 检查对象是否还活着
    safePtr->doSomething();
}

// 3. 用deleteLater代替delete
obj->deleteLater();   // 等事件循环处理完再删除,比直接delete安全

// 4. 用QLatin1String做字符串比较
if (key == QLatin1String("config")) { /* 快 */ }

// 5. 用qobject_cast代替dynamic_cast
MyClass *c = qobject_cast<MyClass*>(obj);   // 失败返回nullptr,不用RTTI

// 6. 用const引用传参
void process(const QByteArray &data);   // 不拷贝

// 7. 用QByteArray::fromHex做十六进制转换,不要自己写
QByteArray data = QByteArray::fromHex("FF AB 01");   // 正确
// 不要用 data = "\xFF\xAB\x01"(不可移植)

// 8. 配置文件路径用QCoreApplication::applicationDirPath()
QString path = QCoreApplication::applicationDirPath() + "/config";
// 不要用 QDir::currentPath()(工作目录可能变)

74. Qt5 vs Qt6 主要区别

复制代码
Qt5                          Qt6
────────────────────────────────────────────────────
#include <QApplication>      #include <QApplication>     (一样)
QT += widgets                 QT += widgets              (一样)
QRegExp                       QRegularExpression         (Qt6移除QRegExp)
QTextCodec                    自动UTF-8,不需要了
QDesktopWidget                QScreen
QApplication::desktop()       QGuiApplication::primaryScreen()
Qt::MidButton                 Qt::MiddleButton
QVariant::Type                QMetaType
qmlRegisterType               qmlRegisterType            (一样)
QQuickView                    QQuickView                 (一样)
setResizeMode                 setResizeMode              (一样)
相关推荐
hef2882 小时前
Prism图形设计从入门到精通:外观设置、图层顺序与微调技巧
开发语言
长谷深风1112 小时前
Java 面试高频:反射机制与异常体系全面解析
java·开发语言·面试·exception·java 反射·java 异常·class 对象
fantasy_arch3 小时前
BasicVSR-lite图像画质增强
开发语言·pytorch
Rust语言中文社区3 小时前
【Rust日报】2026-05-24 Secluso v1.0.2 版本发布
开发语言·后端·rust
吃好睡好便好3 小时前
矩阵的加减运算
开发语言·人工智能·学习·线性代数·算法·matlab·矩阵
吃好睡好便好4 小时前
提取矩阵特定多行元素
开发语言·线性代数·算法·matlab·矩阵
Mister西泽4 小时前
C++ Primer Plus 第六版 编程练习题及详细答案
开发语言·c++·学习·visual studio
froginwe114 小时前
Python 循环嵌套
开发语言
@大迁世界4 小时前
AI还替不了的JS能力
开发语言·前端·javascript·人工智能·ecmascript