LINUX.ORG.RU

Рисование больших растровых картинок в Qt и быстрая перерисовка окна?

 , ,


0

2

Я делаю приложение для научной визуализации на PyQt5. PyQt (и Qt) я толком не знаю, осваиваю на ходу.

Окошко приложения выглядит так - слева элементы управления, остальную часть окна занимает собственно картинка, вокруг нее всякое зарамочное оформление (чиселки, метки к осям и пр). Картинка занимает большую часть окна. Картинка генерится как растр в недрах C++, это относительно дорогая операция. Поверх всего этого надо динамически (вместе с движением мыши) отображать некоторую информацию - выводить координаты точки куда указывает курсор, какие то линии рисовать привязанные к курсору, что то подсвечивать и пр.

Таким образом есть три варианта перерисовки:

  1. самая простая - меняем только то что связано с мышью при ее движении (скажем выводится крест на всю картинку что бы было видно куда по осям графика попадает курсор).

  2. самая сложная - при нажатии на элемент управления перерисовываем все, включая картинку.

  3. промежуточная по сложности, при изменении размера окна - пока размер меняется зарамочное оформление перерисовываем, картинку масштабируем. Когда изменение размера окна закончено, вызываем перерисовку 2.

Пока что в голову пришло вот такое - картинка возвращается из C++ как буфер. Зарамочное оформление (и картинка) рисуются через QPainter на QPixmap, это перерисовка 2. В перерисовке 1 выводится каждый раз этот QPixmap и поверх него набрасывается то что связано с мышью.

Можно ли это как то сделать более Ъ? Мне скажем не нравится то, что приходится много раз делать копии самой большой части изображения. Я бы из C++ рисовал скажем сразу в память QPixmap, да фиг к ней похоже достучишься…

Еще я пока не понял как ловить окончание изменений размера окна (когда юзер мышку отпустил). mouseReleaseEvent в этом случае не вызывается похоже.

★★

Ответ на: комментарий от I-Love-Microsoft

Да, там похоже вместо QPixmap будет QImage. QPixmap из буфера я пока не умею создавать.

Я не знаю будет копирование или нет при создании QImage из буфера (скорее всего да), но точно будет копирование когда картинка будет отрисовываться на виджете в котором зарамочное оформление и пр.

AntonI ★★ ()
Ответ на: комментарий от AntonI

Несколько мыслей для размышления:

  1. QImage можно создавать не владеющий буфером, соответственно можно избежать копирования, по крайней мере пока не нужно будет менять формат (к примеру, из YUV в RGBA).
  2. QPixmap можно просто передать в QLabel для отображения и не заморачиваться использованием QPainter.
  3. Динамические данные навроде координат точки под курсором мыши можно не рисовать прямо на картинке, а поместить сверху другой виджет, перемещая его вслед за курсором.
unC0Rr ★★★★★ ()
Ответ на: комментарий от unC0Rr

Спасибо.

По п1 - глянул доки, вроде оно дефолтом прямо так и создается оказывается (если я правильно понял). Это хорошие новости.

По п2 - меня пока что использование label для отображения картинки слегка вымораживает. Это быстрее чем QPainter? Сейчас они у меня два раза дергаются разные, первый раз что бы че то и правда нарисовать, второй что бы просто показать изображение.

По п3 - там размер виджета будет на всю область. Но может его можно сделать прозрачным? Что то такое было…

