views:

277

answers:

2

Hi, I'd like to implement drag&drop in wxPython that works in similar way that in WordPad/Eclipse etc. I mean the following:

when something is being dropped to WordPad, WordPad window is on top with focus and text is added. In Eclipse editor text is pasted, Eclipse window gains focus and is on top.

When I implement drag&drop using wxPython target window is not brought to front. I implemented drag&drop in similar way to (drag):

import wx

class DragFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None)
        self.tree = wx.TreeCtrl(self, wx.ID_ANY)
        root = self.tree.AddRoot("root item")
        self.tree.AppendItem(root, "child 1")
        self.tree.Bind(wx.EVT_TREE_BEGIN_DRAG, self.__onBeginDrag)        
    def __onBeginDrag(self, event):
        tdo = wx.PyTextDataObject(self.tree.GetItemText(event.GetItem()))
        dropSource = wx.DropSource(self.tree)
        dropSource.SetData(tdo)
        dropSource.DoDragDrop(True)

app = wx.PySimpleApp()
frame = DragFrame()
app.SetTopWindow(frame)
frame.Show()
app.MainLoop()


Second program (drop):

import wx
class TextDropTarget(wx.TextDropTarget):
    def __init__(self, obj):
        wx.TextDropTarget.__init__(self)
        self.obj = obj
    def OnDropText(self, x, y, data):
        self.obj.WriteText(data + '\n\n')
        wx.MessageBox("Error", "Error", style = wx.ICON_ERROR)

class DropFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None)
        text = wx.TextCtrl(self, wx.ID_ANY)
        text.SetDropTarget(TextDropTarget(text))

app = wx.PySimpleApp()
frame = DropFrame()
app.SetTopWindow(frame)
frame.Show()
app.MainLoop()

When you run both programs, place windows in the centre of the screen (part of drop window is visible), then drag a node from drag window to drop window - target window displays message box which isn't visible, target window is hidden behind source window.

How to implement drag&drop that will focus on the second (target) window? I've tried adding window.Show(), window.SetFocus(), even using some functions of WinAPI (through win32gui). I think there should be some standard way of doing this. What am I missing?

A: 

Wouldn't this work?

class DropFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None)
        text = wx.TextCtrl(self, wx.ID_ANY)
        self.SetFocus() # Set's the focus to this window, allowing it to receive keyboard input.
        text.SetDropTarget(TextDropTarget(text))

wx.Frame inherits from wx.Window, that has SetFocus(self).


I have just tested it and it works. Just moved SetFocus before SetDropTarget, as its a cleaner behavior.

voyager
but you are setting focus on creation ,what happens later it looses focus e.g. user goes to other window
Anurag Uniyal
Oddly enough, it worked...
voyager
+1  A: 

You need to do anything you want int DragOver method of DropTarget e.g. there you can raise and set focus on your window

sample working code for target

import wx
class TextDropTarget(wx.TextDropTarget):
    def __init__(self, obj, callback):
        wx.TextDropTarget.__init__(self)
        self.obj = obj
        self._callback = callback

    def OnDropText(self, x, y, data):
        self.obj.WriteText(data + '\n\n')
        wx.MessageBox("Error", "Error", style = wx.ICON_ERROR)

    def OnDragOver(self, *args):
        wx.CallAfter(self._callback)
        return wx.TextDropTarget.OnDragOver(self, *args)

class DropFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None)
        text = wx.TextCtrl(self, wx.ID_ANY)
        text.SetDropTarget(TextDropTarget(text, self._callback))

    def _callback(self):
        self.Raise()
        self.SetFocus()

app = wx.PySimpleApp()
frame = DropFrame()
app.SetTopWindow(frame)
frame.Show()
app.MainLoop()
Anurag Uniyal
This is not exactly what I wanted - this solution raises window OnDragOver, while I'd like to do this in OnDropText - when the text is dropped (like in WordPad). Unfortunately moving this to OnDropText doesn't work :(.
uhzzre
Ok, doing this with OnData works as I wanted :). Thanks for the tip :)
uhzzre