I have written a QT code which starts a new process on a button click, that process have to execute a shell script and append the Std output/error on text Browser dynamically based upon the script result. The code gets failed in the custom Slot. This is what my window.h is
class Window : public QWidget
{
Q_OBJECT
public:
explicit Window(QWidget *parent = 0);
QPushButton *goButton;
QTextBrowser *statusWindow;
QProcess *process;
private slots:
void go_Button_Clicked();
void updateOutput();
};
And this is how my window.cpp is
Window::Window(QWidget *parent) : QWidget(parent)
{
// Text Browser
statusWindow = new QTextBrowser(this);
// Create the Go button, make "this" the parent
goButton = new QPushButton(this);
connect(goButton, SIGNAL (clicked()), this, SLOT (go_Button_Clicked()));
}
void Window::go_Button_Clicked()
{
// Step1 - Create a new process
QProcess *process = new QProcess(this);
// Step2 - Connect the Signals and slot
qDebug() << connect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(updateOutput()));
// Step3 - Start the Script process
process->start("/home/root/script.sh");
qDebug() << "Process in main";
}
void Window::updateOutput()
{
qDebug() << "Process in update Output";
//statusWindow->append(process->readAllStandardOutput());
}
So whenever i uncommented lines in update Output the GUI crashes as soon as i hit the button. using qdebug i managed to find that the GUI crashes bcoz of the line "statusWindow->append(process->readAllStandardOutput());" .
If the line is commented the debug message keeps on printing on console, but with the un-commented line i get the debug message once and then GUI crashes.Here is the debug output.
true
true
Process in main
Process in update Output
Process killed by signal
Any idea whats going on here, and i am familiar with Debugging in QT
Replace:
QProcess *process = new QProcess(this);
by
process = new QProcess(this);
You are using uninitialized member variable process, because in Window::go_Button_Clicked() you are creating a local variable.
Edit
Actually, the code is very error prone. What happens, when the user presses the button multiple times. Then in the slot you may read the the output from wrong process.
One workaround could be: You don't define QProcess as member at all, but do it like now - as a local variable. Then in the slot, you can cast the sender to QProcess*, and unless it fails use this instance. It will be always the right one. Afterwards, don't forget to delete the sender. Use deleteLater() for this.
Inside your void Window::go_Button_Clicked() slote you hide QProcess *process declared at .h file with new variable
QProcess *process = new QProcess(this);
Replace this line with
process = new QProcess(this);
But when you click second time you'll get memory leak, and can not get more data from first process. So you chould change you design somehow.
Related
I have a simple executable file (english.exe) that changes Windows' input language to English. I want to run this executable when the user interacts with various components of the GUI.
If I run the executable in the focusInEvent of QLineEdit, I get the expected behavior: the program runs, and there is no lag.
void MyLineEdit::focusInEvent(QFocusEvent *e)
{
QLineEdit::focusInEvent(e);
QProcess * switchInput = new QProcess(this);
switchInput->start( "c:/english.exe");
}
I want to run analogous code when a particular widget is activated, for which see this answer. (In fact, I would like to use focusInEvent for a QWebEngineView, but this resolved bug report notwithstanding, that event is never called; I am using Qt 5.12.6.) But with the following code, the GUI hangs.
bool MyWidget::event(QEvent *event)
{
bool result = QWidget::event(event);
if( event->type() == QEvent::WindowActivate )
{
// qDebug() << "QEvent::WindowActivate";
QProcess * switchInput = new QProcess(this);
switchInput->start( "c:/english.exe" );
}
return result;
}
Using the qDebug() code, I'm only getting one "QEvent::WindowActivate" message.
What is the difference between the two calls? Is there an alternative solution?
Edit: Whatever solution I find may not be of general interest. The program I'm running is a command-line AutoHotKey script (with contents PostMessage, 0x50, 0, 0x0409,, A) (no output or user interaction). For comparison I tried running ipconfig.exe (to take a command line program at random) and the problem goes away. So there must be something about the AutoHotKey script, or about AutoHotKey itself.
I need to process and close QFileDialog in tests of the application.
The dialog invoked by:
QFileDialog::getOpenFileName( ... );
In the test I 'catch' this dialog by:
QApplication::topLevelWidgets();
Then I choose need file by QFileDialog API.
And now I should close the dialog to get valid answer (filename) from QFileDialog::getOpenFileName();
But QDilaog's slots has no effect (accept(), reject(), close()... ). The dialog stays opened.
The solution described here works, but it is no option in my case. I must to use standard dialogs.
Is there a way to close it properly?
QFileDialog::getOpenFileName is a static method, so you're limited by what you can do with it.
If you want more control, I suggest creating an instance of QFileDialog and using that instead. By calling the instance's close() function, you can programmatically close the dialog.
In response to the comment that this doesn't work, here's example code:-
// Must create the FileDialog on the heap, so we can call close and the dialog is deleted
// Set the Qt::WA_DeleteOnClose flag if the instance is still required
QFileDialog* fileDlg = new QFileDialog(this, QString("Select Config file"), QDir::homePath(), QString("Config (*.xml)"));
// One shot timer to close the dialog programmatically
QTimer *timer = new QTimer(this);
timer->setSingleShot(true);
connect(timer, &QTimer::timeout, [=]()
{
fileDlg->close();
timer->deleteLater();
} );
timer->start(3000);
fileDlg->exec();
In order for to display a native dialog, you have to run exec() or call one of the static functions.
Unfortunately, in Windows, this calls a blocking function in the Windows API making the displayed dialog modal, running it's own event loop. Without returning to the Qt event loop, you cannot execute the close() function using the signals/slots interface.
I tried to bypass this by calling the close() function directly from another thread, but this results in Qt trying to send an event to the underlying dialog. Since sending (as opposed to posting) events across thread boundaries is not allowed in Qt, a fatal error is generated.
So, it seems for Windows at least, this is not possible.
I have not tested on platforms other than Windows. The code I used was:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QFileDialog* fileDlg = new QFileDialog(0, QString("Select Config file"), QDir::homePath(), QString("Config (*.xml)"));
// spawn a new thread
QtConcurrent::run([=](){
QTimer timer;
timer.setSingleShot(true);
QEventLoop *loop = new QEventLoop;
QObject::connect(&timer, &QTimer::timeout, [=](){
fileDlg->close();
fileDlg->deleteLater();
loop->quit();
});
timer.start(3000);
loop->exec();
delete loop;
});
fileDlg->exec();
return a.exec();
}
In Windows you can use WinAPI to close the dialog:
#define WAIT(A) while (!(A)) {}
HWND dialogHandle, button;
WAIT(dialogHandle = FindWindow(NULL, L"Open")); //write here title of the dialog
WAIT(button = FindWindowEx(dialogHandle, NULL, L"Button", L"&Open")); //write here title of the button to click
SendMessage(button, BM_CLICK, 0, 0);
I'm new to Qt, and want to simply display a video in Qt GUI. I basically got everything figured out, except for some details handling the QThread, which is really annoying.
I reformulate my question into a simpler program, hope it will explains better
first I define this QObject class
#include <QObject>
#include <QDebug>
class myObject : public QObject
{
Q_OBJECT
public:
explicit myObject(QObject *parent = 0);
bool stop;
signals:
void finishWork();
public slots:
void dowork();
void onfinishThread();
};
myObject::myObject(QObject *parent) :
QObject(parent)
{
stop = true;
}
void myObject::dowork(){
qDebug()<<"start working!";
while(!stop){
qDebug()<<"working...";
}
emit finishWork();
qDebug()<<"finish do work!";
}
void myObject::onfinishThread(){
qDebug()<<"thread is finished!";
}
then the main function
#include <QCoreApplication>
#include <QThread>
#include <iostream>
#include <windows.h>
#include "myobject.h"
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
myObject* myObj = new myObject();
QThread* myThread = new QThread;
myThread->connect(myObj, SIGNAL(finishWork()), SLOT(quit()));
myObj->connect(myThread, SIGNAL(started()), SLOT(dowork()));
myObj->connect(myThread, SIGNAL(finished()), SLOT(onfinishThread()));
myObj->moveToThread(myThread);
myObj->stop = false;
myThread->start();
cout<<"Press ENTER to continue....."<<endl<<endl;
cin.ignore(1);
myObj->stop = true;
Sleep(10);
if(myThread->isRunning()){
qDebug()<<"The thread is still running?!!!";
}
/*
myObj->stop = false;
Sleep(1000);
myThread->start();
myObj->stop = true;
*/
myObj->deleteLater();
myThread->deleteLater();
return a.exec();
}
As you can see, I even used cin to try let the dowork() run first, but it didn't work at all, the output is
so I'm really confused on how scheduling works for QThread...
Also, if you uncomment the part
/*
myObj->stop = false;
Sleep(1000);
myThread->start();
myObj->stop = true;
*/
the output is exactly the same! only stays a while after printing
The thread is still running?!!!
Would anyone help me with this? Thanks a lot. You may simply copy all the code and test it yourself.
-------------------------Original Question, bad explanation, please ignore....----------------------------------------
I made a videoObj Class with only one function to Query the frames, the function is defined as:
void videoObj::ProcessFrame(){
bool getframe;
qDebug()<<"get in ProcessFrame";
while(!stop_flag){
getframe = capture_.read(imgOriginal_);
if(!getframe){
qDebug()<<"Video End!";
break;
}
cv::cvtColor(imgOriginal_, imgOriginal_, CV_BGR2RGB);
QImage qimgOriginal((uchar*)imgOriginal_.data, imgOriginal_.cols, imgOriginal_.rows, imgOriginal_.step, QImage::Format_RGB888);
emit imgProcessed(qimgOriginal);
this->thread()->msleep(10);
//qDebug()<<"processing frames";
}
emit stopProcess();
qDebug()<<"Stop thread";
}
Basically above code is just query frames and whenever got one emit the
SIGNAL imgProcessed(qimgOriginal)
and whenever the stop_flag is set on, stop the while loop and emit the
SIGNAL stopProcess()
I use this class in the MainWindow Class, here is how I define the connection in the constructor:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
video_obj_ = new videoObj();
video_thread_ = new QThread;
this->connect(video_obj_, SIGNAL(imgProcessed(QImage)), SLOT(onImgProcssed(QImage))); \\this is for displaying the image
video_obj_->connect(video_thread_, SIGNAL(started()), SLOT(ProcessFrame()));
video_obj_->moveToThread(video_thread_);
video_thread_->connect(video_obj_, SIGNAL(stopProcess()), SLOT(quit()));
}
Above code works fine in frame query. The problem I don't understand is, if I set video_obj_->stop_flag on in any MainWiow member function, the ProcessFrame() in videoObj Class should emit stopProcess() signal and trigger quit() of video_thread_, and then the thread should finish, that is video_thread_->finished() is true.
However, if I do something like:
connect(video_thread_, SIGNAL(finished()), this, SLOT(onStopProcess())); //onStopProcess() see below
void MainWindow::on_btnStart_clicked()
{
video_obj_->stop_flag = 1;
this->thread()->msleep(10);
video_obj_->capture_.open(ui->lineEditVidoeAddress->text().toStdString());
//... something about videoCapture setting here, not related
video_obj_->capture_.set(CV_CAP_PROP_POS_FRAMES, 0);
video_obj_->stop_flag = 0;
this->thread()->msleep(10);
if(video_thread_->isRunning()){
qDebug()<<"The thread is still running?!!!";
}
video_thread_->start();
}
void MainWindow::onStopProcess(){
qDebug()<<"thread is finished";
}
It will give me the output:
Stop thread
The thread is still running?!!!
thread is finished
Which means triggering the quit() is not finish the thread, or quit() has not been triggered.
If I use:
video_thread_->wait(); //instead of video_thread_->wait(10);
The program will just freeze.
Is anyone can help me with this, it really confuse me about this quit() and finished()...
Thanks!
Since when you call stop_flag=1, the video_thread_ finishes the current function which is
ProcessFrame
before ProcessFrame finish, quit is called on the video_thread_ through emit stopProcess().
However, quit is different from terminate, terminate can exit the thread any time (but it is not safe), quit works with the event loop, if the thread has no eventloop, quit has no effect.
I guess before qthread execute the next event in the eventloop, it checks some flag, which can be set by quit, it the flag is set by quit, then it won't execute the next event in the eventloop. Or it can also be that a quit event is inserted into the eventloop, and the next event in the eventloop will be quit.
After stop_flag = 1, you called video_thread_->wait, that will block the video_thread_ from executing the next event in the eventloop, thus the quit will not take effect before time out, however, the nextlines which print "not finished?!!!" is executed immediately. Instead of calling video_thread->wait, if you call currentThread()->Sleep(some_enough_time), then there will be time for the video_thread_ to execute the next event and quit.
You can read the Qt documentation of QThread, wait is used with terminate to terminate a thread synchronously.
============================== The new code ================================
When you do:
myObj->connect(myThread, SIGNAL(started()), SLOT(dowork()));
The signal source is myThread, the slot also belongs to myThread, since the object "myThread" is created in the main thread, thus as a object it lives in the main thread. You can call myThread->thread() to see this, it will return the main thread instead of the new thread.
However, the started signal is emitted from the new thread namely the thread that myThread represents, thus the connection is a Qt::QueuedConnection. dowork() is posted in the event queue of main thread, and it'll be executed only after a.exec() which executes the main threads eventloop.
The same thing happens to the other 2 connect calls, all the slots will be executed in the eventloop of the main thread.
First the start is emitted from the new thread when myThread->start is called, dowork is posted in the main thread's event queue.
Nothing really happens before you call a.exec();
So the program will go ahead to cin and then set stop to true, and then print "Thread is still running?!!".
Second When a.exec() is called, the dowork is executed in the main thread and "start work" is printed. Nothing is done because the stop flag is already true, and the finishwork signal is emitted from the main thread, print "finish do work";
Third The last step the finishwork is emitted, and the quit slot is directly called. However, before the new thread can really quit, the main thread has already finished the eventqueue, because no more events are posted to the main thread. The application exits without waiting for quit() to take effect.
To test this is true, do not modify any of your code, just add after
emit finishWork();
currentThread()->sleep(1000);
you will see "thread is finished!" printed, because this make time for the new thread to emit finished(), and the onfinishThread() will be add to the main thread's eventqueue.
BTW, your way of working with thread looks like java style, which is not the standard qt way. You can read this before you work on qt thread.
This is not a scheduling issue.
That you did in your code looks like:
Create a thread.
Then this thread emits a signal that it started, run slot dowork()
start a thread.
Wait for user input
echo about thread is running
execute event loop
At point 3 thread is already running and signalled about that. Because myObj is created in main thread and not moved to any other thread (to process events there), your thread does not do anything else now, but just spins event loop. At the same time event that tells you want to run myObj's dowork() is posted on the main thread. At last then it comes to step 6 you start to execute event loop of it, and first thing it finds is event that it needs to call dowork() on myObj.
To make it clear to you how Qt threads and signal-slot connection works, I recommend you to read this article on Qt blog.
In simple to fix it, you can move your object myObj to the thread that you wan't to run.
But to make this really correct, I bet that you really want is to subclass QRunnable and (re-)implement it's run method to do the stuff you wan't in QThread, so that thread does it's job, than finished correctly, so you can join on it. Or depending on your goal you might be even better with using QtConcurrent::run(aFunction)
I have problem with one class in my project, after click appears new window with QTableWidget and QPushButton, after clicking the button I should have "test" on stdout, but nothing shows, here are parts of this code:
Header:
class ClientsSelector : public QWidget {
Q_OBJECT
public:
ClientsSelector(InvoiceTab* parent);
QWidget *window;
private:
QPushButton *accept;
public slots:
void loadData();
Constructor:
window = new QWidget();
layout = new QGridLayout();
layout->addWidget(table, 0, 0);
/*code*/
accept = new QPushButton(QString::fromUtf8("Load data"));
connect(accept, SIGNAL(clicked()), this, SLOT(loadData()));
layout->addWidget(accept, 0, 1);
/*code*/
window->setLayout(layout);
window->show();
Method:
void ClientsSelector::loadData() {
QTextStream std(stdout);
std << "test" << endl;
}
I have not even one warning nor error. I have nothing on stdout, it looks like button was connected to wrong object(?)
How do you instantiate ClientsSelector? Isn't it a singleton or global variable by chance? Try moving the connect call to a separate init function which is called after the ClientsSelector constructor. It helped me in similar WTF situations. It has something to do with the fact that each QObject inheritor has a static metadata member and you can't be sure about when it is fully initialized until the constructor finishes. connect won't work without that metadata.
See for example here: http://www.qtcentre.org/threads/9479-connect-in-constructor
If still lost, go through this checklist. Qt signals are so easy to use, everybody sometimes forgets it also has some requirements.
Seems like you forgot a closing " on the test printout.
Try using
qDebug() << "test";
instead of QTextstream
You can do following to make sure that the connection was made and slot is called.
connect function returns status of connection. Check if the connection was made properly.
put a breakpoint and see if the loadData() is called when button is pressed.
This might help to find the cause.
For the last few days, I've been trying out the new preferred approach for using QThreads without subclassing QThread. The trouble I'm having is when I try to shutdown a set of threads that I created. I regularly get a "Destroyed while thread is still running" message (if I'm running in Debug mode, I also get a Segmentation Fault dialog). My code is very simple, and I've tried to follow the examples that I've been able to find on the internet.
My basic setup is as follows: I've a simple class that I want to run in a separate thread; in fact, I want to run 5 instances of this class, each in a separate thread. I have a simple dialog with a button to start each thread, and a button to stop each thread (10 buttons). When I click one of the "start" buttons, a new instance of the test class is created, a new QThread is created, a movetothread is called to get the test class object to the thread...also, since I have a couple of other members in the test class that need to move to the thread, I call movetothread a few additional times with these other items. Note that one of these items is a QUdpSocket, and although this may not make sense, I wanted to make sure that sockets could be moved to a separate thread in this fashion...I haven't tested the use of the socket in the thread at this point.
Starting of the threads all seem to work fine. When I use the linux top command to see if the threads are created and running, they show up as expected.
The problem occurs when I begin stopping the threads. I randomly (or it appears to be random) get the error described above.
Class that is to run in separate thread:
// Declaration
class TestClass : public QObject
{
Q_OBJECT
public:
explicit TestClass(QObject *parent = 0);
QTimer m_workTimer;
QUdpSocket m_socket;
Q_SIGNALS:
void finished();
public Q_SLOTS:
void start();
void stop();
void doWork();
};
// Implementation
TestClass::TestClass(QObject *parent) :
QObject(parent)
{
}
void TestClass::start()
{
connect(&m_workTimer, SIGNAL(timeout()),this,SLOT(doWork()));
m_workTimer.start(50);
}
void TestClass::stop()
{
m_workTimer.stop();
emit finished();
}
void TestClass::doWork()
{
int j;
for(int i = 0; i<10000; i++)
{
j = i;
}
}
Inside my main app, code called to start the first thread (similar code exists for each of the other threads):
mp_thread1 = new QThread();
mp_testClass1 = new TestClass();
mp_testClass1->moveToThread(mp_thread1);
mp_testClass1->m_socket.moveToThread(mp_thread1);
mp_testClass1->m_workTimer.moveToThread(mp_thread1);
connect(mp_thread1, SIGNAL(started()), mp_testClass1, SLOT(start()));
connect(mp_testClass1, SIGNAL(finished()), mp_thread1, SLOT(quit()));
connect(mp_testClass1, SIGNAL(finished()), mp_testClass1, SLOT(deleteLater()));
connect(mp_testClass1, SIGNAL(finished()), mp_thread1, SLOT(deleteLater()));
connect(this,SIGNAL(stop1()),mp_testClass1,SLOT(stop()));
mp_thread1->start();
Also inside my main app, this code is called when a stop button is clicked for a specific thread (in this case thread 1):
emit stop1();
Sometimes it appears that threads are stopped and destroyed without issue. Other times, I get the error described above.
Any guidance would be greatly appreciated.
Thanks,
Bryan
http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
"Finally, to prevent nasty crashes because the thread hasn’t fully shut down yet when it is deleted, we connect the finished() of the thread (not the worker!) to its own deleteLater() slot. This will cause the thread to be deleted only after it has fully shut down."
Please try replacing:
connect(mp_testClass1, SIGNAL(finished()), mp_thread1, SLOT(deleteLater()));
with:
connect(mp_thread1, SIGNAL(finished()), mp_thread1, SLOT(deleteLater()));