PDA

View Full Version : Problems with QItemDelegate



Jimmy2775
9th June 2006, 01:03
I am using a QTableView. Each row in my table represents an object, and each column an attribute of the object. When the user selects a new row I want to save the deselected row. I have connected the QTableView's selectionModel's selectionChanged signal to a slot that calls the save method the deselected object, and it works well.

I created a custom delegate as well for my QTableView. The problem I am having is that if I am editing something using the delegate, and then I click on a different row while the editor is still open, then the selectionChanged signal fires before the delegate's setModelData method is called. This means that the object is saved before the editor's data is set, so the changes are lost.

I have looked at the Qt documentation and it mentions a releaseEditor method of QItemDelegate that does not exist :mad: In my save method I have access to my delegate class, but I cannot call the setModelData method because I don't know which widget and index to pass in to the method. Can anyone suggest something I could try in order to get the setModelData method to be called before the object is saved, or how to determine which widget and index to pass to setModelData?

pellis
9th June 2006, 14:32
Sorry if it's a silly question, but why do you want to save unedited data (from the row that's been deselected without being edited)? If you didn't do that then you would be able to have your 'save' code triggered from setModelData in the delegate.

Jimmy2775
9th June 2006, 18:43
No such thing as a silly question :)

I guess I wasn't as clear as I could have been. Here is my problem:


User selects row 1
User double clicks in a cell in row 1
Edit control for that cell appears. User starts editing data.
User selects row 2
View's selectionChanged signal fires, causing row 1 to save
Edit control on row 1 closes, causing delegate's setModelData method to be called


I need to switch the order of the last two steps, or force the edit control to close as part of the save method.

In response to your question, I don't want to save unedited data - the object I am saving has an isDirty() method I can check to prevent saving is the object has not changed. The problem is that the change to the data is not made until after the oppertunity to save has passed (in the selectionChanged slot).

Unfortunately I cannot move my save code into the setModelData method as you suggested, although that was a good idea. The code for saving sends out notifications to the rest of the application, and these can only be sent when the user is finished editing the object and has moved on.

Thank you for your suggestions, though :)

wysota
9th June 2006, 19:58
Why don't you just connect to appropriate model signals like QAbstractItemModel::dataChanged()?

Jimmy2775
9th June 2006, 20:29
I believe that would work in most cases, but not mine. The clean/dirty state of the object is changed when the object is saved, which emits a dataChanged signal to update the QTableView. Unfortunately calling the code to save in the dataChanged slot would create an infinite loop in my case. Furthermore, there are other notifications that the system sends to the model that cause dataChanged to be emitted to update the GUI, and I don't want to save every time that happens.

Is there no call I can make to the QItemDelegate to close the editor?

I have been working on this and am able to call setModelData inside my selectionChanged slot. This solution holds promise, but I need to be able to determine whether or not the editor is open. Does anyone know any way I can determine the editor's state?

Thanks for all your help so far. While I have not solved the problem so far, your suggestions are giving me ideas of approaches to take to solve the problem.

wysota
9th June 2006, 22:08
I believe that would work in most cases, but not mine. The clean/dirty state of the object is changed when the object is saved, which emits a dataChanged signal to update the QTableView. Unfortunately calling the code to save in the dataChanged slot would create an infinite loop in my case.
Why would it create an infinite loop? Do you call setData() from your code? If data is not modified, the signal should not get emitted, so if you "modify" already "modified" item, there is really no modification and no signal emition.


Furthermore, there are other notifications that the system sends to the model that cause dataChanged to be emitted to update the GUI, and I don't want to save every time that happens.
I think you're wrong. dataChanged is emitted when... data is changed and this can happen in at most two cases: item is added to the model (although I think layoutChanged() would be emitted here) or existing item is modified.


Is there no call I can make to the QItemDelegate to close the editor?

I have been working on this and am able to call setModelData inside my selectionChanged slot. This solution holds promise, but I need to be able to determine whether or not the editor is open. Does anyone know any way I can determine the editor's state?

Thanks for all your help so far. While I have not solved the problem so far, your suggestions are giving me ideas of approaches to take to solve the problem.

