- PVSM.RU - https://www.pvsm.ru -
В небольшом цикле статей будет описано использование WxPython для решения вполне конкретной задачи по разработке пользовательского интерфейса, да еще и то, как сделать это решение универсальным. Туториал этот расчитан на тех, кто уже начал изучать эту библиотеку и хочет увидеть что-то более сложное и целостное, чем простейшие примеры (хотя начнется все с относительно простых вещей).

В этой части мы добавим поддерку Drag&Drop к нашему приложению и научим его таким образом создавать новые ноды.
Часть 1: Учимся рисовать [1]
Часть 2: Обработка событий мыши [2]
Часть 3: Продолжаем добавлять фичи + обработка клавиатуры [3]
Часть 4: Реализуем Drag&Drop [4]
Кому интересно, добро пожаловать под кат…
Поддержка Drag&Drop — штука полезная и популярная, но тут мы ее будем использовать не совсем по назначению. Мы таким образом будем создавать новые ноды. Работает это дело достаточно просто. Требуется создать объект класса «wx.TextDropTarget» и передать его в метод «SetDropTarget» класса «wx.Window» от которого наследуется наш канвас. Соответственно, в момент дропа будет вызван метод «wx.TextDropTarget.OnDropText», который нам и надо будет реализовать. Для теста, реализация этого класса будет выглядеть так:
class TextDropTarget(wx.TextDropTarget):
def __init__(self):
wx.TextDropTarget.__init__(self)
def OnDropText(self, x, y, data):
print x, y, data
Теперь при бросании текста в окно, в консоле будет печататься сообщение с позицией и самим текстом. Как вы могли догататься, для принятия файлов, существует похожий класс «wx.FileDropTarget», который работает аналогичным образом, ну или класс «wx.PyDropTarget», который умеет принимать все и вся.
Просто печатать текст не очень полезно, поэтому мы используем полученный текст, чтобы создавать ноды с ним. Но для начала надо немного изменить архитектуру и добавить фабрику нод (это пригодится на будущее). На данный момент — это будет очень простая фабрика:
class NodesFactory(object):
def __init__(self):
pass
def CreateNodeFromDescription(self, nodeDescription):
return SimpleTextBoxNode(text=nodeDescription)
которая просто создает экземплят «SimpleTextBoxNode», который является слегда продвинутым наследником «SimpleBoxNode»:
class SimpleTextBoxNode(SimpleBoxNode):
def __init__(self, **kwargs):
super(SimpleTextBoxNode, self).__init__(**kwargs)
self.text = kwargs.get("text", "No text")
def Render(self, gc):
super(SimpleTextBoxNode, self).Render(gc)
gc.DrawText(self.text, self.position[0]+10, self.position[1]+10)
который в свою очередь рендерит заданный текст поверх прямоугольной ноды.
Осталось к канвасу добавить метод, позволяющий добавлять новые ноды из описания:
def CreateNodeFromDescriptionAtPosition(self, nodeDescription, pos):
node = self._nodesFactory.CreateNodeFromDescription(nodeDescription)
if node:
node.position = pos
self._canvasObjects.append(node)
self.Render()
И немного модернизировать TextDropTarget, чтобы он вызывал этот метод при поступлении текста:
class TextDropTarget(wx.TextDropTarget):
def __init__(self, canvas):
wx.TextDropTarget.__init__(self)
self._canvas = canvas
def OnDropText(self, x, y, data):
print x, y, data
self._canvas.CreateNodeFromDescriptionAtPosition(data, [x, y])
И теперь мы можем создавать новые текстовые ноды, просто бросая фрагменты текста на канвас.
Выглядит все это вот так:
Код, как и всегда, можно найти в соответствующем коммите на GitHub'е [5].
Но кроме создания нод, с помощью Drag&Drop можно также организовать и копирование нод, причем очень легко. Если пользователь в момент начала перетаскивания ноды зажмет Ctrl, нам надо просто инициировать начало Drag&Drop'а и отдать описание ноды. А код создания ноды сделает для нас всю оставшуюся работу. Для инициации Drag&Drop'а мы добавим следующий код в обработчик нажатия левой кнопки мыши:
if evt.ControlDown() and self._objectUnderCursor.clonable:
text = self._objectUnderCursor.GetCloningNodeDescription()
data = wx.TextDataObject(text)
dropSource = wx.DropSource(self)
dropSource.SetData(data)
dropSource.DoDragDrop(wx.Drag_AllowMove)
Тут мы создаем источник Drag&Drop'а и отдаем ему описание, которое мы получили от ноды. Осталось реализовать метод «GetCloningNodeDescription» у ноды и все будет готово. Но сначала реализуем интерфейс:
class ClonableObject(CanvasObject):
def __init__(self, **kwargs):
super(ClonableObject, self).__init__(**kwargs)
self.clonable = True
def GetCloningNodeDescription(self):
"""
GetNodeDescription should return a dictionary that contains
all information required for cloning this node at another position
"""
raise NotImplementedError()
А теперь уже реализацию метода у ноды:
def GetCloningNodeDescription(self):
return self.text
которая просто отдает ее текст.
Текущая версия кода живет тут [6].
Ну и перед завершением 4ой части, добавим последнюю маленькую рюшечку. Сделаем так, чтобы ноды масштабировались под размер текста. Для этого мы немного изменим метод рендеринга текстовой ноды:
def Render(self, gc):
textDimensions = gc.GetTextExtent(self.text)
self.boundingBoxDimensions = [textDimensions[0]+20, textDimensions[1]+20]
super(SimpleTextBoxNode, self).Render(gc)
gc.DrawText(self.text, self.position[0]+10, self.position[1]+10)
Метод «GetTextExtent» в данном случае возвращает размер прямоугольника, который занимает текст. Соответственно перед рендерингом ноды, мы обновляем ее размеры так, чтобы она была на 10 пикселей больше текста с каждой стороны. Вот так теперь выглядит весь этот процесс:
Код живет в этом коммите на GitHub'е [7].
PS: Об опечатках пишите в личку.
Автор: Akson87
Источник [4]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/razrabotka/48108
Ссылки в тексте:
[1] Часть 1: Учимся рисовать: http://habrahabr.ru/post/201336/
[2] Часть 2: Обработка событий мыши: http://habrahabr.ru/post/201538/
[3] Часть 3: Продолжаем добавлять фичи + обработка клавиатуры: http://habrahabr.ru/post/201608/
[4] Часть 4: Реализуем Drag&Drop: http://habrahabr.ru/post/201784/
[5] коммите на GitHub'е: https://github.com/Akson/MoveMe/tree/a12b17abbf161de4560a7f0181a5fc5cca986ee1
[6] тут: https://github.com/Akson/MoveMe/tree/125a719450f1d87228d1c72993acd89c3fa290ac
[7] коммите на GitHub'е: https://github.com/Akson/MoveMe/tree/a0e5566ca05bf332ebdb0d214919a6334c2626c0
Нажмите здесь для печати.