I am writing the client for a client/server application. The clients are supposed to login using a login window. If the login is successful, a "waiting" window appears (this is just a window that contains a label). On the server side there is a barrier that waits for n clients to be logged in; when this happens, a message is broadcasted, the waiting window is supposed to close and a new window appears for every client.
The networking interface is implemented by me, using low-level functions, not the functionality provided by Qt.
The actual waiting loop is something like this:
char buffer[256];
while (strcmp(buffer, "proceed"))
read(sockfd, buffer, 256);
The problem is that if I start this loop in the main thread, the application blocks, for obvious reasons.
How can I make this loop run and not block the application, and close the dialog when it ends?
Later edit: I did also attempt to use QThreads, but, for reasons which I don't fully understand yet, the application still crashes:
class WaitLoop : public QThread {
public:
WaitLoop(NetworkHandler &network) : network(network) {}
private :
NetworkHandler &network;
void run() {
this->network.waitForGameStart();
}
};
In the wait dialog constructor:
WaitLoop *waitLoop = new WaitLoop(network);
connect(waitLoop, SIGNAL(finished()), this, SLOT(gameStartSlot()));
waitLoop->start();
The application still crashes using this approach.
The sanest way to approach this would not be using low-level functions, because you aren't writing in C. Use at least QAbstractSocket to wrap a sockfd. The setSocketDescriptor method lets you do it.
Your code then becomes non-blocking and asynchronous:
class Controller : public QObject {
Q_OBJECT
QStateMachine m_sm;
QState s_init{&m_sm}, s_proceeding{&m_sm};
QAbstractSocket m_socket;
Q_SIGNAL void proceed();
Q_SLOT void onData() {
auto data = m_socket.readAll();
if (data.contains("proceed")) proceed();
}
public:
Controller(QObject * parent = 0) : QObject(parent) {
connect(&m_socket, &QIODevice::readyRead, this, &Controller::onData);
s_init.addTransition(this, &Controller::proceed, &s_proceeding);
m_sm.setInitialState(&s_init);
m_sm.start();
}
bool setup(quintptr fd) {
return m_socket.setSocketDescriptor(fd);
}
};
Through the use of a state machine, it's easy to add more states, react to their transitions (see QState::onEntry signal, etc.), and ensure that the behavior is correct. Fleshing out a UML statechart forces you to think about handling corner cases, etc. See this answer for a full example.
Related
I'm developing an application where a user may parse some binary files. Once he clicks the "parse"-button, he first may select some files, which are parsed afterwards. While the application is processing the files, I'd like to display a modal dialog, which informs the user about the progress (QProgressBar bar) and the already parsed files (QListView list / listModel).
My current approach is to override the exec()-method of a QDialog-sublcass. This way I could just call
parseAssistant.exec()
The current implementation looks like this:
class ParseAssistant : public QDialog { public: int exec(); };
int ParseAssistant::exec()
{
bar->setMaximum(files.size());
this->show();
this->setModal(true);
for (int i = 0; i < files.size(); i++) {
PluginTable* table = parser.parse(files[i]);
// do something with the table
// saveTableintoDB();
// update GUI
// bar->setValue(i);
// listModel->insertRow(0, new QStandardItem(files[i]));
}
this->hide();
return QDialog::Accepted;
}
After this (blocking) method has run, the user either has parsed all files or canceled the progress somewhere. To achieve this I attempted to use QApplication::processEvents in the while-loop (which feels kinda laggy as it's only progressed when a file has finished parsing) or to outsource the heavy calculation(s) to some QConcurrent implementation (::run, ::mapped). Unfortunately, I don't know how to return the program flow back to the exec() method once the QFuture has finished without relying on some CPU-intense loop like:
while (!future.isFinished()) { QApplication::processEvents(); }
Is there a smarter approach to having a modal dialog, which runs a heavy calculation (which may be canceled by the user) without blocking the eventloop?
I wouldn't subclass Qdialog, in the first place, but just use a QFutureWatcher and connect the watcher finished signal to the dialog close slot, this way:
QDialog d;
QFutureWatcher<void> watcher;
QObject::connect(&watcher, &QFutureWatcher<void>::finished, &d, &QDialog::close);
QFuture<void> future = QtConcurrent::run(your_parse_function);
watcher.setFuture(future);
d.exec();
//control returns here when your_parse_function exits
The parse function could be a method in a QObject derived class, like this:
class Parser : public QObject
{
Q_OBJECT
public:
void parse()
{
for (int i = 0; i < files.size(); i++) {
PluginTable* table = parser.parse(files[i]);
emit fileParsed(i, files.size);
// ...
}
}
signals:
void fileParsed(int id, int count);
};
You can connect the fileParsed signal to a slot of choice, and from there set the progress bar value accordingly.
My personal approach would be:
create a separate thread and do the processing there (QThread; std::thread should do the trick as well)
provide a signal that informs about the file currently being processed
possibly another signal informing about progress in %
another signal informs that processing is done, emitted just before the thread ends
provide your dialog with appropriate slots and connect them to the signals (as different threads involved, make sure connection type is Qt::QueuedConnection)
Basically, I've the next code:
class serverTCP : public QObject
{
// Other methods...
signals:
void newInstructions(QJsonDocument jDoc);
public slots:
void responseObtained(QJsonDocument jDoc);
}
class gatherModbus : public QObject
{
// Other methods...
signals:
void responseReady(QJsonDocument jDoc);
public slots:
void executeInstruction(QJsonDocument jDoc);
void myprocess() {
while(true)
{
// Write and read Serial Port
// Save data in json
}
};
}
void main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
int netsNumber = 2; //In reality this is dynamic. It's only a example
serverTCP *server = new serverTCP;
gatherModbus * gather = new gatherModbus[netsNumber];
QThread * threads = new QThread[netsNumber];
// more necessary code...
for(int i = 0; i < netsNumber; i++)
{
gather[i].moveToThread(threads[i]);
QObject::connect(&server, SIGNAL(newInstructions(QJsonDocument)),
&gather[i], SLOT(executeInstruction(QJsonDocument)));
QObject::connect(&gather[i], SIGNAL(responseReady(QJsonDocument)),
&server, SLOT(responseObtained(QJsonDocument)));
QObject::connect(&threads[i], SIGNAL(start()),
&gather[i], SLOT(myprocess()));
// Other signals needed between the objects 'gather' and 'threads'
threads[i].start();
}
a.exec();
}
The problem is that the connections between objects 'server' and 'gather' do not work. The object 'server' is in the same thread as the 'main' function but objects 'gather' have moved to other threads.
What have I to do to make both objects can communicate properly?
My purpose is that the 'server' must be able to send a signal to all objects 'gather' there are. In each of the objects 'gather' must execute the slot and return the 'server' response if any.
If I set up the connection to be the type Qt::DirectConnection slots running on the same thread as the 'main' function and object 'server' and that does not interest me.
Any help or suggestions will be appreciated.
All is in Qt documentation.
First, read that.
Then if you're not satisfied, you can use QCoreApplication::postEvent() (for more informations you need : here)
Both signals/slots (QObject:connect()) and QCoreApplication::postEvent() are thread-safe and can solve your problem.
So events and signal/slots are two parallel mechanisms accomplishing the same things, in general an event will be generated by an outside entity (e.g. Keyboard, Mouswheel) and will be delivered through the event loop in QApplication. In general unless you set up the code you will not be generating events. You might filter them through QObject::installEventFilter() or handle events in subclassed object by overriding the appropriate functions.
Signals and Slots are much easier to generate and receive and you can connect any two QObject subclasses. They are handled through the Metaclass (have a look at your moc_classname.cpp file for more) but most of the interclass communication that you will produce will probably use signals and slots. Signals can get delivers immediately or deferred via a queue (if you are using threads) A signal can be generated
Your demo code seems OK. That's how we organize our current project. You'd better provide more detailed codes if necessary to explain your problem.
BTW, after reading your interests, I'd recommend you the QtConcurrent module which seems fitting your interest better.
Huh... Your code is not ok. This is the source of all your trouble
void myprocess() {
while(true)
{
// Write and read Serial Port
// Save data in json
}
};
If you want the slots newInstructions and responseObtained slots to ever run, myprocess should not be an infinite loop. You need to :
Modify myprocess such that once it is done writing and reading currently available data, it completes
Have a mechanism to know that new processing need to be done. For instance, if you are using a QIODevice subclass (socket, input stream, etc...) you have the signal QIODevice::readyRead() which will notify you there is new data to read from the device. I suspect your newInstructions is supposed to do just that.
connect this mechanism to another call to myprocess to allow the processing to start again
Edit : Given your comment, this is a way to modify the infinite while loop without too much modification.
void myprocess() {
make_one_processing_round();
if(should_continue_processing())
{
QMetaObject::invokeMethod(this, &gatherModbus::myprocess, Qt::QueuedConnection);
}
};
QMetaObject::invokeMethod will schedule this method for execution at the back of the thread QEventLoop queue. Which means other slots can execute.
I can solve my problem adding the next line in the end of "myprocess" method:
QCoreApplication::processEvents(QEventLoop::AllEvents);
The final code of this method is this:
void myprocess() {
while(true)
{
// Write and read Serial Port
// Save data in json
// New line:
QCoreApplication::processEvents(QEventLoop::AllEvents);
}
};
With this line I get that events will processed if any. I don't known if it is the best solution, but it works as wanted.
Thanks to all of you for your help and answers.
I am developing a C++ class library for some computing-intensive tasks (machine vision).
// I am a part of a Qt-agnostic library
class Cruncher
{
/* ... */
public:
void doStuff();
};
Then there's a Qt GUI using that library. I'm creating a worker thread to call the heavy-lifting routines from the library:
// I am a part of a Qt-based GUI which utilizes the library
class Worker : public QThread
{
/* ... */
protected:
virtual void run()
{
/* ... */
Cruncher c;
for (int i = 0; i < count; ++i)
c.doStuff(); // takes some time, and while it's working
// it should communicate status changes which should
// become visible in the GUI
}
};
Now inside doStuff() a lot happens and I want to provide some feedback to the user on what is going on without waiting for doStuff() to return. For one, maybe some finer progress reporting than just increasing the meter by one step after a each call to doStuff(). Also, doStuff() may encounter non-critical failures which let it continue a part of the work, but I'd like a message to appear in the GUI when this happens as Cruncher is working (and Worker is currently busy with a call to doStuff()).
I want the library to remain Qt-independent so I'm not willing to add signals and slots to Cruncher. Any other way to enable it to provide feedback to the GUI to report on its work when it's not a Qt class?
I was considering creating a QTimer which would poll some "status" and "errorMsg" members of Cruncher at fixed intervals while Worker is running, but this seems highly sub-optimal.
I am posting my own answer because though I took #Nim's advice, I'd like the answer to be a little more verbose and hence more useful if someone should have the same problem.
I created the skeleton of a message dispatcher in the library:
// doesn't need to know about Qt
class MessagePort
{
public:
virtual void message(std::string msg) = 0;
};
Next, I added a handle to this object to Cruncher and spiced doStuff() with occasional calls to message():
// now with Super Cow powers!
class Cruncher
{
protected:
MessagePort *msgPort_;
public:
Cruncher(MessagePort *msgPort) : msgPort_(msgPort) {}
void doStuff()
{
while(...)
{
/*...*/
msgPort_->message("Foo caused an overload in Bar!");
}
}
};
Finally, I crafted an implementation of MessagePort inside the GUI using all necessary Qt goodness:
class CruncherMsgCallback : public QObject, public MessagePort
{
Q_OBJECT
public:
CruncherMsgCallback() : QObject(), MessagePort()
{
connect(this, SIGNAL(messageSignal(const QString &)),
GUI, SLOT(messageShow(const QString &)),
Qt::QueuedConnection);
}
virtual void message(std::string msg)
{
emit messageSignal(QString::fromStdString(msg));
}
signals:
void messageSignal(const QString &msg);
};
Finally when the Worker creates an instance of Cruncher, it also gives it a pointer to a working MessagePort:
class Worker
{
protected:
virtual void run()
{
CruncherMsgCallback msgC;
Cruncher c(&msgC); // &msgC works as a pointer to a
// generic MessagePort by upcasting
c.doStuff(); // Cruncher can send messages to the GUI
// from inside doStuff()
}
};
Use a callback function (class) etc, and pass that in during construction. Things you need to report, report via that callback.
You can safely emit signals from the run() method, I think that's the best way to pass information from worker thread to the main thread. Just add the signals to your QThread subclass (avoid adding slots, if you're at all unsure how QThread threading works).
Better make the connections from these signals explicitly queued, to avoid problems. Though the default, automatic connection type should also work and do Queued signal emit, but I think it's better to be explicit in cases like this. Actually also direct signals should work as such, but then you have to take care of thread safety yourself instead of letting Qt handle it for you, and you can't connect to slots which use any of the QtGui classes which only work in the main thread, so it's better to stick to queued connections.
To pass simple information to the run() method, and if immediate reaction is not needed, maybe use a few shared QAtomicInt variables or something like that as flags, which the worker thread checks when convenient. Slightly more complex method, still requiring polling, is to have shared data structure which you protect with mutex. More complex way of communicating to that direction would involve some kind of message queue (just like Qt uses in the event loop of the main thread, when you emit signal to that direction).
In Borland 6 I often use this to unstuck program action:
Application->Processmessages();
Now, with QT 4.8.1, I don't have found in this foreign (for me) documentation of QT.
Can anyone help me?
In Qt, you'd use the static function QApplication::processEvents().
Alas, your issue is that the design of your code is broken. You should never need to call processEvents simply to "unstuck" things. All of your GUI code should consist of run-to-completion methods that take a short time (on the order of single milliseconds: ~0.001s). If something takes longer, you must split it up into smaller sections and return control to the event loop after processing each section.
Here's an example:
class Worker: public QObject
{
Q_OBJECT
int longWorkCounter;
QTimer workTimer;
public:
Worker() : ... longWorkCounter(0) ... {
connect(workTimer, SIGNAL(timeout()), SLOT(longWork());
}
public slots:
void startLongWork() {
if (! longWorkCounter) {
workTimer.start(0);
}
}
private slots:
void longWork() {
if (longWorkCounter++ < longWorkCount) {
// do a piece of work
} else {
longWorkCounter = 0;
workTimer.stop();
}
}
};
A zero-duration timer is one way of getting your code called each time the event queue is empty.
If you're calling third party blocking library code, then the only (unfortunate) fix is to put those operations into slots in a QObject, and move that QObject to a worker thread.
Consider the following in Qt using QtSoap lib:
QtSoapHttpTransport http;
http.setHost("XXXX",3333);
connect(&http, SIGNAL(responseReady()), this, SLOT(getResponse()));
now there is a method i want to call which is:
QtSoapMessage request;
request.setMethod("test");
request.addMethodArgument("xxx","zzzz",xxx);
request.addMethodArgument("xx","xx",xx);
http.submitRequest(Request, "/api/soap");
now i want to have something like this :
QString GetTest(){
while(http.isBusy); // no such a thing as isbusy
return http.getResponse().returnValue().toString();}
or any technique i can use to get the return value or wait for it and get it..
Thanks in advance...
I don't see a problem. The QtSoapHttpTransport reference already has a nice simple example.
If you want to have a getter that blocks and returns only when the response is received, doing active wait (your while loop) is absolutely not a way to go.
You already connect the responseReady signal to your slot, so the only missing thing would be to have a synchronization point that blocks your thread calling getTest until this slot is executed.
class Messenger : public QObject {
Q_OBJECT
public:
Messenger() { /* ... your initialization code with connect ... */ }
void sendRequest() { /* ... your sending code ... */ }
QString getTest() // call this from a worker thread to wait
{ // for a response to arrive and retrieve it
QMutexLocker lock(&responseMutex);
responseReady.wait(&responseMutex);
return http.getResponse().returnValue().toString();
}
public slots:
void getResponse() { // slot called by Qt event loop when response arrives
responseReady.wakeAll();
}
private:
QtSoapHttpTransport http;
QWaitCondition responseReady;
QMutex responseMutex;
};
Note that this design only makes sense if you have a multithreaded application and the thread calling getTest is a working thread, not event-driven thread.
On the other hand, if your application just wants to do something with the received response, there is imho no reason why you need a blocking method in the first place. Just perform your actions in the slot directly - just like it is in the Qt documentation.