Detect end of movement of QMainWindow / QDialog (Qt 4.8) - qt

I’m using Qt 4.8.3 on X11.
I need to know when the user ends with dragging a window around the screen,
this in order to read the final position and eventually start an animation to adjust the window position to an “allowed” one.
I noticed that the QWidget::moveEvent is called for each small movement, but this is very unconvenient because I must perform position checking (and eventually start the animation) only when the user releases the mouse button and the movement is completely finished.
This is the real problem: it seems that there is no way to detect the mouse release event (or to get the mouse buttons status) when the user clicks on the titlebar, since it is controlled by the OS and not by Qt.
I tried also with the QWidget::x11event(XEvent* e)… but the events are collected only inside the window, not the title bar, as well.
Does someone know a way to achieve this?
I suspect that I will have to reimplement the titlebar myself… too bad…

Realizing this is a very old question, it is the first hit that comes up when you try "Qt detecting the end of a window move event". So, I thought I'd add a solution that works well with the current (as of this writing) release of Qt, 5.12.3.
You can set up a small state machine that provides the boundaries for knowing when a top-level window's position has been altered using a QObject::eventFilter(). In Qt 5.12.x, you will receive a QEvent::NonClientAreaMouseButtonPress event when the mouse goes down in the non-client area of your window (e.g., the title bar), a subsequent QEvent::Move event when the window position changes (if at all), and then a final QEvent::NonClientAreaMouseButtonRelease event when the mouse button is released.
Knowing this sequence, and using a persistent Boolean state flag (user_moved_window) to know that the position actually changed, would give you the following code snippet within your QObject::eventFilter() method:
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
QEvent::Type event_type = event->type();
[...]
else if(event_type == QEvent::NonClientAreaMouseButtonPress)
user_moved_window = false;
else if(event_type == QEvent::Move && isVisible())
user_moved_window = true;
else if(event_type == QEvent::NonClientAreaMouseButtonRelease)
{
if(user_moved_window)
{
// do what you need to do to here to respond to
// the end of the reposition event...
user_moved_window = false;
}
}
[...]
return MainWindow::eventFilter(obj, event);
}
You might need to add some additional checks depending on your situation--e.g., to make sure the obj for the event is actually the main window--but this example works well for my production code using Qt 5.12.3.

I have the same problem as yours. The moveEvent is triggered at every point along its move and Qt provides no explicit method to figure out the end of the move.
But now, inspired by the answer of divanov, I found that when we release the mouse after moving the dialog, an event typed 173 would always be triggered. That's QEvent::NonClientAreaMouseMove.
So the code is straightforward.
First install the event filter and announce a member variable: int nLastEvent;.
bool Win::eventFilter(QObject *obj, QEvent *event)
{
if (nLastEvent == QEvent::Move && event->type() == 173)
{
// do what you wanna do here when the mouse is released,
// like attaching the dialog to the main window
}
nLastEvent = event->type();
return QWidget::eventFilter(obj, event);
}
It's simple and efficient enough, isn't it!
Hope it be useful for you, too. :)

