wxPython 中进行拖放的 OLE 方式

发布于 2024-07-12 13:53:37 字数 1339 浏览 7 评论 0原文

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

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 中编码吗?

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

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

I have wxPython app which is running on MS Windows and I'd like it to support drag&drop between its instances (so the user opens my app 3 times and drags data from one instance to another).

The simple drag&drop in wxPython works that way:

  1. User initiates drag: The source window packs necessary data in wx.DataObject(), creates new wx.DropSource, sets its data and calls dropSource.DoDragDrop()
  2. User drops data onto target window: The drop target calls library function GetData() which transfers actual data to its wx.DataObject instance and finally - dataObject.GetData() unpacks the actual data.

I'd like to have some more sophisticated drag&drop which would allow user to choose what data is dragged after he drops.
Scenario of my dreams:

  1. User initiates drag: Only some pointer to the source window is packed (some function or object).
  2. User drops data onto target window: Nice dialog is displayed which asks user which drag&drop mode he chooses (like - dragging only song title, or song title and the artists name or whole album of the dragged artist).
  3. Users chooses drag&drop mode: Drop target calls some function on the dragged data object, which then retrieves data from the drag source and transfers it to the drop target.

The scenario of my dreams seems doable in MS Windows, but the docs for wxWidgets and wxPython are pretty complex and ambigious. Not all wx.DataObject classes are available in wxPython (only wx.PySimpleDataObject), so I'd like someone to share his experience with such approach. Can such behaviour be implemented in wxPython without having to code it directly in winAPI?

EDIT:
Toni Ruža gave an answer with working drag&drop example, but that's not exactly the scenario of my dreams. His code manipulates data when it's dropped (the HandleDrop() shows popup menu), but data is prepared when drag is initiated (in On_ElementDrag()). In my application there should be three different drag&drop modes, and some of them require time-consuming data preparation. That's why I want to postpone data retrieval to the moment user drops data and chooses (potentially costly) d&d mode.

And for memory protection issue - I want to use OLE mechanisms for inter-process communication, like MS Office does. You can copy Excel diagram and paste it into MS-Word where it will behave like an image (well, sort of). Since it works I believe it can be done in winAPI. I just don't know if I can code it in wxPython.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

顾铮苏瑾 2024-07-19 13:53:37

由于您不能使用 标准 数据格式之一要存储对 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 这样的方法在拖放后执行,因此您可能想要执行的任何冗长操作都可以根据您通过拖动传输的文本或其他标准类型的数据来执行(自._text 在我的例子中),然后看...没有 OLE :)

Since you can't use one of the standard data formats to store references to python objects I would recommend you use a text data format for storing the parameters you need for your method calls rather than making a new data format. And anyway, it would be no good to pass a reference to an object from one app to another as the object in question would not be accessible (remember memory protection?).

Here is a simple example for your requirements:

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()

Methods like On_SimpleCopy and On_MessWithIt get executed after the drop so any lengthy operations you might want to do you can do there based on the textual or some other standard type of data you transfered with the drag (self._text in my case), and look... no OLE :)

清风无影 2024-07-19 13:53:37

好吧,看来还是不能按照我想要的方式去做。

可能的解决方案是:

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

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

不管怎样 - 托尼,谢谢你的回答! 稍微玩了一下,它让我想到了 d&d 并改变了我解决问题的方法。

Ok, it seems that it can't be done the way I wanted it.

Possible solutions are:

  1. Pass some parameters in d&d and do some inter-process communication on your own, after user drops data in target processes window.
  2. Use DataObjectComposite to support multiple drag&drop formats and keyboard modifiers to choose current format. Scenario:
    1. User initiates drag. State of CTRL, ALT and SHIFT is checked, and depending on it the d&d format is selected. DataObjectComposite is created, and has set data in chosen format.
    2. User drops data in target window. Drop target asks dropped DataObject for supported format and retrieves data, knowing what format it is in.

I'm choosing the solution 2., because it doesn't require hand crafting communication between processes and it allows me to avoid unnecessary data retrieval when user wants to drag only the simplest data.

Anyway - Toni, thanks for your answer! Played with it a little and it made me think of d&d and of changing my approach to the problem.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文