<?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[Bloom's (Mostly) Technical Blog]]></title><description><![CDATA[Bloom's (Mostly) Technical Blog]]></description><link>https://blog.datascale.win</link><generator>RSS for Node</generator><lastBuildDate>Tue, 07 Apr 2026 19:37:52 GMT</lastBuildDate><atom:link href="https://blog.datascale.win/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[QDataWidgetMappers in PyQt]]></title><description><![CDATA[This will be a brief overview of how I use QDataWidgetMappers to take the manual process out of CRUD'ing PyQt fields.
QDataWidgetMappers are great because you don't need to compile data structures (like dicts or lists) and then loop through them with...]]></description><link>https://blog.datascale.win/qdatawidgetmappers-in-pyqt</link><guid isPermaLink="true">https://blog.datascale.win/qdatawidgetmappers-in-pyqt</guid><category><![CDATA[pyqt5]]></category><category><![CDATA[Python]]></category><dc:creator><![CDATA[judy bloom]]></dc:creator><pubDate>Thu, 23 Feb 2023 22:35:47 GMT</pubDate><content:encoded><![CDATA[<p>This will be a brief overview of how I use QDataWidgetMappers to take the manual process out of CRUD'ing PyQt fields.</p>
<p>QDataWidgetMappers are great because you don't need to compile data structures (like dicts or lists) and then loop through them with .setData (which can get confusing with QModelIndex) to change your database data!</p>
<p>After setting up my python environment, the general project structure looks like this, generally following the MVC design pattern:</p>
<ul>
<li><p>App/</p>
<ul>
<li><p>models.py (I create and handle QSqlTableModels here)</p>
</li>
<li><p>main.py</p>
</li>
<li><p>view_logic.py (which imports the generated *_view_ui.py files)</p>
</li>
<li><p>database.py (handles creating database connection)</p>
</li>
<li><p>db/</p>
<ul>
<li>records.db (sqlite3)</li>
</ul>
</li>
<li><p>ui_views/</p>
<ul>
<li><p>ui_files/</p>
<ul>
<li>*.ui files created using Qt Designer</li>
</ul>
</li>
<li><p>*_view_ui.py, created using the VSCode extension "PYQT Integration" by FengZhou (<a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=zhoufeng.pyqt-integration">found here</a>)</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>I will not be covering how to set up your QSqlTableModels, however, <a target="_blank" href="https://realpython.com/python-pyqt-database/">this link</a> is a great resource.</p>
<p>In my example, I have a database created with this Sqlite3 schema/code (I generally have a separate script, &lt;script.py&gt; in my App's root folder that handles creating/exec'ing my database, so I can easily delete my &lt;.db&gt; file then run &lt;script.py&gt; to have everything setup again) :</p>
<p>&lt;script.py&gt;</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> PyQt5.QtSql <span class="hljs-keyword">import</span> QSqlQuery
create_table_query = QSqlQuery()
<span class="hljs-comment"># code here to connect to database</span>
create_table_query.exec(
        <span class="hljs-string">"""CREATE TABLE "patient_profile" (
            "patient_id"    INTEGER,
            "doc1"    BLOB,
            "doc2"    BLOB,
            "name_title" BLOB,
            "name_fname" BLOB,
            "name_mname" BLOB,
            "name_lname" BLOB,
            "name_suffix" BLOB,
            "name_pref_name" BLOB,
            "address_add1" BLOB,
            ...
            "date_patient_added"    BLOB,
            PRIMARY KEY("patient_id"),
            FOREIGN KEY("doc1") REFERENCES "doctors"("id"),
            FOREIGN KEY("doc2") REFERENCES "doctors"("id")
            )"""</span>)
</code></pre>
<p>Note: I used BLOB datatype to avoid any potential datatype conflicts, especially helpful when using Q-type objects (QDateTimeEdit, QRadioBox, etc...)</p>
<p>In the case of my example, I want to populate a modal QDialog that opens after a user has selected a patient from a QTableView. The QDialog is made in Qt Designer, the .ui file is saved in /ui_views/ui_files/, and the generated *_view_ui.py is saved in /ui_views/.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677185734857/cba65bb9-5af1-4eb5-9bc3-75112e59d42b.png" alt class="image--center mx-auto" /></p>
<p>Getting started in &lt;view_logic.py&gt;</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> PyQt5.QtWidgets <span class="hljs-keyword">import</span> QDataWidgetMapper, QDialog
<span class="hljs-keyword">from</span> ui_views.ui_files <span class="hljs-keyword">import</span> Ui_patient_profile

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PatientProfile</span>(<span class="hljs-params">QDialog</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, parent, patient_profile_model, data_model</span>):</span>
        super().__init__(parent)
        self.patient_profile_ui = Ui_patient_profile()
        self.patient_profile_ui.setupUi(self)
        self.patient_profile_ui.save_changes_btn.clicked.connect(self.save_changes)

        self.patient_profile_model = patient_profile_model
        <span class="hljs-comment"># data_model is a class created in main.py to hold global variables</span>
        self.data_model = data_model
        <span class="hljs-comment"># create mapper and set it's model</span>
        self.patient_mapper = QDataWidgetMapper()
        self.patient_mapper.setModel(self.patient_profile_model)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">save_changes</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">if</span> self.patient_mapper.submit():
            print(<span class="hljs-string">'saved all fields successfully'</span>)
</code></pre>
<p>For my use case, I don't want changes submitted until the user presses a button "Save", so I'll set the submit policy:</p>
<pre><code class="lang-python">patient_mapper.setSubmitPolicy(QDataWidgetMapper.ManualSubmit)
</code></pre>
<p>Then start mapping widgets to their respective column in the QSqlTableModel:</p>
<pre><code class="lang-python">self.patient_mapper.addMapping(self.patient_profile_ui.title_combo, <span class="hljs-number">3</span>)
self.patient_mapper.addMapping(self.patient_profile_ui.firstname_edit, <span class="hljs-number">4</span>)
</code></pre>
<p>For my use case, I wanted to update the index of the patient_mapper based on a selection from a QTableView in my &lt;main.py&gt; (please note that most &lt;main.py&gt; code is omitted for brevity):</p>
<p>&lt;main.py&gt;</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MainWindow</span>(<span class="hljs-params">QMainWindow</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        self.appt_ui = Ui_MainWindow()
        self.appt_ui.setupUi(self)
        self.appt_ui.patient_tableview.selectionModel().selectionChanged.connect(self.patient_selection_changed)
        self.patient_details = PatientProfile()
        <span class="hljs-comment"># we use .exec_ here to ensure window is modal</span>
        self.appt_ui.show_patient_btn.clicked.connect(<span class="hljs-keyword">lambda</span>: self.patient_details.exec_())


    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">patient_selection_changed</span>(<span class="hljs-params">self, current: QModelIndex</span>):</span>
        <span class="hljs-comment"># .selectionChanged.connect passes in "current", which is an index of where we are in the model</span>
        self.patient_details.set_patient_index(current)
</code></pre>
<p>Back to &lt;view_logic.py&gt;</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PatientProfile</span>(<span class="hljs-params">QDialog</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, parent, patient_profile_model, data_model</span>):</span>
        super().__init__(parent)
        <span class="hljs-comment"># omitted code</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">set_patient_index</span>(<span class="hljs-params">self, current</span>):</span>
        self.patient_mapper.setCurrentIndex(current.row())
</code></pre>
<p>If you are a newer Python and/or PyQt coder, this example may seem a bit confusing however I would highly recommend breaking your code down into the general MVC design pattern (the generated *_view_ui.py files as the view, &lt;view_logic.py&gt; as the controller, and &lt;models.py&gt; as the model(s)) as it can save you a lot of confusion in the long run! Speaking from experience haha.</p>
]]></content:encoded></item></channel></rss>