[PyKDE] Model-View application: view not updated

Sibylle Koczian Sibylle.Koczian at T-Online.de
Sat Dec 16 14:19:00 GMT 2006


"Andreas Pakulat" <apaku at gmx.de> schrieb:
> On 15.12.06 16:17:21, Sibylle Koczian wrote:
> > The problem is with the cancel button: the changed records are only
> > changed back after the window lost focus - the view isn't updated
> > directly after clicking the button. It _is_ updated after changing the
> > spin box value - but only if it's a real change, not after changing and
> > changing back again to the old value. I can't understand this, because
> > in both cases the same method is called.
> 
> Hmm, I don't think I follow your description. 

I'll try again. The application is started and shows some records in a QTableView. I edit one or more of these records and decide to cancel my alterations. So I click on "cancel". No change in the view. Now I can do several things to get back the unedited records:

- I click into another window - _now_ the view changes to show the edited records in their original form.

- I choose another value in my spinbox and click on "ok" - the view shows a new set of records, as it should. Back to the first value: it shows the unedited records of the first set, again as it should.

- I try to trick my application by changing the spinbox value twice and clicking "ok" only after returning to the original value: no change, I see the edited records and not the original form.

All of this hasn't really anything to do with database use, the example script doesn't use any database at all.

BTW: Did you look at the
> ready-made models for accessing SQL databases in Qt4?
> 

I did, but: 

I didn't understand how to get the SQL support for Firebird. Especially using Ubuntu which doesn't put Firebird in the standard place Qt4 expects it in.

And I've already got a lot of functions using kinterbasdb with my database (all the work which can be done without a GUI). So I'm not so very happy with the thought of changing the database driver.


> > I've reduced the problem to a smaller application with a small global
> > list in place of the database - the offending behaviour stays the same.
> > But it's still 153 lines of code. Should I post it or is the description
> > sufficient?
> 
> 153 loc is not really large, please post it.
> 

Ok, here it is. Thanks to everybody taking a look!
Sibylle

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
view_update.py

Test update of view with editable model subclassed from QAbstractTableModel.
"""

import sys

from PyQt4 import QtCore, QtGui

gData = [[(101, 'Beckenbauer', 'Franz'), (102, 'Braun', 'Egidius'),
          (103, 'Hackmann', 'Werner'), (104, 'Zwanziger', 'Theo')],
         [(201, 'Doll', 'Thomas'), (202, 'Funkel', 'Friedhelm'),
          (203, 'Hitzfeld', 'Otmar'), (204, 'Magath', 'Felix'),
          (205, 'Veh', 'Armin')],
         [(301, 'Huggel', 'Benjamin'), (302, 'Jones', 'Jermaine'),
          (303, 'Spycher', 'Christoph')]]

cFelder = ['IDNr', 'LastName', 'FirstName']
          
class TestModel(QtCore.QAbstractTableModel):

    def __init__(self, columnTitles, parent=None): 
        QtCore.QAbstractTableModel.__init__(self, parent)
        self._resultRows = []
        self._columnTitles = columnTitles

    def columnCount(self, parent):
        return len(self._columnTitles)

    def rowCount(self, parent):
        return len(self._resultRows)

    def data(self, index, role):
        if not (index.isValid() and role in (QtCore.Qt.DisplayRole,
                                             QtCore.Qt.EditRole)):
            return QtCore.QVariant()
        value = self._resultRows[index.row()][index.column()]
        if value is None:
            value = ""
        if isinstance(value, str):
            try:
                value = unicode(value, "UTF-8")
            except UnicodeError:
                value = unicode(value, "ISO-8859-1")
        else:
            value = unicode(value)
        return QtCore.QVariant(value)
        
    def headerData(self, section, orientation, role):
        if role != QtCore.Qt.DisplayRole:
            return QtCore.QVariant()
        if orientation == QtCore.Qt.Horizontal:
            return QtCore.QVariant(self._columnTitles[section])
        elif orientation == QtCore.Qt.Vertical:
            return QtCore.QVariant(section + 1)
        return QtCore.QVariant()

    def flags(self, index):
        # First column (IDNr) may not be editable
        flags = QtCore.QAbstractItemModel.flags(self, index)
        if index.column() > 0:
            flags |= QtCore.Qt.ItemIsEditable
        return flags

    def setData(self, index, value, role):
        if index.column() > 0:
            s = value.toString()
            self._resultRows[index.row()][index.column()] = str(s)
        else:
            return False
        self.emit(QtCore.SIGNAL('dataChanged(const QModelIndex &, '
                                'const QModelIndex &)'),
                  index, index)
        return True

    def setResultset(self, recno):
        print "TestModel.setResultset called"
        # The items in _resultRows can't be tuples if the model shall be
        # editable
        self._resultRows = [list(rec) for rec in gData[recno]]
        self.reset()

    def submitAll(self, recno):
        print "TestModel.submitAll called"
        gData[recno] = [tuple(rec) for rec in self._resultRows]

class TestView(QtGui.QWidget):

    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.model = TestModel(cFelder)
        self.model.setResultset(1)

        lb = QtGui.QLabel('&List No')
        self.chooseRecord = QtGui.QSpinBox()
        self.chooseRecord.setRange(0, len(gData) - 1)
        self.chooseRecord.setValue(1)
        lb.setBuddy(self.chooseRecord)
        btOk = QtGui.QPushButton('ok')
        self.view = QtGui.QTableView()
        self.view.setModel(self.model)
        self.view.setAlternatingRowColors(True)
        btSubmit = QtGui.QPushButton('&Submit')
        btCancel = QtGui.QPushButton('&Cancel')

        mbox = QtGui.QVBoxLayout()
        hbox = QtGui.QHBoxLayout()
        hbox.addWidget(lb)
        hbox.addWidget(self.chooseRecord)
        hbox.addWidget(btOk)
        mbox.addLayout(hbox)
        mbox.addWidget(self.view)
        hbox = QtGui.QHBoxLayout()
        hbox.addWidget(btSubmit)
        hbox.addWidget(btCancel)
        mbox.addLayout(hbox)
        self.setLayout(mbox)
        self.resize(360, 300)

        self.connect(btOk, QtCore.SIGNAL('clicked()'), self.fillTable)
        self.connect(btCancel, QtCore.SIGNAL('clicked()'), self.fillTable)
        self.connect(btSubmit, QtCore.SIGNAL('clicked()'), self.saveTable)
        self.connect(self.model, QtCore.SIGNAL("modelReset()"),
                     self.resetModel)

    def fillTable(self):
        print "fillTable called"
        self.model.setResultset(self.chooseRecord.value())

    def saveTable(self):
        print "saveTable called"
        self.model.submitAll(self.chooseRecord.value())

    def resetModel(self):
        print "Signal modelReset empfangen"
        self.view.update()
	# This is called after clicking the "Submit" button, but the view _isn't_ updated.

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    test = TestView()
    test.show()
    sys.exit(app.exec_())


-- 
Dr. Sibylle Koczian 
Fasanenstrasse 12 
D-82293 Mittelstetten 




More information about the PyQt mailing list