Qt QAbstractItemModel dropped data signal - c++

I have a model derived from QAbstractItemModel and I implemented Drag&Drop functionality via MIME types (via overriding of QAbstractItemModel::dropMimeData()...).
Short Version/TLDR;
My question is: Is there some signal that QAbstractItemModel emits when MIME data handling is fully complete?
Long Version
When a user reorders a model item via drag&drop, there are some adjustments I need to do in other Views. For this reason, I created a custom signal that I emit right at the end of my override of QAbstractItemModel::dropMimeData(). HOWEVER, when this signal is emited, the moved items have not yet been really moved: they have been copied, but the old items have not yet been removed; hence, I have duplicated and unordered items.

Related

setSelectionModel in QListWidget stops emission of itemSelectionChanged signal

In my QListWidget subclass, when I replace its QItemSelectionModel using setSelectionModel, the list widget stops emitting its itemSelectionChanged signal. In the real code, I'm doing this to install a subclass of QItemSelectionModel, but the problem appears even if I use just a new QItemSelectionModel.
MyListWidget::MyListWidget(QWidget* parent)
: QListWidget(parent)
{
QItemSelectionModel* oldSelectionModel = selectionModel();
QItemSelectionModel* newSelectionModel = new QItemSelectionModel(model(), oldSelectionModel->parent());
setSelectionModel(newSelectionModel);
oldSelectionModel->deleteLater();
}
In the debugger, I can see QItemSelectionModel emit its selectionChanged signal, but QListWidget never emits its corresponding itemSelectionChanged signal.
If I comment out the body of this constructor, things work fine. As before, I can see QItemSelectionModel emit its selectionChanged signal, but now that triggers QListWidget to emit itemSelectionChanged.
It looks as if my call to setSelectionModel causes the connection between QItemSelectionModel::selectionChanged and QListWidget::itemSelectionChanged to break.
By the way, I checked to see if I'm passing the wrong arguments to my new QItemSelectionModel. After I create it, model() and parent() of newSelectionModel are identical to those of oldSelectionModel. I also tried commenting out the call to deleteLater, but that didn't help.
Any help much appreciated.
As per the Qt documentation of QAbstractItemView, setSelectionModel replaces the current selection model and it won't work if setModel() is called after this.
Note that, if you call setModel() after this function, the given
selectionModel will be replaced by one created by the view.
Also, from QListWidget documentation,
QListWidget is a convenience class that provides a list view similar
to the one supplied by QListView, but with a classic item-based
interface for adding and removing items. QListWidget uses an internal
model to manage each QListWidgetItem in the list.
it looks like whenever you add items, the model gets updated / refreshed invalidating your custom selection model (just my thought).
Also, you don't need to delete the old selection model as per Qt docs
Note: It is up to the application to delete the old selection model if
it is no longer needed; i.e., if it is not being used by other views.
This will happen automatically when its parent object is deleted.
However, if it does not have a parent, or if the parent is a
long-lived object, it may be preferable to call its deleteLater()
function to explicitly delete it.
if you want that level of customisation, QListView might be a better option.
QListWidget
QAbstractItemView
Just wondering, have you tried setting custom selection model after adding all the items to the list widget?
This turned out to be a Qt bug. See my two posts on the Qt Forums and the bug report:
http://forum.qt.io/topic/63676/qlistwidget-how-to-prevent-unselect-all/2
http://forum.qt.io/topic/63674/qlistwidget-setselectionmodel-stops-emission-of-itemselectionchanged-signal
https://bugreports.qt.io/browse/QTBUG-50891

Blocking signals on disabled widgets in Qt

