PySide (PyQt) QAbstractItemModel - qt

I am trying to implement a simple model for a treeview and I can't figure out why it isn't working.. It seems that all my nodes are children of my root node although three of them should be children of it's first child. Also I can see that my hasChildren method gets in an infinite loop.. Any help would be greatly appreciated.
here is my code:
#!/usr/bin/env python2
from PySide import QtGui, QtCore
import sys
class SI(object):
children = []
def __init__(self, group=None, parent=None):
self.parent = parent
self.group = group
def data(self, column):
return self.group[column]
def appendChild(self, group):
self.children.append(SI(group, self))
def child(self, row):
return self.children[row]
def childrenCount(self):
return len(self.children)
def hasChildren(self):
if len(self.children) > 0 :
return True
return False
def row(self):
if self.parent:
return self.parent.children.index(self)
return 0
def columnCount(self):
return len(self.group)
class SM(QtCore.QAbstractItemModel):
root = SI(["First", "Second"])
def __init__(self, parent=None):
super(SM, self).__init__(parent)
self.createData()
def createData(self):
for x in [["a", "A"], ["b","B"], ["c", "C"]]:
self.root.appendChild(x)
for y in [["aa", "AA"], ["ab", "AB"], ["ac","AC"]]:
self.root.child(0).appendChild(y)
def columnCount(self, index=QtCore.QModelIndex()):
if index.isValid():
return index.internalPointer().columnCount()
else:
return self.root.columnCount()
def rowCount(self, index=QtCore.QModelIndex()):
if index.row() > 0:
return 0
if index.isValid():
item = index.internalPointer()
else:
item = self.root
return item.childrenCount()
def index(self, row, column, index=QtCore.QModelIndex()):
if not self.hasIndex(row, column, index):
return QtCore.QModelIndex()
if not index.isValid():
item = self.root
else:
item = index.internalPointer()
child = item.child(row)
if child:
return self.createIndex(row, column, child)
return QtCore.QMOdelIndex()
def parent(self, index):
if not index.isValid():
return QtCore.QModelIndex()
item = index.internalPointer()
if not item:
return QtCore.QModelIndex()
parent = item.parent
if parent == self.root:
return QtCore.QModelIndex()
else:
return self.createIndex(parent.row(), 0, parent)
def hasChildren(self, index):
if not index.isValid():
item = self.root
else:
item = index.internalPointer()
return item.hasChildren()
def data(self, index, role=QtCore.Qt.DisplayRole):
if index.isValid() and role == QtCore.Qt.DisplayRole:
return index.internalPointer().data(index.column())
elif not index.isValid():
return self.root.getData()
def headerData(self, section, orientation, role):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return self.root.data(section)
class MyTree(QtGui.QTreeView):
def __init__(self, parent=None, model=SM):
super(MyTree, self).__init__(parent)
self.setModel(model())
class Window(QtGui.QWidget):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.initGui()
def initGui(self):
vlo = QtGui.QVBoxLayout()
tree = MyTree(self)
vlo.addWidget(tree)
self.setLayout(vlo)
self.show()
def main():
app = QtGui.QApplication(sys.argv)
win = Window()
exit(app.exec_())
if __name__ == '__main__':
main()

Hmm I think you have almost got it apart from one (strange!) mistake. You declare children as a class level variable in SI whereas it should be an instance variable since each SI can have children. You don't want a single list for all instances.
So I changed the first lines to:
class SI(object):
def __init__(self, group=None, parent=None):
self.parent = parent
self.group = group
self.children = []
def data(self, column):
return self.group[column]
and it all appears to work OK. Of course I haven't tested in detail but that should get you going.

There's a mistake in implementation of:
def rowCount(self, index=QtCore.QModelIndex()):
Instead of:
if index.row() > 0:
there should be:
if index.column() > 0:
Source:
http://doc.qt.io/qt-5/qtwidgets-itemviews-simpletreemodel-example.html

Related

QAbstractItemModel checkboxes are not checkable

