Slot In Main Qt
EnArBgDeElEsFaFiFrHiHuItJaKnKoMsNlPlPtRuSqThTrUkZh
Qt Tutorials For Beginners – Adding Click Event to QPushbutton Example September 15, 2016 admin Qt 1 In this post we will see how to add the click event to the QPushbutton with an example. Qt documentation: Multi window signal slot connection. A simple multiwindow example using signals and slots. There is a MainWindow class that controls the Main Window view. Qt - Dealing with Databases; Qt Container Classes; Qt Network; Qt Resource System; QTimer; Basic Usage; QTimer::singleShot simple usage; Simple example; Singleshot Timer with Lambda function as slot; Using QTimer to run code on main thread; Signals and Slots; SQL on Qt; Threading and Concurrency; Using Style Sheets Effectively.
This page was used to describe the new signal and slot syntax during its development. The feature is now released with Qt 5.
- Differences between String-Based and Functor-Based Connections (Official documentation)
- Introduction (Woboq blog)
- Implementation Details (Woboq blog)
Note: This is in addition to the old string-based syntax which remains valid.
- 1Connecting in Qt 5
- 2Disconnecting in Qt 5
- 4Error reporting
- 5Open questions
Connecting in Qt 5
There are several ways to connect a signal in Qt 5.
Old syntax
Qt 5 continues to support the old string-based syntax for connecting signals and slots defined in a QObject or any class that inherits from QObject (including QWidget)
New: connecting to QObject member
Here's Qt 5's new way to connect two QObjects and pass non-string objects:
Pros
- Compile time check of the existence of the signals and slot, of the types, or if the Q_OBJECT is missing.
- Argument can be by typedefs or with different namespace specifier, and it works.
- Possibility to automatically cast the types if there is implicit conversion (e.g. from QString to QVariant)
- It is possible to connect to any member function of QObject, not only slots.
Cons
- More complicated syntax? (you need to specify the type of your object)
- Very complicated syntax in cases of overloads? (see below)
- Default arguments in slot is not supported anymore.
New: connecting to simple function
The new syntax can even connect to functions, not just QObjects:
Pros
- Can be used with std::bind:
- Can be used with C++11 lambda expressions:
Cons
- There is no automatic disconnection when the 'receiver' is destroyed because it's a functor with no QObject. However, since 5.2 there is an overload which adds a 'context object'. When that object is destroyed, the connection is broken (the context is also used for the thread affinity: the lambda will be called in the thread of the event loop of the object used as context).
Disconnecting in Qt 5
As you might expect, there are some changes in how connections can be terminated in Qt 5, too.
Old way
You can disconnect in the old way (using SIGNAL, SLOT) but only if
- You connected using the old way, or
- If you want to disconnect all the slots from a given signal using wild card character
Symetric to the function pointer one
Only works if you connected with the symmetric call, with function pointers (Or you can also use 0 for wild card)In particular, does not work with static function, functors or lambda functions.
New way using QMetaObject::Connection
Works in all cases, including lambda functions or functors.
Asynchronous made easier
With C++11 it is possible to keep the code inline
Here's a QDialog without re-entering the eventloop, and keeping the code where it belongs:
Another example using QHttpServer : http://pastebin.com/pfbTMqUm
Error reporting
Tested with GCC.
Fortunately, IDEs like Qt Creator simplifies the function naming
Missing Q_OBJECT in class definition
Type mismatch
Open questions
Default arguments in slot
If you have code like this:
The old method allows you to connect that slot to a signal that does not have arguments.But I cannot know with template code if a function has default arguments or not.So this feature is disabled.
There was an implementation that falls back to the old method if there are more arguments in the slot than in the signal.This however is quite inconsistent, since the old method does not perform type-checking or type conversion. It was removed from the patch that has been merged.
Overload
As you might see in the example above, connecting to QAbstractSocket::error is not really beautiful since error has an overload, and taking the address of an overloaded function requires explicit casting, e.g. a connection that previously was made as follows:
connect(mySpinBox, SIGNAL(valueChanged(int)), mySlider, SLOT(setValue(int));
cannot be simply converted to:
...because QSpinBox has two signals named valueChanged() with different arguments. Instead, the new code needs to be:
Unfortunately, using an explicit cast here allows several types of errors to slip past the compiler. Adding a temporary variable assignment preserves these compile-time checks:
Qt Slot In Main
Some macro could help (with C++11 or typeof extensions). A template based solution was introduced in Qt 5.7: qOverload
The best thing is probably to recommend not to overload signals or slots …
… but we have been adding overloads in past minor releases of Qt because taking the address of a function was not a use case we support. But now this would be impossible without breaking the source compatibility.
Disconnect
Should QMetaObject::Connection have a disconnect() function?
The other problem is that there is no automatic disconnection for some object in the closure if we use the syntax that takes a closure.One could add a list of objects in the disconnection, or a new function like QMetaObject::Connection::require
Callbacks
Function such as QHostInfo::lookupHost or QTimer::singleShot or QFileDialog::open take a QObject receiver and char* slot.This does not work for the new method.If one wants to do callback C++ way, one should use std::functionBut we cannot use STL types in our ABI, so a QFunction should be done to copy std::function.In any case, this is irrelevant for QObject connections.
Qt Development Frameworks Documentation Qt Quarterly | |
« Writing ODF Files with Qt The Panel Stack Pattern » |
Keeping the GUI Responsive
At QtCentre people come to us with the recurring problemof their GUIs freezing during long operations. The issue is notdifficult to overcome but you can do it in many differentways, so I'd like to present a range of possible options thatcan be used depending on the situation you are dealing with.
Performing Long Operations
The first thing to do is to state the domain of the problem andoutline paths that can be taken to solve it. The aforementionedproblem can take one of two forms. The first variationis when a program has to execute a calculation-intensive taskthat is described as a series of operations to be performed insequence in order to obtain the final result. An example ofsuch a task is calculating a Fast Fourier Transform.
The second variation is when a program has to trigger someactivity (for instance a network download) and wait for itto be completed before continuing to the next step of thealgorithm. This variation of the problem is, in itself, easy toavoid when using Qt because most of the asynchronous tasksperformed by the framework emit a signal when they have finisheddoing their job, and you can connect it to a slot that willcontinue the algorithm.
During the calculations (regardless of any usage of signals andslots) all event processing gets halted. As a result, theGUI is not refreshed, user input is not processed, networkactivity stops and timers don't fire—the application looks likeit's frozen and, in fact, the part of it not related to thetime-intensive task is frozen. How long are 'long operations'?Everything that will distract the end user from interactingwith the application is long. One second is long, everythinglonger than two seconds is definitely too long.
Our aim in this article is to keep the functionality whilepreventing the end user from getting irritated by a frozen GUI(and the network and timers). To do that let's take a look atpossible classes of solutions and domains of the problem.
We can reach our final goal of performing calculations in one oftwo ways—either by doing the computation in the main thread(the single-threaded approach) or in separate threads(the multithreaded approach). The latter is widelyknown and used in the Java world, but it is sometimes abusedwhere one thread would do the job just fine. Contrary topopular opinion, threads can often slow down your applicationinstead of speeding it up, so unless you are sureyour program can benefit from being multithreaded (eitherin terms of speed or simplicity), try to avoid spawning newthreads simply because you can.
The domain of the problem can be treated as one of two cases.Either we can or cannot divide the problem into smaller partslike steps, iterations or sub-problems (usually it shouldn't bemonolithic). If the task can be split into chunks, each of themcan either depend on others or not. If they are independent, wecan process them at any time and in arbitrary order. Otherwise,we have to synchronize our work. In the worst case, we can onlydo one chunk at once and we can't start the next one until theprevious one is finished. Taking all these factors intoconsideration, we can choose from different solutions.
Manual event processing
The most basic solution is to explicitly ask Qt to processpending events at some point in the computation. To dothis, you have to call QCoreApplication::processEvents()periodically. †The following example shows how to do this:
This approach has significant drawbacks. For example, imagine youwanted to perform two such loops in parallel—calling one of themwould effectively halt the other until the first one is finished(so you can't distribute computing power among different tasks).It also makes the application react with delays to events.Furthermore the code is difficult to read and analyze, thereforethis solution is only suited for short and simple problems that areto be processed in a single thread, such as splash screens and themonitoring of short operations.
†Actually you should callQCoreApplication::sendPostedEvents(). See the documentationfor processEvents().Using a Worker Thread
A different solution is to avoid blocking the main event loopby performing long operations in a separate thread. Thisis especially useful if the task is performed by a third partylibrary in a blocking fashion. In such a situation it might notbe possible to interrupt it to let the GUI process pending events.
One way to perform an operation in a separate thread thatgives you most control over the process is to useQThread. You can either subclass it and reimplementits run() method, or callQThread::exec() to start the thread's eventloop, or both: subclass and, somewhere in the run() method, callexec(). You can then use signals and slots to communicate with themain thread—just remember that you have to be sure thatQueuedConnection will be used or else threads may lose stability andcause your application to crash.
There are many examples of using threads in both the Qt referencedocumentation and online materials, so we won't make our ownimplementation, but instead concentrate on other interestingaspects.
Waiting in a Local Event Loop
The next solution I would like to describe deals with waitinguntil an asynchronous task is completed. Here, I will show you howto block the flow until a network operation is completed withoutblocking event processing. What we could do is essentially somethinglike this:
This is called busy waiting or spinning—constantlychecking a condition until it is met. In most cases this isa bad idea, it tends to eat all your CPU power and has all thedisadvantages of manual event processing.
Fortunately, Qt hasa class to help us with the task: QEventLoop isthe same class that the application and modal dialogs use inside theirexec() calls. Each instance of this class is connected tothe main event dispatching mechanism and, when its exec()method is invoked, it starts processing events until you tellit to stop using quit().
We can use the mechanism to turn asynchronousoperations into synchronous ones using signals and slots—wecan start a local event loop and tell it to exit when it sees aparticular signal from a particular object:
We use one of the new additions to Qt—a network access manager—tofetch a remote URL. As it works in an asynchronous fashion, we createa local event loop to wait for a finished() signal from the downloader.Furthermore, we instantiate a timer that will terminate the event loopafter five seconds in case something goes wrong. After connectingappropriate signals, submitting the request and starting the timer weenter the newly created event loop. The call to exec() will returneither when the download is complete or five seconds have elapsed(whichever comes first). We find out which is the case by checking ifthe timer is still running. Then we can process the results or tell theuser that the download has failed.
We should note two more things here. Firstly, that a similarapproach is implemented in a QxtSignalWaiter class that ispart of the libqxt project (http://www.libqxt.org).Another thing is that, for some operations, Qt provides a family of'wait for' methods (for exampleQIODevice::waitForBytesWritten())that will do more or less the same as the snippet above but withoutrunning an event loop. However, the 'wait for' solutions will freezethe GUI because they do not run their own event loops.
Solving a Problem Step by Step
If you can divide your problem into subproblems then there is a nice path youcan take to perform the computation without blocking the GUI. You can performthe task in short steps that will not obstruct event processing for longerperiods of time.Start processing and, when you notice you have spent some defined timeon the task, save its state and return to the event loop. There needs to be away to ask Qt to continue your task after it is done with events.
Fortunately there is such a way, or even two. One of them is to use a timerwith an interval set to zero. This special value will cause Qt to emit thetimeout signal on behalf of the timer once its event loop becomes idle. If youconnect to this signal with a slot, you will get a mechanism of callingfunctions when the application is not busy doing other things (similar to howscreen savers work). Here is an example of finding prime numbers in thebackground:
The FindPrimes class makes use of two features—it holds its currentcalculation state (cand and curr variables) so that it cancontinue calculations where it left off, and it monitors (by the use ofQTime::elapsed()) how long it has beenperforming the current step of the task. If the time exceeds a predefinedamount, it returns to the event loop but, before doing so, it starts asingle-shot timer that will call the method again (you might call this approach'deferred recurrency').
I mentioned there were two possibilities of doing a task in steps. The secondone is to use QMetaObject::invokeMethod()instead of timers. This method allows you to call any slot from any object. Onething that needs to be said is that, for this to work in our case, we need tomake sure the call is made using the Qt::QueuedConnection connectiontype, so that the slot is called in an asynchronous way (by default, slot callswithin a single thread are synchronous). Therefore we might substitute thetimer call with the following:
The advantage of this over using timers is that you can pass arguments to theslot (for example, passing it the current state of a calculation). Apart fromthat the two methods are equivalent.
Parallel Programming
Finally, there is the situation where you have to perform a similaroperation on a set of data—for instance, creating thumbnails ofpictures from a directory. A trivial implementation would look like this:
A disadvantage of such an approach is that creating a thumbnail of asingle image might take quite long, and for that time the GUI wouldstill be frozen. A better approach would be to perform the operationin a separate thread:
This solution is perfectly fine, but it doesn't take into considerationthat computer systems are evolving in a different direction than fiveor ten years ago—instead of having faster and faster processing unitsthey are being equipped with multiple slower units (multicore ormultiprocessor systems) that together offer more computing cycles withlower power consumption and heat emission. Unfortunately, the abovealgorithm uses only one thread and is thus executed ona single processing unit, which results in slower execution onmulticore systems than on single core ones (because a singlecore in multicore systems is slower than in single core ones).
To overcome this weakness, we must enter the world of parallelprogramming—we divide the work to be done into as many threads as we haveprocessing units available. Starting with Qt 4.4, there are extensionsavailable to let us do parallel programming: these are provided byQThreadPool and Qt Concurrent.
The first possible course of action is to use so calledrunnables—simple classes whose instances can beexecuted by a thread. Qt implements runnables through itsQRunnable class. You can implement your ownrunnable based on the interface offered byQRunnable and execute it usinganother entity offered by Qt. I mean a thread pool—an objectthat can spawn a number of threads that execute arbitrary jobs. Ifthe number of jobs exceeds the number of available threads, jobswill be queued and executed when a thread becomes available.
Let's go back to our example and implement a runnable that wouldcreate an image thumbnail using a thread pool.
Basically, everything that needs to be done is to implement therun() method from QRunnable. It is donethe same way as subclassing QThread, the onlydifference is that the job is not tied to a thread it creates and thus can beinvoked by any existing thread. After creating an instance ofThumbRunnable we make sure it won't be deleted by thethread pool after job execution is completed. We need to to thatbecause we want to fetch the result from the object. Finally, weask the thread pool to queue the job using the global thread poolavailable for each application and add the runnable to a list forfuture reference.
We then have to periodically check each runnable to see if itsresult is available, which is boring and troublesome, but fortunatelythere is a better approach when you need to fetch a result. QtConcurrent introduces a number of paradigms that can beinvoked to perform SIMD (Single Instruction Multiple Data)operations. Here we will take a look only at one of them, thesimplest one that lets us process each element of a containerand have the results ready in another container.
Easy, isn't it? Just a few lines of code and the watcher will inform usof the state of the SIMD program held in the QFutureobject. It will even let us cancel, pause and resume the program. Here, we usedthe simplest possible variant of the call—using a standalone function.In a real situation you would use something more sophisticated thana simple function—Qt Concurrent lets you use functions, class methodsand function objects. Third party solutions allow you to further extendthe possibilities by using bound function arguments.
Slot In Main Qt Bar
Conclusion
I have shown you a whole spread of solutions based on typeand complexity of problem related to performing time consumingoperations in Qt-based programs. Remember that these are onlythe basics—you can build upon them, for example by creating yourown 'modal' objects using local event loops, swift data processorsusing parallel programming, or generic job runners using thread pools.For simple cases, there are ways to manually request the applicationto process pending events, and for more complex ones dividing the taskinto smaller subtasks could be the right direction.
You can download complete examples that use techniques described in thisarticle from the Qt Quarterly Website. If you would like to discuss your solutions, work together withothers to solve a problem that's bothering you, or simply spend some time withother Qt (cute?) developers, you can always reach me and others willing toshare their knowledge athttp://www.qtcentre.org.
Never again let your GUI get frozen!