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);
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() << "按钮被松开"; }
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(); // 停止定时器
常见问题
-
信号与槽连接失败?
- 检查是否继承
QObject并添加Q_OBJECT宏 - 检查函数名是否拼写错误
- 自定义参数类型是否注册
- 检查是否继承
-
槽函数不执行?
- 检查发送者/接收者对象是否被提前析构
- 跨线程是否使用
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);
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");
颜色对话框(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; |
| 边框 | border、border-radius(圆角) |
border: 2px solid #000; border-radius: 8px; |
| 字体 | font-family、font-size、font-weight |
font: bold 14px "微软雅黑"; |
| 内边距/外边距 | padding、margin |
padding: 6px; margin: 10px; |
| 尺寸 | width、height、min-width |
width: 200px; min-height: 30px; |
资源文件添加
步骤
- 新建资源文件(
.qrc):Qt Creator → 新建 → Qt Resource File - 添加前缀(如
/img) - 添加资源文件(图片、图标等)
- 样式表中引用:
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);
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绘图测试");
}
自定义事件
步骤
- 定义自定义事件类(继承
QEvent) - 重写
event()函数处理自定义事件 - 发送事件(
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编程
特点
- 面向连接、可靠传输、有序
- 适用于文件传输、登录验证等
服务器端流程
- 创建
QTcpServer对象 - 监听端口(
listen()) - 响应
newConnection()信号,获取客户端套接字 - 读写数据
服务器端示例
#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平台
- 下载OpenSSL 1.1.1版本(对应Qt编译架构:32/64位)
- 拷贝
libcrypto-1_1.dll和libssl-1_1.dll到Qt安装目录的bin文件夹(如D:\Qt\Qt5.14.2\5.14.2\mingw73_64\bin)
Linux平台
- 安装OpenSSL:
sudo apt install libssl-dev - 项目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接口
前置准备
- 注册百度AI开放平台账号
- 创建应用,获取
API Key和Secret Key - 领取对应API的免费资源(如图像识别、人脸识别)
核心流程
- 通过
API Key和Secret Key获取access_token(有效期30天) - 携带
access_token调用具体API接口 - 解析返回的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:准备源码
- 从官网下载SQLite3源码
- 解压后获取
sqlite3.c和sqlite3.h - 复制到项目目录,添加到工程
步骤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版本
- Qt Creator中切换到
Release模式 - 点击构建,生成
xxx.exe文件(路径:build-xxx-Desktop_Qt_xxx-Release/release/)
步骤2:复制依赖库
- 新建
package文件夹,拷贝xxx.exe到该文件夹 - 打开Qt命令行工具,切换到
package目录:cd C:\Users\XXX\Desktop\package - 执行打包命令,自动复制依赖库:
windeployqt xxx.exe
步骤3:压缩为单个EXE(可选)
- 下载Enigma Virtual Box工具(链接:https://pan.baidu.com/s/1YghrQhgTPtk2oGxATh5jsA?pwd=m9mn)
- 打开工具,选择主程序
xxx.exe - 添加
package文件夹下所有文件/文件夹(勾选“文件压缩”) - 点击“打包”,生成独立EXE文件
交叉编译到ARM开发板
步骤1:安装交叉编译器
- 下载Qt嵌入式编译器(
Qt-Embedded-5.7.0.tar.bz2) - 解压到
/usr/local:sudo tar -jxvf Qt-Embedded-5.7.0.tar.bz2 -C /usr/local/ - 若提示
bzip2: Cannot exec,安装依赖:sudo apt install bzip2
步骤2:配置别名(简化命令)
- 编辑配置文件:
vim ~/.bashrc - 添加别名:
alias arm-qmake='/usr/local/Qt-Embedded-5.7.0/bin/qmake' - 生效配置:
source ~/.bashrc
步骤3:交叉编译项目
- 进入QT项目目录:
cd /path/to/your/project - 生成Makefile:
arm-qmake - 编译:
make -j4 - 生成的ARM架构可执行文件,拷贝到开发板运行

浙公网安备 33010602011771号