Search/Filtering functionality blocks the Main(GUI) thread - c++

I have a custom table and I have implemented a search/filtering function which goes through all the elements in the table and then hides/shows the item in the table depending upon whether that item/element matches the one we are searching for.
For example, let's say I have a text control and I type "wxwidgets" in it then my custom function will go through all the elements in the table and hide the elements that do not match this "wxwidgets" entry. This works fine and I am correctly able to hide/show the elements. But the problem is that this search blocks the main thread(gui) since I am doing this in the main thread. The table has around 1000 entries or can be more in the future. My question is how can I avoid this blocking of the main thread. I am thinking of using another worker thread that will do this searching of elements. But then I read that "no secondary threads should call gui functions". But then how can i show/hide the elements of the table from the worker thread. For example, currently in the main thread I use Show(true) or Show(false) to show/hide a particular entry from the table and all this is done while I am in a for/while loop. But if i implement this(the for loop) in a worker thread then according to the quoted advice i should not use the Show() functions from inside that worker thread. What can be done in this situation? Also, is there any other way/advice of doing this searching for elements of the table. I am thinking of starting a new detachable thread every time the user enters some text in the text field. And then delete that old thread if the user appends some more text to the text field and start a new thread which starts searching from the beginning. Is this the right solution for this?
The problem is that inside my for loop i am using the wxWidgets functions. For example, this is what my for loop looks like:
void AnotherClass::onTextChanged(wxCommandEvent &event)
{
for(int i = 5; i<154;++i)
{
SomeClass *element = dynamic_cast<SomeClass*>(FindWindowById(i));
if(element.GetLabel() == textEnteredInTextCtrl)
{
element.Show(true);//element found
//update the necessary layout here using layout call
}
else
{
element.Show(false);//element not found
//update the necessary layout here using Layout() call
}
}
}
This is the main part of the search. Now inside the workerthread should/can i use the functions like FindWindowById() and GetLabel()? Are they considered GUI functions or not so that I can use them from the worker thread? Can i or can i not use FindWindowByID() and GetLabel() and other similar functions(like Layout() and Show()) from inside the worker thread. How should I make this work? I mean I know how to use wxThread and send events using QueueEvent and already have another worker thread in my program that does some other calculation but I am asking about how I should make it work in this particular case.
Another solution suggested by QuentinC would be to use a timer. His suggestion is as follows:
In my case, I don't start refreshing the list immediately after the
user has typed a letter in the search box. Instead, when the user has
typed a letter (wxEVT_TEXT), I start or restart a timer of 500ms. Only
when the timer goes out (the user stops typing for 500ms) then the list
is refreshed. Again, this is a measure to avoid a rapid succession of
useless refreshes.
But in this case of using a timer i have several queries. I am sending the wxEVT_TEXT from CustomTextCtrl's onTextChanged method to this class' onTextChanged method. I guess i could start the timer of 500ms inside the onTextChanged method of the CustomTextCtrl class when the user types a letter. But then where should i check that the timer is still running? In the CustomTextCtrl's onTextChanged method or inside the AnotherClass's onTextChanged method?
So for clarification i have two classes:
the CustomTextCtrl class which have a onTextChanged method which uses event.Skip() to forward this event to its parent.
The parent class AnotherClass which also have a onTextChanged method and this method receives this forwarded event and do the searching and updates the table.
Where and how should i start/restart/stop the timer to update the UI?
NOTE: The process of filtering the elements is working perfectly but the only problem is that the main(GUI) thread is blocked when the user type some text inside the textctrl. After lets say 6 or 7 seconds, the text appears inside the textctrl and the UI is updated. I don't want this unresponsiveness of the main UI for 6-7 seconds.
Also, note that i am not using any wxList/wxGrid. I am just using wxPanels and wxStaticText and using show/hide on them.
Edit: One improvement in the code above is that to only use Layout() call from outside the for loop. If i use the Layout() calls from outside the for loop then the search functionality works almost instantly. But this(method) still has the potential of blocking the main thread in the future if the table has many more elements. So i want to use a thread or a timer method. But i don't know how the secondary thread could use the gui functions or how can/should i use a wxTimer method(if any) to solve this problem.

