《快速掌握PyQt5》第十八章 拖放与剪贴板
《快速掌握PyQt5》 专栏已整理成书出版,书名为 《PyQt编程快速上手》 ,详情请见该 链接 。感谢大家一直以来的支持!祝大家PyQt用得越来越顺!
拖放和剪贴板的功能原理基础都是QMimeData类,所以这里我们将这两种放在一起讲。QMimeData当然与MIME相关:MIME是描述消息内容类型的因特网标准,可以简单理解为对文件扩展名的详细解释,通过该解释,程序就可以知道应该以何种方式处理该数据。每个MIME类型由两部分组成,前面是数据的大类别,后面定义具体的种类,例如扩展名为.png的MIME类型为image/png;而QMimeData则给记录自身MIME类型的数据提供了一个容器,用于专门处理MIME类型数据。
详细的MIME类型请参考: MIME参考手册
针对常见的MIME类型,QMimeData类提供了很方便的函数用于处理MIME类型数据:
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()