How to consume QEvent::WindowBlocked so that specific window is always active? - c++

I created myself simple window for debugging purposes. It's completely separate QMainWindow, so that I can put it on the other monitor when testing the app. But when there's a dialog in my application, I cannot access the window. Right now, this is exactly the time I WANT to access it.
I tried to override QWidget::event like so:
DebugWindow.h
#include <QtWidgets/QMainWindow>
QT_BEGIN_NAMESPACE
class QEvent;
QT_END_NAMESPACE
class DebugWindow : public QMainWindow
{
Q_OBJECT
public:
DebugWindow(QWidget *parent = 0);
virtual ~DebugWindow();
protected:
virtual bool event(QEvent*);
};
DebugWindow.cpp
bool TechadminScript::event(QEvent* e)
{
if (e->type() == QEvent::WindowBlocked) {
// accept the event to stop propagation
e->accept();
return true;
}
return false;
}
I have set up breakpoint in the overriden function and it was hit - that means I did something right. But the window is still blocked as it was before.
So I am probably missing something, can somebody help me finish this?

It worked for me setting the window modality as #king_nak suggested. Mind you have to hide() beforehand and show() according to the documentation: https://doc.qt.io/qt-5/qwidget.html#windowModality-prop
bool Object::eventFilter(QObject *, QEvent * const event)
{
if (event->type() != QEvent::WindowBlocked)
return false;
for (auto&& widget : qApp->topLevelWidgets())
{
if (!widget->isModal())
continue;
widget->hide();
widget->setWindowModality(Qt::NonModal);
widget->show();
}
return true;
}

Related

Qt: How to catch QDateEdit click event?

I'm trying to catch mouse click on QDateEdit widget by handling QEvent::MouseButtonRelease event, but can't find a way to do it. I tried to override QWidget::event method of the parent widget, but it seems that events go through children to parent, and QDateEdit internally handles those events without propagating to a parent. Is there any correct solution or workaround?
QDateEdit extends a QWidget class. So you can just inherit QDateEdit and override virtual void mouseReleaseEvent(QMouseEvent *event) function and do what you want there.
Update:
Function mouseReleaseEvent is really not invoke.
Try to install an event filter to the line edit in QDateEdit. Example:
MyDateEdit.h
#include <QDateEdit>
class MyDateEdit : public QDateEdit
{
Q_OBJECT
public:
MyDateEdit(QWidget *parent = 0);
bool eventFilter(QObject* object, QEvent* event) override;
};
MyDateEdit.cpp
#include "MyDateEdit.h"
#include <QDebug>
#include <QEvent>
#include <QLineEdit>
MyDateEdit::MyDateEdit(QWidget *parent) : QDateEdit (parent)
{
installEventFilter(this);
lineEdit()->installEventFilter(this);
}
bool MyDateEdit::eventFilter(QObject* object, QEvent* event)
{
if (object == this || object == lineEdit())
{
if (event->type() == QEvent::MouseButtonRelease)
{
qDebug() << "Mouse release event";
}
}
return QDateEdit::eventFilter(object, event);
}
One way to do this is to install an eventFilter. The eventFilter section of the Qt documentation provides an example of how it is use.
Your window class should override eventFilter
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == dateEdit) {
if (event->type() == QEvent::MouseButtonPress) {
// do what you want to do
// alternatively use QEvent::MouseButtonRelease
return true;
} else {
return false;
}
} else {
// pass the event on to the parent class
return QMainWindow::eventFilter(obj, event);
}
}
In your window constructor, install the filter on the actual widget:
dateEdit->installEventFilter(this);

QWidget loses its parent