Let's consider the following test application:
main.cpp
#include <QApplication>
#include "win.h"
int main(int argc, char** argv)
{
QApplication app(argc, argv);
Win w;
w.show();
return app.exec();
}
win.h:
#include <QWidget>
#include <QEvent>
#include <QMoveEvent>
#include <QDebug>
class Win : public QWidget
{
public:
Win(QWidget *parent = 0) : QWidget(parent) {
this->installEventFilter(this);
}
protected:
bool eventFilter(QObject *obj, QEvent *event) {
if (event->type() == QEvent::Move) {
QMoveEvent *moveEvent = static_cast<QMoveEvent*>(event);
qDebug() << "Move event:" << moveEvent->pos();
} else {
qDebug() << "Event type:" << event->type();
}
return QWidget::eventFilter(obj, event);
}
};
This application just installs event filter on itself and prints to console all received events with special formatting for QMoveEvent to discriminate it in the log.
Typical log:
Event type: 203
Event type: 75
Move event: QPoint(0,0)
Event type: 14
Event type: 17
Event type: 26
Event type: 74
Event type: 77
Move event: QPoint(66,52)
Event type: 12
Event type: 24
Event type: 99
Event type: 77
Event type: 12
Event type: 10
Event type: 11
Move event: QPoint(308,356)
Event type: 19
Event type: 25
Event type: 99
Event type: 18
Event type: 27
Event type: 77
As you see, there are 2 move events, when application was initially created and one, after I finished window movements. I was testing with Qt 4.8.1 and XOrg 7.6.
To check raw X events
Have the test application running.
Get window Id of the test application. To do so execute in command line xwininfo -name WINDOW_NAME, where WINDOW_NAME is the name of the test application's window. Another option is to use xwininfo without parameters, then you have to select the test application window with a mouse pointer.
Run X event monitor xev -id 0x2a00002, where 0x2a00002 is window Id found in a previous step. This will print X events your window receives from X server. ConfigureNotify is X protocol counterpart of QMoveEvent.

Related

How to fake QWheelEvent for tests? [duplicate]

I am happily using QTestLib to write tests for my Qt5 widgets based UI. There has seemed to be no shortage of features and convenience functionality until now, when I tried to find a way to simulate mouse wheel events.
I have looked at the official documentation, and an official example but I can't seem to figure out how to go about simulating mouse wheel events.
Does this not exist? Or am I missing something? How should I create dummy mouse wheel events using QTestLib?
It has been 10 years, and I thought it would be a nice way to mark the occasion by formally submitting a feature request on the bug-tracker for Qt:
Behold QTBUG-71449.
For anyone stumbling into this problem 10 years from now on. We wrote an implementation for our testing suite for Qt4.8 (also tested in Qt5.6, as always no guarantees):
void TestUtility::mouseWheelTurn(
QWidget *widget, // The most top level widget; a MainWindow in our case
int delta, // As in QWheelEvent
QPoint pos, // Mouseposition in the moment of scrolling relative to top level widget
Qt::MouseButtons buttons, // As in QWheelEvent
Qt::KeyboardModifiers modifiers, // As in QWheelEvent
Qt::Orientation orientation, // As in QWheelEvent
int delay) // As in other QTest functions
{
QWidget *toWheelChild = widget->childAt(pos);
if(toWheelChild == NULL) return;
pos = widget->mapToGlobal(pos);
pos = toWheelChild->mapFromGlobal(pos);
QTest::mouseMove(toWheelChild, pos);
QTest::qWait(delay);
QWheelEvent *wheelEvent =
new QWheelEvent(pos, delta, buttons, modifiers, orientation);
QApplication::instance()->postEvent(toWheelChild, wheelEvent);
}

Qt event loop and unit testing?

