<5>, Qt系统相关

article/2025/7/28 6:22:27

目录

一、Qt 事件

1,事件的定义

2,事件的处理

3,鼠标事件

4,按键事件

5,定时器

6,事件分发器

7,事件过滤器

二、Qt 文件

1,输入输出类

2,文件读写类

3,文件和目录信息类

4,示例

三、Qt 多线程

1 ,Qt 多线程概述

2 ,QThread 常用 API

3 ,使用线程

4 ,线程安全

(1) 互斥锁:QMutex、QMutexLocker

(2) 条件变量:QWaitCondition

(3) 信号量:QSemaphore

(4) 读写锁:QReadLocker、QWriteLocker、QReadWriteLock

四、Qt 网络

1, 核心 API 概览

2, 回显服务器和客户端

3, HTTP Client

五、Qt 音视频

1,Qt 音频

2,Qt 视频


一、Qt 事件

1,事件的定义

事件是应用程序内部或者外部产生的事情或者动作的统称。在 Qt 中使用一个对象来表示一个事件。所有的 Qt 事件均继承于抽象类 QEvent。事件是由系统或者 Qt 平台本身在不同的时刻发出的。 当用戶按下鼠标、敲下键盘 ,或者是窗口需要重新绘制的时候 ,都会发出一个相应的事件。

事件名称

描述

鼠标事件

鼠标左键、 鼠标右键、 鼠标滚轮 ,

鼠标的移动 ,鼠标按键的按下和松开

键盘事件

按键类型、按键按下、按键松开

定时器事件

定时时间到达

进入离开事件

鼠标的进入和离开

滚轮事件

鼠标滚轮滚动

绘屏事件

重绘屏幕的某些部分

显示隐藏事件

窗口的显示和隐藏

移动事件

窗口位置的变化

窗口事件

是否为当前窗口

大小改变事件

窗口大小改变

焦点事件

键盘焦点移动

2,事件的处理

事件处理⼀般常⽤的⽅法为:重写相关的 Event 函数。

在 Qt 中,⼏乎所有的 Event 函数都是虚函数,所以可以重新实现。如:在实现⿏标的进⼊和离开事件时,直接重新实现 enterEvent() 和 leaveEvent() 即可。enterEvent() 和 leaveEvent() 函数原型如下:

3,鼠标事件

鼠标点击

void Label::mousePressEvent(QMouseEvent *event)
{if(event->button() == Qt::LeftButton){qDebug()<<"按下左键";}else if(event->button() == Qt::RightButton){qDebug()<<"按下右键";}// 当前 event 对象就包含了鼠标点击的坐标qDebug() << event->x() << "," << event->y();// 以屏幕左上角为原点的坐标qDebug() << event->globalX() << "," << event->globalY();
}void Label::mouseReleaseEvent(QMouseEvent *event)
{if(event->button() == Qt::LeftButton){qDebug()<<"释放左键";}else if(event->button() == Qt::RightButton){qDebug()<<"释放右键";}
}void Label::mouseDoubleClickEvent(QMouseEvent *event)
{if(event->button() == Qt::LeftButton){qDebug()<<"双击左键";}else if(event->button() == Qt::RightButton){qDebug()<<"双击右键";}
}

鼠标移动和滚轮移动

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 把这个选项设置为true,才能够追踪鼠标位置this->setMouseTracking(true);
}void Widget::mouseMoveEvent(QMouseEvent *event)
{qDebug() << event->x() << event->y();
}void Widget::wheelEvent(QWheelEvent *event)
{qDebug() << event->delta();
}

4,按键事件

单键

void Widget::keyPressEvent(QKeyEvent *event)
{if(event->key() == Qt::Key_A){qDebug()<<"A键被按下";}
}

组合键

Qt::NoModifier

无修改键

Qt::ShiftModifier

Shift 键

Qt::ControlModifier

Ctrl 键

Qt::AltModifier

Alt 键

Qt::MetaModifier

Meta键(在Windows上指Windows键 ,在macOS上指Command键)

Qt::KeypadModifier

使用键盘上的数字键盘进行输入时 ,Num Lock键处于打开状态

Qt::GroupSwitchModifier

用于在输入法组之间切换

5,定时器

Qt中的定时器分为QTimerEvent 和QTimer 这2个类。

QTimerEvent类

