I have to check whether my process has finished and I need to convert it to bool because I want to you if.
In MainWindow.h I have created an object
QProcess *action;
In mainwindow.cpp
void MainWindow:: shutdown()
{
action=new QProcess(this);
action->start("shutdown -s -t 600");
//and now I want to use if
if (action has finished)
{
QMessageBox msgBox;
msgBox.setText("Your computer will shutdown in 1 minute.");
msgBox.exec();
}
You should connect to the process's finished signal. Your code will get invoked whenever the process finishes. E.g.
// https://github.com/KubaO/stackoverflown/tree/master/questions/process-finished-msg-38232236
#include <QtWidgets>
class Window : public QWidget {
QVBoxLayout m_layout{this};
QPushButton m_button{tr("Sleep")};
QMessageBox m_box{QMessageBox::Information,
tr("Wakey-wakey"),
tr("A process is done sleeping."),
QMessageBox::Ok, this};
QProcess m_process;
public:
Window() {
m_layout.addWidget(&m_button);
m_process.setProgram("sleep");
m_process.setArguments({"5"});
connect(&m_button, &QPushButton::clicked, &m_process, [=]{ m_process.start(); });
connect(&m_process, (void(QProcess::*)(int))&QProcess::finished, [=]{ m_box.show(); });
}
};
int main(int argc, char ** argv) {
QApplication app{argc, argv};
Window window;
window.show();
return app.exec();
}
Related
I want to update a modal QProgressDialog from my worker thread. However, if I set the dialog to be modal, my application crashes (and the dialog did not show any progress). If I do not, everything goes fine (but the user can tinker around with the rest of the program, which may cause issues).
What am I doing wrong?
Minimum code sample follows:
filereader qfr;
QProgressDialog progress("Importing file.", "Cancel", 0, file_size);
connect(&qfr, &filereader::signalProgress, &progress, &QProgressDialog::setValue, Qt::QueuedConnection);
QThread worker_thread;
std::atomic<bool> success = false;
connect(&worker_thread, &QThread::started,
[&]() {
success = qfr.read_file(/* parameters */);
worker_thread.quit();});
worker_thread.start();
//progress.setWindowModality(Qt::WindowModal); // Works only fine when this line is commented !!
while (worker_thread.isRunning()) {
QApplication::processEvents();
QThread::sleep(0);
}
progress.close();
Your thread is pretty much pointless. It serves no real purpose. You could have as well just called QApplication::processEvents in your read_file method. But you shouldn't, calling processEvents is bad practice.
What you should do is remove that while loop, and make your progress dialog a member of your class. I don't really like how that lambda looks either. I would personally just use filereader::read_file as a slot.
Note that Qt::windowModal blocks input to the parent window. Your progress dialog has no parent. So you would either have to call progress->setModal(true), or progress.setWindowModality(Qt::ApplicationModal);. Or set a parent to it.
Here is a small example (it is not tailor made for your application, but it should point you in the right direction):
#include <QtWidgets>
class Worker : public QObject
{
Q_OBJECT
public:
Worker(QObject *parent = nullptr) : QObject(parent){}
public slots:
void simulateLongProcess()
{
for(int i = 0; i < 101; i++)
{
emit progressChanged(i);
QThread::msleep(100);
}
emit finishedWorking(true);
}
signals:
void progressChanged(int progress);
void finishedWorking(bool result);
};
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr) : QWidget(parent)
{
setLayout(new QHBoxLayout);
progress_dialog.setModal(true);
progress_dialog.setAutoReset(false);
progress_dialog.setCancelButton(nullptr);
QThread *thread = new QThread(this);
connect(thread, &QThread::started, &worker, &Worker::simulateLongProcess);
connect(&worker, &Worker::finishedWorking, thread, &QThread::quit);
connect(&worker, &Worker::progressChanged, &progress_dialog, &QProgressDialog::setValue);
connect(&worker, &Worker::finishedWorking, &progress_dialog, &QProgressDialog::close);
connect(&worker, &Worker::finishedWorking, this, &Widget::handleResult);
QPushButton * start_button = new QPushButton("START");
connect(start_button, &QPushButton::clicked, this, [=]
{
progress_dialog.show();
thread->start();
});
layout()->addWidget(start_button);
resize(400, 300);
}
public slots:
void handleResult(bool result)
{
// do something with the result
}
private:
QProgressDialog progress_dialog;
Worker worker;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
#include "main.moc"
I am designing UI for my opencv application and I'm new in Qt.
I have a QString in my Qt code that is a licence plate. and it can be change when a car arrive to our camera, but we don't know when it change.
and when car arrive we need to show it's plate on a QTableView on MainWindow.
how can I handle it in Qt?
I make my code simple here:
mainWindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
.
.
.
public slots:
void set_plate(QString p);
signals:
void plate_changed(QString newPlate);
private:
Ui::MainWindow *ui;
QString plate;
};
mainWindow.cpp
void MainWindow::set_plate(QString newPlate)
{
if(plate != newPlate){
plate = newPlate;
emit plate_changed(newPlate);
}
}
MainWindow::MainWindow(QString p, QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
plate(p)
{
.
.
.
QTableWidgetItem* item2;
item2 = new QTableWidgetItem(plate);
ui->table->setItem(table->rowCount() - 1, 0, item2);
}
main.cpp
int main(int argc, char *argv[])
{
QString plate;
.
.
.
//some changes on plate
QApplication a(argc, argv);
MainWindow w(plate);
w.show();
return a.exec();
}
thanks
When you detect a car, send a signal to your instance. Connect your emitted signal to your slot and that's all.
https://wiki.qt.io/Qt_for_Beginners#Creating_custom_signals_and_slots
Let's begin with a paraphrase of the code for main that you've posted
void someChangesOnPlate(QString &);
int main(int argc, char *argv[])
{
QString plate;
someChangesOnPlate(plate);
QApplication a(argc, argv);
MainWindow w(plate);
w.show();
return a.exec();
}
It seems that someChangesOnPlate would be a plate detection code that runs continuously in a loop, taking images from the camera, detecting plates, and extracting the plate numbers.
The main mistake in your approach is: while the plate detection code is running, the application window isn't shown yet - the code after someChangesOnPlate hasn't executed yet! The execution is within someChangesOnPlate, after all.
What you need to do, instead, is to run someChangesOnPlate in a separate thread, and to have that thread invoke the main window's newPlate method in a thread-safe fashion. Given C++11, this is a very straightforward change:
void someChangesOnPlate(MainWindow *);
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w(plate);
w.show();
std::thread thread(someChangesOnPlate, &w);
int rc = a.exec();
thread.join(); // wait for the thread to finish
return rc;
}
Within the someChangesOnPlate code, the thread-safe call to newPlate is done as follows:
void someChangesOnPlate(MainWindow * w) {
while (...) {
// process camera images
...
QString plate = ....;
...
QMetaObject::invoke(w, "newPlate", Qt::QueuedConnection, Q_ARG(QString, plate));
...
}
}
Finally, I understand that you want a new item to be added to the table whenever a new plate is indicated. Here's how to do it:
class MainWindow : public QMainWindow {
Q_OBJECT
Ui::MainWindow ui; // no need for it to be a pointer
QString m_lastPlate;
public:
MainWindow(QWidget * parent = 0) : QMainWindow(parent) {
ui.setupUi(this);
}
Q_SLOT void newPlate(const QString & plate) {
if (plate == m_lastPlate) return;
m_lastPlate = plate;
auto item = new QTableWidgetItem(plate);
ui->table->setItem(table->rowCount() - 1, 0, item);
}
}
I have QDialogButtonBox buttons on my dialog which are Ok and Cancel button pair. I have implemented accepted() signal to process when 'Ok' button is pressed but I want to abort quitting dialog if the directory path is invalid.
void SettingsDialog::on_buttonBox_accepted()
{
QDir path( ui->lineEditRootPath->text() );
if ( path.exists( ui->lineEditRootPath->text() ) )
{
QSettings settings; // save settings to registry
settings.setValue(ROOT_PATH, ui->lineEditRootPath->text() );
}
else
{
// abort cancelling the dialog here
}
}
Can the dialog quitting be abort from this handler? Do I have to implement the above code in some other signal? Do I have to use simple button to accomplish this instead of QDialogButtonBox?
This issue comes from the dialog template bundled with Qt Creator. When you create an empty dialog with buttons, the .ui file has connections between the button box and and the underlying dialog. They are created behind your back, so to speak:
So, there really is no problem, since the button box doesn't actually accept the dialog. You must accept the dialog, if you don't then the dialog stays open.
The simple fix is to remove the default connection(s).
Other Nitpicks
You should not use the QDir::exists(const QString &) overload - it won't work. You already provided the path to dir's constructor. Simply use exists().
Thus:
void SettingsDialog::on_buttonBox_accepted()
{
QDir path(ui->lineEditRootPath->text());
if (!path.exists()) return;
QSettings settings; // save settings to registry
settings.setValue(ROOT_PATH, ui->lineEditRootPath->text());
accept(); // accepts the dialog, closing it
}
You could also use the static QFileInfo::exists:
void SettingsDialog::on_buttonBox_accepted()
{
if (! QFileInfo.exists(ui->lineEditRootPath->text()) return;
...
}
Finally, it's probably a nice idea to provide some sort of feedback to the user when an input is invalid. In C++11, that's quite easy to do:
#include <QApplication>
#include <QFileInfo>
#include <QDialog>
#include <QDialogButtonBox>
#include <QLineEdit>
#include <QGridLayout>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QDialog dialog;
QLineEdit edit("/");
QDialogButtonBox buttons(QDialogButtonBox::Ok | QDialogButtonBox::Close);
QGridLayout layout(&dialog);
layout.addWidget(&edit, 0, 0);
layout.addWidget(&buttons, 1, 0);
QObject::connect(&buttons, &QDialogButtonBox::accepted, [&]{
if (!QFileInfo::exists(edit.text())) return;
//...
dialog.accept();
});
QObject::connect(&buttons, &QDialogButtonBox::rejected, [&]{ dialog.reject(); });
QObject::connect(&edit, &QLineEdit::textChanged, [&](const QString&){
if (QFileInfo::exists(edit.text()))
edit.setStyleSheet("");
else
edit.setStyleSheet("* { background: red; }");
});
dialog.show();
return a.exec();
}
After some testing, you've realized that the users have a bad tendency to enter paths that might be on disconnected network volumes. When you attempt to check if they exist, it blocks the GUI just so that the OS can politely tell you "umm, nope".
The solution is to perform the check in a worker thread, so that if it blocks, the UI will not be directly affected. If the worker thread blocks, the path editor background will turn yellow. If the path doesn't exist, the background will turn red and the OK button will be disabled.
One bit of code requires some explanation: QObject::connect(&checker, &Checker::exists, &app, [&](...){...}) connects the checker's signal to a lambda in the thread context of the application object. Since checker's signals are emitted in the checker's thread, without the context (&app), the code would be executed in the checker's thread. We definitely don't want that, the GUI changes must be executed in the main thread. The simplest way to do it is to pass one object we surely know lives in the main thread: the application instance. If you don't pass the proper context, e.g. QObject::connect(&checker, &Checker::exists, [&](...){...})), you'll get undefined behavior and a crash.
#include <QApplication>
#include <QFileInfo>
#include <QDialog>
#include <QDialogButtonBox>
#include <QLineEdit>
#include <QPushButton>
#include <QGridLayout>
#include <QThread>
#include <QTimer>
class Thread : public QThread {
using QThread::run; // final
public:
~Thread() { quit(); wait(); }
};
class Checker : public QObject {
Q_OBJECT
public:
Q_SIGNAL void exists(bool, const QString & path);
Q_SLOT void check(const QString & path) { emit exists(QFileInfo::exists(path), path); }
};
int main(int argc, char *argv[])
{
bool pathExists = true;
QApplication app(argc, argv);
QDialog dialog;
QLineEdit edit("/");
QDialogButtonBox buttons(QDialogButtonBox::Ok | QDialogButtonBox::Close);
QGridLayout layout(&dialog);
layout.addWidget(&edit, 0, 0);
layout.addWidget(&buttons, 1, 0);
QTimer checkTimer;
Checker checker;
Thread checkerThread;
checker.moveToThread(&checkerThread);
checkerThread.start();
checkTimer.setInterval(500);
checkTimer.setSingleShot(true);
QObject::connect(&buttons, &QDialogButtonBox::accepted, [&]{
if (!pathExists) return;
//...
dialog.accept();
});
QObject::connect(&buttons, &QDialogButtonBox::rejected, [&]{ dialog.reject(); });
QObject::connect(&edit, &QLineEdit::textChanged, &checker, &Checker::check);
QObject::connect(&edit, &QLineEdit::textChanged, &checkTimer, static_cast<void (QTimer::*)()>(&QTimer::start));
QObject::connect(&checkTimer, &QTimer::timeout, [&]{ edit.setStyleSheet("background: yellow"); });
QObject::connect(&checker, &Checker::exists, &app, [&](bool ok, const QString & path){
if (path != edit.text()) return; // stale result
checkTimer.stop();
edit.setStyleSheet(ok ? "" : "background: red");
buttons.button(QDialogButtonBox::Ok)->setEnabled(ok);
pathExists = ok;
});
dialog.show();
return app.exec();
}
#include "main.moc"
My example:
main.cpp:
QApplication a(argc, argv);
MainWindow w;
w.start();
return a.exec();
w.start():
if (cf.exec()){
this->show();
} else {
qDebug()<<"Need exit";
//here should be exit
}
At comment place, I tried to do: qApp->exit() and qApp->quit() and this->close() (but 'this' not shown and of cource close() is not working). How can I finish app from any place of code?
Whole code:
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.start();
return a.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "CadrForm.h"
#include "TeacherForm.h"
#include "DepartmentForm.h"
#include "CategoriesForm.h"
#include "PostForm.h"
#include "RanksAndDegreesForm.h"
#include "TeachersRankAndDegreeForm.h"
#include "ConnectionForm.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
void start();
~MainWindow();
signals:
void widgetChanged(QWidget*);
private slots:
void on_actionCadr_triggered();
void resetMainLayout(QWidget* w);
void on_actionTeacher_triggered();
void on_actionDepartment_triggered();
void on_actionPost_triggered();
void on_actionCategories_triggered();
void on_actionRanksAndDegrees_triggered();
void on_actionTeachersRD_triggered();
private:
ConnectionForm cf;
CadrForm *cadrForm;
TeacherForm *teacherForm;
DepartmentForm *departmentForm;
CategoriesForm *categoriesForm;
PostForm *postForm;
RanksAndDegreesForm *ranksAndDegreesForm;
TeachersRankAndDegreeForm *teachersRankAndDegreeForm;
QWidget *current;
Ui::MainWindow *ui;
void addWidgetToMainLayout(QWidget *w);
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
connect(this, SIGNAL(widgetChanged(QWidget*)), this, SLOT(resetMainLayout(QWidget*)));
ui->setupUi(this);
cadrForm = new CadrForm(this);
teacherForm = new TeacherForm(this);
departmentForm = new DepartmentForm(this);
categoriesForm = new CategoriesForm(this);
postForm = new PostForm(this);
ranksAndDegreesForm = new RanksAndDegreesForm(this);
teachersRankAndDegreeForm = new TeachersRankAndDegreeForm(this);
addWidgetToMainLayout(cadrForm);
addWidgetToMainLayout(teacherForm);
addWidgetToMainLayout(departmentForm);
addWidgetToMainLayout(categoriesForm);
addWidgetToMainLayout(postForm);
addWidgetToMainLayout(ranksAndDegreesForm);
addWidgetToMainLayout(teachersRankAndDegreeForm);
}
void MainWindow::start()
{
if (cf.exec()){
this->show();
} else {
qDebug()<<"Need exit";
qApp->quit();
qDebug()<<"still working";
}
}
void MainWindow::addWidgetToMainLayout(QWidget *w)
{
ui->mainLayout->insertWidget(0, w);
ui->mainLayout->itemAt(0)->widget()->hide();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::resetMainLayout(QWidget *w)
{
int index;
if (current)
{
index = ui->mainLayout->indexOf(current);
ui->mainLayout->itemAt(index)->widget()->hide();
}
index = ui->mainLayout->indexOf(w);
if (index != -1) ui->mainLayout->itemAt(index)->widget()->show();
else {
addWidgetToMainLayout(w);
resetMainLayout(w);
}
current = w;
setWindowTitle(current->windowTitle());
}
void MainWindow::on_actionCadr_triggered()
{
emit widgetChanged(cadrForm);
}
void MainWindow::on_actionTeacher_triggered()
{
emit widgetChanged(teacherForm);
}
void MainWindow::on_actionDepartment_triggered()
{
emit widgetChanged(departmentForm);
}
void MainWindow::on_actionPost_triggered()
{
emit widgetChanged(postForm);
}
void MainWindow::on_actionCategories_triggered()
{
emit widgetChanged(categoriesForm);
}
void MainWindow::on_actionRanksAndDegrees_triggered()
{
emit widgetChanged(ranksAndDegreesForm);
}
void MainWindow::on_actionTeachersRD_triggered()
{
emit widgetChanged(teachersRankAndDegreeForm);
}
And ConnectionForm - it's just a QDialog with some GUI and without any additional code.
It looks like the problem is you're not allowed to call QApplication::quit() or QApplication::exit() until the QApplication event loop has actually started. The event loop gets started by QApplication::exec(), so you're calling quit()/exit() too soon for it to have an effect.
You can fix this either by moving the quit()/exit() call so that it's in the event loop (e.g. by moving your MainWindow::start() code to the QMainWindow::showEvent() slot), or by changing your MainWindow::start() to return a value, inspect that value in main, and exit (without calling QApplication::exec()`) if it's a value that indicates your process should exit early.
Since you start the dialog event loop "early" - which is blocking -, you do not get to the event loop of the application.
If this is an intentional design, you better call exit(3) and remove the application event loop.
If you want to have the application event loop running, then you will need to make sure that it runs before you get to the point of your dialog execution.
The quick fix would be to start a single shot QTimer right just before the application event loop is started and that would trigger your "start" method call.
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.start();
QTimer::singleShot(200, w, SLOT(start()));
return a.exec();
}
and change the start to a slot, respectively.
In the long run, you may wish to consider a button and so on for bringing your dialog up, however.
The idiomatic way of queuing a method call until the event loop gets a chance to run is:
QMetaObject::invokeMethod(&w, "start", Qt::QueuedConnection);
Thus, your main would become:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
QMetaObject::invokeMethod(&w, "start", Qt::QueuedConnection);
return a.exec();
}
There is a QApplication::lastWindowClosed() signal. The Qt docs say:
This signal is emitted from QApplication::exec()
when the last visible primary window [...] is closed.
However, I used QApplication::processEvents() instead of QApplication::exec() in a loop. See this minimal example. (save as qapp.h, it must end on .h, and run qmake -project && qmake && make)
#include <QApplication>
#include <QDebug>
#include <QObject>
#include <QMainWindow>
int exitprogram = 0;
class MyMainWindow : public QMainWindow
{
Q_OBJECT
public slots:
void request_exit() {
qDebug() << "exit requested";
exitprogram = 1;
}
};
int main(int argc, char** argv)
{
QApplication app(argc, argv);
MyMainWindow w;
QObject::connect(&app, SIGNAL(lastWindowClosed()),
&w, SLOT(request_exit()));
w.show();
while(!exitprogram)
{
app.processEvents(QEventLoop::AllEvents, 20);
}
}
Is there still a good way to find out, or to even get a signal, if the last such window is being closed?
Whatever reason you have for using processEvents in place of exec is wrong. The two are not equivalent. exec() will, for example, process the deferred deletion events, while processEvents will not. As you've just found out, the lastWindowClosed signal is not emitted either. This should be telling you right there that you're doing it wrong.
The idiomatic Qt way of doing something each time the event loop goes for another iteration, is to use a zero-timeout timer. Those are virtual timers that do not use operating system resources, they are an internal Qt construct.
The example below illustrates the following:
Use of a zero-timeout timer within a QObject.
Use of the State Machine Framework to manage the state of the application. We have three states:
sWindows is the state when the application windows are still shown. The application is set not to quit on the last window being closed.
sSetup is the state reached when the last of the windows was closed. In this state we ask our Object to send its notification signal with the number of times it executed the zero-timeout timer. This will set the proper count in the message label (C++11 code) or in the count label (legacy code). The state machine automatically transitions to the following state.
sMessage is the state when the message labels are shown, and the application is set to quit upon the last window being closed.
The use of a state machine leads to declarative code: you tell the state machine how to behave, without implementing all of the behavior. You only have to implement the behaviors that are specific to your application and not already provided by Qt. The objects that the state machine manages can be very much decoupled, and the code that declares the behavior of the machine is cohesive - it can be all in one function, instead of being spread around. This is considered to be good software design.
Do note that the zero-timeout timer is very diligent: it will force your handler code to execute constantly whenever the event loop is empty. This will force 100% CPU consumption on the core where the GUI thread happens to be executing. If you have nothing to do, you should stop() the timer.
Qt 5 C++11 Code
// https://github.com/KubaO/stackoverflown/tree/master/questions/close-process-19343325
#include <QtWidgets>
int main(int argc, char** argv)
{
QApplication app{argc, argv};
QLabel widget{"Close me :)"};
QLabel message{"Last window was closed"};
int counter = 0;
auto worker = [&]{
counter++;
};
QTimer workerTimer;
QObject::connect(&workerTimer, &QTimer::timeout, worker);
workerTimer.start(0);
QStateMachine machine;
QState sWindows{&machine};
QState sSetup {&machine};
QState sMessage{&machine};
sWindows.assignProperty(qApp, "quitOnLastWindowClosed", false);
sWindows.addTransition(qApp, &QGuiApplication::lastWindowClosed, &sSetup);
QObject::connect(&sSetup, &QState::entered, [&]{
workerTimer.stop();
message.setText(QString("Last window was closed. Count was %1.").arg(counter));
});
sSetup.addTransition(&sMessage);
sMessage.assignProperty(&message, "visible", true);
sMessage.assignProperty(qApp, "quitOnLastWindowClosed", true);
machine.setInitialState(&sWindows);
machine.start();
widget.show();
return app.exec();
}
Qt 4/5 C++11 Code
#include <QApplication>
#include <QLabel>
#include <QStateMachine>
#include <QBasicTimer>
class Object : public QObject {
Q_OBJECT
QBasicTimer m_timer;
int m_counter = 0;
protected:
void timerEvent(QTimerEvent * ev) {
if (ev->timerId() == m_timer.timerId())
m_counter ++;
}
public:
Object(QObject * parent = 0) : QObject{parent} {
m_timer.start(0, this);
}
Q_SLOT void stop() const {
m_timer.stop();
emit countedTo(m_counter);
}
Q_SIGNAL void countedTo(int) const;
};
int main(int argc, char** argv)
{
QApplication app{argc, argv};
Object object;
QLabel widget{"Close me :)"};
QLabel message{"Last window was closed"};
QLabel count;
QStateMachine machine;
QState sWindows{&machine};
QState sSetup{&machine};
QState sMessage{&machine};
sWindows.assignProperty(qApp, "quitOnLastWindowClosed", false);
sWindows.addTransition(qApp, "lastWindowClosed()", &sSetup);
object.connect(&sSetup, SIGNAL(entered()), SLOT(stop()));
count.connect(&object, SIGNAL(countedTo(int)), SLOT(setNum(int)));
sSetup.addTransition(&sMessage);
sMessage.assignProperty(&message, "visible", true);
sMessage.assignProperty(&count, "visible", true);
sMessage.assignProperty(qApp, "quitOnLastWindowClosed", true);
machine.setInitialState(&sWindows);
machine.start();
widget.show();
return app.exec();
}
#include "main.moc"
I modified your code and now it works. I override closeEvent
==> test.cpp <==
#include "windows.hpp"
int exitprogram = 0;
int main(int argc, char** argv)
{
QApplication app(argc, argv);
MyMainWindow w;
w.show();
while(!exitprogram)
{
app.processEvents(QEventLoop::AllEvents, 20);
}
}
==> test.pro <==
TEMPLATE = app
TARGET = test
QT += widgets
INCLUDEPATH += .
HEADERS += windows.hpp
# Input
SOURCES += test.cpp
==> windows.hpp <==
#include <QApplication>
#include <QDebug>
#include <QObject>
#include <QMainWindow>
#include <QCloseEvent>
extern int exitprogram;
class MyMainWindow : public QMainWindow
{
Q_OBJECT
public slots:
void closeEvent(QCloseEvent *event) override {
exitprogram = 1;
event->accept();
}
};