Long Mouse Press on QToolButton calls the released signal - qt

I have a QToolButton defined. I have written two slots for pressed and released signals.
void EyImgGalleryWindow::toolPressed()
{
if(Uptimer->timerId() == -1)
Uptimer->start();
m_CustomGalleryView->scrollUp();
}
void EyImgGalleryWindow::toolReleased()
{
Uptimer->stop();
m_CustomGalleryView->scrollUpRelease();
}
When the pressed() signal is emitted, I am starting a timer and when the released signal is emitted I am stopping the timer.
So here my implementation is when the timer exceeds 3s I am updating a variable. I have to update the variable only when the User have a long press on this QToolButton.
But here my implementation is failing in this case. When I have a long press on QToolButton, It is emitting the Pressed and Released signal on equal intervals. When a long press is there we should get only once the Released signal and Why here it is calling the Released signal multiple times. Is there anything i am doing wrong here?

You can catch long press with next event filter and QElapsedTimer:
QElapsedTimer el;//somewhere
//...
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if(obj == ui->pushButton_13 && event->type() == QEvent::MouseButtonPress)
{
el.start();
}
if(obj == ui->pushButton_13 && event->type() == QEvent::MouseButtonRelease)
{
qDebug() << el.elapsed();//in milliseconds, you can divide by 1000 to get seconds
if(el.elapsed() > 1000)
qDebug() << "long";
else
qDebug() << "not long";
}
return QObject::eventFilter(obj, event);
}
It will work for pushButtons, labels, toolButtons etc.

Related

Can't catch TapAndHoldGesture

I grabGesture()ed one of my buttons:
buttons[0]->grabGesture(Qt::TapAndHoldGesture);
in the constructor, and declared:
bool event(QEvent *event);
in protected slots, and implemented it like this:
bool MyClass::event(QEvent *event)
{
if (event->type() == QEvent::Gesture){
QGestureEvent *gestevent = static_cast<QGestureEvent *>(event);
if (QGesture *gest = gestevent->gesture(Qt::TapAndHoldGesture)){
QTapAndHoldGesture *tapgest = static_cast<QTapAndHoldGesture *>(gestevent->gesture(Qt::TapAndHoldGesture));
cout << "grabbed a gesture event" << endl;
}
return true;
}
cout << "not a gesture event" << endl;
return QWidget::event(event);
}
and I keep getting "not a gesture event" printed to screen however I press (normal press / long press / ... )
What I'm trying to do is a long key press (from the keyboard)
It's said in the Qt Documentation:
A gesture could be a particular movement of a mouse, a touch screen
action, or a series of events from some other source. The nature of
the input, the interpretation of the gesture and the action taken are
the choice of the developer.
So I suppose also a keyboard can trigger QGesture events.
If the class handling the grap (MyClass) event is not the class where the gesture is detected on (QPushButton assuming buttons[0] is a QPushButton), then you need and event filter:
buttons[0]->grabGesture(Qt::TapAndHoldGesture);
buttons[0]->installEventFilter( myClass ); // myClass being a MyClass instance
Now, myClass object will be forwarded all events from buttons[0], this is done using QObject::eventFilter virtual function:
bool MyClass::eventFilter(QObject *obj, QEvent *event)
{
if ( event->type() == QEvent::Gesture && obj == buttons[0] )
{
QGestureEvent *gestevent = static_cast<QGestureEvent *>(event);
if (QGesture *gest = gestevent->gesture(Qt::TapAndHoldGesture)){
QTapAndHoldGesture *tapgest = static_cast<QTapAndHoldGesture *>(gestevent->gesture(Qt::TapAndHoldGesture));
cout << "grabbed a gesture event" << endl;
return true;
}
}
// standard event processing
return Parent::eventFilter(obj, event); // Parent being MyClass parent type, maybe QDialog or QWidget
}

QPlainTextEdit double click event

I need to capture the double click event on a QPlainTextEdit that is inside a QDockWidget.
In my actual code I have installed an event filter in the QDockWidget, to handle resize operations, and in the QPlainTextEdit, to handle the double click events:
// Resize eventfilter
this->installEventFilter(this);
ui->myPlainTextEdit->installEventFilter(this);
But, although it works for the QDockWidget I am unable to catch the double click event for the QPlainTextEdit:
bool MyDockWidget::eventFilter(QObject *obj, QEvent *event) {
if (event->type() == QEvent::Resize && obj == this) {
QResizeEvent *resizeEvent = static_cast<QResizeEvent*>(event);
qDebug("Dock Resized (New Size) - Width: %d Height: %d",
resizeEvent->size().width(),
resizeEvent->size().height());
} else if (obj == ui->myPlainTextEdit && event->type() == QMouseEvent::MouseButtonDblClick) {
qDebug() << "Double click";
}
return QWidget::eventFilter(obj, event);
}
With this code the message "Double click" is never shown. Any idea what is wrong with the code?
QTextEdit inherits a QScrollView and when you double click on the viewport of the QTextEdit, the viewport receives the double click event. You can cross check your current code by double clicking on the edges of the text edit. It will capture the event.
To solve this, add the event filter to the view port in addition to the current event filters you have installed as shown below:
ui->myPlainTextEdit->viewport()->installEventFilter(this);
Next, capture the event using this if statement:
if ((obj == ui->myPlainTextEdit||obj==ui->myPlainTextEdit->viewport()) &&
event->type() == QEvent::MouseButtonDblClick)
{
qDebug() << "Double click"<<obj->objectName();
}
You can capture the click position using QMouseEvent:
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
qDebug()<<QString("Click location: (%1,%2)").arg(mouseEvent->x()).arg(mouseEvent->y());

