Qt事件处理全解析
Qt事件
事件介绍
事件是应用程序内部或者外部产生的事件或者动作的统称。在Qt中使用一个对象来表示一个事件。所有的Qt事件均继承于抽象类QEvent。事件是由系统或者Qt平台本身Qt平台本身在不同的时刻发出的。当⽤⼾按 下⿏标、敲下键盘,或者是窗⼝需要重新绘制的时候,都会发出⼀个相应的事件。⼀些事件是在⽤⼾ 操作时发出,如键盘事件、⿏标事件等,另⼀些事件则是由系统本⾝⾃动发出,如定时器事件。常⻅的 Qt 事件如下:
常见事件描述:
| 事件名称 | 描述 |
| 鼠标事件 | 鼠标左键、右键、滚轮、鼠标的移动、按下和松开 |
|
键盘事件
|
按键类型、按键按下、按键松开
|
|
定时器事件
|
定时时间到达
|
|
进⼊离开事件
|
⿏标的进⼊和离开
|
|
滚轮事件
|
⿏标滚轮滚动
|
|
绘屏事件
|
重绘屏幕的某些部分
|
|
显⽰隐藏事件
|
窗⼝的显⽰和隐藏
|
|
移动事件
|
窗⼝位置的变化
|
|
窗⼝事件
|
是否为当前窗⼝
|
|
⼤⼩改变事件
|
窗⼝⼤⼩改变
|
|
焦点事件
|
键盘焦点移动
|
|
拖拽事件
|
⽤⿏标进⾏拖拽
|
事件的处理
事件处理一般常用的方法为:重写相关的Event函数
#include "mylabel.h"
#include <QDebug>
#include <QMouseEvent>
MyLabel::MyLabel(QWidget *parent)
: QLabel{parent}
{}
void MyLabel::enterEvent(QEnterEvent *event)
{
qDebug()<<"鼠标进入";
}
void MyLabel::mousePressEvent(QMouseEvent *ev)
{
QString pos = QString("鼠标按下的位置为(%1,%2)").arg(ev->x()).arg(ev->y());
qDebug()<<pos;
}

按键事件
Qt中的按键事件是通过QKeyEvent类来实现的。当键盘上的按键被按下或者被释放的时,键盘事件就会触发。

单个按键
#include "widget.h"
#include "ui_widget.h"
#include <QKeyEvent>
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::keyPressEvent(QKeyEvent *e)
{
if(e->key()==Qt::Key_A){
qDebug()<<"A按键被按下了";
}
}

组合按键

#include "widget.h"
#include "ui_widget.h"
#include <QKeyEvent>
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::keyPressEvent(QKeyEvent *e)
{
// 判断Ctrl键是否按下
if(e->modifiers()==Qt::ControlModifier)
{
// 判断A键是否按下
if(e->key()==Qt::Key_A){
qDebug()<<"Ctrl+A按键被按下了";
}
}
}

鼠标事件
在Qt中,鼠标事件使用QMouseEvent类来实现的。当窗口中按下鼠标或者移动鼠标的时候,都会产生鼠标事件。
利用QMouseEvent类可以获取鼠标的哪个键被按下以及鼠标的当前位置等信息。在Qt帮助文档中查找QMouseEvent类

鼠标单击事件
在Qt中,鼠标按下是通过虚函数mousePressEvent()来捕获的
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QMouseEvent>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::mousePressEvent(QMouseEvent *e)
{
if(e->button()==Qt::LeftButton){
qDebug()<<"鼠标左键被按下了";
}
}

鼠标释放事件
鼠标释放事件是通过虚函数moustReleaseEvent()来捕获的。
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QMouseEvent>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::mouseReleaseEvent(QMouseEvent *e)
{
if(e->button()==Qt::LeftButton){
qDebug()<<"鼠标左键被释放";
}
}

