Reputation: 13
I've looked at a number of online tutorials/videos trying to grok making QTreeView work. I understand the basic principals and get the examples to work. However, now I want to make a change to the normal data change mechanism.
Normally the examples show changing data via the GUI. Instead, I need to change the data via a separate running thread (i.e the TreeView will be read-only). In the application, the thread handles retrieving data on a periodic basis and I want to update the TreeView accordingly.
Here is some sample code of what I've tried so far. The TreeView should show the tree structure in the first column and a value for each tree item in the second. The data thread updates tree node values. What I want to see is the value incrementing on its own. Instead I only see the updated value when I mouse on/off the item. This tells me the thread is successful in updating the value, but it would appear that the TreeView isn't responding to the dataChanged signal.
I seem to be close, but I can't see what isn't right...thoughts?
import sys
import threading
import time
from PyQt4 import QtGui, QtCore
class Node(object):
def __init__(self, name, parent=None):
self._name = name
self._value = 0
self._children = []
self._parent = parent
if parent is not None:
parent.addChild(self)
def addChild(self, child):
self._children.append(child)
def name(self):
return self._name
def value(self):
return self._value
def setValue(self, value):
self._value = value
def child(self, row):
return self._children[row]
def childCount(self):
return len(self._children)
def children(self):
return self._children
def parent(self):
return self._parent
def row(self):
if self._parent is not None:
return self._parent._children.index(self)
class Model(QtCore.QAbstractItemModel):
def __init__(self, parent=None):
super(Model, self).__init__(parent)
self._root = None
self.setupData()
def setupData(self):
rootNode = Node('Root')
childNode0 = Node('A', rootNode)
childNode1 = Node('B', rootNode)
childNode2 = Node('1', childNode0)
childNode3 = Node('2', childNode0)
childNode4 = Node('1', childNode1)
childNode5 = Node('2', childNode1)
childNode6 = Node('a', childNode3)
childNode7 = Node('b', childNode3)
childNode8 = Node('c', childNode3)
childNode9 = Node('d', childNode4)
childNode10 = Node('e', childNode4)
childNode11 = Node('f', childNode4)
self._root = rootNode
def rowCount(self, parent=None):
if not parent.isValid():
parentNode = self._root
else:
parentNode = parent.internalPointer()
return parentNode.childCount()
def columnCount(self, parent=None):
return 2
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()
if index.column() == 1:
return node.value()
return None
def headerData(self, section, orientation, role):
if role == QtCore.Qt.DisplayRole:
if section == 0:
return 'Item'
if section == 1:
return 'Value'
return None
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._root
def parent(self, index):
node = self.getNode(index)
parentNode = node.parent()
if parentNode == self._root:
return QtCore.QModelIndex()
return self.createIndex(parentNode.row(), 0, parentNode)
def changeData(self, name, value):
# find the 'name' node and have setData make the change
index = self.searchModel(name)
self.setData(index, value)
def setData(self, index, value, role=QtCore.Qt.EditRole):
if index.isValid():
if role == QtCore.Qt.EditRole:
node = index.internalPointer()
node.setValue(value)
# signal the views that data has changed
self.dataChanged.emit(index, index)
return True
return False
def searchModel(self, name):
def searchNode(node):
for child in node.children():
if child.name().endswith(name):
index = self.createIndex(child.row(), 0, child)
return index
if child.childCount() > 0:
result = searchNode(child)
if result:
return result
return searchNode(self._root)
class DataThread(threading.Thread):
def __init__(self, model):
threading.Thread.__init__(self)
self.__model = model
self.__runflag = False
def run(self):
print 'Running'
value = 0
self.__runflag = True
while self.__runflag:
# sample, change a single value
self.__model.changeData('c', value)
value += 1
time.sleep(1)
print 'Terminated'
def stop(self):
print 'Stopping'
self.__runflag = False
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
model = Model()
view = QtGui.QTreeView()
view.setModel(model)
view.expandAll()
view.show()
data_thread = DataThread(model)
data_thread.start()
app.exec_()
data_thread.stop()
sys.exit()
Upvotes: 1
Views: 1123
Reputation: 5546
The model index that you pass to the dataChanged signal has a column value of 0. However the column that you're updating is 1.
In searchNode
, change the line that constructs the index in:
index = self.createIndex(child.row(), 1, child)
It should work then.
Upvotes: 1