Qt QMessageBox - closes application instead of just message box - qt

have created a QMessageBox that appears when I press a 'Start' button on my application.
When either button is pressed (Ok or Cancel) - I want the message box to close - but the application to remain open.
Unfortunately, when I hit Ok or Cancel the whole application closes. Any tips?
J
Here's my code:
Main.cpp:
#include "openingdialog.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Dialog w;
w.show();
return a.exec();
}
Opening Dialog:
patientsetup::patientsetup(QWidget *parent) :
QDialog(parent),
ui(new Ui::patientsetup)
{
ui->setupUi(this);
connect(ui->okButton, SIGNAL(clicked()), this, SLOT(confirmButtonClicked()));
connect(ui->ACSMode, SIGNAL(clicked()), this, SLOT(ACSButtonClicked()));
connect(ui->PromptMode, SIGNAL(clicked()), this, SLOT(PMPSAIButtonClicked()));
connect(ui->FullMode, SIGNAL(clicked()), this, SLOT(PMPSFullbuttonClicked()));
}
void patientsetup::PMPSAIButtonClicked()
{
direct = new pmps_f(this);
direct->setData(patient, ui->Weight->value(), ui->Height->value(), lbmvalue, bsavalue, gender);
direct->show();
}
pmps_f MainWindow
pmps_f::pmps_f(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::pmps_f)
{
ui->setupUi(this);
connect(ui->startbutton,SIGNAL(clicked(bool)), this, SLOT(start()));
}
void pmps_f::start()
{
QMessageBox msgBox;
msgBox.setText("Pressing continue will start the process with the process");
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Cancel);
int ret = msgBox.exec();
switch(ret) {
case QMessageBox::Ok:
msgBox.close();
break;
case QMessageBox::Cancel:
msgBox.close();
break;
}
}
Full code:
pmps_f::pmps_f(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::pmps_f)
{
ui->setupUi(this);
connect(ui->startbutton,SIGNAL(clicked(bool)), this, SLOT(start()));
}
pmps_f::~pmps_f()
{
delete ui;
}
//public function that pulls the Patient data from the patientsetup.ui interface
void pmps_f::setData(const QString &fileName, const double &weightValue, const double &heightValue, const double &lbmValue, const double &bsaValue, const QString &genderName)
{
ui->file->setText(fileName);
ui->weight->setText(QString::number(weightValue));
ui->height->setText(QString::number(heightValue));
ui->lbm->setText(QString::number(lbmValue));
ui->bsa->setText(QString::number(bsaValue));
ui->gender->setText(genderName);
}
//Sedation start confirmation check
void pmps_f::start()
{
QMessageBox msgBox;
msgBox.setText("Pressing continue will start the sedation process");
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Cancel);
int ret = msgBox.exec();
switch(ret) {
case QMessageBox::Ok:
break;
case QMessageBox::Cancel:
break;
}
}

I managed to the solve the problem using:
setQuitOnLastWindowClosed(false);
However - now the program does not quit properly when the window 'x' button is pushed...
Can anyone help?

Related

The Qt Tray application closes by itself after performing any action in the menu

