how to take screenshot with Paint event (WM_Paint) in QT - c++

i'm trying to take screenshot with Paint event (WM_Paint) event in QT but i dont know how,
i used this code to take screenshot but it is not really good for do this
it must do screenshot when some changes in desktop not take screenshot every 1000 ms with timer
void MainWindow::shootScreen()
{
originalPixmap = QPixmap(); // clear image for low memory situations
// on embedded devices.
originalPixmap = QPixmap::grabWindow(QApplication::desktop()->winId());
//emit getScreen(originalPixmap);
updateScreenshotLabel();
}
void MainWindow::updateScreenshotLabel()
{
this->ui->label_2->setPixmap(originalPixmap.scaled(this->ui->label_2- >size(),
Qt::KeepAspectRatio,
Qt::SmoothTransformation));
}

Use QObject::installEventFilter on the widgets you are interested in, then check for the proper events. For example, in your MainWindow ui initialization:
void MainWindow::yourUiInitFunc()
{
exampleWidget = new QWidget;
...
exampleWidget->installEventFilter(this);
...
}
Then re-implement eventFilter:
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == exampleWidget && event->type() == QEvent::KeyPress)
shootScreen();
return QMainWindow::eventFilter(obj, event);
}

tank you Jon Harper your codes was useful but i changed somethings there
your code just work when some key press event on my project form but i added paint method to your if and now it work on windows events too
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == this && QEvent::Paint == event->type()) {
shootScreen();
}
return false;
}

Related

How to call a user method after show method in qt c++ [duplicate]

