Updating a model in the backend in qt - qt

I am new to Qt.
I have the following situation.
Can someone suggest me how to tackle it.Thanks
I have a custom made model which i should populate in the backend.ie when the program starts there is no guarantee there will be elements in the model.But these elements may be added later in the backend.So if i add a new element in the data structure in the model how do i let the view know the change.And this is an insertion and not a change in the data.
Also if i want the view i display to be sorted how should i notify the view

To update any connected views of an insertion, call beginInsertRows(const QModelIndex& parent, int first, int last) before you modify the model, and endInsertRows() after. Read the docs.
To sort views you need to use a QAbstractProxyModel, it is highly configurable so read the docs before asking more specific questions about it.

Related

Updating sort-order indicator in QTableView/QHeaderView when model is sorted

I want to know how to ensure the sort indicator in the horizontal header of a QTableView is updated when a programmatic sort is performed on the model.
Here's the problem:
QStandardItemModel model(3,1);
QTableView view;
view.setModel( &model );
// Populate the model ensuring it is not in a sorted order
for( int row = 0; row < model.rowCount(); ++row )
{
model.setItem( row , 0 ,
new QStandardItem(QString::number((row+1)%model.rowCount())));
}
view.setSortingEnabled( true );
// At this point everything is consistent since enabling the sorting
// triggers a sort that matches the indicator in the horizontalHeader (see A)
model.sort( 0 , Qt::AscendingOrder );
// However at this point the sort order has been reversed but the
// header's sort indicator remains unchanged (see B)
A: B:
As you can see the sort indicator remains the same and therefore is inconsistent with the actual sort order.
In my application I have two views that interact with the same model and sorting can be triggered from either of them. I don't see anything in QAbstractItemModel that signals when a sort has been performed. It seems like QHeaderView/TableView assume that they are the only thing that can trigger a sort.
Does Qt provide facilities for coping with this that I'm missing? If not, what's the best way of keeping the sort indicator up-to-date without breaking the encapsulation of the multiple views on the model too much?
One of the ItemDataRole enumerators available since Qt 4.8 is InitialSortOrderRole.
http://qt-project.org/doc/qt-4.8/qt.html#ItemDataRole-enum
It should therefore be possible to transmit sort order information through the QAbstractItemModel::headerData method.
I've tried this however and found that QTableView and QHeaderView do not seem to update in response to changes in this headerData role. A customised header view would appear to be necessary...
It might be worth it because passing this information via the model allows any number of views to synchronise without any external agent having to track all the views in existence so that it can distribute notifications. It would also work seamlessly through model proxy stacks such as those built with QSortFilterModelProxy.
The solution I've come up with to avoid breaking encapsulation too much is
to have a signal on each view (on QTableView the sortIndicatorChanged signal suffices and on my custom view I have added a similar signal).
the manager of views connects to these signals
when any view emits such a signal the manager of views calls a slot on all the other views so that they can synchronise their sort indicators
I still feel like I might be missing something - surely this is a common problem? It seems to me that QAbstractItemModel should have a way of transmitting sort-order information to views...

How to delete an item from model view?

I am using QTreeView and QAbstractItemModel to establish view whose data also comes from a tree structure. But when I delete a node from a tree structure(data source), then I found that the model view can't automatically adjust itself, it also use the invalid pointer which I don't know it points to which memory block. I don't know how to refresh or what I need to do to fix this problem.
To delete data from the model, use beginRemoveRows() and endRemoveRows().
beginRemoveRows tells the model that you will now change the underlying data structure.
Then change the structure and call endRemoveRows when done. endRemoveRows will then trigger the notifications to update the views:
beginRemoveRows(QModelIndex(), 0, 0);
m_topLevelNodes.remove(0);
endRemoveRows();
This removes the first top-level row (and its children), assuming that the underlying structure in your model keeps the top-level tree items in a container named m_topLevelNodes.

How can I programmatically commit data from QTableWidget, which contain some items in editing state?