Why are the checkboxes in my TreeView not checkable?
I think there is an issue with the index() and parent() methods, but do not know how to fix it.
setData() also never called...
Any help is really appreciated...
import logging
import sys
from PySide6 import QtCore, QtWidgets
from PySide6.QtCore import QSortFilterProxyModel, QPersistentModelIndex
class DBObject:
def __init__(self, name, parent, children=None, is_checkable=False):
self.name = name
self.parent = parent
self.children = children or list()
self.is_checkable = is_checkable
def __repr__(self):
return f"name: {self.name}, parent: {self.parent.name if self.parent is not None else '-'}"
class Model(QtCore.QAbstractItemModel):
def __init__(self, parent=None):
super().__init__(parent)
self._root = DBObject("root", None)
self.newData()
self.checks = {}
def checkState(self, index):
if index in self.checks.keys():
return self.checks[index]
else:
return QtCore.Qt.CheckState.Unchecked
def newData(self):
items = ["foo", "bar", "baz"]
for x in items:
child = DBObject(x + "0", self._root)
self._root.children.append(child)
for y in items:
child.children.append(DBObject(y + "1", child, None, True))
def columnCount(self, parent=QtCore.QModelIndex()):
return 1
def rowCount(self, parent=QtCore.QModelIndex()):
if not parent.isValid():
return 1
parentItem = parent.internalPointer()
rowCount = len(parentItem.children)
logging.info(f"rowCount({parentItem}): rowCount={rowCount}")
return rowCount
def parent(self, index):
if not index.isValid():
return QtCore.QModelIndex()
item = index.internalPointer()
parentItem = item.parent
logging.info(f"parent({item}): parent={parentItem}")
if parentItem is None:
return QtCore.QModelIndex()
else:
if parentItem.parent is None:
return self.createIndex(0, 0, parentItem)
else:
return self.createIndex(parentItem.parent.children.index(parentItem), 0, parentItem)
def index(self, row, column, parent=QtCore.QModelIndex()):
if not parent.isValid():
if row != 0 or column != 0:
return QtCore.QModelIndex()
else:
logging.info(f"index({row}, {column}, None): index={self._root}")
return self.createIndex(0, 0, self._root)
parentItem = parent.internalPointer()
if 0 <= row < len(parentItem.children):
logging.info(f"index({row}, {column}, {parentItem}): index={parentItem.children[row]}")
return self.createIndex(row, column, parentItem.children[row])
else:
logging.info(f"index({row}, {column}, {parentItem}): index=None")
return QtCore.QModelIndex()
def data(self, index, role=QtCore.Qt.ItemDataRole.DisplayRole):
row = index.row()
col = index.column()
if not index.isValid():
return None
item = index.internalPointer()
if role == QtCore.Qt.ItemDataRole.CheckStateRole and col == 0 and item.is_checkable:
return QtCore.Qt.CheckState.Checked
if role == QtCore.Qt.ItemDataRole.DisplayRole:
return item.name
else:
return None
def setData(self, index, value, role=QtCore.Qt.ItemDataRole.EditRole):
if not index.isValid():
return False
if role == QtCore.Qt.ItemDataRole.CheckStateRole:
self.checks[QPersistentModelIndex(index)] = value
return True
return False
class ProxyModel(QSortFilterProxyModel):
def __init__(self, parent=None):
super().__init__(parent)
self.setFilterKeyColumn(0)
self.setRecursiveFilteringEnabled(True)
def flags(self, index):
if not index.isValid():
return QtCore.Qt.ItemFlag.NoItemFlags
return (
QtCore.Qt.ItemFlag.ItemIsEnabled
| QtCore.Qt.ItemFlag.ItemIsSelectable)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setMinimumSize(640, 480)
centralWidget = QtWidgets.QWidget(self)
self.setCentralWidget(centralWidget)
layout = QtWidgets.QVBoxLayout(centralWidget)
self._treeView = QtWidgets.QTreeView(self)
layout.addWidget(self._treeView)
self._model = Model()
self._proxyModel = ProxyModel()
self._proxyModel.setSourceModel(self._model)
self._treeView.setModel(self._proxyModel)
# self._proxyModel.setFilterFixedString("bar1")
button = QtWidgets.QPushButton("Add")
layout.addWidget(button)
button.clicked.connect(self._Clicked)
def _Clicked(self):
self._model.newData()
self._treeView.expandAll()
def main():
app = QtWidgets.QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
app.exec()
if __name__ == "__main__":
main()
There are various issues with your code:
the most important one is that you're not returning the ItemIsUserCheckable flag;
you're overriding the flag() in the proxy model (uselessly, by the way, since you're just returning the default flags of QAbstractItemModel);
data() always returns Checked no matter its value;
setData() should always emit dataChanged (as the documentation points out);
it's not really clear why you return a QModelIndex() if the top level item has row or column greater than 0;
Change the flag() behavior of the source model, remove that of the proxy (or implement it properly, using the default implementation), and correct both data() and setData().
class Model(QtCore.QAbstractItemModel):
def __init__(self, parent=None):
super().__init__(parent)
self._root = DBObject("root", None)
self.newData()
self.checks = {}
def newData(self):
items = ["foo", "bar", "baz"]
for x in items:
child = DBObject(x + "0", self._root)
self._root.children.append(child)
for y in items:
child.children.append(DBObject(y + "1", child, None, True))
def columnCount(self, parent=QtCore.QModelIndex()):
return 1
def flags(self, index):
flags = super().flags(index)
item = index.internalPointer()
if item and item.is_checkable:
flags |= QtCore.Qt.ItemIsUserCheckable
return flags
def rowCount(self, parent=QtCore.QModelIndex()):
if not parent.isValid():
return 1
return len(parent.internalPointer().children)
def parent(self, index):
if not index.isValid():
return QtCore.QModelIndex()
item = index.internalPointer()
parentItem = item.parent
if parentItem is None:
return QtCore.QModelIndex()
if parentItem.parent is None:
return self.createIndex(0, 0, parentItem)
else:
return self.createIndex(
parentItem.parent.children.index(parentItem), 0, parentItem)
def index(self, row, column, parent=QtCore.QModelIndex()):
if not parent.isValid():
if row != 0 or column != 0:
return QtCore.QModelIndex()
else:
return self.createIndex(0, 0, self._root)
parentItem = parent.internalPointer()
if 0 <= row < len(parentItem.children):
return self.createIndex(row, column, parentItem.children[row])
return QtCore.QModelIndex()
def data(self, index, role=QtCore.Qt.DisplayRole):
if not index.isValid():
return None
item = index.internalPointer()
col = index.column()
if role == QtCore.Qt.CheckStateRole and col == 0 and item.is_checkable:
return self.checks.get(QPersistentModelIndex(index), QtCore.Qt.Unchecked)
if role == QtCore.Qt.DisplayRole:
return item.name
def setData(self, index, value, role=QtCore.Qt.EditRole):
if not index.isValid():
return False
if role == QtCore.Qt.CheckStateRole:
pIndex = QPersistentModelIndex(index)
if self.checks.get(pIndex) != value:
self.checks[pIndex] = value
self.dataChanged.emit(index, index)
return True
return False
Also:
consider that models must be as fast as possible, so you should avoid unnecessary variable definitions (unless they really help coding and readability) and improve code logic;
avoid unnecessary else: blocks that are implicit in the function flow (especially if used to just return None at the end of a function);

