Как создать геометрическую фигуру в python
Перейти к содержимому

Как создать геометрическую фигуру в python

  • автор:

«Черепашья графика» при помощи turtle, рисование при помощи алгоритма

Черепашья графика, turtle – принцип организации библиотеки графического вывода, построенный на метафоре Черепахи, воображаемого роботоподобного устройства, которое перемещается по экрану или бумаге и поворачивается в заданных направлениях, при этом оставляя (или, по выбору, не оставляя) за собой нарисованный след заданного цвета и ширины.

Проще: черепашка ползает по экрану и рисует. Мы управляем черепашкой на плоскости при помощи программы.

Начало работы. Движения

В первой строке необходимо добавить:

import turtle

Мы командуем черепашкой простыми словами на английском языке. left, right – поворот налево и направо, forward и backward – движение вперед и назад. В программе каждое действие – вызов функции из модуля turtle. Простая программа:

import turtle turtle.right(90) turtle.forward(100) turtle.left(90) turtle.backward(100) 

Что произошло:

  • Поворот направо на 90 градусов
  • Движение вперед на 100 шагов (пикселей)
  • Поворот налево на 90 градусов
  • Движение назад на 100 шагов

Не похоже на черепашку, это ползающая стрелка! Исправим это:

import turtle turtle.shape("turtle") turtle.fd(100) turtle.exitonclick() 

Отлично! Теперь это черепашка, пусть и монохромная. Дополнительно, функция exitonclick() позволяет закрыть окно и завершить выполнение программы кликом мышкой по окну.
А еще можно использовать сокращенные названия функций: fd(100) вместо forward(100), rt вместо right, lt вместо left, bk вместо backward.

Геометрические фигуры

Рисуем простые геометрические фигуры:

  • Прямая: просто движение вперед
  • Квадрат: вперед, поворот на 90 градусов и так 4 раза. Повторение команд – значит, можно выполнить их в цикле for!
  • Пятиконечная звезда: вперед, поворот на 144 градусов и так 5 раз.

Если мы хотим выполнить инструкции n раз, мы пишем их в цикле

for i in range(n):

Далее идут инструкции с отступом в 4 пробела. Код с отступами – тело цикла. Когда цикл завершается, отступы больше не ставятся.

Рисуем квадрат:

import turtle square = turtle.Turtle() square.shape("turtle") for i in range(4): square.forward(100) square.right(90) turtle.exitonclick() 

Скучно рисовать одинокие фигуры. Поэтому мы приготовились рисовать сразу несколько и теперь создаем отдельный экземпляр класса Turtle для каждой фигуры. Так мы можем менять цвет линии и другие параметры отдельно для каждой фигуры. Потом, когда мы захотим дорисовать или изменить параметры фигуры, у нее будут сохранены старые параметры. Их не надо будет устанавливать заново, как это было бы без отдельных экземпляров класса для каждой фигуры.

Звезда рисуется также:

Самостоятельно:

  1. Нарисуйте пятиконечную звезду (угол поворота 144 градуса).
  2. Квадрат и звезду в одной программе, на одном графическом поле, но с разными экземплярами класса Turtle.
  3. Восьмиконечную звезду (угол поворота 135 градусов).
  4. Фигуру из анимации в начале страницы.

Решения

Квадрат и звезда

import turtle square = turtle.Turtle() for i in range(4): square.forward(100) square.right(90) starf = turtle.Turtle() for i in range(5): starf.forward(100) starf.right(144) turtle.exitonclick() 

8-конечная звезда

import turtle star = turtle.Turtle() star.hideturtle() for i in range(8): star.forward(100) star.right(135) turtle.exitonclick() 

9-конечная звезда

import turtle nineang = turtle.Turtle() for i in range(9): nineang.forward(100) nineang.left(140) nineang.forward(100) nineang.right(100) turtle.exitonclick() 

Изменяем параметры во время движения

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

import turtle square = turtle.Turtle() square.shape("turtle") square.color('red', 'green') square.begin_fill() for j in range(3): square.left(20) for i in range(4): square.forward(100) square.left(90) square.end_fill() turtle.exitonclick() 

Мы также добавили:

  • color(‘red’, ‘green’) определяет цвет линии и цвет заполнения. Черепашка теперь зеленая!
  • begin_fill() и end_fill() обозначают начало и конец заполнения

Больше программирования!

Напишем обобщенную программу рисования выпуклых равносторонних многоугольников. num_sides – количество граней, side_length – длина грани, angle – угол поворота.

import turtle polygon = turtle.Turtle() num_sides = 6 side_length = 100 angle = 360.0 / num_sides for i in range(num_sides): polygon.forward(side_length) polygon.right(angle) turtle.exitonclick() 

Что будет, если на каждом шаге увеличивать длину пути? В первый день 10 шагов, во второй – 20, далее 30, 40 и так до 200:

import turtle spiral = turtle.Turtle() for i in range(20): spiral.forward(i * 10) spiral.right(144) turtle.exitonclick() 

Координаты на плоскости

Положение на плоскости определяется двумя числами, x и y:

Черепашку в программе можно перемещать функцией goto(x, y). x и y – числа, или переменные. goto(0, 0) переместит черепашку в начало координат.

import turtle spiral = turtle.Turtle() for i in range(20): spiral.fd(i * 10) spiral.rt(144) spiral.goto(0,0) turtle.exitonclick() 

