QListWidget, how to post a question to the user before allowing change?
-
When the user makes a selection from the QListWidget I want to intercept the action and prompt the user for an action before allowing the change.
If the user answers one way then the selection is allowed, however if the user answers the other way I want to stop the selection and keep the existing selection.
Is this possible? There are two signals that are emitted when a selection is made:
currentItemChanged currentRowChangedThank you,
-
When the user makes a selection from the QListWidget I want to intercept the action and prompt the user for an action before allowing the change.
If the user answers one way then the selection is allowed, however if the user answers the other way I want to stop the selection and keep the existing selection.
Is this possible? There are two signals that are emitted when a selection is made:
currentItemChanged currentRowChangedThank you,
@SPlatten
While you wait for a more knowledgable answer: I am not offering code, but I think you would need to handle the mouse events of the widget to prevent selection when the mouse down event occurs (don't call the base class function) and then deal with setting the selection yourself, in response to mouse release events. -
@SPlatten
While you wait for a more knowledgable answer: I am not offering code, but I think you would need to handle the mouse events of the widget to prevent selection when the mouse down event occurs (don't call the base class function) and then deal with setting the selection yourself, in response to mouse release events. -
You can
connect(listWidget->selectionModel(), &QItemSelectionModel::selectionChangedsee https://doc.qt.io/qt-6/qitemselectionmodel.html#selectionChanged
Don't need to intercept events, the arguments tell you exactly what happened. Then spawn a modal dialog to prompt the user so it can't just go back to the table and make other changes unless they answer the prompt first -
When the user makes a selection from the QListWidget I want to intercept the action and prompt the user for an action before allowing the change.
If the user answers one way then the selection is allowed, however if the user answers the other way I want to stop the selection and keep the existing selection.
Is this possible? There are two signals that are emitted when a selection is made:
currentItemChanged currentRowChangedThank you,
@SPlatten said in QListWidget, how to post a question to the user before allowing change?:
intercept the action and prompt the user for an action before allowing the change.
Once you receive these signals, the selection has changed already. So it's not that simple.
@SPlatten said in QListWidget, how to post a question to the user before allowing change?:
a standard way of achieving this
To "intercept" you would have to modify the Qt source.
I would try a custom
QItemSelectionModelor work with mouseEvents as @JonB suggested. -
You can
connect(listWidget->selectionModel(), &QItemSelectionModel::selectionChangedsee https://doc.qt.io/qt-6/qitemselectionmodel.html#selectionChanged
Don't need to intercept events, the arguments tell you exactly what happened. Then spawn a modal dialog to prompt the user so it can't just go back to the table and make other changes unless they answer the prompt first -
Example:
#include <QListWidget> #include <QApplication> #include <QMessageBox> int main(int argc, char *argv[]) { QApplication app(argc,argv); QListWidget mainWid; mainWid.setSelectionMode(QAbstractItemView::ExtendedSelection); for(int i=0;i<5;++i) mainWid.addItem(QStringLiteral("Item ") + QString::number(i)); bool userSelected = true; QObject::connect(mainWid.selectionModel(),&QItemSelectionModel::selectionChanged,[&mainWid,&userSelected](const QItemSelection &selected, const QItemSelection &deselected){ if(!userSelected) return; if(QMessageBox::question(&mainWid,QStringLiteral("Are you sure?"),QStringLiteral("Are you sure you want to change the selection?")) != QMessageBox::StandardButton::Yes){ userSelected=false; mainWid.selectionModel()->select(selected,QItemSelectionModel::Deselect); mainWid.selectionModel()->select(deselected,QItemSelectionModel::Select); userSelected=true; } }); mainWid.show(); return app.exec(); } -
@SPlatten
With all due respect to @VRonin (and I do have respect, he probably knows more than I!), this is not the way I would want to do it.Be aware that his way allows the selection initially, and then reverts be deselecting and then reselecting. This could easily have side-effect consequences, you may well have slots on item selection elsewhere. You asked for:
If the user answers one way then the selection is allowed, however if the user answers the other way I want to stop the selection and keep the existing selection.
[My bold.]
For that behaviour, I like @Pl45m4's suggestion of writing your own custom selection model.... -
@SPlatten
With all due respect to @VRonin (and I do have respect, he probably knows more than I!), this is not the way I would want to do it.Be aware that his way allows the selection initially, and then reverts be deselecting and then reselecting. This could easily have side-effect consequences, you may well have slots on item selection elsewhere. You asked for:
If the user answers one way then the selection is allowed, however if the user answers the other way I want to stop the selection and keep the existing selection.
[My bold.]
For that behaviour, I like @Pl45m4's suggestion of writing your own custom selection model....@JonB , @Pl45m4 , @VRonin , I am creating an engine which will allow anyone to write applications without any knowledge of Qt or low level programming languages. The framework is created in XML, where there XML defines the threads and subscriptions to set-up.
The subscriptions are set-up to any controls that emit signals where the subscribers are slots defined in JavaScript. Forms are defined in XML and there are currently three add ons I've already developed to give low level File I/O access, XML access and Database access. Additional modules are easy to add.
In my example form I have an instance of QListWidget that allows a selection of an individual which is populated from a database table. When a selection is made the fields associated with the selection are populated in the forum. If the user changes any of the control contents and then changes the selection before confirm I want to prompt the user for confirmation. This is all going to be configurable in the XML and script.
I believe now I have all the information to complete this. Thank you for your help.
-
Example:
#include <QListWidget> #include <QApplication> #include <QMessageBox> int main(int argc, char *argv[]) { QApplication app(argc,argv); QListWidget mainWid; mainWid.setSelectionMode(QAbstractItemView::ExtendedSelection); for(int i=0;i<5;++i) mainWid.addItem(QStringLiteral("Item ") + QString::number(i)); bool userSelected = true; QObject::connect(mainWid.selectionModel(),&QItemSelectionModel::selectionChanged,[&mainWid,&userSelected](const QItemSelection &selected, const QItemSelection &deselected){ if(!userSelected) return; if(QMessageBox::question(&mainWid,QStringLiteral("Are you sure?"),QStringLiteral("Are you sure you want to change the selection?")) != QMessageBox::StandardButton::Yes){ userSelected=false; mainWid.selectionModel()->select(selected,QItemSelectionModel::Deselect); mainWid.selectionModel()->select(deselected,QItemSelectionModel::Select); userSelected=true; } }); mainWid.show(); return app.exec(); } -
Ok, the problem can be solved with a simple reimplementation of the selection model:
#include <QItemSelectionModel> class AskSelectionModel : public QItemSelectionModel{ Q_OBJECT Q_DISABLE_COPY_MOVE(AskSelectionModel) public: using QItemSelectionModel::QItemSelectionModel; public slots: void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) override{ askSelect(selection,command); } virtual void actuallySelect(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command){ QItemSelectionModel::select(selection,command); } signals: void askSelect(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command); };int main(int argc, char *argv[]) { QApplication app(argc,argv); QListWidget mainWid; AskSelectionModel* askModel = new AskSelectionModel(mainWid.model(),&mainWid); mainWid.setSelectionMode(QAbstractItemView::ExtendedSelection); mainWid.setSelectionModel(askModel); for(int i=0;i<5;++i) mainWid.addItem(QStringLiteral("Item ") + QString::number(i)); QObject::connect(askModel,&AskSelectionModel::askSelect,[&mainWid,askModel](const QItemSelection &selection, QItemSelectionModel::SelectionFlags command){ if(QMessageBox::question(&mainWid,QStringLiteral("Are you sure?"),QStringLiteral("Are you sure you want to change the selection?")) == QMessageBox::StandardButton::Yes){ askModel->actuallySelect(selection, command); } }); mainWid.show(); return app.exec(); } -
Ok, the problem can be solved with a simple reimplementation of the selection model:
#include <QItemSelectionModel> class AskSelectionModel : public QItemSelectionModel{ Q_OBJECT Q_DISABLE_COPY_MOVE(AskSelectionModel) public: using QItemSelectionModel::QItemSelectionModel; public slots: void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) override{ askSelect(selection,command); } virtual void actuallySelect(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command){ QItemSelectionModel::select(selection,command); } signals: void askSelect(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command); };int main(int argc, char *argv[]) { QApplication app(argc,argv); QListWidget mainWid; AskSelectionModel* askModel = new AskSelectionModel(mainWid.model(),&mainWid); mainWid.setSelectionMode(QAbstractItemView::ExtendedSelection); mainWid.setSelectionModel(askModel); for(int i=0;i<5;++i) mainWid.addItem(QStringLiteral("Item ") + QString::number(i)); QObject::connect(askModel,&AskSelectionModel::askSelect,[&mainWid,askModel](const QItemSelection &selection, QItemSelectionModel::SelectionFlags command){ if(QMessageBox::question(&mainWid,QStringLiteral("Are you sure?"),QStringLiteral("Are you sure you want to change the selection?")) == QMessageBox::StandardButton::Yes){ askModel->actuallySelect(selection, command); } }); mainWid.show(); return app.exec(); }@VRonin said in QListWidget, how to post a question to the user before allowing change?:
QItemSelectionModel
I know this is quite old now, I'm looking at implementing this now, in the slot:void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) override{ askSelect(selection,command); }Is the line askSelect correct? because its a signal in the class, shouldn't it read:
emit askSelect(selection, command);?
-
@VRonin said in QListWidget, how to post a question to the user before allowing change?:
QItemSelectionModel
I know this is quite old now, I'm looking at implementing this now, in the slot:void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) override{ askSelect(selection,command); }Is the line askSelect correct? because its a signal in the class, shouldn't it read:
emit askSelect(selection, command);?
@SPlatten You don't have to use emit.
emit is an empty macro and is only used to make clear to the reader of the code that a signal is emitted.
Also, trying to execute the code is faster than asking in a forum and waiting for an answer :-) -
@VRonin said in QListWidget, how to post a question to the user before allowing change?:
QItemSelectionModel
I know this is quite old now, I'm looking at implementing this now, in the slot:void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) override{ askSelect(selection,command); }Is the line askSelect correct? because its a signal in the class, shouldn't it read:
emit askSelect(selection, command);?
-
Ok, the problem can be solved with a simple reimplementation of the selection model:
#include <QItemSelectionModel> class AskSelectionModel : public QItemSelectionModel{ Q_OBJECT Q_DISABLE_COPY_MOVE(AskSelectionModel) public: using QItemSelectionModel::QItemSelectionModel; public slots: void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) override{ askSelect(selection,command); } virtual void actuallySelect(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command){ QItemSelectionModel::select(selection,command); } signals: void askSelect(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command); };int main(int argc, char *argv[]) { QApplication app(argc,argv); QListWidget mainWid; AskSelectionModel* askModel = new AskSelectionModel(mainWid.model(),&mainWid); mainWid.setSelectionMode(QAbstractItemView::ExtendedSelection); mainWid.setSelectionModel(askModel); for(int i=0;i<5;++i) mainWid.addItem(QStringLiteral("Item ") + QString::number(i)); QObject::connect(askModel,&AskSelectionModel::askSelect,[&mainWid,askModel](const QItemSelection &selection, QItemSelectionModel::SelectionFlags command){ if(QMessageBox::question(&mainWid,QStringLiteral("Are you sure?"),QStringLiteral("Are you sure you want to change the selection?")) == QMessageBox::StandardButton::Yes){ askModel->actuallySelect(selection, command); } }); mainWid.show(); return app.exec(); }@VRonin , can you help with this implementation?
//ABC = Ask Before Change QString strABC(mpobjNode->strGetAttribute (clsXMLnode::mscszAttrAskBeforeChange)); if ( strABC.isEmpty() != true ) { //Use "clsQtAskBeforeChange" to prompt user for confirmation if //changes in child nodes before allowing change clsQtAskBeforeChange* pobjABC(new clsQtAskBeforeChange(model(), this)); setSelectionMode(QAbstractItemView::ExtendedSelection); setSelectionModel(pobjABC); //Now the widget should be set-up, finalise set-up clsXMLinterface::setup(); //Connect signal clsQtListWidget* pobjThis(this); QObject::connect(pobjABC, &clsQtAskBeforeChange::pendingSelect ,[pobjABC, strABC, pobjThis](const QItemSelection& crobjSelection ,QItemSelectionModel::SelectionFlags cmdFlags) { if ( QMessageBox::question(pobjThis, QStringLiteral("Please confirm...") , strABC) == QMessageBox::StandardButton::Yes) { pobjABC->actuallySelect(crobjSelection, cmdFlags); } }); }The above is in my implementation of QListWidget constructor. On the line that contains:
QObject::connect(pobjABC, &clsQtAskBeforeChange::pendingSelectTo the right of it I have the message:
Pass a context object as 3rd connect parameter [clazy-connect-3arg-lambda]This is cryptic, I don't understand what it is telling me, have I done anything wrong because the code seems to work fine?
-
@VRonin , can you help with this implementation?
//ABC = Ask Before Change QString strABC(mpobjNode->strGetAttribute (clsXMLnode::mscszAttrAskBeforeChange)); if ( strABC.isEmpty() != true ) { //Use "clsQtAskBeforeChange" to prompt user for confirmation if //changes in child nodes before allowing change clsQtAskBeforeChange* pobjABC(new clsQtAskBeforeChange(model(), this)); setSelectionMode(QAbstractItemView::ExtendedSelection); setSelectionModel(pobjABC); //Now the widget should be set-up, finalise set-up clsXMLinterface::setup(); //Connect signal clsQtListWidget* pobjThis(this); QObject::connect(pobjABC, &clsQtAskBeforeChange::pendingSelect ,[pobjABC, strABC, pobjThis](const QItemSelection& crobjSelection ,QItemSelectionModel::SelectionFlags cmdFlags) { if ( QMessageBox::question(pobjThis, QStringLiteral("Please confirm...") , strABC) == QMessageBox::StandardButton::Yes) { pobjABC->actuallySelect(crobjSelection, cmdFlags); } }); }The above is in my implementation of QListWidget constructor. On the line that contains:
QObject::connect(pobjABC, &clsQtAskBeforeChange::pendingSelectTo the right of it I have the message:
Pass a context object as 3rd connect parameter [clazy-connect-3arg-lambda]This is cryptic, I don't understand what it is telling me, have I done anything wrong because the code seems to work fine?
@SPlatten
Although this 3-argumentconnect()works, it is more usual to pass 4 arguments, with the 3rd argument being the "context" object for the slot'sthis. That is what the warning is telling you. I believe you can get rid of that via (something like):QObject::connect(pobjABC, &clsQtAskBeforeChange::pendingSelect , pobjABC, [strABC, pobjThis](const QItemSelection& crobjSelection ,QItemSelectionModel::SelectionFlags cmdFlags) { if ( QMessageBox::question(pobjThis, QStringLiteral("Please confirm...") , strABC) == QMessageBox::StandardButton::Yes) { this->actuallySelect(crobjSelection, cmdFlags); } }); -
@SPlatten
Although this 3-argumentconnect()works, it is more usual to pass 4 arguments, with the 3rd argument being the "context" object for the slot'sthis. That is what the warning is telling you. I believe you can get rid of that via (something like):QObject::connect(pobjABC, &clsQtAskBeforeChange::pendingSelect , pobjABC, [strABC, pobjThis](const QItemSelection& crobjSelection ,QItemSelectionModel::SelectionFlags cmdFlags) { if ( QMessageBox::question(pobjThis, QStringLiteral("Please confirm...") , strABC) == QMessageBox::StandardButton::Yes) { this->actuallySelect(crobjSelection, cmdFlags); } });@JonB Since the lambda accesses
this, the third parameter should bethistoo. Otherwise the disconnect will not be done whenthisis deleted but pobjABC is still alive. -
@JonB Since the lambda accesses
this, the third parameter should bethistoo. Otherwise the disconnect will not be done whenthisis deleted but pobjABC is still alive.@Christian-Ehrlicher
I'm lost --- where does the (original) lambda accessthis? The onlythisis the one I put in for thepobjABCwhich I have made the context? In the body I replacedpobjABC->actuallySelect()bythis->actuallySelect(), for clarity; and I chosepobjABCas the context as it felt like that was the primary slot object there. (I did get this right, didn't I? The slot-context-object, mypobjABC, becomesthisin the lambda body?) My thought was to pass that as it won't be disconnected ifpobjABCdies. I admit I didn't look terribly closely, I get awfully mixed with all the OP'spobj...s :) -
@Christian-Ehrlicher
I'm lost --- where does the (original) lambda accessthis? The onlythisis the one I put in for thepobjABCwhich I have made the context? In the body I replacedpobjABC->actuallySelect()bythis->actuallySelect(), for clarity; and I chosepobjABCas the context as it felt like that was the primary slot object there. (I did get this right, didn't I? The slot-context-object, mypobjABC, becomesthisin the lambda body?) My thought was to pass that as it won't be disconnected ifpobjABCdies. I admit I didn't look terribly closely, I get awfully mixed with all the OP'spobj...s :)@JonB I only looked at your answer - the slot accesses
thisand not pobjABC so the signals scope should bethistoo :)