I think you have a wrong approach. Instead of providing your own model mechanisms you should use the existing model-view framework to do all the work. If setData() is called, an item has been changed and it can be considered "dirty". You can tag it as such and when submit() is called, you can synchronise the model with the external backend if you use one.

Jimmy2775
9th June 2006, 22:34
Why would it create an infinite loop? Do you call setData() from your code? If data is not modified, the signal should not get emitted, so if you "modify" already "modified" item, there is really no modification and no signal emition.

The objects I am editing can be edited in other GUI elements of the application as well. When the object is saved, it emits a notification to let all the GUI elements if the app know that the data has changed and they need to update. In the table, this notification is the dataChanged signal. If I use the corresponding slot for saving, then whenever the data is saved it will emit a dataChanged signal which will cause the data to be saved again which will emit another dataChanged signal which will cause the data to be saved again and so on...


I think you have a wrong approach. Instead of providing your own model mechanisms you should use the existing model-view framework to do all the work. If setData() is called, an item has been changed and it can be considered "dirty". You can tag it as such and when submit() is called, you can synchronise the model with the external backend if you use one.

The existing model-view framework is not sufficient to meet the needs of this application. Using the dataChanged() signal to cause the object to save will not work because the QTableView is not the only GUI element that can cause this signal to be emitted. Furthermore, a record can be considered "dirty" even if setData has not been called - it may have been modified by another part of the application or even another user using a different instance of the client but the same server, so I need my own isDirty mechanism. In a simple, stand-alone application I would agree that I'm taking the wrong approach, but in this case I don't have many options.

Essentially I am stuck in my save function that lies in the selectionChanged slot. From there I need to cause the delegate's setModelData function to be called somehow.

wysota
9th June 2006, 23:42
Why don't you use the model-view framework for other gui components as well? If all of them use QAbstractItemModel::setData() and all of them are connected to appropriate model signals (you might need to provide a kind of controller for that), all of them will get updated through the MVC framework. There is no sense in using the model-view pattern only for part of the data exchange -- either use it for all components or don't use it at all.

Jimmy2775
12th June 2006, 19:24
I would love to use the MVC framework for the entire application but as I have mentioned previously it is not sufficient to meet the needs of the application I'm working on.


There is no sense in using the model-view pattern only for part of the data exchange -- either use it for all components or don't use it at all.

I don't agree with that. I think it's important to try and leverage as much of Qt's out-of-the-box functionality as possible, and then extend it to fit any remaining requirements. We have had great success using Qt in this way.

In case anyone has this issue in the future, here's how I was able to fix the problem:

In the selectionChanged() slot, I was able to call the QItemDelegate's setModelData method. The reference to the deselected index from the QAbstractItemView's currentItem() method, and that index can be used to get the appropriate widget using the QAbstractItemView's indexWidget() method.

The answer that I was looking for was how to determine the state of the delegate editor, and this is found by checking QAbstractItemView::state() - this way I can ensure setModelData() is only called when the editor is open.

Thanks for all your help :)

wysota
12th June 2006, 19:37
I don't agree with that. I think it's important to try and leverage as much of Qt's out-of-the-box functionality as possible, and then extend it to fit any remaining requirements. We have had great success using Qt in this way.

You have a right to disagree, but the way you are implementing your app, you are trying to work-around main advantages of Interview, so you are really trying to find a solution "how not to use the model-view framework with the model-view framework".

In what way is Interview not sufficient for you? Because from the things you said, I can't see a point where it might fail.

Jimmy2775
12th June 2006, 21:18
In what way is Interview not sufficient for you? Because from the things you said, I can't see a point where it might fail.
No I don't expect you would - I haven't given much design detail because I wanted to solve my immediate problem and avoid debating design.

My application is a Qt client that connects to a DB server API written in Java. The objects I am modelling are actually pointers to Junc++ion wrappers. Qt is not sufficient for me because it doesn't allow the connected clients to monitor these Java objects and update the GUI whenever any one connected client modifies the object. Obviously this is not an issue with Qt because it was not designed to work this way, but it means I do have to, as you've said, work-around main advantages of Interview, in order to get the client to work properly.

I hope that is a satisfactory answer - I am reluctant to go into further detail to avoid violating NDA's and avoid debating design decisions beyond my control :)