第十八章 拖放与剪贴板
18.1 拖放
18.2 剪贴板
18.3 小结
拖放和剪贴板的功能原理基础都是QMimeData类,所以这里我们将这两种放在一起讲。QMimeData当然与MIME相关:MIME是描述消息内容类型的因特网标准,可以简单理解为对文件扩展名的详细解释,通过该解释,程序就可以知道应该以何种方式处理该数据。每个MIME类型由两部分组成,前面是数据的大类别,后面定义具体的种类,例如扩展名为.png的MIME类型为image/png;而QMimeData则给记录自身MIME类型的数据提供了一个容器,用于专门处理MIME类型数据。
详细的MIME类型请参考:MIME参考手册
针对常见的MIME类型,QMimeData类提供了很方便的函数用于处理MIME类型数据:
判断函数 | 获取函数 | 设置函数 | MIME 类型 |
hasText() | text() | setText() | text/plain |
hasHtml() | html() | setHtml() | text/html |
hasUrls() | urls() | setUrls() | text/uri-list |
hasImage() | imageData() | setImageData() | image/ * |
hasColor() | colorData() | setColorData() | application/x-color |
18.1 拖放
拖放分为拖动和放下两个动作,它们涉及到以下事件:
- DragEnterEvent: 所拖动目标进入接收该事件的窗口或控件时触发;
- DragMoveEvent: 所拖动目标进入窗口或控件后,继续被拖动时触发;
- DragLeaveEvent: 所拖动目标离开窗口或控件时触发;
- DropEvent: 所拖动目标被放下时触发。
下面我们完成一个可以实现拖放txt文件并读取的小程序:
import sys
from PyQt5.QtWidgets import QApplication, QTextBrowser
class Demo(QTextBrowser): # 1
def __init__(self):
super(Demo, self).__init__()
self.setAcceptDrops(True) # 2
def dragEnterEvent(self, QDragEnterEvent): # 3
print('Drag Enter')
if QDragEnterEvent.mimeData().hasText():
QDragEnterEvent.acceptProposedAction()
def dragMoveEvent(self, QDragMoveEvent): # 4
print('Drag Move')
def dragLeaveEvent(self, QDragLeaveEvent): # 5
print('Drag Leave')
def dropEvent(self, QDropEvent): # 6
print('Drag Drop')
# MacOS
txt_path = QDropEvent.mimeData().text().replace('file:///', '/')
# Linux
# txt_path = QDropEvent.mimeData().text().replace('file:///', '/').strip()
# Windows
# txt_path = QDropEvent.mimeData().text().replace('file:///', '')
with open(txt_path, 'r') as f:
self.setText(f.read())
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = Demo()
demo.show()
sys.exit(app.exec_())
1. 继承QTextBrowser,也就是说下面来实现该控件的拖放事件响应函数;
2. setAcceptDrops(True)方法可以让该控件接收放下(Drop)事件;
3. 当拖动目标进入QTextBrowser的那一刹那,触发dragEnterEvent事件,在该响应函数中,我们先判断所拖动目标的MIME类型是否为text/plain,若是的话则调用acceptProposedAction()方法来表明可以在QTextBrowser上进行拖放动作;
4. 当目标进入窗体后,如果不放下而是继续移动的话,则会触发dragMoveEvent事件;
5. 将进入控件后的目标再次拖动到控件之外时,就会触发dragLeaveEvent()事件;
6. 将目标在QTextBrowser中放下后,我们先通过QDropEvent.mimeData().text()方法获取到该文件的URI路径,replace()方法将其中的file:///替换为/,这样得到的值才是我们想要的本地文件路径。最后打开my.txt文件进行读取,并用setText()方法将QTextBrowser的文本设为该my.txt的内容。
注:不同系统使用的路径写法也不同,请注意区分。在Linux上获取的路径中存在'\r\n'所以用strip()去除。
运行截图如下,笔者将放在桌面的my.txt文件拖到程序中,程序显示my.txt文件的内容:
18.2 剪贴板
通常我们在Windows或Linux上使用复制都是按ctrl+c然后按ctrl+v进行粘贴(Mac上为command+c和command+v),这其中就涉及到了剪贴板,当进行复制时,其实是将要复制的内容放到了一个无形的剪贴板上,要粘贴时,再将剪贴板上的内容放到界面上。
当我们浏览CSDN博客,在碰到代码想要复制的时候,右上角会出现一个复制按钮,点击后就可以将所有代码复制到剪贴板上:
我们用PyQt5来实现下类似功能,代码如下:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QTextEdit, QTextBrowser, QPushButton, QGridLayout
class Demo(QWidget):
def __init__(self):
super(Demo, self).__init__()
self.text_edit = QTextEdit(self)
self.text_browser = QTextBrowser(self)
self.clipboard = QApplication.clipboard() # 1
self.clipboard.dataChanged.connect(lambda: print('Data Changed'))
self.copy_btn = QPushButton('Copy', self) # 2
self.copy_btn.clicked.connect(self.copy_func)
self.paste_btn = QPushButton('Paste', self) # 3
self.paste_btn.clicked.connect(self.paste_func)
self.g_layout = QGridLayout()
self.g_layout.addWidget(self.text_edit, 0, 0, 1, 1)
self.g_layout.addWidget(self.text_browser, 0, 1, 1, 1)
self.g_layout.addWidget(self.copy_btn, 1, 0, 1, 1)
self.g_layout.addWidget(self.paste_btn, 1, 1, 1, 1)
self.setLayout(self.g_layout)
def copy_func(self):
self.clipboard.setText(self.text_edit.toPlainText())
def paste_func(self):
self.text_browser.setText(self.clipboard.text())
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = Demo()
demo.show()
sys.exit(app.exec_())
1. 实例化一个剪贴板,并将其dataChanged信号和打印函数连接起来。每当剪贴板内容发生变化的时候,都会触发dataChanged信号。在这里也就是说每当发生变化,控制台都会打印出Data Changed;
2. 当点击copy_btn后,槽函数copy_func()就会启动:
def copy_func(self):
self.clipboard.setText(self.text_edit.toPlainText())
在槽函数中,我们将text_edit中的文本获取过来并通过setText()方法将其设置为剪贴板的文本;
3. 当点击paste_btn后,槽函数paste_func()启动:
def paste_func(self):
self.text_browser.setText(self.clipboard.text())
在槽函数中,我们将text_browser的文本设为剪贴板的文本。当然该槽函数还有另一种实现方法:
def paste_func(self):
mime = self.clipboard.mimeData()
if mime.hasText():
self.text_browser.setText(mime.text())
首先通过mimeData()方法获取剪贴板内容的MIME类型,然后判断mime类型是否为text/plain,是的话则通过text()方法获取,并设为text_browser文本。
当然以上只是针对文本内容,当然还可以复制图片等文件,而剪贴板当然也有相应的方法,以下列出常用的:
方法 | 解释 |
clear() | 清空剪贴板内容 |
mimeData() | 获取剪贴板上的MIME类型数据 |
setMimeData() | 将MIME类型数据放到剪贴板中 |
pixmap() | 获取剪贴板上的QPixmap类型数据 |
setPixmap() | 将QPixmap类型数据放到剪贴板中 |
image() | 获取剪贴板上的QImage类型数据 |
setImage() | 将QImage类型数据放到剪贴板中 |
text() | 获取剪贴板上的文本 |
setText() | 将文本放到剪贴板中 |
运行截图如下,在左侧输入文本,点击Copy,再点击Paste,右侧显示相应文本:
18.3 小结
1. 使用QMimeData类来处理MIME类型数据;
2. 拖放事件一共有四种,分别在拖动目标进入窗口或部件时、目标进入后继续被拖动时、目标离开窗口或控件时以及目标被放下时;
2. 剪贴板的内容发生变化的话,则会触发dataChanged信号。剪贴板针对不同数据类型有相应获取和设置的方法。