Russian Qt Forum

Qt => 2D и 3D графика => Тема начата: marbius от Апрель 25, 2010, 10:27



Название: [РЕШЕНО] Отрисовка части изображения в QPaintEvent при изменении размера окна
Отправлено: marbius от Апрель 25, 2010, 10:27
Доброго времени суток.

Совершенно случайно наткнулся на такую неприятную (при большом количестве выводимых объектов) "особенность":
при изменении размеров окна, на котором происходит отрисовка через QPaintEvent, paintRect QPaintEvent::rect () ВСЕГДА возвращает координаты верхнего левого угла для перерисовки как (0;0) - т.е. контент перерысовывается полностью.
Возможно ли как-то изменить ситуацию так, чтобы, например, при изменении размера окна по вертикали по rect() возвращалась только недостающая нижняя часть окна, действительно необходимая для "дорисовки"?

Спасибо.


Название: Re: Отрисовка части изображения в QPaintEvent при изменении размера окна
Отправлено: alexman от Апрель 25, 2010, 10:34
Используй void QWidget::resizeEvent( QResizeEvent * event ). А в QResizeEvent можно использовать для анализа методы const QSize & oldSize () const, const QSize & size () const.


Название: Re: Отрисовка части изображения в QPaintEvent при изменении размера окна
Отправлено: alexman от Апрель 25, 2010, 10:39
А const QRect & QPaintEvent::rect () const всегда возвращает всю область?


Название: Re: Отрисовка части изображения в QPaintEvent при изменении размера окна
Отправлено: marbius от Апрель 25, 2010, 11:05
Используй void QWidget::resizeEvent( QResizeEvent * event ). А в QResizeEvent можно использовать для анализа методы const QSize & oldSize () const, const QSize & size () const.

Как тогда можно убрать из очереди отрисовку всей области, попадающую в QPaintEvent после изменения размеров окна?
Получается, что область перерисовывается трижды: дважды я инициирую дорисовку "справа" и "снизу", а затем идет обычное сообщение опять-таки на обновление всей области. (введение флагов не помогло)

А const QRect & QPaintEvent::rect () const всегда возвращает всю область?

Не совсем понял вопроса. Я писал, что при изменении размеров окна paintRect rect() возвращает всю область, от (0;0) по (width;height).


Название: Re: Отрисовка части изображения в QPaintEvent при изменении размера окна
Отправлено: alexman от Апрель 25, 2010, 11:50
Используй void QWidget::resizeEvent( QResizeEvent * event ). А в QResizeEvent можно использовать для анализа методы const QSize & oldSize () const, const QSize & size () const.

Как тогда можно убрать из очереди отрисовку всей области, попадающую в QPaintEvent после изменения размеров окна?
Получается, что область перерисовывается трижды: дважды я инициирую дорисовку "сверху" и "снизу", а затем идет обычное сообщение опять-таки на обновление всей области. (введение флагов не помогло)
void QWidget::paintEvent ( QPaintEvent * event ) тоже переопредели!

А const QRect & QPaintEvent::rect () const всегда возвращает всю область?

Не совсем понял вопроса. Я писал, что при изменении размеров окна paintRect возвращает всю область, от (0;0) по (width;height).
Написано же про rect! Метод paintRect даже не нашел!


Название: Re: Отрисовка части изображения в QPaintEvent при изменении размера окна
Отправлено: marbius от Апрель 25, 2010, 12:04
Цитировать
void QWidget::paintEvent ( QPaintEvent * event ) тоже переопредели!

Цитировать
Написано же про rect! Метод paintRect даже не нашел!

Извини, но непродуктивный "разговор": кто-то кого-то не понимает, скорее всего, я.

если я "рисую" самостоятельно, то, скорее всего, метод у меня и так переопределен. вопрос в том, что перерисовка при изменении размеров попадает в очередь, и соответственно в обработчик события, помимо моей воли, так сказать из недр qt. Вопрос сейчас в том, как это можно предотвратить. Я подозреваю, что это происходит из-за перерисовки фона, но могу и ошибаться.

А paintRect - это не метод, это просто моя опечатка. Увы, но разговор шел именно про const QRect & QPaintEvent::rect () const и именно этот метод возвращает нули...


Название: Re: Отрисовка части изображения в QPaintEvent при изменении размера окна
Отправлено: alexman от Апрель 25, 2010, 12:10
Еще можно использовать флаг Qt::WA_OpaquePaintEvent.


