Force update of QTListWidget to add/remove elements - qt

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.

Related

Forking with QT

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;
}

How to do a kill(pid, SIGINT) in windows? [duplicate]

I have (in the past) written cross-platform (Windows/Unix) applications which, when started from the command line, handled a user-typed Ctrl-C combination in the same way (i.e. to terminate the application cleanly).
Is it possible on Windows to send a Ctrl-C/SIGINT/equivalent to a process from another (unrelated) process to request that it terminate cleanly (giving it an opportunity to tidy up resources etc.)?
I have done some research around this topic, which turned out to be more popular than I anticipated. KindDragon's reply was one of the pivotal points.
I wrote a longer blog post on the topic and created a working demo program, which demonstrates using this type of system to close a command line application in a couple of nice fashions. That post also lists external links that I used in my research.
In short, those demo programs do the following:
Start a program with a visible window using .Net, hide with pinvoke, run for 6 seconds, show with pinvoke, stop with .Net.
Start a program without a window using .Net, run for 6 seconds, stop by attaching console and issuing ConsoleCtrlEvent
Edit: The amended solution from #KindDragon for those who are interested in the code here and now. If you plan to start other programs after stopping the first one, you should re-enable CTRL+C handling, otherwise the next process will inherit the parent's disabled state and will not respond to CTRL+C.
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AttachConsole(uint dwProcessId);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern bool FreeConsole();
[DllImport("kernel32.dll")]
static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate HandlerRoutine, bool Add);
delegate bool ConsoleCtrlDelegate(CtrlTypes CtrlType);
// Enumerated type for the control messages sent to the handler routine
enum CtrlTypes : uint
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GenerateConsoleCtrlEvent(CtrlTypes dwCtrlEvent, uint dwProcessGroupId);
public void StopProgram(Process proc)
{
//This does not require the console window to be visible.
if (AttachConsole((uint)proc.Id))
{
// Disable Ctrl-C handling for our program
SetConsoleCtrlHandler(null, true);
GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0);
//Moved this command up on suggestion from Timothy Jannace (see comments below)
FreeConsole();
// Must wait here. If we don't and re-enable Ctrl-C
// handling below too fast, we might terminate ourselves.
proc.WaitForExit(2000);
//Re-enable Ctrl-C handling or any subsequently started
//programs will inherit the disabled state.
SetConsoleCtrlHandler(null, false);
}
}
Also, plan for a contingency solution if AttachConsole() or the sent signal should fail, for instance sleeping then this:
if (!proc.HasExited)
{
try
{
proc.Kill();
}
catch (InvalidOperationException e){}
}
The closest that I've come to a solution is the SendSignal 3rd party app. The author lists source code and an executable. I've verified that it works under 64-bit windows (running as a 32-bit program, killing another 32-bit program), but I've not figured out how to embed the code into a windows program (either 32-bit or 64-bit).
How it works:
After much digging around in the debugger I discovered that the entry point that actually does the behavior associated with a signal like ctrl-break is kernel32!CtrlRoutine. The function had the same prototype as ThreadProc, so it can be used with CreateRemoteThread directly, without having to inject code. However, that's not an exported symbol! It's at different addresses (and even has different names) on different versions of Windows. What to do?
Here is the solution I finally came up with. I install a console ctrl handler for my app, then generate a ctrl-break signal for my app. When my handler gets called, I look back at the top of the stack to find out the parameters passed to kernel32!BaseThreadStart. I grab the first param, which is the desired start address of the thread, which is the address of kernel32!CtrlRoutine. Then I return from my handler, indicating that I have handled the signal and my app should not be terminated. Back in the main thread, I wait until the address of kernel32!CtrlRoutine has been retrieved. Once I've got it, I create a remote thread in the target process with the discovered start address. This causes the ctrl handlers in the target process to be evaluated as if ctrl-break had been pressed!
The nice thing is that only the target process is affected, and any process (even a windowed process) can be targeted. One downside is that my little app can't be used in a batch file, since it will kill it when it sends the ctrl-break event in order to discover the address of kernel32!CtrlRoutine.
(Precede it with start if running it in a batch file.)
I guess I'm a bit late on this question but I'll write something anyway for anyone having the same problem.
This is the same answer as I gave to this question.
My problem was that I'd like my application to be a GUI application but the processes executed should be run in the background without any interactive console window attached. I think this solution should also work when the parent process is a console process. You may have to remove the "CREATE_NO_WINDOW" flag though.
I managed to solve this using GenerateConsoleCtrlEvent() with a wrapper app. The tricky part is just that the documentation is not really clear on exactly how it can be used and the pitfalls with it.
My solution is based on what is described here. But that didn't really explain all the details either and with an error, so here is the details on how to get it working.
Create a new helper application "Helper.exe". This application will sit between your application (parent) and the child process you want to be able to close. It will also create the actual child process. You must have this "middle man" process or GenerateConsoleCtrlEvent() will fail.
Use some kind of IPC mechanism to communicate from the parent to the helper process that the helper should close the child process. When the helper get this event it calls "GenerateConsoleCtrlEvent(CTRL_BREAK, 0)" which closes down itself and the child process. I used an event object for this myself which the parent completes when it wants to cancel the child process.
To create your Helper.exe create it with CREATE_NO_WINDOW and CREATE_NEW_PROCESS_GROUP. And when creating the child process create it with no flags (0) meaning it will derive the console from its parent. Failing to do this will cause it to ignore the event.
It is very important that each step is done like this. I've been trying all different kinds of combinations but this combination is the only one that works. You can't send a CTRL_C event. It will return success but will be ignored by the process. CTRL_BREAK is the only one that works. Doesn't really matter since they will both call ExitProcess() in the end.
You also can't call GenerateConsoleCtrlEvent() with a process groupd id of the child process id directly allowing the helper process to continue living. This will fail as well.
I spent a whole day trying to get this working. This solution works for me but if anyone has anything else to add please do. I went all over the net finding lots of people with similar problems but no definite solution to the problem. How GenerateConsoleCtrlEvent() works is also a bit weird so if anyone knows more details on it please share.
Somehow GenerateConsoleCtrlEvent() return error if you call it for another process, but you can attach to another console application and send event to all child processes.
void SendControlC(int pid)
{
AttachConsole(pid); // attach to process console
SetConsoleCtrlHandler(NULL, TRUE); // disable Control+C handling for our app
GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); // generate Control+C event
}
Edit:
For a GUI App, the "normal" way to handle this in Windows development would be to send a WM_CLOSE message to the process's main window.
For a console app, you need to use SetConsoleCtrlHandler to add a CTRL_C_EVENT.
If the application doesn't honor that, you could call TerminateProcess.
Here is the code I use in my C++ app.
Positive points :
Works from console app
Works from Windows service
No delay required
Does not close the current app
Negative points :
The main console is lost and a new one is created (see FreeConsole)
The console switching give strange results...
// Inspired from http://stackoverflow.com/a/15281070/1529139
// and http://stackoverflow.com/q/40059902/1529139
bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent)
{
bool success = false;
DWORD thisConsoleId = GetCurrentProcessId();
// Leave current console if it exists
// (otherwise AttachConsole will return ERROR_ACCESS_DENIED)
bool consoleDetached = (FreeConsole() != FALSE);
if (AttachConsole(dwProcessId) != FALSE)
{
// Add a fake Ctrl-C handler for avoid instant kill is this console
// WARNING: do not revert it or current program will be also killed
SetConsoleCtrlHandler(nullptr, true);
success = (GenerateConsoleCtrlEvent(dwCtrlEvent, 0) != FALSE);
FreeConsole();
}
if (consoleDetached)
{
// Create a new console if previous was deleted by OS
if (AttachConsole(thisConsoleId) == FALSE)
{
int errorCode = GetLastError();
if (errorCode == 31) // 31=ERROR_GEN_FAILURE
{
AllocConsole();
}
}
}
return success;
}
Usage example :
DWORD dwProcessId = ...;
if (signalCtrl(dwProcessId, CTRL_C_EVENT))
{
cout << "Signal sent" << endl;
}
A solution that I have found from here is pretty simple if you have python 3.x available in your command line. First, save a file (ctrl_c.py) with the contents:
import ctypes
import sys
kernel = ctypes.windll.kernel32
pid = int(sys.argv[1])
kernel.FreeConsole()
kernel.AttachConsole(pid)
kernel.SetConsoleCtrlHandler(None, 1)
kernel.GenerateConsoleCtrlEvent(0, 0)
sys.exit(0)
Then call:
python ctrl_c.py 12345
If that doesn't work, I recommend trying out the windows-kill project:
Choco: https://github.com/ElyDotDev/windows-kill
Node: https://github.com/ElyDotDev/node-windows-kill
void SendSIGINT( HANDLE hProcess )
{
DWORD pid = GetProcessId(hProcess);
FreeConsole();
if (AttachConsole(pid))
{
// Disable Ctrl-C handling for our program
SetConsoleCtrlHandler(NULL, true);
GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); // SIGINT
//Re-enable Ctrl-C handling or any subsequently started
//programs will inherit the disabled state.
SetConsoleCtrlHandler(NULL, false);
WaitForSingleObject(hProcess, 10000);
}
}
Thanks to jimhark's answer and other answers here, I found a way to do it in PowerShell:
$ProcessID = 1234
$MemberDefinition = '
[DllImport("kernel32.dll")]public static extern bool FreeConsole();
[DllImport("kernel32.dll")]public static extern bool AttachConsole(uint p);
[DllImport("kernel32.dll")]public static extern bool GenerateConsoleCtrlEvent(uint e, uint p);
public static void SendCtrlC(uint p) {
FreeConsole();
if (AttachConsole(p)) {
GenerateConsoleCtrlEvent(0, p);
FreeConsole();
}
AttachConsole(uint.MaxValue);
}'
Add-Type -Name 'dummyName' -Namespace 'dummyNamespace' -MemberDefinition $MemberDefinition
[dummyNamespace.dummyName]::SendCtrlC($ProcessID)
What made things work was sending the GenerateConsoleCtrlEvent to the desired process group instead of sending it to all processes that share the console of the calling process and then AttachConsole back to the current process' parent's console.
Yes. The https://github.com/ElyDotDev/windows-kill project does exactly what you want:
windows-kill -SIGINT 1234
It should be made crystal clear because at the moment it isn't.
There is a modified and compiled version of SendSignal to send Ctrl-C (by default it only sends Ctrl+Break). Here are some binaries:
(2014-3-7) : I built both 32-bit and 64-bit version with Ctrl-C, it's called SendSignalCtrlC.exe and you can download it at: https://dl.dropboxusercontent.com/u/49065779/sendsignalctrlc/x86/SendSignalCtrlC.exe https://dl.dropboxusercontent.com/u/49065779/sendsignalctrlc/x86_64/SendSignalCtrlC.exe -- Juraj Michalak
I have also mirrored those files just in case:
32-bit version: https://www.dropbox.com/s/r96jxglhkm4sjz2/SendSignalCtrlC.exe?dl=0
64-bit version: https://www.dropbox.com/s/hhe0io7mcgcle1c/SendSignalCtrlC64.exe?dl=0
Disclaimer: I didn't build those files. No modification was made to the compiled
original files. The only platform tested is the 64-bit Windows 7. It is recommended to adapt the source available at http://www.latenighthacking.com/projects/2003/sendSignal/ and compile it yourself.
In Java, using JNA with the Kernel32.dll library, similar to a C++ solution. Runs the CtrlCSender main method as a Process which just gets the console of the process to send the Ctrl+C event to and generates the event. As it runs separately without a console the Ctrl+C event does not need to be disabled and enabled again.
CtrlCSender.java - Based on Nemo1024's and KindDragon's answers.
Given a known process ID, this consoless application will attach the console of targeted process and generate a CTRL+C Event on it.
import com.sun.jna.platform.win32.Kernel32;
public class CtrlCSender {
public static void main(String args[]) {
int processId = Integer.parseInt(args[0]);
Kernel32.INSTANCE.AttachConsole(processId);
Kernel32.INSTANCE.GenerateConsoleCtrlEvent(Kernel32.CTRL_C_EVENT, 0);
}
}
Main Application - Runs CtrlCSender as a separate consoless process
ProcessBuilder pb = new ProcessBuilder();
pb.command("javaw", "-cp", System.getProperty("java.class.path", "."), CtrlCSender.class.getName(), processId);
pb.redirectErrorStream();
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
Process ctrlCProcess = pb.start();
ctrlCProcess.waitFor();
I found all this too complicated and used SendKeys to send a CTRL-C keystroke to the command line window (i.e. cmd.exe window) as a workaround.
A friend of mine suggested a complete different way of solving the problem and it worked for me. Use a vbscript like below. It starts and application, let it run for 7 seconds and close it using ctrl+c.
'VBScript Example
Set WshShell = WScript.CreateObject("WScript.Shell")
WshShell.Run "notepad.exe"
WshShell.AppActivate "notepad"
WScript.Sleep 7000
WshShell.SendKeys "^C"
// Send [CTRL-C] to interrupt a batch file running in a Command Prompt window, even if the Command Prompt window is not visible,
// without bringing the Command Prompt window into focus.
// [CTRL-C] will have an effect on the batch file, but not on the Command Prompt window itself -- in other words,
// [CTRL-C] will not have the same visible effect on a Command Prompt window that isn't running a batch file at the moment
// as bringing a Command Prompt window that isn't running a batch file into focus and pressing [CTRL-C] on the keyboard.
ulong ulProcessId = 0UL;
// hwC = Find Command Prompt window HWND
GetWindowThreadProcessId (hwC, (LPDWORD) &ulProcessId);
AttachConsole ((DWORD) ulProcessId);
SetConsoleCtrlHandler (NULL, TRUE);
GenerateConsoleCtrlEvent (CTRL_C_EVENT, 0UL);
SetConsoleCtrlHandler (NULL, FALSE);
FreeConsole ();
SIGINT can be send to program using windows-kill, by syntax windows-kill -SIGINT PID, where PID can be obtained by Microsoft's pslist.
Regarding catching SIGINTs, if your program is in Python then you can implement SIGINT processing/catching like in this solution.
Based on process id, we can send the signal to process to terminate forcefully or gracefully or any other signal.
List all process :
C:\>tasklist
To kill the process:
C:\>Taskkill /IM firefox.exe /F
or
C:\>Taskkill /PID 26356 /F
Details:
http://tweaks.com/windows/39559/kill-processes-from-command-prompt/

