Qt / C++ - Monitor specified input without focus - c++

I want to be able to press a specific Qt::Key at any time regardless of focus. For example, ctrl+shift+F activates a method that brings the window back into focus.
I've done google searches for how to monitor input without focus in qt but I can't find anything.

You can do this either by subclassing QApplication and overriding its event(QEvent *) method, or (more commonly) by calling qApp->installEventFilter(someObject), where (someObject) is some Qt object (whichever one is most convenient for you) whose eventFilter(QObject *, QEvent *) method you have overridden with your own implementation that watches for the appropriate QKeyEvent and does the appropriate action in response.

This feature is not implemented in Qt. You can use Qxt. Qxt is an extension library for Qt providing a suite of cross-platform utility classes to add functionality not readily available in Qt. It has Global Shortcut (hot keys) which detects key presses even if the application is minimized or hidden.
After compiling Qxt, link your application to it by adding these to your .pro :
CONFIG += qxt
QXT = core gui
And include QxtGlobalShortcut :
#include <QxtGlobalShortcut>
Example usage :
QxtGlobalShortcut* shortcut = new QxtGlobalShortcut(window);
connect(shortcut, SIGNAL(activated()), window, SLOT(toggleVisibility()));
shortcut->setShortcut(QKeySequence("Ctrl+Shift+F"));

Found the answer here after further searching:
http://qt-project.org/forums/viewthread/35192
I didn't know they were referring to as 'global' hotkeys so I was essentially describing in google search.
I will elaborate since the code linked there isn't accurate (had to tweak a lot).
This is my current code:
#define MOD_NOREPEAT 0x4000
#define MOD_CTRL 0x0002
#define MOD_ALT 0x0001
#define MOD_SHIFT 0x0004
int main(int argc, char *argv[])
{
RegisterHotKey(NULL, 1, MOD_ALT | MOD_SHIFT | MOD_NOREPEAT, 0x46);
QApplication a(argc, argv);
Interface w;
w.show();
a.processEvents();
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
if(w.isClosing)
return msg.wParam;
TranslateMessage(&msg);
DispatchMessage(&msg);
if(msg.message == WM_HOTKEY)
{
if(msg.wParam == 1)
qDebug() << "Hot Key activated: HOME";
}
}
return msg.wParam;
}
The a.processEvents will make your QApplication continue processing events regardless of the while loop. When I exited the program it wouldn't kill because of the loop running in main, so now I check for a bool from the Interface class that overrides closeEvent as such:
void closeEvent(QCloseEvent *);
And in class file:
void Interface::closeEvent(QCloseEvent *)
{
isClosing = true;
}
The only downside is hex codes for hotkeys. If anyone has a solution please let me know. 0x46 is F.

It is possible to detect a pressed key outside the window. You only need to use the built-in Windows libraries. You can read how to do it in this article. Unfortunately, it's not that simple on the Unix systems currently.

Related

Application with multiple windows in Qt

What is a good way to create application with multiple windows in Qt? Something like GIMP. I have already read this answer and it is not what I need.
I have two windows, one with controls and one with OpenGL inside. Right now I have something like this:
int main()
{
// some code
MainWindow mw;
mw.show();
GLWindow gw;
gw.show();
// ...
}
I don't like this for two reasons. First reason is when I start my application, window with OpenGL will be on top (because it is last to call show()) but MainWindow will be buried somewhere under all opened windows. What I need is both windows in front of everything (like GIMP), preferably with focus on MainWindow (I guess I can bring them to front, so that is minor issue). Second reason I don't like this is that my application will be closed completely only when I close both windows.
So I was thinking of having a reference to GLWindow inside MainWindow, and creating it from MainWindow.
Would that be a good way to create application with several windows?
EDIT: GLWindow inherits from QOpenGLWindow.
You are doing right, but with the following simple tricks, you can resolve both issues that cause you do not like your right method:
To activate both windows, just do as follows:
MainWindow mw;
mw.show();
mw.activateWindow();
GLWindow gw;
gw.show();
gw.activateWindow();
To resolve the quit problem, you have to override the closeEvent in both windows. To do that, add the following code into the header file of your both windows:
protected:
void closeEvent(QCloseEvent *event) override;
and in the implementation of the closeEvent, just write qApp->quit();. This way once you close either window, your application will terminate completely.
MainWindow.cpp
void MainWindow::closeEvent(QCloseEvent *event)
{
qApp->quit();
}
and
GLWindow.cpp
void GLWindow::closeEvent(QCloseEvent *event)
{
qApp->quit();
}
I'm not sure I completely understand the situation but, typically, you can make the 'secondary' widgets child dialogs of the QMainWindow. Given your example code that would be something like...
int main ()
{
// some code
MainWindow mw;
mw.show();
/*
* Secondary windows should be created with the QMainWindow mw
* as their parent.
*/
GLWindow gw(&mw);
/*
* Set the Qt::Dlaog window flag to `encourage' the QMainWindow
* and its children to behave as a group.
*/
gw.setWindowFlags(gw.windowFlags() | Qt::Dialog);
gw.show();
// ...
}
Now all widgets behave as a group (possibly subject to the window manager being used) and closing the QMainWindow will, by default, close the application.

