Switch button couple in Qt - c++

I'm building a simple switch from two QPushButton. At the beginning, only one is enabled (let's say button 1) and when you click on it, it's disabled while button 2 is enabled. If you click on button 2, it's disabled and button 1 is enabled.
This is the .cpp code:
#include "SwitchButton.h"
#include "ui_SwitchButton.h"
SwitchButton::SwitchButton(QWidget * parent,
bool initialStatus,
const QString & trueText,
const QString & falseText)
: QWidget(parent), ui(new Ui::SwitchButton)
{
ui->setupUi(this);
ui->trueButton->setText(trueText);
ui->falseButton->setText(falseText);
// NOTE: Redundant, emits the corresponding signal while constructed
if (initialStatus)
on_trueButton_clicked();
else
on_falseButton_clicked();
}
SwitchButton::~SwitchButton()
{
delete ui;
}
void SwitchButton::on_trueButton_clicked()
{
ui->trueButton->setEnabled(false);
ui->falseButton->setEnabled(true);
emit changeStatus(true);
}
void SwitchButton::on_falseButton_clicked()
{
ui->falseButton->setEnabled(false);
ui->trueButton->setEnabled(true);
emit changeStatus(false);
}
Seems pretty straigthforward and it works to a certain extent. While the buttons keep consistent enabled/disabled states, when I switch really fast sometime the background color of the disabled one doesn't become darker. Here's an example:
Simply tabbing out makes the background color fix itself, but I was wondering if I overlooked something and there's a way to avoid this behaviour.
EDIT: I did a bit more testing and found out it has nothing to do with the pairing button but happens even with a single button if you double click fast enough (probably I was doing something like that without noticing).

I was able to reproduce the behaviour: Clicking the button, while the mouse stays hovered over the button sometimes leads to the animation that comes with the hovering not being finished properly.
Updating the disabled button some time after it was disabled clears the wrong animation. Adding the following line in the function that disables "pushButton" cleared the wrong animation:
std::thread([&]{ std::this_thread::sleep_for(std::chrono::milliseconds(500)); pushButton->update();}).detach();
Updating the disabled button immediately after a call to setEnabled(false); doesn't prevent this behaviour, but I wasn't able to find anything about how the mouse hover animation is implemented in the qt sources.
For a workaround, you could implement a proper delayed update of the buttons after some time, e.g. via a QTimer or similar.

Related

Call button click function from grandchild

I'm creating my first C++ wxWidgets application. I'm trying to create some kind of split button where the options are displayed in a grid. I have a custom button class which, when right-clicked on, opens a custom wxPopupTransientWindow that contains other buttons.
When I click on the buttons in the popup, I want to simulate a left click on the main button. I'm trying to achieve this through events, but I'm kinda confused.
void expandButton::mouseReleased(wxMouseEvent& evt)
{
if (pressed) {
pressed = false;
paintNow();
wxWindow* mBtn = this->GetGrandParent();
mBtn->SetLabel(this->GetLabel());
mBtn->Refresh();
wxCommandEvent event(wxEVT_BUTTON);
event.SetId(GetId());
event.SetEventObject(mBtn);
mBtn-> //make it process the event somehow?
wxPopupTransientWindow* popup = wxDynamicCast(this->GetParent(), wxPopupTransientWindow);
popup->Dismiss();
}
}
What is the best way to do this?
You should do mBtn->ProcessWindowEvent() which is a shorter synonym for mBtn->GetEventHandler()->ProcessEvent() already mentioned in the comments.
Note that, generally speaking, you're not supposed to create wxEVT_BUTTON events from your own code. In this particular case and with current (and all past) version(s) of wxWidgets it will work, but a cleaner, and guaranteed to also work with the future versions, solution would be define your own custom event and generate it instead.

How to manually show CMFCToolBarComboBoxButton sub-menu?

Standard behaviour for CMFCToolBarComboBoxButton is to have a clickable button plus a drop-down arrow for displaying a submenu. I want to show the submenu independently of where the click was made. How can I do it?
My code to create the button is, more or less, the following (it has been extracted from a larger project, so I apologize for any missing not-too-important piece of code):
// In class declaration:
CMenu m_menu;
CMFCToolBar m_toolbar;
// Where toolbar initialization takes place:
m_menu.CreateMenu();
// ... populate menu
// ID_BUTTON is the ID in the resource file for the toolbar button, 0 is the index for the button icon
CMFCToolBarMenuButton button(ID_BUTTON, m_menu.GetSafeHmenu(), 0);
m_toolbar.ReplaceButton(ID_BUTTON, button);
I've been looking around for awhile and cannot find a related answer.
The solution happened to be very straightforward, just call the OnClick function of the CMFCToolBarComboBoxButton button from its associated ON_COMMAND.
// ... message map
ON_COMMAND(ID_BUTTON, OnToolbarMenuButtonClicked)
// ...
void MyWnd::OnToolbarMenuButtonClicked()
{
const int index = m_toolbar.CommandToIndex(ID_BUTTON);
auto button = (CMFCToolBarComboBoxButton*)m_toolbar.GetButton(index);
button->OnClick(NULL, TRUE);
}
This behaviour is not documented and, contrary to what common sense told me, it doesn't create an infinite recursive call. It seems that the "main" button is still controlled by CMFCToolBarButton, while just the "arrow-button" is controlled by the CMFCToolBarComboBoxButton.
PS: obviously, and out of the scope of the question, the OnToolbarMenuButtonClicked can be used for a very different purpose, such as the default action while the sub-menu contains other less-frequent options.

