【Qt 6】读写剪贴板
剪贴板是个啥就不用多介绍了,最直观的功能是实现应用程序之间数据共享。就是咱们常说的“复制”、“粘贴”功能。
在 Qt 中,QClipboard 类提供了相关 API 让应用程序具备读/写剪贴板的能力。数据通过 QMimeData 类包装。该类使用 MIME 类型来标识数据。比如,要包装的数据是纯文本内容,就使用 text/plain;如果是 PNG 图像数据,就用 image/png。当然,自定义类型也是可以的,如 application/xxx。
QMimeData 的核心方法是 setData 和 data。setData 方法用来放入数据,data 方法用来取出数据。setData 方法的签名如下:
void setData(const QString &mimetype, const QByteArray &data);
mimetype 参数为字符串,指定数据的 MIME 类型;data 参数就是数据本尊,类型为字节序列。通过 setData 方法的签名,咱们也能知道,QMimeData 类可以放任意内容。要获取数据时,data 方法需要通过 MIME 类型来检索。
为了便于存取常见的数据——如文本、图像、HTML文本等,QMimeData 类提供一些封装好的方法成员:
文本 | setText | 设置普通文本 |
text | 获取普通文本 | |
hasText
|
判断是否存在文本数据 | |
HTML文本 |
setHtml
|
设置 HTML 文本 |
html
|
获取HTML文本 | |
hasHtml
|
判断是否存在 HTML 文本数据 | |
URL |
setUrls
|
设置 URL 列表,参数为 QList<QUrl> |
urls | 获取 URL 列表 | |
hasUrls
|
检测是否存在 URL 列表 | |
图像 |
setImageData
|
设置图像数据 |
imageData
|
获取图像数据 | |
hasImage
|
判断是否存在图像数据 | |
颜色 |
setColorData
|
设置颜色数据 |
colorData
|
获取颜色数据 | |
hasColor
|
是否存在颜色数据 |
QClipboard 类不能直接实例化使用,它由 QGuiApplication 类的静态成员 clipboard 公开。该静态成员返回 QClipboard 类的指针,程序代码将通过这个指针来访问 QClipboard 对象。由于 QApplication 类派生自 QGuiApplication,当然也继承了 clipboard 成员。
下面做一个简单的练习:复制和粘贴文本。
MyWindow 类的头文件。
class MyWindow : public QWidget
{ Q_OBJECT public:
MyWindow(QWidget* parent = nullptr);
~MyWindow();
private:
void _initUi(); // 私有方法
// 下面是私有字段
QLineEdit* _txtInput;
QLabel* _lbTxt;
QPushButton* _btnCopy;
QPushButton* _btnPaste;
// 用来布局控件的
QGridLayout* _layout;
// 下面成员响应 clicked 信号
void onCopy();
void onPaste();
};
_initUi 方法负责初始化窗口上的东西。这个窗口有四个组件:一个 QLineEdit 用来输入文本;一个 QLabel 用来显示文本;然后是两个按钮—— 执行“复制”和“粘贴”操作。
后面两个方法 onCopy 和 onPaste 分别与两个按钮的 clicked 信号绑定。
构造函数的实现比较简单,就是调用 _initUi 方法。
MyWindow::MyWindow(QWidget* parent)
: QWidget::QWidget(parent)
{
// 初始化UI
this -> _initUi();
} MyWindow::~MyWindow()
{
}
析构函数这里啥也不做。
下面是 _intUi 的实现代码。
void MyWindow::_initUi()
{
// 设置一下窗口
this->setWindowTitle("复制粘贴文本");
this->setGeometry(560, 480, 320, 150);
this->setMinimumSize(300, 150); _txtInput = new QLineEdit();
_lbTxt = new QLabel();
_btnCopy = new QPushButton("复制");
_btnPaste = new QPushButton("粘贴");
_layout = new QGridLayout(this);
// 设置空白
_layout->setSpacing(12);
// 放置各控件
_layout->addWidget(_txtInput, 0, 0);
_layout->addWidget(_btnCopy, 1, 0);
_layout->addWidget(_lbTxt, 0, 2);
_layout->addWidget(_btnPaste, 1, 2);
_layout->setColumnStretch(0, 2);
_layout->setColumnStretch(1, 1);
_layout->setColumnStretch(2, 2); // 绑定信号和槽
connect(_btnCopy, &QPushButton::clicked, this, &MyWindow::onCopy);
connect(_btnPaste, &QPushButton::clicked, this, &MyWindow::onPaste);
}
QGridLayout 类也是一个组件,以网格方式布局各组件。网格的行和列是自动划分的。上面代码中其实用到了三列两行:
1、QLineEdit 在第一列第一行;
2、第二列空着,没放东西;
3、 QLabel 组件在第三列第一行;
4、“复制”按钮在第一列第二行;
5、“粘贴”按钮在第二行第二行。
这三行是设定空间比例的。
_layout->setColumnStretch(0, 2);
_layout->setColumnStretch(1, 1);
_layout->setColumnStretch(2, 2);
这意思就是,列的总宽平均分为4份,第一列和第三列都占两份,第二列只占一份。
随后是与 clicked 信号绑定的两个私有方法。
void MyWindow::onCopy()
{
// 获得 QClipboard 的引用
QClipboard* clboard = QApplication::clipboard();
// 设置文本数据
clboard -> setText(_txtInput -> text());
} void MyWindow::onPaste()
{
// 过程差不多
QClipboard* cb = QApplication::clipboard();
QString s = cb->text();
// 显示粘贴的文本
_lbTxt->setText(s);
}
最后是 main 函数的代码:
int main(int argc, char** argv)
{
QApplication app(argc, argv);
MyWindow win;
win.show();
return app.exec();
}
运行程序,先输入一些文本,点击“复制”;再点击“粘贴”,被复制的文本就会显示出来了。
这个例子用了 QClipboard 类公开的封装方法,不需要操作 QMimeData 类。针对常用的数据格式,可直接用。
1、text、setText:设置或获取文本;
2、setImage 和 image:设置或获取图像(QImage类型);
3、setPixmap 和 pixmap:设置或获取图像(QPixmap类型)。
后面两个是读写图像的。
对于图像数据的复制和粘贴,操作流程差不多,大伙伴有兴趣可以试试。
前文提到过,除了常见的数据格式外,QMimeData 允许自定义格式,用 MIME 来标识。
接下来,咱们做个练习,复制和粘贴生日贺卡信息。假设生日贺卡信息包括姓名、生日、祝福语。咱们用自定义的数据格式将其复制,也可以粘贴到应用程序上。MIME 类型为 application/bug。
以下是自定义窗口类的头文件定义。
#ifndef APP_H
#define APP_H
#include <qwidget.h>
#include <qpushbutton.h>
#include <qlineedit.h>
#include <qdatetimeedit.h> class CustWind : public QWidget
{
Q_OBJECT
public:
CustWind(QWidget* parent = nullptr);
private:
QLineEdit* txtName;
QLineEdit* txtWish;
QDateEdit* txtDate;
QPushButton* btnCopy;
QPushButton* btnPaste; // 与 clicked 信号绑定的方法(Slots)
void onCopy();
void onPaste();
}; #endif
在包含头文件时,用带 .h 后缀和不用后是一样的,既可以用 <QWidget> 也可以用 <qwidget.h>,只是作兼容之用。
在构造函数中,初始化各类可视对象。
CustWind::CustWind(QWidget *parent)
: QWidget::QWidget(parent)
{
// 初始化组件
txtName = new QLineEdit();
txtWish = new QLineEdit();
txtDate = new QDateEdit();
// 有效日期范围
txtDate -> setMinimumDate(QDate(1950, 1, 1));
txtDate -> setMaximumDate(QDate(2085, 12, 31));
btnCopy = new QPushButton("复制生日贺卡");
btnPaste = new QPushButton("粘贴生日贺卡");
// 布局
QFormLayout* layout = new QFormLayout(this);
layout->addRow("姓名:", txtName);
layout->addRow("生日:", txtDate);
layout->addRow("祝福语:", txtWish);
QVBoxLayout *sublayout = new QVBoxLayout();
sublayout->addWidget(btnCopy);
sublayout->addWidget(btnPaste);
layout ->addRow(sublayout);
// 连接信号和槽
connect(btnCopy,&QPushButton::clicked,this,&CustWind::onCopy);
connect(btnPaste,&QPushButton::clicked,this,&CustWind::onPaste);
}
这个例子咱们用 QFormLayout 来布局界面。其含义和 HTML 中 <form> 元素差不多,即表单。
下面重点说两个 slot 方法。
第一个是 onCopy ,由复制按钮的点击触发。
void CustWind::onCopy()
{
// 获取数据
QString name = txtName->text();
QString wish = txtWish->text();
QDate birthdate = txtDate->date();
if(name.isEmpty())
{
return; //姓名是空的
}
// 开始序列化
QByteArray data;
QBuffer buff(&data);
// buffer 要先打开
buff.open(QBuffer::WriteOnly);
QDataStream output(&buff);
// 写入数据
output << name << birthdate << wish;
buff.close();
// 包装数据
QMimeData packet;
packet.setData("application/bug", data);
// 把数据放到剪贴板
QClipboard* cb = QApplication::clipboard();
cb->setMimeData(&packet); QMessageBox::information(this,"恭喜","生日贺卡复制成功",QMessageBox::Ok);
}
Qt 里面常用 QDataStream 类来做序列化和反序列化操作。由于它有运算符重载,我们可以使用 C++ 入门时最熟悉的 <<、>> 运算符来输入输出。向 QDataStream 对象写入数据时:
dataStream << a << b << c;
反序列化时:
dataStream >> a >> b >> c;
运算符很TM生动形象地描述出数据的流动方向。注意输入和输出时,数据的顺序必须一致。
QMimeData 类用 setData 方法设置自定义数据时,参数接收的类型是 QByteArray(字节数组)而不是 QDataStream 对象,因此,我们要用 QBuffer 类来中转一下。
1、创建 QByteArray 实例;
2、创建 QBuffer 实例,关联 QByteArray 实例;
3、创建 QDataStream 实例,关联 QBuffer 实例。
QDataStream 类需要 QIODevice 的派生类来完成读写操作,而 QByteArray 类不是 QIODevice 的子类,故要用 QBuffer 来过渡一下。当然了,老周这里为了让这个思路更清晰一些,所以“中规中矩”地写代码。其实,QDataStream 类有接收 QByteArray 类型的参数的,可以省略 QBuffer 的代码。QDataStream 类内部自动创建 QBuffer 对象。
第二个 slot 方法是 onPaste,实现贺卡的粘贴。
void CustWind::onPaste()
{
// 访问剪贴板
QClipboard* cb=QApplication::clipboard();
const QMimeData* dataPack = cb->mimeData();
// 判断一下有没有我们要的数据
if(!dataPack->hasFormat("application/bug"))
{
return;
}
// 取出数据
QByteArray data = dataPack->data("application/bug");
// 反序列化
QBuffer buff(&data);
// 记得先打开 buffer
buff.open(QBuffer::ReadOnly);
QDataStream input(&buff);
// 注意读的顺序
QString name;
QDate birth;
QString wish;
input >> name >> birth >> wish;
// 显示数据
this->txtName->setText(name);
this->txtDate->setDate(birth);
this->txtWish->setText(wish);
}
反序列化的原理与序列化是一样的,只是反向操作罢了。注意读写数据的顺序,写的时候是姓名 - 生日 - 祝福语,读的时候也必须按这个顺序。
最后,是 main 函数。
int main(int argc, char* argv[])
{
QApplication myapp(argc, argv);
CustWind mwindow;
mwindow.show();
return myapp.exec();
}
CMake 文件(CMakeLists.txt)就按照标准文档上直接抄就行了。
cmake_minimum_required(VERSION 3.20)
project(demo LANGUAGES CXX)
find_package(
Qt6
REQUIRED COMPONENTS
Core
Gui
Widgets
)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
add_executable(demo app.h app.cpp)
target_link_libraries(
demo
PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Widgets
)
重点就是三步:
1、声明 CMake 文档支持的版本,应用项目的名称(target)。
2、auto moc 一定要打开,否则编译时会挂。
3、添加源代码、链接相关的库。
好了,完工了,咱们试试。
先运行一个程序实例,输入相关信息,点复制按钮。
然后,关闭这个程序重新运行,或者再运行一个新程序实例,点粘贴按钮。
这样,就完成了自定义数据的复制和粘贴。
【Qt 6】读写剪贴板的更多相关文章
- python3 使用pyperclip读写剪贴板(windows)
2016年5月14日 03:41:38 codegay 使用pyperclip库读写剪贴板非常简单~, 1.使用命令安装: pip install pyperclip 2.然后...就可以了: 以下是 ...
- Qt 二进制文件读写(使用“魔术数字”)
今天开始进入 Qt 的另一个部分:文件读写,也就是 IO.文件读写在很多应用程序中都是需要的.Qt 通过 QIODevice 提供了IO的抽象,这种设备(device)具有读写字节块的能力.常用的IO ...
- QT QSettings读写配置文件
QT QSettings读写配置文件 需要用一个配置文件来保存程序的初始值,同时也需要做保存修改后的值. 那么借助于QSetting 就可以达到目的. 注意,生成的是 ini 文件! //1.创建和读 ...
- 【WP开发】读写剪贴板
在WP 8.1中只有Silverlight App支持操作剪贴板的API,Runtime App并不支持.不过,在WP 10中也引入了可以操作剪贴板的API. 顺便说点题外话,有人会说,我8.1的开发 ...
- Qt文本读写之一:输入输出设备和文件操作
一.输入输出设备 QIODevice类是Qt中所有I/O设备的基础接口类,为诸如QFile.QBuffer和 QTcpSocket等支持读/写数据块的设备提供了一个抽象接口.QIODevice类是抽象 ...
- Qt文件读写操作
原文地址:https://www.cnblogs.com/flowingwind/p/8336159.html QFile Class 1.read读文件 加载文件对象 QFile file(&qu ...
- 一篇文章快速搞懂Qt文件读写操作
导读:Qt当中使用QFile类对文件进行读写操作,对文本文件也可以与QTextStream一起使用,这样读写操作会更加简便.QFileInfo可以用来获取文件的信息.QDir可以用于对文件夹进行操作. ...
- Qt文本读写之二:目录操作
一.简介 QDir类用来访问目录结构及其内容,可以操作路径名.访问路径和文件相关信息以及操作底层的文件系统,还可以访问Qt的资源系统.Qt使用"/"作为通用的目录分隔符和URLs的 ...
- QT文件读写操作笔记
补一下这部分的笔记 简单的东西也记一下 操作系统一般都会提供一些列的标准对话框,如文件选择.字体选择.颜色选择等,这些标准对话框为应用层序提供了一致的观感.Qt对这些标准对话框都定义了相关的类,如:Q ...
- [Qt] 文本文件读写, 摘自官方文档
Reading Files Directly The following example reads a text file line by line: QFile file("in.txt ...
随机推荐
- beta冲刺:总结随笔
这个作业属于哪个课程 <班级的链接> 这个作业要求在哪里 <作业要求的链接> 这个作业的目标 beta冲刺总结 作业正文 .... 其他参考文献 ... 一.预期计划 | 6. ...
- linux虚拟机设置网络显示NetworkManager need to be running
问题描述:在设置一台centOS7虚拟机的网络时出现: 解决方法: 命令行重启NetworkManager systemctl restart NetworkManager 刷新一次就好了
- 深入理解 Python 虚拟机:整型(int)的实现原理及源码剖析
深入理解 Python 虚拟机:整型(int)的实现原理及源码剖析 在本篇文章当中主要给大家介绍在 cpython 内部是如何实现整型数据 int 的,主要是分析 int 类型的表示方式,分析 int ...
- 《操作系统导论》读书笔记1——CPU虚拟化,进程
系列文章目录和关于我 一丶CPU的虚拟化 一个桃子,我们称之为物理(physical)桃子.但有很多想吃这个桃子的 人,我们希望向每个想吃的人提供一个属于他的桃子,这样才能皆大欢喜.我们把给每个 人的 ...
- Java:数据表的字段设计了默认值0不生效的原因
在数据表里给字段设置了默认值为0,但是在插入的时候不生效,数据表设计如下 通过数据表生成的实体类 查看代码 @Data @TableName(value = "user") @No ...
- 【ACM算法竞赛日常训练】DAY4题解与分析【树】【子序列】| 组合数学 | 动态规划
DAY4共2题: 树(组合数学) 子序列(dp,数学) 作者:Eriktse 简介:19岁,211计算机在读,现役ACM银牌选手力争以通俗易懂的方式讲解算法!️欢迎关注我,一起交流C++/Python ...
- TiDB SQL调优案例之避免TiFlash帮倒忙
背景 早上收到某系统的告警tidb节点挂掉无法访问,情况十万火急.登录中控机查了一下display信息,4个TiDB.Prometheus.Grafana全挂了,某台机器hang死无法连接,经过快速重 ...
- 白嫖一个月的ES,完成了与MySQL的联动
前言 <腾讯云 x Elasticsearch三周年>活动来了.文章写之前的思路是:在腾讯云服务器使用docker搭建ES.但是理想很丰满,显示很骨感,在操作过程中一波三折,最后还是含着泪 ...
- stm32的学习笔记1
一 目录结构管理 Libraries是放官方固件库的 MDK-ARM是放产生的文件的,工程存放的目录 USERS是放自己写的代码的 然后是一个解释文件README 在MDK-ARM目录里还要创建两个文 ...
- [Linux]异常配置专题之重复配置的有效性:系统/环境变量 | hosts
1 文由 在项目中经常遇到这种情况,1个hosts文件里同一IP 或 域名存在多个映射配置,那么到底哪个有效?环境变量亦有此问题. 问题本身不难,只是为了避免混淆,进行专门记录,以加深记忆. 2 ho ...