<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Trouble setting up QDataWidgetMapper]]></title><description><![CDATA[<p dir="auto">I'm new to Qt and trying to set some widgets to update when an row in a <code>QTableView</code> is selected. For some reason the selection is not synchronizing between the view's selection model and the data mapper?</p>
<pre><code>files_mapper = QDataWidgetMapper(window)
files_mapper.setModel(files_model)
files_mapper.addMapping(window.ui.dateEdit, FilesModel.Column.DATE)
files_mapper.currentIndexChanged.connect(print)
file_selectionmodel = window.ui.fileTableView.selectionModel()
file_selectionmodel.currentRowChanged.connect(print) file_selectionmodel.currentRowChanged.connect(files_mapper.setCurrentModelIndex)
</code></pre>
<p dir="auto"><code>files_model</code> is a custom <code>QAbstractTableModel</code> subclass (<code>FilesModel</code>).<br />
The signal connection on line 4 never fires, even when the signal on line 6 <em>does</em> fire.<br />
It's very possible that I'm not understanding how all of these pieces work. I feel like the docs do not do a good job of explaining how components are meant to fit together!</p>
]]></description><link>https://forum.qt.io/topic/165118/trouble-setting-up-qdatawidgetmapper</link><generator>RSS for Node</generator><lastBuildDate>Sat, 26 Sep 2026 02:59:34 GMT</lastBuildDate><atom:link href="https://forum.qt.io/topic/165118.rss" rel="self" type="application/rss+xml"/><pubDate>Fri, 25 Sep 2026 03:25:09 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Trouble setting up QDataWidgetMapper on Sat, 26 Sep 2026 00:29:56 GMT]]></title><description><![CDATA[<p dir="auto">From a quick test it looks like hiding columns on the <code>QSortFilterProxyModel</code> was stopping the model from returning Date column data to the mapper. Which makes sense, I think? I will need to find another way to filter the table view columns instead.</p>
]]></description><link>https://forum.qt.io/post/840274</link><guid isPermaLink="true">https://forum.qt.io/post/840274</guid><dc:creator><![CDATA[aarqon]]></dc:creator><pubDate>Sat, 26 Sep 2026 00:29:56 GMT</pubDate></item><item><title><![CDATA[Reply to Trouble setting up QDataWidgetMapper on Sat, 26 Sep 2026 00:14:20 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/jonb">@<bdi>JonB</bdi></a> This seems to work as expected so I will try to figure out what I'm missing in the main project! It might be <code>flags()</code> and <code>setData()</code> in the model class...</p>
<pre><code class="language-python">################################################################################
# Form generated from reading UI file 'designerqvIwSQ.ui'
##
# Created by: Qt User Interface Compiler version 6.11.2
##
# WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################

from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, QMetaObject, QObject, QPoint,
                            QRect, QSize, QTime, QUrl, Qt, QSortFilterProxyModel, QAbstractTableModel)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, QFont, QFontDatabase, QGradient,
                           QIcon, QImage, QKeySequence, QLinearGradient, QPainter, QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QDateEdit, QGridLayout, QHeaderView,
                               QMainWindow, QMenuBar, QSizePolicy, QStatusBar, QTableView, QVBoxLayout, QWidget, QDataWidgetMapper)