In my application I have a QDialog which itself contains a complex, QWidget-derived GUI element. The QDialog is modal and opened with exec() and the embedded GUI element handles all user interactions.
So only this child QWidget knows when the QDialog can be closed, which is done this way:
QDialog* parent=qobject_cast<QDialog*>(parentWidget());
if (parent) parent->close();
This is necessary because the QDialog has to be closed and not only the QWidget.
Now a user reported a situation where QDialog::exec() has returned but where the dialog (or only the GUI element?) was still visible. From the log files I can see QDialog::exec() really has returned and the code right after this call was executed.
So my current assumption: the GUI element has lost its parent so that the close() call shown above was not called because "parent" was null.
Any idea how this can happen? Is there a regular way where the parent of a QWidget can disappear?
Generally speaking, using QDialog::exec to reenter the event loop will cause trouble, because suddenly all the code that runs in the main thread must be reentrant. Most likely you're facing fallout from that. Don't reenter the event loop, and you'll be fine or the problem will become reproducible.
If you need to react to the dialog being accepted or rejected, connect code to the relevant slots. I.e. change this:
void do() {
MyDialog dialog{this};
auto rc = dialog.exec();
qDebug() << "dialog returned" << rc;
}
to that:
class Foo : public QWidget {
MyDialog dialog{this};
...
Foo() {
connect(&dialog, &QDialog::done, this, &Foo::dialogDone);
}
void do() {
dialog.show();
}
void dialogDone(int rc) {
qDebug() << "dialog returned" << rc;
}
};
or, if you want to lazily initialize the dialog:
class Foo : public QWidget {
MyDialog * m_dialog = nullptr;
MyDialog * dialog() {
if (! m_dialog) {
m_dialog = new MyDialog{this};
connect(m_dialog, &QDialog::done, this, &Foo::dialogDone);
}
return m_dialog;
}
...
void do() {
dialog()->show();
}
void dialogDone(int rc) {
qDebug() << "dialog returned" << rc;
}
};
It is a horrible antipattern for the child widget to attempt to meddle with the parent. The knowledge that the widget has a parent should not leak into the widget, it should be localized to the parent. Thus, the child widget should emit a signal that indicates that e.g. the data was accepted. When you create the dialog, connect this signal to the dialog's accept() or close() slots:
class MyWidget : public QWidget {
Q_OBJECT
public:
Q_SIGNAL void isDone();
...
};
class MyDialog : public QDialog {
QGridLayout layout{this};
MyWidget widget;
public:
MyDialog() {
layout.addWidget(&widget, 0, 0);
connect(&widget, &MyWidget::isDone, this, &QDialog::accepted);
}
};

In Qt, how to determine the size of the screen?

I want my application to start maximized because the entire computer will be dedicated to that task, so I found the showMaximized() function that fills that requirement, instead of the standard show(). The problem is that I want my widgets to be ratiometric to the resulting useful size of the app window so that we can change hardware or window managers at some point in the future without touching the app at all.
The best way that I've found on my own to do it is as follows, but it relies on a race condition that usually works but sometimes causes the entire UI to be crammed into the top-left corner. Restarting the app fixes it, but I'd rather not have to tell the non-technical users to do that.
main.h:
#ifndef MAIN_H
#define MAIN_H
#include <QtWidgets>
class ResizeFilter : public QObject
{
Q_OBJECT
public:
ResizeFilter();
protected:
bool eventFilter(QObject* obj, QEvent* event);
signals:
void ResizeEvent(QSize size);
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
static MainWindow* GetInstance();
private:
static MainWindow* singleton;
MainWindow();
~MainWindow();
ResizeFilter* filter;
private slots:
void FinishInit(QSize size);
};
#endif // MAIN_H
main.cpp:
#include "main.h"
#include <QApplication>
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
MainWindow::GetInstance();
return app.exec();
}
ResizeFilter::ResizeFilter()
: QObject()
{
}
bool ResizeFilter::eventFilter(QObject* obj, QEvent* event)
{
if(event->type() == QEvent::Resize)
{
QResizeEvent* resizeEv = static_cast<QResizeEvent*>(event);
emit ResizeEvent(resizeEv->size());
}
return QObject::eventFilter(obj, event);
}
MainWindow* MainWindow::singleton = 0;
MainWindow* MainWindow::GetInstance()
{
if(singleton == 0)
{
singleton = new MainWindow();
}
return singleton;
}
MainWindow::MainWindow()
: QMainWindow(0)
{
filter = new ResizeFilter();
installEventFilter(filter);
showMaximized(); //do this before connecting so we miss the first resize event (wrong size) and catch the second (right size)
connect(filter, SIGNAL(ResizeEvent(QSize)), this, SLOT(FinishInit(QSize)));
}
void MainWindow::FinishInit(QSize size)
{
disconnect(filter, SIGNAL(ResizeEvent(QSize)), this, SLOT(FinishInit(QSize)));
delete filter;
filter = 0;
//build widgets based on size argument
}
MainWindow::~MainWindow()
{
}
I'm also open to a less rube-goldberg way of doing it. This looks a bit messy to me, but it's as compact as I think I can get it with my present knowledge.
(The singleton architecture is for another part of the project.)
You can get the geometry and size of the screen using the DesktopWidget.

