- PVSM.RU - https://www.pvsm.ru -
В небольшом цикле статей будет описано использование WxPython для решения вполне конкретной задачи по разработке пользовательского интерфейса, да еще и то, как сделать это решение универсальным. Туториал этот расчитан на тех, кто уже начал изучать эту библиотеку и хочет увидеть что-то более сложное и целостное, чем простейшие примеры (хотя начнется все с относительно простых вещей).
В прошлой части мы реализовали обработку событий мыши, а теперь мы добавим еще одну фичу и реализуем обработку событий от клавиатуры.
Часть 1: Учимся рисовать [1]
Часть 2: Обработка событий мыши [2]
Кому интересно, добро пожаловать под кат…
В прошлый раз мы научились перемещать ноды, теперь пора учиться их удалять, да и не только их, но и любые объекты на канвасе. Правда для удаления, надо сначала указать, что мы хотим удалить, т.е. выделить объект, так что начнем мы не с удаления, а с выделения.
Для выделения обычно все используют одинарный клик, но, насколько я знаю, wxPython не имеет специального события одинарного клика, так что будем отслеживать его сами. Сделаем мы это простейшим способом: если пользователь надапил на левую кнопку мыши и отпустил ее над одним и тем же пикселем, значит это клик. Для этого нам надо запомнить, где пользователь нажал на кнопку (мы сохраним это в «self._lastLeftDownPos»), а при отпускании сравним и сохраним выделенный объект:
def OnMouseLeftUp(self, evt):
#Selection
if (self._lastLeftDownPos
and self._lastLeftDownPos[0] == evt.GetPosition()[0]
and self._lastLeftDownPos[1] == evt.GetPosition()[1]
and self._objectUnderCursor
and self._objectUnderCursor.selectable):
self._selectedObject = self._objectUnderCursor
self.Render()
Ну и чтобы пользователь видел, что же он там выделил, в код рендеринга канваса добавим следующий фрагмент:
if self._selectedObject:
gc.PushState()
self._selectedObject.RenderSelection(gc)
gc.PopState()
Все? Нет не все, внимательный читатель заметил, что мы проверяли, является ли объект selectable и вызывали новый метод RenderSelection, который должен быть у всех выделяемых объектов. Поэтому, по образу и подобию перемещения, мы добавим новый базовый класс от которого будут наследоваться все объекты, которые могут быть выделены:
class SelectableObject(CanvasObject):
def __init__(self, **kwargs):
super(SelectableObject, self).__init__(**kwargs)
self.selectable = True
def RenderSelection(self, gc):
"""
RenderHighlighting method should draw an
object with a selection border around it.
"""
raise NotImplementedError()
Кстати, раз уж наших базовых классов становится больше и все становится сложнее, теперь их конструкторы принимают сколько угодно параметров и передают их вверх по иерархии наследования в виде словаря, что и делает в данном случае строка «super(SelectableObject, self).__init__(**kwargs)».
Осталось лишь добавить к ноде вот такой код:
def RenderSelection(self, gc):
gc.SetBrush(wx.Brush('#888888', wx.TRANSPARENT))
gc.SetPen(wx.Pen('#CC0000', 3, wx.DOT))
gc.DrawRectangle(self.position[0]-2,
self.position[1]-2,
self.boundingBoxDimensions[0]+4,
self.boundingBoxDimensions[1]+4)
который рендерит выделение вокруг объекта и можно увидеть такой результат, если кликнуть на одну из нод:

Текущий код, как и всегда, можно найти в соответствующем коммите на GitHub'е [3].
Вот мы и добрались до удаления объектов. Для удаления нам надо отследить, когда пользователь нажимает кнопку Delete и собственно удалить элемент, если он удаляемый.
Для начала нам потребуется обработчик события нажатия на кнопки клавиатуры, который мы сделаем зарегистрируем так:
self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPress)
А сам обработчик будет выполнять такой код:
def OnKeyPress(self, evt):
if evt.GetKeyCode() == wx.WXK_DELETE:
if self._selectedObject and self._selectedObject.deletable:
self._selectedObject.Delete()
if self._selectedObject in self._canvasObjects:
self._canvasObjects.remove(self._selectedObject)
self._selectedObject = None
else:
evt.Skip()
#Update object under cursor
pos = self.CalcUnscrolledPosition(evt.GetPosition()).Get()
self._objectUnderCursor = self.FindObjectUnderPoint(pos)
self.Render()
Тут мы, в случае прихода события о нажатии кнопки с кодом wx.WXK_DELETE, проверяем, выделен ли объект и удаляемый ли он. Если есть такой, мы его красиво уведомляем о том, что пора бы ему на покой (вызовом Delete) и удалением из списка объектов на канвасе. evt.Skip() говорит системе, что надо вызывать остальные обработчики этого события зарегистрированные в других местах.
Теперь, уже традиционно, осталось добавить базовый класс для удаляемых объектов:
class DeletableObject(CanvasObject):
def __init__(self, **kwargs):
super(DeletableObject, self).__init__(**kwargs)
self.deletable = True
def Delete(self):
"""
Delete method is called when an object is deleted from a canvas.
"""
raise NotImplementedError()
И можно наслаждаться удалением объектов с канваса. Правда не очень долго, так как добавление объектов будет только в следующей части:)
Текущая версия кода живет в этом коммите на GitHub'е [4].
PS: Об опечатках пишите в личку.
Автор: Akson87
Источник [5]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/48091
Ссылки в тексте:
[1] Часть 1: Учимся рисовать: http://habrahabr.ru/post/201336/
[2] Часть 2: Обработка событий мыши: http://habrahabr.ru/post/201538/
[3] коммите на GitHub'е: https://github.com/Akson/MoveMe/tree/3d8616e281ec32b57efcd325dae49a3c5a495896
[4] этом коммите на GitHub'е: https://github.com/Akson/MoveMe/tree/f5b56b5a75c4d686563a49453a948cf30ae95664
[5] Источник: http://habrahabr.ru/post/201608/
Нажмите здесь для печати.