Вместо звезды-спирали мы получили 5 линий, расходящихся из точки начала координат.

Круг и точка

Не хватает плавных изгибов? На помощь приходят функции dot() и circle():

import turtle turtle.title("Turtle Drawing") circle = turtle.Turtle() circle.shape("turtle") circle.pensize(5) circle.pencolor("cyan") circle.dot(20) circle.penup() circle.goto(0, -100) circle.pendown() circle.circle(100) turtle.exitonclick() 

  • изменили заголовок окна функцией title(),
  • установили толщину линии – pensize(),
  • установили цвет линии – pencolor(),
  • Подняли черепашку перед перемещением – penup() и опустили после – pendown().

Самостоятельно:

  • Используя код из примеров и функцию goto(), нарисовать галерею из 5 или более многоугольников на одном поле. Использовать экземпляр класса turtle.Turtle().
  • Нарисованные многоугольники закрасить разными цветами. Пробуйте стандартные цвета или их шестнадцатеричное представление. Не забудьте кавычки вокруг названия или кода цвета!

Решения

  • У нас есть два варианта нарисовать несколько фигур: используя отдельные классы и не используя их. Рассмотрим оба варианта.
  • Без классов:
import turtle turtle.hideturtle() turtle.speed(10) turtle.color('red') turtle.begin_fill() for i in range(4): turtle.forward(100) turtle.right(90) turtle.end_fill() turtle.penup() turtle.goto(200, 200) turtle.pendown() turtle.color('green') turtle.begin_fill() for i in range(5): turtle.forward(100) turtle.rt(144) turtle.end_fill() turtle.penup() turtle.goto(-200, -200) turtle.pendown() turtle.color('blue') turtle.begin_fill() for i in range(8): turtle.forward(100) turtle.right(135) turtle.end_fill() turtle.penup() turtle.goto(200, -200) turtle.pendown() turtle.color('cyan') turtle.begin_fill() for i in range(3): turtle.forward(100) turtle.lt(120) turtle.end_fill() turtle.penup() turtle.goto(-200, 200) turtle.pendown() turtle.color('magenta') turtle.begin_fill() for i in range(9): turtle.forward(30) turtle.right(140) turtle.forward(30) turtle.left(100) turtle.end_fill() turtle.exitonclick()
import turtle square = turtle.Turtle() square.hideturtle() square.color('red') square.speed(10) square.begin_fill() for i in range(4): square.forward(100) square.right(90) square.end_fill() square.penup()
import turtle def prepare(x, y, color): turtle.penup() turtle.goto(x, y) turtle.pendown() turtle.color(color) turtle.begin_fill() def draw_polygon(num_sides, side_length): angle = 360.0 / num_sides for i in range(num_sides): turtle.forward(side_length) turtle.right(angle) turtle.hideturtle() turtle.speed(10) prepare(0, 0, 'red') draw_polygon(3, 100) turtle.end_fill() prepare(200, 200, 'green') draw_polygon(4, 100) turtle.end_fill() prepare(-200, -200, 'blue') draw_polygon(5, 100) turtle.end_fill() prepare(200, -200, 'cyan') draw_polygon(6, 100) turtle.end_fill() prepare(-200, 200, 'magenta') draw_polygon(7, 100) turtle.end_fill() turtle.exitonclick()
import turtle def prepare(x, y, color): turtle.penup() turtle.goto(x, y) turtle.pendown() turtle.color(color) turtle.begin_fill() def draw_polygon(num_sides, side_length): angle = 360.0 / num_sides for i in range(num_sides): turtle.forward(side_length) turtle.right(angle) turtle.end_fill() turtle.hideturtle() turtle.speed(10) colors = ['red', 'green', 'blue', 'cyan', 'magenta'] xcoords = [0, 200, -200, 200, -200] ycoords = [0, 200, -200, -200, 200] for i in range(5): prepare(xcoords[i], ycoords[i], colors[i]) draw_polygon(i+3, 100) turtle.exitonclick()

Делаем фигуры равновеликими

Площадь квадрата со стороной 100 пикселей – 10 000 квадратных пикселей. Вычислим площади всех фигур со стороной 100 от треугольника до 7-угольника. Формула площади правильного многоугольника содержит тангенс, поэтому «поверим на слово» результату, зависимости количество углов (вершин) – площадь:

  • 3 – 4330.13
  • 4 – 10000
  • 5 – 17204.77
  • 6 – 25980.76
  • 7 – 36339.12

Изобразим ее на графике:

Получается, что площадь 7-угольника в 36339.12 / 4330.13 = 8.4 раза больше, чем площадь треугольника! Это очень заметно на рисунке:

Чтобы фигуры стали равновеликими, надо сделать длину грани вместо константы 100 – переменной, которая зависит от количества углов.

Как: приведем все площади к 10000. Для треугольника площадь увеличится на 10000 / 4330.13 = 2.31 раза. Для 7-угольника – уменьшится в 36339.12 / 10000 = 3.63 раз. Значит, стороны должны измениться в 1.52 и 0.52 раз соответственно, то есть, до 152 и 32.7 пикселей (снова «верим на слово»). Эту зависимость можно нащупать «на глаз», в чем и заключалось задание.

Наша программа без труда масштабируется до большего количества фигур:

Программа, в которой вычисляются точные значения:

