How to kill and rerun the main process in qt? - c++

I have an app which can run another exe files. Some buttons and connect to the executeable files. When i click the button, start running the another program... this is good, but remains open the main window and the program still running. I want to achive that the main window stop running (or disapear) when another program is running, and when i exit from the other app, run (or appear and run) the main process.
I tried this:
void MainWindow::RunSys(QString sh)
{
this->close();
QProcess ps;
if(ps.execute(sh)<0)
{
QMessageBox messageBox;
messageBox.critical(nullptr,"Error",sh + "Error!");
}
this->show();
}
//I call the function this way:
QString sh = settings->value("Run").toString();
connect(pButton, &QToolButton::clicked, [=] { RunSys(sh); });
It's working on Linux. Opens the executable program but the main program is till running on Windows. What can I do?

I could not reproduce the bug, we'll need you to provide a complete, minimal and reproducible example that illustrates the bug.
If it can help you, I've written the following sample code based on yours:
Header
class MainWindow : public QMainWindow
{
Q_OBJECT
protected:
QPushButton * pb;
public:
MainWindow();
void runShellCmd(const QString & cmd);
};
Implementation
MainWindow::MainWindow()
{
// Build the main window
resize(600, 400);
pb = new QPushButton("run cmd", this);
this->setCentralWidget(pb);
// Create the command
QString cmd("ping 127.0.0.1 -n 6");
// Connect the signal
connect(pb, &QPushButton::clicked, [=](){runShellCmd(cmd);});
}
void MainWindow::runShellCmd(const QString & cmd)
{
QProcess ps;
this->close();
int exit_code = ps.execute(cmd);
switch(exit_code)
{
// Do what you want with exit code
default:;
}
this->show();
}
Main function
int main(int argc, char ** argv)
{
QApplication app(argc, argv);
MainWindow w;
w.show();
return app.exec();
}
And it worked fine.
EDIT:
I have understood what's wrong. Your example with calc.exe has whispered me the issue :)
Actually, QProcess::execute() blocks until the command call returns. In this case, it returns right after the launch, not when the windows is closed. So you got the expected behaviour.
I'm afraid that it will be way much harder to do it this way. Because calc.exe being an external program, except if you can read kind of closing signal from an external program, you would not be able to detect when it is closed.

Related

Run QFileDialog::getOpenFileName without separate event loop?

I'm using QFileDialog::getOpenFileName right now. However, as suggested in this article, this crashes when the main application closes while the dialog is open. You can see an example of how to reproduce the crash here:
int main(int argc, char **argv) {
QApplication application{argc, argv};
QMainWindow *main_window = new QMainWindow();
main_window->show();
QPushButton *button = new QPushButton("Press me");
main_window->setCentralWidget(button);
QObject::connect(button, &QPushButton::clicked, [main_window]() {
QTimer::singleShot(2000, [main_window]() { delete main_window; });
QFileDialog::getOpenFileName(main_window, "Close me fast or I will crash!");
});
application.exec();
return 0;
}
I can use QFileDialog with the normal constructor instead, as described here. However, then I don't seem to get the native windows file open dialog.
Is there a way to get a non crashing program and use the native Windows file open dialog through Qt?
If you close your main_window instead of deleting it, you won't get any crash.
By the way, you could check if there is any QFileDialog opened to avoid a wrong app exit.
In the next example, I'm closing the dialog, but you could implement another solution:
#include <QTimer>
#include <QApplication>
#include <QMainWindow>
#include <QPushButton>
#include <QFileDialog>
#include <QDebug>
int main(int argc, char **argv) {
QApplication application{argc, argv};
QMainWindow *main_window = new QMainWindow();
main_window->show();
QPushButton *button = new QPushButton("Press me");
main_window->setCentralWidget(button);
QObject::connect(button, &QPushButton::clicked, [main_window]() {
QTimer::singleShot(2000, [main_window]() {
QObjectList list = main_window->children();
while (!list.isEmpty())
{
QObject *object= list.takeFirst();
if (qobject_cast<QFileDialog*>(object))
{
qDebug() << object->objectName();
QFileDialog* fileDialog = qobject_cast<QFileDialog*>(object);
fileDialog->close();
}
}
main_window->close();
});
QFileDialog::getOpenFileName(main_window, "Close me fast or I will crash!");
});
application.exec();
return 0;
}
The design of your application is broken. The shut down of the application normally happens when the outernmost event loop in the main thread exists. This won't happen while a file dialog is active - by definition, its event loop is running then. Thus you're doing something you shouldn't be doing, and the file dialog is merely a scapegoat, or a canary in the coalmine indicating brokenness elsewhere.