Qt for Python: Qtreeview from a Dictionary

Using Qt's 'Simple Tree Model Example' as a basis the following builds most of the QTreeView from the given dictionary.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
from PySide2.QtWidgets import QApplication, QWidget, QGroupBox, QHBoxLayout, QVBoxLayout, QTreeView
from PySide2.QtCore import Qt, QAbstractItemModel, QModelIndex
class GUIWindow(QWidget):
def __init__(self, parent=None):
super(GUIWindow, self).__init__(parent)
""" Setup UI """
self.setWindowTitle("QTreeView from Dictionary")
groupbox_model = QGroupBox('TreeView') # Create a Group Box for the Model
hbox_model = QHBoxLayout() # Create a Horizontal layout for the Model
vbox = QVBoxLayout() # Create a Vertical layout for the Model Horizontal layout
tree_view = QTreeView() # Instantiate the View
headers = ["Dictionary"]
tree = {'Root': {"Level_1": {"Item_1": 1.10, "Item_2": 1.20, "Item_3": 1.30},
"Level_2": {"SubLevel_1":
{"SubLevel_1_item1": 2.11, "SubLevel_1_Item2": 2.12, "SubLevel_1_Item3": 2.13},
"SubLevel_2":
{"SubLevel_2_Item1": 2.21, "SubLevel_2_Item2": 2.22, "SubLevel_2_Item3": 2.23}},
"Level_3": {"Item_1": 3.10, "Item_2": 3.20, "Item_3": 3.30}}}
# Set the models
model = TreeModel(headers, tree)
tree_view.setModel(model)
hbox_model.addWidget(tree_view) # Add the Widget to the Model Horizontal layout
groupbox_model.setLayout(hbox_model) # Add the hbox_model to layout of group box
vbox.addWidget(groupbox_model) # Add groupbox elements to vbox
self.setLayout(vbox)
class TreeModel(QAbstractItemModel):
def __init__(self, header, data, parent=None):
super(TreeModel, self).__init__(parent)
""" subclassing the standard interface item models must use and
implementing index(), parent(), rowCount(), columnCount(), and data()."""
rootData = header
self.rootItem = TreeNode(rootData)
level = 0
self.createData(data, level)
def createData(self, data, level):
parents = [self.rootItem]
sublevel = 2
if type(data) == dict:
level += 1
for key, values in data.items():
if level > sublevel:
parents.append(parents[-1].child(parents[-1].childCount() - 1))
if level > sublevel + 1:
sublevel += 1
if level > sublevel:
parents.append(parents[-1].child(parents[-1].childCount() - 1))
# Append a new item to the current parent's list of children
parent_item = parents[-1]
parent_item.insertChildren(parent_item.childCount())
parent_item.child(parent_item.childCount() - 1).setData([key])
self.createData(values, level) # Recursion to iterate through nested dict
sublevel += 1
def index(self, row, column, index=QModelIndex()):
""" Returns the index of the item in the model specified by the given row, column and parent index """
if not self.hasIndex(row, column, index):
return QModelIndex()
if not index.isValid():
item = self.rootItem
else:
item = index.internalPointer()
child = item.child(row)
if child:
return self.createIndex(row, column, child)
return QModelIndex()
def parent(self, index):
""" Returns the parent of the model item with the given index
If the item has no parent, an invalid QModelIndex is returned """
if not index.isValid():
return QModelIndex()
item = index.internalPointer()
if not item:
return QModelIndex()
parent = item.parentItem
if parent == self.rootItem:
return QModelIndex()
else:
return self.createIndex(parent.childNumber(), 0, parent)
def rowCount(self, index=QModelIndex()):
""" Returns the number of rows under the given parent
When the parent is valid it means that rowCount is returning the number of children of parent """
parent = self.getItem(index)
return parent.childCount()
def columnCount(self, index=QModelIndex()):
""" Returns the number of columns for the children of the given parent """
if index.isValid():
return index.internalPointer().columnCount()
else:
return self.rootItem.columnCount()
def data(self, index, role=Qt.DisplayRole):
""" Returns the data stored under the given role for the item referred to by the index """
if index.isValid() and role == Qt.DisplayRole:
return index.internalPointer().data(index.column())
elif not index.isValid():
return self.rootItem.getData()
def getItem(self, index):
""" Retrieves the tree node with a given index """
if index.isValid():
item = index.internalPointer()
if item:
return item
return self.rootItem
def headerData(self, section, orientation, role):
""" Returns the data for the given role and section in the header with the specified orientation """
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self.rootItem.data(section)
class TreeNode(object):
def __init__(self, data, parent=None):
self.parentItem = parent
self.itemData = data
self.children = []
def child(self, row):
return self.children[row]
def childCount(self):
return len(self.children)
def childNumber(self):
if self.parentItem:
return self.parentItem.children.index(self)
return 0
def columnCount(self):
return 1
def data(self, column):
return self.itemData[column]
def insertChildren(self, position):
item = TreeNode([1], self)
self.children.insert(position, item)
return True
def parent(self):
return self.parentItem
def setData(self, value):
self.itemData[0] = value
return True
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyle("plastique") # ("cleanlooks")
form = GUIWindow()
form.show()
sys.exit(app.exec_())
Is the above a good approach?
And what code (required/changes) would be necessary to:
Get 'Root' as the top level?
Show the 'values' e.g. 1.10 for key 'Item_1' as the child of 'Item_1'?
Appreciate suggestions.
Solved - If interested the full code that resolves the 2 questions is shown below.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
from PySide2.QtWidgets import QApplication, QWidget, QGroupBox, QHBoxLayout, QVBoxLayout, QTreeView
from PySide2.QtCore import Qt, QAbstractItemModel, QModelIndex
class GUIWindow(QWidget):
def __init__(self, parent=None):
super(GUIWindow, self).__init__(parent)
""" Setup UI """
self.setWindowTitle("QTreeView from Dictionary")
groupbox_model = QGroupBox('TreeView') # Create a Group Box for the Model
hbox_model = QHBoxLayout() # Create a Horizontal layout for the Model
vbox = QVBoxLayout() # Create a Vertical layout for the Model Horizontal layout
tree_view = QTreeView() # Instantiate the View
headers = ["Dictionary Keys", "Dictionary Values"]
tree = {'Root': {"Level_1": {"Item_1": 1.10, "Item_2": 1.20, "Item_3": 1.30},
"Level_2": {"SubLevel_1":
{"SubLevel_1_item1": 2.11, "SubLevel_1_Item2": 2.12, "SubLevel_1_Item3": 2.13},
"SubLevel_2":
{"SubLevel_2_Item1": 2.21, "SubLevel_2_Item2": 2.22,
"SubLevel_2_Item3": 2.23}},
"Level_3": {"Item_1": 3.10, "Item_2": 3.20, "Item_3": 3.30}}}
# Set the models
model = TreeModel(headers, tree)
tree_view.setModel(model)
tree_view.expandAll()
tree_view.resizeColumnToContents(0)
hbox_model.addWidget(tree_view) # Add the Widget to the Model Horizontal layout
groupbox_model.setLayout(hbox_model) # Add the hbox_model to layout of group box
vbox.addWidget(groupbox_model) # Add groupbox elements to vbox
self.setLayout(vbox)
class TreeModel(QAbstractItemModel):
def __init__(self, headers, data, parent=None):
super(TreeModel, self).__init__(parent)
""" subclassing the standard interface item models must use and
implementing index(), parent(), rowCount(), columnCount(), and data()."""
rootData = [header for header in headers]
self.rootItem = TreeNode(rootData)
indent = -1
self.parents = [self.rootItem]
self.indentations = [0]
self.createData(data, indent)
def createData(self, data, indent):
if type(data) == dict:
indent += 1
position = 4 * indent
for dict_keys, dict_values in data.items():
if position > self.indentations[-1]:
if self.parents[-1].childCount() > 0:
self.parents.append(self.parents[-1].child(self.parents[-1].childCount() - 1))
self.indentations.append(position)
else:
while position < self.indentations[-1] and len(self.parents) > 0:
self.parents.pop()
self.indentations.pop()
parent = self.parents[-1]
parent.insertChildren(parent.childCount(), 1, parent.columnCount())
parent.child(parent.childCount() - 1).setData(0, dict_keys)
if type(dict_values) != dict:
parent.child(parent.childCount() - 1).setData(1, str(dict_values))
self.createData(dict_values, indent)
def index(self, row, column, index=QModelIndex()):
""" Returns the index of the item in the model specified by the given row, column and parent index """
if not self.hasIndex(row, column, index):
return QModelIndex()
if not index.isValid():
item = self.rootItem
else:
item = index.internalPointer()
child = item.child(row)
if child:
return self.createIndex(row, column, child)
return QModelIndex()
def parent(self, index):
""" Returns the parent of the model item with the given index
If the item has no parent, an invalid QModelIndex is returned """
if not index.isValid():
return QModelIndex()
item = index.internalPointer()
if not item:
return QModelIndex()
parent = item.parentItem
if parent == self.rootItem:
return QModelIndex()
else:
return self.createIndex(parent.childNumber(), 0, parent)
def rowCount(self, index=QModelIndex()):
""" Returns the number of rows under the given parent
When the parent is valid it means that rowCount is returning the number of children of parent """
if index.isValid():
parent = index.internalPointer()
else:
parent = self.rootItem
return parent.childCount()
def columnCount(self, index=QModelIndex()):
""" Returns the number of columns for the children of the given parent """
return self.rootItem.columnCount()
def data(self, index, role=Qt.DisplayRole):
""" Returns the data stored under the given role for the item referred to by the index """
if index.isValid() and role == Qt.DisplayRole:
return index.internalPointer().data(index.column())
elif not index.isValid():
return self.rootItem.data(index.column())
def headerData(self, section, orientation, role=Qt.DisplayRole):
""" Returns the data for the given role and section in the header with the specified orientation """
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self.rootItem.data(section)
class TreeNode(object):
def __init__(self, data, parent=None):
self.parentItem = parent
self.itemData = data
self.children = []
def child(self, row):
return self.children[row]
def childCount(self):
return len(self.children)
def childNumber(self):
if self.parentItem is not None:
return self.parentItem.children.index(self)
def columnCount(self):
return len(self.itemData)
def data(self, column):
return self.itemData[column]
def insertChildren(self, position, count, columns):
if position < 0 or position > len(self.children):
return False
for row in range(count):
data = [v for v in range(columns)]
item = TreeNode(data, self)
self.children.insert(position, item)
def parent(self):
return self.parentItem
def setData(self, column, value):
if column < 0 or column >= len(self.itemData):
return False
self.itemData[column] = value
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyle("plastique") # ("cleanlooks")
form = GUIWindow()
form.show()
sys.exit(app.exec_())