from dataclasses import dataclass, field
from enum import IntEnum, auto
import sys
from pathlib import Path


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        if not MainWindow.objectName():
            MainWindow.setObjectName(u"MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QWidget(MainWindow)
        self.centralwidget.setObjectName(u"centralwidget")
        self.gridLayout = QGridLayout(self.centralwidget)
        self.gridLayout.setObjectName(u"gridLayout")
        self.verticalLayout = QVBoxLayout()
        self.verticalLayout.setObjectName(u"verticalLayout")
        self.tableView = QTableView(self.centralwidget)
        self.tableView.setObjectName(u"tableView")

        self.verticalLayout.addWidget(self.tableView)

        self.dateEdit = QDateEdit(self.centralwidget)
        self.dateEdit.setObjectName(u"dateEdit")

        self.verticalLayout.addWidget(self.dateEdit)

        self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1)

        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QMenuBar(MainWindow)
        self.menubar.setObjectName(u"menubar")
        self.menubar.setGeometry(QRect(0, 0, 800, 33))
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QStatusBar(MainWindow)
        self.statusbar.setObjectName(u"statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)

        QMetaObject.connectSlotsByName(MainWindow)
    # setupUi

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(QCoreApplication.translate(
            "MainWindow", u"MainWindow", None))
    # retranslateUi

@dataclass
class File:
    path: Path = None
    id: int = None
    date: QDate = None

class FilesModel(QAbstractTableModel):
    class Column(IntEnum):
        ID = 0
        PATH = auto()
        DATE = auto()

    def __init__(self, source: list[Files] = []):
        super().__init__()
        self._files = source

    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.DATE:
                        return f.date
                    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()
    
    def flags(self, index):
        return (
            Qt.ItemFlag.ItemIsSelectable
            | Qt.ItemFlag.ItemIsEnabled
            | Qt.ItemFlag.ItemIsEditable
        )

    def setData(self, index, value, role):
        if role == Qt.ItemDataRole.EditRole:
            f = self._files[index.row()]
            match index.column():
                case self.Column.ID:
                    f.id = value
                    return True
                case self.Column.PATH:
                    f.path = value
                    return True
                case self.Column.DATE:
                    f.date = value
                    return True
                case _:
                    return False


class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)


if __name__ == "__main__":
    app = QApplication(sys.argv)

    window = MainWindow()
    files_model = FilesModel([
        File(path=Path("test path 1"), id=0, date="2026-01-03"),
        File(path=Path("test path 2"), id=1, date="2025-02-14"),
        File(path=Path("test path 3"), id=2, date="2026-09-25")
    ])
    files_proxymodel = QSortFilterProxyModel()
    files_proxymodel.setSourceModel(files_model)
    window.ui.tableView.setModel(files_proxymodel)

    mapper = QDataWidgetMapper()
    mapper.setModel(files_proxymodel)
    mapper.addMapping(window.ui.dateEdit, FilesModel.Column.DATE)
    window.ui.tableView.selectionModel().currentRowChanged.connect(
        lambda c,p: mapper.setCurrentModelIndex(c)
    )

    window.show()
    sys.exit(app.exec())
