gpt4 book ai didi

qt - 在 QTableWidget 中拖放行

转载 作者:行者123 更新时间:2023-12-04 15:24:04 28 4
gpt4 key购买 nike

目标

我的目标是拥有一个 QTableWidget用户可以在其中拖放行。也就是说,用户可以拖放一整行,将其在表格中向上或向下移动到其他两行之间的不同位置。目标如下图所示:

the challenge

我尝试了什么,会发生什么

一旦我填充了 QTableWidget使用数据,我将其属性设置如下:

table.setDragDropMode(QtGui.QAbstractItemView.InternalMove)   
#select one row at a time
table.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
table.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)

类似的代码使得 QListWidget表现得很好:当你在内部移动一个项目时,它被放置在列表的两个元素之间,其余的项目以合理的方式进行排序,没有数据被覆盖(换句话说, View 的行为类似于上图,但它是一个列表)。

相比之下,在使用上述代码修改的表中,事情并没有按计划进行。下图显示了实际发生的情况:

crud

换句话说:当行 i 被删除时,该行在表格中变为空白。此外,如果我不小心将第 i 行拖放到第 j 行(而不是两行之间的空格),第 i 行中的数据将替换第 j 行中的数据。也就是说,在这种不幸的情况下,除了第 i 行变为空白之外,第 j 行也被覆盖。

注意我也尝试添加 table.setDragDropOverwriteMode(False)但它并没有改变行为。

前进的道路?

This bug report可能在 C++ 中包含一个可能的解决方案:似乎他们重新实现了 dropEventQTableWidget ,但我不确定如何干净地移植到 Python。