鼠标双击事件
鼠标双击事件是通过虚函数:mouseDoubleClickEvent()来实现。
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QMouseEvent>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::mouseDoubleClickEvent(QMouseEvent *e)
{
if(e->button()==Qt::LeftButton){
qDebug()<<"鼠标双击";
}
}

鼠标移动事件
鼠标移动事件是通过虚函数:mouseMoveEvent()来实现的。同时为了实时捕获鼠标位置信息,需要通过函数setMouseTracking()来追踪鼠标的位置。
setMouseTracking()函数默认是false,需要设置为true,才能实时捕获鼠标位置信息。否则只有当鼠标按下的时候才能捕获其位置信息。
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QMouseEvent>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
setMouseTracking(true);
}
Widget::~Widget()
{
delete ui;
}
void Widget::mouseMoveEvent(QMouseEvent *e)
{
qDebug()<<"["<<e->x()<<","<<e->y()<<"]";
}

滚轮事件
在Qt中,鼠标滚轮事件是通过QWheelEvent类来实现的。滚轮滑动的距离可以通过delta函数获取。
不过在,Qt6中,已经移除了delta()方法,引入了angleDelta()方法替代它
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QWheelEvent>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
setMouseTracking(true);
}
Widget::~Widget()
{
delete ui;
}
void Widget::wheelEvent(QWheelEvent *e)
{
static int x = 0;
x += e->angleDelta().y(); // y()对应垂直滚轮的移动距离
if(e->angleDelta().y() > 0){
// 滚轮向上滚动
qDebug() << "向上滚动,累计值:" << x;
}else{
// 滚轮向下滚动
qDebug() << "向下滚动,累计值:" << x;
}
}

定时器
Qt中进行窗口程序的处理过程中,要经常周期性的执行某些操作,或者制作一些动画效果,使用定时器就能完成。所谓定时器就是在间隔一定时间后,去指定某一个任务。定时日在很多场景下都会使用到。
Qt中的定时器分为QTimerEvent和QTimer这两个类
- QTimerEvent类用来描述一个定时器事件。在使用需要通过startTimeer()函数来开启一个定时器。这个函数需要输入一个以韩淼为单位的整数作为参数来表明设定的事件,它返回的整型值代表这个定时器。当定时器溢出时就可以在timerEvent函数中获取该定时器的编号来进行相关操作。
- QTImer类实现一个定时器,他提供了更高层次的编程接口,可以使用信号和槽,还可以设置只运行一侧的定时器。
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);
time_id0 = startTimer(1000);
time_id1 = startTimer(2000);
}
Widget::~Widget()
{
delete ui;
}
void Widget::timerEvent(QTimerEvent *e)
{
if(e->timerId()==time_id0){
static int num1 = 1;
ui->label->setText(QString::number(num1++));
}
if(e->timerId()==time_id1){
static int num2 = 1;
ui->label_2->setText(QString::number(num2++));
}
}

QTimer类
示例:在UI界面防止一个Label标签,分别是“开始”,“停止”,当点击“开始”按钮的时候,开始每个1s计数一次,点击“停机”按钮的时候,暂停计数
#include "widget.h"
#include "ui_widget.h"
#include <QTimer>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QTimer* time = new QTimer(this);
connect(ui->btn_start,&QPushButton::clicked,this,[=](){
time->start(1000);
});
connect(time,&QTimer::timeout,this,[=](){
static int num = 1;
ui->label->setText(QString::number(num++));
});
connect(ui->btn_stop,&QPushButton::clicked,this,[=](){
time->stop();
});
}
Widget::~Widget()
{
delete ui;
}