Есть еще QGraphicsView/Scene, но когда я увидел что там надо вытворять что бы задать выравнивание текста, я решил не связываться;-(

AntonI ★★ ()
Ответ на: комментарий от AntonI

По п2 - меня пока что использование label для отображения картинки слегка вымораживает. Это быстрее чем QPainter?

Да, потому что при перемещении виджета скорее всего будет сделана перерисовка только части твоего мегаизображения, а именно под предыдущим положением виджета и под новым.

А вообще QGraphicsScene тебя ждет - это IMHO наиболее родной способ показывать всякое такое.

Pavval ★★★★★ ()
Ответ на: комментарий от Pavval

Пока что так получается:

  1. время перерисовки по схеме изложенной в исходном посте - зарамочное оформление, вставка картинок и пр. занимают у PyQt примерно столько же сколько сколько генерация картинки в C++ (если для C++ используется самый простой вариант, в более сложных вариантах плюсы конечно начинают сильнее лагать). Для окна в fullHD это где то по 0.05 сек на то и то, правда у меня ноутбук с Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz. Я ожидал что будет быстрее, но пока что с этим можно жить.

  2. В С++ делается изображение которое отдается в питон как std::string, по три байта на точку. Из этого изображения делается QImage в формате RGB888, которое потом рисуется через QPainter.drawImage. При некоторых размерах изображения его начинает колбасить - то изображение становится в серую горизонтальную линию, то его просто как то хитро рвет и сдвигает. А при некоторых размерах оно сегфолтится в методе QPainter.drawImage, причем сегфолтится в

qt_convert_rgb888_to_rgb32_ssse3(unsigned int*, unsigned char const*, int)

если gdb не врет.

Поменял у себя изображение на 4х байтное и QImage.Format на RGB32 - падать перестало, отрисовка ускорилась в разы, но цвета как то странно уплыли. Может конечно и мой косяк, но что настораживает - само изображение вменяемое хоть и с уехавшими цветами, а на иконках в ComboBox где варианты палитр отображения сплошная каша, в основном черная. С трехбайтным изображением иконки были нормальные, судя пол всему опять Qt-ные чудеса…

@uberroot, @EXL - может что то подскажете?;-)

AntonI ★★ ()
Ответ на: комментарий от AntonI

Память где-то портится, очевидно. Я бы порекомендовал генерировать изображение в стандарте XPM, а то неизвестно ещё, какие преобразования пройдут над строкой по пути в питон и далее в Qt.

unC0Rr ★★★★★ ()
Ответ на: комментарий от AntonI

Используй нормальные api для работы с видеокартой, если требуется конвертировать - делай это на видеокарте шейдером.

Zyy ()
Последнее исправление: Zyy (всего исправлений: 1)
Ответ на: комментарий от AntonI

зачем QLabel?
Использовать графическую сцену на самом деле проще
Тем более у тебя куча дополнительных элементов по слоям, pixmap у тебя будет в background, при рисовании pixmap конвертируешь это дорогая операция

anonymous2 ★★★★★ ()
Ответ на: комментарий от AntonI

Про преобразования цветов я тебе ничего не скажу, я с этим не сталкивался.

А вот про способы перерисовки - я думаю, что ты когда-нибудь зайдёшь в тупик. Дело в том, что сцена сама прекрасно апдейтит изображение и умеет не перерисовывать лишнее, просто отслеживая у элементов boundingRect. Главное правильно написать обработку событий и расположить слои. Тем более на сцену ты можешь добавлять элементы стандартного UI через QGraphicsProxyWidget, если тебе привычней с этим работать.

А ты, как я понял, пытаешься рисовать на одном слое.

Про события и изменения резмеров окна можешь глянуть здесь.

uberroot ()
Ответ на: комментарий от uberroot

Я не волшебник, я только учусь;-)

Но у меня нет проблем с перерисовкой чего-то когда по окну кто то проехался. У меня есть проблемы просто нарисовать это с нуля, причем мои методы отрисовки (если они вызываются) занимают основное время.

Точнее сейчас уже все как то рисуется, пока что ошибки в моей части. Даже чиселки и метки по осям нормально стоят

AntonI ★★ ()

Картинка генерится как растр в недрах C++, это относительно дорогая операция.

Мне скажем не нравится то, что приходится много раз делать копии самой большой части изображения. Я бы из C++ рисовал скажем сразу в память QPixmap, да фиг к ней похоже достучишься…

Т.е. на фоне генерации копированием можно пренебречь. Если речь пока не о том, что получается 23 fps, а нужно 24.

gag ★★★★★ ()
Ограничение на отправку комментариев: только для зарегистрированных пользователей