相关内容:
  • Reordering items in a QTreeWidget with Drag and Drop in PyQt
  • QT: internal drag and drop of rows in QTableView, that changes order of rows in QTableModel
  • http://www.qtcentre.org/threads/35113-QTableWidget-dropping-between-the-rows-shall-insert-the-item
  • qt: pyqt: QTreeView internal drag and drop almost working... dragged item disappears
  • How to drag & drop rows within QTableWidget
  • QListWidget drag and drop items disappearing from list on Symbian
  • QTableWidget Internal Drag Drop Entire Row
  • 最佳答案

    这似乎是非常奇怪的默认行为。无论如何,请遵循 bug report you linked to 中的代码,我已经成功地将一些东西移植到了 PyQt。它可能会,也可能不会像该代码那样健壮,但它至少似乎适用于您在屏幕截图中提供的简单测试用例!

    以下实现的潜在问题是:

  • 当前选定的行不跟随拖放(因此,如果您移动第三行,则移动后第三行保持选中状态)。这可能不太难解决!
  • 它可能不适用于具有子行的行。我什至不确定 QTableWidgetItem可以生 child ,所以也许没问题。
  • 我还没有测试过选择多行,但我认为它应该可以工作
  • 出于某种原因,尽管在表中插入了一个新行,但我不必删除正在移动的行。这对我来说似乎很奇怪。它几乎看起来像在任何地方插入一行,但结尾并没有增加 rowCount()表的。
  • 我对 GetSelectedRowsFast 的实现和他们的有点不同。它可能不快,并且可能像他们那样存在一些错误(我不检查项目是否已启用或可选择)。我认为这也很容易解决,但如果您在选中一行时禁用它,然后有人执行拖放操作,这只是一个问题。在这种情况下,我认为更好的解决方案可能是在行被禁用时取消选择行,但这取决于我猜你在做什么!

  • 如果您在生产环境中使用此代码,您可能想仔细检查一下,确保一切都有意义。我的 PyQt 端口很可能存在问题,并且我的端口所基于的原始 C++ 算法也可能存在问题。然而,它确实可以证明使用 QTableWidget 可以实现您想要的功能。 .

    更新:注意有一个 additional answer below对于 PyQt5,它也解决了我上面的一些问题。你可能想看看!

    代码:
    import sys, os
    from PyQt4.QtCore import *
    from PyQt4.QtGui import *

    class TableWidgetDragRows(QTableWidget):
    def __init__(self, *args, **kwargs):
    QTableWidget.__init__(self, *args, **kwargs)

    self.setDragEnabled(True)
    self.setAcceptDrops(True)
    self.viewport().setAcceptDrops(True)
    self.setDragDropOverwriteMode(False)
    self.setDropIndicatorShown(True)

    self.setSelectionMode(QAbstractItemView.SingleSelection)
    self.setSelectionBehavior(QAbstractItemView.SelectRows)
    self.setDragDropMode(QAbstractItemView.InternalMove)

    def dropEvent(self, event):
    if event.source() == self and (event.dropAction() == Qt.MoveAction or self.dragDropMode() == QAbstractItemView.InternalMove):
    success, row, col, topIndex = self.dropOn(event)
    if success:
    selRows = self.getSelectedRowsFast()

    top = selRows[0]
    # print 'top is %d'%top
    dropRow = row
    if dropRow == -1:
    dropRow = self.rowCount()
    # print 'dropRow is %d'%dropRow
    offset = dropRow - top
    # print 'offset is %d'%offset

    for i, row in enumerate(selRows):
    r = row + offset
    if r > self.rowCount() or r < 0:
    r = 0
    self.insertRow(r)
    # print 'inserting row at %d'%r


    selRows = self.getSelectedRowsFast()
    # print 'selected rows: %s'%selRows

    top = selRows[0]
    # print 'top is %d'%top
    offset = dropRow - top
    # print 'offset is %d'%offset
    for i, row in enumerate(selRows):
    r = row + offset
    if r > self.rowCount() or r < 0:
    r = 0

    for j in range(self.columnCount()):
    # print 'source is (%d, %d)'%(row, j)
    # print 'item text: %s'%self.item(row,j).text()
    source = QTableWidgetItem(self.item(row, j))
    # print 'dest is (%d, %d)'%(r,j)
    self.setItem(r, j, source)

    # Why does this NOT need to be here?
    # for row in reversed(selRows):
    # self.removeRow(row)

    event.accept()

    else:
    QTableView.dropEvent(event)

    def getSelectedRowsFast(self):
    selRows = []
    for item in self.selectedItems():
    if item.row() not in selRows:
    selRows.append(item.row())
    return selRows

    def droppingOnItself(self, event, index):
    dropAction = event.dropAction()

    if self.dragDropMode() == QAbstractItemView.InternalMove:
    dropAction = Qt.MoveAction

    if event.source() == self and event.possibleActions() & Qt.MoveAction and dropAction == Qt.MoveAction:
    selectedIndexes = self.selectedIndexes()
    child = index
    while child.isValid() and child != self.rootIndex():
    if child in selectedIndexes:
    return True
    child = child.parent()

    return False

    def dropOn(self, event):
    if event.isAccepted():
    return False, None, None, None

    index = QModelIndex()
    row = -1
    col = -1

    if self.viewport().rect().contains(event.pos()):
    index = self.indexAt(event.pos())
    if not index.isValid() or not self.visualRect(index).contains(event.pos()):
    index = self.rootIndex()

    if self.model().supportedDropActions() & event.dropAction():
    if index != self.rootIndex():
    dropIndicatorPosition = self.position(event.pos(), self.visualRect(index), index)

    if dropIndicatorPosition == QAbstractItemView.AboveItem:
    row = index.row()
    col = index.column()
    # index = index.parent()
    elif dropIndicatorPosition == QAbstractItemView.BelowItem:
    row = index.row() + 1
    col = index.column()
    # index = index.parent()
    else:
    row = index.row()
    col = index.column()

    if not self.droppingOnItself(event, index):
    # print 'row is %d'%row
    # print 'col is %d'%col
    return True, row, col, index

    return False, None, None, None

    def position(self, pos, rect, index):
    r = QAbstractItemView.OnViewport
    margin = 2
    if pos.y() - rect.top() < margin:
    r = QAbstractItemView.AboveItem
    elif rect.bottom() - pos.y() < margin:
    r = QAbstractItemView.BelowItem
    elif rect.contains(pos, True):
    r = QAbstractItemView.OnItem

    if r == QAbstractItemView.OnItem and not (self.model().flags(index) & Qt.ItemIsDropEnabled):
    r = QAbstractItemView.AboveItem if pos.y() < rect.center().y() else QAbstractItemView.BelowItem

    return r


    class Window(QWidget):
    def __init__(self):
    super(Window, self).__init__()

    layout = QHBoxLayout()
    self.setLayout(layout)

    self.table_widget = TableWidgetDragRows()
    layout.addWidget(self.table_widget)

    # setup table widget
    self.table_widget.setColumnCount(2)
    self.table_widget.setHorizontalHeaderLabels(['Colour', 'Model'])

    items = [('Red', 'Toyota'), ('Blue', 'RV'), ('Green', 'Beetle')]
    for i, (colour, model) in enumerate(items):
    c = QTableWidgetItem(colour)
    m = QTableWidgetItem(model)

    self.table_widget.insertRow(self.table_widget.rowCount())
    self.table_widget.setItem(i, 0, c)
    self.table_widget.setItem(i, 1, m)

    self.show()


    app = QApplication(sys.argv)
    window = Window()
    sys.exit(app.exec_())

    关于qt - 在 QTableWidget 中拖放行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26227885/

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