I'we started experimenting with unit testing in Qt and would like to hear comments on a scenario that involves unit testing signals and slots.
Here is an example:
The code i would like to test is (m_socket is a pointer to QTcpSocket):
void CommunicationProtocol::connectToCamera()
{
m_socket->connectToHost(m_cameraIp,m_port);
}
Since that is an asynchronous call i can't test a returned value. I would however like to test if the response signal that the socket emits on a successful connection (void connected ()) is in fact emitted.
I've written the test below:
void CommunicationProtocolTest::testConnectToCammera()
{
QSignalSpy spy(communicationProtocol->m_socket, SIGNAL(connected()));
communicationProtocol->connectToCamera();
QTest::qWait(250);
QCOMPARE(spy.count(), 1);
}
My motivation was, if the response doesn't happen in 250ms, something is wrong.
However, the signal is never caught, and I can't say for sure if it's even emitted. But I've noticed that I'm not starting the event loop anywhere in the test project. In the development project, the event loop is started in main with QCoreApplication::exec().
To sum it up, when unit testing a class that depends on signals and slots, where should the
QCoreApplication a(argc, argv);
return a.exec();
be run in the test environment?
I realize this is an old thread but as I hit it and as others will, there is no answer and the answer by peter and other comments still miss the point of using QSignalSpy.
To answer you original question about "where the QCoreApplication exec function is needed", basically the answer is, it isn't. QTest and QSignalSpy already has that built in.
What you really need to do in your test case is "run" the existing event loop.
Assuming you are using Qt 5:
http://doc.qt.io/qt-5/qsignalspy.html#wait
So to modify your example to use the wait function:
void CommunicationProtocolTest::testConnectToCammera()
{
QSignalSpy spy(communicationProtocol->m_socket, SIGNAL(connected()));
communicationProtocol->connectToCamera();
// wait returns true if 1 or more signals was emitted
QCOMPARE(spy.wait(250), true);
// You can be pedantic here and double check if you want
QCOMPARE(spy.count(), 1);
}
That should give you the desired behaviour without having to create another event loop.
Good question. Main issues I've hit are (1) needing to let app do app.exec() yet still close-at-end to not block automated builds and (2) needing to ensure pending events get processed before relying on the result of signal/slot calls.
For (1), you could try commenting out the app.exec() in main(). BUT then if someone has FooWidget.exec() in their class that you're testing, it's going to block/hang. Something like this is handy to force qApp to exit:
int main(int argc, char *argv[]) {
QApplication a( argc, argv );
//prevent hanging if QMenu.exec() got called
smersh().KillAppAfterTimeout(300);
::testing::InitGoogleTest(&argc, argv);
int iReturn = RUN_ALL_TESTS();
qDebug()<<"rcode:"<<iReturn;
smersh().KillAppAfterTimeout(1);
return a.exec();
}
struct smersh {
bool KillAppAfterTimeout(int secs=10) const;
};
bool smersh::KillAppAfterTimeout(int secs) const {
QScopedPointer<QTimer> timer(new QTimer);
timer->setSingleShot(true);
bool ok = timer->connect(timer.data(),SIGNAL(timeout()),qApp,SLOT(quit()),Qt::QueuedConnection);
timer->start(secs * 1000); // N seconds timeout
timer.take()->setParent(qApp);
return ok;
}
For (2), basically you have to coerce QApplication into finishing up the queued events if you're trying to verify things like QEvents from Mouse + Keyboard have expected outcome. This FlushEvents<>() method is helpful:
template <class T=void> struct FlushEvents {
FlushEvents() {
int n = 0;
while(++n<20 && qApp->hasPendingEvents() ) {
QApplication::sendPostedEvents();
QApplication::processEvents(QEventLoop::AllEvents);
YourThread::microsec_wait(100);
}
YourThread::microsec_wait(1*1000);
} };
Usage example below.
"dialog" is instance of MyDialog.
"baz" is instance of Baz.
"dialog" has a member of type Bar.
When a Bar selects a Baz, it emits a signal;
"dialog" is connected to the signal and we need to
make sure the associated slot has gotten the message.
void Bar::select(Baz* baz) {
if( baz->isValid() ) {
m_selected << baz;
emit SelectedBaz();//<- dialog has slot for this
} }
TEST(Dialog,BarBaz) { /*<code>*/
dialog->setGeometry(1,320,400,300);
dialog->repaint();
FlushEvents<>(); // see it on screen (for debugging)
//set state of dialog that has a stacked widget
dialog->setCurrentPage(i);
qDebug()<<"on page: "
<<i; // (we don't see it yet)
FlushEvents<>(); // Now dialog is drawn on page i
dialog->GetBar()->select(baz);
FlushEvents<>(); // *** without this, the next test
// can fail sporadically.
EXPECT_TRUE( dialog->getSelected_Baz_instances()
.contains(baz) );
/*<code>*/
}
I had a similar issue with Qt::QueuedConnection (event is queued automatically if the sender and the receiver belongs to different threads). Without a proper event loop in that situation, the internal state of objects depending on event processing will not be updated. To start an event loop when running QTest, change the macro QTEST_APPLESS_MAIN at the bottom of the file to QTEST_MAIN. Then, calling qApp->processEvents() will actually process events, or you can start another event loop with QEventLoop.
QSignalSpy spy(&foo, SIGNAL(ready()));
connect(&foo, SIGNAL(ready()), &bar, SLOT(work()), Qt::QueuedConnection);
foo.emitReady();
QCOMPARE(spy.count(), 1); // QSignalSpy uses Qt::DirectConnection
QCOMPARE(bar.received, false); // bar did not receive the signal, but that is normal: there is no active event loop
qApp->processEvents(); // Manually trigger event processing ...
QCOMPARE(bar.received, true); // bar receives the signal only if QTEST_MAIN() is used

