@JonB Thanks for taking a look Jon.
(The line formatting in my code block up there is a mistake from when I pasted the code in! My bad there.)
But your QFileSystemModel is hierarchical (has parents)
I'm not using a QFileSystemModel.
FilesModel is a list of files with metadata:
class FilesModel(QAbstractTableModel):
class Column(IntEnum):
ID = 0
PATH = auto()
HASH = auto()
TAGS = auto()
TYPE = auto()
DESCRIPTION = auto()
SOURCES = auto()
DATE = auto()
EXIF = auto()
PARENT = auto()
def __init__(self, source: list[Files] = []):
super().__init__()
self._files = source
self._thumbs = {x.hash: None for x in source}
for file in source:
self._thumbs[file.hash] = QIcon(str(file.path))
def data(self, index, role):
match role:
case Qt.ItemDataRole.DisplayRole | Qt.ItemDataRole.EditRole:
f = self._files[index.row()]
match index.column():
case self.Column.ID:
return f.id
case self.Column.PATH:
return f.path.name
case self.Column.HASH:
return f.hash
case self.Column.TAGS:
return f.tags
case self.Column.TYPE:
return f.type
case self.Column.DESCRIPTION:
return f.description
case self.Column.SOURCES:
return f.sources
case self.Column.DATE:
return f.date
case self.Column.EXIF:
return f.includeExif
case self.Column.PARENT:
return f.parent
case _:
return None
case Qt.ItemDataRole.DecorationRole:
match index.column():
case self.Column.PATH:
return self._thumbs[self._files[index.row()].hash]
case _:
return None
def rowCount(self, index):
return len(self._files)
def columnCount(self, index):
return len(self.Column)
def headerData(self, section, orientation, role):
if orientation == Qt.Orientation.Horizontal:
if role == Qt.ItemDataRole.DisplayRole:
return self.Column(section).name.capitalize()
##########
# Referenced in the above:
##########
type Tag = str
type Hash = str # sha256 hex string
type Date = str # expecting ISO 8601 YYYY-MM-DD
class FileType(IntEnum):
IMAGE = 1
VIDEO = auto()
TEXT = auto()
AUDIO = auto()
@dataclass
class File:
type: FileType = None
path: Path = None
hash: Hash = None # sha256
id: int = None
tags: list[Tag] = field(default_factory=list)
description: str = None
sources: list[str] = field(default_factory=list)
parent: Hash = None
date: QDate = None
includeExif: bool = True
I am also uncertain whether the row numbers in the indexes used/returned in a QItemSelectionModel are suitable/identical to those directly into the model (not selection) which you need to pass to the model/QDataWidgetMapper::setCurrentModelIndex().
I'm a little unsure about that as well however the docs make it very explicit:
https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QDataWidgetMapper.html#PySide6.QtWidgets.QDataWidgetMapper.setCurrentModelIndex
Calls setCurrentIndex() internally. This convenience slot can be connected to the signal currentRowChanged() or currentColumnChanged() of another view’s selection model.
The following example illustrates how to update all widgets with new data whenever the selection of a QTableView named myTableView changes:
mapper = QDataWidgetMapper()
connect(myTableView.selectionModel(), QItemSelectionModel.currentRowChanged,
mapper.setCurrentModelIndex)
The specific connect() call there doesn't seem to be valid (too literally copied from the C++?) but the idea is clear.
(window.ui.fileTableView is a plain QTableView.)
One of the tutorials also uses it in this way:
https://doc.qt.io/qtforpython-6/tutorials/portingguide/chapter3/chapter3.html#python-version
selection_model = self.bookTable.selectionModel()
selection_model.currentRowChanged.connect(mapper.setCurrentModelIndex)