import turtle from math import tan, sqrt, pi def prepare(x, y, color): turtle.penup() turtle.goto(x, y) turtle.pendown() turtle.color(color) turtle.begin_fill() def draw_polygon(num_sides, side_length): angle = 360.0 / num_sides for i in range(num_sides): turtle.forward(side_length) turtle.right(angle) turtle.end_fill() def calc_s(num_sides, side_length): return num_sides * side_length ** 2 / (4 * tan(pi/num_sides)) def calc_side(square): return sqrt(4 * square * tan(pi/num_sides) / num_sides) turtle.hideturtle() turtle.speed(10) colors = ['red', 'green', 'blue', 'cyan', 'magenta', 'black', 'yellow', 'pink', 'brown'] xcoords = [0, 150, -150, 150, -150, 270, -270, 270, -270] ycoords = [0, 150, -150, -150, 150, 270, -270, -270, 270] squares = [] numsides = [] for i in range(9): num_sides = i + 3 square = round(calc_s(num_sides, 100), 2) side_length = round(calc_side(10000), 3) squares.append(square) numsides.append(num_sides) print("Углов:", num_sides, "была площадь:", square, "стала длина грани:", side_length, "изменение в", round(side_length/100, 2), "раз") prepare(xcoords[i], ycoords[i], colors[i]) draw_polygon(num_sides, side_length) turtle.exitonclick() print("Список количество углов:", numsides, end="") print("Список площади:", squares)
Углов: 3 была площадь: 4330.13 стала длина грани: 151.967 изменение в 1.52 раз Углов: 4 была площадь: 10000.0 стала длина грани: 100.0 изменение в 1.0 раз Углов: 5 была площадь: 17204.77 стала длина грани: 76.239 изменение в 0.76 раз Углов: 6 была площадь: 25980.76 стала длина грани: 62.04 изменение в 0.62 раз Углов: 7 была площадь: 36339.12 стала длина грани: 52.458 изменение в 0.52 раз Углов: 8 была площадь: 48284.27 стала длина грани: 45.509 изменение в 0.46 раз Углов: 9 была площадь: 61818.24 стала длина грани: 40.22 изменение в 0.4 раз Углов: 10 была площадь: 76942.09 стала длина грани: 36.051 изменение в 0.36 раз Углов: 11 была площадь: 93656.4 стала длина грани: 32.676 изменение в 0.33 раз Список количество углов: [3, 4, 5, 6, 7, 8, 9, 10, 11] Список площади: [4330.13, 10000.0, 17204.77, 25980.76, 36339.12, 48284.27, 61818.24, 76942.09, 93656.4]

Как построить график (если кто захочет):

pip install matplotlib
import matplotlib.pyplot as plt numsides = [3, 4, 5, 6, 7, 8, 9, 10, 11] squares = [4330.13, 10000.0, 17204.77, 25980.76, 36339.12, 48284.27, 61818.24, 76942.09, 93656.4] plt.plot(numsides, squares, 'or--') plt.xlabel('Количество углов') plt.ylabel('Площадь') plt.show()

Другие полезные функции:

  • turtle.setup(800, 400) устанавливает размеры окна в 800 на 400 пикселей
  • turtle.setworldcoordinates(0, 0, 800, 400) устанавливает начало координат в точку 800, 400
  • turtle.tracer(0, 0) отключает анимацию
  • setpos(x, y) устанавливает черепашку (курсор) в позицию с координатами (x, y)
  • seth(x) устанавливает направление в градусах. 0 – горизонтально направо (на восток), 90 – вверх (на север) и так далее
  • hideturtle() скрывает черепашку (или стрелку, курсор)
  • speed(x) изменяет скорость рисования. Например, speed(11) – почти моментальная отрисовка простых фигур
  • clear() очищает холст от нарисованного
  • reset() очищает холст и возвращает курсор в начало координат

Пример двух рисунков – экземпляров класса Turtle() – на одном полотне

import turtle turtle.title("Turtle Circles") circ = turtle.Turtle() circ.pencolor("purple") circ.fillcolor("orange") circ.shape("circle") circ.pensize(5) circ.speed(10) circ.fd(150) circ.begin_fill() circ.circle(90) circ.end_fill() n = 10 t = turtle.Turtle() while n  

Что произошло:

  1. Задали название окна,
  2. создали экземпляр класса Turtle под именем circ. Все изменения сохраняются для класса circ;
  3. цвет линии и заполняющий цвет,
  4. форму и размер курсора,
  5. установили 10-ю скорость
  6. продвинулись на 150 пикселей вперед от старта,
  7. начали заполнять фигуру цветом,
  8. нарисовали круг
  9. закончили заполнять цветом,
  1. Объявили переменную n и присвоили ей значение 10,
  2. создали новый экземпляр класса Turtle под именем t. У него нет настроек экземпляра класса circ!
  3. В цикле while: пока переменная n меньше или равна 50, рисовать круги радиусом n;
  4. после нарисованного круга увеличить переменную n на 10.
  5. Алгоритм рисования кругов прекратит рисовать круги после 4-го круга.

