QSlider postion changes only when minimazing app - c++

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?

Related

Switch button couple in Qt

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.

Qt event when widget goes out of sight

Is there a way in Qt to handle situation when any widget of Window goes out of sight. I.e if a widget was in tab control and user have changed active tab, or if user just scrolls and widget goes offscreen, and also when it goes back on screen.
Is that possible to add some code to this two events?
Best if this can be done globally...
Is there a way in Qt to handle situation when any widget of Window goes out of sight. I.e if a widget was in tab control and user have changed active tab, or if user just scrolls and widget goes offscreen, and also when it goes back on screen.
The way the question asked makes one think that the widget show-hide-expose state changes need to be handled:
bool MyWidget::event(QEvent* pEvent)
{
if (pEvent->type() == QEvent::Show)
{
// event "shown"
}
else if (pEvent->type() == QEvent::Hide)
{
// event "hidden"
}
else if (pEvent->type() == QEvent::Expose)
{
// event "exposure changed"
// deal with QExposeEvent and evaluate the exposed region
// QExposeEvent* pExposeEvent = reinterpret_cast<QExposeEvent*>(pEvent);
}
return QWidget::event(pEvent);
}
Best if this can be done globally...
Event filter at the top level widget may solve that. Or you can override event() function for the top level widget but finding what exact widget was affected is another thing.
Refer to QExposeEvent description.

QKeyEvent isAutoRepeat not working?

