QT基础


基础组件

QLabel(标签)

功能

显示文本、图像、动态GIF,不支持用户交互。

核心接口

内容类型 设置方法 示例代码
纯文本 setText(QString) ui->label->setText("Hello QT");
富文本 setText(QString)(嵌入HTML) ui->label->setText("<span style='color:red;font-size:16pt'>富文本测试</span>");
静态图像 setPixmap(QPixmap) QPixmap img("./123.jpg"); ui->label->setPixmap(img.scaled(ui->label->size(), Qt::KeepAspectRatio));
动态GIF setMovie(QMovie) QMovie *mov = new QMovie("./234.gif"); mov->setScaledSize(ui->label->size()); ui->label->setMovie(mov); mov->start();
数值 setNum(int/double) ui->label->setNum(123.45);

对齐方式

// 水平居中 + 垂直居中
ui->label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
QLabel各类内容显示效果

QPushButton(按钮)

核心特性

  • 支持快捷键(文本前加&,如&Open对应Alt+O
  • 支持点击、按下、松开等事件

示例代码

// 动态创建按钮
QPushButton *btn = new QPushButton("独立按钮", this);
btn->setGeometry(100, 50, 200, 50); // 位置(x,y) + 尺寸(width,height)
btn->show();

// 按钮事件槽函数
void MainWindow::on_pushButton_clicked() { qDebug() << "按钮被点击"; }
void MainWindow::on_pushButton_pressed() { qDebug() << "按钮被按下"; }
void MainWindow::on_pushButton_released() { qDebug() << "按钮被松开"; }
QPushButton显示效果

QLineEdit(单行输入框)

核心接口

功能 接口 示例
输入回显 setEchoMode(QLineEdit::EchoMode) 密码模式:ui->edit->setEchoMode(QLineEdit::Password);
文本对齐 setAlignment(Qt::AlignFlag) 右对齐:ui->edit->setAlignment(Qt::AlignRight);
占位提示 setPlaceholderText(QString) ui->edit->setPlaceholderText("请输入账号");
获取文本 text() QString msg = ui->edit->text();
设置文本 setText(QString) ui->edit->setText("初始内容");

回显模式枚举

  • QLineEdit::Normal:正常显示(默认)
  • QLineEdit::NoEcho:不显示任何内容
  • QLineEdit::Password:显示密码掩码(如
  • QLineEdit::PasswordEchoOnEdit:编辑时显示原内容,失焦后显示掩码

QTextEdit(文本编辑框)

核心功能

  • 支持富文本(HTML格式)、纯文本
  • 支持多行输入、滚动显示

示例代码

// 富文本设置
ui->textEdit->setHtml("<h2 style='background-color:red;font-family:微软雅黑'>富文本测试</h2>");
// 读取文件内容并显示
QFile file("./test.txt");
if (file.open(QIODevice::ReadOnly)) {
    ui->textEdit->setText(file.readAll());
    file.close();
}

信号与槽

核心概念

Qt 独有的对象间通信机制,无需手动监听事件,通过connect函数关联信号与处理函数(槽)。

关键要求

  • 信号与槽的类必须继承QObject并添加Q_OBJECT
  • 信号只需声明(signals关键字),无需实现
  • 槽函数需声明并实现(public slots关键字)

基本用法

信号与槽声明

class Sender : public QObject {
    Q_OBJECT
signals:
    void sendSignal(); // 无参信号
    void sendSignalArg(QString msg, int value); // 带参信号
};

class Receiver : public QObject {
    Q_OBJECT
public slots:
    void receiveSlot() { qDebug() << "接收无参信号"; }
    void receiveSlotArg(QString msg, int value) { qDebug() << "接收参数:" << msg << value; }
};

连接信号与槽

Sender *sender = new Sender();
Receiver *receiver = new Receiver();

// 无参信号连接
connect(sender, &Sender::sendSignal, receiver, &Receiver::receiveSlot);
// 带参信号连接
connect(sender, &Sender::sendSignalArg, receiver, &Receiver::receiveSlotArg);

发送信号

emit sender->sendSignal(); // 发送无参信号
emit sender->sendSignalArg("测试", 123); // 发送带参信号

连接类型

连接类型 作用 适用场景
Qt::DirectConnection 信号发出时立即执行槽函数(同步) 同一线程
Qt::QueuedConnection 信号放入事件队列,异步执行 跨线程
Qt::BlockingQueuedConnection 跨线程阻塞等待槽函数执行 线程间同步通信
Qt::AutoConnection 自动判断(同线程直接连接,跨线程排队连接) 默认值
Qt::UniqueConnection 避免重复连接 防止槽函数多次执行

信号与槽参数规则

  • 带参信号可连接到无参槽(参数被忽略)
  • 无参信号不能连接到带参槽(编译报错)
  • 参数类型、顺序必须一致(自定义类型需用qRegisterMetaType<Type>("Type")注册)

Lambda 表达式简化槽函数

无需声明槽函数,直接在connect中写处理逻辑:

connect(sender, &Sender::sendSignalArg, [](QString msg, int value) {
    qDebug() << "Lambda接收参数:" << msg << value;
});

QTimer(定时器)

功能

定时发送timeout()信号,实现周期性任务

示例代码

// 创建定时器
QTimer *timer = new QTimer(this);
timer->setInterval(1000); // 间隔1秒
// 连接定时器信号
connect(timer, &QTimer::timeout, this, []() {
    qDebug() << "定时器触发";
});
timer->start(); // 启动定时器
// timer->stop(); // 停止定时器

常见问题

  1. 信号与槽连接失败?

    • 检查是否继承QObject并添加Q_OBJECT
    • 检查函数名是否拼写错误
    • 自定义参数类型是否注册
  2. 槽函数不执行?

    • 检查发送者/接收者对象是否被提前析构
    • 跨线程是否使用QueuedConnection

布局管理器

核心作用

自动管理控件位置与大小,适配窗口缩放,避免手动计算坐标。

基础布局

QHBoxLayout(水平布局)

控件从左到右排列

QWidget *w = new QWidget();
QHBoxLayout *hlay = new QHBoxLayout(w);
hlay->addWidget(new QPushButton("按钮1"));
hlay->addWidget(new QPushButton("按钮2"));
w->show();

QVBoxLayout(垂直布局)

控件从上到下排列

QVBoxLayout *vlay = new QVBoxLayout(w);
vlay->addWidget(new QPushButton("按钮1"));
vlay->addWidget(new QPushButton("按钮2"));
水平/垂直布局效果对比

QGridLayout(网格布局)

多行多列网格排列,支持跨行列

QGridLayout *glay = new QGridLayout(w);
// 行0列0、行0列1、行1列0、行1列1
glay->addWidget(new QPushButton("1"), 0, 0);
glay->addWidget(new QPushButton("2"), 0, 1);
glay->addWidget(new QPushButton("3"), 1, 0);
glay->addWidget(new QPushButton("4"), 1, 1);
// 跨列(行2,列0-1)
glay->addWidget(new QPushButton("跨列按钮"), 2, 0, 1, 2);
QGridLayout网格布局效果

QFormLayout(表单布局)

两列布局(标签+输入控件),适配表单场景

QFormLayout *flay = new QFormLayout(w);
flay->addRow("账号", new QLineEdit());
flay->addRow("密码", new QLineEdit());

高级布局

QStackedLayout(堆栈布局)

同一区域显示多个控件,仅一个可见,支持切换

QStackedLayout *slay = new QStackedLayout(w);
slay->addWidget(new QLabel("页面1"));
slay->addWidget(new QLabel("页面2"));
// 切换页面(索引0-1)
slay->setCurrentIndex(1);

QDockWidget(停靠窗口)

主窗口周围的可停靠/浮动窗口

QMainWindow *mainWin = new QMainWindow();
QDockWidget *dock = new QDockWidget("停靠窗口", mainWin);
dock->setWidget(new QTextEdit());
mainWin->addDockWidget(Qt::RightDockWidgetArea, dock);
mainWin->show();

QSplitter(分割窗口)

可拖拽调整大小的分割区域

QSplitter *splitter = new QSplitter(Qt::Horizontal, w);
splitter->addWidget(new QPushButton("左侧"));
splitter->addWidget(new QTextEdit("右侧"));

布局嵌套技巧

// 垂直布局嵌套水平布局
QVBoxLayout *vlay = new QVBoxLayout(w);
QHBoxLayout *hlay = new QHBoxLayout();
hlay->addWidget(new QPushButton("确定"));
hlay->addWidget(new QPushButton("取消"));
vlay->addWidget(new QLabel("表单区域"));
vlay->addLayout(hlay); // 嵌套水平布局

对话框

对话框分类

类型 特点 打开方式
模态对话框 阻塞父窗口操作,必须关闭才返回 exec()
非模态对话框 不阻塞父窗口,可同时操作 show()

标准对话框(Qt内置)

文件对话框(QFileDialog)

用于选择文件/目录路径

// 打开文件对话框
QString filePath = QFileDialog::getOpenFileName(
    this, "选择文件", "./", "图像文件 (*.png *.jpg);;所有文件 (*.*)"
);
// 保存文件对话框
QString savePath = QFileDialog::getSaveFileName(this, "保存文件", "./test.txt");
QFileDialog打开文件界面

颜色对话框(QColorDialog)

选择颜色

QColor color = QColorDialog::getColor(Qt::red, this, "选择颜色");
ui->label->setStyleSheet(QString("background-color:%1").arg(color.name()));

输入对话框(QInputDialog)

获取用户输入

// 文本输入
QString text = QInputDialog::getText(this, "输入框", "请输入密码", QLineEdit::Password);
// 数值输入
int num = QInputDialog::getInt(this, "数值输入", "请输入年龄", 18, 0, 100);

消息对话框(QMessageBox)

提示信息、确认操作

// 警告对话框
int ret = QMessageBox::warning(this, "警告", "确定删除?", QMessageBox::Yes, QMessageBox::No);
if (ret == QMessageBox::Yes) {
    qDebug() << "执行删除";
}

自定义对话框

示例:模态对话框

// 自定义对话框类
class MyDialog : public QDialog {
    Q_OBJECT
public:
    MyDialog(QWidget *parent = nullptr) : QDialog(parent) {
        setWindowTitle("自定义对话框");
        QVBoxLayout *vlay = new QVBoxLayout(this);
        vlay->addWidget(new QLabel("自定义内容"));
        QPushButton *btn = new QPushButton("确定", this);
        vlay->addWidget(btn);
        connect(btn, &QPushButton::clicked, this, &QDialog::accept);
    }
};

// 打开对话框
MyDialog dialog(this);
if (dialog.exec() == QDialog::Accepted) {
    qDebug() << "对话框确认";
}

文件操作

QFile(文件读写)

核心接口

功能 接口 示例
打开文件 open(QIODevice::OpenMode) 读模式:file.open(QIODevice::ReadOnly)
读取内容 readAll()/readLine() QByteArray data = file.readAll();
写入内容 write(QByteArray) file.write("Hello File");
关闭文件 close() file.close();

示例:文件读写

QFile file("./test.txt");
// 写入文件
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
    file.write("QT文件操作测试\n");
    file.close();
}
// 读取文件
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
    QString content = file.readAll();
    qDebug() << content;
    file.close();
}

