LINUX.ORG.RU

PyQt5 - накидать в область окна картинок, линий, надписей и схоронить это все в .png?

 , , ,


0

1

В дцатый раз в первый класс;-(

Осваиваю тут новый (для себя) PyQt5. Нужно в области форточки навставлять картинок (генеряться на С++ в памяти), линий и текста, и в итоге сохранить все это в файл, лучше в png.

Доки по Qt развесистые, беда в том что их слишком много а логика работы с графикой слегка инопланетная. Поэтому решил все же спросить:

  1. в каком виджете это все делать? Пока что я в дизайнере влепил на окошко QGraphicsView и делаю для него QGraphicsScene. Хотелось бы какого аналога Canvas из родного Tkinter. Это оно?

  2. Мне нужно что бы виджет на котором я рисую масштабировался вместе с окном, при этом у меня слева от виджета гвоздями прибиты всякие элементы управления. Это можно как то сделать в дизайнере (завернуть его в какой то layot) или надо в коде колдунствовать? Я уже задолбался экспериментировать;-(

  3. Чего я там пока вообще не вижу - как все нарисованное сохранить в png, остальные мои хотелки в том или ином виде есть. Элементы управления сохранять не надо;-)

  4. Текст рисуется через QGraphicsScene.addText который возвращает QGraphicsTextItem? Как этот текст выровнять по правому краю например?! В нормальных фреймворках была такая опция прям при вставке текста, тут гуглится какая то содомия на пять строк с курсором и форматом. Проще никак?

  5. Где бы вообще про такое рисование почитать, желательно по русски? ;-(

UPD: Гугловские примеры говорят что это делается через QImage по которому рисует QPainter. Насколько я это вообще понял…

★★★★

Последнее исправление: AntonI (всего исправлений: 3)

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

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

Да, без проблем.

Что-то вроде:

QPixmap pixmap(this->size());
this->render(&pixmap);
pixmap.save("test.png");

Для Python примерно аналогично.

EXL ★★★★★
()

Да, что бы рисовать ты используешь QPainter. У него есть конструктор, который принимает в качестве аргумента QPaintDevice. Которым может быть, помимо всего прочего, QWidget и QPixmap. Масштабирование бывает разным, но его и центрирование изи сделать с QTransform, которое можно скормить QPainter и которым можно трансформировать координаты. Но нужны базовые представления о матрицах и для чего их используют в графике.

anonymous-angler ★☆
()

в каком виджете это все делать? Пока что я в дизайнере влепил на окошко QGraphicsView и делаю для него QGraphicsScene. Хотелось бы какого аналога Canvas из родного Tkinter. Это оно?

Можно и так сказать. Но в Qt нет Canvas в привычном понимании. Экземпляр QGraphicsScene - это сцена, на которой находятся все графические элементы (картинки, круги, линии, текст). Но сама сцена ничего не отображает - это делает экземпляр QGraphicsView, который привязывается к сцене. Ты можешь привязать несколько отображений к одной сцене. Грубо говоря Canvas тут разделён на сущность (Scene) и отображение (View).

Мне нужно что бы виджет на котором я рисую масштабировался вместе с окном, при этом у меня слева от виджета гвоздями прибиты всякие элементы управления. Это можно как то сделать в дизайнере (завернуть его в какой то layot) или надо в коде колдунствовать? Я уже задолбался экспериментировать;-(

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

Чего я там пока вообще не вижу - как все нарисованное сохранить в png, остальные мои хотелки в том или ином виде есть. Элементы управления сохранять не надо;-)

У экземпляра класса QGraphicsView будет метод grab(), делай через него. Примеры можно найти

Текст рисуется через QGraphicsScene.addText который возвращает QGraphicsTextItem? Как этот текст выровнять по правому краю например?! В нормальных фреймворках была такая опция прям при вставке текста, тут гуглится какая то содомия на пять строк с курсором и форматом. Проще никак?

Там всё достаточно просто. Создаёшь экземпляр QGraphicsTextItem, выставляешь цвет, размер и прочее через свойство document() и потом добавляешь на сцену через addItem(). Не забудь выставить setVisible.

Где бы вообще про такое рисование почитать, желательно по русски? ;-(

Лучше всего потренируйся на примерах, которые идут вместе с QtCreator. И есть русские мануалы (которые обычно для более старой версии - но там минимальные отличия). И можешь так же почитать «Жасмин Бланшет, Марк Саммерфилд — Qt 4: Программирование GUI». Не знаю, есть ли для пятой версии Qt, но тебе будет это не так критично. Основы остались почти те же

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

Спасибо! И @EXL, @anonymous-angler тоже спасибо.

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

Ну и придется книжку читать, Бланшета или Шлее.

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

У меня чо то говнокод лезет;-(

Если рисовать QPainter-om поверх QImage, то потом надо вывести QImage в заданную область окна. Координаты я знаю. Как это сделать Ъ? Погуглил каких то примеров, впендюрил на окошко QLabel, этому QLabel делаю setGeometry и потом setPixmap. В label! Оно работает, но это вообще нормально? ;-(

Через QGraphicsScene это будет правильнее/быстрее?

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

Лови пример быстро написанный на коленке для ознакомления и дальнейшего расширения:

https://code.exlmoto.ru/340

Правда х.з. не намудрил ли я там с памятью и не наделал ли утечек, ибо Python и PyQt5 давно не юзал, в C++ проще будет.

Выглядит так: https://baat.z-lab.me/~exl_lab/screens/ScreenWidget.png

Позволяет сохранять отрендеренное изображение и т. д.

Удачи!

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

Через QGraphicsScene это будет правильнее/быстрее?

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

uberroot
()

QGraphicsScene - это для случаев, когда тебе нужна интерактивность с твоей нарисованной сценой, например ты хочешь кликать на элементах и что-то подсвечивать. Для простого отображения лучше рисовать через QPainter.

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

Я тут сообразил что мне нужна интерактивность вроде рисовать проекции курсора мыши на оси координат, и при этом выводить еще точные значения (чиселки) куда курсор попадает.

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

Я тут сообразил что мне нужна интерактивность вроде рисовать проекции курсора мыши на оси координат, и при этом выводить еще точные значения (чиселки) куда курсор попадает.

Ну для этого может и хватить рисования через переопределение функции paintEvent(). А такие вещи как кликабельность элементов, скролинг/масштабирование сцены тебе нужны?

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

И можно сразу делать QImage с нуля каждый раз.

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

Я тут сообразил что мне нужна интерактивность вроде рисовать проекции курсора мыши на оси координат, и при этом выводить еще точные значения (чиселки) куда курсор попадает.

Ну это и через QPainter рисуется отлично:

https://code.exlmoto.ru/342

https://baat.z-lab.me/~exl_lab/screens/MouseMovement.png

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

Спасибо. Ну я как то так и предполагал, но это можно же поверх QImage рисовать? Его то при движении мыши мне перестраивать не надо.

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

Че то у меня не заводится. Есть объект canvas типа QWidget, посаженный в окошко дизайнером.

Есть метод replot

def replot(*args):
    wsz = win.centralwidget.size() 
    sz_x, sz_y = wsz.width()-262, wsz.height()
    win.canvas.setGeometry(262, 0, sz_x, sz_y)
    plt = QtGui.QPainter(win.canvas)
    plt.drawLine(0, 262, sz_x, sz_y)

который вот так цепляется

win.resizeEvent = replot 

win это то что возвращает uic.loadUi.

В итоге ничего не рисуется, в консоли вылетает

QWidget::paintEngine: Should no longer be called
QPainter::begin: Paint device returned engine == 0, type: 1
AntonI ★★★★
() автор топика
Ответ на: комментарий от AntonI

Ты вызываешь методы QPainter вне перегруженного метода paintEvent().

Warning: When the paintdevice is a widget, QPainter can only be used inside a paintEvent() function or in a function called by paintEvent().

https://doc.qt.io/qt-5/qpainter.html#details

Либо рисуй на QImage, QPixmap, а потом рисуй/сохраняй изображение куда тебе надо, либо сделай в соответствии с документацией.

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

Чо то нифига не помогает

class MyWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        uic.loadUi(..., self)
    def paintEvent(self, event): replot()
win = MyWindow()

ошибка та же

Судя по гуглу, я не одинок. То бишь эта шня вместе с дизайнером не заводится?

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

Ты QPainter должен создавать в методе paintEvent() у перегруженного экземпляра класса win.canvas дабы иметь возможность рисовать на канвасе, а не у рандомного виджета.

Судя по гуглу, я не одинок. То бишь эта шня вместе с дизайнером не заводится?

В C++ вкупе с UI-файлами (XML-дрисня) у меня всё заводилось, только нужно было прокинуть кастомные классы.

EXL ★★★★★
()
Последнее исправление: EXL (всего исправлений: 2)
Ответ на: комментарий от EXL

Это не рандомный виджет, это вообще то родительское окно;-)

Блджад, как же все замороченно;-( В общем эту ошибку я победил, спасибо.

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

Снова я;-)

А как сделать перерисовку canvas-а через QPainter (который должен быть в paintEvent) не по каждому чиху а только тогда когда это действительно нужно? Сейчас Canvas наследник QWidget и просто размещен на окне через setGeometry. Когда я запускаюсь у меня перерисовка происходит дважды. Кроме того на окне слева управляющие элементы, когда я над ними мышкой вожу перерисовка дергается случайным образом, хотя я их не нажимаю и они даже никуда не прикручены.

У меня есть два вида перерисовок:

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

  2. легкая, просто крестик от мыши сдвигается и меняется несколько чиселок, остальное остается неизменным - когда курсор мыши двигается над canvas-ом.

Если это все в одном paintEvent, то придется видимо сделать флаг активирующий тяжелую перерисовку? Но я не очень понимаю, как при легкой перерисовке оставлять на экране то, что нарисовано было в тяжелой?

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

Ты можешь ведь манипулировать битмапами виджетов, рисуй их. А в них рисуй только тогда, когда тебе это действительно нужно.

Организуй отдельный виджет для превью, и рисуй в него, а не в MainWindow и т. д.

Способов много, экспериментируй.

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

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

Скажем я могу тяжелую перерисовку делать (или грабить) в картинку, и подкладывать ее. В TKinter было как то попроще, там можно было удалить/добавить примитивы с заданными тэгами, сам он ничего не чистил как этот QPainter… ;-(

AntonI ★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.