获取系统日期以及事件
在Qt中,获取系统的日期以及实时事件可以通过QTimer类和QDataTime类
QDataTime类提供了字符串格式的时间。字符串形式的时间输出格式由toString()方法中的format参数列表决定
示例:获取系统日期以及实时时间
#include "widget.h"
#include "ui_widget.h"
#include <QTimer>
#include <QDateTime>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QTimer* time = new QTimer(this);
connect(ui->btn_start,&QPushButton::clicked,this,[=](){
time->start(1000);
});
connect(ui->btn_stop,&QPushButton::clicked,this,[=](){
time->stop();
});
connect(time,&QTimer::timeout,this,&Widget::TimeUpDate);
}
Widget::~Widget()
{
delete ui;
}
void Widget::TimeUpDate()
{
QString str = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
ui->label->setText(str);
}

事件分发器
在Qt中,事件分发器是一个核心概念,用于处理GUI应用程序中的事件。事件分发器负责讲事件从一个对象传递到另一个对象,直接时间被处理或者被取消。每个继承在QObject类或都可以在本类中重写bool event函数,来实现相关的事件的捕获和拦截
事件分发器工作原理
在Qt中,我们发送的事件都是传给了QObject对象,更具体点是传给了QObject对象的event函数。所有的事件都会进入到这个函数里面,那么我们处理时间就要重写这个event函数。event函数本身不会去处理事件,而是根据事件类型调用不同的事件处理函数。事件分发器就是工作在应用程序下分发时间的过程中。

事件分发器用于分发时间。在此过程中,事件分发器也可以做拦截操作。事件分发器主要是通过bool event函数来实现的。返回值是布尔类型,true表示拦截,不向下分发。
#include "widget.h"
#include "ui_widget.h"
#include <QMouseEvent>
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::mousePressEvent(QMouseEvent *e)
{
if(e->button()==Qt::LeftButton){
qDebug()<<"鼠标左键被按下";
}
}
bool Widget::event(QEvent *event)
{
if(event->type()==QEvent::MouseButtonPress){
qDebug()<<"event中鼠标左键被按下";
return true;
}
return QWidget::event(event);
}

事件过滤器
- 安装时间过滤器
- 重写时间过滤器函数: eventfilter
#include "widget.h"
#include "ui_widget.h"
#include <QMouseEvent>
#include <QDebug>
#include <QEvent>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->label->installEventFilter(this);
}
Widget::~Widget()
{
delete ui;
}
bool Widget::eventFilter(QObject *watched, QEvent *event)
{
if(watched==ui->label){
if(event->type()==QMouseEvent::MouseButtonPress){
QMouseEvent* e = static_cast<QMouseEvent*>(event);
QString str = QString("事件过滤器 鼠标按下 [%1,%2]").arg(e->x()).arg(e->y());
qDebug()<<str;
return true;
}
}
return QWidget::eventFilter(watched,event);
}
#include "mylabel.h"
#include <QMouseEvent>
myLabel::myLabel(QWidget *parent)
: QLabel{parent}
{}
void myLabel::mousePressEvent(QMouseEvent *e)
{
QString str = QString("鼠标按下,[%1,%2]").arg(e->x()).arg(e->y());
qDebug()<<str.toUtf8().data();
}
bool myLabel::event(QEvent *e)
{
// 如果鼠标按下,在event事件分发时做拦截操作
if(e->type()==QEvent::MouseButtonPress){
QMouseEvent* event = static_cast<QMouseEvent*>(e);
QString str = QString("Event鼠标按下,[%1,%2]").arg(event->x()).arg(event->y());
qDebug()<<str.toUtf8().data();
return true;
}
return QLabel::event(e);
}

Qt文件
Qt文件概述
输入输出设备类

文件读写类
- 读文件:QFIle类中提供了多种方法用于读取文件内容:如read()、readAll()、readLine()等等
- 写数据:QFIle类提供了多个方法用户向文件中写内容;如write()、writeData()等
- 关闭文件:文件使用结束之后必须用函数close()关闭文件
访问一个设备之前,需要使用open函数打开该设备,而且必须指定正确的打开模式,QIODevice中所有的打开模式有QIODevice::OpenMode枚举变量定义