QFileInfo(文件信息)

获取文件属性(大小、路径、修改时间等)

QFileInfo info("./test.txt");
qDebug() << "文件名:" << info.fileName();
qDebug() << "绝对路径:" << info.absoluteFilePath();
qDebug() << "文件大小:" << info.size() << "字节";
qDebug() << "最后修改时间:" << info.lastModified().toString();
qDebug() << "是否为文件:" << info.isFile();

QDir(目录操作)

核心功能

  • 遍历目录文件
  • 创建/删除目录

示例代码

QDir dir("./");
// 获取目录下所有文件
QStringList files = dir.entryList(QDir::Files);
qDebug() << "目录文件:" << files;
// 创建目录
if (dir.mkpath("newDir")) {
    qDebug() << "目录创建成功";
}
// 删除目录
dir.rmdir("newDir");

QTextStream(文本流)

简化文本读写,支持格式化输出

QFile file("./test.txt");
if (file.open(QIODevice::ReadWrite | QIODevice::Text)) {
    QTextStream stream(&file);
    // 写入(支持格式化)
    stream << "姓名:" << "张三" << " 年龄:" << 20 << endl;
    // 读取
    QString line;
    while (!stream.atEnd()) {
        line = stream.readLine();
        qDebug() << line;
    }
    file.close();
}