Итог: функции и классы на примере turtle

  • Функция – фрагмент программного кода, к которому можно обратиться по имени. Иногда функции бывают безымянными.
  • У функции есть входные и выходные параметры. Функция fd(150) – фрагмент программного кода, который двигает курсор вперед на заданное во входном значении количество пикселей (150). Выходного значения у функции fd() нет.
  • Когда функцию надо выполнить, после ее названия пишут круглые скобки. fd – просто название, ничего не происходит. fd(100) – функция выполняется с входным параметром 100. Обычно названия функций пишут с маленькой буквы.
  • Класс – программный шаблон для создания объектов, заготовка для чего-то, имеющего собственное состояние. Мы можем нарисовать прямоугольник и назвать его кнопкой, но это еще не кнопка, потому что у нее нет собственных свойств и поведения. Прямоугольник надо научить быть самостоятельной, отличной от других, кнопкой.
  • Turtle – класс, его имя пишется с большой буквы. через оператор присваивания = мы создаем экземпляр класса: circ = turtle.Turtle(). Turtle – класс (шаблон, трафарет, заготовка), circ – его экземпляр (рисунок, набор уникальных цветов, штрихов и свойств). На картинке выше видно, что экземпляр класса circ богат установленными свойствами, а экземпляр t обладает свойствами по умолчанию: тонкая черная линия, треугольный курсор.
  • Программирование с использованием классов и их экземпляров будем называть объектно-ориентированным программированием, ООП. объектно-ориентированный подход необходим при построении графического интерфейса пользователя, GUI.

Графический интерфейс средствами библиотеки turtle.

Нарисуем прямоугольник и сделаем его кнопкой: при нажатии кнопка исчезает и появляется круг:

import turtle wndow = turtle.Screen() wndow.title("Screen & Button") wndow.setup(500, 500) btn1 = turtle.Turtle() btn1.hideturtle() for i in range(2): btn1.fd(80) btn1.left(90) btn1.fd(30) btn1.left(90) btn1.penup() btn1.goto(11,7) btn1.write("Push me", font=("Arial", 12, "normal")) def btnclick(x, y): if 0 

Что произошло:

  1. Задали название и размеры (500 на 500 пикселей) окна,
  2. Создали экземпляр класса btn1 и спрятали курсор (черепашку),
  3. Нарисовали прямоугольник 80 на 30;
  4. подняли перо и перешли на координаты (11, 7);
  5. написали Push me шрифтом Arial 12-го размера, нормальное начертание. Попробуйте вместо normal ключевые слова bold (полужирный), italic (наклонный);

Задаем поведение кнопки:

  • Функции turtle.listen() и turtle.onscreenclick() будут слушать (listen) и реагировать на клик по экрану (onscreenclick). Реакцией будет запуск функции btnclick(x, y)
  • Напишем btnclick(x, y). У нее 2 входных параметра – координаты точки, куда мы кликнули. Наша задача: если клик был по кнопке, спрятать ее и показать оранжевый круг
  • Мы помним: кнопка 80 на 30 пикселей от точки (0, 0). Значит, мы попали по кнопке, если x между 0 и 80 и y между 0 и 30. Условие попадания по кнопке: if 0
  • 1) Убираем кнопку: btn1.clear(), 2) создаем экземпляр класса ball = turtle.Turtle(), 3) устанавливаем ему нужные свойства.

Самостоятельно:

  • Нарисовать вторую кнопку (не изменяя первую!), сделать обработчик нажатия: при клике программа завершается, выполняется функция exit()
  • При нажатии на первую кнопку появляется случайная фигура: при рисовании фигуры использовать random:
from random import randrange print(randrange(30, 201)) # случайное целое число от 30 до 200 

Уточнения

  • Чтобы окно не закрывалось сразу, мы использовали turtle.exitonclick(). Теперь, когда клик обрабатывается функцией, пишем в конце turtle.done().
  • функция exit() самостоятельная, это не команда turtle. Писать turtle.exit() неверно.
  • Случайная фигура – это любая фигура, при рисовании которой используются случайные числа. Например:
from random import randrange circle = turtle.Turtle() circle.circle(randrange(36, 91)) 

Но есть и второй вариант: случайное число будет индексом списка и укажет на одну из заранее подготовленных неслучайных фигур:

from random import randrange figures = ['circle', 'rectangle', 'triangle'] choice = figures[randrange(0, 3)] # случайный индекс от 0 до 2 даст одно из трех слов списка 

Таким приемом можно случайно выбирать цвета фигур. Функция choice делает тоже самое изящнее:

from random import randrange, choice colors = ['red', 'green', 'blue'] color = colors[randrange(0, 3)] another_color = choice(colors) 

Управляем рисунком с клавиатуры

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

  • turtle.onkeypress(fun, key): вызывается функция fun при нажатии клавиши key
  • turtle.onkey(fun, key): вызывается функция fun при отпускании клавиши key

Клавиша задается строкой с ее названием. Например, 'space' – пробел, 'Up' (с заглавной буквы) – стрелка вверх. Клавиши букв задаются заглавными, только если мы хотим нажать именно заглавную (с Shift или Caps Lock).

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

  • xcor() и ycor() выдают координаты по x и y как дробные числа
  • setx(x) и sety(y) устанавливают координаты. x и y – числа

Создадим экземпляр класса Turtle и выведем его координаты:

import turtle circ = turtle.Turtle() circ.shape("circle") circ.color("orange") circ.penup() print(circ.xcor(), circ.ycor())

Получили вывод "0.0 0.0". Теперь напишем функцию up(), которая будет запускаться при нажатии стрелки вверх и перемещать наш circ на 10 пикселей вверх:

