PDA

View Full Version : Contiguous selection and custom proxy model



jz
20th June 2021, 17:11
Hi there,

I'm using PySide2 to write a simple app to display list of items in a grid like layout with some filtering but I have a problem with selection.

It starts with a ListModel of items (called Assets for example) that is plugged into SortFilterModel where I do my filtering based on a name and that is plugged into IdenityProxyModel where I reorganize the list to a table and that is displayed in TableView.

It all "seems" to work until I use CTRL+SHIFT selection and query the self.selectionModel().selectedIndexes() later where I found out it selects the wrong stuff. I'm not really sure if my .index() and .parent() methods are correct at all too.

If you select cells horizontally it seems to work but it behaves odd when selecting vertically. Please see the image for the issue.

Thanks a lot for any help!

13656



from PySide2 import QtWidgets, QtGui, QtCore
from PySide2.QtCore import Qt
import math
import sys


class CustomTableView(QtWidgets.QTableView):
def __init__(self):
super(CustomTableView, self).__init__()
self.setMouseTracking(True)

def mousePressEvent(self, event: QtGui.QMouseEvent) -> None:
if event.button() == Qt.LeftButton:
super(CustomTableView, self).mousePressEvent(event)
elif event.button() == Qt.RightButton:
self.doSomethingWithSelected()

def doSomethingWithSelected(self):
for ix in self.selectionModel().selectedIndexes():
print("Selected Indexes:", ix.row(), ix.column(), ix.data(Qt.DisplayRole))


class CustomListModel(QtCore.QAbstractListModel):
def __init__(self, parent=None):
super(CustomListModel, self).__init__(parent)
self._data = [
"Asset_A1",
"Asset_A2",
"Asset_A3",
"Asset_B1",
"Asset_B2",
"Asset_B3",
"Asset_C1",
"Asset_C2",
"Asset_C3",
"Asset_C4",
"Asset_D1",
"Asset_D2",
"Asset_D3",
"Asset_D4",
]

def rowCount(self, parent=QtCore.QModelIndex()):
return len(self._data)

def data(self, index, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
return self._data[index.row()]

def parent(self, index: QtCore.QModelIndex) -> QtCore.QObject:
return QtCore.QModelIndex()


class CustomList2TableProxyModel(QtCore.QIdentityProxyMo del):
def __init__(self, columns=1, parent=None):
super(CustomList2TableProxyModel, self).__init__(parent)
self._columns = columns

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

def rowCount(self, parent=QtCore.QModelIndex()):
if parent.isValid():
return 0
return math.ceil(self.sourceModel().rowCount()/self.columnCount())

def mapToSource(self, proxyIndex):
if proxyIndex.isValid():
r = proxyIndex.row()
c = proxyIndex.column()
row = r * self.columnCount() + c
return self.sourceModel().index(row, 0)
return QtCore.QModelIndex()

def mapFromSource(self, sourceIndex):
r = math.ceil(sourceIndex.row()/self.columnCount())
c = sourceIndex.row() % self.columnCount()
return self.index(r, c)

def data(self, index, role=Qt.DisplayRole):
r = index.row()
c = index.column()
row = r*self.columnCount() + c
if row < self.sourceModel().rowCount():
return super(CustomList2TableProxyModel, self).data(index, role)

def parent(self, index: QtCore.QModelIndex) -> QtCore.QObject:
return QtCore.QModelIndex()

def index(self, row, column, parent=QtCore.QModelIndex()):
if parent.isValid():
return QtCore.QModelIndex()
return self.createIndex(row, column)

# def sibling(self, row: int, column: int, idx: QtCore.QModelIndex) -> QtCore.QModelIndex:
# print("Sibling", row, column, idx.data(Qt.DisplayRole))


class CustomMainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(CustomMainWindow, self).__init__()
self.initUI()

def initUI(self):
self.mainLayout = QtWidgets.QHBoxLayout()
self.mainWidget = QtWidgets.QWidget()

# List Model
self.listViewWidget = QtWidgets.QWidget()
self.listViewLayout = QtWidgets.QVBoxLayout()
sourceModel = CustomListModel()
self.listView = QtWidgets.QListView()
self.listView.setModel(sourceModel)
self.listViewLayout.addWidget(QtWidgets.QLabel("QAbstractListModel: Source model"))
self.listViewLayout.addWidget(self.listView)
self.listViewWidget.setLayout(self.listViewLayout)

# Filter model
self.filterListViewWidget = QtWidgets.QWidget()
self.filterListViewLayout = QtWidgets.QVBoxLayout()
self.filterListView = QtWidgets.QListView()
self.filterProxy = QtCore.QSortFilterProxyModel()
self.filterProxy.setSourceModel(sourceModel)
self.filterProxy.setFilterRegExp(QtCore.QRegExp("1", Qt.CaseInsensitive, QtCore.QRegExp.FixedString))
self.filterProxy.setFilterKeyColumn(0)
self.filterListView.setModel(self.filterProxy)
self.filterListViewLayout.addWidget(QtWidgets.QLab el("QSortFilterProxyModel: Filter '1'"))
self.filterListViewLayout.addWidget(self.filterLis tView)
self.filterListViewWidget.setLayout(self.filterLis tViewLayout)

# List to Grid model
self.gridViewWidget = QtWidgets.QWidget()
self.gridViewLayout = QtWidgets.QVBoxLayout()
self.gridView = CustomTableView()
self.gridProxy = CustomList2TableProxyModel(2)
self.gridProxy.setSourceModel(self.filterProxy)
self.gridView.setModel(self.gridProxy)
self.gridViewLayout.addWidget(QtWidgets.QLabel("QIdentityProxyModel: List to table model"))
self.gridViewLayout.addWidget(self.gridView)
self.gridViewWidget.setLayout(self.gridViewLayout)

# Assemble main Layout
self.mainLayout.addWidget(self.listViewWidget)
self.mainLayout.addWidget(self.filterListViewWidge t)
self.mainLayout.addWidget(self.gridViewWidget)
self.mainWidget.setLayout(self.mainLayout)
self.setCentralWidget(self.mainWidget)


if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = CustomMainWindow()
w.show()
sys.exit(app.exec_())

jz
22nd June 2021, 09:32
I think I managed to solve it. I had to reimplement sibling method in CustomList2TableProxyModel like so:


def sibling(self, row: int, column: int, idx: QtCore.QModelIndex) -> QtCore.QModelIndex:
return self.createIndex(row, column)