How to use QMenu signals? - qt

It is very straightforward to connect to QMenu::triggered or QMenu::hovered signals by calling QObject::connect and pass the appropriate QAction.
However, I do not know how to use QMenu::aboutToHide signal, as there is no action passed to that signal.
How to use QMenu::aboutToHide and QMenu::aboutToShow signals or those are just virtual functions that can be overridden?

The signals in the world of Qt are not functions, never invoke them. The signals notify that something has happened with the QObject and send information if necessary.
In the case of triggered and hovered it is necessary to send the QAction because several QActions in a QMenu, then the developer thought that it is necessary to know with which QAction was interacting. On the other hand with aboutToShow and aboutToHide the signal does not send anything because it wants to notify is that if the QMenu was shown or hidden, respectively. Is there any need to know that QMenu was shown or hidden if he did it ? no, because the sender did it, I do not use other properties that we do not have at hand.
Example of use:
#include <QApplication>
#include <QMainWindow>
#include <QMenuBar>
#include <QDebug>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMainWindow w;
QMenu *foo_menu = w.menuBar()->addMenu("Foo Menu");
for(const QString & name: {"action1", "action2", "action3"}){
foo_menu->addAction(name);
}
QObject::connect(foo_menu, &QMenu::aboutToShow, [](){
qDebug()<<"aboutToShow";
});
QObject::connect(foo_menu, &QMenu::aboutToHide, [](){
qDebug()<<"aboutToHide";
});
QObject::connect(foo_menu, &QMenu::triggered, [](QAction *action){
qDebug()<< "triggered: " <<action->text();
});
QObject::connect(foo_menu, &QMenu::hovered, [](QAction *action){
qDebug()<< "hovered: " <<action->text();
});
w.show();
return a.exec();
}
And what happens if you have several QMenu that connect to the same slot? How do I know QMenu was shown or hidden?
The solution is to use sender() which is a method that belongs to the QObject class that returns the object that emitted the signal, in this case the QMenu.
Example:
#include <QApplication>
#include <QMainWindow>
#include <QMenuBar>
#include <QDebug>
class MainWindow: public QMainWindow{
public:
MainWindow(QWidget *parent=nullptr):
QMainWindow(parent)
{
for(const QString & name_of_menubar: {"bar1", "bar2", "bar3"}){
QMenu *menu = menuBar()->addMenu(name_of_menubar);
connect(menu, &QMenu::aboutToShow, this, &MainWindow::on_aboutToShow);
connect(menu, &QMenu::aboutToHide, this, &MainWindow::on_aboutToHide);
for(const QString & name: {"action1", "action2", "action3"}){
menu->addAction(name);
}
}
}
private slots:
void on_aboutToShow(){
if(QMenu *menu = qobject_cast<QMenu *>(sender()))
qDebug()<<"aboutToShow" << menu->title();
}
void on_aboutToHide(){
if(QMenu *menu = qobject_cast<QMenu *>(sender()))
qDebug()<<"aboutToHide" << menu->title();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}

Related

How to check if the application has been minimized

Whenever the application window is minimized or maximized i want to link that signal with a function.
This is the code.
int main(int argc, char *argv[])
{
QApplication application(argc, argv);
Renderer w(model ); // This is QWidget
w.show();
QObject::connect(&w, &QWindow::windowStateChanged, [&](Qt::WindowState state) {
});
// how will i define the QObject::connect
return application.exec();
}
What would be the parameters for the QObject::connect function ?
You cannot use the connect function to connect to different slots based on the given value. You can however simply call the functions based on the value by checking the value in your lambda.
At least, you could if you had the signal. However, your connect suggests that w is - or inherits - a QWindow. You can obviously only connect to signals your class provides. As your Renderer is a QWidget, you have to check that class.
The documentation of QWidget tells us, that there is no windowStateChanged signal, but it states:
When the window state changes, the widget receives a changeEvent() of type QEvent::WindowStateChange.
So therefor we can create our own signal and connect to that. This can look similar to the following working example:
#ifndef RENDERER_H
#define RENDERER_H
#include <QWidget>
#include <QEvent>
class Renderer : public QWidget {
Q_OBJECT
signals:
void stateChanged(bool isMaximized);
protected:
void changeEvent(QEvent *e)
{
if(e->type() == QEvent::WindowStateChange) {
emit stateChanged(windowState() & ~Qt::WindowMaximized);
}
QWidget::changeEvent(e);
}
};
#endif // RENDERER_H
int main(int argc, char *argv[])
{
QApplication application(argc, argv);
Renderer w; // This is QWidget
w.show();
QObject::connect(&w, &Renderer::stateChanged, [&](bool maximized) {
qDebug() << "Maximized?" << maximized;
});
return application.exec();
}
I was able to solve by using QApplication::focusWindow()
int main(int argc, char *argv[])
{
QApplication application(argc, argv);
Renderer w; // This is QWidget
w.show();
QObject::connect(QApplication::focusWindow(), &Renderer::stateChanged, [&](bool maximized) {
qDebug() << "Maximized?" << maximized;
});
return application.exec();
}

How to redefine a node expanding in a QTreeWidget?

The way I understand the qtreeview.cpp the expand method in the QTreeView is responsible for expanding nodes. For example it is used in the expandOrCollapseItemAtPos method. I try to redefine a node expansion in the QTreeWidget:
#include <QApplication>
#include <QWidget>
#include <QTreeWidget>
#include <QMessageBox>
class MyTree : public QTreeWidget
{
public:
MyTree(QWidget *parent) : QTreeWidget(parent) {}
expandItem(const QTreeWidgetItem *item) {
QMessageBox msg;
msg.setText("EXPAND ITEM!!");
msg.exec();
QTreeWidget::expandItem(item);
}
expand(const QModelIndex &index) {
QMessageBox msg;
msg.setText("EXPAND!!");
msg.exec();
QTreeWidget::expand(index);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget *mainWidget = new QWidget();
mainWidget->resize(200,100);
MyTree *myTree = new MyTree(mainWidget);
myTree->resize(200,100);
QTreeWidgetItem *node, *leaf;
node = new QTreeWidgetItem(myTree);
node->setText(0,"node");
leaf = new QTreeWidgetItem(node);
leaf->setText(0,"leaf");
mainWidget->show();
return a.exec();
}
But there is no any message box when I expand a node. I tried to comment QTreeWidget::expandItem(item); and QTreeWidget::expand(index); but expanding is still working.
How do I redefine a node expanding in a QTreeWidget?
QTreeWidget::expand and QTreeWidget::expandItem are non-virtual methods. So redefinition is not useful. I will use slot-signal mechanism with QTreeWidget::expanded/collapsed signals.
connect(this, SIGNAL(expanded(QModelIndex)), this, SLOT(myExpand(QModelIndex)));
This should solve your problem -
connect(QTreeWidgetObject, SIGNAL(itemExpanded(QTreeWidgetItem*)), SLOT(slotItemExpanded(QTreeWidgetItem*)));

The line edit widget shows nothing

I've recently started learning Qt and I'm a beginner of it now. So as first example for myself I wrote the following simple example.
The example is named Calculator. It now only has two buttons an a line edit. It's here:
:
My Calculator.h is this:
#ifndef CALCULATOR_H
#define CALCULATOR_H
#include<QDialog>
#include "ui_Calculator.h"
class Calculator : public QDialog, public Ui::Calculator
{
Q_OBJECT
public:
Calculator(QWidget* parent = 0);
private slots:
void myslot();
};
#endif // CALCULATOR_H
And the Calculator.cpp is this:
#include <QtWidgets>
#include "calculator.h"
Calculator::Calculator(QWidget *parent)
:QDialog(parent)
{
setupUi(this);
connect(oneButton,SIGNAL(clicked(bool)), this, SLOT(myslot()));
}
void Calculator::myslot(){
lineEdit -> setText("1");
}
And this is the main.cpp:
#include <QApplication>
#include <QDialog>
#include "ui_Calculator.h"
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
Ui::Calculator ui;
QDialog* dialog = new QDialog;
ui.setupUi(dialog);
dialog -> show();
return app.exec();
}
The program runs fine without any error. But when I click on 1 button, nothing will be printed/shown in the line edit. Why please?
And what part of my program should I change to solve the issue please?
You are setting up the wrong class in your main.
You should use your custom Calculator class and not QDialog.
setupUi only initializes your elements but your code in Calculator never gets called. Your main should look like this:
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
Calculator calc; //using your Calculator class.
calc.show();
return app.exec();
}
And don't include ui_calculator.h but calculator.h

