模型-视图框架完全支持Qt的基本拖放操作,列表、树形和表格部件中的项可以在视图间拖动,数据可以以MIME类型的格式进行导入和导出

Qt提供的标准视图自动支持在视图内部的拖放,其中的项可以被移动以改变显示顺序。在默认情况下,这些视图是不能进行拖放操作的,如果要使用拖放功能,除了项本身也是允许拖放,还需要开启视图的一些属性。

使用项视图部件

QListWidget,QTabWidget和QTreeWidget每一种类型的项都默认配置了一套不同的标识。比如,QListWidgetItem或QTreeWidgetItem的初始标识是启用了enabled, 可复选checkable,可选择selectbale这些标识,同时它们也可以作为拖放操作的数据源;QTableWidgetItem则可以进行编辑操作,并且可以作为拖放操作的目标。

Qt中提供的所有标准项都有一个或两个标志用于拖放操作,为了利用内置的拖放支持,需要在视图中设置相应的属性。

要启动对项的拖动功能,需要把视图的dragEnabled属性设定为True。

要实现将内部或外部项拖放到视图中,需要把视图的viewport()的acceptDrops属性设定为True。

要显示当前拖动的项将被放置在什么地方,则需要设置视图的showDropIndicator属性,它会提供关于项在视图中的持续位置更新信息。

例如,可以用下面的代码在列表部件中启用拖放功能。

         listWidget=QListWidget(self)         listWidget.setSelectionMode(QAbstractItemView.SingleSelection)         listWidget.setDragEnabled(True)         listWidget.viewport().setAcceptDrops(True)         listWidget.setDropIndicatorShown(True)

这样就可以得到一个可以让项在视图里进行复制的列表部件,它甚至可以让用户在包含相同类型数据的视图间拖动项。注意,在这两种情况下,项是本复制而不是被移动。

如果用户要在视图间移动项,那就要设定列表部件的拖放模式dragDropMode。

         listWidget.setDragDropMode(QAbstractItemView.InternalMove)

使用Model-View框架类

建立一个支持拖放的视图和跟使用项视图部件的方式是一样的,比如,可以用建立QListWidget的方法建立一个QListView视图:

         listView=QListView(self)         listView.setSelectionMode(QAbstractItemView.ExtendedSelection)         listView.setDragEnabled(True)         listView.setAcceptDrops(True)         listView.setDropIndicatorShown(True)

因为视图所显示的数据由模型来控制存取,所以模型类也要提供对拖放操作的支持。模型类多拖放动作的支持可以通过重新实现QAbstractItemModel.supportedDropActions()函数来指定。下面我们以自定义类DragDropListModel(QStringListModel)来演示拖放功能。

以下的代码实现复制和移动的操作:

         def supportedDropActions(self):                   return Qt.CopyAction | Qt.MoveAction

虽然可是设定Qt.DropActions里的值的任意组合,但是还是需要重新实现模型中的一些函数才能完成需要支持的功能。例如,为了让一个列表模型能正确地支持Qt.MoveAction动作,不管是直接还是间接从其基类继承,模型必须重新实现QAbstractItemModel.remove()函数。

启动对项的拖放功能

通过重新实现QAbstractItemModel.flags()函数来提供合适的标识,指示模型视图哪些项可以拖动,哪些项可以接收项。例如,通过在返回的标识中包含Qt.ItemsDragEnabled 和Qt.ItemsDropEnabled, 一个基于QAbstractListModel的列表模型就可以使每个项都可以拖放:

         def flags(self, index):                   defaultFlags=QStringListModel.flags(self, index)                   if index.isValid():                            return Qt.ItemIsDragEnabled | Qt.ItemIsDropEnabled | defaultsFlags                   else:                            return Qt.ItemIsDropEnabled | defaultFlags注意,项可以放置在模型的顶层,而拖动操作只对合法项有效。

编码导出数据

当通过拖放操作从模型中导出数据项时,会将它们编码成与一种或多种MIME类型相对应的适当格式。通过重新实现回标准MIME类型的列表的QAbstractItemModel .mimeTypes()函数,来声明可用供项使用的MIME类型。例如,一个仅提供纯文本的模型需提供以下实现:

         def mimeTypes(self):                   return ['application/vnd.text.list']

模型同时还需要提供对公开格式的数据进行编码的代码实现,这可以通过重新实现QAbstractItemModel.mimeData()函数来提供。