Qt splashscreen won't close

I have a QSplashScreen made that runs through a bunch of images to resemble a gif and closes when the main window opens. This works fine on windows, but when I run it on mac it gets funky. Instead of closing when it's gone through all the pictures like it should it starts going through the images in revers order when clicked.
Here is header (splashscreen.h):
class SplashScreen : public QObject
{
Q_OBJECT
public:
explicit SplashScreen(QObject *parent = 0);
private:
QString filename0;
QString filename1;
QString filename;
int frameNum;
Qt::WindowFlags flags;
private slots:
void showFrame(void);
};
and here is implementation (splashscreen.cpp):
SplashScreen::SplashScreen(QObject *parent) :
QObject(parent)
{
QTimer *timer = new QTimer;
timer->singleShot(0, this, SLOT(showFrame()));
frameNum = 0;
}
void SplashScreen::showFrame(void)
{
QSplashScreen *splash = new QSplashScreen;
QTimer *timer = new QTimer;
frameNum++;
QString filename0 = ""; //first and second half of file path
QString filename1 = "";
splash->showMessage("message here", Qt::AlignBottom, Qt::black);
filename = filename0 + QString::number(frameNum) +filename1; // the number for the file is added here
splash->setPixmap(QPixmap(filename)); // then shown in the splashscreen
splash->show();
if (frameNum < 90)
{
timer->singleShot(75, this, SLOT(showFrame()));
}
else if (frameNum == 90)
{
splash->close();
flags |= Qt::WindowStaysOnBottomHint;
return;
}
}
and here is main file (main.cpp):
int main(int argc, char *argv[])
{
Application app(argc, argv);
SplashScreen *splash = new SplashScreen;
QSplashScreen *splashy = new QSplashScreen;
View view; //main window
QTimer::singleShot(10000, splashy, SLOT(close()));
splashy->hide();
QTimer::singleShot(10000, &view, SLOT(show()));
return app.exec();
}
I've got several different ways to close the splash screen but none of them seem to be working. Is this a bug in macs or is there something I can fix in my code?
There are created 90 different QSplashScreen objects. Only the 90th object is closed.
So, it is the main reason for observed behavior.
If you create a new splash screen QSplashScreen *splash = new QSplashScreen; for each frame then the previous screen should be closed and deleted. It is possible to store QSplashScreen *splash as a class member. Otherwise there is a memory leak.
You may consider to use only one instance of QSplashScreen splash as a private SplashScreen class member. The rest of the code may be unchanged (after replacement splash-> by splash.). It will be automatically deleted with deletion of SplashScreen.
Other issues
QTimer should not be instantiated each time to use its static member function. Each call of showFrame() and SplashScreen() creates a new QTimer object that is never deleted and never used.
The splashy also does not make any sense in main(). All three lines related to splashy may be deleted. Actual splash screens are triggered by new SplashScreen. By the way, it is also a leak. In that case it makes sense to instantiate it directly on the main() function stack: SplashScreen splash;
It looks that the private member SplashScreen::flags is not used.

Connect to WebSocket server from Qt UI with Echo Client Example

I'm using Websocket Echo Server Example in CLI and it works fine. I'm trying to connect to this server from my Qt GUI project. I have MainWindow class with an appropriate slot
void MainWindow::on_pushButton_clicked()
{
qDebug() << "Push btn clicked";
EchoClient client(QUrl(QStringLiteral("ws://localhost:1234")));
}
and EchoClient files from CLI Websocket Echo Client Example.
The main problem is that that I can't connect to the server when I push button on the form. However, I see debug message "Push btn clicked". It is supposed to be printed "Hello, world!". But nothing happens, no errors. Even signal void EchoClient::onConnected() is not fired.
But if I move EchoClient client(QUrl(QStringLiteral("ws://localhost:1234"))); to main.cpp it connects:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
EchoClient client(QUrl(QStringLiteral("ws://localhost:1234")));
return a.exec();
}
I am completely new in C++ and Qt.
Why this happens? Is it something with processing threads in UI? What should I know?
Qt 5.4.
inside your on_pushButton_clicked() function you are creating the EchoClient (which is asynchronous) in the stack of the function. As soon as the function exits, the EchoClient object is destroyed from the stack.
You can think about different solutions like creating a private field in the MainWindow class
private EchoClient *client;
then set it to null in the MainWindow constructor:
this->client = NULL;
and only at this point doing something like this in your click() routine:
void MainWindow::on_pushButton_clicked()
{
qDebug() << "Push btn clicked";
if (this->client == NULL)
{
this->client = new EchoClient(QUrl(QStringLiteral("ws://localhost:1234")));
}
else
qWarning() << "Carefull, the client is already running";
}
Then it is up to you taking care of the like cycle of the client object you have created.
Probably you have to think when you want to destroy it, probably via a "reset" button routine.
I suggest this reading: http://doc.qt.io/qt-4.8/qapplication.html#details

