OLE способ сделать drag & drop в wxPython - PullRequest
2 голосов
/ 24 января 2009

У меня есть приложение wxPython, которое работает в MS Windows, и я хотел бы, чтобы оно поддерживало перетаскивание между его экземплярами (чтобы пользователь три раза открывал мое приложение и перетаскивал данные из одного экземпляра в другой).

Простое перетаскивание в wxPython работает следующим образом:

  1. Пользователь инициирует перетаскивание : окно источника упаковывает необходимые данные в wx.DataObject (), создает новый wx.DropSource, устанавливает его данные и вызывает dropSource.DoDragDrop ()
  2. Пользователь удаляет данные в целевое окно. : Цель удаления вызывает библиотечную функцию GetData (), которая передает фактические данные в свой экземпляр wx.DataObject и, наконец, dataObject.GetData () распаковывает фактические данные.

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

  1. Пользователь инициирует перетаскивание : упакован только некоторый указатель на окно источника (некоторая функция или объект).
  2. Пользователь перетаскивает данные в целевое окно : отображается приятное диалоговое окно, которое спрашивает пользователя, какой режим перетаскивания он выбирает (например, перетаскивание только названия песни или названия песни и имени исполнителя или всего альбома перетаскиваемого исполнителя). ).
  3. Пользователи выбирают режим перетаскивания : Перетаскивание вызывает некоторую функцию для перетаскиваемого объекта данных, которая затем извлекает данные из источника перетаскивания и передает их в цель перетаскивания.

Сценарий моей мечты кажется выполнимым в MS Windows, но документы для wxWidgets и wxPython довольно сложны и неоднозначны. Не все классы wx.DataObject доступны в wxPython (только wx.PySimpleDataObject), поэтому я хотел бы, чтобы кто-то поделился своим опытом с таким подходом. Можно ли реализовать такое поведение в wxPython, не кодируя его непосредственно в winAPI?

EDIT: Тони Ружа дал ответ с рабочим примером перетаскивания, но это не совсем сценарий my dreams . Его код манипулирует данными, когда они отбрасываются ( HandleDrop () показывает всплывающее меню), но данные готовятся при инициации перетаскивания (в On_ElementDrag () ). В моем приложении должно быть три разных режима перетаскивания, и некоторые из них требуют много времени на подготовку данных. Вот почему я хочу отложить получение данных до того момента, когда пользователь отбросит данные и выберет (потенциально дорогостоящий) режим d & d.

А что касается защиты памяти - я хочу использовать механизмы OLE для межпроцессного взаимодействия, как это делает MS Office. Вы можете скопировать диаграмму Excel и вставить ее в MS-Word, где она будет вести себя как изображение (ну вроде). Так как это работает, я верю, что это можно сделать в winAPI. Я просто не знаю, смогу ли я написать код в wxPython.

Ответы [ 2 ]

3 голосов
/ 24 января 2009

Поскольку вы не можете использовать один из стандартных форматов данных для хранения ссылок на объекты Python, я бы порекомендовал вам использовать текстовый формат данных для хранения параметров, необходимых для ваших вызовов методов, а не делать новый формат данных. И вообще, было бы бесполезно передавать ссылку на объект из одного приложения в другое, так как данный объект не был бы доступен (помните, защита памяти?).

Вот простой пример для ваших требований:

import wx


class TestDropTarget(wx.TextDropTarget):
    def OnDropText(self, x, y, text):
        wx.GetApp().TopWindow.HandleDrop(text)

    def OnDragOver(self, x, y, d):
        return wx.DragCopy


class Test(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None)

        self.numbers = wx.ListCtrl(self, style = wx.LC_ICON | wx.LC_AUTOARRANGE)
        self.field = wx.TextCtrl(self)

        sizer = wx.FlexGridSizer(2, 2, 5, 5)
        sizer.AddGrowableCol(1)
        sizer.AddGrowableRow(0)
        self.SetSizer(sizer)
        sizer.Add(wx.StaticText(self, label="Drag from:"))
        sizer.Add(self.numbers, flag=wx.EXPAND)
        sizer.Add(wx.StaticText(self, label="Drag to:"), flag=wx.ALIGN_CENTER_VERTICAL)
        sizer.Add(self.field)

        for i in range(100):
            self.numbers.InsertStringItem(self.numbers.GetItemCount(), str(i))

        self.numbers.Bind(wx.EVT_LIST_BEGIN_DRAG, self.On_ElementDrag)
        self.field.SetDropTarget(TestDropTarget())

        menu_id1 = wx.NewId()
        menu_id2 = wx.NewId()
        self.menu = wx.Menu()
        self.menu.AppendItem(wx.MenuItem(self.menu, menu_id1, "Simple copy"))
        self.menu.AppendItem(wx.MenuItem(self.menu, menu_id2, "Mess with it"))
        self.Bind(wx.EVT_MENU, self.On_SimpleCopy, id=menu_id1)
        self.Bind(wx.EVT_MENU, self.On_MessWithIt, id=menu_id2)

    def On_ElementDrag(self, event):
        data = wx.TextDataObject(self.numbers.GetItemText(event.Index))
        source = wx.DropSource(self.numbers)
        source.SetData(data)
        source.DoDragDrop()

    def HandleDrop(self, text):
        self._text = text
        self.PopupMenu(self.menu)

    def On_SimpleCopy(self, event):
        self.field.Value = self._text

    def On_MessWithIt(self, event):
        self.field.Value = "<-%s->" % "".join([int(c)*c for c in self._text])


app = wx.PySimpleApp()
app.TopWindow = Test()
app.TopWindow.Show()
app.MainLoop()

Методы, такие как On_SimpleCopy и On_MessWithIt, выполняются после удаления, поэтому любые длительные операции, которые вы, возможно, захотите сделать, можно выполнять там на основе текстовых или некоторых других стандартных типов данных, передаваемых с помощью перетаскивания (в моем случае self._text) и посмотри ... нет OLE :)

0 голосов
/ 26 января 2009

Хорошо, кажется, что это не может быть сделано так, как я хотел.

Возможные решения:

  1. Передайте некоторые параметры в d & d и выполните некоторое межпроцессное взаимодействие самостоятельно, после того как пользователь сбросит данные в окне целевых процессов.
  2. Используйте DataObjectComposite для поддержки нескольких форматов перетаскивания и модификаторов клавиатуры для выбора текущего формата. Сценарий:
    1. Пользователь инициирует перетаскивание. Проверяется состояние CTRL, ALT и SHIFT, и в зависимости от этого выбирается формат d & d. DataObjectComposite создан и установил данные в выбранном формате.
    2. Пользователь удаляет данные в целевом окне. Цель удаления запрашивает удаленный объект DataObject о поддерживаемом формате и извлекает данные, зная в каком формате.

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

В любом случае - Тони, спасибо за ваш ответ! Немного поигрался с ним, и это заставило меня задуматься о том, чтобы изменить свое отношение к проблеме.

...