I have several thoughts on this subjet
A) If you have a speed issue, then you better profile your code, to see where the bottle neck is.
B) Calling GDI functions from a not-main thread is risky. Maybe just asking for window-id and its label is not that dangerous, but I think calling Show() definitely is.
C) This piece of code is mainly GUI related. I don't think a worker thread is useful here. But stacking similar callings may improve its speed. For this, I have three advises:
Use CallAfter() passing the elemnt.Show() method
Use Freeze() before the loop and Thaw after it.
Call Layout()only once, right after the loop. About this I wonder if Show()/Hide() controls is better than Enable()/Disabñe()
D) Because you call FindWindowById() so many times, and also many user changes, it will be better to cache all affected windows in a container (a std::map with id as the key). Then, inside the loop use the container instead of 'FindWindowById()`.
E) As a last resource, if the GUI is still blocked, use wxYield() every xxx (say 100) loop-iterations. Depending of pending messages this solution may get things worse (reentrances, crossed effects, etc).

Related

QTreeWidget fill from thread Item expanded

I fill my QTreeWidget out of a thread. Works well so far, but after he data is populated, all items are not expanded but they are available. All items I added I set ...setExpanded(true);
This cause at the end the problem, that treeCount() is 1.
I want to avoid to set in constructer expandAll and I also want to avoid to make an extra List to count the items.
If I start the fill method from common app thread as a simple function, everything I ...setExpanded(true); is expanded after populate.
I there a trick to avoid this problem?
Never mangle with a QObject from a different thread. All GUI object operations need to be done in the same thread.
Use a signal/slot mechanism between your threads to communicate new items. Alternatively, set up a new Q*ItemModel in the extra thread and pass it once in a signal.

How to avoid freezing the user interface in a loop (minimal example) [duplicate]

