[SOLVED] QTableView, editing policies, insertRow() and a few more
-
Oke,
So basically your stuck with a GUI that isn't great to work with. The way you described how it works (for your intended use) sounds to me the best solution, setting the delegate for every column individually. IYAM a dialog for new items isn't slower the direct altering cells. You could also make both available. For new/empty items open a dialog, for known cells use the delegates. That sounds to me as the quickest and most reliable GUI.
think your ideas are correct and shouldn't need much changing in working. -
Hi,
I think instead of edit(QModelIndex(row, column)) you should use openPersistentEditor ( const QModelIndex & index ) , and connect the item delegate's signal closeEditor(QWidget*) to a slot where you can implement custom behaveiour for what's next depending on what key has opened it; this signal is emitted when press return or escape while editing
as regarding data validation I think you should do this in item delegate setModelData(...) where you handle each widget editor as in createEditor etc. and in closeEditore slot based on the validation's result you can manage removeRow ...
hope this help!
Cheers!
-
[quote author="NicuPopescu" date="1381321082"]Hi,
I think instead of edit(QModelIndex(row, column)) you should use openPersistentEditor ( const QModelIndex & index ) , and connect the item delegate's signal closeEditor(QWidget*) to a slot where you can implement custom behaveiour for what's next depending on what key has opened it; this signal is emitted when press return or escape while editing
as regarding data validation I think you should do this in item delegate setModelData(...) where you handle each widget editor as in createEditor etc. and in closeEditore slot based on the validation's result you can manage removeRow ...
hope this help!
Cheers![/quote]
Indeed it helps. I have to review your post carefully, this will take me some time hehe, but I suspect this will simplify the whole thing quite a bit. I'll let you know how it goes.
Thank you :)
-
Surely it's something silly, but I am having a hard time understanding how to implement the <to-next-field> thingie on my onCloseEditor(QWidget*) slot that you mentioned.
Since the editor is triggered by the closeEditor(QWidget*) function, and hence, it will receive a pointer to the widget as an argument, I haven't a clue on how to skip to another given index or open a new line.
I've read about the recommended action hint, but that won't be sufficient since, as I said, some fields are to be skipped even if they are visible in the view.
Still reading and googling hehe!
-
you need to know where you are in table view, which cell, right? and the widget editor is helpless ... the workaround is to pass and keep a reference to table view in your subclassed QStyledItemDelegate, even I think is closeEditor slot, the app's focusWidget should be the table view and you could cast it
Cheers!
-
Mmmm, this is turning interesting hehe. Well, my QStyledItemDelegate already receives the table view as the first argument in the constructor, so it's just a matter of making it global to the class (or just pass it to that concrete function). It also receives a QSqlQueryModel, which is used for the completer that there's in the description field. :)
Thank you for all the guidance. Still working on this as time permits, I'll post back.
:)
-
It's truly amazing how well things work when you do them in the right place (as opposed doing to doing them in the wrong place).
The performance is lightyears away, plus some bugs simply went away. Additionally I took rid of some blockSignal()'s that I had around just so that the previous implementation wouldn't iterate over and over due to dataChanged() firing up several times on each row :lol:
Up to now, I've only connected the closeEditor() signal to my own function, where I do some object casting so that I can reach the right members in the right structures, and then do all the field magic.
I didn't need to change much more, though I still haven't gotten into implementing the keyboard sequence. I'll do that tomorrow, probably.
NicuPopescu, or someone else who know the answer, could you hint me onto the right direction in understanding what the difference between the edit() and openPersistentEditor() is?
I've read the docs and googled a bit, but can't find anything relevant. The only thing that docs point out clearly is that edit() doesn't update currentIndex(), so you have to do it before by hand if you plan on using the keyboard and not going crazy on the attempt.
Once again, thank you so much :)
-
you welcome! :)
I checked a little bit the qt's source and both edit() and openPersistentEditor() look quite similar so that the problem is elsewhere ... I think that doing other model updates in onDataChanged determines other dataChange signal emitting ... perhaps there you should have avoided that ... instead in closeEditor slot you can do other updates
-
That's what I figured. The code performs much much better in this place. When doing this stuff in the main class using onDataChanged() as a trigger performance was horrible, plus the whole thing behaved quite erratically (even when I had been careful enough to block the signals while setting the fields so that the slot wasn't called in a cascade fashion).
My current implementation of looks like this:
@void CompletionDelegate::setFieldValues(QWidget *widget)
{
Q_UNUSED(widget);// "model" is for the autocompleter, it holds all the valid values // "tv_model" is the holder of the rows in the current table view, it's a proxy pointing to "general_model" // "general_model" is the model holding ALL the budget rows. Several table views are proxy'ed to this one QModelIndex index; index = tv->currentIndex(); mySortFilterProxyModel *tv_model = new mySortFilterProxyModel; tv_model = qobject_cast<mySortFilterProxyModel*>(tv->model()); QSqlTableModel *general_model = new QSqlTableModel; general_model = qobject_cast<QSqlTableModel*>(tv_model->sourceModel()); QModelIndex source_index; source_index = tv_model->mapToSource(index); /* do all the magic here, read fields, set fields, whatever */
}
@Then you just need to
@ connect(this, SIGNAL(closeEditor(QWidget*)), this, SLOT(setFieldValues(QWidget*)));@
And that's about it. For future reference, and in case someone else finds this useful.
-
This is mostly solved, but there's one thing still... I don't want to open a new thread (not for now at least) because it's so tightly connected to this.
This setFieldValues() function above is connected to closeEditor(), right? I am now reimplementing eventfilter() to get a finer control on what the delegate does when some keys are pressed. So far, I've managed to make it revert the changes in the current row when pressing ESC. But I am not sure how would I go about editing the next cell or adding a new row "the right way (tm)".
The sure thing is that, once the description field is edited, setFieldValues() must be called so the rest of the row makes sense, before skipping to the next field.
So, I guess the sanest approach would be to emit closeEditor() from eventFilter(), that should trigger setFieldValues() conveniently.
But, for that, I have to have a QWidget*. Since my func is not using that for anything, I've tried to just call it with a new one, but the thing is not working as it should, and by debugging the eventFilter() (I'm discovering how painful that is, by the way) it seems that the indexes are invalid.
Right now I have this:
@
bool CompletionDelegate::eventFilter(QObject *object, QEvent *event)
{
QModelIndex index;
index = tv->currentIndex();mySortFilterProxyModel *tv_model = new mySortFilterProxyModel; tv_model = qobject_cast<mySortFilterProxyModel*>(tv->model()); QSqlTableModel *general_model = new QSqlTableModel; general_model = qobject_cast<QSqlTableModel*>(tv_model->sourceModel()); QModelIndex source_index; source_index = tv_model->mapToSource(index); if(event->type() == QEvent::KeyPress) { QKeyEvent *key_event = static_cast<QKeyEvent*>(event); if(key_event->key() == Qt::Key_Escape) { general_model->revertAll(); return true; } else if(/*key_event->key() == Qt::Key_Return ||*/ key_event->key() == Qt::Key_Tab) { if(index.column() == 3) { emit closeEditor(new QWidget); return true; } } } return QStyledItemDelegate::eventFilter(object, event);
}
@There must be something fundamentally wrong with my approach, but I am new to this stuff and I am not sure what kind of functionality is supposed to go in each place...
Can someone give me some tip on how to proceed?
Thank you :)
-
I think you should let QStyledItemDelegate::eventFilter() with its default implementation for Tab,Backtab,Enter,Return,Esc ... and install an event filter for table view object and treat there mostly Tab and Backtab: on Tab do your row insertion and for both return true, for filtering the event out to prevent the editor opening (default implementation for delegate eventFilter) ... just for the record: QStyledItemDelegate::eventFilter() does filtering for its own editors while an intalled filter does this for other one(who installed the filter) ... so you can insatll even the delegate object as event filter for table view, but then it must be checked to which object was sent the Tab key ... perhaps is better to use other QObject class to do filtering
hope this helps you :)
Cheers!
-
Thanks. With all your suggestions I have this working the way I wanted now. There's still place for some refinement here and there, but I'll worry about that at a later stage :)