QTableView does not show values in column after setItemDelegateForColumn

I haveve implemented a table model (inherited QAbstractTableModel) and added it on a QTableView.
I wanted to add delegate to use custom edit widgets for my data items (for example, use a QSlider for some double values). Alas, after I've added a delegate, the table view does not shows values in this column anymore.
Can anyone tell me, how to fix it?
Here's the code:
# -*- coding: utf-8 -*-
import os, sys
from enum import IntEnum, unique
from collections import OrderedDict
from PyQt5 import QtCore, QtGui, QtWidgets
#unique
class BGModelCols(IntEnum):
alpha = 0
alpha_for_nans = 1
color_map = 2
is_boolean = 3
x_size_px = 4
y_size_px = 5
is_visible = 6
class ScanRadarSimulator(QtCore.QObject):
backgrounds_supported = ["height", "elevation", "visibility", "closing_angles", "land_rcs"]
#property
def background_names(self):
return [self.tr("Land height"), self.tr("Elevation"), self.tr("Visibility"), self.tr("Closing angles"),\
self.tr("Land RCS")]
def __init__(self, parent=None):
super().__init__(parent)
class BackgroundTableModel(QtCore.QAbstractTableModel):
def __init__(self, radar_simulator, parent=None):
super().__init__(parent)
self._radar_simulator = radar_simulator
self._background_names = self._radar_simulator.background_names
assert isinstance(self._radar_simulator, ScanRadarSimulator)
self.column_names = {BGModelCols.alpha: self.tr("α-channel"),
BGModelCols.alpha_for_nans: self.tr("α-channel for NANs"),
BGModelCols.color_map: self.tr("Color map"),
BGModelCols.is_boolean: self.tr("Is boolean mask"),
BGModelCols.x_size_px: self.tr("X pixels"),
BGModelCols.y_size_px: self.tr("Y pixels"),
BGModelCols.is_visible: self.tr("Is visible")}
self._background_items = OrderedDict()
for bg_id in radar_simulator.backgrounds_supported:
bg_dict = {BGModelCols.alpha: 0.7,
BGModelCols.alpha_for_nans: 0.0,
BGModelCols.color_map: "jet",
BGModelCols.is_boolean: False,
BGModelCols.x_size_px: 4000,
BGModelCols.y_size_px: 4000,
BGModelCols.is_visible: False}
self._background_items[bg_id] = bg_dict
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self._radar_simulator.backgrounds_supported)
def columnCount(self, parent=QtCore.QModelIndex()):
return len(BGModelCols)
def flags(self, index=QtCore.QModelIndex()):
if index.isValid():
return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable
def data(self, index=QtCore.QModelIndex(), role=QtCore.Qt.DisplayRole):
if not index.isValid():
return QtCore.QVariant()
row, col = index.row(), index.column()
print("DATA", row, col)
col_item = BGModelCols(col)
if role == QtCore.Qt.DisplayRole:
return str(self._background_items[radar_simulator.backgrounds_supported[row]][col_item])
elif role == QtCore.Qt.EditRole:
return self._background_items[radar_simulator.backgrounds_supported[row]][col_item]
else:
return QtCore.QVariant()
def setData(self, index, value, role=QtCore.Qt.EditRole):
if not index.isValid():
return False
if role == QtCore.Qt.EditRole:
row, col = index.row(), index.column()
col_item = BGModelCols(col)
self._background_items[radar_simulator.backgrounds_supported[row]][col_item] = value
self.dataChanged.emit(index, index)
return True
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.DisplayRole:
if orientation == QtCore.Qt.Horizontal:
return self.column_names[BGModelCols(section)]
elif orientation == QtCore.Qt.Vertical:
return self._background_names[section]
else:
return QtCore.QVariant()
class AlphaChannelDelegate(QtWidgets.QItemDelegate):
def __init__(self, parent=None):
super().__init__(parent)
def createEditor(self, parent, option, index):
print("CREATING EDITOR")
slider = QtWidgets.QSlider(parent)
slider.setMinimum(0)
slider.setMaximum(100)
slider.setOrientation(QtCore.Qt.Horizontal)
return slider
def setModelData(self, editor, model, index):
# row, col = index.row(), index.column()
# col_item = BGModelCols(col)
# model._background_items[model._radar_simulator.backgrounds_supported[row]][col_item] = editor.value() / 100.
print("setModelData")
model.setData(index, editor.value() / 100., QtCore.Qt.EditRole)
def setEditorData(self, editor, index):
print("setEditorData")
row, col = index.row(), index.column()
col_item = BGModelCols(col)
val = int(index.model()._background_items[index.model()._radar_simulator.backgrounds_supported[row]][col_item] * 100)
editor.setValue(val)
def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)
def paint(self, painter, option, index):
QtWidgets.QApplication.style().drawControl(QtWidgets.QStyle.CE_ItemViewItem, option, painter)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
app.setStyle("fusion")
radar_simulator = ScanRadarSimulator()
table_view = QtWidgets.QTableView()
alpha_delegate = AlphaChannelDelegate(table_view)
table_view.setItemDelegateForColumn(int(BGModelCols.alpha), alpha_delegate)
table_view.setEditTriggers(QtWidgets.QAbstractItemView.AllEditTriggers)
table_view.setModel(BackgroundTableModel(radar_simulator))
table_view.show()
sys.exit(app.exec_())
Well, it will work if comment out the paint method.
^_^