My first naive at updating my progress bar was to include the following lines in my loop which is doing the processing, making something like this:
while(data.hasMoreItems())
{
doSomeProcessing(data.nextItem())
//Added these lines but they don't do anything
ui->progressBar->setValue(numberProcessed++);
ui->progressBar->repaint();
}
I thought adding the repaint() would make the execution pause while it updated the GUI, but apparently it's not that simple. After looking at the questions:
QProgressBar Error
Progress bar is not showing progress
it looks like I'm going to have to put the data processing in a different thread and then connect a signal from the data processing thread to the GUI thread to update the progressbar. I'm rather inexperienced with GUIs and threads and I was wondering if anyone could just point me in the right direction, ie what Qt classes should I be looking at using for this. I'd guess I need a QThread object but I've been looking through the QProgressBar documentation but it doesn't bring up the topic of threading.
As #rjh and #Georg have pointed out, there are essentially two different options:
Force processing of events using QApplication::processEvents(), OR
Create a thread that emits signals that can be used to update the progress bar
If you're doing any non-trivial processing, I'd recommend moving the processing to a thread.
The most important thing to know about threads is that except for the main GUI thread (which you don't start nor create), you can never update the GUI directly from within a thread.
The last parameter of QObject::connect() is a Qt::ConnectionType enum that by default takes into consideration whether threads are involved.
Thus, you should be able to create a simple subclass of QThread that does the processing:
class DataProcessingThread : public QThread
{
public:
void run();
signals:
void percentageComplete(int);
};
void MyThread::run()
{
while(data.hasMoreItems())
{
doSomeProcessing(data.nextItem())
emit percentageCompleted(computePercentageCompleted());
}
}
And then somewhere in your GUI code:
DataProcessingThread dataProcessor(/*data*/);
connect(dataProcessor, SIGNAL(percentageCompleted(int)), progressBar, SLOT(setValue(int));
dataProcessor.start();
You need to call QApplication::processEvents() periodically inside your processing loop to let it handle UI events.
As Georg says, Qt is a single-threaded co-operative multitasking environment. You get full control of your process until you relinquish it voluntarily with processEvents() - until you do that, Qt can't update the UI elements, handle async HTTP requests, handle input, or pretty much anything else. It's up to you to make sure that stuff gets a timeslice while you're in a long processing loop.
You can create a sub-class of QThread that emits a signal progressChanged, which you connect to the QProgressBar.
connect() makes the connections auto connections per default. That means that the signal-slot-mechanism already takes care of the threading issues for you, so you don't need to worry about that.

How to show paginated text from a QTextDocument in QML?

I currently have a C++ class inheriting from QQuickPaintedItem. I use it to paint layouted, paginated richtext from a QTextDocument via QTextDocument::drawContents (or by directly calling its QTextDocumenLayout's draw method).
However, as stated in QQuickPaintedItems documentation, there are threading issues to be aware of:
Warning: Extreme caution must be used when creating QObjects, emitting signals, starting timers and similar inside this function as these will have affinity to the rendering thread.
Specifically, in this case, QTextDocumentLayoutPrivate has timers which get started/stopped when QTextDocumenLayout::draw is called. Unfortunately, the QTextDocument and thus the timers lives in the qml main thread, while paint is called in the render thread, leading to messages like
QBasicTimer::start: Timers cannot be started from another thread
While this doesn't affect the functionality of my application (so far), this is probably not a good thing™.
Therefore, my question is whether there is a better way to show the paginated text in QML (not necessarily involving QQuickPaintedItem).
For now I'm still using the QQuickPaintedItem and when paint is called I do the following:
First check whether the QTextDocument has its affinity set to its current thread. If yes, I'll proceed as normal.
Otherwise QMetaObject::invokeMethod is used to call a method which moves the document to the rendering thread, and calls update to trigger a repaint, which now works as the thread affinity is correct. At the end of paint, the QTextDocument's thread affinity is set back to the original thread.
This works as far as I can tell (as in, no more warnings), but feels conceptually rather wrong.

MFC application freezing while updating CListCtrl

I have a CListCtrl in my MFC application. The list needs to be updated when I get some notification from the server. Updating list works quite good when there are less notification as operations on the list are less. But in case of heavy load, list control and in turn the application gets freeze.
I am aware of updating UI items in the separate thread in case of bulk updates, but in this case I have the notifications that can come in any order and in any volume, I need to handle in such way that my main thread is not getting blocked.
If anyone faced the issue before please suggest the approach for this case.
You could put all the updates into a queue. Then do a limited number of updates from the queue to the control in the OnIdle function. OnIdle is called when your GUI message queue is empty. It could do up to, say, 20 updates and then return. The main thread would than process any GUI input and when finished with that it would call OnIdle again. In this way you delay and spread out the updates while keeping the GUI alive.
I had a similar situation and resolved it using a Timer. Only when the Timer ticked the ListCtrl could got updated.
Side note: You should do
SetRedraw(FALSE);
before a bulk update
and
SetRedraw(TRUE);
after.
You should not have the control drawing itself at a one-by-one item level, when doing a bulk operation.
Use Thread.I had the same problem and I solved it by just having the loop of adding elements into the clistctrl in thread function. That is,is the POSTMESSAGE() function in the thread should be called as much time as we want to add elements .
MFC Application getting stuck when adding list control elements
Also refer the above link to get some idea

When to use QThread::exec()

I've checked a satisfying explanation but could not find. Usually docs mention that in order to use signals/slots between threads, we need to use event loops and start them by calling exec.
However I can see that w/o using exec(), I can still send signals and handle them across threads.
What's the exact use of it?
Use QThread::exec() when you want to run the event loop Qt provides for you in the QThread class. If you don't call exec(), you need to create your own event loop that processes Qt events (that is, if you want signals / slots to work). This is almost certainly more work than it's worth, unless you have very specific needs.
You say you can still send signals / slots? My guess is that you're not actually running anything on a different thread. This is a very common issue when using QThread. Put a breakpoint inside the code you think is running on a different thread and have a look at the stack trace - you may be in for a shock!
A rough example.
Suppose you have a text box. On each letter user types on the text box you want to perform some background task. You can setup a QThread for that. Emit something whenever the contents of text box changes. Assign a slot from your QThread that handles the background task. Emit something from QThread when the task finished. Handle this signal from main thread. Connect them. Start the thread when the text box is created (or any appropriate time). If you call exec() from your QThread::run() then you don't need to start() the thread multiple times.
If you don't use this mechanism, you may need to create (and/or start()) a QThread each time the content of text box changes, perform the background task and get result. This time you can still use signal/slot between main thread and this thread, but you need to start() the thread multiple times.