Generating a QMouseEvent manually within the keyPressEvent in Qt

I have overridden the contentsMousePressEvent in my listview like this.
void AppListView::contentsMousePressEvent(QMouseEvent *e)
{
AppGenericListView::contentsMousePressEvent(e);
if (e->button() == Qt::RightButton)
emit rightClicked();
emit multiSelection();
}
Here is my keyPressEvent.
void AppListView::keyPressEvent(QKeyEvent * e)
{
AppGenericListView::keyPressEvent(e);
if ((e->key() == Qt::Key_Up) || (e->key() == Qt::Key_Down))
{
QListViewItem * item = currentItem();
if (item)
{
const QRect rect = itemRect(item);
QMouseEvent mEvt(QEvent::MouseButtonPress, rect.center(), Qt::LeftButton, Qt::LeftButton);
contentsMousePressEvent(&mEvt);
}
}
}
For now, this code working fine. Please note that i'm not creating a dynamic QMouseEvent object. What i want to know is will this cause a crash in future ? Does contentMousePressEvent need a dyanamic object ? Qt doc doesn't say much about it. Any ideas ....
It won't crash, because you are not using event loop. But i think you should, for two reasons:
You are simulating mouse press event but you are not serving it to the object as a such. For example, you don't serve your fake mouse event to mousePressEvent(). So for "natural" mouse press events and your simulated one, the application will behave differently. And you may forget why is that and you may get inexplicable crashes when your code evolves.
The original system keypress event handling will be blocked by handling a mouse press event. You can't know who (what thread) will connect() to your signals emitted from overriden mouse event handler. Don't be surprised if you get a deadlock.
Such a half baked shortcuts are good only as temporary solutions. In the long run, they will shoot at your back. If you really want a shortcut, stop pretending it's a mouse event and implement a special separate method which will be called also from the "real" mouse event. If you want a real mouse event, handled properly, create a dynamic QMouseEvent and enqueue it at the event loop.
QMouseEvent* evt = new QMouseEvent(QEvent::MouseButtonPress,
rect.center(),this->mapToGlobal(rect.center()),
Qt::LeftButton, Qt::LeftButton);
QCoreApplication::postEvent(this,evt);
The event handlers don't take ownership of the events they receive. So your current code is fine.
It would be deleted automatically (and cause a crash), if you were passing it to QCoreApplication::postEvent to be sent asynchronously.

QT GUI freezes when capturing video from webcam using OpenCV

