Взаимодействие с главным потоком
Взаимодействие с главным потоком
При запуске приложения Qt работает только один поток — главный. Только этот поток может создать объект QApplication или QCoreApplication и вызвать для него функцию exec(). После вызова exec() этот поток либо ожидает возникновения какого-нибудь события, либо обрабатывает какое-нибудь событие.
Главный поток может запускать новые потоки, создавая объекты подкласса QThread, как мы это делали в предыдущем разделе. Если эти новые потоки должны взаимодействовать друг с другом, они могут совместно использовать переменные под управлением мьютексов, блокировок чтения/записи, семафоров или специальных событий. Но ни один из этих методов нельзя использовать для связи с главным потоком, поскольку они будут блокировать цикл обработки событий и «заморозят» интерфейс пользователя.
Для связи вторичного потока с главным потоком необходимо использовать межпоточные соединения сигнал—слот. Обычно механизм сигналов и слотов работает синхронно, т.е. связанный с сигналом слот вызывается сразу после генерации сигнала, используя прямой вызов функции.
Однако когда вы связываете объекты, «живущие» в других потоках, механизм взаимодействия сигналов и слотов становится асинхронным. (Такое поведение можно изменить с помощью пятого параметра функции QObject::connect().) Внутри эти связи реализуются путем регистрации события. Слот затем вызывается в цикле обработки событий потока, в котором находится объект получателя. По умолчанию объект QObject существует в потоке, в котором он был создан; в любой момент можно изменить расположение объекта с помощью вызова функции QObject::moveToThread().
Рис. 18.3. Приложение Image Pro.
Для иллюстрации работы соединений сигнал—слот с разными потоками мы рассмотрим программный код приложения Image Pro — процессора изображений, обеспечивающего базовые возможности и позволяющего пользователю поворачивать, изменять размер и цвет изображения. В данном приложении используется один вторичный поток для выполнения операций над изображениями без блокировки цикла обработки событий. Это имеет существенное значение при обработке изображений очень большого размера. Вторичный поток имеет список выполняемых задач или «транзакций», и он генерирует события для главного окна, чтобы сообщать о том, как идет процесс их выполнения.
01 ImageWindow::ImageWindow()
02 {
03 imageLabel = new QLabel;
04 imageLabel->setBackgroundRole(QPalette::Dark);
05 imageLabel->setAutoFillBackground(true);
06 imageLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop);
07 setCentralWidget(imageLabel);
08 createActions();
09 createMenus();
10 statusBar()->showMessage(tr("Ready"), 2000);
11 connect(&thread, SIGNAL(transactionStarted(const QString &)),
12 statusBar(), SLOT(showMessage(const QString &)));
13 connect(&thread, SIGNAL(finished()),
14 this, SLOT(allTransactionsDone()));
15 setCurrentFile("");
16 }
Интересной частью конструктора ImageWindow являются два соединения сигнал—слот. В обоих случаях сигнал генерируется объектом TransactionThread, который мы вскоре рассмотрим.
01 void ImageWindow::flipHorizontally()
02 {
03 addTransaction(new FlipTransaction(Qt::Horizontal));
04 }
Слот flipHorizontally() создает транзакцию зеркального отражения и регистрирует ее при помощи закрытой функции addTransaction(). Функции flipVertically(), resizeImage(), convertTo32Bit(), convertTo8Bit() и convertTo1Bit() реализуются аналогично.
01 void ImageWindow::addTransaction(Transaction *transact)
02 {
03 thread.addTransaction(transact);
04 openAction->setEnabled(false);
05 saveAction->setEnabled(false);
06 saveAsAction->setEnabled(false);
07 }
Функция addTransaction() добавляет транзакцию в очередь транзакций вторичного потока и отключает команды Open, Save и Save As на время обработки транзакций.
01 void ImageWindow::allTransactionsDone()
02 {
03 openAction->setEnabled(true);
04 saveAction->setEnabled(true);
05 saveAsAction->setEnabled(true);
06 imageLabel->setPixmap(QPixmap::fromImage(thread.image()));
07 setWindowModified(true);
08 statusBar()->showMessage(tr("Ready"), 2000);
09 }
Слот allTransactionsDone() вызывается, когда очередь транзакций TransactionThread становится пустой.
Теперь давайте рассмотрим класс TransactionThread:
01 class TransactionThread : public QThread
02 {
03 Q_OBJECT
04 public:
05 void addTransaction(Transaction *transact);
06 void setImage(const QImage &image);
07 QImage image();
08 signals:
09 void transactionStarted(const QString &message);
10 protected:
11 void run();
12 private:
13 QMutex mutex;
14 QImage currentImage;
15 QQueue<Transaction *> transactions;
16 };
Класс TransactionThread содержит список обрабатываемых транзакций, которые выполняются по очереди в фоновом режиме.
01 void TransactionThread::addTransaction(Transaction *transact)
02 {
03 QMutexLocker locker(&mutex);
04 transactions.enqueue(transact);
05 if (!isRunning())
06 start();
07 }
Функция addTransaction() добавляет транзакцию в очередь транзакций и запускает поток транзакции, если он еще не выполняется. Доступ к переменной—члену transactions защищается мьютексом, потому что главный поток мог бы ее модифицировать функцией addTransaction() во время прохода по транзакциям transactions вторичного потока.
01 void TransactionThread::setImage(const QImage &image)
02 {
03 QMutexLocker locker(&mutex);
04 currentImage = image;
05 }
06 QImage TransactionThread::image()
07 {
08 QMutexLocker locker(&mutex);
09 return currentImage;
10 }
Функции setImage() и image() позволяют главному потоку установить изображение, для которого будут выполняться транзакции, и получить обработанное изображение после завершения всех транзакций. И вновь мы защищаем доступ к переменной—члену при помощи мьютекса.
01 void TransactionThread::run()
02 {
03 Transaction *transact;
04 forever {
05 mutex.lock();
06 if (transactions.isEmpty()) {
07 mutex.unlock();
08 break;
09 }
10 QImage oldImage = currentImage;
11 transact = transactions.dequeue();
12 mutex.unlock();
13 emit transactionStarted(transact->message());
14 QImage newImage = transact->apply(oldImage);
15 delete transact;
16 mutex.lock();
17 currentImage = newImage;
18 mutex.unlock();
19 }
20 }
Функция run() просматривает очередь транзакций и по очереди выполняет все транзакции путем вызова для них функции apply().
После старта транзакции мы генерируем сигнал transactionStarted() с сообщением, выводимым в строке состояния приложения. Когда обработка всех транзакций завершается, функция run() возвращает управление и QThread генерирует сигнал finished().
01 class Transaction
02 {
03 public:
04 virtual ~Transaction() { }
05 virtual QImage apply(const QImage &image) = 0;
06 virtual QString message() = 0;
07 };
Класс Transaction является абстрактным базовым классом, предназначенным для определения операций, которые пользователь может выполнять с изображением. Виртуальный деструктор необходим, потому что нам приходится удалять экземпляры подклассов Transaction через указатель transaction. (Кроме того, если мы его не предусмотрим, некоторые компиляторы выдадут предупреждение.) Transaction имеет три конкретных подкласса: FlipTransaction, ResizeTransaction и ConvertDepthTransaction. Нами будет рассмотрен только подкласс FlipTransaction; другие два подкласса имеют аналогичное определение.
01 class FlipTransaction : public Transaction
02 {
03 public:
04 FlipTransaction(Qt::Orientation orientation);
05 QImage apply(const QImage &image);
06 QString message();
07 private:
08 Qt::Orientation orientation;
09 };
Конструктор FlipTransaction принимает один параметр, который задает ориентацию зеркального отражения (по горизонтали или по вертикали).
01 QImage FlipTransaction::apply(const QImage &image)
02 {
03 return image.mirrored(
04 orientation == Qt::Horizontal, orientation == Qt::Vertical);
05 }
Функция apply() вызывает QImage::mirrored() для объекта QImage, полученного в виде параметра, и возвращает сформированный объект QImage.
01 QString FlipTransaction::message()
02 {
03 if (orientation == Qt::Horizontal) {
04 return QObject::tr("Flipping image horizontally...");
05 } else {
06 return QObject::tr("Flipping image vertically...");
07 }
08 }
Функция messageStr() возвращает сообщение, отображаемое в строке состояния в ходе выполнения операции. Данная функция вызывается из функции transactionThread::run(), кoгдa гeнepиpyeтcя cигнaл transactionStarted().
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
Взаимодействие
Взаимодействие Уведомите свою группу и начальство о неприятностях. Изложите свой план по выходу из кризиса. Обратитесь к ним за информацией и советом. Избегайте сюрпризов. Ничто не сердит людей и не делает их менее рациональными так, как сюрпризы. Сюрпризы повышают
Управление потоком
Управление потоком Управление потоком осуществляется прикладным процессом с помощью команд системного вызова ioctl(2):#include <sys/types.h>#include <stropts.h>#include <sys/conf.h>int ioctl(int fildes, int command, ... /* arg */);Хотя часть команд обрабатывается исключительно головным модулем потока, другие
8.13. Отсутствие управления потоком в UDP
8.13. Отсутствие управления потоком в UDP Теперь мы проверим, как влияет на работу приложения отсутствие какого-либо управления потоком в UDP. Сначала мы изменим нашу функцию dg_cli так, чтобы она отправляла фиксированное число дейтаграмм. Она больше не будет читать из
Взаимодействие с DNS-сервером
Взаимодействие с DNS-сервером Если вы хотите, чтобы к клиенту DHCP мог непосредственно обратиться любой узел сети, добиться этого можно двумя способами. Вы можете сконфигурировать сервер DHCP так, чтобы он предоставлял клиенту фиксированный IP-адрес (необходимые действия
10.7 Управление потоком
10.7 Управление потоком Получатель TCP загружается поступающим потоком данных и определяет, какой объем информации он сможет принять. Это ограничение воздействует на отправителя TCP. Представленное ниже объяснение данного механизма является концептуальным, и разработчики
3.3. Взаимодействие процессов
3.3. Взаимодействие процессов Из всех средств межпроцессного взаимодействия, которыми так богаты UNIX-подобные ОС, в этой главе мы рассмотрим только конвейеры и
Взаимодействие транзакций
Взаимодействие транзакций Интересен процесс определения, является ли текущая версия мусором или, возможно, она еще нужна какой-то транзакции.Для описания этого процесса придется ввести несколько важных понятий. Прежде всего, надо отметить, что все определения строятся
Глава 8. Управление потоком.
Глава 8. Управление потоком. В этой главе ...~ Использование управляющих структур для управления происходящим~ Проверка условий с помощью условных выражений~ Принятие решений с помощью операторов If. . .Then и SelectCase~ Повторение выполнения действий с помощью операторов For...Next, For
Управление потоком с помощью Go То
Управление потоком с помощью Go То Если ваша программа вдруг стала неуправляемой, укажите ей, куда идти, - с помощью передачи управления другому участку программного кода в процедуре. Оператор Go To в совокупности со специальным оператором метки в месте назначения позволяет
18.2.3. Взаимодействие с РОР-сервером
18.2.3. Взаимодействие с РОР-сервером Многие серверы электронной почты пользуются почтовым протоколом (Post Office Protocol — POP). Имеющийся в Ruby класс POP3 позволяет просматривать заголовки и тела всех сообщений, хранящихся для вас на сервере, и обрабатывать их как вы сочтете нужным.
5.2.10. Конструкции управления потоком данных
5.2.10. Конструкции управления потоком данных Конструкции IF, CASE, LOOP, WHILE, REPLACE ITERATE и LEAVE полностью выполнены.Многие из этих конструкций содержат другие инструкции, как обозначено спецификациями грамматики в следующих разделах. Такие конструкции могут быть вложены. Например,
Взаимодействие с не объектным ПО
Взаимодействие с не объектным ПО До сих пор, элементы ПО выражались полностью в ОО-нотации. Но программы появились задолго до распространения ОО-технологии. Часто возникает необходимость соединить объектное ПО с элементами, написанными, например, на языках С, Fortran или
Взаимодействие с мышью
Взаимодействие с мышью Для взаимодействия с мышью служит объект Mouse. Единственный экземпляр этого объекта под именем Mouse создается самим Flash.Прежде всего, этот объект предоставляет методы hide и show. Первый метод скрывает курсор мыши, а второй — вновь выводит его на экран. Ни