In Delphi I often made an OnAfterShow event for the main form. The standard OnShow() for the form would have little but a postmessage() which would cause the OnafterShow method to be executed.
I did this so that sometimes lengthy data loading or initializations would not stop the normal loading and showing of the main form.
I'd like to do something similar in a Qt application that will run on a desktop computer either Linux or Windows.
What ways are available to me to do this?
You can override showEvent() of the window and call the function you want to be called with a single shot timer :
void MyWidget::showEvent(QShowEvent *)
{
QTimer::singleShot(50, this, SLOT(doWork());
}
This way when the windows is about to be shown, showEvent is triggered and the doWork slot would be called within a small time after it is shown.
You can also override the eventFilter in your widget and check for QEvent::Show event :
bool MyWidget::eventFilter(QObject * obj, QEvent * event)
{
if(obj == this && event->type() == QEvent::Show)
{
QTimer::singleShot(50, this, SLOT(doWork());
}
return false;
}
When using event filter approach, you should also install the event filter in the constructor by:
this->installEventFilter(this);
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;
}

How to recognize the type of a click on a QWidget

Using the mouse events of QWidget it is possible to catch the user's interaction with the widget. However, Qt does not deliver only the resulting action (a click or a double click), but provides the whole sequence of events as it is. Thus, the action should be recognized manually and my question is: How? QMouseEvent::flags() doesn't help much, since the only flag Qt::MouseEventCreatedDoubleClick is never set, as reported here.
In other words, how to properly emit those two signals, defined in the header file of MyWidget (derived from QWidget):
void clicked();
void doubleClicked();
having those slots:
void MyWidget::mousePressEvent(QMouseEvent *event)
{
...
}
void MyWidget::mouseReleaseEvent(QMouseEvent *event)
{
...
}
void MyWidget::mouseDoubleClickEvent(QMouseEvent *event)
{
...
}
By properly I mean that if the user double clicks MyWidget only MyWidget::doubleClicked should be emited instead of MyWidget::clicked followed by MyWidget::doubleClicked;
I met the same problem before. I think it is by design of Qt. This may not be a best solution, but what you can do is to create an event filter and 'wait' for a while:
bool m_bClicked = false;
bool eventFilter(QObject* object, QEvent* event)
{
if(event->type() == QEvent::MouseButtonPress)
{
// Wait for 400 milliseconds
m_bClicked = true;
QTimer::singleShot(400, this, SLOT(eventMousePressTimer()));
return true;
}
else if(event->type() == QEvent::MouseButtonDblClick)
{
// Double-clicked
m_bClicked = false;
return true;
}
return false;
}
void eventMousePressTimer()
{
if(m_bClicked)
{
// Single-clicked
}
}

My Qt eventFilter() doesn't stop events as it should

Something is fundamentally wrong with my eventFilter, as it lets every single event through, while I want to stop everything. I've read lots of documentation on QEvent, eventFilter() and so on, but obviously I'm missing something big. Essentially, I'm trying to create my own modal-functionality for my popup-window class based on QDialog. I want to implement my own since the built-in setModal(true) includes a lot of features, e.g. playing QApplication::Beep(), that I want to exclude. Basically, I want to discard all events going to the QWidget (window) that created my popup. What I have so far is,
// popupdialog.h
#ifndef POPUPDIALOG_H
#define POPUPDIALOG_H
#include <QDialog>
#include <QString>
namespace Ui {class PopupDialog;}
class PopupDialog : public QDialog
{
Q_OBJECT
public:
explicit PopupDialog(QWidget *window=0, QString messageText="");
~PopupDialog();
private:
Ui::PopupDialog *ui;
QString messageText;
QWidget window; // the window that caused/created the popup
void mouseReleaseEvent(QMouseEvent*); // popup closes when clicked on
bool eventFilter(QObject *, QEvent*);
};
...
// popupdialog.cpp
#include "popupdialog.h"
#include "ui_popupdialog.h"
PopupDialog::PopupDialog(QWidget *window, QString messageText) :
QDialog(NULL), // parentless
ui(new Ui::PopupDialog),
messageText(messageText),
window(window)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose, true); // Prevents memory leak
setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
ui->message_text_display->setText(messageText);
window->installEventFilter(this);
//this->installEventFilter(window); // tried this also, just to be sure ..
}
PopupDialog::~PopupDialog()
{
window->removeEventFilter(this);
delete ui;
}
// popup closes when clicked on
void PopupDialog::mouseReleaseEvent(QMouseEvent *e)
{
close();
}
Here's the problem, the filter doesn't work. Note that if I write a std::cout
inside the if(...), I see that it does trigger whenever events are sent to window, it just doesn't stop them.
bool PopupDialog::eventFilter(QObject *obj, QEvent *e)
{
if( obj == window )
return true; //should discard the signal (?)
else
return false; // I tried setting this to 'true' also without success
}
When the user interacts with the main program, a PopupDialog can be created like this:
PopupDialog *popup_msg = new PopupDialog(ptr_to_source_window, "some text message");
popup_msg->show();
// I understand that naming the source 'window' might be a little confusing.
// I apologise for that. The source can in fact be any 'QWidget'.
Everything else works as expected. Only the event filter fails. I want the filter to remove events sent to the window that created the popup; like mouse clicking and key pressing, until the popup is closed. I'm expecting to be extremely embarrassed when someone points out a trivial fix in my code.
You have to ignore all events that arrive in the widget tree of the window. Therefore, you need to install the eventFilter application-wide and check, if the object you are filtering on is a descendant of window. In other words: Replace
window->installEventFilter(this);
by
QCoreApplication::instance()->installEventFilter(this);
and implement the event filter function this way:
bool PopupDialog::eventFilter(QObject *obj, QEvent *e)
{
if ( !dynamic_cast<QInputEvent*>( event ) )
return false;
while ( obj != NULL )
{
if( obj == window )
return true;
obj = obj->parent();
}
return false;
}
I tried it, tested it and it worked for me.
Note: Using event filters in Qt is a bit messy in my experience, since it is not quite transparent what is happening. Expect bugs to pop up from time to time. You may consider disabling the main window instead, if you and your clients don't have a problem with the grayed-out main window as a consequence.
After the massive amount of responses, feedback, suggestions and time ivested in extensive research I've finally found what I believe to be the optimal, and safest solution. I wish to express my sincere gratidtude to everyone for their aid to what Kuba Ober describes as "(...) not as simple of a problem as you think".
We want to filter out all certain events from a widget, including its children. This is difficult, because events may be caught in the childrens default eventfilters and responded to, before they are caught and filtered by the the parent's custom filter for which the programmer implements. The following code solves this problem by installing the filter on all children upon their creation. This example assumes the use of Qt Creator UI-forms and is based on the following blog post: How to install eventfilters for all children.
// The widget class (based on QMainWindow, but could be anything) for
// which you want to install the event filter on, includings its children
class WidgetClassToBeFiltered : public QMainWindow
{
Q_OBJECT
public:
explicit WidgetClassToBeFiltered(QWidget *parent = 0);
~WidgetClassToBeFiltered();
private:
bool eventFilter(QObject*, QEvent*);
Ui::WidgetClassToBeFiltered *ui;
};
...
WidgetClassToBeFiltered::WidgetClassToBeFiltered(QWidget *parent) :
QMainWindow(parent), // Base Class constructor
ui(new Ui::WidgetClassToBeFiltered)
{
installEventFilter(this); // install filter BEFORE setupUI.
ui->setupUi(this);
}
...
bool WidgetClassToBeFiltered::eventFilter(QObject *obj, QEvent* e)
{
if( e->type() == QEvent::ChildAdded ) // install eventfilter on children
{
QChildEvent *ce = static_cast<QChildEvent*>(e);
ce->child()->installEventFilter(this);
}
else if( e->type() == QEvent::ChildRemoved ) // remove eventfilter from children
{
QChildEvent *ce = static_cast<QChildEvent*>(e);
ce->child()->removeEventFilter(this);
}
else if( (e->type() == QEvent::MouseButtonRelease) ) // e.g. filter out Mouse Buttons Relases
{
// do whatever ..
return true; // filter these events out
}
return QWidget::eventFilter( obj, e ); // apply default filter
}
Note that this works, because the eventfilter installs itself on added children! Hence, it should also work without the use of UI-forms.
Refer this code to filter out specific event:-
class MainWindow : public QMainWindow
{
public:
MainWindow();
protected:
bool eventFilter(QObject *obj, QEvent *ev);
private:
QTextEdit *textEdit;
};
MainWindow::MainWindow()
{
textEdit = new QTextEdit;
setCentralWidget(textEdit);
textEdit->installEventFilter(this);
}
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == textEdit) {
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
qDebug() << "Ate key press" << keyEvent->key();
return true;
} else {
return false;
}
} else {
// pass the event on to the parent class
return QMainWindow::eventFilter(obj, event);
}
}
If you want to set more specific event filter on multiple widgets you can refer following code:
class KeyPressEater : public QObject
{
Q_OBJECT
protected:
bool eventFilter(QObject *obj, QEvent *event);
};
bool KeyPressEater::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
qDebug("Ate key press %d", keyEvent->key());
return true;
} else {
// standard event processing
return QObject::eventFilter(obj, event);
}
}
KeyPressEater *keyPressEater = new KeyPressEater(this);
QPushButton *pushButton = new QPushButton(this);
pushButton->installEventFilter(keyPressEater);