QSlider postion changes only when minimazing app

I am using QSlider element in my app as a toogle switch. I took this project over from a colleage of mine who left. So far so good, except, that this switch has a black label around it. I can't really change the style and rework everything so, I am stuck with label and slider as switch.
I need to handle events from button clicked and button released. I do it like this-
bool CustomSlider::eventFilter(QObject *obj, QEvent *ev )
{
if (ev->type() == QEvent::MouseButtonRelease)
{
changeSliderValue();
}
if(ev->type() == QEvent::MouseButtonPress){
lastSaved = ui->horizontalSlider->value();
}
return QWidget::eventFilter(obj,ev);
And changeSliderValue()-
void CustomSlider::changeSliderValue()
{
int current = ui->horizontalSlider->value();
if(lastSaved==current){
if(current){
ui->horizontalSlider->setValue(0);
}else{
ui->horizontalSlider->setValue(1);
}
}
}
So, in short, if I click, it saves state of slider and when releasing it checks against saved state. I do it because QSlider has button pressed signal as well. I have put debug messages in my code and funny thing is debug messages fire. So my code works. If I call ui->horizontalSlider->value() I get the correct(changed) value But the slider is not moving. I have tried checking sequences, if it does not change value twice(it does not), I have tried calling repaint() and so far nothing works. Except if don't minimize(via minimize button) or click in different app(like web browser) and then I can see that slider moves to correct postion. I am completely confused. Has anyone encoutered something like this before?

QSystemTrayIcon activated signal: DoubleClick without Trigger

I want to show context menu on left click and run app on double click.
For this i have next code:
...
connect(this, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(slotActivated(QSystemTrayIcon::ActivationReason)));
...
void MyTray::slotActivated(ActivationReason reason)
{
if(reason==QSystemTrayIcon::DoubleClick)
startApp();
else
if(reason==QSystemTrayIcon::Trigger
|| reason==QSystemTrayIcon::MiddleClick)
contextMenu()->popup(QCursor::pos());
}
It works, but for double click case i got two slot calls - one for Trigger and only then for DoubleClick. As result context menu shown and hidding in a moment.
Is there is a way to avoid this?
Rather than using slotActivated, you need to handle the mouse events.
Whilst these aren't directly available in QSystemTrayIcon, it does allow you to install an event filter and handle the mouse events from there.

How to disable minimizing by taskbar icon click

I've stumbled across very strange behaviour during work on my program.
I've written custom changeEvent class, which allows me to hide program to SysTray on minimizing.
But when i double click on taskbar app icon, the function goes crazy. It creates 2 to 4 systray icons and on requesting window show again, it just shows main window borders without any content inside.
Here's my changeEvent code:
void MainWindow::changeEvent(QEvent *e) {
QMainWindow::changeEvent(e);
if(e->type()==QEvent::WindowStateChange)
if(isMinimized()) {
trayIcon=new QSystemTrayIcon(QIcon(":/icon/itime.ico"));
connect(trayIcon,SIGNAL(activated(QSystemTrayIcon::ActivationReason)),this,SLOT(on_show(QSystemTrayIcon::ActivationReason)));
QAction *showAction=new QAction("Pokaż",trayIcon);
connect(showAction,SIGNAL(triggered()),this,SLOT(on_show()));
QMenu *trayIconMenu=new QMenu;
trayIconMenu->addAction(showAction);
trayIcon->setContextMenu(trayIconMenu);
trayIcon->show();
this->hide();
}
}
on_show(QSystemTrayIcon::ActivatioReason) SLOT:
void MainWindow::on_show(QSystemTrayIcon::ActivationReason reason) {
if(reason) {
if(reason!=QSystemTrayIcon::DoubleClick)
return;
}
if(this->isMinimized()) {
this->raise();
this->showNormal();
this->setWindowState(Qt::WindowActive);
trayIcon->hide();
}
}
on_show() SLOT is just the same besides that first if.
Soo, I would like to know whether there is any way to disable minimizing of window by taskbar icon click.
If there's none, then maybe you have any ideas what can go wrong in here when doubleclicking on icon in taskbar?
Thanks for help!
I've managed to work around that problem by overloading closeEvent function and leaving alone changeEvent function.
So, I'm using boolean flag to distinct between closing of program by menu item and by clicking "X" button and the rest stays just the same, as posted in my earlier post with one change.
I've moved this whole block of code to window constructor in order to prevent multiple creation of trayIcon, as pointed out by Nicolas.
trayIcon=new QSystemTrayIcon(QIcon(":/icon/itime.ico"));
connect(trayIcon,SIGNAL(activated(QSystemTrayIcon::ActivationReason)),this,SLOT(on_show(QSystemTrayIcon::ActivationReason)));
QAction *showAction=new QAction("Pokaż",trayIcon);
connect(showAction,SIGNAL(triggered()),this,SLOT(on_show()));
QMenu *trayIconMenu=new QMenu;
trayIconMenu->addAction(showAction);
trayIcon->setContextMenu(trayIconMenu);
Thanks for your help!