Uncheck a QPushButton with autoExclusive - qt

Ubuntu 22.04, Qt 6.4.0.
I have some QPushButtons that belong to the same parent.
I set all of them with checkable and autoExclusive properties to true.
The doc about the latter states:
If auto-exclusivity is enabled, checkable buttons that belong to the
same parent widget behave as if they were part of the same exclusive
button group. In an exclusive button group, only one button can be
checked at any time;
Emphasis is mine.
From what I understand, they say that only one button "can" be checked. This means that more than one button can never be checked. But it allows to no buttons checked. Otherwise I was expecting something like: "only one button must be checked at any time".
Anyway, my problem is I cannot uncheck a button after it was checked.
It remains checked no matter I click on it.
I also tried programmatically:
ui->myCheckableButton->setChecked(false);
no way.
Disabling the autoExclusive property I can disable them, but of course is not the intended behavior.
What am I missing here?
How to uncheck a button when autoExclusive property is active?

Sadly, you are experiencing the intended behavior of Qt.
The source code for QAbstractButton::setChecked contains lines that makes it clear a button belonging to an exclusive group cannot be unchecked (unless it is the only button in that group).
if (!checked && d->queryCheckedButton() == this) {
// the checked button of an exclusive or autoexclusive group cannot be unchecked
#if QT_CONFIG(buttongroup)
if (d->group ? d->group->d_func()->exclusive : d->autoExclusive)
return;
if (d->group)
d->group->d_func()->detectCheckedButton();
#else
if (d->autoExclusive)
return;
#endif
}
d->queryCheckedButton() returns nullptr if only 1 button belongs to the group. The rest is straight forward:
If Qt was compiled to use button groups and the button group is exclusive, do not uncheck the button (return; instead).
Otherwise, if the button itself is autoexclusive, do not uncheck it.
I checked the exclusive property of QButtonGroup, the description makes the behavior clear:In an exclusive group, the user cannot uncheck the currently checked button by clicking on it; instead, another button in the group must be clicked to set the new checked button for that group..
It is unfortunate the description for QAbstractButton does not contain that sentence too, although I will say it is not incorrect since 0 buttons may initially be checked (then, when 1 is checked, it will never allow to uncheck everything, which is the missing part).
Your modified sentence using must would have a meaning in the lines of:When using the QAbstractButton class, you must make sure not to allow more than 1 button to be checked.There are several instances of sentences built this way and AFAICT, all of them describe things you must do for your implementation to work.
The good news in all of that is that your solution of disabling the autoExclusive property seems no have no impact whatsoever on the signals emitted by the button.You may for instance create a subclass of QPushButton (if that is what you are using), remove the autoExclusive property, first thing in the mousePressEvent and re-enable it last thing in the mouseReleaseEvent.
Alternatively, you can make your button non autoexclusive and connect signals when it is toggled (to true) to unckeck the other buttons.This is probably how I would do it.

Related

Icon, Resource, or Font for the Check Mark of a QAction

When a checkable QAction (without an icon) is added to a QMenu, a check mark shows when it's checked. I've subclassed QAction to provide a tri-state system with checked, unchecked, and partially checked, but I would like my checked indicator to exactly match the built-in check mark, if possible. I just can't figured out where that built-in check mark comes from so that I can re-use or duplicate it. The check mark resource that I've used isn't very close, and there are other checked actions on the menu. I've looked at the QAction and QMenu code, but I don't see where this is drawn.
What I've experimented with in my custom icons is to use a typical check mark for the checked state, and use a blurred check mark for the Qt::PartiallyChecked state. I like the effect, but I would like my base check mark to be the same as the built-in one. Can anyone point me in the right direction?
Incidentally, I had asked a separate question about how to do tri-state actions, and while that answer--using a QWidgetAction--worked well technically, I didn't care for the appearance and am going this route instead (so please don't refer me to that).

QAction Qt::Key_Return shortcut is triggered by user entering values in GUI