用来描述一个定时器事件。在使用时需要通过startTimer() 函数来开启一个定时器,这个函数需要输入一个以毫秒为单位的整数作为参数来表明设定的时间,它返回的整型值代表这个定时器。 当定时器溢出时(即定时时间到达)就可以在 timerEvent() 函数中获取该定时器的编号来进行相关操作。

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 把这个选项设置为true,才能够追踪鼠标位置this->setMouseTracking(true);timer_id1 = startTimer(1000);timer_id2 = startTimer(2000);
}void Widget::timerEvent(QTimerEvent *event)
{if(event->timerId() == timer_id1){static int num1 = 1;// lb1每隔1秒加一次ui->label->setText(QString::number(num1++));}if(event->timerId() == timer_id2){static int num2 = 1;// lb2每隔2秒加一次ui->label_2->setText(QString::number(num2++));}
}

QTimer类

用来实现一个定时器,它提供了更高层次的编程接口,如:可以使用信号和槽,还可以设置只运行一次的定时器。

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{connect(ui->pushButton, &QPushButton::clicked,[=](){time->start(1000);});connect(time,&QTimer::timeout,[=](){static int num = 1;ui->label->setText(QString::number(num++));});connect(ui->pushButton_2, &QPushButton::clicked,[=](){time->stop();});
}

6,事件分发器

在 Qt 中,我们发送的事件都是传给了 QObject 对象,更具体点是传给了 QObject 对象的 event() 函数。所有的事件都会进⼊到这个函数⾥⾯,那么我们处理事件就要重写这个 event() 函数。event() 函数本⾝不会去处理事件,⽽是根据 事件类型(type值)调⽤不同的事件处理函数。

7,事件过滤器

在 Qt 中,⼀个对象可能经常要查看或拦截另外⼀个对象的事件,如对话框想要拦截按键事件,不让别的组件接收到,或者修改按键的默认值等。通过上⾯的学习,我们已经知道,Qt 创建了 QEvent事件对象之后,会调⽤QObject 的 event()函数 处理事件的分发。显然,我们可以在 event()函数 中实现拦截的操作。由于 event()函数是 protected 的,因此,需要继承已有类。如果组件很多,就需要重写很多个event()函数。这当然相当⿇烦,更不⽤说重写 event()函数还得⼩⼼⼀堆问题。好在 Qt 提供了另外⼀种机制来达到这⼀⽬的:事件过滤器。

二、Qt 文件

1,输入输出类

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,文件读写类

在 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

文件存在则打开失败 ,不存在则创建文件

3,文件和目录信息类

QFileInfo 是 Qt 提供的一个用于获取文件和目录信息的类 ,如获取文件名、文件大小、文件修改日期等。QFileInfo类中提供了很多的方法 ,常用的有:

isDir() 检查该文件是否是目录;

isExecutable() 检查该文件是否是可执行文件;

fileName() 获得文件名;

completeBaseName() 获取完整的文件名;

suffix() 获取文件后缀名;

completeSuffix() 获取完整的文件后缀;

size() 获取文件大小;

isFile() 判断是否为文件;

fileTime() 获取文件创建时间、修改时间、最近访问时间等;

4,示例

class MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();void handleAction1();void handleAction2();private:Ui::MainWindow *ui;QPlainTextEdit* edit;
};MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);this->setWindowTitle("简单的记事本");// 获取到菜单栏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);// 指定一个输入框edit = new QPlainTextEdit();QFont font;font.setPixelSize(20);edit->setFont(font);this->setCentralWidget(edit);// 连接QAction的信号槽connect(action1,&QAction::triggered,this,&MainWindow::handleAction1);connect(action2,&QAction::triggered,this,&MainWindow::handleAction2);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::handleAction1()
{// 先弹出"打开文件"对话框,让用户选择打开哪个文件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::handleAction2()
{// 先弹出"保存文件"对话框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();
}

三、Qt 多线程

1 ,Qt 多线程概述

在 Qt 中 ,多线程的处理一般是通过 QThread类 来实现。

QThread 代表一个在应用程序中可以独立控制的线程 ,也可以和进程中的其他线程共享数据。

QThread 对象管理程序中的一个控制线程。

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 ,使用线程

创建线程的步骤:

(1) 自定义一个类 ,继承于 QThread ,并且只有一个线程处理函数(和主线程不是同一个线程) ,这个线程处理函数主要就是重写父类中的 run() 函数。

(2) 线程处理函数里面写入需要执行的复杂数据处理;

(3) 启动线程不能直接调用 run() 函数 ,需要使用对象来调用 start() 函数实现线程启动;

(4) 线程处理函数执行结束后可以定义一个信号来告诉主线程;

(5) 最后关闭线程。

Qt::AutoConnection

在 Qt 中 ,会根据信号和槽函数所在的线程自动选择连接类型。

如果信号和槽函 数在同一线程中 ,那么使用 Qt:DirectConnection 类型;

如果它们位于不同的 线程中 ,那么使用Qt::QueuedConnection 类型。

Qt::DirectConnection

当信号发出时 ,槽函数会立即在同一线程中执行。这种连接类型适用于信号和 槽函数在同一线程中的情况 ,可以实现直接的函数调用 ,但需要注意线程安全 性。

Qt::QueuedConnection

当信号发出时 ,槽函数会被插入到接收对象所属的线程的事件队列中 ,等待下 一次事件循环时执行。

这种连接类型适用于信号和槽函数在不同线程中的情况 ,可以确保线程安全。

Qt::BlockingQueuedConnec tion

与 Qt:QueuedConnection 类似 ,但是发送信号的线程会被阻塞 ,直到槽函数执行完毕 ,这种连接类型适用于需要等待槽函数执行完毕再继续的场景 ,但需要注意可能引起线程死锁的风险。

Qt::UniqueConnection

这是一个标志 ,可以使用位或与上述任何一种连接类型组合使用。

4 ,线程安全

实现线程互斥和同步常用的类有:

(1) 互斥锁:QMutex、QMutexLocker

互斥锁是一种保护和防止多个线程同时访问同一对象实例的方法 ,在 Qt 中 ,互斥锁主要是通过 QMutex类来处理。

特点:QMutex 是 Qt 框架提供的互斥锁类 ,用于保护共享资源的访问 ,实现线程间的互斥操作。

用途:在多线程环境下 ,通过互斥锁来控制对共享数据的访问 ,确保线程安全。

(2) 条件变量:QWaitCondition

在多线程编程中,假设除了等待操作系统正在执行的线程之外,某个线程还必须等待某些条件满足才能执行 ,这时就会出现问题。这种情况下,线程会很自然地使用锁的机制来阻塞其他线程 ,因为这只是线程的轮流使用,并且该线程等待某些特定条件 ,人们会认为需要等待条件的线程 ,在释放互斥锁或读写锁之后进入了睡眠状态 ,这样其他线程就可以继续运行。当条件满足时,等待条件的线程将被另一个线程唤醒。

在 Qt 中 ,专门提供了 QWaitCondition类 来解决像上述这样的问题。

特点:QWaitCondition 是 Qt 框架提供的条件变量类 ,用于线程之间的消息通信和同步。

用途:在某个条件满足时等待或唤醒线程 ,用于线程的同步和协调。

(3) 信号量:QSemaphore

有时在多线程编程中,需要确保多个线程可以相应的访问一个数量有限的相同资源。例如,运行程序 的设备可能是非常有限的内存 ,因此我们更希望需要大量内存的线程将这一事实考虑在内,并根据可用的内存数量进行相关操作,多线程编程中类似问题通常用信号量来处理。信号量类似于增强的互斥锁,不仅能完成上锁和解锁操作, 而且可以跟踪可用资源的数量。

特点:QSemaphore 是 Qt 框架提供的计数信号量类 ,用于控制同时访问共享资源的线程数量。

用途: 限制并发线程数量 ,用于解决一些资源有限的问题。

(4) 读写锁:QReadLocker、QWriteLocker、QReadWriteLock

特点:

QReadWriteLock 是读写锁类 ,用于控制读和写的并发访问。

QReadLocker 用于读操作上锁 ,允许多个线程同时读取共享资源。

QWriteLocker 用于写操作上锁 ,只允许一个线程写入共享资源。

用途:在某些情况下 ,多个线程可以同时读取共享数据 ,但只有一个线程能够进行写操作。读写锁提 供了更高效的并发访问方式。

四、Qt 网络

1, 核心 API 概览

核心类有两个:QTcpServer 和 QTcpSocket

名称

类型

说明

对标原生 API

listen(const QHostAddress&, quint16 port)

方法

绑定指定的地址和端口号, 并开始监 听.

bind 和 listen

nextPendingConnection()

方法

从系统中获取到一个已经建立好的 tcp 连接.

返回一个 QTcpSocket , 表示这个 客户端的连接.

通过这个 socket 对象完成和客户端 之间的通信.

accept

newConnection

信号

有新的客户端建立连接好之后触发.

QTcpSocket 用户客户端和服务器之间的数据交互

名称

类型

说明

对标原生 API

readAll()

方法

读取当前接收缓冲区中的所有数据.

返回 QByteArray 对象.

read

write(const QByteArray& )

方法

把数据写入 socket 中.

write

deleteLater

方法

暂时把 socket 对象标记为无效. Qt 会在下个事件循环中析构释放该对 象.

无 (但是类似于 "半自动化的 垃圾回收")

readyRead

信号

有数据到达并准备就绪时触发.

无 (但是类似于 IO 多路复用 中的通知机制)

disconnected

信号

连接断开时触发.

无 (但是类似于 IO 多路复用 中的通知机制)

2, 回显服务器和客户端

核心类是两个:QTcpServer 和 QTcpSocket。

QTcpServer 用于监听端口, 和获取客户端连接。

名称

类型

说明

对标原生 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 多路复用 中的通知机制)

