第十八节_PySide6基本窗口控件深度补充_剪贴板与拖曳功能(QMimeData 类) 上篇

文章目录

  • 前言
  • [一、QMimeData 基础](#一、QMimeData 基础)
    • [1.什么是 MIME 类型](#1.什么是 MIME 类型)
    • [2.QMimeData 类的基本功能](#2.QMimeData 类的基本功能)
    • [3.QMimeData 的内部结构](#3.QMimeData 的内部结构)
    • [4.QMimeData 的实际应用](#4.QMimeData 的实际应用)
  • 二、剪贴板(Clipboard)
  • 总结

前言

PySide6中剪贴板(Clipboard)和拖曳(Drag and Drop)是两种常用的数据交换机制。它们允许在应用程序内部或不同应用程序之间传输数据,本节将分别详细介绍这两个主题。


一、QMimeData 基础

无论是拖曳还是剪贴板都需要将 QMimeData 作为数据传输中介,QMimeData 是 Qt 中用于封装 MIME 类型数据的类,它是剪贴板和拖放操作的数据传输核心。MIME(Multipurpose Internet Mail Extensions)最初用于电子邮件,现在被广泛用于描述数据类型。

1.什么是 MIME 类型

MIME 类型是一种标准化的方式,用于标识数据的格式。格式为:type / subtype;
常见 MIME 类型:

① text/plain - 纯文本;

② text/html - HTML 文本;

③ text/uri-list - URL 列表;

④ image/png - PNG 图像;

⑤ image/jpeg - JPEG 图像;

⑥ application/octet-stream - 二进制数据;

⑦ application/json - JSON 数据。

2.QMimeData 类的基本功能

python 复制代码
from PySide6.QtCore import QMimeData, QByteArray

# 创建 QMimeData 对象
mime_data = QMimeData()

# 1. 设置标准数据
mime_data.setText("Hello World")                     # 设置纯文本
mime_data.setHtml("<b>Hello</b> World")             # 设置 HTML
mime_data.setUrls([QUrl("file:///path/to/file")])   # 设置 URL 列表
mime_data.setImageData(pixmap.toImage())            # 设置图像数据
mime_data.setColorData(QColor(255, 0, 0))           # 设置颜色数据

# 2. 检查数据
has_text = mime_data.hasText()                      # 检查是否有文本
has_html = mime_data.hasHtml()                      # 检查是否有 HTML
has_urls = mime_data.hasUrls()                      # 检查是否有 URL
has_image = mime_data.hasImage()                    # 检查是否有图像
has_color = mime_data.hasColor()                    # 检查是否有颜色
has_format = mime_data.hasFormat("text/plain")      # 检查特定格式

# 3. 获取数据
text = mime_data.text()                            # 获取文本
html = mime_data.html()                            # 获取 HTML
urls = mime_data.urls()                            # 获取 URL 列表
image = mime_data.imageData()                      # 获取图像
color = mime_data.colorData()                      # 获取颜色

# 4. 获取所有可用的格式
formats = mime_data.formats()                      # 返回字符串列表

3.QMimeData 的内部结构

QMimeData 内部使用字典结构存储数据,键是 MIME 类型,值是 QByteArray:

python 复制代码
class QMimeDataPrivate:
    def __init__(self):
        self.data = {}  # {mime_type: QByteArray}
        
# 实际使用
mime_data = QMimeData()
mime_data.setData("text/plain", "Hello".encode('utf-8'))
mime_data.setData("text/html", "<b>Hello</b>".encode('utf-8'))

4.QMimeData 的实际应用

button_QMime.py 的例程代码:

python 复制代码
import sys
import os

from PySide6.QtWidgets import *
from PySide6.QtGui import *
from PySide6.QtCore import *

class ButtonQMime(QPushButton):
    def __init__(self, title=None, parent=None):
        super().__init__(title, parent)
        self.setAcceptDrops(True)

    def dragEnterEvent(self, e):
        if e.mimeData().hasFormat("text/plain"):
            e.accept()
        else:
            e.ignore()

    def dropEvent(self, e):
        self.setText(e.mimeData().text())


class ButtonMyQMime(QPushButton):
    def __init__(self, title=None, parent=None):
        super().__init__(title, parent)
        self.setAcceptDrops(True)
        self.mime = QMimeData()
        qb = QByteArray(bytes('abcd_wo爱中国_123', encoding='utf8'))
        self.mime.setData('my_mimetype',qb)

    def dragEnterEvent(self, e):
        if self.mime.hasFormat('my_mimetype'):
            e.accept()
        else:
            e.ignore()

    def dropEvent(self, e):
        self.setText('自定义format结果为:'+self.mime.data('my_mimetype').data().decode('utf8'))

主程序代码:

python 复制代码
		#QMimeData 数据传输中介

        # 创建新的布局
        self.grid_layout = QGridLayout(self.ui.m_widget)
        self.label = QLabel('拖拽到窗口显示拖拽format信息',self)
        self.grid_layout.addWidget(self.label)

        self.ui.m_widget.setAcceptDrops(True)

        # 图像显示标签
        self.ui.lab_Show.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.ui.lab_Show.setStyleSheet("border: 2px dashed gray; padding: 10px;")
        self.ui.lab_Show.setMinimumHeight(300)

    def dragEnterEvent(self, e):
        _str = ''
        mime = e.mimeData()

        # 识别拖拽的文件
        if mime.hasUrls():
            path_list = e.mimeData().urls()
            _str = '\n'.join(a.path() for a in path_list)
            _str = '拖拽的文件路径为:\n' + _str + '\n\n'

        # 识别拖拽的文字
        if mime.hasText():
            _str = _str + '拖拽的文字内容为:\n' + mime.text() + '\n\n'

        format_list = mime.formats()
        self.label.setText(_str + '拖拽的formats为:\n'+'\n'.join(format_list))

①本例程主要通过 button 和 button2 分别实现了标准及自定义 QMimeData 的方法,通过 Drag 体现出来,QMimeData 在拖曳

过程中提供数据交换的通道;

②自定义了两个 QPushButton 子类,然后在 UI 界面对 QPushButton 控件分别进行提升(提升操作在第十六节有做介绍),主要用于演示 QMimeData 的标准方法和自定义数据的方式;

③在 ButtonQMime 代码中,dragEnterEvent() 函数中的 e 是 QDragEnterEvent 类型,拖曳动作进入该按钮时会触发该事件;dropEvent() 函数中的 e 是 QDropEvent 类型,拖曳动作在按钮上被释放时会触发该事件。两个 e.mimeData() 函数都是 QMimeData 类型的,存储拖曳的数据信息。该代码表示在一个拖曳操作中,QMimeData 如果判断拖曳的是文本就会接受这个拖曳操作,并且在拖曳释放的时候设置按钮的 text 为拖曳的文本;

④QMimeData 中的常见格式如果无法满足需求,则可以使用自定义的格式(mimeType),在 ButtonMyQMime 代码中新增了一个 mimeType,并把按钮的 text 修改为 mimeType 对应的数据。实现的效果是只要拖曳到按钮,按钮的 text 自动修改为 "abcd_wo爱中国_123" 。需要注意的是,setData 接收的参数的类型为 QByteArray,这是 Qt 支持的二进制格式,所以必须把数据转换成这种格式才可以通过 QMimeData 传输,这就增加了额外的工作量。实际上,对于上面的案例,也可以通过self.mime.setText(abcd_wo爱中国_123') 传递字符串,同时使用 self.mime.text()函数获取字符串;

⑤在主程序中的 dragEnterEvent 事件针对的是窗口,拖曳到窗口中时会触发,通过介绍可以大体理解 QMimeData 类型数据的保存和传输。

二、剪贴板(Clipboard)

QClipboard 提供了对系统剪贴板的访问,可以在应用程序之间复制和粘贴数据。QClipboard 的操作同样是使用 QMimeData 进行传输数据。

1.获取 QClipboard 实例

python 复制代码
from PySide6.QtWidgets import QApplication
from PySide6.QtGui import QClipboard

# 获取剪贴板实例
clipboard = QApplication.clipboard()

2.基本数据类型操作

python 复制代码
# 1. 文本操作
clipboard.setText("Hello, Clipboard!")  # 设置文本
text = clipboard.text()                 # 获取文本

# 2. HTML 操作
clipboard.setHtml("<b>Bold Text</b>")   # 设置HTML
html = clipboard.html()                 # 获取HTML

# 3. 图像操作
from PySide6.QtGui import QPixmap
pixmap = QPixmap("image.png")
clipboard.setPixmap(pixmap)             # 设置图像
pixmap = clipboard.pixmap()             # 获取图像

# 4. URL 操作
from PySide6.QtCore import QUrl
urls = [QUrl("file:///path/to/file"), QUrl("https://example.com")]
clipboard.setUrls(urls)                 # 设置URL列表
urls = clipboard.urls()                 # 获取URL列表

3.实践操作

python 复制代码
		# QClipboard 剪贴板操作
        # 当前图像变量
        self.current_pixmap = None

        #槽函数
        self.ui.text_copy_btn.clicked.connect(self.copy_text_to_clipboard)
        self.ui.text_paste_btn.clicked.connect(self.paste_text_from_clipboard)
        self.ui.text_clear_btn.clicked.connect(self.ui.text_edit.clear)

        self.ui.image_load_btn.clicked.connect(self.load_image)
        self.ui.image_copy_btn.clicked.connect(self.copy_image_to_clipboard)
        self.ui.image_paste_btn.clicked.connect(self.paste_image_from_clipboard)
        self.ui.image_clear_btn.clicked.connect(self.clear_image_display)

    def copy_text_to_clipboard(self):
        """复制文本到剪贴板"""
        text = self.ui.text_edit.toPlainText()
        if text:
            clipboard = QApplication.clipboard()
            clipboard.setText(text)
            self.statusBar().showMessage(f"已复制文本到剪贴板: {text[:30]}...", 2000)
        else:
            self.statusBar().showMessage("警告: 没有文本可复制", 2000)

    def paste_text_from_clipboard(self):
        """从剪贴板粘贴文本"""
        clipboard = QApplication.clipboard()
        text = clipboard.text()

        if text:
            self.ui.text_edit.setText(text)
            self.statusBar().showMessage(f"已从剪贴板粘贴文本: {text[:30]}...", 2000)
        else:
            self.statusBar().showMessage("警告: 剪贴板中没有文本", 2000)

    def load_image(self):
        """加载图像"""
        file_path, _ = QFileDialog.getOpenFileName(self, "选择图像文件","","图像文件 (*.png *.jpg *.jpeg *.bmp *.gif *.webp)")

        if file_path:
            pixmap = QPixmap(file_path)
            if not pixmap.isNull():
                self.current_pixmap = pixmap

                # 缩放图像以适应标签
                scaled_pixmap = pixmap.scaled(
                    self.ui.lab_Show.size(),
                    Qt.AspectRatioMode.KeepAspectRatio,
                    Qt.TransformationMode.SmoothTransformation
                )
                self.ui.lab_Show.setPixmap(scaled_pixmap)
                self.ui.lab_Show.setText("")
                self.statusBar().showMessage(f"已加载图像: {file_path}", 2000)
            else:
                QMessageBox.warning(self, "错误", "无法加载图像文件")

    def copy_image_to_clipboard(self):
        """复制图像到剪贴板"""
        if self.current_pixmap:
            # 复制到剪贴板
            clipboard = QApplication.clipboard()
            clipboard.setPixmap(self.current_pixmap)
            self.statusBar().showMessage("已复制示例图像到剪贴板", 2000)
        else:
            QMessageBox.warning(self, "错误", "没有图像显示,无法复制图像文件到剪贴板!")

    def paste_image_from_clipboard(self):
        """从剪贴板粘贴图像"""
        clipboard = QApplication.clipboard()

        if clipboard.mimeData().hasImage():
            image = clipboard.image()
            if not image.isNull():
                self.current_pixmap = QPixmap.fromImage(image)
                self.ui.lab_Show.setPixmap(self.current_pixmap.scaled(
                    self.ui.lab_Show.size(), Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation
                ))
                self.ui.lab_Show.setText("")
                self.statusBar().showMessage("已从剪贴板粘贴图像", 2000)

            else:
                self.statusBar().showMessage("错误: 剪贴板中的图像无效", 2000)
        else:
            self.statusBar().showMessage("警告: 剪贴板中没有图像", 2000)

    def clear_image_display(self):
        """清空图像显示"""
        self.ui.lab_Show.clear()
        self.ui.lab_Show.setText("图像将显示在这里")
        self.current_pixmap = None
        self.statusBar().showMessage("已清空图像显示", 2000)

①QClipboard 是 PySide6 中用于访问系统剪贴板的类,支持文本、图像、HTML等多种格式的数据传输。上述例程只介绍了文本和图像的基本相关操作;

②通过 QApplication.clipboard() 获取剪贴板实例,clipboard.setText(text) 复制文本到剪贴板;text = clipboard.text() 从剪贴板粘贴文本;

③通过 clipboard.setPixmap(self.current_pixmap) 复制图像到剪贴板;通过 clipboard.mimeData().hasImage() 判断剪粘板上是否有图像数据,然后 image = clipboard.image() 从剪贴板上获取图像。

注意点:
①在实际应用中发现,为什么无法在文件夹复制图像,然后粘贴到程序中?

根本原因:从文件夹复制图像文件通常不是复制图像数据本身,而是复制文件路径,这需要处理不同的MIME类型。所以可以通过解析复制的路径对图像进行加载显示,这里可以探索实操下;

②为什么不能与其他程序共享剪贴板图像?

这个问题很常见,通常是由于以下原因导致的:

1、MIME类型不匹配:不同程序使用不同的剪IME类型来存储图像数据;

2、图像格式限制:某些程序只支持特定格式的图像剪贴板数据;

3、平台差异:Windows、macOS和Linux处理剪贴板图像的方式不;

4、Qt内部格式:QClipboard默认使用Qt特定的内部格式;

总结

QMimeData 的核心特性

特性 说明
多格式支持 同时存储多种格式的数据
平台兼容 在不同平台间保持一致的 MIME 类型
数据封装 将原始数据封装为标准格式
类型检查 提供方便的格式检查方法
数据转换 支持格式间的自动转换

后续会专门详细介绍 Drag 与 Drop 功能,该功能比 Clipboard 功能更常用,也更重要。

相关推荐
酷酷的佳2 小时前
python--面向对象(3)
python
百锦再2 小时前
Python实现开源AI模型引入及测试全过程
人工智能·python·ai·开源·aigc·模型·自然语言
有时有晌2 小时前
影视解说混剪工具
python·影视解说·影视混剪
BoBoZz192 小时前
ImplicitPolyDataDistance 隐式距离显示
python·vtk·图形渲染·图形处理
m5655bj2 小时前
如何通过 Python 在 Excel 中添加或删除图片
python·excel
qq_356196952 小时前
day42Dataset和Dataloader@浙大疏锦行
python
free-elcmacom2 小时前
机器学习高阶教程<6>推荐系统高阶修炼手册:混排、多任务与在线学习,解锁精准推荐新境界
人工智能·python·学习·算法·机器学习·机器人
西西学代码2 小时前
Flutter---常用打印图标
前端·python·flutter
企微自动化2 小时前
企业微信外部群自动化系统的异常处理机制设计
开发语言·python