一.Qt事件
1.1 事件介绍
事件是应用程序内部或者外部产⽣的事情或者动作的统称。在 Qt 中使用⼀个对象来表示⼀个事件。所有的 Qt 事件均继承于抽象类 QEvent。事件是由系统或者 Qt 平台本⾝在不同的时刻发出的。当用户按下⿏标、敲下键盘,或者是窗⼝需要重新绘制的时候,都会发出⼀个相应的事件。⼀些事件是在用户操作时发出,如键盘事件、⿏标事件等,另⼀些事件则是由系统本⾝⾃动发出,如定时器事件。常见的 Qt 事件如下:
常见时间描述
事件名称 | 描述 |
---|---|
⿏标事件 | ⿏标左键、⿏标右键、⿏标滚轮,⿏标的移动,⿏标按键的按下和松开 |
键盘事件 | 按键类型、按键按下、按键松开 |
定时器事件 | 定时时间到达 |
进⼊离开事件 | ⿏标的进⼊和离开 |
滚轮事件 | ⿏标滚轮滚动 |
绘屏事件 | 重绘屏幕的某些部分 |
显⽰隐藏事件 | 窗⼝的显⽰和隐藏 |
移动事件 | 窗⼝位置的变化 |
窗⼝事件 | 是否为当前窗口 |
⼤⼩改变事件 | 窗口⼤⼩改变 |
焦点事件 | 键盘焦点移动 |
拖拽事件 | 用⿏标进⾏拖拽 |
1.12 事件的处理
事件处理⼀般常用的方法为:重写相关的 Event 函数。
在 Qt 中,几乎所有的 Event 函数都是虚函数,所以可以重新实现。如:在实现⿏标的进⼊和离开事件时,直接重新实现 enterEvent() 和 leaveEvent() 即可。enterEvent() 和 leaveEvent() 函数原型如下:
1.2 按键事件
Qt 中的按键事件是通过 QKeyEvent 类来实现的。当键盘上的按键被按下或者被释放时,键盘事件便会触发。在帮助⽂档中查找QKeyEvent 类如下
1.2.1 单个按键
示例:当某个按键被按下时,输出:某个按键被按下了
#include "widget.h"
#include "ui_widget.h"
#include<QDebug>
#include<QKeyEvent>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::keyPressEvent(QKeyEvent *event)
{
// qDebug()<<event->key();
if(event->key()==Qt::Key_A&&event->modifiers()==Qt::ControlModifier)
{
qDebug()<<"按下了ctrl+A";
}
}
1.2.2 组合按键
在 Qt 助手中搜索:Qt::KeyboardModifier,如下图示:
Qt::KeyboardModifier 中定义了在处理键盘事件时对应的修改键。在 Qt 中,键盘事件可以与修改键⼀起使用,以实现⼀些复杂的交互操作。KeyboardModifier 中修改键的具体描述如下:
Qt::NoModifier | ⽆修改键 |
Qt::ShiftModifier | Shift 键 |
Qt::ControlModifier | Ctrl 键 |
Qt::AltModifier | Alt 键 |
Qt::MetaModifier | Meta键(在Windows上指Windows键,在macOS上指Command键) |
Qt::KeypadModifier | 使⽤键盘上的数字键盘进⾏输⼊时,Num Lock键处于打开状态 |
Qt::GroupSwitchModifier | ⽤于在输⼊法 组之间 切换 |
示例
#include "widget.h"
#include "ui_widget.h"
#include<QDebug>
#include<QKeyEvent>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::keyPressEvent(QKeyEvent *event)
{
// qDebug()<<event->key();
if(event->key()==Qt::Key_A&&event->modifiers()==Qt::ControlModifier)
{
qDebug()<<"按下了ctrl+A";
}
}
1.3鼠标事件
在 Qt 中,鼠标事件是用 QMouseEvent 类来实现的。当在窗⼝中按下鼠标或者移动⿏标时,都会产⽣⿏标事件。
1.3.1鼠标单击事件
在 Qt 中,⿏标按下是通过虚函数 mousePressEvent() 来捕获的。mousePressEvent() 函数原型如下:
[virtual protected] void QWidget::mousePressEvent(QMouseEvent *event)
鼠标左右键及滚轮的表示
- Qt::LeftButton ⿏标左键
- Qt::RightButton ⿏标右键
- Qt::MidButton ⿏标滚轮
1.3.2 ⿏标双击事件
⿏标双击事件是通过虚函数:mouseDoubleClickEvent() 来实现的。mouseDoubleClickEvent()
函数原型如下:
[virtual protected] void QWidget::mouseDoubleClickEvent(QMouseEvent *event)
1.3.3 ⿏标移动事件
⿏标移动事件是通过虚函数:mouseMoveEvent() 来实现的。同时为了实时捕获⿏标位置信息,需要
通过函数 setMouseTracking() 来追踪⿏标的位置。**mouseMoveEvent()**函数原型如下:
[virtual protected] void QWidget::mouseMoveEvent(QMouseEvent *event)
**setMouseTracking()**函数原型如下:
void setMouseTracking(bool enable)
说明:
setMouseTracking() 函数默认是 false,需要设置为 true,才能实时捕获⿏标位置信息。否则只有当⿏标按下时才能捕获其位置信息。
1.3.4 滚轮事件
在 Qt 中,鼠标滚轮事件是通过 QWheelEvent 类来实现的。滚轮滑动的距离可以通过 delta() 函数获取。delta() 函数原型如下:
int QGraphicsSceneWheelEvent::delta() const
其中返回值代表滚轮滑动的距离。正数表⽰滚轮相对于用户向前滑动,负数表示滚轮相对于用户向后滑动。
#include "widget.h"
#include "ui_widget.h"
#include<QDebug>
#include<QWheelEvent>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
total=0;
}
Widget::~Widget()
{
delete ui;
}
void Widget::wheelEvent(QWheelEvent *event)
{
total+=event->delta();
// qDebug()<<event->delta();//鼠标滚轮滚动事件
qDebug()<<total;
}
1.4定时器
Qt中的定时器分为 QTimerEvent
和 QTimer
这2个类。
- QTimerEvent类用来描述⼀个定时器事件。在使⽤时需要通过 startTimer() 函数来开启⼀个定时器,这个函数需要输⼊⼀个以毫秒为单位的整数作为参数来表明设定的时间,它返回的整型值代表这个定时器。当定时器溢出时(即定时时间到达)就可以在 timerEvent() 函数中获取该定时器的编号来进⾏相关操作。
- QTimer类 来实现⼀个定时器,它提供了更⾼层次的编程接⼝,如:可以使⽤信号和槽,还可以设置只运⾏⼀次的定时器。
1.5.1 QTimerEvent
示例1:在UI界面上放置两个 Label 控件,⼀个让其1秒数字累加⼀次,⼀个让其2秒数字累加⼀次。
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//启动定时器
//timerID是定时器的身份标识
timerId = this->startTimer(1000);
}
Widget::~Widget()
{
delete ui;
}
void Widget::timerEvent(QTimerEvent *event)
{
//如果一个程序中存在多个定时器(startTimer),此时每个定时器都会触发 timerEvent函数
//先判断当前触发的定时器是不是想要触发的
if(event->timerId()!=this->timerId)
{
return;//不是直接忽略
}
int value = ui->lcdNumber->intValue();
if(value <= 0)
{
//停止
this->killTimer(this->timerId);
return;
}
value--;
ui->lcdNumber->display(value);
}
moveEvent resizeEvent
#include "widget.h"
#include "ui_widget.h"
#include<QDebug>
#include<QMoveEvent>
#include<QResizeEvent>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::moveEvent(QMoveEvent *event)
{
qDebug()<<event->pos();
}
void Widget::resizeEvent(QResizeEvent *event)
{
qDebug()<<event->size();
}
运行程序即可获得坐标
移动窗口也可获得做标
二.Qt文件
2.1 Qt文件概述
文件操作是应用程序必不可少的部分。Qt 作为⼀个通用开发库,提供了跨平台的文件操作能力。 Qt提供了很多关于⽂件的类,通过这些类能够对⽂件系统进进操作,如文件读写、⽂件信息获取、文件复制或重命名等。
2.2输入输出设备类
在 Qt 中,⽂件读写的类为 QFile 。QFile 的⽗类为 QFileDevice ,QFileDevice 提供了⽂件交互操作的底层功能。 QFileDevice 的⽗类是 QIODevice,QIODevice 的⽗类为 QObject 。
QIODevice 是 Qt 中所有输⼊输出设备(input/output device,简称 I/O 设备)的基础类,I/O 设备就是能进⾏数据输⼊和输出的设备,例如⽂件是⼀种 I/O 设备,⽹络通信中的 socket 是 I/O 设备, 串⼝、蓝⽛等通信接⼝也是 I/O 设备,所以它们也是从 QIODevice 继承来的。Qt 中主要的⼀些 I/O 设备
类的继承关系如下图所示
上图中各类的说明如下:
- QFile 是⽤于⽂件操作和⽂件数据读写的类,使⽤ QFile 可以读写任意格式的⽂件。
- QSaveFile 是⽤于安全保存⽂件的类。使⽤ QSaveFile 保存⽂件时,它会先把数据写⼊⼀个临时⽂件,成功提交后才将数据写⼊最终的⽂件。如果保存过程中出现错误,临时⽂件⾥的数据不会被写⼊最终⽂件,这样就能确保最终⽂件中不会丢失数据或被写⼊部分数据。 在保存⽐较⼤的⽂件或复杂格式的⽂件时可以使⽤这个类,例如从⽹络上下载⽂件等
- QTemporaryFile 是⽤于创建临时⽂件的类。使⽤函数 QTemporaryFile::open() 就能创建⼀个⽂件名唯⼀的临时⽂件,在 QTemporaryFile 对象被删除时,临时⽂件被⾃动删除。
- QTcpSocket 和 QUdpSocket 是分别实现了 TCP 和 UDP 的类。
- QSerialPort 是实现了串⼝通信的类,通过这个类可以实现计算机与串⼝设备的通信。
- QBluetoothSocket 是⽤于蓝⽛通信的类。⼿机和平板计算机等移动设备有蓝⽛通信模块,笔记本电脑⼀般也有蓝⽛通信模块。通过QBluetoothSocket类,就可以编写蓝⽛通信程。如编程实现笔记本电脑与⼿机的蓝⽛通信。
- QProcess 类⽤于启动外部程序,并且可以给程序传递参数。
- QBuffer 以⼀个 QByteArray 对象作为数据缓冲区,将 QByteArray 对象当作⼀个 I/O 设备来读写。
2.3文件读写类
在 Qt 中,⽂件的读写主要是通过 QFile 类来实现。在 QFile 类中提供了⼀些⽤来读写⽂件的⽅法。对于⽂件的操作主要有:
- 读数据:QFile 类中提供了多个⽅法⽤于读取⽂件内容;如 read()、readAll()、readLine()等。
- 写数据:QFile 类中提供了多个⽅法⽤于往⽂件中写内容;如 write()、writeData()等
- 关闭⽂件:⽂件使⽤结束后必须⽤函数 close() 关闭⽂件。
访问⼀个设备之前,需要使⽤ open()函数 打开该设备,⽽且必须指定正确的打开模式,QIODevice 中所有的打开模式由QIODevice::OpenMode 枚举变量定义,其取值如下
QIODevice::NotOpen | 没有打开设备 |
QIODevice::ReadOnly | 以只读⽅式打开设备 |
QIODevice::WriteOnly | 以只写⽅式打开设备 |
QIODevice::ReadWrite | 以读写⽅式打开设备 |
QIODevice::Append | 以追加⽅式打开设备,数据将写到⽂件末尾 |
QIODevice::Truncate | 每次打开⽂件后重写⽂件内容,原内容将被删除 |
QIODevice::Text | 在读⽂件时,⾏尾终⽌符会被转换为 ‘\n’;当写⼊⽂件时,⾏尾终⽌符会被转换为本地编码。如 Win32上为’\r\n’; |
QIODevice::Unbuffered | ⽆缓冲形式打开⽂件,绕过设备中的任何缓冲区 |
QIODevice::NewOnly | ⽂件存在则打开失败,不存在则创建⽂件 |
示例:读取文件内容
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QPlainTextEdit>
#include<QFileDialog>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//获取菜单栏
QMenuBar*menubar = this->menuBar();
//添加菜单
QMenu*menu = new QMenu("文件");
menubar->addMenu(menu);
//添加菜单项
QAction*action1 = new QAction("打开");
QAction*action2 = new QAction("保存");
menu->addAction(action1);
menu->addAction(action2);
//指定一个输入框
QPlainTextEdit *edit =new QPlainTextEdit();
QFont font;
font.setPointSize(20);
edit->setFont(font);
this->setCentralWidget(edit );
//连接QActio 的信号槽
connect(action1,&QAction::triggered,this,&MainWindow::handActio1);
connect(action2,&QAction::triggered,this,&MainWindow::handActio2);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::handActio1()
{
//先弹出一个打开文件对话框
QString path = QFileDialog::getOpenFileName(this);
//把文件名显示到状态栏中
QStatusBar* statusbar = this->statusBar();
statusbar->showMessage(path);//显示路劲
//根据用户选择的路劲,构造一个QFile对象
QFile file(path);
bool ret = file.open(QIODevice::ReadOnly);
if(!ret)
{
statusbar->showMessage(path + "打开文件失败!");
return;
}
//读取文件
QString text = file.readAll();
//关闭文件
file.close();
//读取的内容设置到输入框中
edit->setPlainText(text );
}
void MainWindow::handActio2()
{
//弹出保存文件对话框
QString path = QFileDialog::getSaveFileName(this);
//在状态栏显示文件名
QStatusBar* statusbar = this->statusBar();
statusbar->showMessage(path);
//根据选择的路劲,构造一个QFile对象 ,并打开文件
QFile file(path);
bool ret = file.open(QFile::WriteOnly);
if(!ret)
{
statusbar->showMessage(path+ "打开失败");
return;
}
//写文件
const QString& text =edit ->toPlainText();
file.write(text.toUtf8());
//关闭文件
file.close();
}
2.4文件读写类
QFileInfo 是 Qt 提供的⼀个⽤于获取⽂件和⽬录信息的类,如获取⽂件名、⽂件大小、⽂件修改⽇期等。QFileInfo类中提供了很多的⽅法,常⽤的有:
- isDir() 检查该⽂件是否是⽬录;
- sExecutable() 检查该⽂件是否是可执⾏⽂件;
- fileName() 获得⽂件名;
- completeBaseName() 获取完整的⽂件名;
- suffix() 获取⽂件后缀名;
- completeSuffix() 获取完整的⽂件后缀;
- size() 获取⽂件大小;
- isFile() 判断是否为⽂件;
- fileTime() 获取⽂件创建时间、修改时间、最近访问时间等;
示例
#include "widget.h"
#include "ui_widget.h"
#include<QFileDialog>
#include<QDebug>
#include<QFileInfo>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
//弹出对话框
QString path = QFileDialog::getOpenFileName(this);
//构造一个QFileInfo对象
QFileInfo fileInfo(path);
//打印相关属性
qDebug()<<fileInfo.fileName();//获取文件名
qDebug()<<fileInfo.suffix();//获取文件名后缀
qDebug()<<fileInfo.path();//路劲
qDebug()<<fileInfo.size();//大小
qDebug()<<fileInfo.isFile();//判断是否为文件
qDebug()<<fileInfo.isDir();//检查该文件是否是目录
qDebug()<<fileInfo.completeSuffix();//获取文件后缀
}
QT多线程
3.1 QT多线程概述
在 Qt 中,多线程的处理⼀般是通过 QThread类 来实现。
QThread 代表⼀个在应⽤程序中可以独⽴控制的线程,也可以和进程中的其他线程共享数据
QThread 对象管理程序中的⼀个控制线程。
3.2 QThread 常用 API
run() | 线程的⼊⼝函数… |
start() | 通过调⽤ run() 开始执⾏线程。操作系统将根据优先级参数调度线程。如果线程已经在运⾏,这个函数什么也不做。 |
currentThread() | 返回⼀个指向管理当前执⾏线程的 QThread的指针。 |
isRunning() | 如果线程正在运⾏则返回true;否则返回false。 |
sleep() / msleep() /usleep() | 使线程休眠,单位为秒 / 毫秒 / 微秒 |
wait() | 阻塞线程,直到满⾜以下任何⼀个条件:与此 QThread 对象关联的线程已经完成执⾏(即当它从run()返回时)。如果线程已经完成,这个函数将返回 true。如果线程尚未启动,它也返回 true。已经过了⼏毫秒。如果时间是 ULONG_MAX(默认值),那么等待永远不会超时(线程必须从run()返回)。如果等待超时,此函数将返回 false。这提供了与 POSIX pthread_join() 函数类似的功能。 |
terminate() | 终⽌线程的执⾏。线程可以⽴即终⽌,也可以不⽴即终⽌,这取决于操作系统的调度策略。在terminate() 之后使⽤ QThread::wait() 来确保。 |
finished() | 当线程结束时会发出该信号,可以通过该信号来实现线程的清理⼯作。 |
3.3 使用线程
创建线程的步骤:
1.自定义⼀个类,继承于 QThread,并且只有⼀个线程处理函数(和主线程不是同⼀个线程),这个线程处理函数主要就是重写⽗类中的 run() 函数。
2. 线程处理函数⾥⾯写⼊需要执⾏的复杂数据处理;
3. 启动线程不能直接调⽤ run() 函数,需要使⽤对象来调⽤ start() 函数实现线程启动;
4. 线程处理函数执⾏结束后可以定义⼀个信号来告诉主线程;
5. 最后关闭线程。
示例
thread.h
#ifndef THREAD_H
#define THREAD_H
#include <QWidget>
#include<QThread>
class Thread : public QThread
{
Q_OBJECT
public:
Thread();
//从写父类方法
void run();
signals:
void notify();
};
#endif // THREAD_H
thread.cpp
#include "thread.h"
Thread::Thread()
{
}
void Thread::run()
{
for(int i =0 ;i < 10;i++)
{
//sleep 本身是Thread的成员函数,就可以直接调用
sleep(1);
//发送一个信号,通知主线程
emit notify();
}
}
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//链接信号槽,通过信号槽更新界面
connect(&thread,&Thread::notify,this,&Widget::handle);
//启动线程
thread.start();
}
Widget::~Widget()
{
delete ui;
}
void Widget::handle()
{
//修改界面内容
int val = ui->lcdNumber->intValue();
val-=5;
ui->lcdNumber->display(val );
}
3.4 线程安全
实现线程互斥和同步常用的类有:
- 互斥锁:QMutex、QMutexLocker
- 条件变量:QWaitCondition
- 信号量:QSemaphore
- 读写锁:QReadLocker、QWriteLocker、QReadWriteLock
3.4.1 互斥锁
互斥锁是⼀种保护和防⽌多个线程同时访问同⼀对象实例的⽅法,在 Qt 中,互斥锁主要是通过QMutex类来处理。
- QMutex
特点:QMutex 是 Qt 框架提供的互斥锁类,⽤于保护共享资源的访问,实现线程间的互斥操作。
用途:在多线程环境下,通过互斥锁来控制对共享数据的访问,确保线程安全。
QMutex mutex;
mutex.lock(); //上锁
//访问共享资源
//...
mutex.unlock(); //解锁
- QMutexLocker
特点:QMutexLocker 是 QMutex 的辅助类,使⽤ RAII(Resource Acquisition Is Initialization)⽅式对互斥锁进⾏上锁和解锁操作。
用途:简化对互斥锁的上锁和解锁操作,避免忘记解锁导致的死锁等问题。
QMutex mutex;
{
QMutexLocker locker(&mutex); //在作⽤域内⾃动上锁
//访问共享资源
//...
} //在作⽤域结束时⾃动解锁
- QReadWriteLocker、QReadLocker、QWriteLocker
特点: - QReadWriteLock 是读写锁类,⽤于控制读和写的并发访问。
- QReadLocker ⽤于读操作上锁,允许多个线程同时读取共享资源。
- QWriteLocker ⽤于写操作上锁,只允许⼀个线程写⼊共享资源。
⽤途:在某些情况下,多个线程可以同时读取共享数据,但只有⼀个线程能够进⾏写操作。读写锁提供了更⾼效的并发访问⽅式。
通过加锁操作解决线程安全问题
thread.h
#ifndef THREAD_H
#define THREAD_H
#include <QWidget>
#include<QThread>
#include<QMutex>
class Thread : public QThread
{
Q_OBJECT
public:
Thread();
//添加一个static 成员
static int num;
//创建锁
static QMutex mutex;
void run() ;
};
#endif // THREAD_H
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
thread.cpp
#include "thread.h"
int Thread::num =0;
QMutex Thread::mutex;
Thread::Thread()
{
}
void Thread::run()
{
for(int i=0;i<50000;i++)
{
QMutexLocker locker(&mutex);
// mutex.lock();//枷锁
num++;
// mutex.unlock();//解锁
}
}
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include"thread.h"
#include<QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建线程对象
Thread t1;
Thread t2;
t1.start();
t2.start();
//加上线程的等待,让主线程等待这两线程结束
t1.wait();
t2.wait();
//打印结果
qDebug()<<Thread::num;
}
Widget::~Widget()
{
delete ui;
}
3.4.2 条件变量
在多线程编程中,假设除了等待操作系统正在执⾏的线程之外,某个线程还必须等待某些条件满⾜才能执⾏,这时就会出现问题。这种情况下,线程会很⾃然地使⽤锁的机制来阻塞其他线程,因为这只是线程的轮流使⽤,并且该线程等待某些特定条件,⼈们会认为需要等待条件的线程,在释放互斥锁或读写锁之后进⼊了睡眠状态,这样其他线程就可以继续运⾏。当条件满⾜时,等待条件的线程将被另⼀个线程唤醒
在 Qt 中,专⻔提供了 QWaitCondition类 来解决像上述这样的问题。
特点:QWaitCondition 是 Qt 框架提供的条件变量类,⽤于线程之间的消息通信和同步。
⽤途:在某个条件满⾜时等待或唤醒线程,⽤于线程的同步和协调。
3.4.3 信号量
有时在多线程编程中,需要确保多个线程可以相应的访问⼀个数量有限的相同资源。例如,运⾏程序的设备可能是⾮常有限的内存,因此我们更希望需要⼤量内存的线程将这⼀事实考虑在内,并根据可⽤的内存数量进⾏相关操作,多线程编程中类似问题通常⽤信号量来处理。信号量类似于增强的互斥锁,不仅能完成上锁和解锁操作,⽽且可以跟踪可⽤资源的数量。
特点:QSemaphore 是 Qt 框架提供的计数信号量类,⽤于控制同时访问共享资源的线程数量。
⽤途:限制并发线程数量,⽤于解决⼀些资源有限的问题。
QSemaphore semaphore(2); //同时允许两个线程访问共享资源
//在需要访问共享资源的线程中
semaphore.acquire(); //尝试获取信号量,若已满则阻塞
//访问共享资源
//...
semaphore.release(); //释放信号量
//在另⼀个线程中进⾏类似操作
四.Qt网络
在进⾏⽹络编程之前, 需要在项⽬中的 .pro ⽂件中添加 network 模块.
4.1 UDP Socket
4.1.1 核心API概览
主要的类有两个。QUdpsocket
和QNetworkDatagram
QUdpSocket 表示⼀个 UDP 的 socket ⽂件.
名称 | 类型 | 说明 | 对标原生API |
---|---|---|---|
bind(const QHostAddress&,quint16) | ⽅法 | 绑定指定的端⼝号. | bind |
receiveDatagram() | ⽅法 | 返回 QNetworkDatagram . 读取⼀个 UDP 数据报. | recvfrom |
writeDatagram(const QNetworkDatagram&) | ⽅法 | 发送⼀个 UDP 数据报. | sendto |
readyRead | 信号 | 在收到数据并准备就绪后触发. | ⽆ (类似于 IO 多路复⽤的通知机制) |
QNetworkDatagram 表示⼀个 UDP 数据报.
名称 | 类型 | 说明 | 对标原生API |
---|---|---|---|
QNetworkDatagram(const QByteArray&, const QHostAddress& , quint16 ) | 构造函数 | 通过 QByteArray , ⽬标 IP 地址,⽬标端⼝号 构造⼀个 UDP 数据报.通常⽤于发送数据时. | ⽆ |
data() | ⽅法 | 获取数据报内部持有的数据. 返回QByteArray | ⽆ |
senderAddress() | ⽅法 | 获取数据报中包含的对端的 IP 地址. | ⽆, recvfrom 包含了该功能. |
senderPort() | ⽅法 | 获取数据报中包含的对端的端⼝号. | ⽆, recvfrom 包含了该功能 |