import turtle circ = turtle.Turtle() circ.shape("circle") circ.color("orange") circ.penup() def up(): y = circ.ycor() + 10 circ.sety(y) turtle.listen() turtle.onkeypress(up, 'Up') turtle.done()

Очень похоже на нажатие мышкой! Функцию up() можно сократить до одной строчки:

def up(): circ.sety(circ.ycor() + 10)

Будет работать, но функции в одну строчку писать не принято. Для таких случаев используют анонимные функции: у них может вовсе не быть имени. В Python в качестве анонимных функций используются лямбда-выражения, мы их уже использовали для сортировки. Так будет выглядеть лямбда-функция up:

up = lambda: circ.sety(circ.ycor() + 10)

Она используется у нас только в одном месте, внутри функкии turtle.onkeypress(). А почему бы не соединить их вместе? Так будет выглядеть наша программа в сокращенном виде:

import turtle circ = turtle.Turtle() circ.shape("circle") circ.color("orange") circ.penup() turtle.listen() turtle.onkeypress(lambda: circ.sety(circ.ycor() + 10), 'Up') turtle.done()

Всего 8 строк, и функции действительно не понадобилось имени! Как видим, язык Python дает возможность писать разными стилями, и мы можем выбирать на свой вкус: писать развернуто и красиво (как писал Гавриил Романович Державин) или кратко (как Эрнест Хемингуэй).

Самостоятельно:

  • Добавить движение circ влево, вправо и вниз
  • Скорость движения (у нас пока 10 пикселей за раз) сделать переменной

Соединяем все вместе

У нас уже есть кнопка с текстом и обработчик клика мышкой. Соединим все в одну программу:

import turtle wndow = turtle.Screen() wndow.title("Circle game") wndow.setup(500, 500) btn1 = turtle.Turtle() btn1.hideturtle() for i in range(2): btn1.fd(80) btn1.left(90) btn1.fd(30) btn1.left(90) btn1.penup() btn1.goto(4, 5) btn1.write("Start!", font=("Arial", 12, "normal")) circ = turtle.Turtle() circ.hideturtle() circ.shape("circle") circ.color("orange") def btnclick(x, y): if 0

Есть стартовый экран, управляемый с клавиатуры персонаж. Добавим препятствие, и уже почти готова игра!

import turtle wndow = turtle.Screen() wndow.title("Circle game") wndow.setup(500, 500) btn1 = turtle.Turtle() btn1.hideturtle() for i in range(2): btn1.fd(80) btn1.left(90) btn1.fd(30) btn1.left(90) btn1.penup() btn1.goto(4, 5) btn1.write("Start!", font=("Arial", 12, "normal")) circ = turtle.Turtle() circ.hideturtle() circ.shape("circle") circ.color("orange") sq = turtle.Turtle() sq.hideturtle() sq.penup() sq.setposition(-20, 70) def btnclick(x, y): if 0 

Самостоятельно:

  • Нарисовать фигуры, которые надо обойти герою игры. Это может быть простой лабиринт!
  • Написать условное выражение (если координаты circ больше заданных величин), при котором наступает победа, и игра заканчивается. Это может быть выход из лабиринта!
  • В программе должно быть реализовано движение во все 4 стороны. Можно использовать стрелки, можно – клавиши WASD. Также можно реализовать движение по диагонали, когда за один ход изменяются обе координаты.

Подсказки

  • Движение во все 4 стороны:
import turtle circ = turtle.Turtle() circ.shape("circle") circ.color("orange") circ.penup() turtle.listen() turtle.onkeypress(lambda: circ.setx(circ.xcor() + 10), 'Right') turtle.onkeypress(lambda: circ.setx(circ.xcor() - 10), 'Left') turtle.onkeypress(lambda: circ.sety(circ.ycor() + 10), 'Up') turtle.onkeypress(lambda: circ.sety(circ.ycor() - 10), 'Down') turtle.done()
turtle.onkeypress(up, 'Up') # движение вверх по кнопке вверх turtle.onkeypress(up, 'w') # движение вверх по кнопке w​ turtle.onkeypress(up, 'W') # движение​ вверх по W (w с нажатой Shift или CapsLock)
def upright(): x = circ.xcor() + 10 y = circ.ycor() + 10 circ.setx(x) circ.sety(y)

Игра с подсчетом ходов

import turtle def prepare_fig(fig, x, y): fig.hideturtle() fig.penup() fig.setposition(x, y) fig.speed(13) def draw_square(fig, color, side_length): fig.pendown() fig.fillcolor(color) fig.begin_fill() for i in range(4): fig.fd(side_length) fig.rt(90) fig.end_fill() def message(text, color): circ.hideturtle() circ.goto(0, 0) circ.color(color) sq.clear() sq2.clear() print(moves) circ.write(text, font=("Arial", 12, "bold")) def win_or_die(moves): if -20 < circ.xcor() < 40 and 10 < circ.ycor() < 70: message(GAME_OVER_MSG + str(moves), 'red') if -60 < circ.xcor() < -20 and 50 < circ.ycor() < 90: message(WIN_MSG + str(moves), 'green') def movey(deltay): global moves y = circ.ycor() + deltay circ.sety(y) moves += 1 win_or_die(moves) def movex(deltax): global moves x = circ.xcor() + deltax circ.setx(x) moves += 1 win_or_die(moves) wndow = turtle.Screen() wndow.title("Circle game") wndow.setup(500, 500) circ = turtle.Turtle() circ.penup() circ.shape("circle") circ.color("orange") sq = turtle.Turtle() prepare_fig(sq, -20, 70) draw_square(sq, 'red', 60) sq2 = turtle.Turtle() prepare_fig(sq2, -60, 90) draw_square(sq2, 'green', 40) moves = 0 GAME_OVER_MSG = 'Game over!\nСделано шагов: ' WIN_MSG = 'Победа!\nСделано шагов: ' STEP = 10 turtle.listen() turtle.onkeypress(lambda: movey(STEP), 'Up') turtle.onkeypress(lambda: movey(-STEP), 'Down') turtle.onkeypress(lambda: movex(STEP), 'Right') turtle.onkeypress(lambda: movex(-STEP), 'Left') turtle.done()

