QTableView 如何从单元格中获取纯文本

How to get plain text from a cell QTableView

我正在尝试在两个项目之间进行拖放操作,但无法从我在 table 中单击的单元格中获取信息。我有一个定制 table,它有一个定制模型。

class PyTableWidget(QTableView):
    def __init__(self, *args):
        super().__init__()
        self.setDragEnabled(True)

        self.set_stylesheet(*args)

    def onLeftClick(self, index):
                print('onClick index.row: %s, index.col: %s' % (index.row(), index.column()))

    def mouseMoveEvent(self, event):
        if event.buttons() == Qt.LeftButton:
            drag = QDrag(self)
            mime = QMimeData()
            drag.setMimeData(mime)
            drag.exec(Qt.MoveAction)

这是 table 的自定义模型:

class CustomizedNumpyModel(QAbstractTableModel):
    def __init__(self, data, parent=None):
        QAbstractTableModel.__init__(self, parent)
        self._data = np.array(data.values)
        self._cols = data.columns
        self.r, self.c = np.shape(self._data)

    def data(self, index, role=Qt.DisplayRole):

        if role == Qt.DisplayRole:
            value = self._data[index.row(), index.column()]

            if isinstance(value, float):
                return "%.2f" % value

            if isinstance(value, str):
                return '%s' % value

            return unicode(value)

    def rowCount(self, parent=None):
        return self.r

    def columnCount(self, parent=None):
        return self.c

    def headerData(self, p_int, orientation, role):
        if role == Qt.DisplayRole:
            if orientation == Qt.Horizontal:
                return str(self._cols[p_int])

            if orientation == Qt.Vertical:
                return p_int
        return None

    def flags(self, index):
        return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable

    def setData(self, index, value, role=Qt.EditRole):
        index_value = None
        based_columns = [6, 8, 10, 12]
        if role == Qt.EditRole:
            row = index.row()
            column = index.column()
            tmp = str(value)
            if tmp != '':
                if column in based_columns:
                    if column == 6 and tmp in self._cols:
                        index_no = np.where(self._cols == tmp)[0][0]
                        self._data[row, column + 1] = self._data[row, index_no]
                        self._data[row, column] = tmp
                    elif column in [8, 10, 12]:
                        for x in range(97, 181):
                            if self._data[row, x] == int(tmp):
                                index_value = x
                                break

                        col_name = self._cols[index_value]
                        col_name = col_name.removesuffix('_rank')
                        self._data[row, column + 1] = col_name
                        self._data[row, column] = tmp
                    self.dataChanged.emit(index, index)
                return True
            else:
                return False

这里是接受掉落的另一个小部件中的代码

class PyCustomButton(QPushButton):
    def __init__(self, *args):
        super().__init__()
        self.setCursor(Qt.PointingHandCursor)
        self.setAcceptsDrops(True)
        self._lista = []

    def dragEnterEvent(self, event):
        if event.mimeData().hasText():
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        self._lista.append(event.mimeData().text())
        print(self._lista)

table有以下方面:

Names
First
Second

我想要纯文本,即 FirstSecond 从 table 拖放到自定义按钮中。

简单的解决方案是使用 indexAt(). Note that it's generally better to start a drag action when the user has moved the mouse by a certain amount of pixels, which defaults to the QApplication startDragDistance() 属性 获取“选定”索引。

class PyTableWidget(QTableView):
    _startPos = None
    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            index = self.indexAt(event.pos())
            if index.isValid():
                self._startPos = event.pos()
        super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        if event.buttons() == Qt.LeftButton and self._startPos is not None:
            delta = (event.pos() - self._startPos).manhattanLength()
            if delta >= QApplication.startDragDistance():
                drag = QDrag(self)
                mime = QMimeData()
                mime.setText(self.indexAt(self._startPos).data())
                drag.setMimeData(mime)
                drag.exec(Qt.MoveAction)
                self._startPos = None
                return
        super().mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        self._startPos = None
        super().mouseReleaseEvent(event)

请注意,Qt 项目视图已经支持拖放:要在自定义模型中启用该支持,您还需要在 flags() 中添加 Qt.ItemIsDragEnabled 并确保 table 支持通过使用 setDragEnabled(True)(就像您所做的那样)或通过将视图的 dragDropMode 至少设置为 DragOnly.

来拖动

唯一剩下的“问题”是从拖动 mime 数据中获取 视图外部的数据,它使用内部格式序列化内容。

虽然这种方法可能看起来更复杂和困难,但如果您打算支持多个项目视图之间的拖放,它也是一个更好的解决方案,因为该序列化格式是所有 Qt 视图的通用和标准格式。

class CustomizedNumpyModel(QAbstractTableModel):
    # ...
    def flags(self, index):
        return (Qt.ItemIsEnabled 
            | Qt.ItemIsSelectable 
            | Qt.ItemIsEditable 
            | Qt.ItemIsDragEnabled)


class PyTableWidget(QTableView):
    def __init__(self, *args):
        super().__init__(*args)
        self.setDragEnabled(True)

    # nothing else to implement in this class


class PyCustomButton(QPushButton):
    def __init__(self, *args):
        super().__init__()
        self.setCursor(Qt.PointingHandCursor)
        self.setAcceptDrops(True)
        self._lista = []

    def dragEnterEvent(self, event):
        if 'application/x-qabstractitemmodeldatalist' in event.mimeData().formats():
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        mimeData = event.mimeData()
        if mimeData.hasText():
            self._lista.append(mimeData.text())
        elif 'application/x-qabstractitemmodeldatalist' in mimeData.formats():
            names = []
            stream = QDataStream(mimeData.data(
                'application/x-qabstractitemmodeldatalist'))
            while not stream.atEnd():
                # all fields must be read, even if not used
                row = stream.readInt()
                column = stream.readInt()
                for _ in range(stream.readInt()):
                    role = stream.readInt()
                    value = stream.readQVariant()
                    if role == Qt.DisplayRole:
                        names.append(value)
            self._lista.extend(names)
        print(self._lista)

注意 setData() 必须 always return 一个 bool。删除最后一个 else: 并将 return False 推回函数的主缩进级别。