示例1:读取文件内容
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
#include <QFileDialog>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
connect(ui->btn,&QPushButton::clicked,this,[=](){
QString path = QFileDialog::getOpenFileName(this,"打开文件","C:\\Users\\Lenovo\\Desktop");
ui->lineEdit->setText(path);
QFile file(path);
file.open(QIODeviceBase::ReadOnly);
QString content = file.readAll();
ui->textEdit->setText(content);
file.close();
});
}
Widget::~Widget()
{
delete ui;
}

实例2:向文件中写入内容
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
#include <QFileDialog>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
connect(ui->btn,&QPushButton::clicked,this,[=](){
QString path = QFileDialog::getOpenFileName(this,"打开文件","C:\\Users\\Lenovo\\Desktop");
ui->lineEdit->setText(path);
QFile file(path);
file.open(QIODeviceBase::Append);
file.write("新增内容");
file.close();
});
}
Widget::~Widget()
{
delete ui;
}


文件和目录信息类
QFileInfo是Qt提供的一个用户获取文件和目录信息的类,如获取文件名,文件大小,文件修改日期等。QFileInfo类中提供了很多的方法。
- isDir()检查该文件是否是目录
- isExecutable()检查该文件是否为可执行文件
- fileName()获取文件名
- completaBasename()获取完整的文件名
- suffix()获取文件后缀名
- completeSuffix()获取完整的文件后缀
- size()获取文件大小
- isFile()判断是否为文件
- fileTime()获取文件创建时间、修改时间、最近访问时间
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
#include <QFileDialog>
#include <QFileInfo>
#include <QDateTime>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
connect(ui->btn,&QPushButton::clicked,this,[=](){
QString path = QFileDialog::getOpenFileName(this,"打开文件","C:\\Users\\Lenovo\\Desktop");
ui->lineEdit->setText(path);
QFileInfo fileInfo(path);
qDebug()<<"文件名为:"<<fileInfo.fileName();
qDebug()<<"后缀名为:"<<fileInfo.suffix();
qDebug()<<"文件大小为:"<<fileInfo.size();
qDebug()<<"文件路径为:"<<fileInfo.path();
qDebug()<<"是否为文件"<<fileInfo.isFile();
QDateTime time1 = fileInfo.fileTime(QFileDevice::FileBirthTime);
qDebug()<<"创建时间为:"<<time1.toString("yyyy-MM-dd hh:mm:ss");
QDateTime time2 = fileInfo.lastModified();
qDebug()<<"最近修改时间为:"<<time2.toString("yyyy-MM-dd hh:mm:ss");
qDebug() << "是否为目录:" << fileInfo.isDir();
});
}
Widget::~Widget()
{
delete ui;
}