以下代码实现将索引参数相关的项数据编码成纯文本并存储在QMiMeData对象中。

         def mimeData(self, indexes):                   mmData =QMimeData()                   encodedData=QByteArray()                   stream =QDataStream(encodeData, QIODevice.WriteOnly)                  for index in indexes:                            if index.isValid():                                     text =self.data(index, Qt.DisplayRole)                                     stream.writeQString(str(text))                   mmData.setData('application/vnd.text.list', ecodedData)                   return mmData

注意:自定义的数据类型必须申明为meta objects,并且要为其实现流操作。

将释放的数据插入到模型中

模型处理释放的数据的方法依赖于的类型(列表,表格或树形)以及它向用户显示其内容的方法。通常情况下,最适合模型底层数据存储的方法就是处理释放的数据的方法。

不同类型的模型会用不同的方法处理释放的数据。列表和表格模型只提供一个存储项的平面结构。因此,当数据被释放到视图中的一个现有的项上面时,它们可以插入新的行(和列),或者使用提供的数据覆盖掉模型里项的内容。树形模型一般是向他们的底层数据增加包含新数据的子项。

通过重新实现模型的QAbstractItemModel::dropMimeData()函数来实现对释放数据的处理。例如,一个处理简单字符串列表的模型可以提供一种实现,该实现将处理放入现有项目的数据与放入模型顶层(即无效项目)的数据分开处理。

通过重新实现QAbstractItemModel :: canDropMimeData(),模型可以禁止删除某些项目,或者取决于删除的数据。该模型首先必须确保应该执行操作,所提供的数据采用可以使用的格式,并且其在模型中的目的地是有效的:

         def canDropMimeData(self, data, action, row, column, parent):                   if data.hasFormat('application/vnd.text.list') is False:                            return False                   if column > 0:                            return False                   return True          def dropMimeData(self, data, action, row, column, parent):                   if self.canDropMimeData(data,action,row,column,parent) is False                            return False                   if action == Qt.IgnoreAction:                            return True

如果提供的数据不是纯文本,或者给出的用于放置的列号是无效的,那么这个字符串列表模型可以将此操作标志为失败。

根据数据是否被放置在一个现有的项上面作为判断,插入模型的数据将做不同的处理。在这个例子中,我们允许把数据放在现有项之间,列表的第一个项之前,以及在列表的最后一个项之后。

当一个放下操作发生时,如果父项相对应的模型索引是有效的,意味着放下操作发生在一个项上面,如果是无效的,则意味着放下操作发生在视图中对应于模型顶层的某个位置。

                   beginRow =-1                   if row != -1:                            beginRow =row

先检查指定的行号看它是否可以用来将项插入到模型中,不管父项的索引是否有效:

                   elif parent.isValid():                            beginRow=parent.row()

如果父项索引是有的,则放下操作发生在一个项上。在本列表模型中,我们找出项的行号,并用这个值把放下的项插入到模型的顶层。

                   else:                            beginRow =self.rowCount(QModelIndex())

当放下动作发生在视图的某个位置,同时行号又是不可用的,那我们就把项添加在模型的顶层项。在层次结构模型中,当放下动作发生在一个项上时,插入的项作为该项的子项插入到模型中。

解码导入的数据