I'm new in QT and I ran into a problem.
I decided to try writing a small application using Qt and QSystemTrayIcon. To start, I decided to reproduce the example from the official site of Qt.
https://doc.qt.io/qt-5/qtwidgets-desktop-systray-example.html?
This is my header file:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QSystemTrayIcon>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
QMenu *trayIconMenu;
QAction *launchAction;
QAction *quitAction;
QSystemTrayIcon *trayIcon;
public slots:
void changeEvent(QEvent*);
void trayIconActivated(QSystemTrayIcon::ActivationReason reason);
void trayActionExecute();
void setTrayIconActions();
void showTrayIcon();
};
#endif // MAINWINDOW_H
This is my source code file:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setTrayIconActions();
this->showTrayIcon();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::showTrayIcon() {
trayIcon = new QSystemTrayIcon(this);
QIcon trayImage(":/images/trayIcon.png");
trayIcon->setIcon(trayImage);
trayIcon->setContextMenu(trayIconMenu);
connect(trayIcon,
SLOT(activated(QSystemTrayIcon::ActivationReason)),
this,
SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason)));
trayIcon->show();
}
void MainWindow::trayActionExecute() {
QMessageBox::information(this, "TrayIcon", "Info text");
}
void MainWindow::trayIconActivated(QSystemTrayIcon::ActivationReason
reason) {
switch (reason) {
case QSystemTrayIcon::Trigger:
case QSystemTrayIcon::DoubleClick:
this->trayActionExecute();
break;
default:
break;
}
}
void MainWindow::showMsg() {
QMessageBox::information(this, "Tray message", "Hello world!");
}
void MainWindow::setTrayIconActions() {
launchAction = new QAction("Launch", this);
quitAction = new QAction("Exit", this);
connect (launchAction, SIGNAL(triggered()), this, SLOT(showMsg()));
connect (quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
// Setting system tray's icon menu
trayIconMenu = new QMenu(this);
trayIconMenu -> addAction (launchAction);
trayIconMenu -> addAction (quitAction);
}
void MainWindow::changeEvent(QEvent *event) {
QMainWindow::changeEvent(event);
if (event->type() == QEvent::WindowStateChange) {
if (isMinimized()) {
this->hide();
}
}
}
When I perform any action in the context menu, the application simply closes.
I thought that the point was to override the method QCloseEvent and that would fix the situation. So, i reimplent it:
void MainWindow::closeEvent(QCloseEvent *event) {
#ifdef Q_OS_OSX
if (!event->spontaneous() || !isVisible()) {
return;
}
#endif
if (trayIcon->isVisible()) {
QMessageBox::information(this, tr("Systray"),
tr("The program will keep running in the "
"system tray"));
hide();
event->ignore();
}
}
What can be wrong?
Thanks.
The issue can be reproduced by minimizing the main window, which hides it as per your changeEvent() implementation, and subsequently clicking "Launch" in the system tray icon menu. The message box is displayed, and afterwards the application simply closes.
This is caused by a "feature" of QT called QuitOnLastWindowClosed which is enabled by default. If it is enabled, the application quits when the last non-hidden window is closed - in your example, this is the message box spawned by the "Launch" command (which counts as a window)!
You can solve the issue by calling QApplication::setQuitOnLastWindowClosed(false); at some point after creating the QApplication, e.g. in the main.cpp which likely sets up your application:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setQuitOnLastWindowClosed(false);
MainWindow w;
w.show();
return a.exec();
}
On a side note, your connect() syntax in showTrayIcon() is wrong: It should spell SIGNAL(activated(QSystemTrayIcon::ActivationReason)) instead of SLOT(activated(QSystemTrayIcon::ActivationReason)).
But I would highly recommend to use the new signal/slot syntax (https://wiki.qt.io/New_Signal_Slot_Syntax) instead:
connect(trayIcon, &QSystemTrayIcon::activated, this, &MainWindow::trayIconActivated);
This has the advantage of showing you any mistakes (like unknown signals/slots or incompatible parameters) at compile-time instead of runtime.

setQuitOnLastWindowClosed(false) has disabled 'x' button on window / dialog QT

In order to get my MessageBox working correctly (and not shutting the whole application) - I have had to setQuitOnLastWindowClosed to False.
This means however that pushing the 'x' button on my QDialog does not exit the application properly. Any help?
Here is the code:
Main.cpp
#include "openingdialog.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setQuitOnLastWindowClosed(false);
Dialog w;
w.show();
return a.exec();
}
Dialog code:
#include "openingdialog.h"
#include "ui_openingdialog.h"
#include "patientsetup.h"
#include <QDesktopServices>
#include <QUrl>
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
connect(ui->okButton, SIGNAL(clicked()), this, SLOT(okButtonClicked()));
connect(ui->guideButton, SIGNAL(clicked(bool)), this, SLOT(guideButtonClicked()));
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::okButtonClicked()
{
psetup = new pmps_f(this);
psetup->show();
}
void Dialog::guideButtonClicked()
{
QDesktopServices::openUrl(QUrl::fromLocalFile("C:/PMPS/PMPSv1/Instructionsforuse.pdf"));
}
MessageBox code:
void pmps_f::start()
{
QMessageBox msgBox;
msgBox.setText("Pressing OK will start the sedation process with the baseline target effect-site Ce of 0.5mcg/ml");
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Cancel);
int ret = msgBox.exec();
switch(ret) {
case QMessageBox::Ok:
break;
case QMessageBox::Cancel:
break;
}
}
Construction of MainWindow:
pmps_f::pmps_f(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::pmps_f)
{
ui->setupUi(this);
connect(ui->startbutton,SIGNAL(clicked(bool)), this, SLOT(start()));
}

Trouble displaying sequence of images with setPixmap in Qlabel

