Виды примитивов, система координат, единицы измерений

На этом занятии рассмотрим основные графические примитивы в wxPython. И прежде чем что-либо изображать, нужно знать как направлена система координат контекста устройства. По умолчанию она выглядит так:

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

(10, 100), (50, 200)

то получим следующий отрезок:

И так для всех точек и всех графических примитивов. Рассмотрим их подробнее. С двумя из них мы уже немного познакомились на предыдущем занятии – это:

  • линия – метод DrawLine(self, x1, y1, x2, y2);
  • прямоугольник – метод DrawRectangle(self, x, y, width, height).

И реализуются они следующим образом:

import wx
 
class MyFrame(wx.Frame):
    def __init__(self, parent, title):
        super().__init__(parent, title=title, pos=(0, 0), size=(700, 400))
        self.Bind(wx.EVT_PAINT, self.OnPaint)
 
    def OnPaint(self, e):
        dc = wx.PaintDC(self)
 
        dc.DrawLine(10, 100, 50, 200)
        dc.DrawRectangle(50, 10, 200, 100)
 
app = wx.App()
frame = MyFrame(None, 'wxPython')
frame.Show()
app.MainLoop()

В обработчике события EVT_PAINT выполняется прорисовка графических элементов окна в клиентской области.

Помимо этих двух примитивов, часто используются следующие:

  • DrawArc – дуга;
  • DrawBitmap – картинка;
  • DrawCircle – круг;
  • DrawEllipse – эллипс;
  • DrawPoint – точка;
  • DrawPolygon – полигон (набор точек, соединенных линиями);
  • DrawSpline – сплайн;
  • DrawText – текст.

Полный их список, а также синтаксис, можно посмотреть на странице:

https://docs.wxpython.org/wx.DC.html

Они достаточно просты в использовании:

Все это, например, можно реализовать так:

    def OnPaint(self, e):
        dc = wx.PaintDC(self)
 
        dc.DrawLine(10, 100, 50, 200)
        dc.DrawRectangle(50, 10, 200, 100)
 
        dc.SetFont(wx.Font( wx.FontInfo(16) ))
 
        dc.DrawArc(300, 10, 350, 50, 325, 25); dc.DrawText("Arc", 370, 20)
        dc.DrawCircle(320, 150, 50); dc.DrawText("Circle", 300, 140)
        dc.DrawEllipse(400, 120, 120, 70); dc.DrawText("Ellipse", 420, 140)
 
        dc.DrawBitmap(wx.Bitmap("brush2.jpg"), 300, 250)
 
        dc.DrawPolygon(((130, 240), (180, 270), (280, 240), (320, 210), (140, 200)))
        dc.DrawText("Polygon", 150, 220)
 
        dc.DrawSpline(((440, 270), (480, 270), (485, 210), (525, 210)))
 
        dc.SetPen(wx.Pen('GREEN'))
        w, h = self.GetSize()
        for i in range(1000):
            x = random.randint(1, w - 1)
            y = random.randint(1, h - 1)
            dc.DrawPoint(x, y)

Вначале рисуем линию и прямоугольник. Для линии задаем начальные и конечные координаты, а у прямоугольника координату верхнего левого угла, ширину и высоту. Далее, устанавливаем размер шрифта для контекста dc в 16 пунктов. Методом DrawArc рисуем дугу: указываем начальную, конечную координаты и центр дуги. Затем, вызываем метод DrawText для отображения текста с начальными координатами (370, 20). Следующий метод DrawCircle рисует круг с центром (320, 150) и радиусом 50 пикселей. Затем, DrawEllipse отображает эллипс, вписанный в прямоугольник с координатами (400, 120) и (120, 70). Метод DrawBitmap рисует изображение brush2.jpg в координатах (300, 250). После этого, рисуем полигон (DrawPolygon) с набором точек, указанных в кортеже. Последняя и первая точка автоматически замыкаются линией. Следующий метод DrawSpline рисует ломаную в виде сплайна, проходящего через указанные координаты. Ну и, наконец, метод DrawPoint рисует 1000 точек со случайными координатами в пределах клиентской области окна. В результате получаем такие графические элементы:

Положение начала координат

Во всех наших примерах мы полагали, что начало координат находится в левом верхнем углу. Однако, при необходимости, мы можем его переместить и расположить в любой другой точке, например, в центре:

Это делается с помощью метода:

DC.SetDeviceOrigin(self, x, y)

где x, y – новое положение начала системы координат. Давайте в качестве примера расположим ее в центре клиентской области окна и нарисуем линии, исходящие из этого центра по кругу:

    def OnPaint(self, e):
        dc = wx.PaintDC(self)
 
        size_x, size_y = self.GetClientSize()
        dc.SetDeviceOrigin(size_x // 2, size_y // 2)
 
        radius = math.hypot(size_x / 2, size_y / 2)
        angle = 0
 
        while (angle < 2 * math.pi):
            x = round(radius * math.cos(angle))
            y = round(radius * math.sin(angle))
            dc.DrawLine((0, 0), (x, y))
            angle = angle + 2 * math.pi / 360

В результате, получим такое изображение:

Видите, все линии теперь исходят из центра окна, с начальной координатой всех линий (0, 0). Это как раз и показывает, что начало координат находится в центре.

Единицы измерения координат

По умолчанию единицами измерения координат являются пикселы, то есть, наборы точек, которые и составляют изображение на устройстве. Однако, их количество зависит от разрешения устройства отображения, например:

  • HD – 1280 x 720 точек;
  • Full HD – 1920 x 1080 точек.

Это значит, что линия в 100 пикселей на разном разрешении будет выглядеть с разным масштабом: чем более высокое разрешение, тем меньше будет ее визуальная длина. Но, мы можем это обойти и использовать другие единицы измерений, например, миллиметры. Для этого нужно вызвать метод:

DC. SetMapMode(mode)

и указать режим отображения:

  • wx.MM_TEXT – пикселы;
  • wx.MM_METRIC – миллиметры;
  • wx.MM_LOMETRIC – 1/10 миллиметра;
  • wx.MM_POINTS – пункт (1/72 дюйма);
  • wx.MM_TWIPS – 1/20 пункта (или 1/1440 дюйма).

Например, изобразим линию в 10 см в нашем окне. Для этого воспользуемся режимом wx.MM_METRIC и запишем обработчик OnPaint следующим образом:

    def OnPaint(self, e):
        dc = wx.PaintDC(self)
        dc.SetMapMode(wx.MM_METRIC)
 
        dc.DrawLine(20, 10, 120, 10)
 
        dc.SetMapMode(wx.MM_TEXT)
        dc.DrawBitmap(wx.Bitmap("ruler.jpg"), 30, 40)

Смотрите, здесь линия по горизонтали составляет 100 единиц, то есть, 100 мм, что равно 10 см. Ниже отображается линейка в реальном масштабе и, т.к. это просто изображение, то перед ее рисованием возвращаем режим в пикселы (wx.MM_TEXT). В итоге получим следующую картину:

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

Вот так в wxPython выполняется рисование графических примитивов.

Видео по теме