Load two columns at start in QColumnView

Is there any way to load more than one column at start in QColumnView?
I tried simulating the click on the desired index in the tree view. Though the click event is received it doesn't load the second column. Tried calling the createColumn as well with the index. But both approaches didn't work.
from PyQt4 import QtCore, QtGui
import os
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)
class MyModel(QtGui.QFileSystemModel):
def __init__(self):
super().__init__()
self.checkedIndexes = {}
self.parentChecked=False
def flags(self,index):
flags=super().flags(index)|QtCore.Qt.ItemIsUserCheckable
return flags
def checkState(self, index):
if index in self.checkedIndexes:
return self.checkedIndexes[index]
else:
return QtCore.Qt.Checked
def data(self, index, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.CheckStateRole:
if index.column() == 0:
return self.checkState(index)
else:
return super().data(index, role)
def setData(self, index, value, role):
if (role == QtCore.Qt.CheckStateRole and index.column() == 0):
self.checkedIndexes[index] = value
self.dataChanged.emit(index,index)
return True
return super().setData(index, value, role)
def hasChildren(self,index):
hasChildren=super().hasChildren(index)
path=super().filePath(index)
dirIter=QtCore.QDirIterator(path,QtCore.QDir.AllDirs|QtCore.QDir.NoDotAndDotDot|QtCore.QDir.NoSymLinks)
if dirIter.hasNext():
return True
else:
return False
return hasChildren
class columnView(QtGui.QDialog):
def __init__(self,parent=None):
super().__init__(parent)
self.ui = Ui_Dialog()
self.ui.setupUi(self)
self.model=MyModel()
self.model.setFilter(QtCore.QDir.AllDirs|QtCore.QDir.NoDotAndDotDot|QtCore.QDir.NoSymLinks)
path=os.path.expanduser("~")
self.model.setRootPath(path)
self.ui.columnView.setModel(self.model)
#print("path=",path)
self.ui.columnView.setRootIndex(self.model.index(path))
self.ui.columnView.updatePreviewWidget.connect(self.closePreview)
self.show()
openIndex=self.model.index(os.path.join(path,"Documents"))
self.ui.columnView.createColumn(openIndex)
#QtCore.QMetaObject.invokeMethod(self.ui.columnView, "clicked", QtCore.Qt.QueuedConnection, QtCore.Q_ARG(QtCore.QModelIndex, openIndex))
self.ui.columnView.clicked.connect(self.rowClicked)
self.ui.closePushButton.clicked.connect(self.close)
def rowClicked(self,index):
print("row clicked=",self.model.filePath(index))
def closePreview(self,index):
self.ui.columnView.setPreviewWidget(None)
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName(_fromUtf8("Dialog"))
Dialog.resize(596, 389)
self.verticalLayout = QtGui.QVBoxLayout(Dialog)
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
self.columnView = QtGui.QColumnView(Dialog)
self.columnView.setObjectName(_fromUtf8("columnView"))
self.verticalLayout.addWidget(self.columnView)
self.horizontalLayout = QtGui.QHBoxLayout()
self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.closePushButton = QtGui.QPushButton(Dialog)
self.closePushButton.setObjectName(_fromUtf8("closePushButton"))
self.horizontalLayout.addWidget(self.closePushButton)
self.verticalLayout.addLayout(self.horizontalLayout)
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
Dialog.setWindowTitle(_translate("Dialog", "Dialog", None))
self.closePushButton.setText(_translate("Dialog", "Close", None))
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
view = columnView()
sys.exit(app.exec_())
Though both TreeView and ColumnView is designed to display hierarchical data, I feel that when compared with TreeView, the ColumnView implementation was given less significance and highly frustrating. In TreeView you can do the above easily with QTreeView.expand(index).
The only way to do is to select the row with the index using the selection model
self.ui.columnView.selectionModel().setCurrentIndex(index,QtGui.QItemSelectionModel.Current|QtGui.QItemSelectionModel.Select)
This will highlight the row and will load the corresponding next column.
Ref: https://forum.qt.io/topic/76588/loading-two-columns-at-start-in-qcolumnview

QDataWidgetMapper and QAbstractTableModel

I have a QAbstractTableModel displayed by a QTableView. The table has 2 columns and a couple of rows. Each cell is a QLineEdit. My question is if there is a way of using QDataWidgetMapper::addMapping with my custom model (for the QLineEdit cells).
I think you mean QSignalMapper:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
from PyQt4 import QtGui, QtCore
class MyLineEdit(QtGui.QLineEdit):
focused = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(MyLineEdit, self).__init__(parent)
self.installEventFilter(self)
def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.FocusIn:
self.focused.emit()
return super(MyLineEdit, self).eventFilter(obj, event)
class MyTableModel(QtCore.QAbstractTableModel):
def __init__(self, data, headers, parent=None):
super(MyTableModel, self).__init__(parent)
self.data = data
self.headers = headers
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self.data)
def columnCount(self, parent=QtCore.QModelIndex()):
return len(self.data[0])
def data(self, index, role):
if not index.isValid():
return QtCore.QVariant()
elif role != QtCore.Qt.DisplayRole:
return QtCore.QVariant()
return QtCore.QVariant(self.data[index.row()][index.column()])
def headerData(self, section, orientation, role):
if orientation == QtCore.Qt.Horizontal \
and role == QtCore.Qt.DisplayRole:
return QtCore.QVariant(self.headers[0][section])
elif orientation == QtCore.Qt.Vertical \
and role == QtCore.Qt.DisplayRole:
return QtCore.QVariant(self.headers[1][section])
return QtCore.QVariant()
class MyWindow(QtGui.QWidget):
def __init__(self, parent=None):
super(MyWindow, self).__init__(parent)
headers = [range(3), range(5)]
data = [
str(x) * len(headers[0])
for x in range(len(headers[1]))
]
self.model = MyTableModel(data, headers, self)
self.tableView = QtGui.QTableView(self)
self.tableView.setModel(self.model)
self.layoutHorizontal = QtGui.QHBoxLayout(self)
self.layoutHorizontal.addWidget(self.tableView)
self.signalMapper = QtCore.QSignalMapper(self)
self.signalMapper.mapped.connect(self.on_signalMapper_mapped)
widgetNumber = 0
for rowNumber in range(self.model.rowCount()):
for columnNumber in range(self.model.columnCount()):
cellIndex = self.model.index(rowNumber, columnNumber)
lineEdit = MyLineEdit(self)
lineEdit.setText("row: {0} col: {1}".format(rowNumber, columnNumber))
lineEdit.focused.connect(self.signalMapper.map)
self.signalMapper.setMapping(lineEdit, widgetNumber)
self.tableView.setIndexWidget(cellIndex, lineEdit)
widgetNumber += 1
#QtCore.pyqtSlot(int)
def on_signalMapper_mapped(self, i):
print self.signalMapper.mapping(i).text()
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
app.setApplicationName('MyWindow')
main = MyWindow()
main.resize(333, 222)
main.show()
sys.exit(app.exec_())

Resources