На этом занятии продолжим
говорить о графике и рассмотрим такую вещь как clipping или, другими
словами, ограничение области рисования для контекста устройства (device context – DC). Этот
инструмент, во-первых, позволяет ускорять работу с графикой, т.к. мы можем
попросту сказать: что нужно рисовать, а что нет. И, во-вторых, с его помощью
можно получать некоторые дополнительные визуальные эффекты. Но, обо всем
поподробнее.
В самом простом случае
мы можем создать прямоугольную ограничивающую область с помощью метода:
DC.SetClippingRegion(self,
x, y, width, height)
И после
рисования обязательно нужно вызвать метод для уничтожения этого региона:
DC.DestroyClippingRegion(self)
Давайте для нашего
примера с рисованием линий из центра клиентской области:
import wx
import math
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)
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
app = wx.App()
frame = MyFrame(None, 'wxPython')
frame.Show()
app.MainLoop()
Создадим вот
такой прямоугольный регион рисования:
dc.SetClippingRegion(20, 20, 200, 100)
а в конце метода
OnPaint уничтожим его:
dc.DestroyClippingRegion()
При запуске
программы увидим такую картину:
Видите,
рисование было ограничено нашим регионом, за его пределами все осталось без
изменений – стандартный фон окна. Причем, скорость работы программы также
увеличилась, так как стала меньше площадь прорисовки графических объектов.
Но в wxPython можно создавать
регионы и на основе полигонов с помощью метода:
DC.SetDeviceClippingRegion(self,
region)
Здесь region – это объект
класса wx.Region, который определяет форму региона. Давайте его зададим набором
вот таких точек:
points = (((0, 85), (75, 75), (100, 10), (125, 75), (200, 85),
(150, 125), (160, 190), (100, 150), (40, 190), (50, 125)))
И на их основе
создадим регион:
region = wx.Region(points)
Затем, вызывая метод:
dc.SetDeviceClippingRegion(region)
Получим вот
такое изображение:
Или, сделать
так. Вместо линий воспользуемся градиентной заливкой:
dc.GradientFillLinear((0, 10, 210, 200), '#FF0000', '#444444', wx.EAST)
и получим
красную звезду:
Операции над регионами
Класс wx.Region дает нам
дополнительные возможности создания еще более сложных форм посредством
операций:
объединения,
пересечения, вычитания и XOR (симметричного вычитания)
Чтобы было проще
это понять, возьмем прямоугольные регионы:
region1 = wx.Region(100, 20, 70, 70)
region2 = wx.Region(120, 40, 70, 70)
и, затем, первый
объединим со вторым:
Соответственно,
в качестве ограничивающей области рисования, выберем результат объединения, то
есть, region1:
dc.SetDeviceClippingRegion(region1)
При запуске
программы увидим следующее:
То есть, площади
двух наших регионов были объединены и результатом стала вот такая область более
сложной формы.
Теперь, давайте сделаем
пересечение этих регионов:
region1.Intersect(region2)
И, чтобы мы
видели начальные формы этих квадратов, перед ними нарисуем соответствующие
прямоугольники:
dc.SetBrush(wx.Brush('#ffffff'))
dc.DrawRectangle(100, 20, 70, 70)
dc.DrawRectangle(120, 40, 70, 70)
Запускаем
программу и видим следующее:
Результирующая
область была получена путем пересечения соответствующих прямоугольных областей.
Причем, результат был сохранен в первом регионе, т.к. мы из него вызывали метод
Intersect.
Далее, сделаем
вычитание из первого региона второго:
region1.Subtract(region2)
Получилась
вполне ожидаемая картина. Если сделать наоборот, из второго вычесть первый:
region2.Subtract(region1)
dc.SetDeviceClippingRegion(region2)
То все будет
выглядеть уже так:
Ну и последняя
операция XOR дает следующий
эффект:
Здесь точки,
относящиеся одновременно к обоим регионам – исключаются. Вот такими операциями
можно получать регионы самых разных форм.