对dropMimeData()实现的同时也必须对数据进行解码, 并把它插入到模型的底层数据结构中。在本文模型中,编码后的项可以被解码并加入到一个字符串列表中。

         encodedData = data.data('application/vnd.text.list')         stream = QDataStream (encodeData, QIODevice.ReadOnly)         newItems =[]         rows = 0         while stream.atEnd() is False:                   text = stream.readQString()                   newItem.append(str(text)                   rows += 1

字符串就可以插入到底层数据。为了保持一致性,可以通过模型自己的接口实现:

         self.insertRows(beginRow, rows, QModelIndex())         for text in newItems:                   idx = self.index(beginRow, 0, QModelIndex())                   self.setData(idx, text)                   beginRow += 1         return True

注意,模型通常要提供 QAbstractItemModel::insertRows()函数和 QAbstractItemModel::setData()函数的实现。

演示代码

建立演示文件dragdropdemo.py完整代码如下:

import sysfrom PyQt5 import QtCore, QtGui, QtWidgetsfrom PyQt5.QtCore import (Qt, QStringListModel,QModelIndex,                          QMimeData,QByteArray, QDataStream, QIODevice)from PyQt5.QtWidgets import (QApplication, QMainWindow, QListView, QAbstractItemView) class DragDropListModel(QStringListModel):    def __init__(self, parent=None):        super(DragDropListModel, self).__init__(parent)            def supportedDropActions(self):        return Qt.CopyAction | Qt.MoveAction        def flags(self, index):        defaultFlags = QStringListModel.flags(self,index)                if index.isValid():            return Qt.ItemIsDragEnabled | Qt.ItemIsDropEnabled | defaultFlags        else:            return Qt.ItemIsDropEnabled | defaultFlags            def mimeTypes(self):        return ['application/vnd.text.list']        def mimeData(self, indexes):        mmData = QMimeData()        encodedData = QByteArray()        stream = QDataStream(encodedData, QIODevice.WriteOnly)                for index in indexes:            if index.isValid():                text = self.data(index, Qt.DisplayRole)                stream.writeQString(str(text))                        mmData.setData('application/vnd.text.list', encodedData)        return mmData        def canDropMimeData(self, data, action, row, column, parent):        if data.hasFormat('application/vnd.text.list') is False:            return False        if column > 0:            return False               return True        def dropMimeData(self, data, action, row, column, parent):        if self.canDropMimeData(data, action, row, column, parent) is False:            return False                if action == Qt.IgnoreAction:            return True                beginRow = -1        if row != -1:            beginRow = row        elif parent.isValid():            beginRow = parent.row()        else:            beginRow = self.rowCount(QModelIndex())                    encodedData = data.data('application/vnd.text.list')        stream = QDataStream(encodedData, QIODevice.ReadOnly)        newItems=[]        rows = 0                while stream.atEnd() is False:            text = stream.readQString()            newItems.append(str(text))            rows += 1                    self.insertRows(beginRow, rows, QModelIndex())        for text in newItems:            idx = self.index(beginRow, 0, QModelIndex())            self.setData(idx, text)            beginRow += 1                    return True             class DemoDragDrop(QMainWindow):    def __init__(self, parent=None):        super(DemoDragDrop, self).__init__(parent)                    # 设置窗口标题        self.setWindowTitle('实战PyQt5: Model-View框架 拖放 演示')              # 设置窗口大小        self.resize(480, 320)              self.initUi()            def initUi(self):        listView = QListView(self)        listView.setSelectionMode(QAbstractItemView.ExtendedSelection)        listView.setDragEnabled(True)        listView.setAcceptDrops(True)        listView.setDropIndicatorShown(True)                ddm = DragDropListModel()        ddm.setStringList(['Item 1', 'Item 2', 'Item 3', 'Item 4'])        listView.setModel(ddm)                self.setCentralWidget(listView)    if __name__ == '__main__':    app = QApplication(sys.argv)    window = DemoDragDrop()    window.show()    sys.exit(app.exec())  

运行结果如下图:

视图中条目的拖放功能演示

本文知识点

  • 启动MV框架视图类的拖放功能;
  • 设置项的拖放操作;
  • 编码导出数据;
  • 将数据插入到释放的位置;
  • 解码导入数据。

喜欢本文内容就关注, 收藏,点赞,评论和转发。

winform listview 设置选中项 图片_实战PyQt5: 069-MV框架中的项视图拖放功能相关推荐

  1. pyqt5 qlabel无法显示图片_实战PyQt5: 011-单选框控件QRadioButton

    单选框QRadioButton简介 QRadioButton为单选按钮, 可以选中(打开)或者取消选中(关闭).在一组单选按钮中,一次只能选中其中的一个按钮.选中或者取消选中QRadioButton, ...

  2. qss qmenu 设置icon位置大小_实战PyQt5: 125-使用配置文件来保存和恢复应用的设置...

    在通常情况下,用户使用一个软件的时候,期望保存一些数据状态,比如窗口的大小和位置,主题,选项,最近操作过的文件等等信息,在下一次启动软件的时候就自动加载这些信息,让软件恢复到上一次退出时的状态.在Wi ...

  3. android listview设置选中时的item的背景色

    今天实在不知道写些什么内容,所以自己在网上找了些资料,觉得还可以,所以还是写一下,觉得每天写些东西的习惯不能荒废. 默认情况下使用ListView背景色是黑色,选中item的高亮颜色是菊黄色,很多时候 ...

  4. qtabwidget的图标_实战PyQt5: 050-选项卡控件QTabWidget

    QTabWidget简介 在GUI应用软件中,多页面的切换的使用范围十分广泛,在操作过程中,用户使用鼠标点击不同的标题栏就可以方便展示不同页面的内容,众多界面共用一块显示区域,在有限的就显示区域中可以 ...

  5. qchart 坐标轴设置_实战PyQt5: 156-QChart图表之更换图表主题

    图表主题 QChart定义了多种图表主题,可以创建不同风格的图表显示,在调整图表主题风格的时候,为了使整个应用的风格看起来更和谐一致,一般要使用应用程序的背景调色板来调整应用的颜色以适配图表所选的主题 ...

  6. qchart 设置线颜色_实战PyQt5: 137-QChart图表之散点图

    散点图(scatter chart)将序列显示为一组点.值由点在图表中的位置表示.类别由图表中的不同标记表示.散点图通常用于比较跨类别的聚合数据.在QChart中,使用类QScatterSeries创 ...

  7. button 样式_实战PyQt5: 111-可以使用QSS样式表的部件

    可以使用QSS样式表的部件 QSS样式表支持各种部件,属性,伪状态和子控件,从而可以实现定制部件的外观,下面我们将详细列出可以使用QSS样式表的部件: QAbstractScrollArea:所有派生 ...

  8. pyqt5从子目录加载qrc文件_实战PyQt5: 045-添加资源文件

    添加资源文件 在使用PyQt进行图形界面开发的时候不免要用到一些外部资源,比如图片,qss配置文件等.在前面代码中,遇到这类问题,我们使用绝对路径的方式来解决,这种方式,本身有其不方便之处(比如,调整 ...

  9. pyqt5 qwidget 边框_实战PyQt5: 116-画刷类QBrush

    QBrush简介 QBrush类定义由QPainter绘制的形状的填充图案.一个画刷可以有颜色,填充图案,渐变和纹理属性. 样式填充使用Qt.BrushStyle枚举变量定义填充图案.默认的填充样式为 ...

最新文章

  1. Dubbo 如何成为连接异构微服务体系的最佳服务开发框架
  2. NLP/CV模型跨界,视觉Transformer赶超CNN?
  3. Cacti 插件中setup.php 文件的编写
  4. WebGL(五)——WEBGL缓冲区,绘制三角形
  5. 三农电子商务创业创新大赛作品_全国大学生电子商务“创新、创意及创业”挑战赛五邑大学校赛...
  6. 电机编码器调零步骤_蒂森电梯编码器整定和主机整定大全
  7. 码说 | 并查集(以HDU1232为例)
  8. havlenapetr ffmpeg的移植
  9. thinkphp在nginx下pathinfo支持
  10. win8内置管理员用户无法激活此应用
  11. 如何枚举系统的视音频采集设备
  12. ansi编码转换器安卓版_ANSI Converter-ANSI码转换器
  13. 机器学习及其Matlab实现——从基础到实践
  14. matlab画正态分布图简单算法
  15. CAD教程:如何批量生成CAD填充边界?
  16. 华师大计算机科学与技术考研科目,2020华东师范大学计算机与软件工程考研初试科目、参考书目、招生人数汇总...
  17. 中图杯获奖作品计算机组,高教杯”全国大学生先进成图技术与产品信息建模创新大赛试题资料.doc...
  18. Java操作ffmpeg为视频添加音乐
  19. 雷达原理习题答案【西电】
  20. Java 常用的基本关键字和常用单词

热门文章

  1. 阿里云ECS使用cloudfs4oss挂载OSS
  2. Leetcode中单链表题总结
  3. 【BZOJ 2323】 2323: [ZJOI2011]细胞 (DP+矩阵乘法+快速幂*)
  4. mysql补充(1)校对集utf8_unicode_ci与utf8_general_ci
  5. java 22 - 8 多线程之线程生命周期图解
  6. ATL的GUI程序设计(前言)
  7. 多个DIV排列时居中
  8. homepod怎么设置为中文_设置 HomePod
  9. 母根服务器对接 不准发信息,中国的母根服务器
  10. linux系统root默认密码是多少钱,linux root默认密码忘记后的解决方法