0

I am constructing a GUI which displays the contents of a SQL database table and which should allow addition and modification of records through the GUI, using the base PyQt4 classes QTableView and QSqlTableModel. I am having problems with editing the table view.

Here is a Minimal, Complete and Verifiable version of the complete code. It does require changing the login info of whatever SQL database you are using, but besides that the code is sufficient to run the interface:

import sys
import site
from subprocess import Popen
from PyQt4.QtCore import(Qt)
from PyQt4.QtGui import(QMainWindow, QApplication, QWidget, QPushButton, qApp,
QVBoxLayout, QTableView, QApplication)
from PyQt4.QtSql import(QSqlDatabase, QSqlQuery, QSqlQueryModel, QSqlTableModel)


class Window(QMainWindow):

    def __init__(self):

        super(Window, self).__init__()

        # Open and connect to database - this needs to be changed for the particular db you are using
        self.__database = QSqlDatabase.addDatabase('QPSQL')
        self.__database.setHostName('localhost')
        self.__database.setDatabaseName('~/Desktop/H2 testing/TESTING/db/db')
        self.__database.setPort(5435)
        self.__database.setUserName('grav')
        self.__database.setPassword('XXXXXXXXXX')
        self.__database.open()

        ok = self.__database.open()
        if ok == False:
            print 'Could not open database'
            print 'Text: ', self.__database.lastError().text()
            print 'Type: ', str(self.__database.lastError().type())
            print 'Number: ', str(self.__database.lastError().number())
            print 'Loaded drivers:', str(QSqlDatabase.drivers())

        # Create the QSqlTableModel using the database
        self.modelDirections = QSqlTableModel(None, self.__database)
        self.modelDirections.setTable('PUBLIC.DIRECTIONS')
        self.modelDirections.setEditStrategy(QSqlTableModel.OnFieldChange)
        self.modelDirections.select()

        # Create the QTableView and connect to the QSqlTableModel
        self.tableDirections = QTableView()
        self.tableDirections.setModel(self.modelDirections)

        # Create a QPushButton to add a row to the table
        self.buttonAddDir = QPushButton('Add direction')
        self.buttonAddDir.clicked.connect(self.createDirection)

        # Set up the rest of the window with the QTableView and the QPushButton
        vbox = QVBoxLayout()
        vbox.addWidget(self.tableDirections)
        vbox.addWidget(self.buttonAddDir)
        stretchBox = QWidget()
        stretchBox.setLayout(vbox)
        self.setCentralWidget(stretchBox)
        self.show()


    def createDirection(self):

        # Define and execute query to determine current max direction serial
        model = QSqlQueryModel()
        query = 'SELECT * FROM directions WHERE id=(SELECT MAX(id) FROM directions)'
        model.setQuery(query)
        if model.record(0).value('id').toString() == '':
            newDirectionSerial = 0
        else:
            newDirectionSerial = int(model.record(0).value('id').toString()) + 1

        # Define queries to insert new direction record
        queryText = 'INSERT INTO public.directions (id, text, olddir, opposite) \
        VALUES (%s, NULL, 1, NULL)' % (newDirectionSerial)
        query = QSqlQuery()
        query.exec_(queryText)


if __name__ == "__main__":

    app = QApplication(sys.argv)
    newWindow = Window()
    sys.exit(app.exec_())

On loading the GUI, the table displays correctly in the QTableView. However, I have 2 problems:

  1. I am able to double-click into a field in the table and begin editing, but then when I double-click into another field, anything I entered in the first field disappears.

    When I edit a field and then switch to editing a new field, I get the message "Data changed" even though whatever I entered disappears.

    My best guess here is that the data is being changed in the QSqlTableModel but then for some reason not translating through to the underlying database field, and when the view refreshes, it does so with an updated model from the still-empty database field.

  2. When I add a new record using the button, the new record doesn't show up. If I close out the script and then start it up again, the new record shows up.

These may well be 2 separate problems but I have a feeling they're related.

I have managed to gather some more information about issue 1 since first posting this problem: After connecting the QSqlTableModel.dataChanged signal to a reporter function, I'm able to confirm that

  1. The signal is indeed firing when I edit the table-view fields,
  2. The data is not being transferred from the model to the database, because of a problem with the query. The database is returning the following error:

ERROR: Column "text" not found; SQL statement: UPDATE PUBLIC.DIRECTIONS SET "text"='test' WHERE "id" = 1 AND "text" = 'constitutes' AND "dir" = 1 AND "opposite" = 2 [42122-164] (42S22) QPSQL: Unable to create query

I'm able to access the database through another application and I test-ran variants of this query. Stripping the quotes around the field names and getting rid of the trailing [42122-164] produces a successful query and the behavior I've been looking for.

That's great - but I want the QSqlTableView to handle this, and don't know how. Anyone clues to how to modify the query behavior of the view (without completely rebuilding it) would be immensely appreciated!

Grav
  • 347
  • 1
  • 2
  • 15
  • `db.commit(); model.select()`. – ekhumoro Feb 20 '17 at 01:04
  • That seems like it will work with the buttons which add and subtract rows, since they call a specific method. But I don't think that will work for the simple case of editing text in the actual fields of the table... – Grav Feb 20 '17 at 01:30
  • [setEditStrategy()](https://doc.qt.io/qt-4.8/qsqltablemodel.html#EditStrategy-enum). – ekhumoro Feb 20 '17 at 02:19
  • That is currently OnFieldChange, which should be sufficient for the purpose... – Grav Feb 20 '17 at 02:44
  • Unfortunately, it doesn't seem to cut it. Everything is set up according to the examples and your suggestions, it just will not sync the view and the database for some reason... – Grav Mar 04 '17 at 03:02
  • You'll have to provide an [mcve]. – ekhumoro Mar 04 '17 at 03:12
  • I hope the version above is more helpful. Thanks, ekhumoro, for altering me to MCV principles and for your help! – Grav Mar 04 '17 at 18:26
  • Sorry, but I cannot reproduce any of the issues in your question when using a SQLite3 db - everything works exactly as expected. I don't use PostgreSQL, so I can't really comment on that. – ekhumoro Mar 04 '17 at 19:34
  • @ekhumoro, I added a bit more information to this one after digging into the issue more. Maybe it will be more intelligible to you now. Thanks if that's the case! – Grav Jun 22 '17 at 02:13
  • You should triple check that the `text` column is named correctly in the db. Make sure you haven't defined it with a trailing space or something. – ekhumoro Jun 22 '17 at 18:51
  • Indeed, the columns are named correctly. No trailing spaces or anything of that sort... – Grav Jun 23 '17 at 00:33

0 Answers0