I use the events QEvent::ApplicationActivate and QEvent::ApplicationDeactivate to show/hide some part of my app when needed.
But now, thoses events are flagged as deprecated :
This enum has been deprecated. Use ApplicationStateChange instead.
So I tried to switch to this event. It is triggered when it should, but I can't find a way to get the application state with some sort of cast or any getters.
Any ideas ?
Just as for any other event, you need to cast it to more specific type to get event properties. The class is QApplicationStateChangeEvent. The documentation is surprisingly silent about it, but it exists and is declared in event.h. The following code works fine in my Qt 5.1 installation:
#include <QApplicationStateChangeEvent>
bool MainWindow::eventFilter(QObject *o, QEvent *e) {
if (e->type() == QEvent::ApplicationStateChange) {
qDebug() << "state:"
<< static_cast<QApplicationStateChangeEvent*>(e)->applicationState();
}
return QMainWindow::eventFilter(o, e);
}
Related
I've subclassed Qt QAbstractSpinBox to create ComplexSpinBox that handles complex numbers.
To do this, I'm overriding keyPressEvent(), mousePressEvent(), and wheelEvent() and if the keyboard Ctrl modifier is active then the imaginary portion of the complex number is incremented or decremented. Otherwise the real portion is updated. (Also including a custom validator for complex numbers.)
All is cool except that holding the up and down arrow buttons down doesn't make them repeat.
What am I leaving out?
Well, one, if not the way to do this is to add
private:
QTimer *timer;
in the definition and create it in the constructor:
timer = new QTimer(this);
connect (timer, &QTimer::timeout,
[=]() {
// do your thing
});
Then somewhere in the mousePressEvent(QMouseEvent *mouseEvent) method
timer->start (100);
For reasons that aren't clear to me, mouseReleaseEvent() doesn't get triggered in this environment so you have to use an event filter. Back in the definition, you'll need:
protected:
bool eventFilter(QObject *object, QEvent *event);
and the method
bool ComplexSpinBox::eventFilter(QObject *object, QEvent *event)
{
if (object == this && event->type() == QEvent::MouseButtonRelease) {
timer->stop ();
}
return false;
}
I use Qt to develop my application, and I want to listen the global mouse and keyboard events, so I can do something after detect these events. On windows platform, I use SetWindowsHookEx API. But I don't know how to do the similar thing on Linux.
My code on Windows as below:
/*********************listen mouse event*****************************/
mouseHook = SetWindowsHookEx(WH_MOUSE_LL, mouseProc, NULL, 0);
if (mouseHook == NULL) {
qDebug() << "Mouse Hook Failed";
}
/*********************listen keyboard event*****************************/
keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, keyBoardProc, NULL, 0);
if (keyboardHook == NULL) {
qDebug() << "Keyboard Hook Failed";
}
Thank you for your answer sincerely!
Use the vast and helpful Qt Event System. That way you can use the same code on Windows as well as Linux.
I would recommend adding a eventFilter to your application. If the event caught is a QEvent::KeyPress or QEvent::MouseButtonPress( or QEvent::MouseButtonDblClick based on your requirement), take the necessary action.
If the event filter is to be applied for a particular widget in the main window, in the constructor of the main window, add
ui->myWidget->installEventFilter(this);
Now you need to implement the protected method eventFilter for the main window.
In the header file.
protected:
bool eventFilter(QObject* obj, QEvent* event);
Implementation
bool MainWindow::eventFilter(QObject* obj, QEvent* event)
{
if(event->type() == QEvent::KeyPress)
{
QKeyEvent* keyEvent = static_cast<QKeyEvent *>(event);
qDebug() << "Ate key press " << keyEvent->text();
return true;
}
else if(event->type() == QEvent::MouseButtonPress)
{
qDebug() << "Mouse press detected";
return true;
}
// standard event processing
return QObject::eventFilter(obj, event);
}
It is also possible to filter all events for the entire application, by installing an event filter on the QApplication or QCoreApplication object. You can read more on that from the documentation link I provided in the first line.
If the event filter has to be reused, I would recommend adding a dummy class which inherits from QObject. In this class you can just implement the function eventFilter. You can now pass an instance of this class to the function installEventFilter and communicate with the other objects using SIGNALS.
EDIT:
If you are looking to capture events even outside the application, Qt itself does not support that (yet). But you can use the Qxt library that adds support for it using the QxtGlobalShortcut class.
I'm trying to create a Ctrl + Mousewheel macro to zoom in and out of an image view in my application.
Currently I am trying to use the current code:
new QShortcut(QKeySequence(Qt::CTRL + QWidget::wheelEvent(QWheelEvent *event)), this, SLOT(zoom()));
However I get the error QWheelEvent does not refer to a value. I have all the necessary includes in my header file so I do not understand why I'm getting the error.
Is it illegal to bind the widget event in conjunction within a QKeySequence? If so, how should I handle the event?
You can't use QKeySequence in this way. You should reimplement wheelEvent or use next event filter (it is example how to zoom in/out in textEdit, you can use this code for your special case):
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if(obj == ui->plainTextEdit && event->type() == QEvent::Wheel )
{
QWheelEvent *wheel = static_cast<QWheelEvent*>(event);
if( wheel->modifiers() == Qt::ControlModifier )
if(wheel->delta() > 0)
ui->plainTextEdit->zoomIn(2);
else
ui->plainTextEdit->zoomOut(2);
}
return QObject::eventFilter(obj, event);
}
Main idea: catch wheel event and check is Ctrl modifier is pressed.
To use eventFilter you should also:
protected:
bool eventFilter(QObject *obj, QEvent *event);//in header
and
qApp->installEventFilter(this);//in constructor
Note: I showed example with event filter because it is not require subclassing, it is not better or something else, reimplement wheelEvent with similar code and you will get absolutely same result.
Is there a signal that tells when 'show' function finishes?
I have a problem in my code: If I write:
QMainWinObj.show();
QMainWinObj.someGuiFunc();
the code doesn't work. But, if I write:
QMainWinObj.show();
sleep(3000);
QMainWinObj.someGuiFunc();
It does.
So I think the problem is that 'show' dosn't finish its jub before I call 'someGuiFunc'. That's why I want to have some kind of a sign that 'show' is finished..
This may be a bit dated but since nobody else answered it except the one:
Since there is no "Show" signal I suggest overriding the show event like this:
In your mainwindow.cpp file:
void MainWindow::show()
{
QMainWindow::show();
QApplication::processEvents();
emit windowShown();
}
In your mainwindow.h file, somewhere in MainWindow's declaration:
...
class MainWindow: public QMainWindow
{
...
signals:
void windowShown();
...
}
...
Then, when you go to the designer, right click on the main window (very top of the object tree), and select "Change signals/slots". In the "Signals" frame, click the "+" button, and you will need to add "windowShown()" and then press enter, and then the OK button (note that the elipses "..." denote other code that is already in your header).
That's it -- you can now use the signals/slots editor to link slots up to the 'windowShown' signal whenever you want. Now if you want something more like Microsoft's "Loaded" event which I think is used in .NET you will need to create some instance variable and flag it so that every time the window is shown, it isnt emitted, for example:
void MainWindow::show()
{
QMainWindow::show();
QApplication::processEvents();
emit windowShown();
if (firstTimeShown == true)
{
emit windowLoaded();
firstTimeShown = false;
}
}
Also, don't forget to initialize the variable to 'true' in your constructor:
MainWindow::MainWindow(QObject* parent)
...
{
firstTimeShown = true; // put this somewhere before ui->setupUi()
}
If you decide to put it in the initializer list however, make sure it is in proper order. The compiler will complain if the variables are not instantiated in a top-to-bottom fashion as declared in the class' header.
Now, make sure when you define firstTimeShown in your header, that you make it private. And lets not forget the added signals:
class MainWindow : public QMainWindow
{
...
signals:
void windowLoaded();
void windowShown();
private:
bool firstTimeShown;
...
That's about it. With the flexibility of signals and slots, its pretty easy to mimic any event that you might find from windows forms or from MFC. It just takes a little effort on the programmer's part. Once you get the hang of it however it it'll be like second nature.
note: there probably are optimizations or better and more precise ways of making the "Loaded" and "Shown" signals perform but I have left things like this out for simplicity's sake. And to come back to the question at hand, calling QApplication::processEvents() is most likely what you want to do instead of waiting a fixed amount of time because who knows how long it will take if the user is running 100 other things on top of it, etc, etc. Hope that helped, the extra explanation was included hoping that it might give you a better way to do the things that you want to do instead of waiting for something to be done, 'knowing' it is done is a much better alternative.
There is no such signal, but having QMainWindow subclassed you can override showEvent event.
void MainWindow::showEvent(QShowEvent *){
//your code
}
More info here: http://qt-project.org/doc/qt-4.8/qwidget.html#showEvent
Be aware it's called every time your window is about to be displayed.
Problem can decide without subclassing, just installing event filter like this:
class CWidgetIsPainting_EF : public QObject
{
bool m_bIsPainted = false;
public:
CWidgetIsPainting_EF( QObject * parent = 0 ) : QObject (parent) { }
inline bool IsPainted() const { return m_bIsPainted; }
inline void setIsPainted( bool bIsPainted ) { m_bIsPainted = bIsPainted; }
protected:
bool eventFilter( QObject * obj, QEvent *event )
{
if (event->type() == QEvent::Paint)
{
m_bIsPainted = true;
return true;
};
return QObject::eventFilter(obj, event);
}
};
...
...
CWidgetIsPainting_EF * pPaintingEF = new CWidgetIsPainting_EF( m_pWidget );
m_pWidget->installEventFilter( pPaintingEF );
...
...
while ( !pPaintingEF->IsPainted() )
QApplication::processEvents();
Override bool event(QEvent *event) and catch the 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;
}
I have an open Qt Mac app.
I am clicking the app Icon
Is there a way to get a notification for this in the app?
I couldn't get the original answer to compile properly due to deprecation warnings (post-OS X 10.5) and type errors; I changed a few type names and got it to compile, but the code still didn't work.
It turns out that newer versions of Qt (4.8.7+, including 5.x; I use 5.4.1) implement the method we want to add, and class_addMethod fails if the method already exists. See this QTBUG.
Note: the above bug report contains a slightly different solution (I found it after fixing the issue myself).
One solution, that works for me, is to check if the method exists. If it does, we replace it. If not, we simply add it.
I have not tested this code on older Qt versions, but it uses OPs logic, so it should work.
Here's my code. As in OPs case, all code is in the .cpp file of a QApplication subclass.
#ifdef Q_OS_MAC
#include <objc/objc.h>
#include <objc/message.h>
void setupDockClickHandler();
bool dockClickHandler(id self,SEL _cmd,...);
#endif
My QApplication subclass constructor contains
#ifdef Q_OS_MAC
setupDockClickHandler();
#endif
And finally, somewhere in the same file (at the bottom, in my case):
#ifdef Q_OS_MAC
void setupDockClickHandler() {
Class cls = objc_getClass("NSApplication");
objc_object *appInst = objc_msgSend((objc_object*)cls, sel_registerName("sharedApplication"));
if(appInst != NULL) {
objc_object* delegate = objc_msgSend(appInst, sel_registerName("delegate"));
Class delClass = (Class)objc_msgSend(delegate, sel_registerName("class"));
SEL shouldHandle = sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:");
if (class_getInstanceMethod(delClass, shouldHandle)) {
if (class_replaceMethod(delClass, shouldHandle, (IMP)dockClickHandler, "B#:"))
qDebug() << "Registered dock click handler (replaced original method)";
else
qWarning() << "Failed to replace method for dock click handler";
}
else {
if (class_addMethod(delClass, shouldHandle, (IMP)dockClickHandler,"B#:"))
qDebug() << "Registered dock click handler";
else
qWarning() << "Failed to register dock click handler";
}
}
}
bool dockClickHandler(id self,SEL _cmd,...) {
Q_UNUSED(self)
Q_UNUSED(_cmd)
// Do something fun here!
qDebug() << "Dock icon clicked!";
// Return NO (false) to suppress the default OS X actions
return false;
}
#endif
Also see the Apple documentation on the applicationShouldHandleReopen:hasVisibleWindows: method.
In order for this to compile, you also need to link with some extra frameworks.
Using qmake, I added the following to my .pro file:
LIBS += -framework CoreFoundation -framework Carbon -lobjc
Those flags are of course exactly what you should add to the c++ or clang++ command line, if you compile manually.
That should be everything that's required.
It's crazy, but i got it, and without any Objective-C coding:
I derived QApplication. In the *.cpp portion of my derived class i put:
#ifdef Q_OS_MAC
#include <objc/objc.h>
#include <objc/message.h>
bool dockClickHandler(id self,SEL _cmd,...)
{
Q_UNUSED(self)
Q_UNUSED(_cmd)
((MyApplictionClass*)qApp)->onClickOnDock();
return true;
}
#endif
in my derived application class constructor I put:
#ifdef Q_OS_MAC
objc_object* cls = objc_getClass("NSApplication");
SEL sharedApplication = sel_registerName("sharedApplication");
objc_object* appInst = objc_msgSend(cls,sharedApplication);
if(appInst != NULL)
{
objc_object* delegate = objc_msgSend(appInst, sel_registerName("delegate"));
objc_object* delClass = objc_msgSend(delegate, sel_registerName("class"));
const char* tst = class_getName(delClass->isa);
bool test = class_addMethod((objc_class*)delClass, sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:"), (IMP)dockClickHandler,"B#:");
if (!test)
{
// failed to register handler...
}
}
#endif
Added this simple method to my application class (note it's referred to from the handler at the top of my answer)
void MyApplictionClass::onClickOnDock()
{
// do something...
}
Works like charm.
Starting from Qt5.4.0 you can handle QEvent, that related to click on dock: QEvent::ApplicationActivate.
https://bugreports.qt.io/browse/QTBUG-10899
https://doc.qt.io/qt-5/qevent.html
The problem with QEvent::ApplicationActivate is that it will be emitted for every activation - eg., even if you switch to the app on Application Switcher. The native behavior is to show the app only on Dock icon click, not when you are switching by cmd+tab.
But, there is a hack that works at least for Qt 5.9.1. The Dock icon click produces 2 sequential QEvent::ApplicationStateChangeEvent events, meanwhile cmd+tab - only one.
So, this code will emit Dock click signal quite accurately. App class is the application class inherited from QApplication and also an event filter for itself.
bool App::eventFilter(QObject* watched, QEvent* event)
{
#ifdef Q_OS_MACOS
if (watched == this && event->type() == QEvent::ApplicationStateChange) {
auto ev = static_cast<QApplicationStateChangeEvent*>(event);
if (_prevAppState == Qt::ApplicationActive
&& ev->applicationState() == Qt::ApplicationActive) {
emit clickedOnDock();
}
_prevAppState = ev->applicationState();
}
#endif // Q_OS_MACOS
return QApplication::eventFilter(watched, event);
}