Qt keyPressEvent, "Hold", and keyReleaseEvent Handling with Buttons/Mouse Clicks

I am writing a Qt program to simulate a piece of hardware and I would like to simulate button press, hold, and release events. In my application, I'd like to handle input from both the keyboard and mouse clicks to make things convenient to the user (i.e. me). I've noticed some odd behavior and I don't understand it.
The application uses QPushButton with autoRepeat enabled and a 100 ms autoRepeatDelay and autoRepeatInterval. If I mouse click on a button, I receive alternating "pressed" and "released" events. I would have expected to see 1 to N-1 "pressed" events followed by a "released" event. Why is Qt behaving that way?
I've also implemented the following code to handle button presses from the keyboard:
void MyApp::keyPressEvent(QKeyEvent *event)
{
QString s = QString("My PRESS key is %1. The counter is %2").arg(event->text(), QString::number(keyCounter));
qDebug() << s;
keyCounter++;
}
void MyApp::keyReleaseEvent(QKeyEvent *event)
{
QString s = QString("My RELEASE key is %1. The counter is %2").arg(event->text(), QString::number(keyCounter));
qDebug() << s;
keyCounter = 0;
}
bool MyApp::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress)
{
this->keyPressEvent(dynamic_cast<QKeyEvent*>(event));
return true;
}
else if (event->type() == QEvent::KeyRelease)
{
this->keyReleaseEvent(dynamic_cast<QKeyEvent*>(event));
return true;
}
else
{
return QObject::eventFilter(obj, event);
}
}
Here I see two types of behavior. For alphanumeric keys, I see the alternating "pressed" and "released" events. For arrow keys, I only see the "released" events. Again, I would have expected to see 1 to N-1 "pressed" events followed by a "released" event. Why do the arrow keys behave differently than the alphanumeric keys?
Is what I'm trying to do possible in Qt?
Here was my solution: First I disabled autoRepeat and stopped handling keyPressEvents because I found that the arrow keys weren't generating them. Instead I registered shortcuts for the keyboard buttons I wanted to use:
QShortcut *shortcutUp = new QShortcut(QKeySequence("Up"), this);
QObject::connect(shortcutUp, SIGNAL(activated()), this, SLOT(on_upButton_pressed()));
shortcutUp->setAutoRepeat(false);
Then in the on_upButton_pressed() function (for example) I set a flag indicating the button was pressed. This flag is cleared in the on_upButton_released() function. That flag is checked on a 100 ms interval (using a QTimer). If the flag is true, I call on_upButton_pressed() again. This means:
Each mouse click or keyboard button press generates one "Press" event
The "Press" event sets a flag that is checked by a QTimer
If the flag is true, another "Press" event is generated
When the mouse or keyboard button is released, the "Release" event is generated and the flag is cleared.
It's working now.
This is caused by some keys in QT generate auto-repeat event:
Here I see two types of behavior. For alphanumeric keys, I see the
alternating "pressed" and "released" events. For arrow keys, I only
see the "released" events. Again, I would have expected to see 1 to
N-1 "pressed" events followed by a "released" event. Why do the arrow
keys behave differently than the alphanumeric keys?
In QT5 you can detect key held down by function isAutoRepeat() of QKeyEvent object. If the key is held down then isAutoRepeat() will return true. for example:
void MainWindow::keyPressEvent(QKeyEvent *event)
{
if (event->isAutoRepeat())
{
return;
}
if (!event->isAutoRepeat())
{
qDebug() << "[MainWindow::keyPressEvent()] " << event->key() << "; " << event->isAutoRepeat();
}
}
void MainWindow::keyReleaseEvent(QKeyEvent *event)
{
if (event->isAutoRepeat())
{
return;
}
qDebug() << "[MainWindow::keyReleaseEvent()] " << event->key() << "; " << event->isAutoRepeat();
}

QMainWindow close() signal not emitted

