模型数据未更改时刷新视图(Qt / PySide / PyQt)?

时间:2015-10-11 03:32:03

标签: qt pyqt pyside

我有一个标准项目模型的树形视图,我可以使用一个旋转框来更改视图中的行高(请参阅下面的SSCCE)。这不会改变视图的内容,只会改变它的外观,有点像调整主窗口的大小,除非我必须自己做:

enter image description here

我从委托的sizeHint方法中更改行高。在sizeHint之内,我从spinbox获取值并将行高设置为该值。为了确保实际调用大小提示,我在更改spinbox值时刷新视图。

我的问题是:在纯粹化妆品变化的情况下,推荐视图刷新的推荐方法是什么?是否有专门为这种情况建立的方法? 显然,这个问题假设我调整行高的一般策略是合理的,我也可以对其进行修正。

有一些方法可以告诉视图现在是时候重新获取数据并重新绘制内容:layoutChangedresetsetModeldataChanged。地狱,我发现即使只是在树上调用expandAll就足以更新我的视图以显示新的行高。

在实践中,我发现使用layoutChanged工作:

QtGui.QStandardItemModel.layoutChanged.emit()

这是一种不常见的用法,因为当你重新安排数据时(例如,通过排序)更多。这是我在下面的SSCCE中包含的内容,因为它有效。我还尝试遵循更常见的发布dataChanged的做法:

 QtGui.QStandardItemModel.dataChanged.emit(QtCore.QModelIndex(), QtCore.QModelIndex())

这对我不起作用。即使它确实如此,它也会成为一种黑客攻击,因为它告诉观点,模型中的数据已经发生了变化。什么时候没有。

无论如何,在网上有很多关于你在模型中更改数据时要做什么的讨论(参见相关帖子),但是当你只是想简单地刷新视图时我没有找到关于该做什么的讨论出于纯粹的美容原因。

交叉发布

我在Qt中心发布了同样的问题:

http://www.qtcentre.org/threads/63982-Best-way-to-refresh-view-for-cosmetic-%28non-model%29-changes

我在那里得到了一个答案,已纳入下面接受的答案。

相关帖子

SSCCE

import sys
from PySide import QtGui, QtCore

class MainTree(QtGui.QMainWindow):
    def __init__(self, parent = None):
        QtGui.QMainWindow.__init__(self)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.createRowHeightSpinbox() #create first otherwise get errors
        self.tree = SimpleTree(self)
        self.setCentralWidget(self.tree)       
        #Add spinbox to toolbar
        self.rowHeightAction = QtGui.QAction("Change row height", self)
        self.toolbar = self.addToolBar("rowHeight")
        self.toolbar.addWidget(QtGui.QLabel("Row height "))
        self.toolbar.addWidget(self.rowHeightSpinBox)  
        #Expand and resize tree
        self.tree.expandAll()
        self.tree.resizeColumnToContents(0) 
        self.tree.resizeColumnToContents(1) 

    def createRowHeightSpinbox(self):
        self.rowHeightSpinBox = QtGui.QSpinBox()
        self.rowHeightSpinBox.setRange(10, 50)
        self.rowHeightSpinBox.setValue(18)
        self.rowHeightSpinBox.valueChanged.connect(self.refreshView)  #showimage uses the spinbox attribute to scale image

    def refreshView(self):
        self.tree.model.layoutChanged.emit()


class SimpleTree(QtGui.QTreeView):
    def __init__(self, parent = None):    
        QtGui.QTreeView.__init__(self, parent)
        self.setUniformRowHeights(True) #optimize
        self.model = QtGui.QStandardItemModel()
        self.rootItem = self.model.invisibleRootItem()
        item0 = [QtGui.QStandardItem('Sneeze'), QtGui.QStandardItem('You have been blocked up')]
        item00 = [QtGui.QStandardItem('Tickle nose'), QtGui.QStandardItem('Key first step')]
        item1 = [QtGui.QStandardItem('Get a job'), QtGui.QStandardItem('Do not blow it')]
        self.rootItem.appendRow(item0)
        item0[0].appendRow(item00) 
        self.rootItem.appendRow(item1)
        self.setModel(self.model)
        self.setItemDelegate(ExpandableRows(self))


class ExpandableRows(QtGui.QStyledItemDelegate):
    def __init__(self, parent=None):
        QtGui.QStyledItemDelegate.__init__(self, parent)
        self.parent = parent

    def sizeHint(self, option, index):
        rowHeight = self.parent.window().rowHeightSpinBox.value()
        text = index.model().data(index)
        document = QtGui.QTextDocument()
        document.setDefaultFont(option.font)
        document.setPlainText(text)  #for html use setHtml
        return QtCore.QSize(document.idealWidth() + 5,  rowHeight) 


def main():
    app = QtGui.QApplication(sys.argv)
    #myTree = SimpleTree()
    myMainTree = MainTree()
    myMainTree.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

1 个答案:

答案 0 :(得分:2)

<强>解决方案

原则解决方案是在spinbox值更改时发出QAbstractItemDelegate.sizeHintChanged。这是因为您只想致电代理人sizeHint,而这正是此方法的作用。

在OP的示例中,大小提示旨在更改spinbox中的值。您可以将来自旋转框的valueChanged信号连接到代理的sizeHintChanged信号,如下所示:

class ExpandableRows(QtGui.QStyledItemDelegate):
    def __init__(self, parent=None):
        QtGui.QStyledItemDelegate.__init__(self, parent)
        self.parent = parent
        self.parent.window().rowHeightSpinBox.valueChanged.connect(self.emitSizeChange)

    def emitSizeChange(self):
        self.sizeHintChanged.emit(QtCore.QModelIndex())

分析

如OP中所示,您不想拨打dataChanged,因为它实际上并不工作,并且因为您的数据实际上没有变化。此外,在调用layoutChanged时,它的原则性较小,因为它在技术上意味着用于告诉视图模型的项目已经重新排列,而它们没有重新排列。

警告:我相信sizeHintChanged期望使用有效的索引,但我的解决方案是使用无效索引。因为它有效,所以我将它与无效QtCore.QModelIndex()一起留下。也许有人可以找到改进并编辑这个答案。

原则上比快速好吗?

注意当你以原则方式执行时,它实际上比使用layoutChanged技巧慢一点。特别是运行layoutChanged大约需要70微秒,而发送sizeHintChanged大约需要100微秒。这并不取决于我测试的模型的大小(最多1000行)。这种30微秒的差异非常小,在大多数应用程序中可以忽略不计,但如果有人真的想要完全优化速度,他们可能会采用layoutChanged技巧。

layoutChanged技巧还有一个简单的好处:它不涉及搞乱代理,但在主窗口类上使用直观简单的方法。另外,因为它并不依赖于委托中实现的任何方法,所以诀窍(可以说)似乎更加模块化。 &#34;适当&#34;方式取决于在委托和主窗口之间创建更脆弱的依赖关系,这意味着当开发人员修改一个或另一个时,它将更容易破坏应用程序。

总而言之,一个案例可以表明,在几乎每一种可衡量的方式中,来自OP的黑客攻击都优于&#34;有原则的&#34;解决方案。

<强>确认

我在QtCentre交叉发布的同一个问题的答案中转向了sizeHintChanged的存在:

http://www.qtcentre.org/threads/63982-Best-way-to-refresh-view-for-cosmetic-%28non-model%29-changes