Всего 61 строка кода!

Ссылки

  • Simple drawing with turtle
  • Turtle Graphics in Python
  • The Beginner's Guide to Python Turtle
  • Turtle examples
  • Give Python turtle a rectangular shape for ping pong game

Модуль pygame.draw

Функции модуля pygame.draw рисуют геометрические примитивы на поверхности – экземпляре класса Surface . В качестве первого аргумента они принимают поверхность. Поэтому при создании той или иной поверхности ее надо связать с переменной, чтобы потом было что передать в функции модуля draw . Поскольку мы пока используем только одну поверхность – главную оконную, то ее будем указывать в качестве первого параметра, а при создании свяжем с переменной:

import pygame as pg import sys sc = pg.display.set_mode((300, 200)) # здесь будут рисоваться фигуры pg.display.update() while 1: for i in pg.event.get(): if i.type == pg.QUIT: sys.exit() pg.time.delay(1000)

В большинстве случаев фигуры прорисовывают внутри главного цикла, так как от кадра к кадру картинка на экране должна меняться. Поэтому на каждой итерации цикла в функции модуля draw передаются измененные аргументы (например, каждый раз меняется координата x).

Однако у нас пока не будет никакой анимации, и нет смысла перерисовывать фигуры на одном и том же месте на каждой итерации цикла. Поэтому создавать примитивы будем в основной ветке программы. На данном этапе цикл while нужен лишь для того, чтобы программа самопроизвольно не завершалась.

После прорисовки, чтобы увидеть изменения в окне игры, необходимо выполнить функцию update() или flip() модуля display . Иначе окно не обновится. Рисование на поверхности – одно, а обновление состояния главного окна – другое. Представьте, что в разных местах тела главного цикла на поверхности прорисовываются разные объекты. Если бы каждое такое действие приводило к автоматическому обновлению окна, то за одну итерацию оно обновлялось бы несколько раз. Это приводило бы как минимум к бессмысленной трате ресурсов, так как скорость цикла связана с FPS.

Итак, первый аргумент функций рисования – поверхность, на которой размещается фигура. В нашем случае это будет sc . Вторым обязательным аргументом является цвет. Цвет задается в формате RGB, используется трехэлементный целочисленный кортеж. Например, (255, 0, 0) определяет красный цвет.

Далее идут специфичные для каждой фигуры аргументы. Последним у большинства является толщина контура.

Все функции модуля draw возвращают экземпляры класса Rect – прямоугольные области, имеющие координаты, длину и ширину. Не путайте функцию rect() модуля draw и класс Rect , это разные вещи.

Начнем с функции rect() модуля draw :

pygame.draw.rect(sc, (255, 255, 255), (20, 20, 100, 75)) pygame.draw.rect(sc, (64, 128, 255), (150, 20, 100, 75), 8)

Прямоугольники

Если указывается толщина контура (последний аргумент второго прямоугольника), то фигура окажется незаполненной, а цвет определит цвет рамки. Третьим аргументом является кортеж из четырех чисел. Первые два определяют координаты верхнего левого угла прямоугольника, вторые – его ширину и высоту.

Следует отметить, что в функцию draw.rect() и некоторые другие третьим аргументом можно передавать не кортеж, а заранее созданный экземпляр Rect . В примере ниже показан такой вариант.

Обычно цвета выносят в отдельные переменные-константы. Это облегчает чтение кода:

WHITE = (255, 255, 255) BLACK = (0, 0, 0) GRAY = (125, 125, 125) LIGHT_BLUE = (64, 128, 255) GREEN = (0, 200, 64) YELLOW = (225, 225, 0) PINK = (230, 50, 230) r1 = pygame.Rect((150, 20, 100, 75)) pygame.draw.rect(sc, WHITE, (20, 20, 100, 75)) pygame.draw.rect(sc, LIGHT_BLUE, r1, 8)

Чтобы нарисовать линию, а точнее – отрезок, надо указать координаты его концов. При этом функция line() рисует обычную линию, aaline() – сглаженную (толщину для последней указать нельзя):

pygame.draw.line(sc, WHITE, [10, 30], [290, 15], 3) pygame.draw.line(sc, WHITE, [10, 50], [290, 35]) pygame.draw.aaline(sc, WHITE, [10, 70], [290, 55])

Линии

Координаты можно передавать как в виде списка, так и кортежа.

Функции lines() и aalines() рисуют ломанные линии:

