网站首页 > 技术文章 正文
模型-视图框架完全支持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 sys
from PyQt5 import QtCore, QtGui, QtWidgets
from 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框架视图类的拖放功能;
- 设置项的拖放操作;
- 编码导出数据;
- 将数据插入到释放的位置;
- 解码导入数据。
喜欢本文内容就关注, 收藏,点赞,评论和转发。
猜你喜欢
- 2024-10-20 Qt 开发经验总结 qt软件开发
- 2024-10-20 Qt QTableWidget用法总结 qtablewidget qtableview
- 2024-10-20 实战PyQt5: 068-MV框架中的项视图部件
- 2024-10-20 Qt开发经验小技巧231-235 qt开发入门简介
- 2024-10-20 Python+PyQt5进阶(5) pyqt5 django
- 2024-10-20 Qt项目升级到Qt6经验总结 qt更新界面
- 2024-10-20 QTableWidget表格中增删数据 qtablewidget清空表格
- 2024-10-20 C/C++ Qt StatusBar 底部状态栏应用
- 2024-10-20 Qt的常用控件 qt控件详解
- 2024-10-20 Python PyQt5通过QTableWidget表格控件操作SQLite数据库
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)