setSelectionModel in QListWidget stops emission of itemSelectionChanged signal - c++

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

Related

Is a QTreeView::sortByColumn() slot in QTreeView of Qt 5?

In Qt docs http://doc.qt.io/qt-5/model-view-programming.html#model-subclassing-reference, it's said that, If your model is sortable, i.e, if it reimplements the QAbstractItemModel::sort() function, both QTableView and QTreeViewprovide an API that allows you to sort your model data programmatically. In addition, you can enable interactive sorting (i.e. allowing the users to sort the data by clicking the view's headers), by connecting the QHeaderView::sortIndicatorChanged() signal to the QTableView::sortByColumn() slot or the QTreeView::sortByColumn() slot, respectively. However, theQTreeView::sortByColumn() is not a slot in QTreeView in Qt 5.4. Is there any thing wrong?The source code in Src/qtbase/src/widgets/itemviews/qtreeview.h shows that.
QTreeView::sortByColumn wasn't a slot before: http://qt-project.org/doc/qt-4.8-snapshot/qtreeview.html#sortByColumn
And QTreeView::sortByColumn isn't a slot now: http://doc.qt.io/qt-5/qtreeview.html#sortByColumn
There is nothing "wrong" with this implementation. The behavior of Qt 5 QTreeView will work exactly like a Qt 4 QTreeView with respect to sortByColumn.

How to get UI objects to only appear in dialog AFTER button is clicked

I have a dialog that initially has several buttons, let's call them Write, View, OK, and Cancel.
The way it should to is to have the dialog upon creation only have those three buttons and nothing more.
When the Write button is cancelled, it's supposed to create a QLineEdit object in the window above the buttons where the user can enter a new string,which when OK is then clicked will be added to an external QStringList.
When View is clicked, LineEdit should go away (if it's up) and a QListView to come up instead to view everything in that list.
The problem is, I know how to use hide() to get objects that are already in the dialog to NOT appear.
but I am having trouble figuring out how to get an object not currently on the table to appear. I'm new to using Qt so it may be something easy I'm just accidentally overlooking (in fact I hope it is).
Could anyone please offer advice? Thanks!
Just create the items normally and then set:
ui->control->setVisible(false);
after you have created the UI (after ui->setupUi(this);) possibly in the constructor (in case you use code generated by Qt Creator).
And when you need them:
ui->control->setVisible(true);
Doc for this:
http://qt-project.org/doc/qt-4.8/qwidget.html#visible-prop
when using a QListView you should also have a QListModel that provides the data to it, if you only have QStrings then a QStringListModel is premade for you to use
to add a row you can do:
int rows = model->rowCount();
model->addRow(rows,1);
QModelIndex index = model->index(rows,0);
model->setData(index, string);

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.

When will cellChanged signal be fired in QTableWidget?

I am using a QTableWidget to display and edit a data matrix. For validation purpose, I used the QLineEdit as items in this table. As following,
pTable=new QTableWidget(N,N,this);
pItem=new QLineEdit();
pItem->setText(tr("%1").arg(pInfra->adjacencyM(i,j)));
rx=new QRegExp("0|1");
validatorRegexp=new QRegExpValidator(*rx,0);
pItem->setValidator(validatorRegexp);
pTable->setCellWidget(i,j,pItem);
Since I want to know if data in certain cell has been changed, so I tried cellChanged(int, int) signal, and connect it with my own slot cellEdited(int,int), like this
connect(pTable,SIGNAL(cellChanged(int,int)),this, SLOT(cellEdited(int,int)));
But, when I edit QLineEdit in the cell, I can not catch this signal. When will this signal be fired? Or can I do this using another signal or in some other way?
Thanks!
The problem is that the cellChanged() signal is emittet only if the table model is issued the setData() method, which normally comes from the QLineEdit of the delegate. Since You have your own mechanism by setting the cell widget the setData() method of the model will never be called. Which means you'll have to connect to the textChanged() or the textEdited() signal of the QLineEdit object you put in the cells.
Another valid option is the approach mentioned by beduin in the comment.
Also possible: You could subclass the used delegate and make it create QLineEdit objects with your validator. Which would be the cleanest approach since you don't interfere with the model/view architecture and can rely on the signals the table object is sending.
Best regards
D
Not aware about causes of this problem. Considering another ways. You can catch QLineEdit signal textChanged and use QSignalMapper to bind signal, fired by each QLineEdit to particular cell number. Maybe itsn't the best qway to do that, but you can use it in case this problem won't be solved.

Qt/C++: Signal for when a QListWidgetItem is checked?

In my form I have a QListWidget which contains checkable QListWidgetItems. I'm looking for a way to capture the event of a QListWidgetItem being checked/unchecked. I don't see any such signal existing for this but maybe I'm wrong. What I'm currently doing is using the QListWidget::itemClicked() signal and checking the checkState of the QListWidgetItem, but this isn't what I want because this event happens any time the item is clicked, not just went the checkmark is toggled. Can anyone give some assistance? Thanks!
Apparently no such signal is provided, your best bet is to use QListWidget::itemChanged(QListWidgetItem* item) , and scan the resulting item->checkState(). This should be a slight improvement over using itemClicked
An extra option is to use your own QAbstractListModel with a QListView. At first this does add some extra code as you need to add your own management code . But you do get a lower level access. Basically because the QListView will ask your model what to do. It will also relay input back to your listmodel where you can hook into it.
Alternatively you could subclass QStandardItemModel and catch certain edits related to changing the checkbox.