Take action after the main form is shown in a Qt desktop application

In Delphi I often made an OnAfterShow event for the main form. The standard OnShow() for the form would have little but a postmessage() which would cause the OnafterShow method to be executed.
I did this so that sometimes lengthy data loading or initializations would not stop the normal loading and showing of the main form.
I'd like to do something similar in a Qt application that will run on a desktop computer either Linux or Windows.
What ways are available to me to do this?
You can override showEvent() of the window and call the function you want to be called with a single shot timer :
void MyWidget::showEvent(QShowEvent *)
{
QTimer::singleShot(50, this, SLOT(doWork());
}
This way when the windows is about to be shown, showEvent is triggered and the doWork slot would be called within a small time after it is shown.
You can also override the eventFilter in your widget and check for QEvent::Show event :
bool MyWidget::eventFilter(QObject * obj, QEvent * event)
{
if(obj == this && event->type() == QEvent::Show)
{
QTimer::singleShot(50, this, SLOT(doWork());
}
return false;
}
When using event filter approach, you should also install the event filter in the constructor by:
this->installEventFilter(this);
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;
}

Make QSpinBox react to mouse wheel events when cursor is not over it

I am using Qt 5.3.2 with Qt Creator 3.2.1 with MinGW 4.8.2 on Windows 7. I have a QSpinBox and can change its value with the mouse wheel only if the mouse is over the QSpinBox. If the mouse is not over the QSpinBox, scrolling the mouse wheel has no effect, even though the QSpinBox still has focus. What do I need to do to be able to change values in the QSpinBox that has focus with the mouse wheel even if the mouse is not hovering over it? Setting mouseTracking to true does not have that effect.
Use eventFilter to do this. Install it on your mainWindow:
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == this && event->type() == QEvent::Wheel)
{
QWheelEvent *wheelEvent = static_cast<QWheelEvent *>(event);
if(wheelEvent->delta() > 0)
ui->spinBox->setValue(ui->spinBox->value() + 1);
else
ui->spinBox->setValue(ui->spinBox->value() - 1);
}
}
It is just example, so you can improve it as you want.
Or use this:
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == this && event->type() == QEvent::Wheel)
{
QApplication::sendEvent(ui->spinBox,event);
}
}
In this example, when you detect wheel event, you send it to your spinbox.
But don't forget
protected:
bool eventFilter(QObject *obj, QEvent *event);//in header
and
qApp->installEventFilter(this);//in constructor
As DmitrySazonov recommended. We will detect wheelEvents when our spinBox in focus, when spinBox losed focus, we don't react on wheel(other widgets react normal). We do this in one eventFilter. To do this provide new bool variable. For example:
private:
bool spin;//in header
Initialize it in constructor:
spin = false;
And your eventFilter should be.
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if(obj == ui->spinBox && event->type() == QEvent::FocusIn)
spin = true;
if(spin)
{
if (obj == this && event->type() == QEvent::Wheel)
{
QApplication::sendEvent(ui->spinBox,event);
}
}
if(obj == ui->spinBox && event->type() == QEvent::FocusOut)
spin = false;
}
Or do just this, without additional variable:
if (obj == this && event->type() == QEvent::Wheel)
{
if(ui->spinBox->hasFocus())
QApplication::sendEvent(ui->spinBox,event);
}
I did not mention it in the question but I have more that one QSpinBox and testing them all seems sub-optimal, so I need a generic message forwarder. Based on the Chernobyl's code I made my own version of the message filter:
bool MainWindow::eventFilter(QObject *obj, QEvent *event){
if (obj == this && event->type() == QEvent::Wheel)
{
auto focusWidget = QApplication::focusWidget();
if (focusWidget){
qApp->removeEventFilter(this);
QApplication::sendEvent(focusWidget, event);
qApp->installEventFilter(this);
return true;
}
}
return false;
}
This forwards all QWheelEvents to the QWidget with the focus. One could also add other events that need to be forwarded.
The qApp->removeEventFilter and qApp->installEventFilter inside the event filter is the only way I found that prevents the event filter calling itself when scrolling on the main window causing a stack overflow (condition focusWidget != this does not help). There isprobably a way to prevent the infinite recursion without reinstalling the event filter on every QWheelEvent.