Есть ли у QPainter в PyQt5 методы как у библиотеки turtle?
Я пишу программу с графическим интерфейсом на PyQt5. Она должна отрисовывать картинку L-системы. Для этого я использую QWidget, на котором собственно и рисую.
Столкнулся с проблемой: я хочу написать код так, чтобы не пришлось создавать массив с координатами точек, а для этого очень подошли бы команды аналогичные turtle.left(), turtle.right() и turtle.forward(), но только для QPainter.
Подскажите пожалуйста, есть ли такие? А если нет, то хотелось бы узнать, как конвертировать канвас черепашки в QPixmap, чтобы отображать его на лейбле.
Уточнение: программа перед стартом принимает файл с аксиомой и теоремами, которые она получает через диалог с пользователем(QFileDialog), который выбирает текстовый файл с информацией о нужном фрактале. Прочитав файл, программа создаёт виджет со слайдером, который переключает количество рекурсий, и отрисовывает на виджете результат.
Вот код:
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QWidget, QApplication, QFileDialog, QSlider, QLabel
class Example(QWidget):
def __init__(self):
super().__init__()
self.fa = QFileDialog.getOpenFileName(self, 'Выбрать файл', '')[0]
self.initUI()
def initUI(self):
self.setGeometry(200, 50, 600, 630)
self.setWindowTitle('L-система с QT и черепашкой')
self.step = 0
self.sl = QSlider(Qt.Horizontal, self)
self.sl.resize(600, 30)
self.sl.move(0, 600)
self.sl.valueChanged.connect(self.sob)
self.lay = QLabel(self)
self.lay.resize(600, 600)
def sob(self):
self.step = (self.sl.value() + 1) // 20
f = open(self.fa, mode='rt')
a = f.readlines()
f.close()
inf = []
for i in range(1, len(a)):
inf.append(a[i].strip())
for i in range(2, len(inf)):
inf[i] = inf[i].split()
a = inf[1]
for i in range(self.step):
s = ''
for g in range(len(a)):
for h in range(2, len(inf)):
if a[g] == inf[h]:
s += inf[h]
a = s
for i in range(len(a)):
if a[i] == 'F':
# тут должна быть команда, которая рисует отрезак заданной длинны,
# не принимая при этом координаты начальной точки
pass
elif a[i] == '-':
# тут должна быть команда, которая поворачивает "карандаш" на
# заданное количество градусов
pass
elif a[i] == '+':
# тут должна быть команда, которая поворачивает "карандаш" на
# заданное количество градусов, но в противоположную стороно от предыдущей
pass
elif a[i] == 'X':
# просто пропуск действия
pass
elif a[i] == 'Y':
# туоже просто пропуск действия
pass
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec())
Ответы (1 шт):
Если я вас правильно понял, а L-Systems — математическая красота растений,
то попробуйте пример:
import sys
from PyQt5 import QtGui, QtWidgets, QtCore, Qt
class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
self.angle = 25
self.transform = QtGui.QTransform()
self.translate2 = 0
self.recursions = 10
self.resize(670, 645)
self.origin = (self.width()/2, self.height())
self.pal = QtGui.QPalette()
self.pal.setColor(QtGui.QPalette.Background, QtGui.QColor(0, 0, 0))
self.setPalette(self.pal)
self.pen_branch = QtGui.QPen()
self.pen_branch.setColor(QtGui.QColor(255, 255, 255))
self.init_UI()
def init_UI(self):
self.slider_angle = QtWidgets.QSlider(QtCore.Qt.Horizontal, self)
self.slider_angle.setMinimum(-8000)
self.slider_angle.setMaximum(8000)
self.slider_angle.setGeometry(0, 30, self.width(), 30)
self.slider_angle.valueChanged.connect(
lambda value, x=0 : self.setAngleValue(value))
self.setAngleValue(2500)
self.slider_angle.setValue(2500)
def setAngleValue(self, value):
self.angle = value/100
self.update()
### функция branch могла быть реализована так:
# после первого вызова функции rotate другая ветвь должна использовать удвоенный угол,
# чтобы двигаться в противоположном направлении.
#
# Обратите внимание: предполагая, что угол равен 30°, если вы вызываете rotate(r)
# для правой ветви, тогда вам нужно повернуть (-r * 2) для левой;
# таким образом угол "сбрасывается" до исходного значения,
# а затем поворачивается на противоположную сторону.
#
# def branch(self, p, x, r, length):
# p.drawLine(0, 0, 0, -length)
# if length > 1 :
# p.translate(0, -length)
# p.rotate(r)
# self.branch(p, 10, r, length * 0.66)
#
# # повернуть на противоположную сторону
# p.rotate(-r * 2)
# self.branch(p, 10, -r, length * 0.66)
# # обратите внимание, что translation и rotation инвертируются
# p.rotate(r)
# p.translate(0, length)
###
# Обычно лучше использовать функции save() https://doc.qt.io/qt-5/qpainter.html#save
# и restore() https://doc.qt.io/qt-5/qpainter.html#restore , которые позволяют
# использовать несколько уровней состояний рисования:
# вы сохраняете состояние, применяете все необходимые изменения
# (pen, brush и любые другие преобразования), тогда вы можете восстановить
# предыдущее состояние автоматически;
# это упрощает разработку и позволяет получить более читаемый и понятный код;
# просто помните, что состояния всегда нужно восстанавливать
# до исходного сохраненного "уровня".
# Вот как может выглядеть функция branch лучше:
def branch(self, p, x, r, length):
p.drawLine(0, 0, 0, -length)
if length > 3:
# сохранить состояние, "первый" уровень (для текущей функции)
p.save()
p.translate(0, -length)
# сохранить состояние для правой ветви
p.save()
p.rotate(r)
self.branch(p, 10, r, length * .66)
# восстановить на предыдущий "первый" уровень
p.restore()
# сохранить для левой ветви
p.save()
p.rotate(-r)
self.branch(p, 10, -r, length * .66)
# восстановить снова
p.restore()
# восстановить предыдущее состояние
p.restore()
def paintEvent(self, e):
p = QtGui.QPainter(self)
p.setRenderHint(QtGui.QPainter.Antialiasing)
p.setPen(self.pen_branch)
p.drawText(20, 20, 'angle: ' + str(self.angle))
p.drawText(100, 20, 'trans2: ' + str(self.translate2))
p.translate (self.width()/2, self.height())
self.branch(p, 10, self.angle, 200)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())