pygame.draw.lines(sc, WHITE, True, [[10, 10], [140, 70], [280, 20]], 2) pygame.draw.aalines(sc, WHITE, False, [[10, 100], [140, 170], [280, 110]])

Ломаные

Координаты определяют места излома. Количество точек может быть произвольным. Третий параметр ( True или False ) указывает замыкать ли крайние точки.

Функция polygon() рисует произвольный многоугольник. Задаются координаты вершин.

pygame.draw.polygon(sc, WHITE, [[150, 10], [180, 50], [90, 90], [30, 30]]) pygame.draw.polygon(sc, WHITE, [[250, 110], [280, 150], [190, 190], [130, 130]]) pygame.draw.aalines(sc, WHITE, True, [[250, 110], [280, 150], [190, 190], [130, 130]])

Многоугольники

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

Так же как в случае rect() функция polygon() может принимать толщину контура.

Функция circle() рисует круги. Указывается центр окружности и радиус:

pygame.draw.circle(sc, YELLOW, (100, 100), 50) pygame.draw.circle(sc, PINK, (200, 100), 50, 10)

Круги

В случае эллипса передается описывающая его прямоугольная область:

pygame.draw.ellipse(sc, GREEN, (10, 50, 280, 100))

Эллипс

pi = 3.14 pygame.draw.arc(sc, WHITE, (10, 50, 280, 100), 0, pi) pygame.draw.arc(sc, PINK, (50, 30, 200, 150), pi, 2*pi, 3)

Дуги

Указывается прямоугольник, описывающий эллипс, из которого вырезается дуга. Четвертый и пятый аргументы – начало и конец дуги, выраженные в радианах. Нулевая точка справа.

Практическая работа. Анимация

На данном этапе мы уже готовы создать анимацию. Никакого движения объектов на экране монитора нет. Просто от кадра к кадру изменяются цвета пикселей экрана. Например, пиксель с координатами (10, 10) светится синим цветом, в следующем кадре синим загорается пиксель (11, 11), в то время как (10, 10) становится таким же как фон. В следующем кадре синей будет только точка (12, 12) и так далее. При этом человеку будет казаться, что синяя точка движется по экрану по диагонали.

Суть алгоритма в следующем. Берем фигуру. Рисуем ее на поверхности. Обновляем главное окно, человек видит картинку. Стираем фигуру. Рисуем ее с небольшим смещением от первоначальной позиции. Снова обновляем окно и так далее.

Как "стереть" старую фигуру? Для этого используется метод fill() объекта Surface . В качестве аргумента передается цвет, т. е. фон можно сделать любым, а не только черным, который задан по-умолчанию.

Ниже в качестве примера приводится код анимации круга. Объект появляется с левой стороны, доходит до правой, исчезает за ней. После этого снова появляется слева. Ваша задача написать код анимации квадрата, который перемещается от левой границе к правой, касается ее, но не исчезает за ней. После этого возвращается назад – от правой границы к левой, касается ее, опять двигается вправо. Циклы движения квадрата повторяются до завершения программы.

import pygame import sys FPS = 60 WIN_WIDTH = 400 WIN_HEIGHT = 100 WHITE = (255, 255, 255) ORANGE = (255, 150, 100) clock = pygame.time.Clock() sc = pygame.display.set_mode((WIN_WIDTH, WIN_HEIGHT)) # радиус будущего круга r = 30 # координаты круга # скрываем за левой границей x = 0 - r # выравнивание по центру по вертикали y = WIN_HEIGHT // 2 while 1: for i in pygame.event.get(): if i.type == pygame.QUIT: sys.exit() # заливаем фон sc.fill(WHITE) # рисуем круг pygame.draw.circle(sc, ORANGE, (x, y), r) # обновляем окно pygame.display.update() # Если круг полностью скрылся за правой границей, if x >= WIN_WIDTH + r: # перемещаем его за левую x = 0 - r else: # Если еще нет, # на следующей итерации цикла # круг отобразится немного правее x += 2 clock.tick(FPS)

Курс с примерами решений практических работ:
pdf-версия

X Скрыть Наверх

Pygame. Введение в разработку игр на Python

Pygame/Pygame.draw()

