home | login | register | DMCA | contacts | help |      
mobile | donate | ВЕСЕЛКА

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
А Б В Г Д Е Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Э Ю Я


my bookshelf | genres | recommend | rating of books | rating of authors | reviews | new | форум | collections | читалки | авторам | add
fantasy
space fantasy
fantasy is horrors
heroic
prose
  military
  child
  russian
detective
  action
  child
  ironical
  historical
  political
western
adventure
adventure (child)
child's stories
love
religion
antique
Scientific literature
biography
business
home pets
animals
art
history
computers
linguistics
mathematics
religion
home_garden
sport
technique
publicism
philosophy
chemistry
close

реклама - advertisement



Взаимодействие с главным потоком

При запуске приложения Qt работает только один поток — главный. Только этот поток может создать объект QApplication или QCoreApplication и вызвать для него функцию exec(). После вызова exec() этот поток либо ожидает возникновения какого-нибудь события, либо обрабатывает какое-нибудь событие.

Главный поток может запускать новые потоки, создавая объекты подкласса QThread, как мы это делали в предыдущем разделе. Если эти новые потоки должны взаимодействовать друг с другом, они могут совместно использовать переменные под управлением мьютексов, блокировок чтения/записи, семафоров или специальных событий. Но ни один из этих методов нельзя использовать для связи с главным потоком, поскольку они будут блокировать цикл обработки событий и «заморозят» интерфейс пользователя.

Для связи вторичного потока с главным потоком необходимо использовать межпоточные соединения сигнал—слот. Обычно механизм сигналов и слотов работает синхронно, т.е. связанный с сигналом слот вызывается сразу после генерации сигнала, используя прямой вызов функции.

Однако когда вы связываете объекты, «живущие» в других потоках, механизм взаимодействия сигналов и слотов становится асинхронным. (Такое поведение можно изменить с помощью пятого параметра функции QObject::connect().) Внутри эти связи реализуются путем регистрации события. Слот затем вызывается в цикле обработки событий потока, в котором находится объект получателя. По умолчанию объект QObject существует в потоке, в котором он был создан; в любой момент можно изменить расположение объекта с помощью вызова функции QObject::moveToThread().

QT 4: программирование GUI на С++

Рис. 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 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().


Синхронизация потоков | QT 4: программирование GUI на С++ | Применение классов Qt во вторичных потоках