Qt menubar not showing

I'm building a simple C++ application on Mac OS X 10.9 with Qt 5.2.1 using CMake (without MOC).
I am starting the executable from the command-line. The problem is that the menu bar is not showing up at all, the Terminal menu bar is still visible but not clickable. When I switch windows temporarily and then come back to the window of this application, I at least see the standard "application" menu with "About". The "About" action is now working and shows the dialog. The toolbar button also works as expected.
What else I tried (and didn't work):
using the pre-defined menuBar()
use setMenuBar()
new menuBar(0)
menubar->setVisible(true)
When I check the isVisible() it returns false, also if I set it to visible in the line before.
I wonder whether the lack of using MOC can be the reason for this?
Below I attached a reduced example.
#include <QtGui>
#include <QtWidgets>
class MainWindow : public QMainWindow {
public:
MainWindow();
private:
void create_actions_();
void create_menus_();
void create_toolbar_();
void about_();
QMenuBar* menu_bar_;
QMenu* file_menu_;
QMenu* help_menu_;
QToolBar* file_toolbar_;
QAction* action_about_;
};
MainWindow::MainWindow() {
resize(800, 600);
create_actions_();
create_menus_();
create_toolbar_();
}
void MainWindow::create_actions_() {
action_about_ = new QAction(tr("About"), this);
connect(action_about_, &QAction::triggered, this, &MainWindow::about_);
}
void MainWindow::create_menus_() {
menu_bar_ = new QMenuBar(this);
file_menu_ = menu_bar_->addMenu(tr("&File"));
menu_bar_->addSeparator();
help_menu_ = menu_bar_->addMenu(tr("&Help"));
help_menu_->addAction(action_about_);
menu_bar_->setNativeMenuBar(true);
}
void MainWindow::create_toolbar_() {
file_toolbar_ = addToolBar(tr("File"));
file_toolbar_->addAction(action_about_);
file_toolbar_->setIconSize(QSize(16, 16));
}
void MainWindow::about_() {
QMessageBox::about(this, tr("About"), tr("FooBar"));
}
int main(int argc, char **argv) {
QApplication app(argc, argv);
MainWindow main_window;
main_window.show();
const int exit_code = app.exec();
return exit_code;
}
CMakeLists.txt
FIND_PACKAGE(Qt5Core)
FIND_PACKAGE(Qt5Gui)
FIND_PACKAGE(Qt5OpenGL)
FIND_PACKAGE(Qt5Widgets)
FIND_PACKAGE(Qt5Declarative)
FIND_PACKAGE(Qt5MacExtras)
ADD_EXECUTABLE(main main.cc)
qt5_use_modules(main Core Gui Widgets Declarative MacExtras)
Thanks a lot in advance!
OK, solved the problem myself. It appears you cannot add a separator to the menubar.
Removing the menu_bar_->addSeparator(); solved the problem.

how to restart my own qt application?

i just asking myself how to restart my own qt application?
Can somebody please show me an example?
To restart application, try:
#include <QApplication>
#include <QProcess>
...
// restart:
qApp->quit();
QProcess::startDetached(qApp->arguments()[0], qApp->arguments());
I'm taking the other answers solutions, but better. No need for pointers, but there is a need for a ; after the while statement of a do { ... } while( ... ); construct.
int main(int argc, char *argv[])
{
const int RESTART_CODE = 1000;
do
{
QApplication app(argc, argv);
MainWindow main_window(app);
} while( app.exec() == RESTART_CODE);
return return_from_event_loop_code;
}
Assuming that 1337 is your restart code:
main.cxx
int main(int argc, char * argv[])
{
int result = 0;
do
{
QCoreApplication coreapp(argc, argv);
MyClass myObj;
result = coreapp.exec();
} while( result == 1337 );
return result;
}
myClass.cxx
qApp->exit(1337);
To restart a running Qt application (at least in Qt 5.15.2) you can do the following:
#include <QApplication>
#include <QProcess>
//...
QString program = qApp->arguments()[0];
QStringList arguments = qApp->arguments().mid(1); // remove the 1st argument - the program name
qApp->quit();
QProcess::startDetached(program, arguments);
Doing a real process restart without subclassing:
QCoreApplication a(argc, argv);
int returncode = a.exec();
if (returncode == -1)
{
QProcess* proc = new QProcess();
proc->start(QCoreApplication::applicationFilePath());
}
return returncode;
Edit for Mac OS like earlier example.
To restart call
QCoreApplication::exit(-1);
somewhere in your code.
Take a look at How to restart an application thread on qtcentre.org, where muisei gives this code
#define RESTART_CODE 1000
int main(int argc, char *argv[])
{
int return_from_event_loop_code;
QPointer<QApplication> app;
QPointer<MainWindow> main_window;
do
{
if(app) delete app;
if(main_window) delete main_window;
app = new QApplication(argc, argv);
main_window = new MainWindow(app);
return_from_event_loop_code = app->exec();
}
while(return_from_event_loop_code==RESTART_CODE)
return return_from_event_loop_code;
}
I just used the method described above and I noticed that my application crashes on restart.
...then I switched the following lines of code:
if(app) delete app;
if(main_window) delete main_window;
to:
if(main_window) delete main_window;
if(app) delete app;
and it behaves OK. For some reason the window must be deleted first.
Just a note for future readers.
EDIT: ...and a different approach for those who want a real process-restart: You can declare a myApp::Restart() method in your subclass of QApplication. The following version works OK on both MS-Windows & MacOS:
// Restart Application
void myApp::Restart(bool Abort)
{
// Spawn a new instance of myApplication:
QProcess proc;
#ifdef Q_OS_WIN
proc.start(this->applicationFilePath());
#endif
#ifdef Q_OS_MAC
// In Mac OS the full path of aplication binary is:
// <base-path>/myApp.app/Contents/MacOS/myApp
QStringList args;
args << (this->applicationDirPath() + "/../../../myApp.app");
proc.start("open", args);
#endif
// Terminate current instance:
if (Abort) // Abort Application process (exit immediattely)
::exit(0);
else
this->exit(0); // Exit gracefully by terminating the myApp instance
}
This slight variation on Rubenvb's idea works with PyQt. clearSettings is the method that triggers the restart.
class GuiMain
#Most of implementation missing
def clearSettings(self):
#Clearing the settings missing
QApplication.exit(GuiMain.restart_code)
restart_code = 1000
#staticmethod
def application_main():
"""
The application's main function.
Create application and main window and run them.
"""
while True:
app = QApplication(sys.argv)
window = GuiMain()
window.show()
ret = app.exec_()
if ret != GuiMain.restart_code:
break
del window
del app
Here is the code:
main.cpp:
int main(int argc, char *argv[])
{
int currentExitCode = 0;
do {
QApplication a(argc, argv);
MainWindow w;
w.show();
currentExitCode = a.exec();
} while( currentExitCode == MainWindow::EXIT_CODE_REBOOT );
return currentExitCode;
}
mainwindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
static int const EXIT_CODE_REBOOT;//THIS IS THE IMPORTANT THING TO ADD TO YOUR CODE
~MainWindow();
private slots:
void slotReboot();//AND THIS ALSO
//ALL THE OTHER VARIABLES
}
The slotReboot() is the slot that will receive the signal of the QAction I'm going to show in the mainwindow.cpp
mainwindow.cpp
First initialize EXIT_CODE_REBOOT :
int const MainWindow::EXIT_CODE_REBOOT = -123456789;
and declare a QAction pointer:
QAction* actionReboot;
then in the MainWindow constructor:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
actionReboot = new QAction( this );
actionReboot->setText( tr("Restart") );
actionReboot->setStatusTip( tr("Restarts the application") );
connect( actionReboot, SIGNAL (triggered()),this, SLOT (slotReboot()));
}
And finally you need to send the signal (in the part of your code you need), in this way:
actionReboot->trigger();
I did the code I showed following these instructions: How to make an application restartable - Qt Wiki
You can use my open source library:
https://marketplace.qt.io/products/main-loop-wdt-for-qt-qml
It's a watchdog timer for the main qt loop, but I have a function for forced reboot, with different strategies: startdetached + exit, exec system call on Linux / macOS, and delayed restart (for example, exit and restart after 3 seconds)