How to call function after window is shown?

Using Qt I create a QMainWindow and want to call a function AFTER the windows is shown. When I call the function in the constructor the function (a dialog actually) get's called before the window is shown.
If you want to do something while the widget is made visible, you can override QWidget::showEvent like this:
class YourWidget : public QWidget { ...
void YourWidget::showEvent( QShowEvent* event ) {
QWidget::showEvent( event );
//your code here
}
After analyzing the solutions above, it turns that all of them, including the heavily upvoted ones, are faulty.
Many recommend something like this:
class MyWidget : public QWidget {
// ...
};
void MyWidget::showEvent(QShowEvent* event) {
QWidget::showEvent(event);
DoSomething();
}
void MyWidget::DoSomething() {
// ...
}
This works as long as there is no QCoreApplication::processEvents(); in DoSomething. If there is one, it processes all events in the queue, including the QShowEvent which called MyWidget::showEvent in the first place. When it gets to the original QShowEvent, it calls MyWidget::showEvent again, causing an infinite loop.
If this happens, there are three solutions:
Solution 1. Avoid calling processEvents in MyWidget::DoSomething, instead call update or repaint when necessary. If DoSomething calls something else, these functions should avoid processEvents also.
Solution 2. Make DoSomething a slot, and replace direct call to DoSomething() by
QTimer::singleShot(0, this, SLOT(DoSomething()));
Since zero interval timer fires only when after all events in the queue are processed, it will process all events, including the original QShowEvent, remove them from the queue, and only then call DoSomething. I like it the most.
Since only zero interval timer fires only when after all events in the queue are processed, you should not try to "improve" it by lengthening the interval, for instance
QTimer::singleShot(50, this, SLOT(DoSomething())); // WRONG!
Since 50 ms is usually enough time for processing events in the queue, that would usually work, causing an error which is hard to reproduce.
Solution 3. Make a flag which prevents calling DoSomething the second time:
class MyWidget : public QWidget {
// ...
};
void MyWidget::showEvent(QShowEvent* event) {
if (is_opening)
return;
is_opening = true;
QWidget::showEvent(event);
DoSomething();
is_opening = false;
}
void MyWidget::DoSomething() {
// ...
}
Here, is_opening is a boolean flag which should be initialized as false in constructor.
try this:
in mainwindow.h:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
void showEvent(QShowEvent *ev);
private:
void showEventHelper();
Ui::MainWindow *ui;
}
in mainwindow.cpp:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
void MainWindow::showEvent(QShowEvent *ev)
{
QMainWindow::showEvent(ev);
showEventHelper();
}
void MainWindow::showEventHelper()
{
// your code placed here
}
Follow Reza Ebrahimi's example, but keep this in mind:
Do not omit the 5th parameter of connect() function which specifies the connection type; make sure it to be QueuedConnection.
I.E.,
connect(this, SIGNAL(window_loaded), this, SLOT(your_function()), Qt::ConnectionType(Qt::QueuedConnection | Qt::UniqueConnection));
I believe that you'd achieve what you need if you do it this way.
There are several types in signal-slot connections: AutoConnection, DirectConnection, QueuedConnection, BlockingQueuedConnection (+ optional UniqueConnection). Read the manual for details. :)
Assuming you want to run your code in the UI thread of the window after the window has been shown you could use the following relatively compact code.
class MainWindow : public QMainWindow
{
// constructors etc omitted.
protected:
void showEvent(QShowEvent *ev)
{
QMainWindow::showEvent(ev);
// Call slot via queued connection so it's called from the UI thread after this method has returned and the window has been shown
QMetaObject::invokeMethod(this, "afterWindowShown", Qt::ConnectionType::QueuedConnection);
}
private slots:
void afterWindowShown()
{
// your code here
// note this code will also be called every time the window is restored from a minimized state
}
};
It does invoke afterWindowShown by name but that sort of thing is fairly common practice in Qt. There are ways of avoiding this but they're a bit more verbose.
Note that this code should work for any QWidget derived class, not just QMainWindow derived classes.
In theory it might be possible for a very quick user to invoke some sort of action on the UI of the displayed window before afterWindowShown can be called but it seems unlikely. Something to bear in mind and code defensively against perhaps.
I found a nice answer in this question which works well, even if you use a Sleep() function.
So tried this:
//- cpp-file ----------------------------------------
#include "myapp.h"
#include <time.h>
#include <iosteream>
MyApp::MyApp(QWidget *parent)
: QMainWindow(parent, Qt::FramelessWindowHint)
{
ui.setupUi(this);
}
MyApp::~MyApp()
{
}
void MyApp::showEvent(QShowEvent *event) {
QMainWindow::showEvent(event);
QTimer::singleShot(50, this, SLOT(window_shown()));
return;
}
void MyApp::window_shown() {
std::cout << "Running" << std::endl;
Sleep(10000);
std::cout << "Delayed" << std::endl;
return;
}
//- h-file ----------------------------------------
#ifndef MYAPP_H
#define MYAPP_H
#include <QtWidgets/QMainWindow>
#include <qtimer.h>
#include <time.h>
#include "ui_myapp.h"
class MyApp : public QMainWindow
{
Q_OBJECT
public:
MyApp(QWidget *parent = 0);
~MyApp();
protected:
void showEvent(QShowEvent *event);
private slots:
void window_shown();
private:
Ui::MyAppClass ui;
};
#endif // MYAPP_H
I solved it without a timer using Paint event. Works for me at least on Windows.
// MainWindow.h
class MainWindow : public QMainWindow
{
...
bool event(QEvent *event) override;
void functionAfterShown();
...
bool functionAfterShownCalled = false;
...
}
// MainWindow.cpp
bool MainWindow::event(QEvent *event)
{
const bool ret_val = QMainWindow::event(event);
if(!functionAfterShownCalled && event->type() == QEvent::Paint)
{
functionAfterShown();
functionAfterShownCalled = true;
}
return ret_val;
}
The best solution for me is count once paint event:
.H
public:
void paintEvent(QPaintEvent *event);
.CPP
#include "qpainter.h"
#include <QMessageBox> // example
int contPaintEvent= 0;
void Form2::paintEvent(QPaintEvent* event)
{
if (contPaintEvent ==0 )
{
QPainter painter(this);
QMessageBox::information(this, "title", "1 event paint"); // example
// actions
contPaintEvent++;
}
}
Reimplement method void show() like this:
void MainWindow::show()
{
QMainWindow::show();
// Call your special function here.
}