Qt多线程
Qt多线程概述
在Qt中,多线程的处理一般是通过QThread来实现的
QThread代表一个在应用程序中可以独立控制的线程,也可以和进程中的其他线程共享数据
QThread对象管理程序中的一个控制线程
常用API
| run() | 线程的入口函数 |
| start() | 通过调用run()开始执行线程。操作系统将根据优先级参数调度线程。如果线程已经在运行,这个函数什么也不做。 |
| currentThread() | 返回一个指向管理当前执行线程的QThread指针 |
| isRunning() | 如果线程正在运行,返回true;否则,返回false |
| sleep()/msleep()/usleep() | 是线程休眠,单位为s/ms/us |
| wait() |
阻塞线程,知道以下任何一个条件
与此 QThread 对象关联的线程已经完成执⾏(即当它从run()返回时)。如果线程已经完成,这个函数将返回 true。如果线程尚未启动,它也返回 true。
已经过了⼏毫秒。如果时间是 ULONG_MAX(默认值),那么等待永远不会超时(线程必须从run()返回)。如果等待超时,此函数将返回 false。这提供了与 POSIX pthread_join() 函数类似的功能。
|
| terminate() | 中值线程的执行。线程可以立即中值,也可以不终止。这取决于操作系统的调度策略。在terminate()之后使用QThread::wait()来确保 |
| finished() | 当线程结束时会发出该信号,可以通过该信号来实现线程的清理工作。 |
使用线程
创建线程的步骤:
- 自定义一个类,继承自QThread,并且只有一个线程处理函数(和主线程不是同一个线程),这个线程处理函数主要是重写父类中的run()函数
- 线程处理函数里面写入需要执行的复杂数据处理
- 启动线程不能直接调用run()函数,需要使用对象来调用start()函数来实现线程启动
- 线程处理函数执行结束后可以定义一个信号来告诉主线程
- 最后关闭线程
// widget.cc
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
connect(&t,&TimeThread::sendTime,this,&Widget::showTime);
}
Widget::~Widget()
{
t.stop(); // 通知线程退出
t.wait(); // 等待线程安全结束
delete ui;
}
void Widget::on_pushButton_clicked()
{
t.start(); //开启线程
}
void Widget::showTime(QString Time)
{
ui->label->setText(Time);
}
//timethread.cc
#include "timethread.h"
#include <QTime>
#include <QDebug>
TimeThread::TimeThread() {}
void TimeThread::run()
{
running = true;
while(running)
{
QString time = QTime::currentTime().toString("hh:mm:ss");
emit sendTime(time);
sleep(1);
}
}
void TimeThread::stop()
{
running = false;
}

- 线程函数内部不允许操作UI图形界面,一般用数据处理
- connect()函数第五个参数表示的是连接的方式,且只有在多线程的时候才有意义
|
Qt::AutoConnection
|
在 Qt 中,会根据信号和槽函数所在的线程⾃动选择连接类型。如果信号和槽函数在同⼀线程中,那么使⽤ Qt:DirectConnection 类型;如果它们位于不同的线程中,那么使⽤Qt::QueuedConnection 类型。
|
|
Qt::DirectConnection
|
当信号发出时,槽函数会⽴即在同⼀线程中执⾏。这种连接类型适⽤于信号和槽函数在同⼀线程中的情况,可以实现直接的函数调⽤,但需要注意线程安全性
|
|
Qt::QueuedConnection
|
当信号发出时,槽函数会被插⼊到接收对象所属的线程的事件队列中,等待下⼀次事件循环时执⾏。这种连接类型适⽤于信号和槽函数在不同线程中的情况,可以确保线程安全。
|
|
Qt::BlockingQueuedConnec
tion
|
与 Qt:QueuedConnection 类似,但是发送信号的线程会被阻塞,直到槽函数执⾏完毕,这种连接类型适⽤于需要等待槽函数执⾏完毕再继续的场景,但需要注意可能引起线程死锁的⻛险。
|
|
Qt::UniqueConnection
|
这是⼀个标志,可以使⽤位或与上述任何⼀种连接类型组合使⽤
|
线程安全
实现线程互斥和同步的常用类有:
- 互斥锁:QMutex,QMutexLocker
- 条件变量:QWaitCondition
- 信号量:QSemaphore
- 读写锁:QReadLocker、QWriteLocker,QReadWriteLock
互斥锁
互斥锁是一种保护和防止多个线程同时访问同一对象实例的方法,在Qt中,互斥锁主要是通过QMutex类来处理的
- QMutex
特点:QMutex是Qt框架的互斥锁类,用于保护共享资源的访问,实现线程间的互斥操作。
用途:在多线程环境下,通过互斥锁来控制对共享数据的访问,确保线程安全
- QMutexLocker
特点:QMutexLocker是QMutex的辅助类,使用RAII方式对互斥锁进行上锁和解锁操作
用途:简化对互斥锁的上锁和解锁操作,避免顽疾解锁导致死锁的问题
- QReadWriteLocker,QReadLocker,QWriteLocker
特点:
QReadWriteLock是读写锁类,用于控制读和写的并发访问
QReadLocker用于读操作上锁,允许多个线程同时读取共享资源
QWriteLocker用于写操作上锁,只运行一个线程写入共享资源。
#include "mythread.h"
#include <QDebug>
QMutex myThread::mutex;
int myThread::num = 0;
myThread::myThread(QObject* parent):
QThread(parent)
{
}
void myThread::run()
{
while(running){
mutex.lock();
qDebug() << "Current Thread: " << this << ", Value: " << this->num++;
mutex.unlock();
QThread::sleep(2);
}
}
void myThread::stop()
{
running = false;
}
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
t1 = new myThread(this);
t2 = new myThread(this);
t1->start();
t2->start();
}
Widget::~Widget()
{
t1->stop();
t1->wait();
t2->stop();
t2->wait();
delete ui;
}

