gpt4 book ai didi

python-3.x - 在 PyQt5 中,如何使用拖放正确移动 QTableView 中的行

转载 作者:行者123 更新时间:2023-12-04 10:09:48 25 4
gpt4 key购买 nike

我(只是)希望能够使用 QTableView ■ 拖放机制以移动现有行。我找到了很多来源(例如 hereherehere ),它们描述了拖放、插入等的某些方面,但我仍在努力使其适用于我的情况。

这是我正在寻找的解决方案应该具备的能力:

  • 处理“Qt-free”数据结构,例如元组列表。
  • 对数据结构进行操作。即当项目的顺序得到
    在 View 中修改它应该在数据结构中修改
  • 标准拖放启用列表的外观和感觉:
  • 选择/移动整行
  • 显示整条线的下降指示器
  • 必须仍然可以进行进一步的操作,例如删除/编辑单元格
    即不受拖放方法的影响

  • This tutorial显示了一个非常接近我需要的解决方案,但它使用了 QStandardItemModel而不是 QAbstractTableModel这对我来说看起来是半最佳的,因为我必须在基于 QStandardItem 的“镜像”数据结构上进行操作这是 QStandardItemModel 所需要的(我对吗?)

    下面附上了代表我当前进度的代码。

    目前我看到两种可能的方法:

    方法一 :针对 QAbstractTableModel实现并实现所有需要的事件/插槽来修改底层数据结构:
    * pro:最通用的方法
    * pro:无冗余数据
    * con:我不知道如何获知已完成的拖放
    操作以及什么索引被移动到哪里

    在我附加的代码中,我跟踪我知道的所有相关方法并打印出所有参数。这是我将第 2 行拖到第 3 行时得到的结果
    dropMimeData(data: ['application/x-qabstractitemmodeldatalist'], action: 2, row: -1, col: -1, parent: '(row: 2, column: 0, valid: True)')
    insertRows(row=-1, count=1, parent=(row: 2, column: 0, valid: True))
    setData(index=(row: 0, column: 0, valid: True), value='^line1', role=0)
    setData(index=(row: 0, column: 1, valid: True), value=1, role=0)
    removeRows(row=1, count=1, parent=(row: -1, column: -1, valid: False))

    此输出为我提出了以下问题:
  • 为什么要moveRow/moveRows不被叫?他们什么时候会被召唤?
  • 为什么是 insertRow/removeRow不叫,只叫 insertRows/removeRows ?
  • -1 的行索引是什么?意思?
  • 我可以用 dropMimeData 中提供的 MIME 数据做什么?我以后应该用它来复制数据吗?

  • 方法二 : 使用 QStandardItemModel并与 QStandardItemModel 管理的数据并行修改您的数据.
    * 专业人士:有一个 working example
    * 反对:您管理一个必须一致的冗余数据结构
    与另一个内部管理的数据结构。
    * 反对:也没有确切地知道如何做到这一点

    Here is my current approach using QAbstractTableModel:


    from PyQt5 import QtWidgets, QtCore, QtGui

    class MyModel(QtCore.QAbstractTableModel):
    def __init__(self, data, parent=None, *args):
    super().__init__(parent, *args)
    self._data = data

    def columnCount(self, parent):
    return 2

    def rowCount(self, parent):
    return len(self._data)

    def headerData(self, column: int, orientation, role: QtCore.Qt.ItemDataRole):
    return (('Regex', 'Category')[column]
    if role == QtCore.Qt.DisplayRole and orientation == QtCore.Qt.Horizontal
    else None)

    def data(self, index, role: QtCore.Qt.ItemDataRole):
    if role not in {QtCore.Qt.DisplayRole, QtCore.Qt.EditRole}:
    return None

    print("data(index=%s, role=%r)" % (self._index2str(index), self._role2str(role)))
    return (self._data[index.row()][index.column()]
    if index.isValid()
    and role in {QtCore.Qt.DisplayRole, QtCore.Qt.EditRole}
    and index.row() < len(self._data)
    else None)

    def setData(self, index: QtCore.QModelIndex, value, role: QtCore.Qt.ItemDataRole):

    print("setData(index=%s, value=%r, role=%r)" % (self._index2str(index), value, role))
    return super().setData(index, value, role)

    def flags(self, index):
    return (
    super().flags(index)
    | QtCore.Qt.ItemIsDropEnabled
    | (QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsDragEnabled)
    if index.isValid() else QtCore.Qt.NoItemFlags)

    def dropMimeData(self, data, action, row, col, parent: QtCore.QModelIndex):
    """Always move the entire row, and don't allow column 'shifting'"""
    print("dropMimeData(data: %r, action: %r, row: %r, col: %r, parent: %r)" % (
    data.formats(), action, row, col, self._index2str(parent)))
    assert action == QtCore.Qt.MoveAction
    return super().dropMimeData(data, action, row, 0, parent)

    def supportedDragActions(self):
    return QtCore.Qt.MoveAction

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

    def removeRow(self, row: int, parent=None):
    print("removeRow(row=%r):" % (row))
    return super().removeRow(row, parent)

    def removeRows(self, row: int, count: int, parent=None):
    print("removeRows(row=%r, count=%r, parent=%s)" % (row, count, self._index2str(parent)))
    return super().removeRows(row, count, parent)

    def insertRow(self, index, parent=None):
    print("insertRow(row=%r, count=%r):" % (row, count))
    return super().insertRow(row, count, parent)

    def insertRows(self, row: int, count: int, parent: QtCore.QModelIndex = None):
    print("insertRows(row=%r, count=%r, parent=%s)" % (row, count, self._index2str(parent)))
    return super().insertRows(row, count, parent)

    @staticmethod
    def _index2str(index):
    return "(row: %d, column: %d, valid: %r)" % (index.row(), index.column(), index.isValid())

    @staticmethod
    def _role2str(role: QtCore.Qt.ItemDataRole) -> str:
    return "%s (%d)" % ({
    QtCore.Qt.DisplayRole: "DisplayRole",
    QtCore.Qt.DecorationRole: "DecorationRole",
    QtCore.Qt.EditRole: "EditRole",
    QtCore.Qt.ToolTipRole: "ToolTipRole",
    QtCore.Qt.StatusTipRole: "StatusTipRole",
    QtCore.Qt.WhatsThisRole: "WhatsThisRole",
    QtCore.Qt.SizeHintRole: "SizeHintRole",

    QtCore.Qt.FontRole: "FontRole",
    QtCore.Qt.TextAlignmentRole: "TextAlignmentRole",
    QtCore.Qt.BackgroundRole: "BackgroundRole",
    #QtCore.Qt.BackgroundColorRole:
    QtCore.Qt.ForegroundRole: "ForegroundRole",
    #QtCore.Qt.TextColorRole
    QtCore.Qt.CheckStateRole: "CheckStateRole",
    QtCore.Qt.InitialSortOrderRole: "InitialSortOrderRole",
    }[role], role)


    class MyTableView(QtWidgets.QTableView):
    class DropmarkerStyle(QtWidgets.QProxyStyle):
    def drawPrimitive(self, element, option, painter, widget=None):
    """Draw a line across the entire row rather than just the column we're hovering over.
    This may not always work depending on global style - for instance I think it won't
    work on OSX."""
    if element == self.PE_IndicatorItemViewItemDrop and not option.rect.isNull():
    option_new = QtWidgets.QStyleOption(option)
    option_new.rect.setLeft(0)
    if widget:
    option_new.rect.setRight(widget.width())
    option = option_new
    super().drawPrimitive(element, option, painter, widget)

    def __init__(self):
    super().__init__()
    self.setStyle(self.DropmarkerStyle())
    # only allow rows to be selected
    self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
    # disallow multiple rows to be selected
    self.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
    self.setDragEnabled(True)

    self.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
    self.setDropIndicatorShown(True) # default
    self.setAcceptDrops(False) # ?
    self.viewport().setAcceptDrops(True) # ?
    self.setDragDropOverwriteMode(False)


    class HelloWindow(QtWidgets.QMainWindow):
    def __init__(self) -> None:
    super().__init__()

    model = MyModel([("^line0", 0),
    ("^line1", 1),
    ("^line2", 2),
    ("^line3", 3)])

    table_view = MyTableView()
    table_view.setModel(model)
    table_view.verticalHeader().hide()
    table_view.setShowGrid(False)

    self.setCentralWidget(table_view)


    def main():
    app = QtWidgets.QApplication([])
    window = HelloWindow()
    window.show()
    app.exec_()

    if __name__ == "__main__":
    main()

    最佳答案

    MyData 类应该从 QStandardItemModel 继承
    修改您的代码以解决拖放和扩展类函数调用问题。

    from PyQt5 import (QtWidgets, QtCore)
    from PyQt5.QtWidgets import (QApplication, QTableView)
    from PyQt5.QtGui import (QStandardItem, QStandardItemModel)


    class MyModel(QStandardItemModel):
    def __init__(self, data, parent=None, *args):
    super().__init__(parent, *args)
    self._data = data

    for (index, data) in enumerate(data):
    first = QStandardItem('Item {}'.format(index))
    first.setDropEnabled(False)
    first.setEditable(False)
    second = QStandardItem(data[0])
    second.setDropEnabled(False)
    second.setEditable(False)
    self.appendRow([first, second])

    def columnCount(self, parent):
    return 2

    def rowCount(self, parent):
    return len(self._data)

    def headerData(self, column: int, orientation, role: QtCore.Qt.ItemDataRole):
    return (('Regex', 'Category')[column]
    if role == QtCore.Qt.DisplayRole and orientation == QtCore.Qt.Horizontal
    else None)

    def data(self, index, role: QtCore.Qt.ItemDataRole):
    if role not in {QtCore.Qt.DisplayRole, QtCore.Qt.EditRole}:
    return None

    print("data(index=%s, role=%r)" % (self._index2str(index), self._role2str(role)))
    return (self._data[index.row()][index.column()]
    if index.isValid() and role in {QtCore.Qt.DisplayRole, QtCore.Qt.EditRole} and index.row() < len(
    self._data)
    else None)

    def setData(self, index: QtCore.QModelIndex, value, role: QtCore.Qt.ItemDataRole):
    print("setData(index=%s, value=%r, role=%r)" % (self._index2str(index), value, role))
    return super().setData(index, value, role)

    def flags(self, index):
    return (
    super().flags(index)
    | QtCore.Qt.ItemIsDropEnabled
    | (QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsDragEnabled)
    if index.isValid() else QtCore.Qt.NoItemFlags)

    def dropMimeData(self, data, action, row, col, parent: QtCore.QModelIndex):
    """Always move the entire row, and don't allow column 'shifting'"""
    print("dropMimeData(data: %r, action: %r, row: %r, col: %r, parent: %r)" % (
    data.formats(), action, row, col, self._index2str(parent)))
    assert action == QtCore.Qt.MoveAction
    return super().dropMimeData(data, action, row, 0, parent)

    def supportedDragActions(self):
    return QtCore.Qt.MoveAction

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

    def removeRow(self, row: int, parent=None):
    print("removeRow(row=%r):" % (row))
    return super().removeRow(row, parent)

    def removeRows(self, row: int, count: int, parent=None):
    print("removeRows(row=%r, count=%r, parent=%s)" % (row, count, self._index2str(parent)))
    return super().removeRows(row, count, parent)

    def insertRow(self, index, parent=None):
    print("insertRow(row=%r, count=%r):" % (row, count))
    return super().insertRow(row, count, parent)

    def insertRows(self, row: int, count: int, parent: QtCore.QModelIndex = None):
    print("insertRows(row=%r, count=%r, parent=%s)" % (row, count, self._index2str(parent)))
    return super().insertRows(row, count, parent)

    @staticmethod
    def _index2str(index):
    return "(row: %d, column: %d, valid: %r)" % (index.row(), index.column(), index.isValid())

    @staticmethod
    def _role2str(role: QtCore.Qt.ItemDataRole) -> str:
    return "%s (%d)" % ({
    QtCore.Qt.DisplayRole: "DisplayRole",
    QtCore.Qt.DecorationRole: "DecorationRole",
    QtCore.Qt.EditRole: "EditRole",
    QtCore.Qt.ToolTipRole: "ToolTipRole",
    QtCore.Qt.StatusTipRole: "StatusTipRole",
    QtCore.Qt.WhatsThisRole: "WhatsThisRole",
    QtCore.Qt.SizeHintRole: "SizeHintRole",

    QtCore.Qt.FontRole: "FontRole",
    QtCore.Qt.TextAlignmentRole: "TextAlignmentRole",
    QtCore.Qt.BackgroundRole: "BackgroundRole",
    # QtCore.Qt.BackgroundColorRole:
    QtCore.Qt.ForegroundRole: "ForegroundRole",
    # QtCore.Qt.TextColorRole
    QtCore.Qt.CheckStateRole: "CheckStateRole",
    QtCore.Qt.InitialSortOrderRole: "InitialSortOrderRole",
    }[role], role)


    class MyTableView(QTableView):
    class DropMarkerStyle(QtWidgets.QProxyStyle):
    def drawPrimitive(self, element, option, painter, widget=None):
    """Draw a line across the entire row rather than just the column we're hovering over.
    This may not always work depending on global style - for instance I think it won't
    work on OSX."""
    if element == self.PE_IndicatorItemViewItemDrop and not option.rect.isNull():
    option_new = QtWidgets.QStyleOption(option)
    option_new.rect.setLeft(0)
    if widget:
    option_new.rect.setRight(widget.width())
    option = option_new
    super().drawPrimitive(element, option, painter, widget)

    def __init__(self):
    super().__init__()
    self.setStyle(self.DropMarkerStyle())
    self.verticalHeader().hide()
    self.setShowGrid(False)
    # only allow rows to be selected
    self.setSelectionBehavior(self.SelectRows)
    # disallow multiple rows to be selected
    self.setSelectionMode(self.SingleSelection)
    self.setDragDropMode(self.InternalMove)
    self.setDragDropOverwriteMode(False)


    class HelloWindow(QtWidgets.QMainWindow):
    def __init__(self) -> None:
    super().__init__()
    model = MyModel([("^line0", 0),
    ("^line1", 1),
    ("^line2", 2),
    ("^line3", 3)])
    table_view = MyTableView()
    table_view.setModel(model)
    self.setCentralWidget(table_view)


    def main():
    app = QApplication([])
    window = HelloWindow()
    window.show()
    app.exec_()


    if __name__ == "__main__":
    main()

    关于python-3.x - 在 PyQt5 中,如何使用拖放正确移动 QTableView 中的行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61387248/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com