QDialog exec() can not exit process

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QDialog dlg;
dlg.exec();
return a.exec();
}
That's all my code, but when I close the window, The process isn't exit, it seems that drop in the loop a.exec().
Generally speaking, calling any exec is a bad idea, other than QCoreApplication::exec() or QDrag::exec(). The presence of exec() and waitForXxx() methods is an enticing trap for the unwary. Those methods are "easy" to use, but that ease comes at a price of hard to track bugs. Don't use them.
You should simply show the dialog:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMessageBox msg;
msg.setText("Hello");
msg.addButton(QMessageBox::Close);
msg.show();
return a.exec();
}
If you wish to wait for the dialog to be accepted or rejected, you should use the dialog's clickedButton slot. QMessageBox has a long-standing bug that makes the accepted and rejected signals useless :(
// https://github.com/KubaO/stackoverflown/tree/master/questions/messagebox-show-25545652
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif
#include <functional>
[...]
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QMessageBox msg;
msg.setText("Continue?");
msg.addButton(QMessageBox::Yes);
msg.addButton(QMessageBox::No);
auto onClick = [&msg]() {
auto role = msg.buttonRole(msg.clickedButton());
if (role == QMessageBox::NoRole)
QApplication::quit();
if (role == QMessageBox::YesRole) {
auto label = new QLabel("I'm running");
label->setAttribute(Qt::WA_DeleteOnClose);
label->show();
}
};
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
QObject::connect(&msg, &QMessageBox::buttonClicked, onClick);
#else
QObject::connect(&msg, SIGNAL(buttonClicked(QAbstractButton*)),
new FunctorSlot{onClick, &msg}, SLOT(call()));
#endif
msg.show();
return app.exec();
}
#include "main.moc"
For Qt 4, you need the following helper:
// Qt 4 only
struct FunctorSlot : public QObject {
Q_OBJECT
public:
std::function<void()> callable;
template <typename Fun>
FunctorSlot(Fun && fun, QObject * parent = {}) :
QObject{parent}, callable{std::forward<Fun>(fun)} {}
Q_SLOT void call() {
callable();
}
};
Possible solution:
QApplication a(argc, argv);
QDialog dlg;
QTimer::singleShot( &dlg, 0, SLOT(exec()) );
return a.exec();
It will work well. First - application event loop will be started. Then dialog event loop will be executed. After closing of dialog, both dialog and application loop will be finished. Application loop will be terminated automatically (by default), when last window is closed.
But, as noted by #thuga - there are no reason to call exec(). It is enough to call show() method.

