Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/282.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/jsf-2/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 当模型中的数据更改时,如何刷新视图?_Python_Pyqt_Pyqt4_Qtreeview_Qabstractitemmodel - Fatal编程技术网

Python 当模型中的数据更改时,如何刷新视图?

Python 当模型中的数据更改时,如何刷新视图?,python,pyqt,pyqt4,qtreeview,qabstractitemmodel,Python,Pyqt,Pyqt4,Qtreeview,Qabstractitemmodel,我正在开发一个简单的树目录资源管理器,它基于一个带有模型视图/控制器/实现的qtreeview。我需要使用一些线程递归地搜索子文件夹,并为qtreeview的模型/数据提供信息。所有这些都很好。 但我的问题是,当数据更改时,视图不会刷新 我尝试了一些不同的方法,但我对任何一种解决方案都不满意: QtGui.QStandardItemModel.dataChanged.emit(QtCore.QModelIndex(), QtCore.QModelIndex()) 从模型的setData发出数据

我正在开发一个简单的树目录资源管理器,它基于一个带有模型视图/控制器/实现的qtreeview。我需要使用一些线程递归地搜索子文件夹,并为qtreeview的模型/数据提供信息。所有这些都很好。 但我的问题是,当数据更改时,视图不会刷新

我尝试了一些不同的方法,但我对任何一种解决方案都不满意:

QtGui.QStandardItemModel.dataChanged.emit(QtCore.QModelIndex(), QtCore.QModelIndex())
从模型的setData发出数据更改应该会更新视图,但它对我不起作用。而且我还没有找到一种优雅的方法来找到qmodelIndex。我只是反复循环所有数据以找到正确的索引

from PyQt4 import QtCore, QtGui
from PyQt4.QtGui import *
from PyQt4.QtCore import *

import time
import traceback, sys, os
from glob import glob
from random import randrange
import traceback

DEPTH = 0
threadpool = QThreadPool()

##########################################
###### Example thread function #####
##########################################
def listFolders( parent ):
    global DEPTH
    time.sleep(2)
    if DEPTH>4:
        return {'fileList':[], 'parent':parent}
    else:
        DEPTH+=1
    fileList = []
    for item in range(randrange(1,5)):
        fileList.append('item_'+str(item))

    return {'fileList':fileList, 'parent':parent}



##########################################
###### simple threading #####
##########################################
class WorkerSignals(QObject):
    finished = pyqtSignal()
    error = pyqtSignal(tuple)
    result = pyqtSignal(object)
    progress = pyqtSignal(int)

class Worker(QRunnable):
    def __init__(self, fn, *args, **kwargs):
        super(Worker, self).__init__()
        # Store constructor arguments (re-used for processing)
        self.fn = fn
        self.args = args
        self.kwargs = kwargs
        self.signals = WorkerSignals()

    @pyqtSlot()
    def run(self):
        try:
            result = self.fn(*self.args, **self.kwargs)
        except:
            traceback.print_exc()
            exctype, value = sys.exc_info()[:2]
            self.signals.error.emit((exctype, value, traceback.format_exc()))
        else:
            self.signals.result.emit(result)  # Return the result of the processing
        finally:
            self.signals.finished.emit()  # Done


##########################################
###### Model for qtreeview #####
##########################################
class SceneGraphModel(QtCore.QAbstractItemModel):
    def __init__(self, root ,parent=None):
        super(SceneGraphModel, self).__init__(parent)
        self._rootNode = root

    def rowCount(self, parent):
        if not parent.isValid():
            parentNode = self._rootNode
        else:
            parentNode = parent.internalPointer()
        return parentNode.childCount()


    def columnCount(self, parent):
        return 1

    def data(self, index, role):

        if not index.isValid():
            return None

        node = index.internalPointer()

        if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
            if index.column() == 0:

                return node.name()

    def setData(self, index, value, role=QtCore.Qt.EditRole):
        if index.isValid():
            if role == QtCore.Qt.EditRole:
                node = index.internalPointer()
                node.setName(value)
                return True
        return False

    def headerData(self, section, orientation, role):
        if role == QtCore.Qt.DisplayRole:
            if section == 0:
                return "Scenegraph"

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


    def parent(self, index):
        node = self.getNode(index)
        parentNode = node.parent()
        if parentNode == self._rootNode:
            return QtCore.QModelIndex()
        if parentNode == None:
            row = 0
        else:
            row = parentNode.row()
        return self.createIndex(row, 0, parentNode)

    def index(self, row, column, parent):
        parentNode = self.getNode(parent)
        childItem = parentNode.child(row)

        if childItem:
            return self.createIndex(row, column, childItem)
        else:
            return QtCore.QModelIndex()


    def getNode(self, index):
        if index.isValid():
            node = index.internalPointer()
            if node:
                return node

        return self._rootNode

##########################################
###### Node class that contain the qtreeview datas #####
##########################################
class Node(object):
    def __init__(self, name, parent=None):
        self._name = name
        self._children = []
        self._parent = parent
        if parent is not None:
            parent.addChild(self)

    def typeInfo(self):
        return "folder"

    def addChild(self, child):
        self._children.append(child)

    def name(self):
        return self._name

    def child(self, row):
        return self._children[row]

    def childCount(self):
        return len(self._children)

    def parent(self):
        return self._parent

    def row(self):
        if self._parent is not None:
            return self._parent._children.index(self)

    def __repr__(self):
        return 'NODE_'+self.name()