</code></pre>
]]></description><link>https://forum.qt.io/post/840273</link><guid isPermaLink="true">https://forum.qt.io/post/840273</guid><dc:creator><![CDATA[aarqon]]></dc:creator><pubDate>Sat, 26 Sep 2026 00:14:20 GMT</pubDate></item><item><title><![CDATA[Reply to Trouble setting up QDataWidgetMapper on Fri, 25 Sep 2026 22:26:56 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/aarqon">@<bdi>aarqon</bdi></a><br />
If you are not using a <code>QFileSystemModel</code>/hierarchical model but a flat list model instead then not sure what your issue is.  If you want help please paste a <em>complete but minimal</em> (e.g. preferably simplified/reduced from what you have shown so far) example of whole code in a "blob" showing problem which we can copy &amp; paste.</p>
]]></description><link>https://forum.qt.io/post/840272</link><guid isPermaLink="true">https://forum.qt.io/post/840272</guid><dc:creator><![CDATA[JonB]]></dc:creator><pubDate>Fri, 25 Sep 2026 22:26:56 GMT</pubDate></item><item><title><![CDATA[Reply to Trouble setting up QDataWidgetMapper on Fri, 25 Sep 2026 20:09:34 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/jonb">@<bdi>JonB</bdi></a> Thanks for taking a look Jon.<br />
(The line formatting in my code block up there is a mistake from when I pasted the code in! My bad there.)</p>
<blockquote>
<p dir="auto">But your QFileSystemModel is hierarchical (has parents)</p>
</blockquote>
<p dir="auto">I'm not using a <code>QFileSystemModel</code>.<br />
<code>FilesModel</code> is a list of files with metadata:</p>
<pre><code>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
</code></pre>
<blockquote>
<p dir="auto">I am also uncertain whether the row numbers in the indexes used/returned in a <code>QItemSelectionModel</code> are suitable/identical to those directly into the model (not selection) which you need to pass to the model/<code>QDataWidgetMapper::setCurrentModelIndex()</code>.</p>
</blockquote>
<p dir="auto">I'm a little unsure about that as well however the docs make it very explicit:<br />
<a href="https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QDataWidgetMapper.html#PySide6.QtWidgets.QDataWidgetMapper.setCurrentModelIndex" target="_blank" rel="noopener noreferrer nofollow ugc">https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QDataWidgetMapper.html#PySide6.QtWidgets.QDataWidgetMapper.setCurrentModelIndex</a></p>
<blockquote>
<p dir="auto">Calls <code>setCurrentIndex()</code> internally. This convenience slot can be connected to the signal <code>currentRowChanged()</code> or <code>currentColumnChanged()</code> of another view’s selection model.</p>
<p dir="auto">The following example illustrates how to update all widgets with new data whenever the selection of a <code>QTableView</code> named myTableView changes:</p>
</blockquote>
<pre><code>mapper = QDataWidgetMapper()
connect(myTableView.selectionModel(), QItemSelectionModel.currentRowChanged,
mapper.setCurrentModelIndex)
</code></pre>
<p dir="auto">The specific <code>connect()</code> call there doesn't seem to be valid (too literally copied from the C++?) but the idea is clear.<br />
(<code>window.ui.fileTableView</code> is a plain <code>QTableView</code>.)</p>
<p dir="auto">One of the tutorials also uses it in this way:<br />
<a href="https://doc.qt.io/qtforpython-6/tutorials/portingguide/chapter3/chapter3.html#python-version" target="_blank" rel="noopener noreferrer nofollow ugc">https://doc.qt.io/qtforpython-6/tutorials/portingguide/chapter3/chapter3.html#python-version</a></p>
<pre><code>selection_model = self.bookTable.selectionModel()
selection_model.currentRowChanged.connect(mapper.setCurrentModelIndex)
</code></pre>
]]></description><link>https://forum.qt.io/post/840271</link><guid isPermaLink="true">https://forum.qt.io/post/840271</guid><dc:creator><![CDATA[aarqon]]></dc:creator><pubDate>Fri, 25 Sep 2026 20:09:34 GMT</pubDate></item><item><title><![CDATA[Reply to Trouble setting up QDataWidgetMapper on Fri, 25 Sep 2026 12:10:12 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/aarqon">@<bdi>aarqon</bdi></a><br />
I have crossed out stuff in my initial reply where I did not look correctly at your code.</p>
<p dir="auto">In a word: your code would work if the <code>QDataWidgetMapper</code> had a <em>flat</em> model, like a list model or a non-hierarchical table model.  But your <code>QFileSystemModel</code> is hierarchical (has <em>parent</em>s) and so its indexes do not work for QDWM as-is.  The key is <a href="https://doc.qt.io/qt-6/qdatawidgetmapper.html#setRootIndex" target="_blank" rel="noopener noreferrer nofollow ugc">void QDataWidgetMapper::setRootIndex(const QModelIndex &amp;index)</a></p>
<blockquote>
<p dir="auto">Sets the root item to index. This can be used to display a branch of a tree. Pass an invalid model index to display the top-most branch.</p>
</blockquote>
<p dir="auto">We need to set that to the <em>parent</em> of the selected item to make the <code>index</code> passed in correct.  A complete example of this working is:</p>
<pre><code class="language-python">import sys
from PySide6 import QtWidgets

def selection_changed(index, _previous):
    dwp.setRootIndex(index.parent())
    dwp.setCurrentModelIndex(index)


if __name__ == '__main__':
    app = QtWidgets.QApplication([])

    window = QtWidgets.QWidget()
    window.setGeometry(100, 100, 400, 400)
    layout = QtWidgets.QVBoxLayout()
    window.setLayout(layout)

    fs = QtWidgets.QFileSystemModel()
    fs.setRootPath("/home/jon")

    tv = QtWidgets.QTreeView(window)
    tv.setModel(fs)
    tv.setRootIndex(fs.index("/home/jon"))
    layout.addWidget(tv)

    dwp = QtWidgets.QDataWidgetMapper()
    dwp.setModel(fs)
    leName = QtWidgets.QLineEdit()
    layout.addWidget(leName)
    dwp.addMapping(leName, 0)

    fsm = tv.selectionModel()
    fsm.currentChanged.connect(selection_changed)

    window.show()
    sys.exit(app.exec())