Reliably showing a "please wait" dialog while doing a lengthy blocking operation in the main Qt event loop

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.

How to call plain function from exec()?

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.

QMainWindow stops receiving QEvent::UpdateRequest when user opens menu or resizes window

MyWindow which inherits from QMainWindow. MyWindow contains a QGLWidget that displays an animation.
The problem is that the animation pauses whenever I open a menu or resize the window.
The animation is implemented by calling QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)) periodically, then calling redrawing each time the window receives the QEvent::UpdateRequest, like this:
bool MyWindow::event(QEvent *event)
{
qDebug() << event;
switch (event->type())
{
case QEvent::UpdateRequest:
render();
return true;
default:
return QMainWindow::event(event);
}
}
As seen from qDebug(), while a menu is open or the window is being resized, the window stops receiving update request events.
Is there a setting on QMainWindow/QWidget to make it continue to receive update request events? Or is there some better way to implement the animation?
Edit: I'm on Mac OS X.
This may be a Qt bug. I'll investigate.
Alas, you're way overcomplicating your code.
The postEvent should be simply replaced by this->update(). Behind the scenes it posts the event for you.
One can simply connect a QTimer instance's signal to widget, SLOT(update()). If you want to save on a QObject instance, use QBasicTimer and reimplement timerEvent as follows: void MyWidget::timerEvent(QTimerEvent* ev) { if (ev.timerId() == m_timer.timerId()) update(); }
There's no need to deal with event() reimplementation. Simply reimplement paintEvent() - that's what it's for.
Qt GUI updates are performing on MainThread. So slow gui response is reasonable, if you have many gui functionality does at same time. So generally, do not overload MaiThread with so many heavey function calls.
Probable solution to speed up your GUI response.
If PostEvent is called by your MainThread( if you are using timer from main gui thread ), instead move those to backend functionality in
a worker thread and postEvent once it has been done.
you call QCoreApplication::processEvents(), after your render(); function in MainThread.
This will help system to process all the other events that are in the event-loop before to continue
Please check, following link How to improve GUI response
Note: When creating and triggering the timer it will run within your thread by default, it wont start another thread.
Since I haven't heard any more from Kuba Ober about the possibility of this being a Qt bug, I went ahead and filed a bug report: https://bugreports.qt-project.org/browse/QTBUG-33382
I was able to partially work around the problem by calling the render() function more directly — that is, instead of sending an event, receiving the event, and having the event handler call the function. I accomplished this with a dispatch queue (but not the main dispatch queue, since that's tied to the default run loop so it has the same problem). However, working with the QGLWidget on multiple threads was difficult. After trying for a while to use the moveToThread() function to make this work, and considering other factors involved in the project, I decided to use something other than Qt to display this window.

Resources