pygame.draw() — метод, используемый для создания геометрических объектов с возможностью изменения их параметров.

  • 1 Описание
  • 2 Создание фигур и их параметры
    • 2.1 Линии
    • 2.2 Прямоугольник
    • 2.3 Контур прямоугольника
    • 2.4 Многоугольник
    • 2.5 Круг/окружность
      • 2.5.1 Круг
      • 2.5.2 Окружность

      Описание [ ]

      Метод pygame.draw() используется для создания геометрических объектов с возможностью изменения их параметров.

      Создание фигур и их параметры [ ]

      Каждая фигура имеет собственные параметры.

      Pygame draw line

      Линии [ ]

      pygame.draw.line(root, (255, 255, 0), [100, 100], [200, 200], 3)
      • pygame.draw.line( — начало метода;
      • root — название окна игры;
      • (255, 255, 0) — цвет линии, RGB;
      • [100, 100] — координаты начала линии;
      • [200, 200] — координаты конца линии;
      • 3 — толщина линии.

      Прямоугольник [ ]

      Pygame

      pygame.draw.rect(root, (255, 255, 0), (100, 100, 100, 100))

      Pygame draw rect 2

      • pygame.draw.rect( — начало метода;
      • root — название окна игры;
      • (255, 255, 0) — цвет фигуры, RGB;
      • (100, 100, — положение фигуры по x и y;
      • 100, 100) — ширина и высота фигуры;
      • ) — конец метода.

      Контур прямоугольника [ ]

      pygame.draw.rect(root,blue, ( 0, 0), ( 1, 1), 3)
      • pygame.draw.rect( — начало метода;
      • root — название окна игры;
      • (255, 255, 0) — цвет контура, RGB;
      • (100, 100, — положение фигуры по x и y;
      • 100, 100) — ширина и высота фигуры;
      • 10 — толщина контура.

      Многоугольник [ ]

      Pygame

      pygame.draw.polygon(root, (255, 255, 0), [[100, 100], [200, 100], [200, 200], [100, 200]])
      • pygame.draw.polygon( — начало метода;
      • root — название окна игры;
      • (255, 255, 0) — цвет фигуры, RGB;
      • [ — начало списка вершин многоугольника;
      • [100,100] — координаты левой верхней вершины;
      • [200, 100] — координаты левой нижней вершины;
      • [200, 200] — координаты правой нижней вершины;
      • [200, 100] — координаты правой верхней вершины;
      • ] — конец списка вершин многоугольника;
      • ) — конец метода.

      Круг/окружность [ ]

      Pygame

      Круг [ ]
      pygame.draw.circle(root, (255, 255, 0), (150, 150), 100)

      Pygame

      • pygame.draw.circle( — начало метода;
      • root — название окна игры;
      • (255, 255, 0) — цвет круга;
      • (150, 150) — координаты центра круга;
      • 100 — радиус круга.
      Окружность [ ]
      pygame.draw.circle(root, (255, 255, 0), (150, 150), 100, 10)
      • pygame.draw.circle( — начало метода;
      • root — название окна игры;
      • (255, 255, 0) — цвет окружности;
      • (150, 150) — координаты центра окружности;
      • 100 — радиус окружности;
      • 10 — толщина контура окружности.

      Ссылки [ ]

      • Pygame documentation
      • Лаборатория линуксоида. Урок 3 — геометрические примитивы

      Добавляем легенду и рисуем геометрические фигуры на графиках

      Давайте предположим, что в наших координатных осях показаны два графика:

      import numpy as np import matplotlib.pyplot as plt fig = plt.figure(figsize=(7, 4)) ax = fig.add_subplot() ax.plot(np.arange(0, 5, 0.25)) ax.plot(np.arange(0, 10, 0.5)) plt.show()

      Если мы теперь попытаемся отобразить легенду – их краткое описание, с помощью метода:

      ax.legend()

      то увидим пустой квадратик в верхнем левом углу. Почему в нем ничего нет? Дело в том, что нам нужно для каждой линии (графика) добавить символьное имя. Это делается с помощью именованного параметра label:

      ax.plot(np.arange(0, 5, 0.25), label='line1') ax.plot(np.arange(0, 10, 0.5), label='line2')

      Теперь, при запуске программы мы видим следующее окно:

      Причем, легенда использует цвет и тип линии графика и если его изменить, например, так:

      ax.plot(np.arange(0, 5, 0.25), '--o', label='line1') ax.plot(np.arange(0, 10, 0.5), ':s', label='line2')

      то это автоматически приведет к изменению и в окне легенды:

      Также мы можем самостоятельно указывать метки линий при отображении легенды. Для этого достаточно передать список меток в метод legend():

      ax.legend(['1', '2'])

      Соответственно, параметры label в этом случае можно уже не прописывать.

      Наконец, в третьем варианте, мы можем указать список линий и меток при отображении информации в легенде:

      line1, = ax.plot(np.arange(0, 5, 0.25), '--o', label='line1') line2, = ax.plot(np.arange(0, 10, 0.5), ':s', label='line2') ax.legend((line2, line1), ['Линия 2', 'Линия 1'])

      Далее, с помощью параметра loc можно указывать расположение окна легенды в пределах координатных осей. Этот параметр может принимать следующие значения:

      ['best', 'upper right', 'upper left', 'lower left', 'lower right', 'right', 'center left', 'center right', 'lower center', 'upper center', 'center']

      Названия здесь говорят сами за себя. Например, можно записать:

      ax.legend((line2, line1), ['Линия 2', 'Линия 1'], loc='upper right')

      и легенда будет расположена в верхнем правом углу. Аналогично используются и другие значения.

      Если требуется более точное указание местоположения легенды, то можно воспользоваться параметром bbox_to_anchor и указать координаты информационного окна:

      ax.legend((line2, line1), ['Линия 2', 'Линия 1'], bbox_to_anchor=(0.5, 0.7))

      Здесь значения прописываются в диапазоне от 0 до 1 как доли от размеров координатных осей.

      Пакет matplotlib позволяет отображать формулы, записанные в формате TeX. Для этого описание нужно заключить между символами $ и использовать режим записи r (row – «сырая» строка без экранирования символов). Например, можно сформировать следующие подписи у графиков:

      ax.legend((line2, line1), [r'$f(x) = a \cdot b + c$', r'$f(x) = k \cdot x + b$'])

      В результате, увидим такое окно легенды:

      Причем, такую TeX-нотацию можно применять для любых текстовых элементов пакета matplotlib.

      Наконец, для оформления информации в окне легенды можно использовать следующие параметры:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *