Can connectSlotsByName connect to selection model changes? - qt

In my main window (QMainWindow) I have a QTableView (named commandsTableView). Now I want to react on its selection changes.
I made a slot and connected it manually to ui.commandsTableView->selectionModel(). All works fine.
But then I thought: why not use auto-connection (especially that there will be more connections to be done)? At least it will add more force to consistent naming rules.
Yet I wasn't able to find proper name syntax. I tried:
on_commandsTableView_selectionModel_selectionChanged,
on_commandsTableViewSelectionModel_selectionChanged,
on_commandsTableView_selectionChanged,
on_commandsTableView___selectionChanged
but neither worked. In all cases there is there is a message on output when running the app (with corresponding slot name, here only first given as an example):
QMetaObject::connectSlotsByName: No matching signal for on_commandsTableView_selectionModel_selectionChanged(QItemSelection,QItemSelection)
(Why there are no assertions in response for connection errors - that I cannot understand. I lost much time wondering what is wrong before I spotted those - and alike - messages on output.)
The object returned by ui.commandsTableView->selectionModel() has an empty name. But setting it to selectionModel prior to making a call to connectSlotsByName doesn't help either.

According to the documentation connectSlotsByName() only supports signatures like
void on_<object name>_<signal name>(<signal parameters>);
According to the sources that's the only form it checks (watch how it collects a list of children, then matches parent's method names against names of the children).
Hence, to be able to use auto-connection you would have needed a named selection model, which would continue existing from the call to connectSlotsByName() onwards. Each time you change the selection model (not likely) or the model (likely) you'd have to name the selection model and auto-connect again. But alas connectSlotsByName() will duplicate all other connections as it doesn't seem to check if connections are unique, so we have to connect signals to such dynamic children as models, scenes etc manually.

I think it's
on_selectionModel_selectionChanged(const QItemSelection & selected, const QItemSelection & deselected)

Related

Why it uses d->eventFilters.prepend(obj) not append(obj) in function(QObject::installEventFilter)

Why it uses d->eventFilters.prepend(obj) not append(obj) in function(QObject::installEventFilter),i want to know why design it in such way.I just curious about it.
void QObject::installEventFilter(QObject *obj)
{
Q_D(QObject);
if (!obj)
return;
if (d->threadData != obj->d_func()->threadData) {
qWarning("QObject::installEventFilter(): Cannot filter events for objects in a different thread.");
return;
}
// clean up unused items in the list
d->eventFilters.removeAll((QObject*)0);
d->eventFilters.removeAll(obj);
d->eventFilters.prepend(obj);
}
It's done that way because the most recently installed event filter is to be processed first, i.e. it needs to be at the beginning of the filter list. The filters are invoked by traversing the list in sequential order from begin() to end().
The most recently installed filter is to be processed first because the only two simple choices are to either process it first or last. And the second choice is not useful: when you filter events, you want to decide what happens before anyone else does. Well, but then some new user's filter will go before yours, so how that can be? As follows: event filters are used to amend functionality - functionality that already exists. If you added a filter somewhere inside the existing functionality, you'd effectively be interfacing to a partially defined system, with unknown behavior. After all, even Qt's implementation uses event filters. They provide the documented behavior. By inserting your event filter last, you couldn't be sure at all what events it will see - it'd all depend on implementation details of every layer of functionality above your filter.
A system with some event filter installed is like a layer of skin on the onion - the user of that system only sees the skin, not what's inside, not the implementation. But they can add their own skin on top if they wish so, and implement new functionality that way. They can't dig into the onion, because they don't know what's in it. Of course that's a generalization: they don't know because it doesn't form an API, a contract between them and the implementation of the system. They are free to read the source code and/or reverse engineer the system, and then insert the event filter anywhere in the list they wish. After all, once you get access to QObjectPrivate, you can modify the event filter list as you wish. But then you're responsible for the behavior of not only what you added on top of the public API, but of many of the underlying layers too - and your responsibility broadens. Updating the toolkit becomes next to impossible, because you'd have to audit the code and/or verify test coverage to make sure that something somewhere in the internals didn't get broken.

What is the effect of calling QAbstractItemModel::beginInsertRows() and endInsertRows() if no insertion actually takes place?

I'm implementing drag/drop behavior in my model, which is derived from QAbstractItemModel. My code (C++) for the drop event looks something like this:
beginInsertRows(destination_index, row, row);
destination->AcquireDroppedComponent(component);
endInsertRows();
The call to AcquireDroppedComponent can fail for a number of reasons and reject the drop, in which case no new rows will be inserted in the index stored in destination_index. My question is will calling begin/endInsertRows cause problems if this happens? My limited testing on Windows 7 so far shows no undesirable behavior, but I want to be thorough and not rely on the specific behavior of one platform. I can check beforehand if the drop will succeed or not, but I'd like to avoid the extra code if I can. My question also applies for the other begin/end functions like beginRemoveRows, beginInsertColumns, etc.
Calling these methods without doing the actions you indicate breaks their contract. How the clients of your model will cope with that is essentially undefined.
I can check beforehand if the drop will succeed or not, but I'd like to avoid the extra code if I can.
That "extra" code is absolutely necessary.
I'd refactor your code to perform acquisition and model change separately:
if (destination->acquireDroppedComponent(component)) {
beginInsertRows(destination_index, row, row);
destination->insertDroppedComponent(component);
endInsertRows();
}
The acquireDroppedComponent would store the data of the dropped object without modifying the model, and return true if it was successful and the data is usable. You then would call insertDroppedComponent to perform the model change.

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...

A clean and intuitive way to implement a list of clickable strings to invoke functions?

For the Qt App I'm writing, I'd like to have a list of clickable functions, which, when clicked will allow the user to supply required input arguments.
What I'm specifically looking for is a selection of widgets which provide a clean and intuitive interface for the following tasks:
User scrolls through a list of functions for performing computations (in my case, from glm).
Once a function is found, the user clicks on the item; a popup window opens, which specifies the required input arguments (e.g., vec3, vec4, etc.).
The idea here is that the functions themselves already exist: they just need an interface, which in a nutshell, provides a pseudo interpreter to process and output their results to a GLWidget, which will update the data passed accordingly by sending it to its corresponding shader.
I've looked at QListView, and its Widget variant, but it appears to be more suited towards filesystem data, such as images or text files, though I'm not quite sure. So far, it seems to be the only thing which could be considered as realistically usable in this scenario.
Is there a recommended way to do this? I'm fairly new to Qt in general, thus my knowledge is pretty limited.
The view isn't really important in your case. You need to create/reuse a adapted model.
This model have to contain the relation between what your view displays and the action that you want to launch.
For example, if your commands are text like bash commands, you can create a view that displays "list files", "Copy files" and a model that contains the data ("list files" = 'ls -l'), ("copy files" = 'ls -l'), etc.
You can store different data (using QVariant) in a same item with different roles: Qt::DisplayRole corresponds to the data that the view displays and Qt::UserRole what you want.
So, if you only have to store a command line associated to a name, you can store the name in the item with the Qt::DisplayRole and the command line as a QString (or other) using Qt::UserRole.
See QAbstractItemModel::data() and QAbstractItemModel::setData(), for more information.

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