I'm trying to display a sequence of images through a Qlabel using setPixmap. I have a QStringList containing the image file names and a for loop which iterates through the images with a 5 second wait after each image. However, only the last image file is ever being displayed. Currently the screen remains blank during the wait of the first iterations until the last image is finally shown. I've read that using a for loop wont work and that I should be using signals and slots instead. I'm new to this concept though and I would really appreciate an example to point me in the right direction.
Here is my current code:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),ui(new Ui::MainWindow){
ui->setupUi(this);
QStringList images;
QString imageName;
images << "redScreen.png" << "blueScreen.png" << "greenScreen.png";
for(int x=0; x < images.size(); x++){
imageName = images.at(x);
this->displayScreen(imageName, 5);
}
}
void MainWindow::displayScreen(QString imageName, int wait){
QTimer t;
QEventLoop loop;
QPixmap myImage;
myImage.load(":/images/" + imageName);
ui->imageLabel->setPixmap(myImage);
ui->imageLabel->repaint();
// 5 second wait between next iteration
t.start(wait*1000);
connect(&t, SIGNAL(timeout()), &loop, SLOT(quit()));
loop.exec();
}
The reentrant wait-via-eventloop hack is a great source of hard-to-diagnose bugs. Don't use it. It's very, very rare that you'll need to instantiate your own event loop. Even rather complex projects can entirely avoid it.
You should simply run a timer and react to timer ticks. Here's one example:
#include <QApplication>
#include <QImage>
#include <QGridLayout>
#include <QLabel>
#include <QBasicTimer>
class Widget : public QWidget {
QGridLayout m_layout;
QLabel m_name, m_image;
QStringList m_images;
QStringList::const_iterator m_imageIt;
QBasicTimer m_timer;
void timerEvent(QTimerEvent * ev) {
if (ev->timerId() == m_timer.timerId()) tick();
}
void tick() {
display(*m_imageIt);
m_imageIt ++;
const bool loop = false;
if (m_imageIt == m_images.end()) {
if (loop)
m_imageIt = m_images.begin();
else
m_timer.stop();
}
}
void display(const QString & imageName) {
QImage img(":/images/" + imageName);
m_name.setText(imageName);
m_image.setPixmap(QPixmap::fromImage(img));
}
public:
Widget(QWidget * parent = 0) : QWidget(parent), m_layout(this) {
m_images << "redScreen.png" << "blueScreen.png" << "greenScreen.png";
m_imageIt = m_images.begin();
m_layout.addWidget(&m_name, 0, 0);
m_layout.addWidget(&m_image, 1, 0);
tick();
m_timer.start(5000, Qt::CoarseTimer, this);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
A code similar to the below must work for the task you mentioned. ( needs cleaning/class organization though )
QTimer timer;
int x=0;
QStringList images;
QString imageName;
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),ui(new Ui::MainWindow){
ui->setupUi(this);
images << "redScreen.png" << "blueScreen.png" << "greenScreen.png";
connect( &timer, SIGNAL(timeout()), this, SLOT(ChangeImageSlot()) );
timer.start(5000);
}
void ChangeImageSlot()
{
imageName = images.at(x++);
this->displayScreen(imageName, 5);
if( x < images.size() )
timer.start(5000);
}
Best solution is DeepBlack with a couple of QTimer, but if you want at you risk you can try insert an processEvent() function inside the for loop of display image.

How to update the QT Mainwindow at each loop in mainwindow.cpp

My main.cpp look like this:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
An in my mainwindow.cpp I want to show a different image at each loop in "while", so it would look like this:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
image = load_an_image
int i=0;
while (i<15)
{
show image in the MainWindow
waitkey (wait until I press a key or wait some time)
do something to this image for the next loop
i++
}
}
However the Mainwindow does not show up until the "while" is finished and I cannot find how to show the MainWindow at each loop.
Can anyone give me any advice ?
GUI will not update itself until gui thread is free of other tasks. However, you can force it using
qApp->processEvents();
Following is example of very bad coding style, but that might be what you want.
#include "mainwindow.h"
#include <QApplication>
#include <thread>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
qint8 k = 15;
using namespace Qt;
QPalette pal;
QColor col = red;
while (k--)
{
std::this_thread::sleep_for(std::chrono::milliseconds(250));
pal.setBrush(w.backgroundRole(), QBrush(col));
w.setPalette(pal);
col = col == red ? blue : red;
qApp->processEvents();
}
return a.exec();
}
To run this, you will have to add QMAKE_CXXFLAGS += -std=c++11 to your '.pro' file.
And if you want to understand things better, i recommend to read about qt events.
You could delay handling the image by using a Qtimer. Something like this: -
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QTimer::singleShot(5, this, SLOT(timeout());
}
// create as a slot in the MainWindow derived class
void MainWindow::timeout()
{
image = load_an_image();
int i=0;
while (i<15)
{
// show image in the MainWindow
// waitkey (wait until I press a key or wait some time)
// do something to this image for the next loop
i++
}
}
However, it would be better handled by loading the first image and then reacting to key events, rather than waiting directly in the main thread...
void MainWindow::keyReleaseEvent(QKeyEvent* keyEvent)
{
if(keyEvent->key() == Qt::Key_Space) // use the space bar, for example
{
if(m_imageFrame < 15)
{
// update the image
}
}
else
{
QMainWindow::keyReleaseEvent(keyEvent);
}
}

