The previous question was PySide2 QListView QTableView sync problem
Imagine to have another dict4 in the data structure:
'dict4':{'k8':'v8', 'EXISTING_DICT':'dict2'},
Meaning, that a dictionary could include another existing dictionary.
So the representation in the QTableView shouldn't be directly showed, but:
1) Show just the name of it in the QTableView:
k1 | v1
-------
k2 | v2
-------
k3 | v3
-------
dict2
2) If double click it on it: select the existing dictionary in the QListView, which will trigger to show its contents in the QTableView, which in this case:
k4 | v4
In this case the idea is to use a role to indicate that the field points to another item, then use setSpan to join items and finally check if the item selected in the QTableView points to another element by selecting if it is so
from PySide2 import QtCore, QtGui, QtWidgets
dict_of_dicts={
'dict1':{'k1':'v1', 'k2':'v2', 'k3':'v3', 'EXISTING_DICT': 'dict3'},
'dict2':{'k4':'v4', 'EXISTING_DICT': 'dict1'},
'dict3':{'k5':'v5', 'k6':'v6', 'k7':'v7', 'EXISTING_DICT': 'dict4'},
'dict4':{'k8':'v8', 'EXISTING_DICT':'dict2'},
}
def create_model_from_dict(d, parent=None):
model = QtGui.QStandardItemModel(0, 2, parent)
for k, v in dict_of_dicts.items():
it = QtGui.QStandardItem(k)
model.appendRow(it)
for k_, v_ in v.items():
if k_ != "EXISTING_DICT":
it.appendRow([QtGui.QStandardItem(k_), QtGui.QStandardItem(v_)])
else:
child_it = QtGui.QStandardItem(v_)
child_it.setTextAlignment(QtCore.Qt.AlignCenter)
child_it.setData(True, QtCore.Qt.UserRole + 1000)
it.appendRow(child_it)
return model
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
model = create_model_from_dict(dict_of_dicts, self)
self.tableview = QtWidgets.QTableView()
self.tableview.setModel(model)
self.tableview.selectionModel().selectionChanged.connect(self.handleSelectionChangedTV)
self.listview = QtWidgets.QListView()
self.listview.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
self.listview.setModel(model)
self.listview.selectionModel().selectionChanged.connect(self.handleSelectionChangedLV)
self.listview.selectionModel().select(model.index(0, 0), QtCore.QItemSelectionModel.Select)
hlay = QtWidgets.QHBoxLayout(self)
hlay.addWidget(self.listview)
hlay.addWidget(self.tableview)
@QtCore.Slot(QtCore.QItemSelection)
def handleSelectionChangedLV(self, item):
ixs = item.indexes()
if ixs:
self.tableview.setRootIndex(ixs[0])
model = self.tableview.model()
self.tableview.clearSpans()
for r in range(model.rowCount(self.tableview.rootIndex())):
index = model.index(r, 0, self.tableview.rootIndex())
if index.data(QtCore.Qt.UserRole + 1000):
self.tableview.setSpan(r, 0, 1, 2)
@QtCore.Slot(QtCore.QItemSelection)
def handleSelectionChangedTV(self, item):
ixs = item.indexes()
if ixs:
ix = ixs[0]
if ix.data(QtCore.Qt.UserRole + 1000):
items = self.listview.model().findItems(ix.data())
if items:
self.tableview.clearSelection()
self.listview.selectionModel().select(items[0].index(), QtCore.QItemSelectionModel.ClearAndSelect)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
UPDATE:
from PySide2 import QtCore, QtGui, QtWidgets
dict_of_dicts={
'dict1':{'k1':'v1', 'k2':'v2', 'k3':'v3', 'EXISTING_DICT': 'dict3'},
'dict2':{'k4':'v4', 'EXISTING_DICT': 'dict1'},
'dict3':{'k5':'v5', 'k6':'v6', 'k7':'v7', 'EXISTING_DICT': 'dict4'},
'dict4':{'k8':'v8', 'EXISTING_DICT':'dict2'},
}
def create_model_from_dict(d, parent=None):
model = QtGui.QStandardItemModel(0, 2, parent)
for k, v in dict_of_dicts.items():
it = QtGui.QStandardItem(k)
model.appendRow(it)
for k_, v_ in v.items():
if k_ != "EXISTING_DICT":
it.appendRow([QtGui.QStandardItem(k_), QtGui.QStandardItem(v_)])
else:
child_it = QtGui.QStandardItem(v_)
child_it.setTextAlignment(QtCore.Qt.AlignCenter)
child_it.setData(True, QtCore.Qt.UserRole + 1000)
it.appendRow(child_it)
return model
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
model = create_model_from_dict(dict_of_dicts, self)
self.tableview = QtWidgets.QTableView()
self.tableview.setModel(model)
self.tableview.doubleClicked.connect(self.handleSelectionChangedTV)
self.listview = QtWidgets.QListView()
self.listview.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
self.listview.setModel(model)
self.listview.selectionModel().selectionChanged.connect(self.handleSelectionChangedLV)
self.listview.selectionModel().select(model.index(0, 0), QtCore.QItemSelectionModel.Select)
hlay = QtWidgets.QHBoxLayout(self)
hlay.addWidget(self.listview)
hlay.addWidget(self.tableview)
@QtCore.Slot(QtCore.QItemSelection)
def handleSelectionChangedLV(self, item):
ixs = item.indexes()
if ixs:
self.tableview.setRootIndex(ixs[0])
model = self.tableview.model()
self.tableview.clearSpans()
for r in range(model.rowCount(self.tableview.rootIndex())):
index = model.index(r, 0, self.tableview.rootIndex())
if index.data(QtCore.Qt.UserRole + 1000):
self.tableview.setSpan(r, 0, 1, 2)
@QtCore.Slot(QtCore.QModelIndex)
def handleSelectionChangedTV(self, ix):
if ix.data(QtCore.Qt.UserRole + 1000):
items = self.listview.model().findItems(ix.data())
if items:
self.tableview.clearSelection()
self.listview.selectionModel().select(items[0].index(), QtCore.QItemSelectionModel.ClearAndSelect)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
UPDATE2
from PySide2 import QtCore, QtGui, QtWidgets
dict_of_dicts={
'dict1':{'k1':'v1', 'k2':'v2', 'k3':'v3', 'EXISTING_DICT': 'dict3'},
'dict2':{'k4':'v4', 'EXISTING_DICT': 'dict1'},
'dict3':{'k5':'v5', 'k6':'v6', 'k7':'v7', 'EXISTING_DICT': 'dict4'},
'dict4':{'k8':'v8', 'EXISTING_DICT':'dict2'},
}
def create_model_from_dict(d, parent=None):
model = QtGui.QStandardItemModel(0, 2, parent)
for k, v in dict_of_dicts.items():
it = QtGui.QStandardItem(k)
model.appendRow(it)
for k_, v_ in v.items():
if k_ != "EXISTING_DICT":
it.appendRow([QtGui.QStandardItem(k_), QtGui.QStandardItem(v_)])
else:
child_it = QtGui.QStandardItem(v_)
child_it.setTextAlignment(QtCore.Qt.AlignCenter)
child_it.setData(True, QtCore.Qt.UserRole + 1000)
it.appendRow(child_it)
return model
class TableView(QtWidgets.QTableView):
leftDoubleClicked = QtCore.Signal(QtCore.QModelIndex)
def mouseDoubleClickEvent(self, event):
super(TableView, self).mouseDoubleClickEvent(event)
if event.buttons() & QtCore.Qt.LeftButton:
ix = self.indexAt(event.pos())
if ix.isValid(): self.leftDoubleClicked.emit(ix)
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
model = create_model_from_dict(dict_of_dicts, self)
self.tableview = TableView()
self.tableview.setModel(model)
self.tableview.leftDoubleClicked.connect(self.handleSelectionChangedTV)
self.listview = QtWidgets.QListView()
self.listview.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
self.listview.setModel(model)
self.listview.selectionModel().selectionChanged.connect(self.handleSelectionChangedLV)
self.listview.selectionModel().select(model.index(0, 0), QtCore.QItemSelectionModel.Select)
hlay = QtWidgets.QHBoxLayout(self)
hlay.addWidget(self.listview)
hlay.addWidget(self.tableview)
@QtCore.Slot(QtCore.QItemSelection)
def handleSelectionChangedLV(self, item):
ixs = item.indexes()
if ixs:
self.tableview.setRootIndex(ixs[0])
model = self.tableview.model()
self.tableview.clearSpans()
for r in range(model.rowCount(self.tableview.rootIndex())):
index = model.index(r, 0, self.tableview.rootIndex())
if index.data(QtCore.Qt.UserRole + 1000):
self.tableview.setSpan(r, 0, 1, 2)
@QtCore.Slot(QtCore.QModelIndex)
def handleSelectionChangedTV(self, ix):
if ix.data(QtCore.Qt.UserRole + 1000):
items = self.listview.model().findItems(ix.data())
if items:
self.tableview.clearSelection()
self.listview.selectionModel().select(items[0].index(), QtCore.QItemSelectionModel.ClearAndSelect)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())