3, HTTP Client

核心 的API,关键类主要有三个:

QNetworkAccessManager 和 QNetworkRequest 和 QNetworkReply

QNetworkAccessManager 提供了 HTTP 的核心操作

方法

说明

get(const QNetworkRequest& )

发起一个 HTTP GET 请求. 返回 QNetworkReply 对象.

post(const QNetworkRequest& , const

QByteArray& )

发起一个 HTTP POST 请求. 返回 QNetworkReply 对 象.

QNetworkRequest 表示一个 HTTP 请求(不含 body)

方法

说明

QNetworkRequest(const QUrl& )

通过 URL 构造一个 HTTP 请求.

setHeader(QNetworkRequest::KnownHeaders header,

const QVariant &value)

设置请求头.

取值

说明

ContentTypeHeader

描述 body 的类型.

ContentLengthHeader

描述 body 的长度.

LocationHeader

用于重定向报文中指定重定向地址. (响应中使用, 请求 用不到)

CookieHeader

设置 cookie

UserAgentHeader

设置 User-Agent

QNetworkReply 表示一个 HTTP 响应. 这个类同时也是 QIODevice 的子类.

方法

说明

error()

获取出错状态.

errorString()

获取出错原因的文本.

readAll()

读取响应 body.

header(QNetworkRequest::KnownHeaders header)

