QtQuick key press event propagation - c++

I have Qt GUI application. Main window contains QtQuick component which placed on it using window container:
QQuickView * view = new QQuickView ();
QWidget * container = QWidget::createWindowContainer (view, this);
I'd like to handle all key press events under whole window. But I faced with problem that I can't handle key events when focus acquired by QtQuick component even if I've set the window as parent for it.
The documentation say that such behaviour is expected:
QWidget *QWidget::createWindowContainer(QWindow *window, QWidget *parent = > Q_NULLPTR, Qt::WindowFlags flags = Qt::WindowFlags())
...
The window container has a number of known limitations:
...
Focus Handling; It is possible to let the window container instance have any focus policy and it will delegate focus to the window via a call to QWindow::requestActivate(). However, returning to the normal focus chain from the QWindow instance will be up to the QWindow instance implementation itself. For instance, when entering a Qt Quick based window with tab focus, it is quite likely that further tab presses will only cycle inside the QML application. Also, whether QWindow::requestActivate() actually gives the window focus, is platform dependent.
My question is: is there way to handle key press event under whole window even if focus is acquired by QtQuick component?
Minimal and complete example available at GitLab.

One possible (but ugly) solution is:
Handle key press event in QML;
Notify C++ code from QML by signal;
In C++ code generate key press event;
Handle native event and regenerated event in common event filter.
Example available at GitLab.

Related

How to write QT system tray app without a window class, and integrate it with another process?

Here is my setup:
A background process that keeps running and does it's job.
A launcher which launches the aforementioned process and monitors it, relaunches it if crashed or killed.
I wish to add a system tray access to the launcher process (and the launcher process ideally will contain code for system tray display) and enable basic options (start, stop etc) to be triggered from the system tray context menu. The system tray does not need a window of it's own. Just a windowless system tray with a Context menu that contains 2-3 options.
Since the all code written so far is in C/C++ and I need it to run on Windows and Linux, QT comes across as obvious choice. I have found it quite frustrating to get past basic QT launcher tray display. Nearly every example I have seen of QSystemTrayIcon includes a 'mainwindow' inheritance.
Below is the code I am using to create system tray.
#include <QtWidgets/QApplication>
#include <QtCore/QDebug>
#include <QtGui/QIcon>
#include <QtWidgets/QSystemTrayIcon>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMenu>
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QPixmap oPixmap(32,32);
//QMenu* menu1 = new QMenu(); // want to get a context menu from system tray
oPixmap.load ("systemTrayIcon.png");
QIcon oIcon( oPixmap );
QSystemTrayIcon *trayIcon = new QSystemTrayIcon(oIcon);
qDebug() << trayIcon->isSystemTrayAvailable();
trayIcon->setContextMenu( menu1);
trayIcon->setVisible(true);
trayIcon->showMessage("Test Message", "Text", QSystemTrayIcon::Information, 1000);
return app.exec();
}
The code displays system tray alright, but I haven't been able to get around on how to add menus to it. What I want is:
1) Add the context menu to the system tray above without adding any window class (unless that is not possible)
2) Connect those context menu items to functions in my existing code
3) The app.exec() seems to be an infinite loop that processes QT events. However, since my launcher has it's own event loop, I want to make it so that the QT event loop is integrated with my launcher loop. In other words, add some non-QT tasks to the event loop.
Given the clarification from the comments, you have a couple of options on how to get code called for context menu or activation actions.
A receiver object: basically what the examples where using, just that you don't derive your receiver class from any window type.
For macro based signal/slot connections, the base type needs to be QObject or something derived from that, for function pointer based connect it can be any class
class MyReceiver : public QObject
{
Q_OBJECT
public slots:
void onActivated(QSystemTrayIcon::ActivationReason reason);
};
// in main()
MyReceiver receiver;
// macro based connect
connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
&receiver, SLOT(onActivated(QSystemTrayIcon::ActivationReason)));
// or function pointer based connect
connect(trayIcon, &QSystemTrayIcon::activated,
&receiver, &MyReceiver::onActivated);
Connect to stand-alone functions
void onActivated(QSystemTrayIcon::ActivationReason reason);
connect(trayIcon, &QSystemTrayIcon::activated, &onActivated);
With a C++11 capable environment, connect to a lambda
connect(trayIcon, &QSystemTrayIcon::activated,
[](QSystemTrayIcon::ActivationReason reason) {});
For the context menu the same techniques apply, the "sender" objects are the QAction items you add to the menu and their signal is triggered() or toggled(bool) depending on whether the action can be just clicked or toggled between and "on" and "off" state.

QWidget event when overlaid dialog closed?

I have a complex widget on the screen which is visible. By pushing a button a dialog appears but parts of the original widget is still visible. When closing the dialog I would like to refresh some data of my particular widget ... but this widget does not know anything about that dialog. Is there any event I can check for this purpose ? Or any other way to get to know when the dialogs closed ?
First I used
virtual void QWidget::showEvent( QShowEvent* event );
but it is not called when the parts of the widget is already visible.
EDIT
I'm sorry but I wasn't precise enough. When I wrote complex, I meant that I have a main window, which has a child widget which also has child widget and so on (for about file level). That lowest level child widget initiates the opening of the dialog. The other widget which needs the closing event of this dialog is also an embedded widget but somewhere else in the application. When I wrote sending the signal through many classes I meant this. In this case if want to notify my final widget I have to connect the closing signal from my source widget to several intermediate widgets which does not even interested in that signal. I also didn't want to (and can't) connect these signal/slot in the main window because of the mentioned structure. In this is there any advice ?
send the signal through many objects just for this purpose
There's no such thing as sending a signal "through" objects unless you insist on manually threading it through a bunch of them. So this doesn't make much sense unless you explain why you want to "thread" the signal through many objects ("threading" as in a string through a needle, not as in a processing thread).
A signal-slot connection has two endpoints. All you need is:
A signal at the dialog.
A slot at the supercomplex widget.
A connection set up outside of both of them. To make life simple, both the widget and the dialog could be instantiated in main(), and a connection set up before anything even appears on the screen.
The dialog signal you're looking for is simply QDialog::finished, if you have a properly designed dialog. Otherwise, use an EventSignaler of some sort to convert a QEvent::close to a signal.
The slot in the supercomplex widget should be e.g.:
Q_SLOT void refreshSomething();
Its signature/name shouldn't refer to the fact that some dialog was closed: that's coupling the API too much to the external detail of some dialog being there. The slot should get the dialog to update/refresh what you need. The reasons for invoking it are irrelevant to the widget.
Inside the widget, the slot's implementation should most likely simply call update() on one or more subwidgets, perhaps with a relevant region - if you wish to, you can have the region as an optional slot parameter.
See this question about how to forward signals or slots inside of a complex composite class that contains many objects. It's supposed to be very simple; if it's not you'll have to edit the question to indicate why it's complex for you.
At the simplest, with the most decoupled code - where the dialog knows nothing about the widget, and vice versa - as it should be, you can have:
class Dialog : public QDialog { ... };
class Widget : public QMainWindow {
Q_OBJECT
Q_SIGNAL void showDialog();
Q_SLOT void refreshSomething();
...
};
int main(int argc, char ** argv) {
QApplication app{argc, argv};
...
Widget widget;
Dialog dialog;
QObject::connect(&widget, &Widget::showDialog, &dialog, &QWidget::show);
QObject::connect(&dialog, &QDialog::finished, &widget, &Widget::refreshSomething);
widget.show();
return app.exec();
}

Context Menu works for child but not parent Widget

I'm developing a desktop program that displays data in several QWidget windows, and I'm attempting to use a context menu to allow the user to copy/save an image of the window for use elsewhere. I encounter a pretty strange error when trying to get the context menu to appear in the window. I initially used the Qt Design mode to create the on_Plot_customContextMenuRequested(const QPoint &pos) slot for the entire window (entire Qwidget?), which did not work. When I create the on_SignalPlot_customContextMenuRequested slot, it works perfectly, but only on that specific widget, which is a subset of the entire window. I use identical code for each slot, and the debug output shows that the individual widget context menu request signal is emitted but the signal for the whole window is not. Is there a way to get it to work for the whole window?
Could the fact that the two child widgets take up the entire window cause the issue? I use a grid layout to ensure that the plots resize with the window.
(I'd show an image of the designer layout, but I don't have enough reputation.)
Does not work (code for whole window):
void Plot::on_Plot_customContextMenuRequested(const QPoint &pos)
{
qDebug()<<"plot context menu requested";
qDebug()<<pos;
QMenu* menu=new QMenu();
menu->addAction(copyWinAct);
menu->addAction(saveWinAct);
menu->exec(QCursor::pos());
}
Works Perfectly (code for individual plot/widget):
void Plot::on_SignalPlot_customContextMenuRequested(const QPoint &pos)
{
qDebug()<<"plot context menu requested";
qDebug()<<pos;
QMenu* menu=new QMenu();
menu->addAction(copyWinAct);
menu->addAction(saveWinAct);
menu->exec(QCursor::pos());
}
Thanks for your help.
If anyone else has this problem, I've found a solution. By connecting the customContextMenuRequested signal to one slot, each sub-widget will display the same context menu. Setting the overall widget to the same slot will make the whole window behave in the same manner. I added the following code to the class default constructor and created the corresponding slot to get everything to behave properly.
this->setContextMenuPolicy(Qt::CustomContextMenu);
connect(this,SIGNAL(customContextMenuRequested(QPoint)),this,SLOT(contextMenuSlot(QPoint)));
QList<QWidget *> windowChildren=this->findChildren<QWidget *>();
foreach (QWidget *child, windowChildren)
{
child->setContextMenuPolicy(Qt::CustomContextMenu);
connect(child,SIGNAL(customContextMenuRequested(QPoint)),this,SLOT(contextMenuSlot(QPoint)));
}
Good luck to anyone else fighting this problem.