I have a combo which is disabled, but adding an element to it will emit the currentIndexChanged(int) signal.
I expected signals to be naturally turned off when a widget is disabled, but it's not the case. I know there is blockSignals(bool), but if there are many widgets whose signals must be "blocked when disabled", blockSignals would require a Boolean state for each widget.
How can I disable the signals sent by a widget when it is disabled (and not alter its blockSignals state)?
EDIT
To clarify: since this is a widget, user cannot interact with it when it's disabled, but some signals are emitted when altering the widget programmatically. In my case there are two interesting signals:
currentIndexChanged(int) and activated(int)
The problem in my code is that I sometimes alter the combo programmatically AND I wish it to emit a signal, and sometimes it's the user that alters the combo by interacting. That's why I am using currentIndexChanged and not activated.
In both cases, anyway, I don't want the signals to be emitted when widget is disabled.
The QComboBox signals are user interaction based from end user point of view if you only have a QComboBox and nothing else as your question seems to imply.
I simply cannot reproduce the issue. I have just made a short program where I cannot get any of the QComboBox signals emitted since I cannot simply interact with the widget.
Edit: It might be a good idea to upate your question with more context for the casual readers, but based on further clarification in comments, yes, programatically it might be the case, but then signals might be useful to process programmatically, too, with corresponding slots, so it is not a major improvement if Qt blocks them automatically.
Luckily, the feature you wish to have is already available:
bool QObject::blockSignals(bool block)
If block is true, signals emitted by this object are blocked (i.e., emitting a signal will not invoke anything connected to it). If block is false, no such blocking will occur.
The return value is the previous value of signalsBlocked().
Note that the destroyed() signal will be emitted even if the signals for this object have been blocked.
If you want to do it for many widgets, create a simple function that you call instead of myWidget->setDisabled(true);:
inline bool disableAndBlockSignals(QWidget *widget)
{
widget->setDisabled(true);
return widget->blockSignals(true);
}
If you want to disable only some of them, say, currentIndexChanged, you can use disconnect manually then.
You can disconnect signals with the QObject::disconnect(); when you want to block them and then reconnecting them when you want to unblock them.
In your case, the signals are the sole source of state information carried on to other objects. If you disable them, the other objects won't ever get any notification that the state was changed. This can cause bugs in the objects that depend on being informed of your widget's state.
There are at least two solutions:
Don't change the widget's state. You can certainly defer the update of the widget's contents until after it gets reenabled.
Create a proxy that monitors the originating widget's state, and queues up the signals (with compression) until the widget gets reenabled.
Due to those workarounds, your design may require a rework. Perhaps it'd be better if you could cope with signals from those disabled widgets. You should also evaluate whether disabling a widget doesn't break the user experience. What if the user wants to see the contents of a widget, but doesn't mean to change the current setting? A disabled widget in such a case is going too far. You can make you own, perhaps subclassed, widget, acting so that the control is not disabled, but the current element stays fixed. This could even be a separate object, applicable to any control - through judicious leverage of the user property.

Qt Signal Slot Architecture Unwanted Infinite Loop