多界面操作

无数据传递

父窗口(QMainWindow)跳转子窗口

// 子窗口构造(QWidget需添加独立窗口标记)
class SubWidget : public QWidget {
public:
    SubWidget(QWidget *parent = nullptr) : QWidget(parent, Qt::Window) {}
};

// 父窗口跳转
void MainWindow::on_pushButton_clicked() {
    SubWidget *sub = new SubWidget(this);
    sub->show();
    this->hide(); // 隐藏父窗口
}

// 子窗口返回父窗口
void SubWidget::on_pushButton_clicked() {
    MainWindow *parent = dynamic_cast<MainWindow*>(this->parentWidget());
    if (parent) {
        parent->show();
        this->close();
    }
}

有数据传递

方法1:公有接口传递

// 子窗口添加接口
class SubWidget : public QWidget {
public:
    void setData(QString msg) { ui->label->setText(msg); }
};

// 父窗口传递数据
SubWidget *sub = new SubWidget(this);
sub->setData(ui->lineEdit->text());
sub->show();

方法2:信号与槽传递

// 父窗口定义信号
signals:
    void sendData(QString msg);

// 子窗口定义槽
public slots:
    void receiveData(QString msg) { ui->label->setText(msg); }

// 连接信号与槽
SubWidget *sub = new SubWidget(this);
connect(this, &MainWindow::sendData, sub, &SubWidget::receiveData);
emit sendData(ui->lineEdit->text()); // 发送数据
sub->show();

样式表(界面美化)

基础语法

模仿CSS,通过setStyleSheet()设置控件样式,语法格式:选择器 { 属性: 值; }

核心选择器