How can I autocommit data from QTableWidget, that is in editing state, when I fire some command?
Assume, that there is some grid and data in it (editable thorough delegate that fires QComboBox editor). So, one starting to select option in combo, but do not finish editing, then hit some button, that executes action, that uses data from that combo, but new choise is not committed yet :\
How can I programmatically finish editing in table?
I mean some not strict 'loop all items and finish editing' way, that I consider as bad and ugly.
OOPS: worked too much, so, haven't realised, that there could be only one pending editor at time. Question is still here.
There is a protected slot named "commitData" in the tableWidget. You can inherit from tableWidget, then add your own public method (or slot) and send a signal (or simply call commitData method) from there.
There is one problem. You'll need to provide the editor object, but tableWidget gives you no way to get the pointer you need.
If you're using your own createEditor method, you can save the pointer to the editor somewhere, where your method can get it. It's a hack, but it's the only way i know.
The current editor does not seem to be accessible from outside of the view, but its content is committed when the current model index changes. So a simple way to force a commit seems to be to call
table->setCurrentIndex (QModelIndex ())
plus restoring your previous current index afterwards if the widget is not discarded yet.
This is quite an old question but it still came up quite high on Google so just in case anyone else needs the answer QTableView has a protected method
void currentChanged(const QModelIndex &current, const QModelIndex &previous)
which causes the data to be commited and QTableWidget is built on QTableView so that should still work. I found this info on the Qt Forum.

In Qt, create a table with an blank editable row

This is a Qt-specific question.
It's convenient to be able to add new data to a table by typing content into a blank row at the bottom of a table. When the data is committed, a new blank row is added to the table.
Has anyone found a way of implementing this in a generic way, that fits into Qt's model-view programming architecture? My closest attempt involves creating a proxy model, such that the rowCount() returned from the model is always one greater than the source model.
QAbstractTableModel* sourceModel ; // Data is stored here
QBlankRowModel* model ; // Proxy model that adds one to rowCount()
QTableView* view ; // View
view->setModel( model ) ;
model->setSourceModel( sourceModel ) ;
Any suggestions are welcome. Thanks.
From a design-perspective, this should be part of the view, not the model. Therefore, I suggest implementing a view with the functionality and leave the model unchanged. KOffice Kexi does just this with kexitableview (screenshot, documentation). Maybe you want to use some of their code.
BTW, you might still be able to use your hack and combine it with my suggestion by putting it inside a new table view implementation YourTableView:
QBlankRowModel re-implements the
QAbstractTableModel
interface. It returns sourceModel.rowCount()+1 as the QBlankRowModel::rowCount().
It returns a QVariant() if the n+1th row is requested in QBlankRowModel::data().
All the rest within QBlankRowModel is forwarded to the sourceModel (with editing
the n+1th row in QBlankRowModel buffered and replaced with inserting into the
sourceModel when finished).
The new YourTableView inherits from
QTableView and wraps the sourceModel within
YourTableView::setModel(), calling
QTableView::setModel(QBlankRowModel(sourceModel)).
Thereby, your hack is localized at one point.
Your solutions seems a little hackish. Your problem is not only additions, it's also editions. What happens when your user edits a row, the typed data goes directly to your "data layer" even before the user commits his edition?
A better solution would be to restrict the role of your sourceModel. Rather than being a "direct" representation of your data, it should be a "buffered" representation of it. When the sourceModel is created, you make a copy of your data in some kind of Row() instances. The sourceModel, having its own copy of the data can then freely play around, perform editions and additions, and only commit the data to your model layer when the user commits his edits.
If you want a PyQt example of such a table, you can look at the source of a project of mine:
http://hg.hardcoded.net/moneyguru/
You might have to dig around to actually find the "buffering" logic because it's not in the PyQt code itself, but rather the "cross-platform" part of the code:
http://hg.hardcoded.net/moneyguru/src/tip/core/gui/table.py
This logic is then used in my QAbstractItemModel subclass:
http://hg.hardcoded.net/moneyguru/src/tip/qt/controller/table.py
Sounds like a reasonable solution, as it should work for any model that you might want as the actual table model, ie. SqlTableModel or just a plain one. As long as you add the row when the user is done editing and take care not to add the row when the user did not add any data.

Removing rows from QTreeWidget (qt programming)

what's the best way to remove a row (QTreeWidgetItem) from a QTreeWidget?
The QTreeWidget content has been set by:
myQTreeWidget->insertTopLevelItems(0, items); // items = QList<QTreeWidgetItem*>
then I remove an item from my QList "items" and I try to clear/reset the QTreeWidget
packList->clear();
packList->insertTopLevelItems(0, items);
but my app crashes here!
Suggestions?
Your problem is that calling packList->clear() deletes the tree widget items contained by the tree. (See the documentation about QTreeWidget::clear(), which includes a note about the items being removed from the tree before deleting.) You'll either need to find a way to remove the items, or not maintain a list of them separately from the tree.
On a slightly-related note, if you are trying to keep track of other data along with the tree, I'd recommend you try to use the models paradigm. In non-trivial cases, it has usually been worth my while to convert to that technique, rather than using the widgets/items.
From what this documentation says, you should be able to do it with:
packList->takeTopLevelItem(index);
Which returns removes and returns the item at the supplied index.

Resources