Qt 5 - How to send data back from child dialog in real time

I have a settings dialog that has some settings that require another dialog to fully configure. My window shows a preview of the data as it's being tweaked by these settings. Upon clicking on the configuration button I launch another modal dialog with some knobs to twist to fine tune the particular setting.
I wish to send the result of the twisting of the knobs on the child dialog back to the parent dialog so that it can show the new preview data as the knobs on the child are being played with. The way I imagine it is I have a "refresh preview" function in the parent that is called after modification of it's data preview member variables. The question is, how do I do this? How can I access getters/setters from the parent dialog as a modal child dialog? If I do access them will the preview change or will it be blocked because of the modality of the child?
Thank you!
In Qt world, it is generally encouraged to exploit the Signal/Slot mechanism. In short, classes can send signals when something changes within that class. And slots can receive such signals provided we notified the receiving classes appropriately.
Let us look at how we can do it for our present case.
In our settings dialog constructor, we include this code (assumption is that you display the "another" dialog when a button is pressed):
Dialog *dialog = new Dialog();
connect(dialog->dial(), &QDial::valueChanged, this, &QSettingsDialog::changeTemp);
Code walkthrough:
Our QDialog has been constructed with a QDial object, dial. We access that member pointer with dialog->dial().
We tie the signal that emits the value changed on the dial to the slot called changeTemp that receives the value changed and sets the display widget on the settings dialog (parent) accordingly.
The changeTemp method might be like so:
void QSettingsDialog::changeTemp(int val)
{
lineEdit->setText(QString::number(val));
}
Notes:
You need to declare the Q_OBJECT macro on all classes that need to implement Signals and slot. In this case, both the settings dialog and the child dialog.
The above signal/slot signature is the new Qt5 signature. If you are on a version below 5.0, the signature is different. Refer to the docs.

QDockWidget is closed if main window is minimized

I'm using Qt 4.7 on Windows 7 Ultimate 32 bit.
The QMainWindow of my program has a QDockWidget. I've noticed that if I minimize the main window by the minimize button on the title bar, after restoring it the dock widget is closed. I didn't write any support for a feature like this!
How does this happen and how to prevent this?
Thanks.
I encountered this error when writing my own application. I have QDockWidget with options for my application. Using Qt Creator I created menu with QAction actionMenu which was checkable. Then I connected QDockWidget and QAction like this:
QObject::connect(ui->dockWidget, SIGNAL(visibilityChanged(bool)),
ui->actionMenu, SLOT(setChecked(bool)));
QObject::connect(ui->actionMenu, SIGNAL(toggled(bool)),
ui->dockWidget, SLOT(setVisible(bool)));
The order of connection is not important. And then when I minimized application with QDockWidget being visible, after I restored it QDockWidget was closed and actionMenu was unchecked.
In fact there are two solutions. First which works for me is to use SIGNAL(triggered(bool)) instead of SIGNAL(toggled(bool)):
QObject::connect(ui->dockWidget, SIGNAL(visibilityChanged(bool)),
ui->actionMenu, SLOT(setChecked(bool)));
QObject::connect(ui->actionMenu, SIGNAL(triggered(bool)),
ui->dockWidget, SLOT(setVisible(bool)));
The second solution uses action which you can obtain from QDockWidget:
// Retrieve action from QDockWidget.
QAction *action = ui->dockWidget->toggleViewAction();
// Adjust any parameter you want.
action->setText(QString("&Menu"));
action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_M));
action->setStatusTip(QString("Press to show/hide menu widget."));
action->setChecked(true);
// Install action in the menu.
ui->menuOptions->addAction(action);
I know for sure that SIGNAL(toggled(bool)) was causing in my application closure of QDockWidget.
I faced the same problem... I managed to get rid of it by using a method called StoreWindowsLayout and RestoreWindowsLayout.
StoreWindowsLayout will save the content of the ByteArray returned by the Method QMainwindow::saveState().
RestoreWindowsLayout will restore that bytearray, and therefore your windows layout, the qdockwidget visibility state and so on...
I call StoreWindowsLayout on ApplicationMainFrm::changeEvent, on ApplicationMainFrm::closeEvent (it's likely this one you'll need) and in ApplicationMainFrm::hide().
Then I use restoreWindowsLayout in ApplicationMainFrm::showEvent.
Exemple of use of restoreWindowsLayout in my MainForm :
void ApplicationMainFrm::showEvent(QShowEvent* pEvent)
{
QMainWindow::showEvent(pEvent);
restoreWindowsLayout();
}
Hope it helps !