Is it possible to implement SystemTrayIcon functionality in Qt Quick application

I am writing a QtQuick desktop application. I use both c++ (for functionality) and QML (for UI) in it.
I use QQuickView to show the interface written in QML.
I want this application to reside in System Tray when minimised.
I mean a functionality similar to this example. http://qt-project.org/doc/qt-4.8/desktop-systray.html .
I am trying to implement this feature but could not find a way to do this in my Qt Quick application.
Here is my main.cpp code:
#include <QGuiApplication>
#include <QQmlEngine>
#include <QQmlContext>
#include <QQmlFileSelector>
#include <QQuickView>
#include "myapp.h"
int main(int argc, char* argv[])
{
QGuiApplication app(argc,argv);
app.setApplicationName(QFileInfo(app.applicationFilePath()).baseName());
QDir::setCurrent(qApp->applicationDirPath());
MyApp myappObject;
QQuickView view;
view.connect(view.engine(), SIGNAL(quit()), &app, SLOT(quit()));
view.rootContext()->setContextProperty("myappObject", &myappObject);
new QQmlFileSelector(view.engine(), &view);
view.setSource(QUrl("qrc:///myapp.qml"));
view.setResizeMode(QQuickView::SizeRootObjectToView);
view.show();
return app.exec();
}
Please help by providing any hint/pointers to do this.
Thanks.
I was facing the same challenge today and ended up using the following solution within main(). Works great for me when using Qt 5.3. You should of course implement a better way to check whether the first root object is your application window object or not.
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QMessageBox>
#include <QAction>
#include <QMenu>
#include <QSystemTrayIcon>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
if (!QSystemTrayIcon::isSystemTrayAvailable()) {
QMessageBox::critical(0, QObject::tr("Systray"),
QObject::tr("I couldn't detect any system tray "
"on this system."));
return 1;
}
QApplication::setQuitOnLastWindowClosed(false);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
QObject *root = 0;
if (engine.rootObjects().size() > 0)
{
root = engine.rootObjects().at(0);
QAction *minimizeAction = new QAction(QObject::tr("Mi&nimize"), root);
root->connect(minimizeAction, SIGNAL(triggered()), root, SLOT(hide()));
QAction *maximizeAction = new QAction(QObject::tr("Ma&ximize"), root);
root->connect(maximizeAction, SIGNAL(triggered()), root, SLOT(showMaximized()));
QAction *restoreAction = new QAction(QObject::tr("&Restore"), root);
root->connect(restoreAction, SIGNAL(triggered()), root, SLOT(showNormal()));
QAction *quitAction = new QAction(QObject::tr("&Quit"), root);
root->connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
QMenu *trayIconMenu = new QMenu();
trayIconMenu->addAction(minimizeAction);
trayIconMenu->addAction(maximizeAction);
trayIconMenu->addAction(restoreAction);
trayIconMenu->addSeparator();
trayIconMenu->addAction(quitAction);
QSystemTrayIcon *trayIcon = new QSystemTrayIcon(root);
trayIcon->setContextMenu(trayIconMenu);
trayIcon->setIcon(QIcon(":/resources/DatagnanLogoColor.png"));
trayIcon->show();
}
return app.exec();
}
Copy the Windos class (window.cpp/window.h) from systray example to your project, port it to Qt5 if necessary and open both from your main file:
int main(int argc, char* argv[])
{
// ...
QQuickView view;
// ...
view.show();
Window window;
window.show();
return app.exec();
}

Resources