I have an issue with associating a keypress to a QAction. I'm mapping Enter to an action like this:
myAction->setShortcut(Qt::Key_Return);
The problem is that the QAction is also triggered when I type a value in a QSpinBox and then press Enter. I was expecting the spinbox to consume the event (so it's not caught by the mainwindow) but it's not working like that.
I've seen that I can add a context to my shortcut, I tried all values but this does not solve my issue.
#Matthew It is indeed rejecting the event:
case Qt::Key_Enter:
case Qt::Key_Return:
d->edit->d_func()->control->clearUndo();
d->interpret(d->keyboardTracking ? AlwaysEmit : EmitIfChanged);
selectAll();
event->ignore();
emit editingFinished();
return;
I find this behavior quite strange.
From my knowledge the QSpinBox default behavior does wait until you press return to accept the value. Technically the value is set/accepted as soon as you change it.
If you're looking to change that behaviour, i.e. when you hit return on your QSpinBox you the value is actually set/accepted, then you will most likely have to handle the associated key press event yourself on the QSpinBox and mark it as accepted (i.e. event->accepted() ).
Otherwise, this sounds like it is working as originally designed although not as you want.
I have exactly the same problem, only in PyQt and with QLineEdit
It may be that QActions are always considered menu actions, or global, and have a higher priority than Widget Default keys.
My "solution" was to create a global list of my actions with important keys (Return, Tab, Backspace etc.) as shortcuts.
Then I hook into QLineEdit focusInEvent and focusOutEvent. Each time the event happens I loop over the actions-list and deactivate them all on focusIn and activate them again on focusOut. Then I give the event to the original focus event handlers.
I really hope there is a better solution.

QTableView: Best way to change activation-trigger to double-click

In my application, I have one tableview of items, and a side-panel "preview":ing the latest selected item.
I want clicking on an item to change the selection, and double-clicking to cause a "run"-action to be performed. More specifically, I want the "run"-action (including key-navigation and pressing enter) to be bound to the "activation" of the item in the table-row.
My problem is; single-clicks does not only change the selection, but fires the "activated" signal on the item. I would like to tweak it such that:
Navigation Keys, Single Mouse Click: Selection-change, update preview-panel
Enter Key, Double Mouse Click: Activate/run/open action triggered.
Is there a nice clean way to do it, or are overriding the onclick/doubleclick events my best option? Or is there some other tabular list-widget better suiting my needs?
I would connect the slot for the preview action to the currentChanged() signal of the table view's selectionModel(). This covers single clicks and key navigation.
Then there's two options for the double clicks and Enter key presses:
Subclass your tableview, override doubleClickEvent() and keyPressEvent() and fire your custom signal in there, with maybe the model index or something else as an argument. Then just connect your run method to your own signal as you have full control over when it is fired.
If you don't want to subclass, you can use the installEventFilter() mechanism.
Either I'm getting your approach wrong or I'm too tired, but if you want to trigger a run event you should avoid the activated signal completely. Set the signal slot mechanism so that your double click and Enter key press event trigger your run() function, and then the single click/nav buttons should trigger the 'activated' slot which will return your index in the tableview.
I'm pretty certain Qt wants you to be explicit about which signal points to which slot or it'll ignore it or point to a default.

Changing a dialog button's text at runtime in Qt

I have a button named 'Start', and when it's clicked, a lengthy operation starts and I want it to change to a button named 'Stop'. The behavior when you click it obviously changes as well. What is the best way to implement this - by changing the button's text and reconnecting the 'clicked()' signal to a different slot, or by having two buttons and then hiding 'Start' and showing 'Stop'.
Both of your options work. A simple third alternative is to change the button text (changing the icon would be a good idea too), and saving the "state" (playing/not playing) somewhere. In your connected slot, just do the Right Thing depending on current state. (That way you don't have to reconnect anything).
Another option is to use the push button with setCheckable(true) set. This way the button acts more like a toggle-button (stays depressed when first clicked, raises back with second click), and combining that with your dynamic text/icon change.
If you use this, you should use the toggled(bool) signal rather than the clicked() one. The slot argument tells you whether the button is "active" or not. (This can also be queried with isChecked().)

QWidget signal sent when menu action selected - how do I prevent this?

In more than one of my Qt applications I've noticed that, whenever the menu bar is clicked, the last signal to have been sent from a widget within the GUI is re-sent before the menu action is invoked. Most of the time this doesn't matter; but on some occasions it matters very much.
In a few cases where the widget's signal is connected to one of its own slots, it's straightforward to begin the slot with a
if (hasFocus())
{
// ...
}
...block so that such spurious signals, not generated by the user actually clicking on the widget, can be ignored.
However, I've recently identified that this behaviour is responsible for several related bugs where the spurious signals are passed on through several layers of the program before being acted upon, so simply checking whether a particular widget has focus is not trivial to implement.
My question, therefore, is:
why on earth does clicking on a menu item cause a signal to be emitted from a widget elsewhere on the screen? I can't find this behaviour documented anywhere?
how do I stop it?
Many thanks,
Stephen.
As you deduced yourself, the QLineEdit::editingFinished signal can be emitted twice if the user press enter and the QLineEdit loses the focus later. An old bug was created but behaviour has not been changed : https://bugreports.qt.io/browse/QTBUG-40.
Your solution is fine : overload QLineEdit and check if the value has changed before triggering the signal. You can use the flag QLineEdit::isModified for the check : it has a default value of false and is changed to true whenever the user changes the line edit's contents. It has to be manually reset to false when you emit the signal.
You can also do that check in the receiver object, just checking if the value has a meaning (is different from the last recorded value for example) and process it or not.
Therefore the polluting signals would not be a problem. It is in my opinion the cleanest solution, then you have a robust model. Because the user could also hit enter a few times with the same text value and you have to handle this anyway.
You can also use QLineEdit::returnPressed but this requires the user to always press enter to validate a value. Not always intuitive but forces the user to explicitly validate the inputs.

Resources