[PyQt] QAbstractItemModel's "dataChanged" signal not working?

Claudio Felix felix.claudio at gmail.com
Wed Mar 3 23:38:12 GMT 2010


2010/3/2 Mark Summerfield <list at qtrac.plus.com>:
> On 2010-02-27, Claudio Felix wrote:
>> Hi everyone,
>>
>> I'm using a QSqlRelationalTableModel for a simple dialog where I can
>> add/delete periods related to a particular customer, which is chosen
>> by a QComboBox. The periods table is filtered by customer (whose ID is
>> a foreign key) and shown through a QTableView. There's an "Add" button
>> which basically inserts a new row in the Periods table and sets the
>> view to edit mode, so the period data can be entered:
>>
>>     def addRecord(self):
>>         row = self.model.rowCount()
>>         customerid = self._getRecordID(self.customerComboBox,
>> self.customersModel, "CUSTOMER_ID")
>>         self.enableControls(False)
>>         self.model.insertRow(row)
>>         index = self.model.index(row, CUSTOMER_ID)
>>         self.model.setData(index, QVariant(customerid))
>>         index = self.model.index(row, PERIOD_YEAR)
>>         self.periodsTableView.setCurrentIndex(index)
>>         self.periodsTableView.edit(index)
>>
>> I noticed that, while the view is in edit mode (with the asterisk '*'
>> appearing in the leftmost field), if the user clicks on the add button
>> again, an empty row appears in the view and the asterisk appears in
>> the next row, with this message on the console:
>>
>> edit: index was invalid
>> edit: editing failed
>>
>> The same problem happens in the "assetmanager.pyw" example from the
>> (great!) book "Rapid GUI Programming with Python and QT", which is my
>> main guide. That way, in my limited experience with PyQT, I tried to
>> work around the undesirable behavior creating the method
>> "enableControls" for the dialog, which is called in the addRecord
>> method so it makes it impossible for the user to click on "add" again
>> while the view is in edit mode. The idea then was to re-enable the
>> controls when the data was finally committed by the view. That's when
>> the main problem comes up. I tried to use the model's "dataChanged"
>> signal for calling my "enableControls" method, but it looks like it
>> never gets emitted, although the record does get written to the
>> database table. Does anybody can confirm that or help me avoiding the
>> problem at all? I used the following signature for the signal, exactly
>> the same shown on QAbstractItemModel's documentation:
>>
>> self.connect(self.model, SIGNAL("dataChanged(const QModelIndex&,const
>> QModelIndex&)"), self.enableControls)
>>
>> That line is declared in my dialog's __init__, along with all the
>> other (working) signals. self.model referes to the
>> QSqlRelationalTableModel.
>>
>> Thanks for any help!
>
> Hi Claudio,
>
> I must admit that I've grown very frustrated with Qt's database support,
> particularly with SQLite. I've found that the book's database examples
> (which all use SQLite since that is supplied with Qt) exhibit varying
> differences in behavior depending on the Qt version.
>
> Regarding your specific problem, I can't see anything obviously wrong
> with your connection. Personally, I would have written it as
>
>    self.connect(self.model, SIGNAL("dataChanged(QModelIndex,QModelIndex)"),
>                 self.enableControls)
>
> but that should make no difference.
>
> Is your model a QSqlRelationalTableModel or a subclass? If it is a
> subclass and you have reimplemented setData() then you must emit the
> dataChanged() signal in your reimplemented setData() method. But if
> you're using QSqlRelationalTableModel directly then it isn't obvious to
> me what you're doing wrong. However, dataChanged() might be the wrong
> signal for reenabling the Add button since it is emitted for every
> change to every field, whereas I'd have thought you wanted to enable the
> Add button only when the record was inserted? So maybe you could try
> connecting to the QAbstractItemModel::rowsInserted() signal?
>
> I had a quick go at changing assetmanager.pyw to do this but without
> success (using PyQt 4.6 and Qt 4.5.2); maybe things have improved with
> Qt 4.6.
>
> --
> Mark Summerfield, Qtrac Ltd, www.qtrac.eu
>    C++, Python, Qt, PyQt - training and consultancy
>        "C++ GUI Programming with Qt 4" - ISBN 0132354160
>

Hello Mark,

Thanks for your help, and specially for the great book you've written.
It's been a really helpful source, very nice to read. It has saved me
from quite a lot of endless trial and error loops!

Back to the signals, I initially thought "rowsInserted" would be
emitted as soon as the view entered editing mode, and since I needed
something to reenable the controls - I had disabled them as soon in
the beggining of addRecord - the idea was to use "dataChanged"
because, since the model edit strategy was set to
QSqlTableModel.OnRowChange, I thought it would be emitted only when
the whole row was commited to the model.

Anyway, your suggestion worked fine. Now I disable the buttons in the
end of "addRecord" method, after calling edit on the view. Then I
reenable the controls triggered by rowInserted signal, which happens
really when the data is commited, so it reenables controls at the
right time. By the way, I was using QSqlRelationalTableModel itself,
not a subclass of it.

Thank you!

Cheers,

Claudio


More information about the PyQt mailing list