I am using Opencv for some real-time video processing.
As a front-end I am using QT framework.
On my GUI, I have an input image window (mapped to a Label) and an output image window (mapped to another Label) and 3 push buttons. One to Start input video capture, the second to process the video (code not written yet), and third to Exit.
I am currently able to stream my video and display it on the Front-end. But this locks my GUI and am unable to Exit.
I tried using QTimers (using suggestions from this and the QT forum), but my GUI still remains locked.
Would appreciate if someone can point my in the right direction.
Below is the code:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp> // for cvtColor
#include <iostream>
#include <QTimer>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_buttonCaptureVideo_clicked();
void on_buttonExit_clicked();
public slots:
virtual void doNextFrame() {repaint();}
private:
Ui::MainWindow *ui;
CvCapture *capture; // OpenCV Video Capture Variable
IplImage *frame; // Variable to capture a frame of the input video
cv::Mat source_image; // Variable pointing to the same input frame
cv::Mat dest_image; // Variable to output a frame of the processed video
QTimer *imageTimer;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
cvReleaseImage(&frame);
cvReleaseCapture(&capture);
}
void MainWindow::on_buttonCaptureVideo_clicked()
{
// Set to 25 frames per second
const int imagePeriod = 1000/25; // ms
imageTimer = new QTimer(this);
imageTimer->setInterval(imagePeriod);
connect(imageTimer, SIGNAL(timeout()), this, SLOT(doNextFrame()));
// Use the default camera
capture = cvCreateCameraCapture(-1);
while(capture)
{
// Capture a frame
frame = cvQueryFrame(capture);
// Point to the same frame
source_image = frame;
// Resize Image
cv::resize(source_image, source_image, cv::Size(128,128) , 0, 0);
// Change to RGB format
cv::cvtColor(source_image,source_image,CV_BGR2RGB);
// Convert to QImage
QImage qimg = QImage((const unsigned char*) source_image.data, source_image.cols, source_image.rows, QImage::Format_RGB888); // convert to QImage
// Display on Input Label
ui->labelInputVideo->setPixmap(QPixmap::fromImage(qimg));
// Resize the label to fit the image
ui->labelInputVideo->resize(ui->labelInputVideo->pixmap()->size());
}
}
void MainWindow::on_buttonExit_clicked()
{
connect(ui->buttonExit, SIGNAL(clicked()), qApp, SLOT(closeAllWindows()));
}
When you click your button, the
while(capture) { ... }
loop will run forever, as capture will never be set to NULL.
This means the code flow never leaves your loop and thus the main thread cannot process anything else, like e.g. repainting.
The QTimer will emit its timeout() signals, but they will be placed as Events in Qt's Event Queue. As long as your on_buttonCaptureVideo_clicked() method is running, those Events will not be processed.
Here are my suggestions how to make it work:
This code:
// Set to 25 frames per second
const int imagePeriod = 1000/25; // ms
imageTimer = new QTimer(this);
imageTimer->setInterval(imagePeriod);
connect(imageTimer, SIGNAL(timeout()), this, SLOT(doNextFrame()));
// Use the default camera
capture = cvCreateCameraCapture(-1);
belongs into the constructor of MainWindow as you want to set that up one time only. There is no need to do it again when the user clicks the button the second, third, etc time.
The code which is within your while loop should go into the doNextFrame() slot (without the while construct).
Then your button will only do
imageTimer->start();
and then e.g. do
imageTimer->stop();
when it is clicked again.
Example code:
void MainWindow::on_buttonCaptureVideo_clicked()
{
if( imageTimer->isActive() )
{
imageTimer->stop();
}
else
{
imageTimer->start();
}
}
What will happen if you do that?
When you click the button, your on_buttonCaptureVideo_clicked() clicked slot will be called from the GUI thread, the timer will be started, and the method will return almost instantly.
Now the GUI thread is free and able to handle repaints etc.
From that point on, the timer will be sending timeout() signals every 40ms. Whenever the GUI thread is free, it will handle this signal and call your doNextFrame slot.
This slot will capture the next frame and return when it is done. When it is done, the GUI thread will be able to process other Events (e.g. repaint) again.
As soon as you click the button again, the timer will stop, and no new timeout() events will be sent. If you still see a couple of frames after the button has been clicked, this could mean that the timer events were sent faster than they could be processed.
Preface: I am not strong in C++ so I cannot offer specific code, but I am experienced in PyQt
This is a common pitfall for newcomers to Qt. What it seems your on_buttonCaptureVideo_clicked is doing is entering into a loop in your main GUI thread to do work. In QT, you want to avoid doing anything busy in your main thread. The Qt eventloop needs to be able to constantly process and flush your GUI events as they come in. What you are doing is blocking the event loop.
There are two different things you can do here. The first is the more basic approach but allows you to see more immediate results. You can "pump" the eventloop. Depending on how fast your while loop iterates, you can call qApp->processEvents();. This will allow Qt to process the pending GUI events and make your app appear more responsive. Its basically sharing time between your while loop and the main loop. Maybe you want to call this on every nth frame. Depends how often you want to make sure the GUI refreshes.
The other option, which is more preferable, is to place your capture loop into a QThread. When a new frame is available you can emit a signal with the frame data. The signal will get placed into the Qt event loop to be processed with everything else and will not hold up your GUI. Generally this is the approach you want to take with any heavy crunching or long running callables.
Edit
I just realized that you start a QTimer in addition to doing a loop in the main thread. If you want to use a QTimer, and your image processing is not too heavy (it doesn't take long per cycle) then you should move everything into the doNextFrame and completely remove the while loop. If your doNextFrame is a heavy process, then you should be using a QThread and signals.

Qt can not respond key Press event immediately

Environment: Ubuntu, Qt Creator
In my Qt app, I found that sometimes Qt doesn't respond to my key press event immediately, but if I wait a while, it eventually responds.
I think something is blocking the UI.
As I know, if a Qt's component (QWidget etc.) is being destroyed, the Qt UI will be blocked. I have checked my code, there is no component being destroyed at the time I'm pressing the up/down key.
I really want to know is there any other things can block Qt UI.
{
...
connect(webViewWidget, SIGNAL(loadfinished()), this, SLOT(addItem()));
...
}
void addItem()
{
delete webViewWidget; // will this delete block UI?
mListWidget = new ScrollWidget();
mScrollArea = new ScrollArea(this);
for(int i=0; i<Datalen; i++)
{
mListWidget->addSubItem(itemWidget);
}
}
void keyPressEvent(QKeyEvent *event)
{
switch(event->key)
{
case UP_KEY:
scroll up;
break;
case DOWN_KEY:
scroll down;
break;
default:
break;
}
}
In general, your key press event will not be processed before all other events which were put into the application's event queue before pressing your key are processed.
Therefore it could be any kind of event which has not finished processing. Maybe you can figure out if there are any events, e.g. by using QApplication::hasPendingEvents or by inheriting from QApplication and adding debug output whenever an event is added or fully processed.
Destruction of objects is usually not a concern, unless you are doing a lot of work in the destructor. Destroying a webview may take long. You probably should not be destroying it like you do. Instrument that delete (see code below) and see how long it takes.
Your own code may be calling APIs that block. Are you calling any third party libraries? Are you calling any wait... methods in Qt's own API?
If you're unsure, you can instrument every slot and every reimplemented virtual method like xxxEvent(...). You'd need to instrument only slots and reimplemented QObject/QWidget methods, not every method in your code.
You may be producing an event storm, perhaps by posting lots of events in a loop, or by sending a lot of signals that are hooked up to slots connected via a Qt::QueuedConnection. Make sure you're not calling repaint() from within paintEvent() for example.
The instrumentation example below uses RAII and is very easy to apply. Alternatively, you can use a profiler.
#include <QElapsedTimer>
#define INSTRUMENT() Instrument instr__ument(__FUNCTION__)
#define INSTRUMENTLIM(lim) Instrument instr__ument(__FUNCTION__, (lim))
class Instrument {
QElapsedTimer timer;
int limit;
const char * function;
public:
Instrument(const char * name, int timeLimitMs = 20) :
function(name), limit(timeLimitMs) { timer.start(); }
~Instrument() {
if (timer.elapsed() > limit) {
qDebug("%s was slow, took %d ms", function, timer.elapsed());
}
}
}
void slot(...)
{
INSTRUMENT();
...
}
void addItem()
{
INSTRUMENT();
delete webViewWidget; // will this delete block UI?
mListWidget = new ScrollWidget();
mScrollArea = new ScrollArea(this);
for(int i=0; i<Datalen; i++)
{
mListWidget->addSubItem(itemWidget);
}
}

Resources