Subclassing QStandardItemModel to avoid QAbstractItemModel - qt

I'm implementing a Model/View for a tree like structure, and I've decided to try the QStandardItemModel on which I want to wrap on it a specific class, (which I call here "appSpecificClass").
Basically, I want part of that class (like names, or some data), to be shown in the model, and when I change the model (in edit role, or drag and drop), I want that to have consequences on the appSpecificClass (which is, when I change a name that is showing on the model, the name on the object associated with the model's item of the appSpecificClass also changes).
So, I started from subclassing the QStandardItem by a appSpecificItem, which only has a pointer to the appSpecificClass. When I construct the appSpecificItem, the text and icons are called from appSpecificClass and everything works fine.
However, when change data from appSpecificItem, naturally it does not change the appSpecificClass, because so far I didn't found any way of interacting with the appSpecificItem's pointer via overloading a virtual function (or else)
Does anyone knows how to do this/if this is possible? What can I do such that if for instance the signal
QStandardItemModel::itemChanged ( QStandardItem * item )
is emitted, I can change a the appSpecificItem's pointer.
If not, is there any good tutorial about implementing a Model from scratch? I've tried myself some, but it is not an easy task. Ideally I would like a QStandardItemModel like model, but a little more abstraction on it (such that I can put my appSpecificClass on it).

Related

How to forward signals in a QAbstractItemModel wrapper model

I intend to create my own item model, derived from QAbstractItemModel. The model does not contain data but wraps some data repository. That repository emits signals after item(s) are insert, removed, renamed, etc.
Whenever something changes in the repository, my item model needs to forward those signals.
However the repository has standalone signals like void itemRemoved(int index); while QAbstractItemModel has begin/end pairs of protected functions (and signals) like beginInsertRows() and endInsertRows().
How should I handle this? E.g. I could connect a slot like the following to the repository's itemRemoved() signal:
void RepositoryItemRemoved(int i)
{
beginInsertRows(QModelIndex(), i, i);
endInsertRows();
}
Based on the above example: Is it valid to call beginInsertRows() / endInsertRows() sequently after a row has been inserted in the repository?
I've had a similar scenario, where the data is in a different object, and the model is just a wrapper, and only created if that data set is displayed in a view. I used a pointer to the model object in the data object, checking if it is null on insert operations, and if not call beginInsertRows() and endInsertRows() through it. Naturally, since those are protected, the data class would have to be declared a friend to the model class.
The documentation stresses that it is important to call beginInsertRows() before any data is inserted:
When reimplementing insertRows() in a subclass, you must call this
function before inserting data into the model's underlying data store...
...Otherwise, the views may end up in an invalid state.
You should test with a view, or alternatively, examine the actual implementation in the source.
I've had somewhat similar scenario as well, only in my case the Qt model wrapped the underlying model a little more literally: the underlying model contained sufficiently more data than the view had to know about. So I let the Qt model to contain its own list of small pieces of each underlying model's data item for the view/delegate to deal with. So the slots processing the updates from the underlying model looked like this:
void RepositoryItemRemoved(int i)
{
beginRemoveRows(QModelIndex(), i, i);
removeModelItem(i);
endRemoveRows();
}
Such design solved the problem of view's invalid state although it may be impractical for the use cases in which letting the Qt model contain its own items list would mean duplicating the sufficient amount of data being worked with.

How to reset a user-defined tree model in Qt

I have finally implemented my own tree model (inherited from QAbstractItemModel) for a QTreeView.
A blueprint of what I did can be found here:
http://www.trinitydesktop.org/docs/qt4/itemviews-simpletreemodel.html
So, I have:
the user-defined tree items, which are pure C++ (no Qt) and these are wrapped by
the TreeModel class which is inherited from QAbstractItemModel (like in the example link above).
I now have a generated tree hierarchy of tree items from (1.). This hierarchy has thousands of items, and I want to insert this hierarchy into my existing model at runtime.
How do I do that?
(All I have is the root-node to the c++ tree hierarchy as TreeItem pointer and a QModelIndex of the existing model where the "new sub-tree" has to be inserted)
I found modelAboutToBeReset(), modelReset(), and endResetModel() from here: http://qt-project.org/doc/qt-4.8/qabstractitemmodel.html#beginResetModel
But I don't know if these are the right functions, and, if they are, how to use them.
Any ideas?
You should call modelAboutToBeReset() before removing real items from your model. This call will "freeze" all views from requesting any data. After removing all real items you should call endResetModel() - it will unfreeze data requesting and force all connected views to update it content.
This is what i did:
void
TreeModel::addNewSubTreeToModel( TreeNode* t_rootOfNewTree, TreeNode* t_addNewSubTreeAsChildOfThisItem )
{
beginResetModel();
t_rootOfNewTree->setParent(t_addNewSubTreeAsChildOfThisItem);
t_addNewSubTreeAsChildOfThisItem->addChild(t_rootOfNewTree);
endResetModel();
}

Accessing methods from another class inside a QWidget

I have a class/ QDialog (let's call it "Frame") that contains other classes. This is what it looks like:
In the above screenshot, everything inside the green rectangle is actually a separate class/custom QWidget (let's call it "Page3" since it is the third item in the list) placed inside a QStackedWidget while everything outside the rectangle is part of Frame. Everything inside the rectangle is therefore separate from Frame even though it appears to be part of the same form. Clicking the Overview and SQLite Journal objects causes a separate page to load inside the QStackedWidget. All of these classes must be able to communicate with each other.
The problem is, I’m not sure how to access Frame's public methods or variables from inside Page3. See, one of the functions of Page3 is to unlock the OK button in Frame when the contents of the two password fields (Password & Repeat) match. To do this, Page3 needs to call the method that unlocks the buttons in Frame. I need to communicate with the currently-running instance of Frame instead of creating a new copy so instantiating Frame from inside Page3 doesn't work. I can't use parent() either because that simply refers to the QStackedWidget inside Frame instead of Frame itself.
I'd appreciate it if someone could tell me how to do this.
I prefer to do this kind of thing (communication between a child widget and its parent) using signals and slots. Why? Because if the child depends explicitly on the parent, you end up with a circular dependency, and it's harder to change your design in the future.
The simplest solution is to have Page3 emit an "unlockOk" signal which is connected to a slot in Frame which does the actual "unlocking" of the button. Frame could connect the signal and slot in its constructor, or wherever else it's actually instantiating the Page3 object.
If you want to take it a step further, you could make the signals more generic; for example, signals called "inputValid" (which would be emitted when the password fields match) and "inputInvalid", which would be connected to "unlockOk" and "lockOk" slots. The reason for doing this is that you could re-use the signals in other parts of your application if you need to, and their names clearly indicate what they're communicating.

Qt Get signal-slot connection information from a widget

I have a feeling this isn't possible with the current API, but I have to ask. Is it possible to query a particular QObject's signal or slot name (from the metaObject) and retrieve all the QObjects and their slot or signals names that are connected to it?
I'm doing this because, in effect, I have a large number of layouts that contain an identical arrangement of widgets, for each layout there an object and each of the layout's widgets control the various properties of it. I want to keep one layout, and connect it's widgets' signal/slots to all the other objects in the same pattern, but in order to do this I need to 'record' all the signal-slot data.
Is it possible?
There is an interesting file in Qt - %Qtdir%/src/corelib/kernel/qobject_p.h, it contains class QObjectPrivate, used by Qt internally.
Use
static QObjectPrivate *get(QObject *o) function to get QObjectPrivate member for your widgets, and try to call its interesting members like
QObjectList receiverList(const char *signal) const; or QObjectList senderList() const;. File is totally undocumented, but it seems to contain exactly what you need...

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.

Resources