В этой части курса PyCairo, мы нарисуем несколько базовых примитивов. Мы воспользуемся операциями заливки и обводки, пунктир, окончаниями линий и их изгибы.
Линии
Линия является базовым векторным объектом. Для того, чтобы нарисовать линию, мы применим два метода. Начало линии определяется методом move_to() . Конец линии определяется методом line_to() .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
#!/usr/bin/python from gi.repository import Gtk, Gdk import cairo class MouseButtons: LEFT_BUTTON = 1 RIGHT_BUTTON = 3 class Example(Gtk.Window): def __init__(self): super(Example, self).__init__() self.init_ui() def init_ui(self): self.darea = Gtk.DrawingArea() self.darea.connect("draw", self.on_draw) self.darea.set_events(Gdk.EventMask.BUTTON_PRESS_MASK) self.add(self.darea) self.coords = [] self.darea.connect("button-press-event", self.on_button_press) self.set_title("Lines") self.resize(300, 200) self.set_position(Gtk.WindowPosition.CENTER) self.connect("delete-event", Gtk.main_quit) self.show_all() def on_draw(self, wid, cr): cr.set_source_rgb(0, 0, 0) cr.set_line_width(0.5) for i in self.coords: for j in self.coords: cr.move_to(i[0], i[1]) cr.line_to(j[0], j[1]) cr.stroke() del self.coords[:] def on_button_press(self, w, e): if e.type == Gdk.EventType.BUTTON_PRESS \ and e.button == MouseButtons.LEFT_BUTTON: self.coords.append([e.x, e.y]) if e.type == Gdk.EventType.BUTTON_PRESS \ and e.button == MouseButtons.RIGHT_BUTTON: self.darea.queue_draw() def main(): app = Example() Gtk.main() if __name__ == "__main__": main() |
В нашем примере мы в случайном порядке нажимаем левой кнопкой мыши в окне. Каждое нажатие сохраняется в списке. При нажатии правой кнопкой мыши в окне, все точки соединяются со всеми точками, указанными в списке. Повторное нажатие правой кнопкой мыши очищает окно.
1 2 3 4 |
class MouseButtons: LEFT_BUTTON = 1 RIGHT_BUTTON = 3 |
Документация GTK указывает на то, что левая кнопка мыши у нас находится под номером 1, правая кнопка мыши под номером 3. Мы создадим собственный класс, чтобы обозначить клавиши мыши собственными идентификаторами.
1 |
self.darea.set_events(Gdk.EventMask.BUTTON_PRESS_MASK) |
Некоторые действия не указаны по умолчанию, это касается и действий, вызванных нажатием мыши. Нам нужно указать действия, вызванные нажатием мыши при помощи метода the set_event() .
1 |
self.darea.connect("button-press-event", self.on_button_press) |
При помощи данного кода, мы получаем реакцию на действия, вызванные нажатием мыши.
1 2 |
cr.set_source_rgb(0, 0, 0) cr.set_line_width(0.5) |
Линии написаны черным цветом с толщиной 0.5 точки.
1 2 3 4 5 6 |
for i in self.coords: for j in self.coords: cr.move_to(i[0], i[1]) cr.line_to(j[0], j[1]) cr.stroke() |
Мы связываем каждую точку в списке со всеми остальными. Метод stroke() рисует указанную линию.
1 |
del self.coords[:] |
В конце все координаты удаляются, и мы можем приступить к новому объекту.
1 2 3 4 5 6 7 |
def on_button_press(self, w, e): if e.type == Gdk.EventType.BUTTON_PRESS \ and e.button == MouseButtons.LEFT_BUTTON: self.coords.append([e.x, e.y]) ... |
При нажатии левой клавиши мыши, мы добавляем координаты х и у в список self.coords .
1 2 3 4 |
if e.type == Gdk.EventType.BUTTON_PRESS \ and e.button == MouseButtons.RIGHT_BUTTON: self.darea.queue_draw() |
При нажатии правой кнопкой мыши, мы вызываем метод queue_draw() , который перерисовывает область рисования. Все точки теперь соединены с линиями.
Есть вопросы по Python?
На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!
Паблик VK
Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!
Заливка и Контуры
Операция мазков рисует контуры фигуры, а операция заливки заполняет пространство фигуры.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
#!/usr/bin/python from gi.repository import Gtk import cairo import math class Example(Gtk.Window): def __init__(self): super(Example, self).__init__() self.init_ui() def init_ui(self): darea = Gtk.DrawingArea() darea.connect("draw", self.on_draw) self.add(darea) self.set_title("Fill & stroke") self.resize(230, 150) self.set_position(Gtk.WindowPosition.CENTER) self.connect("delete-event", Gtk.main_quit) self.show_all() def on_draw(self, wid, cr): cr.set_line_width(9) cr.set_source_rgb(0.7, 0.2, 0.0) w, h = self.get_size() cr.translate(w/2, h/2) cr.arc(0, 0, 50, 0, 2*math.pi) cr.stroke_preserve() cr.set_source_rgb(0.3, 0.4, 0.6) cr.fill() def main(): app = Example() Gtk.main() if __name__ == "__main__": main() |
В данном примере мы нарисуем круг и зальем его сплошным цветом.
1 |
import math |
Данный модуль нужен для константы pi , которая применяется при рисовании круга.
1 2 |
cr.set_line_width(9) cr.set_source_rgb(0.7, 0.2, 0.0) |
Мы указываем ширину линии при помощи метода set_line_width() . При помощи метода set_source_rgb() мы применяем, в данном случае, темно-красный цвет.
1 |
w, h = self.get_size() |
Таким образом, мы указываем ширину и высоту окна. Нам нужны эти данные для указания центра круга в окне.
1 2 3 |
cr.translate(w/2, h/2) cr.arc(0, 0, 50, 0, 2*math.pi) cr.stroke_preserve() |
Нам нужно, чтобы наш круг находился строго по центру для этого используем метод translate(). Метод arc() добавляет новый путь окружности к контексту рисования Cairo. Наконец, метод stroke_preserve() описывает контур круга. В отличии от метода stroke() , он также сохраняет форму для будущего рисунка.
1 2 |
cr.set_source_rgb(0.3, 0.4, 0.6) cr.fill() |
Мы меняем цвет рисунка и выполняем заливку круга новым цветом при помощи метода fill() .
Пунктирные линии
Каждая линия может быть нарисована с различными пунктирными прочерками. Пунктир определяет стиль линии. Шаблон пунктира применяется при помощи модуля set_dash() . Шаблон задается списком тире, список является набором «плавающих» вариантов пунктира. С их помощью отображаются, либо же скрываются части шаблона пунктира.
Тот или иной пунктир указывается методом stroke() для создания линии. Если количество пунктирных линий указано как 0, то пунктирование отключается и линия будет сплошной. Если количество пунктирных линий указано как 1, то шаблон применяется с симметричным чередованием частей размера, в соответствии с указанным размером пунктирной линии.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
def on_draw(self, wid, cr): cr.set_source_rgba(0, 0, 0, 1) cr.set_line_width(2) cr.set_dash([4.0, 21.0, 2.0]) cr.move_to(40, 30) cr.line_to(250, 30) cr.stroke() cr.set_dash([14.0, 6.0]) cr.move_to(40, 50) cr.line_to(250, 50) cr.stroke() cr.set_dash([1.0]) cr.move_to(40, 70) cr.line_to(250, 70) cr.stroke() |
Мы рисуем три линии с тремя разными пунктирами.
1 |
cr.set_dash([4.0, 21.0, 2.0]) |
У нас есть шаблоны из трех чисел. С начала линии 4 точки отображаются, следующие 21 не отображаются, следующие 2 отображаются, затем следующие 4 точки не отображаются, следующие 21 точка отображается и следующие 2 точки не отображаются. Данный шаблон применяется от начала и до конца линии.
1 |
cr.set_dash([14.0, 6.0]) |
В данном шаблоне 14 точек отображаются, затем следующие 6 точек не отображаются
1 |
cr.set_dash([1.0]) |
Мы создали симметричный шаблон пунктирной линии с чередующимися отображаемыми и не отображаемыми точками.
Окончания линий
Окончания линий означают конечную точку нашей линии.
- cairo.LINE_CAP_BUTT
- cairo.LINE_CAP_ROUND
- cairo.LINE_CAP_SQUARE
В Cairo существует три разных окончания линий с разными стилями.
Линия с окончанием cairo.LINE_CAP_SQUARE имеет отличный от cairo.LINE_CAP_BUTT размер. Если линия имеет ширину х единиц, линия с окончанием cairo.LINE_CAP_SQUARE будет иметь ширину х\2 в начале и конце.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
def on_draw(self, wid, cr): cr.set_source_rgba(0, 0, 0, 1) cr.set_line_width(12) cr.set_line_cap(cairo.LINE_CAP_BUTT) cr.move_to(30, 50) cr.line_to(150, 50) cr.stroke() cr.set_line_cap(cairo.LINE_CAP_ROUND) cr.move_to(30, 90) cr.line_to(150, 90) cr.stroke() cr.set_line_cap(cairo.LINE_CAP_SQUARE) cr.move_to(30, 130) cr.line_to(150, 130) cr.stroke() cr.set_line_width(1.5) cr.move_to(30, 35) cr.line_to(30, 145) cr.stroke() cr.move_to(150, 35) cr.line_to(150, 145) cr.stroke() cr.move_to(155, 35) cr.line_to(155, 145) cr.stroke() |
В данном примере нарисованы три линии с тремя разными окончаниями. Это также наглядно демонстрирует разницу в размерах линий благодаря трем дополнительным тонким вертикальным линиям.
1 |
cr.set_line_width(12) |
Толщина наших линий будет 12 единиц. Ширина линии по умолчанию – 2 единицы.
1 2 3 4 |
cr.set_line_cap(cairo.LINE_CAP_ROUND) cr.move_to(30, 90) cr.line_to(150, 90) cr.stroke() |
Мы нарисовали горизонтальную линию с окончанием cairo.LINE_CAP_ROUND .
1 2 3 4 5 |
cr.set_line_width(1.5) cr.move_to(30, 35) cr.line_to(30, 145) cr.stroke() |
Одна из трех вертикальных линий используется для демонстрации разниц в размерах.
Изгибы линий
Линии могут пересекаться в трех различных стилях.
- cairo.LINE_JOIN_MITER
- cairo.LINE_JOIN_BEVEL
- cairo.LINE_JOIN_ROUND
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
def on_draw(self, wid, cr): cr.set_line_width(14) cr.rectangle(30, 30, 100, 100) cr.set_line_join(cairo.LINE_JOIN_MITER) cr.stroke() cr.rectangle(160, 30, 100, 100) cr.set_line_join(cairo.LINE_JOIN_BEVEL) cr.stroke() cr.rectangle(100, 160, 100, 100) cr.set_line_join(cairo.LINE_JOIN_ROUND) cr.stroke() |
В данном примере мы нарисуем три толстых прямоугольника с различными изгибами линий
1 |
cr.set_line_width(14) |
Толщина линий 14 единиц.
1 2 3 |
cr.rectangle(30, 30, 100, 100) cr.set_line_join(cairo.LINE_JOIN_MITER) cr.stroke() |
Мы нарисовали прямоугольник со стилем изгиба линий cairo.LINE_JOIN_MITER.
Кривая Безье
Кривая Безье представлена в виде кривых линий, которые указаны в соответствии с определенными математическими формулами. Математический метод расчет кривых был разработан французским математиком Пьером Безье в конце 1960-х для производства автомобилей Рено.
1 |
curve_to(x1, y1, x2, y2, x3, y3) |
Метод curve_to() добавляет кубический сплайн в указанный путь. Параметры координат х и у первой контрольной точки, координаты х и у для второй контрольной точки и координаты х и у в конце кривой.
1 2 3 4 5 |
def on_draw(self, wid, cr): cr.move_to(20, 40) cr.curve_to(320, 200, 330, 110, 450, 40) cr.stroke() |
В данном примере кривая Безье написана с применением метода curve_to().
Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.
E-mail: vasile.buldumac@ati.utm.md
Образование
Universitatea Tehnică a Moldovei (utm.md)
- 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»