剪贴板是个啥就不用多介绍了,最直观的功能是实现应用程序之间数据共享。就是咱们常说的“复制”、“粘贴”功能。
在 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 | 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 后缀和不用后是一样的,既可以用 也可以用 ,只是作兼容之用。
在构造函数中,初始化各类可视对象。- 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 中 元素差不多,即表单。
下面重点说两个 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);
- }
复制代码 运算符很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,实现贺卡的粘贴。- dataStream << a << b << c;
复制代码 反序列化的原理与序列化是一样的,只是反向操作罢了。注意读写数据的顺序,写的时候是姓名 - 生日 - 祝福语,读的时候也必须按这个顺序。
最后,是 main 函数。- dataStream >> a >> b >> c;
复制代码 CMake 文件(CMakeLists.txt)就按照标准文档上直接抄就行了。- 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);
- }
复制代码 重点就是三步:
1、声明 CMake 文档支持的版本,应用项目的名称(target)。
2、auto moc 一定要打开,否则编译时会挂。
3、添加源代码、链接相关的库。
好了,完工了,咱们试试。
先运行一个程序实例,输入相关信息,点复制按钮。
然后,关闭这个程序重新运行,或者再运行一个新程序实例,点粘贴按钮。
这样,就完成了自定义数据的复制和粘贴。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |