PyQT 列表视图没有响应 datachanged 信号

Posted

技术标签:

【中文标题】PyQT 列表视图没有响应 datachanged 信号【英文标题】:PyQT list view not responding to datachanged signal 【发布时间】:2014-03-21 13:35:18 【问题描述】:

我一直在关注一些教程并尝试设置列表模型。我的主窗口有两个访问同一模型的列表视图。当我更新一个列表中的项目时,另一个列表在获得焦点之前不会自行更新(我单击它)。所以看起来 dataChanged 信号没有被发出,但我无法弄清楚我的代码与我所基于的任何示例有何不同。

ma​​in.py

class Main(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(Main, self).__init__(parent)
        self.ui = uic.loadUi("mainwindow.ui", self)

        # Test model and listviews
        data = [10,20,30,40,50]
        myModel = model.MyListModel(data)
        self.ui.listView.setModel(myModel)
        self.ui.listView_2.setModel(myModel)

model.py

class MyListModel(QtCore.QAbstractListModel):
    def __init__(self, data=[], parent=None):
        super(MyListModel, self).__init__(parent)
        self.__data = data

    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self.__data)

    def data(self, index, role=QtCore.Qt.DisplayRole):
        row = index.row()
        if role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
            return str(self.__data[row])

        if role == QtCore.Qt.ToolTipRole:
            return 'Item at 0'.format(row)

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

    def setData(self, index, value, role=QtCore.Qt.EditRole):
        if role == QtCore.Qt.EditRole:
            self.__data[index.row()] = value
            self.dataChanged.emit(index, index)
            return True
        return False

谁能看出这里有什么问题?仅供参考,我使用的是 PyQT5.2.1 和 Python 3.3。

【问题讨论】:

【参考方案1】:

问题在于dataChanged 信号的签名。在 Qt4 中它看起来像这样:

    dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight)

但在 Qt5 中,它看起来像这样:

    dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight,
                const QVector<int> & roles = QVector<int>())

当我使用 PyQt-5.1.1 尝试您的示例代码时,尝试在没有第三个参数的情况下发出信号时出现错误。严格来说,这是不正确的行为,因为第三个参数有一个默认值。所以这也许就是行为发生变化的原因。

但您现在似乎必须明确发出一个空列表作为dataChanged 的第三个参数,以便在 PyQt5 中正常工作:

    self.dataChanged.emit(index, index, [])

或者,当然,发出实际已更改的角色列表:

    self.dataChanged.emit(index, index, [QtCore.Qt.EditRole])

【讨论】:

谢谢!它现在按预期工作。 Qt4 的网络示例太多,而 Qt5 的网络示例却很少,所有的小差异都让我很着迷。 请注意,如果你想从另一个线程发出这个,qt 不支持。数据项不能跨线程传输。您将不断收到有关注册 QVector 的错误。解决方案是从 thread1 显式传递“emit”命令到 qt/main 线程并从那里发出, @MMM。数据肯定可以安全地跨线程发出。看来您可能发现了一个错误。错误是来自 Qt 还是来自 PyQt(即您是否获得了 python 回溯)? 是的。正因为如此。 bugreports.qt.io/browse/QTBUG-46517 Qt 并不真正支持跨线程发射数据模型项。由于他们拒绝注册 QVector,当使用队列连接(跨线程信号发射)时,该特定信号目前无法发射。因为从主线程发射不使用排队连接,所以当我使用主线程时,同样的信号发射成功 @MMM。实际上,只要先注册了相关类型,就可以发出信号。不过,这只有在模型未连接到视图时才是线程安全的。而且由于模型通常连接到视图,我认为 Qt 默认不注册类型是有道理的。【参考方案2】:

解决办法:

self.dataChanged.emit(index, index, ())

不适合我(python 2.7,PyQt5)。 但以下其中一项会起作用:

self.dataChanged.emit(index, index, [])

self.dataChanged.emit(index, index, list())

【讨论】:

以上是关于PyQT 列表视图没有响应 datachanged 信号的主要内容,如果未能解决你的问题,请参考以下文章

QListView 拒绝显示子类化的 QAbstractListModel

Qt dataChanged信号较慢然后隐藏/显示视图

QTableView 似乎对 dataChanged 信号没有反应

如何使用 pyqt5 在列表视图中列出所有磁盘?

PyQt QListView 拖放问题,用于在一个列表中导入和重新排列

PyQt5 可拖动图标从列表视图到另一个