读取响应指定 header 的值.

五、Qt 音视频

1,Qt 音频

在 Qt 中 ,音频主要是通过 QSound 类来实现。但是需要注意的是 QSound 类只支持播放 wav 格式的音频文件。也就是说如果想要添加音频效果 ,那么首先需要将非wav格式 的音频文件转换为 wav 格式。

示例

#include "widget.h"
#include "ui_widget.h"
#include <QSound>  //添加音频头文件Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget){ui->setupUi(this);//实例化对象QSound *sound = new QSound(":/1.wav",this);connect(ui->btn,&QPushButton::clicked,[=](){ sound->play(); //播放});}

2,Qt 视频

在 Qt 中 ,视频播放的功能主要是通过 QMediaPlayer类 和 QVideoWidget类 来实现。在使用这两个类 时要添加对应的模块 multimedia 和 multimediawidgets 。

示例

// widget.h#include <QWidget>
#include <QHBoxLayout>  //水平布局
#include <QVBoxLayout>  //垂直布局
#include <QVideoWidget> //显示视频
#include <QMediaPlayer> //播放声音
#include <QPushButton> //按钮
#include <QStyle> //设置图标
#include <QFileDialog> //选择文件/文件夹class Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();public slots:void chooseVideo();private:QMediaPlayer *mediaPlayer;QVideoWidget *videoWidget;QVBoxLayout *vbox;//创建两个按钮:选择视频按钮和开播放按钮QPushButton *chooseBtn,*playBtn;};// widget.cpp#include "widget.h"
#include <QMediaPlayer>
#include <QSlider>Widget::Widget(QWidget *parent): QWidget(parent)
{//对象实例化mediaPlayer = new QMediaPlayer(this);videoWidget = new QVideoWidget(this);//设置播放画面的窗口videoWidget->setMinimumSize(600,600);//实例化窗口布局---垂直布局this->vbox = new QVBoxLayout(this);this->setLayout(this->vbox);//实例化选择视频按钮chooseBtn = new QPushButton("选择视频 ",this);//实例化播放按钮playBtn = new QPushButton(this);//设置图标代替文件playBtn->setIcon(this->style()->standardIcon(QStyle::SP_MediaPlay)); 64//实例化一个水平布局,将以上控件放入水平布局中QHBoxLayout *hbox = new QHBoxLayout;//添加控件hbox->addWidget(chooseBtn);hbox->addWidget(playBtn);//将播放窗口和水平布局都添加到垂直布局中vbox->addWidget(videoWidget);//布局中添加布局vbox->addLayout(hbox);//将选择视频对应的按钮和槽函数进行关联connect(chooseBtn,&QPushButton::clicked,this,&Widget::chooseVideo);
}void Widget::chooseVideo()
{//选择视频,返回一个播放视频的名字QString name = QFileDialog::getSaveFileName(this,"选择视频",".","WMV(*.wmv)");//设置媒体声音mediaPlayer->setMedia(QUrl(name));
}