So, I have an application where if a particular button is kept pressed it plays an audio device, when the button is released it stops the audio device. I use keyPressEvent and KeyReleaseEvent to implement this which is similar to the code below:
void MainWindow::keyPressEvent(QKeyEvent *event)
{
if(event->isAutoRepeat())
{
event->ignore();
}
else
{
if(event->key() == Qt::Key_0)
{
qDebug()<<"key_0 pressed"<<endl;
}
else
{
QWidget::keyPressEvent(event);
}
}
}
void MainWindow::keyReleaseEvent(QKeyEvent *event)
{
if(event->isAutoRepeat())
{
event->ignore();
}
else
{
if(event->key() == Qt::Key_0)
{
qDebug()<<"key_0 released"<<endl;
}
else
{
QWidget::keyReleaseEvent(event);
}
}
}
But apparently isAutoRepeat function isn't working as I can see continuous print out of key_0 pressed and key_0 released despite the fact I haven't released the 0 key after I have pressed it. Is my code wrong or something else is wrong?
Thanks.
EDIT
I think this is happening because the MainWindow loses the keyboard focus. How can I actually find out which widget has the focus? I'm actually using some widgets when Qt::Key_0 pressed, but I thought I set all those possible widgets to Qt::NoFocus, I guess it's not working.
I'm trying to know which widget has the focus by doing the following:
QWidget * wigdet = QApplication::activeWindow();
qDebug()<<wigdet->accessibleName()<<endl;
but it always prints an empty string. How can I make it print the name of the widget which has the keyboard focus?
So as I also stumbled over this issue (and grabKeyboard didn't really help), I begun digging in qtbase. It is connected to X11 via xcb, and by default, in case of repeated keys, X11 sends for each repeated key a release-event immediately followed by a key-press-event. So holding down a key results in a sequence of XCB_BUTTON_RELEASE/XCB_BUTTON_PRESS-events beeing sent to the client (try it out with xev or the source at the end of this page).
Then, qt (qtbase/src/plugins/platforms/xcb/qxcbkeyboard.cpp) tries to figure out from these events whether its an autorepeat case: when a release is received, it uses a lookahead feature to figure if its followed by a press (with timestamps close enough), and if so it assumes autorepeat.
This does not always work, at least not on all platforms. For my case (old and outworn slow laptop (Intel® Celeron(R) CPU N2830 # 2.16GHz × 2) running ubuntu 16.04), it helped to just put a usleep (500) before that check, allowing the press event following the release event to arrive... it's around line 1525 of qxcbkeyboard.cpp:
// look ahead for auto-repeat
KeyChecker checker(source->xcb_window(), code, time, state);
usleep(500); // Added, 100 is to small, 200 is ok (for me)
xcb_generic_event_t *event = connection()->checkEvent(checker);
if (event) {
...
Filed this as QTBUG-57335.
Nb: The behaviour of X can be changed by using
Display *dpy=...;
Bool result;
XkbSetDetectableAutoRepeat (dpy, true, &result);
Then it wont send this release-press-sequences in case of a hold down key, but using it would require more changes to the autorepeat-detection-logic.
Anyway solved it.
The problem was that I have a widget which is a subclass of QGLWidget which I use to show some augmented reality images from Kinect. This widget takes over the keyboard focus whenever a keyboard button is pressed.
To solve this problem, I needed to call grabKeyboard function from the MainWindow class (MainWindow is a subclass of QMainWindow), so this->grabKeyboard() is the line I needed to add when key_0 button is pressed so that MainWindow doesn't lose the keyboard focus, and then when the key is released I needed to add the line this->releaseKeyboard() to resume normal behaviour, that is, other widgets can have the keyboard focus.

QTooltip that does not depend on mouse

I currently am attempting to display a tooltip on focusOutEvent of a widget. This tooltip basically validates the data inside the Widget (QLineEdit). Now the problem is I want to display the errors or issues using a tooltip . Here is what I am doing
void MyLineEdit::focusOutEvent(QFocusEvent *e)
{
QLineEdit::focusOutEvent(e);
QToolTip::showText( this->mapToGlobal( QPoint( 0, 0 ) ), "Something got it" );
emit(focussed(false));
}
Now the problem is the QTooltip is not displayed that is probably because the mouse is moving. My question is how can I make QTooltip be displayed and keep it there until I wish to turn it off ??
A tooltip will only show up if a QHelpEvent was intercepted. While you can send one from focusOutEvent using QCoreApplication::PostEvent and subclass QWidget::event like this
bool MyLineEdit::event (QEvent *event)
{
if (event->type() == QEvent::ToolTip) {
QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
QToolTip::showText(helpEvent->globalPos(), "Something got it");
return false;
}
return QWidget::event(event);
}
, this has two undesirable consequences.
1) The event would still be triggered whenever a tooltip would be shown normally (that is, when your mouse pointer stays on the widget for a couple of seconds). You will have to implement some kind of recognition mechanism to distinguish your own help events from the rest.
2) The tooltip is only active while the respective widget is focused. So, displaying a tooltip when the focus is lost will show it only for a couple of seconds, until the next event loop is processed.
There might be a workaround for the latter, but at this point, it is probably better to leave the tooltips for their intended purpose and implement your own pseudo-tooltip which you would control directly.

How to make a context menu disappear without crashing the app?

Please take a look at this picture:
You can see a QTableView with some stupid content and a context menu in the center of it. My issue is if I click the table (no matter which button was pressed) view when that context menu is on (and I expect the context menu to disappear as it happens in Windows program and then appear back in a new place if the right button was pressed) my program immediately crash. I create it like this:
connect(tableView, SIGNAL(customContextMenuRequested(const QPoint&)),
this, SLOT(showContextMenu(const QPoint&)));
void MainWindow :: showContextMenu(const QPoint &_point)
{
QPoint pos = tableView->mapToGlobal(_point);
QModelIndex index = tableView->currentIndex();
int row = index.row();
QMenu menu;
menu.addAction("Test 1");
menu.addAction("Test 2");
QAction *action = menu.exec(pos);
QString text = action->text();
if (text == "Test 1")
qDebug("Test 1");
else
if (text == "Test 2")
qDebug("Test 2");
else
qDebug("Vzdroch");
}
I have no idea why it crashes. There's no such thing as debugger in QtCreator, i.e. it is but installing it is as complicated as launching a rocket to space. What I need is just to handle mouse clicks beyond context menu area as I normally do.
I understand that it might be really difficult for you to find out why it crashes, so I'm gonna ease my question a bit. Is there a way to make that context menu disappear when the mouse goes beyond its area? There's a signal named hovered() in Qt. It is emitted when user mouse is over a widget, so I was searching for a signal, let's call it unhovered(), emitted when user takes mouse off a widget. Unfortunately I failed to find such a signal. Is there a way to let my program know that mouse is off?
Hope I've described my issue fully.
QMenu::exec returns 0 if no menu item was selected.
You need to check action before you dereference it, otherwise you'll dereference a null pointer which leads to undefined behavior.
QAction *action = menu.exec(pos);
if (!action) {
qDebug() << "no menu selected";
} else {
QString text = action->text();
...
}