QSystemTrayIcon, open other dialog than mainwindow closes the application

As the title says, if I make a systemtray icon which has an option to open an other dialog (e.g. preferences) through there, when I close this other dialog, the whole application closes when I call
this>close(); from withing that preferences dialog.
Take this example code:
main.cpp:
#include <QtGui/QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
trayIcon = new QSystemTrayIcon(this);
trayIcon->setIcon(QIcon(":/icons/error.png"));
//replace 'error' with 'video' and recompile. The indicator isn't shown!
trayIcon->setToolTip("Test");
QMenu *changer_menu = new QMenu;
Show_action = new QAction(tr("S&how"),this);
Show_action->setIconVisibleInMenu(true);
connect(Show_action, SIGNAL(triggered()), this, SLOT(show_me()));
changer_menu->addAction(Show_action);
changer_menu->addSeparator();
Preferences_action = new QAction(tr("Preferences"), this);
Preferences_action->setIconVisibleInMenu(true);
connect(Preferences_action, SIGNAL(triggered()), this, SLOT(showpref()));
changer_menu->addAction(Preferences_action);
Quit_action = new QAction(tr("&Quit"), this);
Quit_action->setIconVisibleInMenu(true);
connect(Quit_action, SIGNAL(triggered()), this, SLOT(quit_me()));
changer_menu->addAction(Quit_action);
trayIcon->setContextMenu(changer_menu);
}
void MainWindow::showpref(){
pref=new Preferences(this);
pref->exec();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
trayIcon->show();
this->hide();
}
void MainWindow::show_me(){
this->show();
}
void MainWindow::quit_me(){
this->close();
}
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QSystemTrayIcon>
#include "preferences.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_clicked();
void show_me();
void quit_me();
void showpref();
private:
Ui::MainWindow *ui;
QSystemTrayIcon *trayIcon;
QAction *Show_action;
QAction *Preferences_action;
QAction *Quit_action;
Preferences *pref;
};
#endif // MAINWINDOW_H
preferences.cpp:
#include "preferences.h"
#include "ui_preferences.h"
Preferences::Preferences(QWidget *parent) :
QDialog(parent),
ui(new Ui::Preferences)
{
ui->setupUi(this);
}
Preferences::~Preferences()
{
delete ui;
}
void Preferences::on_pushButton_clicked()
{
/HERE THE WHOLE PROGRAM CLOSES. I WANT ONLY THE PREFERENCES DIALOG TO CLOSE, THE INDICATOR TO STAY
close();
}
preferences.h:
#ifndef PREFERENCES_H
#define PREFERENCES_H
#include <QDialog>
namespace Ui {
class Preferences;
}
class Preferences : public QDialog
{
Q_OBJECT
public:
explicit Preferences(QWidget *parent = 0);
~Preferences();
private slots:
void on_pushButton_clicked();
private:
Ui::Preferences *ui;
};
#endif // PREFERENCES_H
icons.qrc:
error.png
file error.png here:
http://i.imgur.com/beSvX.png
Keep all of the above files to the same dir and compile as:
qmake -project
qmake *.pro
qmake
make
Thanks for any help!
Make a little test, open main window, don't close it. Open preference window and close it. Your application shouldn't quit in this way. Now close the main window and application will quit. This happens because of QApplication property "quitOnLastWindowClosed" which is by default set to true. You should call
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setQuitOnLastWindowClosed(false);
MainWindow w;
w.show();
return a.exec();
}
Also note that you leak memory from MainWindow while creating new instance of preferences everytime you want to show preferences. You could do something like this:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
pref(NULL)
{
// snap, your code goes here
}
void MainWindow::showpref(){
if( ! pref )
pref=new Preferences(this);
pref->exec();
}

Resources