#include "mythread.h"
#include <QDebug>
QMutex myThread::mutex;
int myThread::num = 0;
myThread::myThread(QObject* parent):
QThread(parent)
{
}
void myThread::run()
{
while(running){
{
QMutexLocker lock(&this->mutex);
qDebug() << "Current Thread: " << this << ", Value: " << this->num++;
}
QThread::sleep(1);
}
}
void myThread::stop()
{
running = false;
}

条件变量
信号量
Qt网络
UDP Socket
核⼼ API 概览
|
名称
|
类型 | 说明 | 对应API接口 |
|
bind(const QHostAddress&,
quint16)
|
⽅法
|
绑定指定的端⼝号
|
bind |
|
receiveDatagram()
|
⽅法
|
返回 QNetworkDatagram . 读取
⼀个 UDP 数据报
|
recvfrom
|
|
writeDatagram(const
QNetworkDatagram&)
|
⽅法
|
发送⼀个 UDP 数据报
|
sendto
|
|
readyRead
|
信号 |
在收到数据并准备就绪后触发
|
⽆ (类似于 IO 多路复⽤的通
知机制)
|
|
名称
|
类型 | 说明 | 对应API接口 |
|
QNetworkDatagram(const
QByteArray&, const
QHostAddress& , quint16 )
|
构造函数 |
通过 QByteArray , ⽬标 IP 地址,
⽬标端⼝号 构造⼀个 UDP 数据报.
通常⽤于发送数据时.
|
无 |
|
data()
|
⽅法
|
获取数据报内部持有的数据. 返回
QByteArray
|
无 |
|
senderAddress()
|
⽅法
|
获取数据报中包含的对端的 IP 地
址.
|
⽆, recvfrom 包含了该功
能.
|
|
senderPort()
|
⽅法
|
获取数据报中包含的对端的端⼝号
|
⽆, recvfrom 包含了该功
能.
|
回显服务器
#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QNetworkDatagram>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 1. 设置窗口标题
this->setWindowTitle("服务器");
// 2. 实例化socket
socket = new QUdpSocket(this);
// 3. 连接信号槽,处理收到的请求
connect(socket,&QUdpSocket::readyRead,this,&Widget::processRequest);
// 4. 绑定端口
bool ret = socket->bind(QHostAddress::Any, 9090);
if(!ret){
QMessageBox::critical(nullptr, "服务器启动出错", socket->errorString());
return;
}
}
Widget::~Widget()
{
delete ui;
}
void Widget::processRequest()
{
// 1. 读取请求
const QNetworkDatagram& requestDatagram = socket->receiveDatagram();
QString request = requestDatagram.data();
// 2. 根据请求计算响应
const QString& response = process(request);
// 3. 把响应写回客户端
QNetworkDatagram responseDatagram(response.toUtf8(),
requestDatagram.senderAddress(), requestDatagram.senderPort());
socket->writeDatagram(responseDatagram);
// 显⽰打印⽇志
QString log = "[" + requestDatagram.senderAddress().toString() + ":" +
QString::number(requestDatagram.senderPort())
+ "] req: " + request + ", resp: " + response;
ui->listWidget->addItem(log);
}
QString Widget::process(const QString& reqeust)
{
return reqeust;
}
回显客户端
#include "widget.h"
#include "ui_widget.h"
#include <QNetworkDatagram>
const QString& SERVER_IP = "127.0.0.1";
const quint16 SERVER_PORT = 9090;
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 1. 设置窗⼝名字
this->setWindowTitle("客⼾端");
// 2. 实例化 socket
socket = new QUdpSocket(this);
connect(socket, &QUdpSocket::readyRead, this, [=]() {
const QNetworkDatagram responseDatagram = socket->receiveDatagram();
QString response = responseDatagram.data();
ui->listWidget->addItem(QString("服务器说: ") + response);
});
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
// 1. 获取输入框中的内容
const QString& text = ui->lineEdit->text();
// 2. 构造请求数据
QNetworkDatagram requestDatagram(text.toUtf8(),QHostAddress(SERVER_IP),SERVER_PORT);
// 3. 发送请求
socket->writeDatagram(requestDatagram);
// 4. 消息添加到列表框中
ui->listWidget->addItem("客户端说"+text);
// 5. 清空输入框
ui->lineEdit->setText("");
}