Название: Re: Отрисовка части изображения в QPaintEvent при изменении размера окна
Отправлено: marbius от Апрель 25, 2010, 12:24
Еще можно использовать флаг Qt::WA_OpaquePaintEvent.

Увы, все те же (0;0) только теперь уже без фона :(

Скорее всего придется  смириться с медленной отрисовкой.... Но, все же как-то же это должно решаться...

alexman, спасибо за потраченное время.

Вопрос открыт.




Название: Re: Отрисовка части изображения в QPaintEvent при изменении размера окна
Отправлено: alexman от Апрель 25, 2010, 13:33
1. Хранить текущий rect и предыдущий rect (оба в глобальных координатах).
2. В void QWidget::resizeEvent ( QResizeEvent * event ), void QWidget::moveEvent ( QMoveEvent * event ) rect-ы пересчитываются.
3. В void QWidget::paintEvent ( QPaintEvent * event ) рисовка (по обоим rect-ам можно проанализировать, что перерисовывать).
4. QPoint QWidget::mapToGlobal ( const QPoint & pos ) const - получинение глобальных координат.


Название: Re: Отрисовка части изображения в QPaintEvent при изменении размера окна
Отправлено: marbius от Апрель 25, 2010, 13:51
1. Хранить текущий rect и предыдущий rect (оба в глобальных координатах).
2. В void QWidget::resizeEvent ( QResizeEvent * event ), void QWidget::moveEvent ( QMoveEvent * event ) rect-ы пересчитываются.
3. В void QWidget::paintEvent ( QPaintEvent * event ) рисовка (по обоим rect-ам можно проанализировать, что перерисовывать).
4. QPoint QWidget::mapToGlobal ( const QPoint & pos ) const - получинение глобальных координат.

Спасибо за ответ, но соль в том, что:
а) если "не извращаться", то потом при выходе из QWidget::resizeEvent, в очереди все мои попытки отрисовать свои прямоугольники "съедаются" глобальной перерисовкой с координатами (0;0; width*height);
б) если "поизвращаться" и ввести разного рода флаги-блокировки, то получается, что контент будет отрисовываться трижды, как я уже писАл, причем самое печальное, что опять-таки последней будет глобальная перерисовка.

Возможный вариант решения, как всегда прост и уже неоднократно описан: использовать QPixmap, и при QWidget::paintEvent выводить только его, а уже при QWidget::resizeEvent расчитывать свои прямоугольники и выводить на QPixmap. Все же думал, что есть более "прямое" решение.


Название: Re: Отрисовка части изображения в QPaintEvent при изменении размера окна
Отправлено: alexman от Апрель 25, 2010, 13:56
Возможный вариант решения, как всегда прост и уже неоднократно описан: использовать QPixmap, и при QWidget::paintEvent выводить только его, а уже при QWidget::resizeEvent расчитывать свои прямоугольники и выводить на QPixmap. Все же думал, что есть более "прямое" решение.
Да, это быстро работает! Проверенно много раз!


Название: Re: Отрисовка части изображения в QPaintEvent при изменении размера окна
Отправлено: marbius от Апрель 25, 2010, 14:07
Да, это быстро работает! Проверенно много раз!

Offtopic
Но это лишние затраты памяти. И к тому же насколько я понял, в Qt и так используется буферизация вывода, а так получается, что еще один дополнительный буфер. Вот это меня смущает при использовании QPixmap. Поправьте меня, если я ошибаюсь.


Название: Re: Отрисовка части изображения в QPaintEvent при изменении размера окна
Отправлено: Igors от Апрель 25, 2010, 14:44
1) Какую область (region, rectangle) нужно перерисовывать - занимается ОС. Не видно оснований почему при resize нужно "инвалидировать" только часть - в большинстве случаев окно должно быть полностью обновлено если его размер изменился

2) Кто буферирует - дело темное (платформо-зависимое). Напр. на Mac это сам OC

3) QPixmap - верная дорога. Опасения по поводу расходов на буфер беспочвенны. Прикинем: окно 1024 х 1024, расход памяти 4 Мб. Это по нынешним временам не объем. Для сравнения если Вы масштабируете картинку в несколько раз - очень быстро расходы превышают 100 Мб, но я еще не видел попыток сэкономить память на этом  :)


Название: Re: Отрисовка части изображения в QPaintEvent при изменении размера окна
Отправлено: marbius от Апрель 25, 2010, 15:03
... Не видно оснований почему при resize нужно "инвалидировать" только часть - в большинстве случаев окно должно быть полностью обновлено если его размер изменился...

