Редактирование: Keeping the GUI Responsive
Материал из Wiki.crossplatform.ru
Внимание: Вы не представились системе. Ваш IP-адрес будет записан в историю изменений этой страницы.
Правка может быть отменена. Пожалуйста, просмотрите сравнение версий, чтобы убедиться, что это именно те изменения, которые вас интересуют, и нажмите «Записать страницу», чтобы изменения вступили в силу.
Текущая версия | Ваш текст | ||
Строка 1: | Строка 1: | ||
- | + | __NOTOC__ | |
- | + | ||
by Witold Wysota | by Witold Wysota | ||
<div class="introduction"> | <div class="introduction"> | ||
На сайт QtCentre люди приходят с повторяющейся проблемой, их GUI перестает отвечать во время выполнения длительных операций. Эту проблему не трудно преодолеть, но вы можете сделать это по-разному, поэтому я хотел бы представить ряд возможных вариантов, которые могут быть использованы в зависимости от ситуации. | На сайт QtCentre люди приходят с повторяющейся проблемой, их GUI перестает отвечать во время выполнения длительных операций. Эту проблему не трудно преодолеть, но вы можете сделать это по-разному, поэтому я хотел бы представить ряд возможных вариантов, которые могут быть использованы в зависимости от ситуации. | ||
- | + | *[[#performinglongoperations | Выполнение длительных операций]] | |
+ | *[[#manualeventprocessing | Manual event processing]] | ||
+ | *[[#usingaworkerthread | Using a Worker Thread]] | ||
+ | *[[#waitinginalocaleventloop | Waiting in a Local Event Loop]] | ||
+ | *[[#solvingaproblemstepbystep | Solving a Problem Step by Step]] | ||
+ | *[[#parallelprogramming | Parallel Programming]] | ||
+ | *[[#conclusion | Conclusion]] | ||
</div><div id="performinglongoperations"></div> | </div><div id="performinglongoperations"></div> | ||
Строка 11: | Строка 16: | ||
Первое, что нужно сделать, это определить области проблемы и наметить пути для ее решения. Указанные проблемы могут принимать одну из двух форм. Первая, когда программа должна выполнить задачу, которая описывается как ряд операций, которые будут выполняться последовательно, с тем чтобы получить конечный результат. Примером такой задачи вычисления быстрого преобразования Фурье. | Первое, что нужно сделать, это определить области проблемы и наметить пути для ее решения. Указанные проблемы могут принимать одну из двух форм. Первая, когда программа должна выполнить задачу, которая описывается как ряд операций, которые будут выполняться последовательно, с тем чтобы получить конечный результат. Примером такой задачи вычисления быстрого преобразования Фурье. | ||
- | + | The second variation is when a program has to trigger some activity (for instance a network download) and wait for it to be completed before continuing to the next step of the algorithm. This variation of the problem is, in itself, easy to avoid when using Qt because most of the asynchronous tasks performed by the framework emit a signal when they have finished doing their job, and you can connect it to a slot that will continue the algorithm. | |
+ | |||
+ | During the calculations (regardless of any usage of signals and slots) all event processing gets halted. As a result, the GUI is not refreshed, user input is not processed, network activity stops and timers don't fire—the application looks like it's frozen and, in fact, the part of it not related to the time-intensive task ''is'' frozen. How long are "long operations"? Everything that will distract the end user from interacting with the application is long. One second is long, everything longer than two seconds is definitely ''too long''. | ||
- | + | Our aim in this article is to keep the functionality while preventing the end user from getting irritated by a frozen GUI (and the network and timers). To do that let's take a look at possible classes of solutions and domains of the problem. | |
- | + | We can reach our final goal of performing calculations in one of two ways—either by doing the computation in the main thread (the ''single-threaded approach'') or in separate threads (the ''multithreaded approach''). The latter is widely known and used in the Java world, but it is sometimes abused where one thread would do the job just fine. Contrary to popular opinion, threads can often slow down your application instead of speeding it up, so unless you are sure your program can benefit from being multithreaded (either in terms of speed or simplicity), try to avoid spawning new threads 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 parts like steps, iterations or sub-problems (usually it shouldn't be monolithic). If the task can be split into chunks, each of them can either depend on others or not. If they are independent, we can process them at any time and in arbitrary order. Otherwise, we have to synchronize our work. In the worst case, we can only do one chunk at once and we can't start the next one until the previous one is finished. Taking all these factors into consideration, we can choose from different solutions. | |
- | + | ||
+ | <div id="manualeventprocessing"></div> | ||
===Manual event processing=== | ===Manual event processing=== | ||
The most basic solution is to explicitly ask Qt to process pending events at some point in the computation. To do this, you have to call [[Qt:Документация 4.3.2//qcoreapplication#processEvents | QCoreApplication::processEvents()]] periodically. '''[[#footnote | †]]''' The following example shows how to do this: | The most basic solution is to explicitly ask Qt to process pending events at some point in the computation. To do this, you have to call [[Qt:Документация 4.3.2//qcoreapplication#processEvents | QCoreApplication::processEvents()]] periodically. '''[[#footnote | †]]''' The following example shows how to do this: | ||
Строка 65: | Строка 72: | ||
tT.setSingleShot(true); | tT.setSingleShot(true); | ||
- | connect(&tT, SIGNAL(timeout()), &q, SLOT(quit())); | + | connect(&tT, SIGNAL(timeout()), &q, SLOT(quit())); |
- | connect(&manager, SIGNAL(finished(QNetworkReply*)), | + | connect(&manager, SIGNAL(finished(QNetworkReply*)), |
- | &q, SLOT(quit())); | + | &q, SLOT(quit())); |
QNetworkReply *reply = manager.get(QNetworkRequest( | QNetworkReply *reply = manager.get(QNetworkRequest( | ||
QUrl("http://www.qtcentre.org"))); | QUrl("http://www.qtcentre.org"))); | ||
Строка 85: | Строка 92: | ||
We should note two more things here. Firstly, that a similar approach is implemented in a <tt>QxtSignalWaiter</tt> class that is part of the libqxt project ([http://www.libqxt.org http://www.libqxt.org]). Another thing is that, for some operations, Qt provides a family of "wait for" methods (for example [[Qt:Документация 4.3.2//qiodevice#waitForBytesWritten | QIODevice::waitForBytesWritten()]]) that will do more or less the same as the snippet above but without running an event loop. However, the "wait for" solutions will freeze the GUI because they do not run their own event loops. | We should note two more things here. Firstly, that a similar approach is implemented in a <tt>QxtSignalWaiter</tt> class that is part of the libqxt project ([http://www.libqxt.org http://www.libqxt.org]). Another thing is that, for some operations, Qt provides a family of "wait for" methods (for example [[Qt:Документация 4.3.2//qiodevice#waitForBytesWritten | QIODevice::waitForBytesWritten()]]) that will do more or less the same as the snippet above but without running an event loop. However, the "wait for" solutions will freeze the GUI because they do not run their own event loops. | ||
- | + | <div id="solvingaproblemstepbystep"></div> | |
===Solving a Problem Step by Step=== | ===Solving a Problem Step by Step=== | ||
Строка 223: | Строка 230: | ||
Never again let your GUI get frozen! | Never again let your GUI get frozen! | ||
- | |||
- |