C++ Global Hotkeys with platform APIs

I'm working on an application for taking screenshots on Windows, OSX and Linux in C++/Qt. Now I need to set global hotkeys, so the user can take screenshots when the application is running in the background. I tried with Qxt and UGlobalHotkey, which are both Qt libraries, but neither of them seemed to work.
I tried to implement it for OSX with Carbon (tutorial), but I need to call a class member function, which just doesn't work. Could someone provide me with an example? You can find my code here. The function i need to call is new_screenshot().
Or is there any other way to achieve something like this? I really need my application to take a screenshot from the background, otherwise it's pretty useless (yes, I should probably have implemented it at the very beginning to see if it even works). Would it maybe be better to have a separate client for every platform (Cocoa Swift for OSX, GTK for Linux, C# client for Windows)? I have often thought about this the past few days.
Do I understand correctly that you want to call new_screenshot from the hot key event handler? If so, InstallApplicationEventHandler lets you pass a pointer to user data in 4th argument. Pass a pointer to your MainWindow instance (based on code from the tutorial):
MainWindow *mainWindow = ... // get main window somehow
InstallApplicationEventHandler(&MyHotKeyHandler,1,&eventType,mainWindow,NULL);
Then you can use it in the event handler.
OSStatus MyHotKeyHandler(EventHandlerCallRef nextHandler,EventRef theEvent, void *userData)
{
//Do something once the key is pressed
static_cast<MainWindow*>(userData)->new_screenshot();
return noErr;
}
I did something in the past with MFC and WIN32 API....so it only works on Windows...but pressing ALT+F10 was able to hide/show a window...
void CWinHideDlg::OnButtonActive()
{
CString tmp;
GetDlgItemText(IDC_BUTTON_ACTIVE,tmp);
if(0 == strcmp(tmp.GetBuffer(tmp.GetLength()),"Activate"))
{
m_myAtom=GlobalAddAtom("MY_GLOBAL_HOT_HIDE_KEY");
int err=RegisterHotKey(this->GetSafeHwnd(),m_myAtom,MOD_ALT,VK_F10);
SetDlgItemText(IDC_BUTTON_ACTIVE,"Stop");
CButton *pBtn = (CButton *)GetDlgItem(IDC_BUTTON_UNHIDE);
pBtn->EnableWindow(TRUE);
SetDlgItemText(IDC_STATIC_INFO,"Set the mouse over the window \nand press ALT + F10 to hide it...");
}
else
{
UnregisterHotKey(this->GetSafeHwnd(),m_myAtom);
GlobalDeleteAtom(m_myAtom);
CButton *pBtn = (CButton *)GetDlgItem(IDC_BUTTON_UNHIDE);
pBtn->EnableWindow(FALSE);
SetDlgItemText(IDC_BUTTON_ACTIVE,"Activate");
}
}
Basically this code activates/deactivates the hot key ALT+F10, once it activates you can hide/unhide a running window on the system by setting the mouse pointer over the window and press ALT+F10...
This is from the WindowProc function:
if(message == WM_HOTKEY)
{
CString tmp;
POINT pc;
GetCursorPos(&pc);
if(GetAsyncKeyState(VK_F10))
{
HWND hwnd=::WindowFromPoint(pc);
if(hwnd)
{
tmp.Format("%08Xh",hwnd);
m_HideWins.InsertString(m_HideWins.GetCount(),tmp);
::ShowWindow(hwnd,SW_HIDE);
}
}
}
You can use the code to register your own HOT Key and use it to take a screenshot...
Hope it helps...

Qt event when anything changed on the window/screen + Screenshot

I'm thinking of extending a QT4 application with some debug possibilities, to make it easier analyzing customer issues. The application already has a "Debug" mode, when this is enabled, a lot of log entries generated, which is hard to read.
What I would like to achive is taking a screenshot of the application, whenever something is changed on the GUI. I know that it may take a lot of pictures, but generally Debug mode is not enabled for a long time. The problem is I cannot find such an event/signal. So I have two question:
Is there such an event I could subscribe? I mean, an event that is
fired whenever anything changes on the screen.
Can I take a screenshot of the application using Qt?
Thanks in advance!
I'd do it using an event filter and a QTimer, something like this:
class MyEventFilter : public QObject
{
public:
MyEventFilter() : _screenshotPending(false) {/* empty */}
virtual bool eventFilter(QObject * o, QEvent * e)
{
if (e->type() == QEvent::Paint)
{
if (_screenshotPending == false)
{
// we'll wait 500mS before taking the screenshot
// that way we aren't trying to take 1000 screenshots per second :)
_screenshotPending = true;
QTimer::singleShot(500, this, SLOT(TakeAScreenshot()));
}
}
return QObject::eventFilter(o, e);
}
public slots:
void TakeAScreenshot()
{
_screenshotPending = false;
// add the standard Qt code for taking a screenshot here
// see $QTDIR/examples/widgets/desktop/screenshot for that
}
private:
bool _screenshotPending; // true iff we've called QTimer::singleShot() recently
};
int main(int argc, char ** argv)
{
MyEventFilter filter;
QApplication app(argc, argv);
app.installEventFilter(&filter);
[...]
return app.exec();
}
Generally, when some widget changes Qt needs to repaint it, so the event you would be interested in is QEvent::Paint. The problem here is that there will be tons of these events for widgets that overlap each other. You can override QApplication::notify() to catch all paint events before they are even delivered to receivers.
As for making screenshots of Qt application - there are several similar questions here on SO, for example screenshot of a qt application from inside the application or Taking screenshot of a specific window - C++ / Qt
Here is also a thread discussing dumping widgets to images in paintEvent().
As for your second question, here is some of my old code that can take a screenshot of a window. You can use this code like so:
HDC WinDC = GetDC(HWND_OF_YOUR_WINDOW);
HBITMAP image = ScreenshotUtility::fromHDC(WinDC);
Then you can convert the HBITMAP to a Qt Pixmap object and work with it how you like: QPixmap pixmap = QPixmap::fromWinHBITMAP(image);.
EDIT: this is Windows-specific code, not sure what the equivalent on other systems may be.

Restoring or bringing to front Qt desktop application