TCP Socket
核⼼ API 概览
|
名称
|
类型
|
说明
|
对标原⽣ API
|
|
listen(const QHostAddress&,
quint16 port)
|
⽅法
|
绑定指定的地址和端⼝号, 并开始监
听
|
bind 和 listen
|
|
nextPendingConnection()
|
⽅法
|
从系统中获取到⼀个已经建⽴好的
tcp 连接.
返回⼀个 QTcpSocket , 表⽰这个
客⼾端的连接.
通过这个 socket 对象完成和客⼾端
之间的通信
|
accept |
|
newConnection
|
⽅法
|
有新的客⼾端建⽴连接好之后触发
|
⽆ (但是类似于 IO 多路复⽤
中的通知机制)
|
QTcpSocket ⽤⼾客⼾端和服务器之间的数据交互.
|
名称
|
类型 | 说明 | 对标原⽣ API |
|
readAll()
|
方法 |
读取当前接收缓冲区中的所有数据.
返回 QByteArray 对象
|
read
|
|
write(const QByteArray& )
|
方法 |
把数据写⼊ socket 中
|
write
|
|
deleteLater
|
方法 |
暂时把 socket 对象标记为⽆效. Qt
会在下个事件循环中析构释放该对
象.
|
⽆ (但是类似于 "半⾃动化的
垃圾回收")
|
|
readyRead
|
信号 |
有数据到达并准备就绪时触发
|
⽆ (但是类似于 IO 多路复⽤
中的通知机制)
|
|
disconnected
|
信号 |
连接断开时触发
|
⽆ (但是类似于 IO 多路复⽤中的通知机制)
|
回显服务器
#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QTcpSocket>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 1. 设置窗口标题
this->setWindowTitle("服务器");
// 2. 实例化TCP
tcpServer = new QTcpServer(this);
// 3. 通过信号槽, 处理客⼾端建⽴的新连接.
connect(tcpServer, &QTcpServer::newConnection, this,
&Widget::processConnection);
// 4. 绑定监听端口
bool ret = tcpServer->listen(QHostAddress::Any,9090);
if(!ret){
QMessageBox::critical(nullptr, "服务器启动失败!", tcpServer->errorString());
exit(1);
}
}
Widget::~Widget()
{
delete ui;
}
void Widget::processConnection()
{
// 1. 获取到新的连接对应的 socket
QTcpSocket* clientSocket = tcpServer->nextPendingConnection();
QString log = QString("[") + clientSocket->peerAddress().toString()+ ":"
+ QString::number(clientSocket->peerPort()) + "] 客⼾端上线!";
// 2. 通过信号槽,处理收到的请求
connect(clientSocket,&QTcpSocket::readyRead,this,[=](){
QString request = clientSocket->readAll();
const QString& response = process(request);
clientSocket->write(response.toUtf8());
QString log = QString("[") + clientSocket->peerAddress().toString()
+ ":" + QString::number(clientSocket->peerPort()) + "] req: " +
request + ", resp: " + response;
ui->listWidget->addItem(log);
});
// 3. 通过信号槽, 处理断开连接的情况
connect(clientSocket, &QTcpSocket::disconnected, this, [=]() {
QString log = QString("[") + clientSocket->peerAddress().toString()
+ ":" + QString::number(clientSocket->peerPort()) + "] 客⼾端下线!";
ui->listWidget->addItem(log);
// 删除 clientSocket
clientSocket->deleteLater();
});
}
QString Widget::process(const QString &reqeust)
{
return reqeust;
}
回显客户端
#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 1. 设置窗口标题
this->setWindowTitle("客户端");
// 2. 实例化TCP
socket = new QTcpSocket(this);
// 3. 和服务器建立连接
socket->connectToHost("127.0.0.1",9090);
if (!socket->waitForConnected()) {
QMessageBox::critical(nullptr, "连接服务器出错!", socket->errorString());
exit(1);
}
connect(socket, &QTcpSocket::readyRead, this, [=]() {
QString response = socket->readAll();
qDebug() << response;
ui->listWidget->addItem(QString("服务器说: ") + response);
});
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
// 1. 获取输入框中的内容
const QString& message = ui->lineEdit->text();
// 2. 把消息显示在屏幕上
ui->listWidget->addItem("客户端说:"+message);
// 3. 发送消息
socket->write(message.toUtf8());
// 4. 清楚输入框
ui->lineEdit->setText("");
}