http://www.hkcw.cn/article/kftpKSUaQO.shtml

相关文章

PCB设计教程【强化篇】——USB拓展坞DRC导出生产文件

前言 本教程基于B站Expert电子实验室的PCB设计教学的整理&#xff0c;为个人学习记录&#xff0c;旨在帮助PCB设计新手入门。所有内容仅作学习交流使用&#xff0c;无任何商业目的。若涉及侵权&#xff0c;请随时联系&#xff0c;将会立即处理 目录 前言 丝印调整 DRC 检查…

第十一讲 | 多态

多态 一、多态的概念二、多态的定义及实现1、动态多态的构成条件&#xff08;1&#xff09;、实现多态还有两个必须重要条件&#xff08;2&#xff09;、虚函数&#xff08;3&#xff09;、虚函数的重写/覆盖&#xff08;4&#xff09;、多态场景的一个选择题&#xff08;5&…

火语言UI组件--文件对话框

【组件功能】&#xff1a;选择单个或多个文件的对话框。 样式预览 设置 基础设置 属性名称属性释义输入值类型标题(title)对话框的标题字符串类型默认路径(defaultPath)对话框的默认展示路径字符串类型多选(multiSelections)是否允许多选布尔型(true / false)显示隐藏文件(s…

rl_sar功能包详解

文章目录 1. 功能包概述2. 目录结构详解2.1 核心目录结构2.2 各目录功能src/ 目录 - C源代码实现scripts/ 目录 - Python脚本实现include/ 目录 - C头文件library/ 目录 - 核心库和第三方依赖models/ 目录 - 预训练模型库launch/ 目录 - ROS启动文件worlds/ 目录 - Gazebo仿真世…

InternVL2.5-多模态大模型评估专业图片

具备图像理解功能的大模型InternVL2.5&#xff0c;能有效解析大部分图片。 对于专业图片如医学细胞切片&#xff0c;从专业角度解析&#xff0c;能推动模型应用到更广泛的领域。 InternVL2.5解析示例 prompt(胸部癌变细胞图片,来自PanNuke) 请评估这个组织的风险 InternVL2.…

解决 IDEA 在运行时中文乱码问题

直接说解决办法 编译 IDEA 所在目录的启动的 .vmoptions 文件&#xff0c;添加以下JVM 参数即可 -Dfile.encodingUTF-8如下图所示&#xff0c;Help > Edit Custom VM Options&#xff0c;随后在编辑框中添加-Dfile.encodingUTF-8 的 JVM 参数

【Linux】进程的生命之旅——诞生、消逝与守候(fork/exit/wait)

&#x1f3ac; 个人主页&#xff1a;谁在夜里看海. &#x1f4d6; 个人专栏&#xff1a;《C系列》《Linux系列》《算法系列》 ⛰️ 一念既出&#xff0c;万山无阻 目录 &#x1f4d6;一、进程创建 1.fork函数 &#x1f4da;高层封装特性 &#x1f4da;fork返回值 2.写时拷…

《Linux权威指南:从小白到系统管理员(上册)》深度解析与实践指南

&#x1f482; 个人网站:【 摸鱼游戏】【神级代码资源网站】【星海网址导航】摸鱼、技术交流群&#x1f449; 点此查看详情 引言 Linux 作为现代计算的核心操作系统之一&#xff0c;广泛应用于服务器、云计算、嵌入式开发等领域。《Linux权威指南&#xff1a;从小白到系统管理…

【Linux】信号

目录 一、信号的概念二、信号的产生2.1 通过键盘进行信号的产生2.2 通过系统调用进行信号的产生2.2.1 kill函数2.2.2 raise函数2.2.3 abort函数 2.3 通过异常的方式进行信号的产生2.4 通过软件条件的方式进行信号的产生2.4.1 关闭管道读端2.4.2 alarm函数 2.5 Core Dump&#x…

「模型部署系列」ubuntu 使用vllm部署Qwen3-8B模型

