使用QTreeView编辑字典
问题描述:
我有一个简化的代码示例,其中一个窗口仅用一个按钮创建。使用QTreeView编辑字典
按下它会弹出一个对话框的Qt其中TestDialog
作为需要参数Python字典。该字典显示在对话框内的可编辑QTreeView
中。
改变对好或您可以点击一些值后取消接受或放弃更改。一旦对话框关闭,我的意图是从主窗口中检索修改后的字典dialog.get_data()
,它现在只返回原始未修改的字典。点击Ok按钮后,检索到的字典将打印到标准输出。
我的问题是,如何修改树视图时修改字典?有没有办法自动附加一个函数来执行每个项目的修改?因此,在编辑时,例如,树视图中的float
,那么相应的值将更新为字典中的浮点数?
字典没有固定的大小,它的类型可能会改变。类型列表是有限的并且已知,并且对于该示例可以被缩减为{int
,str
,float
,Other
}。同样可以假定父母不应该是可编辑的,孩子只能在第二列进行编辑,就像下面的例子所示。
这里是我的代码有:
import sys
from PyQt4 import QtGui, QtCore, uic
from copy import deepcopy
class TestDialog(QtGui.QDialog):
def __init__(self, data):
super(TestDialog, self).__init__()
self.data = deepcopy(data)
# Layout
btOk = QtGui.QPushButton("OK")
btCancel = QtGui.QPushButton("Cancel")
self.tree = QtGui.QTreeView()
hbox = QtGui.QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(btOk)
hbox.addWidget(btCancel)
vbox = QtGui.QVBoxLayout()
vbox.addLayout(hbox)
vbox.addWidget(self.tree)
self.setLayout(vbox)
self.setGeometry(300, 300, 600, 400)
# Button signals
btCancel.clicked.connect(self.reject)
btOk.clicked.connect(self.accept)
# Tree view
self.tree.setModel(QtGui.QStandardItemModel())
self.tree.setAlternatingRowColors(True)
self.tree.setSortingEnabled(True)
self.tree.setHeaderHidden(False)
self.tree.setSelectionBehavior(QtGui.QAbstractItemView.SelectItems)
self.tree.model().setHorizontalHeaderLabels(['Parameter', 'Value'])
for x in self.data:
if not self.data[x]:
continue
parent = QtGui.QStandardItem(x)
parent.setFlags(QtCore.Qt.NoItemFlags)
for y in self.data[x]:
value = self.data[x][y]
child0 = QtGui.QStandardItem(y)
child0.setFlags(QtCore.Qt.NoItemFlags |
QtCore.Qt.ItemIsEnabled)
child1 = QtGui.QStandardItem(str(value))
child1.setFlags(QtCore.Qt.ItemIsEnabled |
QtCore.Qt.ItemIsEditable |
~ QtCore.Qt.ItemIsSelectable)
parent.appendRow([child0, child1])
self.tree.model().appendRow(parent)
self.tree.expandAll()
def get_data(self):
return self.data
class Other(object):
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return '(%s, %s)' % (self.x, self.y)
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
btn = QtGui.QPushButton('Button', self)
btn.resize(btn.sizeHint())
btn.clicked.connect(self.show_dialog)
self.data = {}
# This example will be hidden (has no parameter-value pair)
self.data['example0'] = {}
# A set example with an integer and a string parameters
self.data['example1'] = {}
self.data['example1']['int'] = 14
self.data['example1']['str'] = 'asdf'
# A set example with a float and other non-conventional type
self.data['example2'] = {}
self.data['example2']['float'] = 1.2
self.data['example2']['other'] = Other(4, 8)
def show_dialog(self):
dialog = TestDialog(self.data)
accepted = dialog.exec_()
if not accepted:
return
self.data = deepcopy(dialog.get_data())
print self.data
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec_())
答
您可以连接到模型的itemChanged信号:
self.tree.model().itemChanged.connect(self.handleItemChanged)
处理程序会是这个样子:
def handleItemChanged(self, item):
parent = self.data[item.parent().text()]
key = item.parent().child(item.row(), 0).text()
parent[key] = type(parent[key])(item.text())
注使用type
进行值的转换不一定会出现k用于定制类,如Other
。因此,您必须确保此类的构造函数可以转换字符串表示形式,或者在将字符串传递给构造函数之前将其解析为合适的参数。
另外请注意,我没有打扰在上面的示例代码中处理QString
值。如果你使用Python 3,这不是问题,因为它们默认会自动转换为Python字符串。在这个
import sip
sip.setapi('QString', 2)
from PyQt4 import QtGui, QtCore, uic
有关详细信息,请参阅Selecting Incompatible APIs在PyQt4 Docs:但对于Python 2里,你可以通过执行以下操作切换此行为。