I've problem with qt signal-slot system.
First I've created a class which is called System in Singleton pattern, so I can access it's instance where I want. System has a signal SelectionChanged.
I've a list widget and I am connecting it's itemSelectionChanged signal to my custom slot which is called onSelectionChanged. In onSelectionChanged slot, I am emitting System's SelectionChanged signal. There is no problem yet.
In my software design, a selection of object(s) can be used by many GUI widgets or custom classes and System's SelectionChanged signal can be emited by widgets other then the list widget.
So I am creating a slot called OnSystemSelectionChanged in the list widget then connect it to the System's SelectionChanged signal. The OnSystemSelectionChangedSlot is like this.
void MyListWidget::OnSystemSelectionChanged(QObject *sender)
{
if (sender == this) return;
// Then I want to get a list of selected objects and set them as selection of this widget like this:
this->SetSelection(System::Instance()->GetSelectedObjects());
}
But the problem is when I start to set the list widget's selected items, it is going to emit itemSelectionChanged signal and my onSelectionChanged slot will be called. Then the slot will emit System's SelectionChanged signal and then OnSystemSelectionChanged will be called too. It will stop through sender parameter but there is no method for setting list widget's selected items at once.
How can I figure this problem out.
I hope I did explain my problem well. Thanks in advance.
Edit: Spelling and grammer errors are corrected.
There are a few ways of dealing with this in Qt.
Idioms
Use multiple views with one underlying model. This handles propagation of changes to multiple view controls automatically and you don't need to do anything extra. You can use QDataWidgetMapper to link "plain old" widgets to the data elements in a model. I'd say that this should be the preferred way of doing things. Having an underlying model for all of your UI is a step in the direction of good software design anyway.
When propagating changes between data models, implement both a DisplayRole and an EditRole. The views will nominally modify the models using one of the roles (say, the EditRole), while you can, programmatically, modify the models using the other role (say, the DisplayRole). You handle the dataChanged signals from the model in your own slot, properly dealing with the roles, and call setData on the other models with the other role. This prevents the loops.
For controls that are not QAbstractItemViews, implement two signals: one emitted on any change, another one emitted only on changes based on keyboard/mouse input. This is the interface exposed by QAbstractButton, for example: the toggled(bool) signal is the former, the clicked() is the latter. You then only connect to the input-based signals.
Your own code must propagate programmatic changes to all the interlinked controls, since changing one control from your code won't modify the others. This should not be a problem, since well designed code should encapsulate the implementation details of UI controls from rest of the code. Your dialog/window class will thus expose its properties in a way that's not coupled to the number of controls showing a particular property.
Hackish Let's-Hope-They-Won't-Become Idioms
Use a flag inhibiting signal emission (Bartosz's answer).
Break the signal/slot connections for the duration of the change (Bartosz's answer).
Use QObject::blockSignals().
There are two possible solutions I can think of:
add a flag which makes possible to ignore particular signals:
void MyListWidget::OnSystemSelectionChanged(QObject *sender)
{
if (sender == this || inhibitSelectionChanged)
return;
this->inhibitSelectionChanged = true;
this->SetSelection(System::Instance()->GetSelectedObjects());
this->inhibitSelectionChanged = false;
}
disconnect the slot from the signal, and reconnect it after changing the selection:
void MyListWidget::OnSystemSelectionChanged(QObject *sender)
{
if (sender == this)
return;
this->disconnect(SIGNAL(SelectionChanged()));
this->SetSelection(System::Instance()->GetSelectedObjects());
this->connect(
this, SIGNAL(SelectionChanged()),
this, SLOT(OnSystemSelectionChanged(QObject*)));
}
I found my solution in QObject::blockSignals() method. It will prevent emitting signals from the list widget while I am setting selected items.
Thanks for all the answers and solutions especialy for BartoszKP's. This solution is looks like the official way of his first solution.
The problem: you've tried to cut corners and created a singleton. Not a classic case for singleton.
Signals and slots are used for notifications, each object notifies interested objects about what it did or to reflect its new state.
I'm suggesting changing the design as follows:
No singleton signal.
Each Object has its own signal and slot for a relevant event (e.g. selection change).
The application or a higher level object (that created the widgets/objects) performs the signal to slot connection. If those widgets are placed in a list, this is very simple.

How to attach a signal to a slot for an object created dynamically

I currently have multiple checkboxes that are created dynamically how can I asssign signal to them.These checkboxes are attached to a QStandardItemModel. I need to know when they are clicked.This is how i am creating checkboxex
QStandardItem* chk_all = new QStandardItem();
chk_all->setCheckable(true);
chk_all->setCheckState(Qt::Unchecked);
To attach a signal to a slot you need the address of the sender which is the object. Since the object does not exist in the ui at design time how do i gets its address. So that i could complete the connect statement
QObject::connect("what goes here" ,SIGNAL(clicked()), this, SLOT(CheckClicked())); //Tester
These checkboxes are not widgets or separate objects and the QStandardItem class doesn't derive from QObject, you can't connect each of them individually to a slot.
Since their state is stored in the model, checking or unchecking them will make the model emit the signals dataChanged(QModelIndex,QModelIndex) and itemChanged(QStandardItem*).
But these signals are also emitted for changes other than the checkbox state. You might have to store the checkbox previous state in the model as well (with QStandardItem::setData/data and a custom data role) to be able to compare it to the new state and detect a change.

How to know when all QAbstractItemModel::data() functions finished

I wrote a QAbstractItemModel based class and implemented data(const QModelIndex & ar_index, int a_role) const function. I refresh the model using:
beginInsertRows(QModelIndex(), 0, 0);
// fill model
endInsertRows();
Inside data function I update some information X needed after refreshing.
I checked that data functions are invoked after refreshing the model and debugging I verified that my X variable is properly setted.
After refreshing, I need to fill another widget with this X value. How do I know that all data refresh was finished and correctly show Xvalue? Which signal is emmited after completelly refreshing the model?
There is no standard signal to use here -- the Qt's MVC API is designed to support not only static models, but also the dynamic ones, and for these, there cannot ever be a signal telling "all done". For example, in Trojita, a Qt IMAP e-mail client, rowsInserted is emitted to indicate that a new e-mail has arrived, but this happens even before the library knows anything about the new arrivals -- not even their UIDs. Only after the remote server has responded to the library's commands with data, the dataChanged is emitted to indicate that the "data represented by the model" has changed and that the attached views shall update.
You said you're reimplementing your own QAIM from scratch. This means that you can set any contracts you see fit here. Are some of the data immutable, i.e. guaranteed to never change after they have been loaded? If so, you can use a custom role like RoleIsItemFetched and after each change to the model (that is, after modelReset, rowsInserted, rowsMoved and dataChanged) check whether your data(someIndex, RoleIsItemFetched).toBool() function returns true. If so, you have your data, if not, you have to keep waiting.
An alternative to that might be to introduce your own signal. Don't be afraid of that, the MVC API is great for presenting data to users, but if you think you need more control over what is returned and have full control over the model and the "views", go ahead and make the code do what you need.