HTTP Client
核⼼ API
|
⽅法
|
说明 |
|
get(const QNetworkRequest& )
|
发起⼀个 HTTP GET 请求. 返回 QNetworkReply 对象
|
|
post(const QNetworkRequest& , const
QByteArray& )
|
发起⼀个 HTTP POST 请求. 返回 QNetworkReply 对
象.
|
| ⽅法 | 说明 |
|
QNetworkRequest(const QUrl& )
|
通过 URL 构造⼀个 HTTP 请求
|
|
setHeader(QNetworkRequest::KnownHeaders
header,
const QVariant &value)
|
设置请求头
|
其中的 QNetworkRequest::KnownHeaders 是⼀个枚举类型, 常⽤取值
|
取值
|
说明
|
|
ContentTypeHeader
|
描述 body 的类型
|
|
ContentLengthHeader
|
描述 body 的⻓度.
|
|
LocationHeader
|
⽤于重定向报⽂中指定重定向地址. (响应中使⽤, 请求
⽤不到)
|
|
CookieHeader
|
设置 cookie
|
|
UserAgentHeader
|
设置 User-Agent
|
|
⽅法
|
说明
|
|
error()
|
获取出错状态
|
|
errorString()
|
获取出错原因的⽂本
|
|
readAll()
|
读取响应 body
|
|
header(QNetworkRequest::KnownHeaders
header)
|
读取响应指定 header 的值
|
Qt ⾳视频
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
player = new QMediaPlayer(this);
// 设置音频文件(确保资源文件中存在 1.wav)
player->setSource(QUrl(":/1.wav"));
connect(ui->btn, &QPushButton::clicked, [=](){
player->play();
});
}
Widget::~Widget()
{
delete ui;
}
Qt视频
核⼼API概览
|
setMedia()
|
设置当前媒体源
|
|
setVideoOutput()
|
将QVideoWidget视频输出附加到媒体播放器。
如果媒体播放器已经附加了视频输出,将更换⼀个新的
|
更多推荐




所有评论(0)