在wxPython中执行拖放操作的OLE方式

时间:2009-01-24 15:29:45

标签: python windows drag-and-drop wxpython ole

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

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

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

    我想要一些更复杂的拖放功能,这样用户就可以选择在之后拖动拖动的数据。
    我的梦想的情景:

    1. 用户启动拖动:只有一些指向源窗口的指针被打包(某些功能或对象)。
    2. 用户将数据删除到目标窗口:显示一个很好的对话框,询问用户选择哪种拖放模式(例如 - 只拖动歌曲标题,歌曲标题和艺术家名称或整个专辑拖拉的艺术家)。
    3. 用户选择拖放模式:删除目标会在拖动的数据对象上调用某些函数,然后从拖动源检索数据并将其传输到放置目标。
    4. 我梦想的场景似乎在MS Windows中可行,但是wxWidgets和wxPython的文档非常复杂和暧昧。并非所有的wx.DataObject类都可以在wxPython中使用(只有wx.PySimpleDataObject),所以我希望有人分享他使用这种方法的经验。这种行为可以在wxPython中实现,而不必直接在winAPI中编码吗?

      编辑: ToniRuža给出了一个工作拖放示例的答案,但这并不完全是 my dreams 的情景。他的代码在删除时操纵数据( HandleDrop()显示弹出菜单),但是在启动拖动时准备数据(在 On_ElementDrag()中)。在我的应用程序中,应该有三种不同的拖放模式,其中一些需要耗时的数据准备。这就是为什么我想推迟数据检索到用户丢弃数据并选择(可能代价高昂)d& d模式的时刻。

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

2 个答案:

答案 0 :(得分:3)

由于您不能使用standard数据格式之一来存储对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:)

答案 1 :(得分:0)

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

可能的解决方案是:

  1. 在用户在目标进程窗口中删除数据后,在d&amp; d中传递一些参数并自行进行一些进程间通信。
  2. 使用DataObjectComposite支持多种拖放格式和键盘修改器以选择当前格式。场景:
    1. 用户启动拖动。检查CTRL,ALT和SHIFT的状态,并根据它选择d&amp; d格式。创建了DataObjectComposite,并以所选格式设置数据。
    2. 用户在目标窗口中删除数据。删除目标要求删除DataObject以获取支持的格式并检索数据,了解它的格式。
  3. 我正在选择解决方案 2。,因为它不需要在流程之间进行手工制作通信,它允许我在用户只想拖动最简单的数据时避免不必要的数据检索。 / p>

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