Ну, я могу возразить (опять-таки частный случай): я тяну окошко за нижнюю границу, у меня невалидная только та часть, которая появляется снизу.

Или вот более худший вариант: есть QWidget - главное окно, в котором находится еще один QWidget - мой виджет, допустим размером 100х100 пикселей, причем для главного окна я установил setMinimumSize (200, 200); я меняю размеры главного окна (тяну мышью, например). Зачем мне перерисовывать дочерний виджет каждый раз при изменении размеров главного окна? (возможно, этот вариант больше offtopic и суть другой темы, но все же имеет место быть).

Чтож, QPixmap - все же более верное и быстрое решение, чем копаться в недрах qt.
Принимаю последнее как решение проблемы.



Название: Re: Отрисовка части изображения в QPaintEvent при изменении размера окна
Отправлено: GreatSnake от Апрель 26, 2010, 09:54
Qt::WA_StaticContents


Название: Re: Отрисовка части изображения в QPaintEvent при изменении размера окна
Отправлено: marbius от Апрель 26, 2010, 10:59
Qt::WA_StaticContents

Цитировать
Indicates that the widget contents are north-west aligned and static. On resize, such a widget will receive paint events only for parts of itself that are newly visible. This flag is set or cleared by the widget's author.

К сожалению, теория расходится с практикой: перерисовывается область (0;0; width*height)


Название: Re: Отрисовка части изображения в QPaintEvent при изменении размера окна
Отправлено: GreatSnake от Апрель 26, 2010, 11:44
Цитировать
К сожалению, теория расходится с практикой: перерисовывается область (0;0; width*height)
Похоже, что именно так и есть.
Есть большое подозрение, что тролли оставили поддержку этого флага только для top-level окон.
Кстати, вы не указали paintEvent() какого виджета отрабатывает?


Название: Re: Отрисовка части изображения в QPaintEvent при изменении размера окна
Отправлено: marbius от Апрель 26, 2010, 12:15
Как всегда, все гениальное просто :)

Установка следующих аттрибутов решает проболему:

setAttribute(Qt::WA_StaticContents,true);
setAttribute(Qt::WA_NoBackground,true);

Всем спасибо.
Вопрос закрыт.


Название: Re: Отрисовка части изображения в QPaintEvent при изменении размера окна
Отправлено: marbius от Апрель 26, 2010, 12:17
Кстати, вы не указали paintEvent() какого виджета отрабатывает?

наследника от QWidget


Название: Re: Отрисовка части изображения в QPaintEvent при изменении размера окна
Отправлено: alexman от Апрель 26, 2010, 12:20
Qt::WA_NoBackground - This value is obsolete. Use WA_OpaquePaintEvent instead.


Название: Re: Отрисовка части изображения в QPaintEvent при изменении размера окна
Отправлено: marbius от Апрель 26, 2010, 12:23
Qt::WA_NoBackground - This value is obsolete. Use WA_OpaquePaintEvent instead.

Согласен. Только ОБЯЗАТЕЛЬНО совместно с Qt::WA_StaticContents, т.е. чтобы оба указанных аттрибута были установлены:

РЕШЕНИЕ

Код:
setAttribute(Qt::WA_StaticContents,true);
setAttribute(Qt::WA_OpaquePaintEvent,true);


Название: Re: Отрисовка части изображения в QPaintEvent при изменении размера окна
Отправлено: alexman от Апрель 26, 2010, 12:39
Qt::WA_NoBackground - This value is obsolete. Use WA_OpaquePaintEvent instead.

Согласен. Только ОБЯЗАТЕЛЬНО совместно с Qt::WA_StaticContents, т.е. чтобы оба указанных аттрибута были установлены:
Код:
setAttribute(Qt::WA_StaticContents,true);
setAttribute(Qt::WA_OpaquePaintEvent,true);
Да, надо взять на заметку.


Название: Re: Отрисовка части изображения в QPaintEvent при изменении размера окна
Отправлено: marbius от Апрель 26, 2010, 12:45
Мои извинения, что не указал изначально, но это решение для Windows-платформы, на других еще не проверял. Возможно, там появятся свои нюансы (грабли).


Название: Re: Отрисовка части изображения в QPaintEvent при изменении размера окна
Отправлено: GreatSnake от Апрель 26, 2010, 12:49
Под X11 работает.