I have made my app into a single instance app using the RunGuard code found on this SO question:
Qt: Best practice for a single instance app protection
What I'd like to do is when the user tries to start the application while there is one running is to bring the existing running application to the front, and if minimised, restore it.
In my Delphi Windows programming days I used to broadcast a Windows message from the new application before closing it. The existing app would receive this and restore itself and come to the front.
Is something like this possible with Qt on Windows and Linux platforms?
Did you have any specific trouble with QtSingleApplication? It should be sufficient for what you want and will enable you to send a message to the running application. You just need a slot to get that message and if it matches what you expect then you restore it.
http://doc.qt.digia.com/solutions/4/qtsingleapplication/qtsingleapplication-example-trivial.html
The logview object is also set as the application's activation window. Every time a message is received, the window will be raised and activated automatically.
For some reason setActivationWindow() and activateWindow() don't work for me. This is my workaround:
#include <QWidget>
#include <qtsingleapplication.h>
class Window : public QWidget
{
Q_OBJECT
public:
Window(QWidget *parent = 0) : QWidget(parent) {}
public slots:
void readMessage(const QString &str) { showNormal(); }
};
int main(int argc, char *argv[])
{
QtSingleApplication instance(argc, argv);
Window *window = new Window;
window->show();
QObject::connect(&instance, SIGNAL(messageReceived(const QString &)), window, SLOT(readMessage(const QString &)));
if (instance.sendMessage(""))
return 0;
return instance.exec();
}
#include "main.moc"
In common, it is not possible without IPC. QtSingleApplication provide such IPC, but you will get extra dependency from QtNetwork module. (As #svlasov answered)
First problem that you will have: you can't raise any window of application if this application is not foreground. There are solutions for Windows and OS X, how to force raising of windows.

Launch application function using keyboard shortcut in GNU/Linux

I created an application using Qt in GNU/Linux and I run in the background. I want to execute certain application functionalities when user presses some key combinations, for example Ctrl+Alt+A...
I know it is possible, Gnome Pie does it but I don't know how I can capture the keys. I tried using the examples provided in this question but none of them worked...also I wouldn't want to run my application as root...
Can anyone point me some resources or give me some hints on that?
EDIT:
#iharob suggested I should use libkeybinder. I found it, tried it but it uses GTK and GTK doesn't play well with Qt...I'm not even a GTK beginner, never worked with it but I think the GTK event loop conflicts with the Qt event loop; when I emit a Qt signal from the callback which gets called after the key was pressed(which is also after gtk_init was called) the application crashes.
What would be great is if I could create a class that emits a signal whenever a keyboard key combination was pressed(e.g. Ctrl+Alt+A).
As far as I see and as #SamVarshavchik pointed out libkeybinder uses libx11 in the background so you could just use libx11 in order to get rid of the GTK event loop which is not very Qt friendly. AFAIK KDE's KAction uses the same technique for their global short keys so I think this technique will play well with Qt's event loop.
These things being said, you can use a hot-key example as presented here:
x11_hot_key.pro:
#-------------------------------------------------
#
# Project created by QtCreator 2015-05-04T01:47:22
#
#-------------------------------------------------
QT += core
QT -= gui
TARGET = x11_hot_key
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp
CONFIG += link_pkgconfig
PKGCONFIG += x11
main.cpp:
#include <QCoreApplication>
#include <iostream>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Display* dpy = XOpenDisplay(0);
Window root = DefaultRootWindow(dpy);
XEvent ev;
unsigned int modifiers = ControlMask | ShiftMask;
int keycode = XKeysymToKeycode(dpy,XK_Y);
Window grab_window = root;
Bool owner_events = False;
int pointer_mode = GrabModeAsync;
int keyboard_mode = GrabModeAsync;
XGrabKey(dpy, keycode, modifiers, grab_window, owner_events, pointer_mode,
keyboard_mode);
XSelectInput(dpy, root, KeyPressMask );
while(true)
{
bool shouldQuit = false;
XNextEvent(dpy, &ev);
switch(ev.type)
{
case KeyPress:
cout << "Hot key pressed!" << endl;
XUngrabKey(dpy,keycode,modifiers,grab_window);
shouldQuit = true;
default:
break;
}
if(shouldQuit)
break;
}
XCloseDisplay(dpy);
return a.exec();
}
or you could just use this simple library as presented here which also has some simple examples together with a handy Makefile for you to get along with.
As I don't have knowledge of an asynchronous correspondent to XGrabKey, a problem you will have is that the while(true) loop never returns and blocks the main thread thus the application so what you want is to move that in a separate thread and connect it to the main thread using signals and slots. This shouldn't be a big issue though and won't affect your application's performance because AFAIK XNextEvent blocks until your key is hit so the processor won't be uselessly processing...
Hope this helps.
A brief look at libkeybinder's very small source indicates that all it does in install a keygrab on the X display's root window.
This should be doable, but it won't be easy, and requires some knowledge and understanding of the low level X Window System protocol. It should be possible for both Qt and libxcb to coexist peacefully in one process. The way I would try to implement something like this would be as follows:
Start a separate thread.
The thread would open a separate connection to the X server, enumerate all screens on the display, obtain each screen's root window, install a key grab on each root window, then enter a loop reading X events from the xcb_connection_t handle.
Upon receipt of a key event (the only key events I expect to process in this loop would be the ones corresponding to the grabbed key), immediately ungrab the keyboard so that the X server can proceed on its merry way, then notify your application's main thread, in some form or fashion, that the key has been pressed.
Your application will have to have some means of stopping this thread, when it's time to quit.
Possible solution would be to simulate this behavior - have a small standalone application that sends signal to your background process (there are many variants doing this, signal() would be probably the simplest). Then attach that application for desired key binding in window manager for particular environment. It may require to learn how to do that for various window managers, but result could be cleaner and faster to implement.