To warn before closing a window that inherits from QMainWindow, I reimplemented its closeEvent, which works fine when I emit close() manually. However, clicking the "x" button does not trigger this; it just exits.
It does emit aboutToQuit() for the application, which I can use to "recover" the window after it already closes. But I want to the warning to precede the initial closing.
I'm not sure where the issue is. The window is top-level and there are no running threads. Have I misunderstood what signal is actually connected to the button click...? It is close(), right?
In your mainwindow class header( the closeEvent must be virtual ):
public:
/*!
* \brief closeEvent
* \param event
*/
virtual void closeEvent ( QCloseEvent * event );
Then in the cpp
void MainWindow::closeEvent( QCloseEvent *event )
{
//! Ignore the event by default.. otherwise the window will be closed always.
event->ignore();
if(!EntitiesSaverObserver::Instance()->isAllSaved())
{
QMessageBox msgBox;
msgBox.setWindowIcon(QIcon(":/Resources/Icons/warning.png"));
msgBox.setIconPixmap(QPixmap(":/Resources/Icons/warning.png"));
QString strToShow = QString("Some Entities has been created or modified...");
msgBox.setText(strToShow);
msgBox.setInformativeText("Do you want to save your changes?");
msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Save);
int ret = msgBox.exec();
switch (ret) {
case QMessageBox::Save:
{
// Save was clicked
qDebug() << "SAVE";
//! Do your stuff here
// ....
event->accept();
break;
}
case QMessageBox::Discard:
{
// Don't Save was clicked
qDebug() << "DISCARD";
event->accept();
break;
}
case QMessageBox::Cancel:
{
// Cancel was clicked
qDebug() << "CANCEL";
break;
}
default:
// should never be reached
break;
}
} else {
event->accept(); // Do not need to save nothing... accept the event and close the app
}
}
Moreover, if you want to put a button in your toolbar as a QAction, you could connect the signal and then:
void MainWindow::on_actionExit_triggered()
{
close();
}
This would call the close event of your main window. I hope this helps you.
just create a signal-slot QObject::connect(yourButton, SIGNAL(clicked()), this, SLOT(close()));

Implementing Timeout in QT

I am trying to implement timeout in QT. I want to perform following task so I require timeout.
In application i have implemented menu. If I entered choose of option from menu, it will execute related screen. This screen should timeout after 15 seconds, if I am not getting any key event before 15 seconds. Following is my code:
bool cMeasurementUnit::eventFilter(QObject *obj, QEvent *event)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if(event->type() == QEvent::KeyPress)
{
if((keyEvent ->key()) == Qt::Key_Tab)
{
if(m_pWidgetFirstTabFocus->hasFocus())
{
m_pWidgetFirstTabFocus->setStyleSheet(QString::fromUtf8("background-color: rgb(255, 255, 255);"));
}
m_pWidgetFirstTabFocus = m_pWidgetFirstTabFocus->nextInFocusChain() ;
while((m_pWidgetFirstTabFocus->focusPolicy()) == Qt::NoFocus)
{
m_pWidgetFirstTabFocus = m_pWidgetFirstTabFocus->nextInFocusChain() ;
if(m_pWidgetFirstTabFocus == this)
{
m_pWidgetFirstTabFocus = MEASUREMENT_UNIT_FIRST_TAB;
}
}
m_pWidgetFirstTabFocus->setStyleSheet(QString::fromUtf8("background-color: rgb(207, 207, 207);"));
}
else if((keyEvent ->key()) == Qt::Key_Return)
{
SaveChannelUnit();
return true ;
}
else if((keyEvent ->key()) == Qt::Key_Up)
{
if (((QComboBox *)m_pWidgetFirstTabFocus)->currentIndex() == 0)
{
((QComboBox *)m_pWidgetFirstTabFocus)->setCurrentIndex((((QComboBox *)m_pWidgetFirstTabFocus)->count() - 1)) ;
return true ;
}
}
else if((keyEvent ->key()) == Qt::Key_Left)
{
return true;
}
}
return QObject::eventFilter(obj, event);
}
I have tried to implement using QTimer::singleShot(15000, this, SLOT(DeleteClass()));
but it is not working. Please help me regarding this issue.I have implemented QTimer::singleShot inside if(event->type() == QEvent::KeyPress) statement in above code so that whenever I press a key it will reinitialize QTimer::singleShot and screen of class cMeasurementUnit will not get timeout, otherwise it will be timeout after 15seconds. Following is the code for DeleteClass, is it correct? if not will you please tell me correct way to do it? Thanks in advance
void cMeasurementUnit::DeleteClass()
{
DPRINTF("IN FUNCTION %s\n",__FUNCTION__);
delete this;
}
You can use QTimer to run checks periodically and QElapsedTimer to calculate inactivity time.
In the header:
QElapsedTimer elapsed_timer;
In the initialization:
elapsed_timer.start();
QTimer* timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(timeout()));
timer->start(1000); // number of milliseconds between checks
// install event filter for target widget, etc.
In the timeout slot:
if (elapsed_timer.elapsed() > 15000) { // timeout interval in msec
//perform close actions, e.g. widget->close()
}
In the event filter: the following code should be executed if an appropriate key event was received:
elapsed_timer.restart();

Resources