How to capture signal from Q(double)spinbox

I'm writing a mini application by means of Qt 4.7. And I have a reoccurring problem with some QSpinBoxes and QDoubleSpinBoxes. I set the editingFinished() signal and when I change the value in any of these fields they send two signals: when the spinbox loses focus and when enter is pressed. So when I press the tab or enter button my program makes the calculations twice. Is there any smart and easy way to set only lostFocus signal?
P.S.
I'm newbie in Qt. Sorry for my english, I still learning.
edit:
Thanks a lot for your help netrom!
But it is still something wrong...Should it looks like below? I can compile it and run, but it seems SpinBox still react on Enter button.
dialog.h:
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QSpinBox>
#include <QKeyEvent>
namespace Ui {
class SpinBox;
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
private slots:
void on_spinBox_editingFinished();
};
class SpinBox : public QSpinBox
{
Q_OBJECT
public:
explicit SpinBox(QWidget *parent = 0) : QSpinBox(parent) { }
protected:
void keyPressEvent(QKeyEvent *event) {
switch (event->key()) {
case Qt::Key_Return:
case Qt::Key_Enter:
return;
default: break;
}
QSpinBox::keyPressEvent(event);
}
};
#endif // DIALOG_H
You could try checking if the spinbox widget has the focus at the beginning of your slot, it should tell you if the editingFinished() signal was the result of Enter/Return key or the loss of focus.
void Dialog::on_spinBox_editingFinished() {
if(ui->spinBox->hasFocus())
return;
// rest of your code
}
You could override keyPressEvent(QKeyEvent*) and ignore the event when enter is pressed. Another way to do it would be to override focusOutEvent(QFocusEvent*) but make sure that setFocusPolicy() is set to something else than Qt::NoFocus.
Here's an example of the first method: You inherit from QSpinBox and override the keyPressEvent() method and make it ignore the enter/return key:
class SpinBox : public QSpinBox {
Q_OBJECT
public:
SpinBox(QWidget *parent = NULL) : QSpinBox(parent) { }
protected:
void keyPressEvent(QKeyEvent *event) {
switch (event->key()) {
case Qt::Key_Return:
case Qt::Key_Enter:
return;
default: break;
}
QSpinBox::keyPressEvent(event);
}
};
Now just use the editingFinished() signal which will only be given when the focus is lost (by using the mouse or tab key, for instance).