##########################################
###### qtreeview containing the threading #####
##########################################
class DirectoryTree(QTreeView):
    def __init__(self):
        super(DirectoryTree, self).__init__()

        #create root node
        self.rootNode   = Node('root')
        #add model to treeview
        self._model = SceneGraphModel(self.rootNode)
        self.setModel(self._model)
        #recurive loop with thread to add more datas
        self.loop( self.rootNode )


    def thread(self, path):
        return listFolders(path)

    def threadResult(self, result ):
        for item in result['fileList']:
            newNode = Node(item,result['parent'])
            self.loop(newNode)


    def loop(self, parent ):
        worker = Worker( self.thread, parent )
        worker.signals.result.connect( self.threadResult )
        threadpool.start(worker)



##########################################
###### window with countdown #####
##########################################

class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        self.counter = 0
        self.layout = QVBoxLayout()
        self.l = QLabel("Start")
        self.layout.addWidget(self.l)
        w = QWidget()
        w.setLayout(self.layout)
        self.setCentralWidget(w)

        self.treeView = DirectoryTree()
        self.layout.addWidget(self.treeView)

        self.show()

        self.timer = QTimer()
        self.timer.setInterval(1000)
        self.timer.timeout.connect(self.recurring_timer)
        self.timer.start()


        self.setGeometry(0, 0, 650, 550)
        self.setWindowTitle("shot tree")
        self.centerOnScreen()

    def centerOnScreen (self):
        resolution = QtGui.QDesktopWidget().screenGeometry()
        self.move((resolution.width() / 2) - (self.frameSize().width() / 2),
                  (resolution.height() / 2) - (self.frameSize().height() / 2)) 


    def recurring_timer(self):
        self.counter +=1
        self.l.setText("Counter: %d" % self.counter)

        ##### This is a hack to refresh the view
        ##### i want to remove this line 
        ##### and properly emit the changes from the node class to refresh the qtreeview
        self.treeView.expandAll()


app = QApplication([])
window = MainWindow()
app.exec_()
这是我的代码示例。主窗口中会执行一个倒计时:self.treeView.expandAll 每秒钟都要强制更新视图,我想找到更好的解决方案

我发现的相关话题:


这个问题与线程无关。对于要通知的视图,模型必须在更改之前发出layoutAboutToBeChanged信号,在更改之后发出layoutChanged信号,但为此,节点必须访问模型,因此必须将模型作为节点属性。通过该更改,您不再需要QTimer来更新视图

class SceneGraphModel(QtCore.QAbstractItemModel):
    def __init__(self, root, parent=None):
        super(SceneGraphModel, self).__init__(parent)
        self._rootNode = root
        self._rootNode._model = self

    def rowCount(self, parent=QtCore.QModelIndex()):
        if not parent.isValid():
            parentNode = self._rootNode
        else:
            parentNode = parent.internalPointer()
        return parentNode.childCount()

    def columnCount(self, parent=QtCore.QModelIndex()):
        return 1

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if not index.isValid():
            return None

        node = index.internalPointer()

        if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
            if index.column() == 0:
                return node.name()

    def setData(self, index, value, role=QtCore.Qt.EditRole):
        if index.isValid():
            if role == QtCore.Qt.EditRole:
                node = index.internalPointer()
                node.setName(value)
                return True
        return False

    def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
        if role == QtCore.Qt.DisplayRole:
            if section == 0:
                return "Scenegraph"

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

    def parent(self, index):
        node = self.getNode(index)
        parentNode = node.parent()
        if parentNode == self._rootNode:
            return QtCore.QModelIndex()
        if parentNode is None:
            row = 0
        else:
            row = parentNode.row()
        return self.createIndex(row, 0, parentNode)

    def index(self, row, column, parent=QtCore.QModelIndex()):
        parentNode = self.getNode(parent)
        childItem = parentNode.child(row)
        if childItem:
            return self.createIndex(row, column, childItem)
        else:
            return QtCore.QModelIndex()

    def getNode(self, index):
        if index.isValid():
            node = index.internalPointer()
            if node:
                return node
            print("node", node)
        return self._rootNode


class Node(object):
    def __init__(self, name, parent=None):
        self._name = name
        self._children = []
        self._parent = parent
        self._model = None
        if parent is not None:
            parent.addChild(self)

    def typeInfo(self):
        return "folder"

    def addChild(self, child):
        self._model.layoutAboutToBeChanged.emit()
        self._children.append(child)
        child._model = self._model
        self._model.layoutChanged.emit()

    def name(self):
        return self._name

    def setName(self, name):
        self._name = name

    def child(self, row):
        return self._children[row] if row < len(self._children) else None

    def childCount(self):
        return len(self._children)

    def parent(self):
        return self._parent

    def row(self):
        return 0 if self.parent() is None else self._parent._children.index(self)

    def __repr__(self):
        return 'NODE_' + self.name()

非常感谢。这正是我所缺少的。我不知道如何使用LayoutAboutBechange和layoutChanged。现在一切都有意义了@nicosalto模型是一个独立于视图的元素,例如一个模型可以在多个视图中,因此我问您应该通知谁:模型到视图,还是视图到模型?模型应该通知视图;我实际上是在学习这个很酷的youtube教程@在观看这些视频之前,我建议您阅读官方文档:,另一方面,如果您是初学者,我建议您不要组合主题:模型和线程对于初学者来说是一个糟糕的组合,因为您总是会责怪线程。