Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > С/С++: Кроссплатформенное программирование, Qt/Gtk+/wxWidgets > Рисование из потока в Gui. Пример Mandelbrot.


Автор: SirFreeFrag 14.3.2011, 00:16
День добрый.

Подскажите как правильно передать подготовленное изображение в Gui поток для отрисовки. Делаю, как в примере Mandelbrot. Там сделано таким образом:

Код

//Класс потока
class RenderThread : public QThread 
{
    Q_OBJECT
    ...
signals:
    void renderedImage(const QImage &image, double scaleFactor);

protected:
    void run();
    ...
};

void RenderThread::run()
{
    QImage image(resultSize, QImage::Format_RGB32);
    //Дальше что-то рисуется на QImage
    ...
    emit renderedImage(image, scaleFactor);
};


//И в потоке Gui  слот виджета  updatePixmap принимает
//сигнал renderedImage из дочернего потока и отрисовывает изображение

class MandelbrotWidget : public QWidget
{
    Q_OBJECT
    ...
private slots:
    void updatePixmap(const QImage &image, double scaleFactor);
    ...
};

void MandelbrotWidget::updatePixmap(const QImage &image, double scaleFactor)
{
    ...
    pixmap = QPixmap::fromImage(image);
    ...
    update();
 };



Вроде работает, но смущает следующая вещь. В Gui поток изображение передается с помощью сигнала renderedImage через объект image. Объект image объявлен и создается в методе класса потока run(), то бишь по сути являетстя локальным. Т.к сигнал из метода посылается другому (основному) потоку, то тип сигнала будет с постановкой в очередь. Мне казалось что слот updatePixmap (который обрабатывает сигнал renderedImage) может и выполнится поздней, чем закончится выполнение run(). Соответственно и объект image (т.к. это локальный объект метода run) будет уничтожен раньше, чем его обработает слот updatePixmap. Но как ни странно, вроде работает.

Собственно вопрос, почему этого не происходит, или где я ошибаюсь? И как правильно и потоко-безопасно передавать постоянно обновляющееся изображение в главный поток для отрисовки. Если можно, поделитесь примерами.

PS: Сори, забыл добавить в заголовок темы, что речь идет о Qt (4.7.1)

Автор: borisbn 14.3.2011, 00:32
SirFreeFrag, обрати внимание, как они связывают сигнал потока со своим слотом
Код

    connect(&thread, SIGNAL(renderedImage(QImage,double)), this, SLOT(updatePixmap(QImage,double)));

видишь, знака ссылки ( & ) уже нет. поэтоу, наверное, и работает. Если же убрать и в сигнале, и в слоте ссылку, а передавать объект QImage по значению, то вообще никаких проблем не будет, т.к. при emit'е сигнала создастся копия этого имаджа. Судя по всему из-за этого коннекта так и получается, но я бы не советовал так делать...

и ещё... обрати внимание, что run начинается с
Код

    forever {
...

а выход из него будет только при разрушении объекта thread в главном окне.

в общем, правильно делаешь, что обращаешь внимание на такие вещи.

Автор: SirFreeFrag 14.3.2011, 00:50
borisbn, если я правильно понял, ты имеешь виду, что image передается в данном случае не по ссылке, а по значению?
Если так, то не хотелось бы, что бы лишний раз производилось копирование image при передаче его в updatePixmap.

Т.к в Qt Examples это единственный пример в разделе Threading, был бы благодарен за линки на альтернативные реализации. Может есть какая-нитбудь общепринятая реализация решения данной задачи?

Добавлено через 12 минут и 10 секунд
Цитата(borisbn @ 14.3.2011,  00:32)
и ещё... обрати внимание, что run начинается с
Код

    forever {
...

а выход из него будет только при разрушении объекта thread в главном окне.

Ммм...вот этот момент я пропустил, просто в моей реализации не требовался бесконечный цикл в run().

Автор: borisbn 14.3.2011, 01:03
Цитата(SirFreeFrag @  14.3.2011,  00:50 Найти цитируемый пост)
если я правильно понял, ты имеешь виду, что image передается в данном случае не по ссылке, а по значению?

не уверен. возможно, всё-таки, всё работает из-за того, что они уверены, что run не закончится раньше, чем полностью отработает слот. Если передавать не по ссылке, а по значению, то об этом и дкмать не нужно.

Цитата(SirFreeFrag @  14.3.2011,  00:50 Найти цитируемый пост)
то не хотелось бы, что бы лишний раз производилось копирование image при передаче его в updatePixmap


из документации по QImage
Цитата

QImage objects can be passed around by value since the QImage class uses implicit data sharing. QImage objects can also be streamed and compared.

почитай про http://doc.qt.nokia.com/4.7-snapshot/implicit-sharing.html#implicit-data-sharing

Есть ещё вариант... Не очень кошерный, но работает. Можно передавать объект по ссылке, но коннектить сигнал со слотом с явным указанием типа коннекта: http://doc.qt.nokia.com/4.7-snapshot/qt.html#ConnectionType-enum
В этом случае слот вызовется в своём (GUI-шном) потоке, но управление потоку, вызвавшему emit, будет возвращено только после того, как все слоты, связанные с сигналом, завершат свою работу.

Автор: SirFreeFrag 14.3.2011, 01:25
Цитата

почитай про Implicit Data Sharing

Познавательно. Спасибо.

Автор: SirFreeFrag 14.3.2011, 15:44
Блин, вроде гуглил до создания темы, а сейчас нашел точно такой же тред  smile 
http://www.linux.org.ru/forum/development/5192523

Насколько я понял из него, в данном случае при передаче image слоту все таки создается копия объекта, но за счет как раз выше упомянутого "Implict Data Sharing", накладные расходы при этом малы и копия будет уничтожена после того, как будет уничтожена последняя ссылка на нее (в данном примере, после того, как отработает слот updatePixmap). Если это так, то вопрос:

Код

signals:
void renderedImage(const QImage &image);

и
Код

signals:
void renderedImage(QImage image);

Эти варианты будут работать одинаково? В обоих случаях будет происходить "копирование" объекта?

Автор: borisbn 14.3.2011, 15:53
Цитата(SirFreeFrag @  14.3.2011,  15:44 Найти цитируемый пост)
Эти варианты будут работать одинаково? В обоих случаях будет происходить "копирование" объекта?

проверь smile
создай вместо QImage свой объект с выводом в конструкторе и деструкторе в qDebug() соответствующей информации. Только не забудь зарегистрировать его через qRegisterMetaType

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)