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).
我有wxPython应用程序,它运行在MS Windows上,我希望它支持在它的实例之间拖拽(这样用户打开我的应用程序3次,并将数据从一个实例拖到另一个实例)。
The simple drag&drop in wxPython works that way:
wxPython中简单的拖放操作是这样的:
- User initiates drag: The source window packs necessary data in wx.DataObject(), creates new wx.DropSource, sets its data and calls dropSource.DoDragDrop()
- 用户发起拖拽:源窗口在wx. dataobject()中包含必需的数据,创建新的wx。DropSource,设置数据并调用dropSource.DoDragDrop()
- 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.
- 用户将数据删除到目标窗口:drop目标调用库函数GetData(),将实际数据传输到它的wx。DataObject实例和最后- DataObject . getdata()解压缩实际数据。
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:
我想要一些更复杂的拖放操作,这样用户就可以选择在他掉下之后拖拽的数据。我的梦想的场景:
- User initiates drag: Only some pointer to the source window is packed (some function or object).
- 用户开始拖拽:只有一些指向源窗口的指针被打包(一些函数或对象)。
- 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).
- 用户将数据扔到目标窗口上:弹出对话框,询问用户选择的拖放模式(比如拖拽歌曲标题,或歌曲名称,以及艺术家姓名或拖拽艺术家的完整专辑)。
- 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.
- 用户选择拖放模式:Drop target调用拖动数据对象上的某个函数,然后从拖动源检索数据并将其传输到Drop目标。
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?
在MS Windows中,我的梦的场景似乎是可行的,但是wxWidgets和wxPython的文档相当复杂和复杂。并不是所有的天气。DataObject类可以在wxPython中使用(只有wx.PySimpleDataObject),所以我希望有人可以用这种方法来分享他的经验。这种行为可以在wxPython中实现,而不需要在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.
编辑:托尼Ruža给答案与工作进行拖拽的例子,但这并不是我的梦想的场景。当它被删除时,他的代码会操作数据(HandleDrop()显示弹出菜单),但是在开始拖拽时(On_ElementDrag())会准备数据。在我的应用程序中应该有三个不同的拖放模式,其中一些需要耗时的数据准备。这就是为什么我要将数据检索推迟到用户掉落数据的时刻,并选择(可能昂贵的)d&d模式。
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.
对于内存保护问题,我希望使用OLE机制进行进程间通信,就像Office一样。您可以复制Excel图表,并将其粘贴到MS-Word中,在那里它将表现得像一个图像(嗯,某种程度上)。我相信它可以在winAPI中完成。我只是不知道是否可以在wxPython中编码。
2 个解决方案
#1
3
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?).
由于您不能使用标准的数据格式来存储对python对象的引用,所以我建议您使用文本数据格式来存储方法调用所需的参数,而不是创建新的数据格式。无论如何,将一个应用程序的引用传递给另一个应用程序,因为这个对象是无法访问的(还记得内存保护吗?)
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 :)
像On_SimpleCopy和On_MessWithIt这样的方法会在删除之后执行,所以您可能想要做的任何冗长的操作都可以基于您通过拖动(self)传输的文本或其他标准类型的数据进行。在我的例子中),然后看…没有OLE:)
#2
0
Ok, it seems that it can't be done the way I wanted it.
好吧,看来我不可能按照我想要的方式去做。
Possible solutions are:
可能的解决方案是:
- Pass some parameters in d&d and do some inter-process communication on your own, after user drops data in target processes window.
- 在d&d中传递一些参数,在用户在目标进程窗口中删除数据后,自己进行一些进程间通信。
- Use DataObjectComposite to support multiple drag&drop formats and keyboard modifiers to choose current format. Scenario:
- 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.
- 用户启动阻力。检查了CTRL、ALT和SHIFT的状态,并根据它的不同选择了d&d格式。创建DataObjectComposite,并以选定的格式设置数据。
- User drops data in target window. Drop target asks dropped DataObject for supported format and retrieves data, knowing what format it is in.
- 用户在目标窗口中删除数据。Drop target请求删除DataObject以支持格式和检索数据,了解它的格式。
- 使用DataObjectComposite支持多个拖放格式和键盘修改器来选择当前格式。场景:用户启动阻力。检查了CTRL、ALT和SHIFT的状态,并根据它的不同选择了d&d格式。创建DataObjectComposite,并以选定的格式设置数据。用户在目标窗口中删除数据。Drop target请求删除DataObject以支持格式和检索数据,了解它的格式。
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.
我选择的是溶液2。因为它不需要手工制作过程之间的通信,它允许我在用户想要拖动最简单的数据时避免不必要的数据检索。
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.
不管怎样——托尼,谢谢你的回答!和它玩了一会儿,它让我想到了d&d,并改变了我对这个问题的看法。
#1
3
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?).
由于您不能使用标准的数据格式来存储对python对象的引用,所以我建议您使用文本数据格式来存储方法调用所需的参数,而不是创建新的数据格式。无论如何,将一个应用程序的引用传递给另一个应用程序,因为这个对象是无法访问的(还记得内存保护吗?)
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 :)
像On_SimpleCopy和On_MessWithIt这样的方法会在删除之后执行,所以您可能想要做的任何冗长的操作都可以基于您通过拖动(self)传输的文本或其他标准类型的数据进行。在我的例子中),然后看…没有OLE:)
#2
0
Ok, it seems that it can't be done the way I wanted it.
好吧,看来我不可能按照我想要的方式去做。
Possible solutions are:
可能的解决方案是:
- Pass some parameters in d&d and do some inter-process communication on your own, after user drops data in target processes window.
- 在d&d中传递一些参数,在用户在目标进程窗口中删除数据后,自己进行一些进程间通信。
- Use DataObjectComposite to support multiple drag&drop formats and keyboard modifiers to choose current format. Scenario:
- 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.
- 用户启动阻力。检查了CTRL、ALT和SHIFT的状态,并根据它的不同选择了d&d格式。创建DataObjectComposite,并以选定的格式设置数据。
- User drops data in target window. Drop target asks dropped DataObject for supported format and retrieves data, knowing what format it is in.
- 用户在目标窗口中删除数据。Drop target请求删除DataObject以支持格式和检索数据,了解它的格式。
- 使用DataObjectComposite支持多个拖放格式和键盘修改器来选择当前格式。场景:用户启动阻力。检查了CTRL、ALT和SHIFT的状态,并根据它的不同选择了d&d格式。创建DataObjectComposite,并以选定的格式设置数据。用户在目标窗口中删除数据。Drop target请求删除DataObject以支持格式和检索数据,了解它的格式。
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.
我选择的是溶液2。因为它不需要手工制作过程之间的通信,它允许我在用户想要拖动最简单的数据时避免不必要的数据检索。
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.
不管怎样——托尼,谢谢你的回答!和它玩了一会儿,它让我想到了d&d,并改变了我对这个问题的看法。