2

我有在 MS Windows 上运行的 wxPython 应用程序,我希望它支持其实例之间的拖放(因此用户打开我的应用程序 3 次并将数据从一个实例拖到另一个实例)。

wxPython 中的简单拖放就是这样工作的:

  1. 用户发起拖动:源窗口在 wx.DataObject() 中打包必要的数据,创建新的 wx.DropSource,设置其数据并调用 dropSource.DoDragDrop()
  2. 用户将数据拖放到目标窗口:拖放目标调用库函数 GetData(),它将实际数据传输到其 wx.DataObject 实例,最后 - dataObject.GetData() 解包实际数据。

我想要一些更复杂的拖放功能,允许用户选择在他下降后拖动哪些数据。我的梦想
场景:

  1. 用户发起拖拽:只有一些指向源窗口的指针被打包(一些函数或对象)。
  2. 用户将数据拖放到目标窗口:显示漂亮的对话框,询问用户他选择了哪种拖放模式(例如 - 仅拖动歌曲标题,或歌曲标题和艺术家姓名或拖动艺术家的整个专辑)。
  3. 用户选择拖拽模式:Drop target 在拖拽的数据对象上调用一些函数,然后从拖拽源中取出数据并传送到drop target。

我梦想的场景在 MS Windows 中似乎是可行的,但是 wxWidgets 和 wxPython 的文档非常复杂且模棱两可。并非所有 wx.DataObject 类都在 wxPython 中可用(仅 wx.PySimpleDataObject),所以我希望有人分享他对这种方法的经验。这样的行为是否可以在 wxPython 中实现而无需直接在 winAPI 中进行编码?

编辑:Toni Ruža 通过工作拖放示例给出了答案,但这并不是我梦寐以求的场景。他的代码在数据被拖放时操作数据(HandleDrop()显示弹出菜单),但数据在启动拖动时准备好(在On_ElementDrag()中)。在我的应用程序中应该有三种不同的拖放模式,其中一些需要耗时的数据准备。这就是为什么我想将数据检索推迟到用户丢弃数据并选择(可能成本高昂)d&d 模式的那一刻。

对于内存保护问题 - 我想使用 OLE 机制进行进程间通信,就像 MS Office 一样。您可以复制 Excel 图表并将其粘贴到 MS-Word 中,它的行为就像图像(嗯,有点)。由于它有效,我相信它可以在 winAPI 中完成。我只是不知道我是否可以在 wxPython 中对其进行编码。

4

2 回答 2

3

由于您不能使用其中一种标准数据格式来存储对 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 :)

于 2009-01-24T19:59:58.243 回答
0

好吧,似乎无法按照我想要的方式完成。

可能的解决方案是:

  1. 在用户在目标进程窗口中删除数据后,在 d&d 中传递一些参数并自行进行一些进程间通信。
  2. 使用DataObjectComposite支持多种拖放格式和键盘修饰符来选择当前格式。设想:
    1. 用户启动拖动。检查 CTRL、ALT 和 SHIFT 的状态,并根据它选择 d&d 格式。DataObjectComposite 已创建,并已以所选格式设置数据。
    2. 用户在目标窗口中放置数据。放置目标向放置的 DataObject 询问支持的格式并检索数据,知道它是什么格式。

我选择解决方案2.,因为它不需要在进程之间进行手工通信,并且当用户只想拖动最简单的数据时,它可以让我避免不必要的数据检索。

无论如何 - 托尼,谢谢你的回答!玩了一下,它让我想到了 d&d 并改变了我解决问题的方法。

于 2009-01-26T14:40:39.423 回答