选择器类型 用法 示例
类型选择器 按控件类型匹配 QLabel { color: red; }(所有QLabel变红)
ID选择器 按控件objectName匹配 QPushButton#myBtn { background: blue; }
后代选择器 匹配子控件 QGroupBox QLineEdit { border: 1px solid #000; }
状态选择器 按控件状态匹配 QPushButton:hover { background: lightblue; }(鼠标悬浮)

常用属性

类别 常用属性 示例
颜色 color(前景色)、background-color(背景色) color: #333; background-color: #f5f5f5;
边框 borderborder-radius(圆角) border: 2px solid #000; border-radius: 8px;
字体 font-familyfont-sizefont-weight font: bold 14px "微软雅黑";
内边距/外边距 paddingmargin padding: 6px; margin: 10px;
尺寸 widthheightmin-width width: 200px; min-height: 30px;

资源文件添加

步骤

  1. 新建资源文件(.qrc):Qt Creator → 新建 → Qt Resource File
  2. 添加前缀(如/img
  3. 添加资源文件(图片、图标等)
  4. 样式表中引用:background-image: url(:/img/123.jpg);

示例:按钮美化

ui->pushButton->setStyleSheet(R"(
    QPushButton {
        color: white;
        background-color: #2f54eb;
        border: none;
        border-radius: 4px;
        padding: 8px 16px;
        font-size: 14px;
    }
    QPushButton:hover {
        background-color: #4064ff;
    }
    QPushButton:pressed {
        background-color: #1d39c4;
    }
)");

视图窗口

分类

类型 特点 适用场景
Item-Based 直接操作条目,无需模型 简单列表、表格(QListWidget、QTableWidget)
Model-Based 数据与视图分离,支持动态更新 复杂数据展示(QTreeView、QTableView)

QListWidget(列表视图)

核心操作

// 添加文本条目
ui->listWidget->addItem("条目1");
// 添加带图标准条目
QListWidgetItem *item = new QListWidgetItem(QIcon("./123.jpg"), "带图标条目");
ui->listWidget->addItem(item);
// 添加自定义控件
QWidget *widget = new QWidget();
QHBoxLayout *hlay = new QHBoxLayout(widget);
hlay->addWidget(new QLabel("自定义"));
hlay->addWidget(new QPushButton("按钮"));
QListWidgetItem *item2 = new QListWidgetItem(ui->listWidget);
item2->setSizeHint(widget->size());
ui->listWidget->setItemWidget(item2, widget);

信号与槽

// 双击删除条目
connect(ui->listWidget, &QListWidget::itemDoubleClicked, [=](QListWidgetItem *item) {
    ui->listWidget->takeItem(ui->listWidget->row(item));
});

QTableWidget(表格视图)

核心操作

// 设置行列数
ui->tableWidget->setRowCount(3);
ui->tableWidget->setColumnCount(2);
// 设置表头
ui->tableWidget->setHorizontalHeaderLabels({"列1", "列2"});
// 添加单元格内容
ui->tableWidget->setItem(0, 0, new QTableWidgetItem("单元格(0,0)"));
// 添加按钮控件
QPushButton *btn = new QPushButton("按钮", this);
ui->tableWidget->setCellWidget(0, 1, btn);
// 自动拉伸列
ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
QTableWidget显示效果

QTreeWidget(树状视图)

示例:创建树结构

// 根节点
QTreeWidgetItem *root = new QTreeWidgetItem(ui->treeWidget);
root->setText(0, "根节点");
// 子节点
QTreeWidgetItem *child1 = new QTreeWidgetItem(root);
child1->setText(0, "子节点1");
// 孙子节点
QTreeWidgetItem *grandson = new QTreeWidgetItem(child1);
grandson->setText(0, "孙子节点");
// 展开所有节点
ui->treeWidget->expandAll();

MDI窗口(多文档界面)

示例:创建多子窗口

QMdiArea *mdiArea = new QMdiArea(this);
this->setCentralWidget(mdiArea);

// 创建子窗口1
QMdiSubWindow *sub1 = new QMdiSubWindow();
sub1->setWidget(new QTextEdit("子窗口1"));
sub1->setWindowTitle("文档1");
mdiArea->addSubWindow(sub1);

// 创建子窗口2
QMdiSubWindow *sub2 = new QMdiSubWindow();
sub2->setWidget(new QTextEdit("子窗口2"));
sub2->setWindowTitle("文档2");
mdiArea->addSubWindow(sub2);

// 平铺排列子窗口
mdiArea->tileSubWindows();

事件处理

核心概念

Qt 程序是事件驱动的,所有用户操作(点击、按键、窗口缩放)都会触发对应事件,通过重写事件处理函数响应。

常见事件类型

事件类 触发场景 处理函数
QMouseEvent 鼠标点击、移动、释放 mousePressEvent()mouseMoveEvent()
QKeyEvent 键盘按键按下/松开 keyPressEvent()keyReleaseEvent()
QTimerEvent 定时器触发 timerEvent()
QPaintEvent 窗口重绘 paintEvent()
QCloseEvent 窗口关闭 closeEvent()

鼠标事件示例

void MainWindow::mousePressEvent(QMouseEvent *event) {
    // 左键点击
    if (event->button() == Qt::LeftButton) {
        qDebug() << "鼠标左键点击位置:" << event->pos();
    }
    // 右键点击
    else if (event->button() == Qt::RightButton) {
        qDebug() << "鼠标右键点击";
    }
}

void MainWindow::mouseMoveEvent(QMouseEvent *event) {
    // 按住左键移动
    if (event->buttons() & Qt::LeftButton) {
        qDebug() << "鼠标移动:" << event->pos();
    }
}

键盘事件示例

void MainWindow::keyPressEvent(QKeyEvent *event) {
    switch (event->key()) {
        case Qt::Key_Left:
            this->move(this->x() - 10, this->y()); // 左移窗口
            break;
        case Qt::Key_Right:
            this->move(this->x() + 10, this->y()); // 右移窗口
            break;
        case Qt::Key_Up:
            this->move(this->x(), this->y() - 10); // 上移窗口
            break;
        case Qt::Key_Down:
            this->move(this->x(), this->y() + 10); // 下移窗口
            break;
    }
}

绘图事件(QPaintEvent)

示例:绘制图形

void MainWindow::paintEvent(QPaintEvent *even vt) {
    QPainter painter(this);
    painter.setPen(Qt::red); // 画笔颜色
    painter.setBrush(Qt::yellow); // 画刷颜色
    // 绘制矩形
    painter.drawRect(50, 50, 200, 100);
    // 绘制文本
    painter.setFont(QFont("Arial", 16));
    painter.drawText(100, 200, "QT绘图测试");
}

自定义事件

步骤

  1. 定义自定义事件类(继承QEvent
  2. 重写event()函数处理自定义事件
  3. 发送事件(QCoreApplication::postEvent()

示例代码

// 1. 定义自定义事件
class MyEvent : public QEvent {
public:
    static const QEvent::Type Type = static_cast<QEvent::Type>(QEvent::User + 1);
    MyEvent() : QEvent(Type) {}
};

// 2. 重写事件处理函数
bool MainWindow::event(QEvent *event) {
    if (event->type() == MyEvent::Type) {
        qDebug() << "收到自定义事件";
        return true;
    }
    return QMainWindow::event(event);
}

// 3. 发送事件
QCoreApplication::postEvent(this, new MyEvent());

软键盘

核心思路

通过自定义按钮类,点击时发送QKeyEvent事件,模拟键盘输入到焦点控件。

自定义按钮类

// KeyButton.h
#ifndef KEYBUTTON_H
#define KEYBUTTON_H
#include <QPushButton>
#include <QKeyEvent>
#include <QApplication>

class KeyButton : public QPushButton {
    Q_OBJECT
public:
    explicit KeyButton(QString text, QWidget *parent = nullptr) : QPushButton(text, parent) {
        connect(this, &QPushButton::clicked, this, &KeyButton::onClicked);
    }
private slots:
    void onClicked() {
        // 发送按键事件到焦点控件
        QKeyEvent *event = new QKeyEvent(QEvent::KeyPress, text().at(0).toLatin1(), Qt::NoModifier, text());
        QApplication::postEvent(QApplication::focusWidget(), event);
    }
};
#endif

软键盘布局

// 主窗口中创建软键盘
QWidget *keyboard = new QWidget(this);
QGridLayout *glay = new QGridLayout(keyboard);
// 添加数字按钮
QStringList nums = {"1","2","3","4","5","6","7","8","9","0"};
int idx = 0;
for (int i=0; i<3; i++) {
    for (int j=0; j<3; j++) {
        glay->addWidget(new KeyButton(nums[idx++]), i, j);
    }
}
glay->addWidget(new KeyButton(nums[9]), 3, 1); // 0在中间
keyboard->show();
软键盘显示效果

网络编程

UDP编程

特点

  • 无连接、不可靠、速度快
  • 适用于实时传输(音视频、游戏)

服务器端示例

#include <QUdpSocket>

QUdpSocket udpSocket;
// 绑定端口
udpSocket.bind(QHostAddress::Any, 8888);
// 接收数据
connect(&udpSocket, &QUdpSocket::readyRead, [&]() {
    while (udpSocket.hasPendingDatagrams()) {
        QByteArray datagram;
        datagram.resize(udpSocket.pendingDatagramSize());
        QHostAddress senderAddr;
        quint16 senderPort;
        udpSocket.readDatagram(datagram.data(), datagram.size(), &senderAddr, &senderPort);
        qDebug() << "收到UDP数据:" << datagram;
        // 回复数据
        udpSocket.writeDatagram("收到", senderAddr, senderPort);
    }
});

客户端示例

QUdpSocket udpSocket;
// 发送数据
udpSocket.writeDatagram("Hello UDP", QHostAddress::LocalHost, 8888);
// 接收回复
connect(&udpSocket, &QUdpSocket::readyRead, [&]() {
    QByteArray datagram;
    datagram.resize(udpSocket.pendingDatagramSize());
    udpSocket.readDatagram(datagram.data(), datagram.size());
    qDebug() << "UDP回复:" << datagram;
});

TCP编程

特点

  • 面向连接、可靠传输、有序
  • 适用于文件传输、登录验证等

服务器端流程

  1. 创建QTcpServer对象
  2. 监听端口(listen()
  3. 响应newConnection()信号,获取客户端套接字
  4. 读写数据

服务器端示例

#include <QTcpServer>
#include <QTcpSocket>

QTcpServer server;
// 监听端口
server.listen(QHostAddress::Any, 9999);
// 新客户端连接
connect(&server, &QTcpServer::newConnection, [&]() {
    QTcpSocket *clientSocket = server.nextPendingConnection();
    qDebug() << "新客户端连接:" << clientSocket->peerAddress().toString();
    // 发送欢迎信息
    clientSocket->write("欢迎连接TCP服务器");
    // 接收客户端数据
    connect(clientSocket, &QTcpSocket::readyRead, [=]() {
        qDebug() << "TCP客户端数据:" << clientSocket->readAll();
    });
    // 客户端断开连接
    connect(clientSocket, &QTcpSocket::disconnected, [=]() {
        qDebug() << "客户端断开连接";
        clientSocket->deleteLater();
    });
});

客户端示例

QTcpSocket clientSocket;
// 连接服务器
clientSocket.connectToHost(QHostAddress::LocalHost, 9999);
// 连接成功
connect(&clientSocket, &QTcpSocket::connected, [&]() {
    qDebug() << "TCP连接成功";
    clientSocket.write("Hello TCP Server");
});
// 接收服务器数据
connect(&clientSocket, &QTcpSocket::readyRead, [&]() {
    qDebug() << "TCP服务器数据:" << clientSocket.readAll();
});

HTTP使用

HTTP协议基础

  • 应用层协议,基于TCP
  • 无状态,请求-响应模式
  • 支持GET(获取资源)、POST(提交数据)等方法

Qt HTTP核心类

  • QNetworkAccessManager:管理网络请求
  • QNetworkRequest:封装请求信息(URL、请求头)
  • QNetworkReply:接收响应数据

GET请求示例

#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>

QNetworkAccessManager manager;
QUrl url("https://www.baidu.com");
QNetworkRequest request(url);
// 发送GET请求
QNetworkReply *reply = manager.get(request);
// 接收响应
connect(reply, &QNetworkReply::finished, [=]() {
    if (reply->error() == QNetworkReply::NoError) {
        qDebug() << "GET响应:" << reply->readAll();
    } else {
        qDebug() << "GET错误:" << reply->errorString();
    }
    reply->deleteLater();
});

POST请求示例

QNetworkAccessManager manager;
QUrl url("http://httpbin.org/post");
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
// POST数据
QByteArray postData = "name=qt&version=5.14";
QNetworkReply *reply = manager.post(request, postData);
// 接收响应
connect(reply, &QNetworkReply::finished, [=]() {
    if (reply->error() == QNetworkReply::NoError) {
        qDebug() << "POST响应:" << reply->readAll();
    }
    reply->deleteLater();
});

解决HTTPS TLS错误

Windows平台

  1. 下载OpenSSL 1.1.1版本(对应Qt编译架构:32/64位)
  2. 拷贝libcrypto-1_1.dlllibssl-1_1.dll到Qt安装目录的bin文件夹(如D:\Qt\Qt5.14.2\5.14.2\mingw73_64\bin

Linux平台

  1. 安装OpenSSL:sudo apt install libssl-dev
  2. 项目pro文件添加:
INCLUDEPATH += /usr/include/openssl
LIBS += -lssl -lcrypto

JSON数据操作

JSON基础格式

  • 对象:{ "key": "value" }(对应QJsonObject
  • 数组:[value1, value2](对应QJsonArray
  • 支持数据类型:字符串、数字、布尔、null、对象、数组

JSON解析(从字符串/文件)

示例:解析天气JSON数据

#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>

// JSON字符串(实际可从网络/文件读取)
QString jsonStr = R"(
{
    "reason": "查询成功",
    "result": {
        "city": "广州",
        "realtime": {
            "temperature": "30",
            "humidity": "76"
        },
        "future": [
            {"date": "2024-08-20", "weather": "大雨"},
            {"date": "2024-08-21", "weather": "雷阵雨"}
        ]
    },
    "error_code": 0
}
)";

// 解析步骤
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonStr.toUtf8());
if (!jsonDoc.isNull()) {
    QJsonObject rootObj = jsonDoc.object();
    qDebug() << "状态:" << rootObj["reason"].toString();
    qDebug() << "错误码:" << rootObj["error_code"].toInt();
    
    // 解析嵌套对象
    QJsonObject resultObj = rootObj["result"].toObject();
    qDebug() << "城市:" << resultObj["city"].toString();
    
    QJsonObject realtimeObj = resultObj["realtime"].toObject();
    qDebug() << "温度:" << realtimeObj["temperature"].toString() << "℃";
    
    // 解析数组
    QJsonArray futureArr = resultObj["future"].toArray();
    for (int i=0; i<futureArr.size(); i++) {
        QJsonObject itemObj = futureArr[i].toObject();
        qDebug() << itemObj["date"].toString() << ":" << itemObj["weather"].toString();
    }
}

JSON封装(生成JSON字符串/文件)

示例:封装用户数据

// 创建JSON对象
QJsonObject user1Obj;
user1Obj["name"] = "张三";
user1Obj["age"] = 20;
user1Obj["gender"] = "男";

QJsonObject user2Obj;
user2Obj["name"] = "李四";
user2Obj["age"] = 22;
user2Obj["gender"] = "女";

// 创建JSON数组
QJsonArray userArr;
userArr.append(user1Obj);
userArr.append(user2Obj);

// 根对象
QJsonObject rootObj;
rootObj["users"] = userArr;

// 生成JSON字符串
QJsonDocument jsonDoc(rootObj);
QByteArray jsonData = jsonDoc.toJson(QJsonDocument::Indented); // 格式化输出

// 写入文件
QFile file("./users.json");
if (file.open(QIODevice::WriteOnly)) {
    file.write(jsonData);
    file.close();
}

百度API接口

前置准备

  1. 注册百度AI开放平台账号
  2. 创建应用,获取API KeySecret Key
  3. 领取对应API的免费资源(如图像识别、人脸识别)

核心流程

  1. 通过API KeySecret Key获取access_token(有效期30天)
  2. 携带access_token调用具体API接口
  3. 解析返回的JSON结果

图像识别示例

步骤1:获取access_token

// 请求URL
QString tokenUrl = "https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%1&client_secret=%2";
QString apiKey = "你的API Key";
QString secretKey = "你的Secret Key";
tokenUrl = tokenUrl.arg(apiKey).arg(secretKey);

// 发送请求
QNetworkAccessManager manager;
QNetworkReply *reply = manager.get(QNetworkRequest(QUrl(tokenUrl)));
connect(reply, &QNetworkReply::finished, [=]() {
    if (reply->error() == QNetworkReply::NoError) {
        QJsonObject obj = QJsonDocument::fromJson(reply->readAll()).object();
        QString accessToken = obj["access_token"].toString(); // 保存token
    }
    reply->deleteLater();
});

步骤2:调用图像识别API

// 图像转Base64(API要求)
QImage img("./test.jpg");
QByteArray imgData;
QBuffer buffer(&imgData);
img.save(&buffer, "JPG");
QString base64Img = imgData.toBase64();

// 调用动物识别API
QString apiUrl = QString("https://aip.baidubce.com/rest/2.0/image-classify/v1/animal?access_token=%1").arg(accessToken);
QNetworkRequest request(QUrl(apiUrl));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");

// POST数据
QByteArray postData = QString("image=%1").arg(QString::fromUtf8(QUrl::toPercentEncoding(base64Img))).toUtf8();
QNetworkReply *apiReply = manager.post(request, postData);
connect(apiReply, &QNetworkReply::finished, [=]() {
    if (apiReply->error() == QNetworkReply::NoError) {
        qDebug() << "识别结果:" << apiReply->readAll();
    }
    apiReply->deleteLater();
});

其他API(拓展)

  • 车牌识别:https://aip.baidubce.com/rest/2.0/ocr/v1/license_plate
  • 人脸识别(注册):https://aip.baidubce.com/rest/2.0/face/v3/faceset/user/add
  • 人脸识别(搜索):https://aip.baidubce.com/rest/2.0/face/v3/search

数据库操作

Qt内置数据库接口(QSql模块)

步骤1:配置项目

pro文件添加:QT += sql

步骤2:连接SQLite数据库

#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>

// 添加数据库驱动
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("test.db"); // 数据库文件

// 打开数据库
if (!db.open()) {
    qDebug() << "数据库打开失败:" << db.lastError().text();
    return;
}

// 执行SQL语句(创建表)
QSqlQuery query;
if (!query.exec("CREATE TABLE person (id INT PRIMARY KEY, name TEXT, age INT)")) {
    qDebug() << "创建表失败:" << query.lastError().text();
}

// 插入数据
query.exec("INSERT INTO person (id, name, age) VALUES (1, '张三', 20)");
// 查询数据
query.exec("SELECT * FROM person");
while (query.next()) {
    qDebug() << "ID:" << query.value(0).toInt() 
             << "姓名:" << query.value(1).toString() 
             << "年龄:" << query.value(2).toInt();
}

db.close();

SQLite3源码集成

步骤1:准备源码

  1. 从官网下载SQLite3源码
  2. 解压后获取sqlite3.csqlite3.h
  3. 复制到项目目录,添加到工程

步骤2:核心操作示例

#include "sqlite3.h"

sqlite3 *db;
char *errMsg;

// 打开数据库
if (sqlite3_open("test.db", &db) != SQLITE_OK) {
    qDebug() << "打开失败:" << sqlite3_errmsg(db);
    return;
}

// 创建表
const char *createSql = "CREATE TABLE student (id INT, name TEXT)";
if (sqlite3_exec(db, createSql, nullptr, nullptr, &errMsg) != SQLITE_OK) {
    qDebug() << "创建表失败:" << errMsg;
    sqlite3_free(errMsg);
}

// 插入数据
const char *insertSql = "INSERT INTO student VALUES (1, '李四')";
sqlite3_exec(db, insertSql, nullptr, nullptr, &errMsg);

// 查询数据(回调函数)
static int callback(void *data, int colCount, char **colValue, char **colName) {
    for (int i=0; i<colCount; i++) {
        qDebug() << colName[i] << ":" << colValue[i];
    }
    return 0;
}
sqlite3_exec(db, "SELECT * FROM student", callback, nullptr, &errMsg);

// 关闭数据库
sqlite3_close(db);

交叉编译到ARM开发板

问题解决

编译时出现undefined reference to symbol 'dlsym@@GLIBC_2.4'错误,需在Makefile中添加动态链接库:

LIBS += -ldl

QT摄像头操作

1 PC端(Qt Multimedia模块)

步骤1:配置项目

pro文件添加:

QT += multimedia
QT += multimediawidgets

步骤2:核心操作(实时显示+拍照)

#include <QCamera>
#include <QCameraInfo>
#include <QCameraViewfinder>
#include <QCameraImageCapture>

// 1. 获取摄像头列表
QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
if (cameras.isEmpty()) return;

// 2. 创建摄像头对象
QCamera *camera = new QCamera(cameras[0]);

// 3. 创建取景器(显示画面)
QCameraViewfinder *viewfinder = new QCameraViewfinder(ui->label);
viewfinder->resize(ui->label->size());
viewfinder->show();

// 4. 绑定摄像头与取景器
camera->setViewfinder(viewfinder);

// 5. 拍照功能
QCameraImageCapture *imageCapture = new QCameraImageCapture(camera);
camera->setCaptureMode(QCamera::CaptureStillImage);
connect(imageCapture, &QCameraImageCapture::imageCaptured, [=](int id, const QImage &img) {
    img.save("./photo.jpg"); // 保存照片
});

// 6. 启动摄像头
camera->start();

// 拍照触发(按钮点击)
void MainWindow::on_pushButton_clicked() {
    imageCapture->capture();
}

ARM开发板端(V4L2驱动)

核心思路

开发板默认不支持Qt Multimedia,需通过V4L2驱动封装摄像头操作类。

V4L2封装类(简化版)

class V4l2Api {
public:
    V4l2Api(const char *device = "/dev/video7") { strcpy(device_name, device); }
    int video_init(); // 初始化摄像头
    void video_start(); // 开始采集
    int get_frame(char *buffer, int *size); // 获取YUV数据
    bool yuv422torgb24(unsigned char *yuv, unsigned char *rgb, int w, int h); // YUV转RGB
    void video_stop(); // 停止采集
    void vido_destory(); // 释放资源
private:
    int fd;
    char device_name[28];
    unsigned char *mptr[4]; // 内存映射地址
    unsigned int size[4];
};

开发板端显示示例

// 初始化
V4l2Api *v4l2 = new V4l2Api("/dev/video7");
v4l2->video_init();
v4l2->video_start();

// 定时器刷新界面
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, [=]() {
    char yuvBuffer[640*480*2]; // YUV422格式
    char rgbBuffer[640*480*3];
    int size;
    v4l2->get_frame(yuvBuffer, &size);
    // YUV转RGB
    v4l2->yuv422torgb24((unsigned char*)yuvBuffer, (unsigned char*)rgbBuffer, 640, 480);
    // 显示到UI
    QImage img((unsigned char*)rgbBuffer, 640, 480, QImage::Format_RGB888);
    ui->label->setPixmap(QPixmap::fromImage(img.scaled(ui->label->size())));
});
timer->start(50);

QT音视频操作

核心类

类名 功能 依赖模块
QMediaPlayer 音视频播放控制(播放、暂停、进度) multimedia
QVideoWidget 视频显示控件 multimediawidgets
QMediaPlaylist 播放列表管理(顺序、循环、随机) multimedia

视频播放示例

#include <QMediaPlayer>
#include <QVideoWidget>
#include <QMediaPlaylist>

// 1. 创建播放器
QMediaPlayer *player = new QMediaPlayer(this);

// 2. 创建视频显示控件
QVideoWidget *videoWidget = new QVideoWidget(ui->widget);
videoWidget->resize(800, 480);

// 3. 绑定播放器与显示控件
player->setVideoOutput(videoWidget);

// 4. 设置播放列表
QMediaPlaylist *playlist = new QMediaPlaylist(player);
// 添加本地视频
playlist->addMedia(QUrl::fromLocalFile("./test1.mp4"));
playlist->addMedia(QUrl::fromLocalFile("./test2.mp4"));
// 设置循环播放
playlist->setPlaybackMode(QMediaPlaylist::Loop);
player->setPlaylist(playlist);

// 5. 控制播放
ui->playBtn->clicked.connect(player, &QMediaPlayer::play);
ui->pauseBtn->clicked.connect(player, &QMediaPlayer::pause);
ui->stopBtn->clicked.connect(player, &QMediaPlayer::stop);

// 6. 进度条同步
connect(player, &QMediaPlayer::positionChanged, [=](qint64 pos) {
    ui->progressBar->setValue(pos / 1000); // 秒为单位
});
connect(player, &QMediaPlayer::durationChanged, [=](qint64 dur) {
    ui->progressBar->setMaximum(dur / 1000);
});
// 拖动进度条跳转
connect(ui->progressBar, &QSlider::sliderMoved, [=](int value) {
    player->setPosition(value * 1000);
});

音频播放

无需QVideoWidget,直接设置音频文件即可:

player->setMedia(QUrl::fromLocalFile("./test.mp3"));
player->play();

Windows下打包QT程序

步骤1:编译Release版本

  1. Qt Creator中切换到Release模式
  2. 点击构建,生成xxx.exe文件(路径:build-xxx-Desktop_Qt_xxx-Release/release/

步骤2:复制依赖库

  1. 新建package文件夹,拷贝xxx.exe到该文件夹
  2. 打开Qt命令行工具,切换到package目录:
    cd C:\Users\XXX\Desktop\package
    
  3. 执行打包命令,自动复制依赖库:
    windeployqt xxx.exe
    

步骤3:压缩为单个EXE(可选)

  1. 下载Enigma Virtual Box工具(链接:https://pan.baidu.com/s/1YghrQhgTPtk2oGxATh5jsA?pwd=m9mn)
  2. 打开工具,选择主程序xxx.exe
  3. 添加package文件夹下所有文件/文件夹(勾选“文件压缩”)
  4. 点击“打包”,生成独立EXE文件

交叉编译到ARM开发板

步骤1:安装交叉编译器

  1. 下载Qt嵌入式编译器(Qt-Embedded-5.7.0.tar.bz2
  2. 解压到/usr/local
    sudo tar -jxvf Qt-Embedded-5.7.0.tar.bz2 -C /usr/local/
    
  3. 若提示bzip2: Cannot exec,安装依赖:
    sudo apt install bzip2
    

步骤2:配置别名(简化命令)

  1. 编辑配置文件:
    vim ~/.bashrc
    
  2. 添加别名:
    alias arm-qmake='/usr/local/Qt-Embedded-5.7.0/bin/qmake'
    
  3. 生效配置:
    source ~/.bashrc
    

步骤3:交叉编译项目

  1. 进入QT项目目录:
    cd /path/to/your/project
    
  2. 生成Makefile:
    arm-qmake
    
  3. 编译:
    make -j4
    
  4. 生成的ARM架构可执行文件,拷贝到开发板运行

posted @ 2026-01-07 08:48  Jaklin  阅读(58)  评论(0)    收藏  举报