Using 5.0.2 on Win 7
I have 2 “ImageLoop” classes [which extend QWidget] where each take up half the screen, split vertically. Each contains a QLabel for displaying a list of jpg files. So, inside a for loop, I give each widget their list of images, and emit a “listfull” signal which I have connected to a slot – “playList” – in each of the two widgets. Unfortunately, it appears that only the first widget’s signal ever gets emitted as only the first widget is ever updated.
I am new to Qt programming and maybe I am misunderstanding the slot/signal system. I thought the pseudocode below would, for each instance, fill the list, emit the signal, and each instance would go off on their merry way — basically each widget simultaneously and independently showing images. So, question is what am I missing? Or am I going to have to spawn each of these in their own thread?
Thanks!
Pseudocode
for(int i=0; i<2; i++){
Create ImageLoop instance
connect(instance, SIGNAL(listfull()), instance, SLOT(playList()));
instance->FillList(arrayOfImageFileNames);
}
//inside of ImageLoop class
void FillList(arrayOfImageFileNames) {
//adds all files to an internal list
//when finished
emit listfull();
}
//inside of ImageLoop class
void playList() {
//code to loop through each image and show it
}
for(int i=0; i<2; i++){
Create ImageLoop instance
connect(instance, SIGNAL(listfull()), instance, SLOT(playList()));
instance->FillList(arrayOfImageFileNames);
}
You are expecting this pseudo-code to asynchronously call playList() on both imageLoop instances. What's happening instead is Qt is connecting listFull to playList directly, making this pseudo-code equivalent to:
for(int i=0; i<2; i++){
Create ImageLoop instance
instance->FillList(arrayOfImageFileNames);
instance->playList();
}
My guess is playList is also synchronous, which means it will finish, and only then will the for loop continue to the second ImageLoop instance.
Qt doesn't allow you to have multiple GUI threads, nor can you update the GUI from multiple threads, so you need to show and image then send a signal asynchronously to show the next image.
Chances are, what you really want is to start a timer when fillList() is done that calls a slot showNextImage() after a certain delay.
Related
using QtCreator to make a loftier interface to a sofware.
There is basically a set of buttons to tune and inputs, a start and stop job
buttons, my problem comes from an infinite loop that freezes the display so I came up with using fork() so that the loop have to compete with the main program instead of eating up the whole resources (no multithreading), but the program crashes spiting:
[xcb] Unknown sequence number while processing queue
[xcb] Most likely this is a multi-threaded client and XInitThreads has not
been called
[xcb] Aborting, sorry about that.
a.out: ../../src/xcb_io.c:274: poll_for_event: Assertion
`!xcb_xlib_threads_sequence_lost' failed.
the fonction calling the loop is called 'ON', 'OFF' is supposed to exit the forked process.
//in button 'ON' func
ps = fork();
if(getpid() == ps)
{
while(1)
{
strcpy(word, charset(minlength, maxlength, N));
ui->pass->setText(word);//operation on the display
....SNIP
}
}
//In button 'OFF' func
if(getpid() == ps)
exit(0);
I'm really asking what is the correct way of starting a while(1) and be able to break, exit, or return from it while not freezing the window using QT, thanks.
You crash probably here:
ui->pass->setText(word);//operation on the display
as in Qt, you can not change UI from non UI threads directly. Only from signals and slots mechanism.
The proper way to not freeze UI is obviously to compute lengthy operations in another thread.
You can achieve this in several ways:
One is by sub-classing QObject class to create 'worker object' which would perform all heavy operations. You create new QThread object that should live as long as you need your object. And use QObject::moveToThread method to move created object to new thread. To control your worker object you should send signals from object and call it's slots also via signal-slot mechanism. If you call them directly - they will be executed in caller thread (so do not perform stuff like worker->startHeavyJob(); in UI thread). Instead emit signal in UI (emit sigStartHeavyStuff();) and connect it to slot of your worker object (slotDoHeavyStuff();)
if you do not want to bother with this (if operation is pretty small)
- you can use QApplication::processEvents() to process events in UI event loop while going in your infinite while loop.
Another way is to use QtConcurrentRun framework to run function in separate thread which manages itself. Threads are taken from thread pool and are managed by Qt. This approach looks like something you want to do. Though you still will be able to access UI objects only through signals and slots.
I see one big issue in the presented code that is causing your freeze: You never let Qt process anything while you are in the loop. You need to allow Qt to run it's event loop. The easiest way is to use QApplication::processEvents() inside the loop.
I'm also not a fan of a while(1) loop for a couple of reasons. The first of which is that it can eat machine cycles waiting for things to happen. I doubt you really need to run the code as fast as possible, you could probably get away with some sleeping in your loop.
The other issue is that it is hard to break out. A cleaner approach would be something like this
void MyClass::on_pushButton_ON_clicked()
{
MyClass::done = false; // this is a class attribute
while (!MyClass::done) {
QApplication::processEvents();
//...
}
}
void MyClass::on_pushButton_OFF_clicked()
{
MyClass::done = true;
}
I've got a Qt app that needs to call an expensive non-Qt function (e.g. to unzip a ~200MB zip file), and since I'm calling that function from the main/GUI thread, the Qt GUI freezes up until the operation completes (i.e. sometimes for 5-10 seconds).
I know that one way to avoid that problem would be to call the expensive function from a separate thread, but since there isn't much the user can do until the unzip completes anyway, that seems like overkill. I can't add processEvents() calls into the expensive function itself, since that function is part of a non-Qt-aware codebase and I don't want to add a Qt dependency to it.
The only thing I want to change, then, is to have a little "Please wait" type message appear during the time that the GUI is blocked, so that the user doesn't think that his mouse click was ignored.
I currently do that like this:
BusySplashWidget * splash = new BusySplashWidget("Please wait…", this);
splash->show();
qApp->processEvents(); // make sure that the splash is actually visible at this point?
ReadGiantZipFile(); // this can take a long time to return
delete splash;
This works 95% of the time, but occasionally the splash widget doesn't appear, or it appears only as a grey rectangle and the "Please wait" text is not visible.
My question is, is there some other call besides qApp->processEvents() that I should also do to guarantee that the splash widget becomes fully visible before the lengthy operation commences? (I suppose I could call qApp->processEvents() over and over again for 100mS, or something, to convince Qt that I'm really serious about this, but I'd like to avoid voodoo-based programming if possible ;))
In case it matters, here is how I implemented my BusySplashWidget constructor:
BusySplashWidget :: BusySplashWidget(const QString & t, QWidget * parent) : QSplashScreen(parent)
{
const int margin = 5;
QFontMetrics fm = fontMetrics();
QRect r(0,0,margin+fm.width(t)+margin, margin+fm.ascent()+fm.descent()+1+margin);
QPixmap pm(r.width(), r.height());
pm.fill(white);
// these braces ensure that ~QPainter() executes before setPixmap()
{
QPainter p(&pm);
p.setPen(black);
p.drawText(r, Qt::AlignCenter, t);
p.drawRect(QRect(0,0,r.width()-1,r.height()-1));
}
setPixmap(pm);
}
Moving to another thread is the correct way to go but for simple operations, there's a much less complicated way to accomplish this without the pain of managing threads.
BusySplashWidget splash("Please wait…", this);
QFutureWatcher<void> watcher;
connect(&watcher, SIGNAL(finished()), &splash, SLOT(quit()));
QFuture<void> future = QtConcurrent::run(ReadGiantZipFile);
watcher.setFuture(future);
splash.exec(); // use exec() instead of show() to open the dialog modally
See the documentation about the QtConcurrent framework for more information.
I have 2 classes: one maintains some loop (at leas for 2-3 minutes; and is inherited from QObject) and another shows up a progress dialog (inherited from QDialog).
I want to start the loop as soon as the dialog is shown. My first solution was:
int DialogClass::exec()
{
QTimer::singleShot(0, LoopClassPointer, SLOT(start()));
return __super::exec();
}
There is a problem with throwing exceptions from slots. so I considered a possibility to make public slot start() just a public function. But now I don't know how to make it works well. Things like this:
int DialogClass::exec()
{
LoopClassPointer->start();
QApplication::processEvents();
return __super::exec();
}
don't help. The dialog doesn't appears.
Is there a common approach to this kind of situations?
some details, according to questions:
I have to work with system with its own styles, so we have a common approach in creating any dialogs: to inherit them from stytle class, which is inherited from QDialog.
my 'LoopClassPointer' is an exported class from separate dll (there is no UI support in it).
I have a 'start' button in main app, which connected with a slot, which creates progress dialog and 'LoopClassPointer'. at the moment I send 'LoopClassPointer' instance in the dialog and don't whant to make significant changes in the architecture.
Take a look at QtDemo->Concurrent Programming->Run function
e.g. in Qt 4.8: http://qt-project.org/doc/qt-4.8/qtconcurrent-runfunction.html
In this situation, I recommend you separate the logic of the loop from the dialog. Gui elements should always be kept separate.
It's great that your worker class is derived from QObject because that means you can start it running on a separate thread: -
QThread* m_pWorkerThread = new QThread;
Worker* m_pWorkerObject = new Worker; // assuming this object runs the loop mentioned
// Qt 5 connect syntax
connect(m_pWorkerThread, &QThread::started, m_pWorkerObject, &WorkerObject::start);
connect(m_pWorkerThread, &QThread::finished, m_pWorkerThread, &QThread::deleteThis);
m_pWorkerObject->moveToThread(m_pWorkerThread);
m_pWorkerThread->start();
If you're not familiar with using QThread, then start by reading this.
The only other thing you require is to periodically send signals from your worker object with progress of its work and connect that to a slot in the dialog, which updates its display of the progress.
I get continuous answers from a server with a delay from 1 sec. I append() this answers to an QTextEdit field. But the changes first displayed when the method call has finished.
How can I displayed the changes immediately?
I have tryed update() but it doesent work..
void ClientWidget::setAnswer(ValueStream *resultStream){
std::vector<std::string> answer;
for(int i = 0; i < 15; i++){
value tmpResultValue;
if(resultStream->get(tmpResultValue)){
this->client.parseResult(tmpResultValue, answer);
std::vector<QString> qAnswer = vectorStringToVectorQstring(answer);
for (unsigned int n = 0; n < qAnswer.size(); n++){
this->answerTextEdit->append(qAnswer[n]);
}
this->answerTextEdit->update();
}
answer.clear();
}
resultStream->close();
delete resultStream;
}
after this->answerTextEdit->append(qAnswer[n]); the changes should displayed but they dont displayed immediately
When you set the TextEdit widget's text, a signal is emitted that it has changed, in order for the widget to update what you see. That signal is placed in a queue of messages that are processed as events in the Qt event loop.
While you're processing the incoming data, Qt's event loop cannot continue until you finish. An easy, but not the best way of handling this is to call QApplication::processEvents to allow the event loop to run; this can be very inefficient as all events in the queue are processed.
A better way to handle time-consuming processing is to move it onto a new thread, which is reasonably easy to do with QThread. That way, you can process the incoming requests from the server and emit a signal from your thread to the main thread, which can then update the TextEdit widget.
To understand how to use QThread, I suggest reading this article. It really isn't difficult to do and I recommend you try it, rather than adding a call to QApplication::processEvents.
You can call
QApplication::processEvents();
after calling your method.
if you can change ValueStream to emit a signal each time a new value is available with the value as a parameter, then it will become much easier. just connect a slot to it where you append the answer to the answerTextEdit
I'm attempting to wrap a GUI around an existing management console app. The main function is to search for network devices, which is given a timeout and is essentially a blocking call until the timeout has expired (using sleep to do the blocking). In this example the call is this->Manager->Search(...).
My issue is that I want the QListWidget to display "Searching..." while the search is taking place, and then update with the results at the completion of the search. My on-click code for the Search button is as follows:
void ManagerGUI::on_searchButton_clicked()
{
ui->IPList->clear();
new QListWidgetItem(tr("Searching..."), ui->IPList);
ui->IPList->repaint();
this->Manager->Search(static_cast<unsigned int>(this->ui->searchTime->value()*1000.0));
ui->IPList->clear();
if(this->Manager->GetNumInList() != 0)
this->displayFoundInList(this->Manager->GetFoundList());
else
new QListWidgetItem(tr("No Eyes Found"), ui->IPList);
ui->IPList->repaint();
}
When I hit the button, the QListWidget IPList does not update until after the timeout has taken place (and I assume until after this callback has terminated). Does anyone have any suggestions? I was under the impression that calling ui->IPList->repaint() would cause an immediate redraw of the list.
Additional info:
QT Version 5.1.0 32-Bit
Compiled using VS2012
Running on Win7 Pro 64-bit (but to be ported to OSX and Linux, so nothing win-specific please)
1) You don't need to call repaint directly.
2) You should do your search asynchronously. It is big topic - you should learn basics of Qt first.
Start with signals and slots and then learn about QThread or QtConcurrent. Then implement a class that will do searching and send necessary signals: first signal on search start, second signal - on search stop. Then connect slots to this signals and work with your list view insine this slots.
Problem is that your "Search manager" blocks Qt's event loop. Thats why listview does not repainted.
You need a signal slot system because your search is blocking. Ideally you should do the search in a new thread. However you can cheat with a processEvents()
void ManagerGUI::on_searchButton_clicked()
{
ui->IPList->clear();
new QListWidgetItem(tr("Searching..."), ui->IPList);
emit signalStartSearch();
}
void ManageGUI::slotStartSearch()
{
// Process any outstanding events (such as repainting)
QCoreApplication::processEvents();
this->Manager->Search(static_cast<unsigned int>(this->ui->searchTime->value()*1000.0));
emit signalSearchCompleted();
}
void ManagerGUI::slotSeachCompleted()
{
ui->IPList->clear();
if(this->Manager->GetNumInList() != 0) {
ui->IPList->setUpdatesEnabled(false);
this->displayFoundInList(this->Manager->GetFoundList());
ui->IPList->setUpdatesEnabled(true);
} else {
new QListWidgetItem(tr("No Eyes Found"), ui->IPList);
}
}
Ideally you would want the Manager->Search to emit the signal and then use QtConcurrent::run to do the search in another thread.