1、下载vllm v0.8.5&#xff08;此处已经下好了&#xff0c;去仓库拉资源&#xff09; 2、 下载Qwen3-8B 方式1: 在下载前&#xff0c;请先通过如下命令安装ModelScope pip install modelscope 命令行下载 下载完整模型库 modelscope download --model Qwen/Qwen3-8B 下…

亮数据与 AI 深度集成:构建电商策略自动化系统新范式

目录 1.引言&#xff1a;电商增长遇瓶颈&#xff0c;AI 能否破局&#xff1f;2.挖掘痛点&#xff1a;精准营销为何难以落地&#xff1f;3.解决之道&#xff1a;为什么选择亮数据而不是传统爬虫&#xff1f;3.1轻松绕过反爬机制&#xff0c;保障数据采集稳定性3.2 零代码门槛&am…

YOLOv12环境配置,手把手教你使用YOLOv12训练自己的数据集和推理(附YOLOv12网络结构图),全文最详细教程

文章目录 前言一、YOLOv12代码下载地址1.YOLOv12模型结构图 二、YOLO环境配置教程1.创建虚拟环境2.激活虚拟环境3.查询自己电脑可支持最高cuda版本是多少&#xff08;无显卡的同学可以跳过这个步骤&#xff09;4.pytorch安装5.验证 PyTorch GPU 是否可用&#xff08;没有显卡的…

Nginx下载与安装(Liunx环境)

1、Nginx下载 官网地址&#xff1a;https://nginx.org/en/download.html 2、安装依赖包 //安装gcc yum install gcc-c //安装PCRE pcre-devel yum install -y pcre pcre-devel //安装zlib yum install -y zlib zlib-devel //安装Open SSL yum install -y openssl openssl-deve…

雷达中实信号与复信号

一、什么是实信号和复信号 实信号是指信号的时域取值在数学表示和物理实现中始终为实数的信号&#xff0c;其基本的表达式为&#xff1a;&#xff1b;复信号是指时域取值在数学表示中始终为复数的信号&#xff0c;其基本的表达式为&#xff1a;。从实信号与复信号的定义可知&am…

【存储基础】NUMA架构

文章目录 1. 前置知识:物理CPU和CPU核心物理CPUCPU核心关系 2. NUMA架构2.1 NUMA架构是什么&#xff1f;2.2 NUMA架构详解2.3 查看NUMA信息2.4 NUMA架构在分布式存储中的应用数据本地化 Data Locality计算与存储协同调度NUMA感知的网络通信内存池优化与跨节点均衡 3 补充&#…

HTTP协议解析

HTTP&#xff08;超文本传输协议&#xff09;是万维网的基础协议&#xff0c;自1991年诞生以来&#xff0c;已成为最广泛使用的应用层协议。本文将深入解析HTTP协议的核心概念、工作原理及实际应用。 HTTP协议基础 什么是HTTP&#xff1f; HTTP (全称为 "超文本传输协…

小麦“颗粒归仓”有了“最强大脑”

全国小麦主产区自南向北陆续进入紧张抢收阶段,夏种也全面展开。河南夏种已完成四成,以玉米、花生为主。安徽夏种已完成近三成,以水稻和玉米为主。各地如何针对天气情况抢抓收获“窗口期”,确保粮食“颗粒归仓”?目前,安徽4300多万亩的小麦收获已接近尾声。当记者来到安徽…

数据结构:递归(Recursion)

目录 示例1&#xff1a;先打印&#xff0c;再递归 示例2&#xff1a;先递归&#xff0c;再打印 递归的两个阶段 递归是如何使用栈内存 复杂度分析 递归中的静态变量 内存结构图解 递归&#xff1a;函数调用自己 必须有判断条件来使递归继续或停止 我们现在通过这两个示…

Python入门手册:类和对象

在Python中&#xff0c;面向对象编程&#xff08;OOP&#xff09;是一种核心的编程范式。通过类和对象&#xff0c;我们可以模拟现实世界中的事物和行为&#xff0c;使代码更加模块化、可复用和易于维护。今天&#xff0c;就让我们深入探讨Python中的类和对象&#xff0c;包括它…

从冷上电到main()函数,Bootloader都做了什么?

目录 1、硬件初始化 2、引导模式与应用模式的抉择 3、启动代码 在嵌入式系统中&#xff0c;从设备上电到执行应用程序的main()函数&#xff0c;Bootloader扮演着至关重要的角色。作为系统启动的首个程序&#xff0c;Bootloader负责初始化硬件、设置运行环境&#xff0c;并最…