</code></pre>
<p dir="auto">You may have to a bit careful if you intend to use this for <em>editing</em> selected files/directories (possibly to cope correctly with an unsaved edit in the line edit when you click elsewhere), but I don't even know whether you intend to allow that.  For now this at least shows the selected item in the line edit at the bottom of the window.</p>
]]></description><link>https://forum.qt.io/post/840265</link><guid isPermaLink="true">https://forum.qt.io/post/840265</guid><dc:creator><![CDATA[JonB]]></dc:creator><pubDate>Fri, 25 Sep 2026 12:10:12 GMT</pubDate></item><item><title><![CDATA[Reply to Trouble setting up QDataWidgetMapper on Fri, 25 Sep 2026 11:50:10 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/aarqon">@<bdi>aarqon</bdi></a> said in <a href="/post/840257">Trouble setting up QDataWidgetMapper</a>:</p>
<blockquote>
<p dir="auto">The signal connection on line 4 never fires, even when the signal on line 6 does fire.</p>
</blockquote>
<p dir="auto">Your code is not legal Python and produces an error at runtime on</p>
<pre><code>file_selectionmodel.currentRowChanged.connect(print) file_selectionmodel.currentRowChanged.connect(files_mapper.setCurrentModelIndex)
</code></pre>
<p dir="auto">You cannot have multiple statements on a single line like this in Python.  You would either need a <code>;</code> (semi-colon) or separate lines, and I don't know which you have.  At which point I do not know what your current "even when the signal on line 6 does fire" means exactly?  Both of these statements?  One of them?  Do we know whether the second statement connecting <code>file_selectionmodel</code> to <code>files_mapper</code> is even being executed?  If not line #4 is not going to fire.</p>
<p dir="auto"><s>If that is not it: you are connecting a <code>QItemSelectionModel::currentRowChanged(const QModelIndex &amp;current, const QModelIndex &amp;previous)</code> which sends <code>QModelIndex</code> arguments and passing (the first) index to <code>QDataWidgetMapper::setCurrentModelIndex()</code> which accepts an <code>int index</code> parameter.  I think C++ would produce a type compilation error for this where perhaps Python/PySide/PyQt lets it through.  This probably means <code>QDataWidgetMapper::currentIndexChanged()</code> does not get fired?</s></p>
<p dir="auto"><s>That needs correcting.  To start testing you might omit the <code>QItemSelectionModel</code> stuff and verify line #4 works when you do some direct  <code>QDataWidgetMapper::setCurrentModelIndex(int index)</code> with an explicit integer index.  Always break down your code into simplest steps when testing how something is behaving while developing/debugging.</s></p>
<p dir="auto">When you <code>connect()</code> from Python it is your job to look at the parameters sent by the <em>signal</em> against the parameters accepted by the <em>slot</em>.  If they do not match correctly for what you want you have to write some code --- either another "intermediate" slot function or a Python <em>lambda</em> with correct parameters which then calls the slot you really wanted.  For example, here <em>quite untested by me</em> you might need <em>something like</em>:</p>
<pre><code>file_selectionmodel.currentRowChanged.connect(lambda current = current : files_mapper.setCurrentModelIndex(current.row()))
</code></pre>
<p dir="auto">I have not tried testing the code or the Python syntax as I don't have your model code to copy.  I am also uncertain whether the row numbers in the indexes used/returned in a <code>QItemSelectionModel</code> are suitable/identical to those directly into the model (not selection) which you need to pass to the model/<code>QDataWidgetMapper::setCurrentModelIndex()</code>.  Try to get it right yourself, if you are stuck say so and I will try actually testing the required Python code.</p>
<p dir="auto"><strong>UPDATE</strong><br />
I think what I have written about your <code>currentRowChanged()</code>/<code>setCurrentModelIndex()</code> parameters is incorrect, I did not look at your code closely enough.  I am now trying to get this working correctly in PySide6 and will post when I have it....</p>
]]></description><link>https://forum.qt.io/post/840262</link><guid isPermaLink="true">https://forum.qt.io/post/840262</guid><dc:creator><![CDATA[JonB]]></dc:creator><pubDate>Fri, 25 Sep 2026 11:50:10 GMT</pubDate></item></channel></rss>