wxPython:在父对象中处理拖放 - 事件传播问题

0 投票
2 回答
1135 浏览
提问于 2025-04-15 16:51

我有一个使用wxPython的程序,想要实现拖动一组控件来重新排序的功能。每组控件都放在一个面板上,我希望这个面板对象能够处理拖放操作。

现在,如果我在面板上点击并拖动,是可以正常工作的,但如果我在面板内部的任何控件上点击,就不行了。这是因为我用来触发拖动的wx.EVT_LEFT_DOWN事件并不是一个命令事件,所以它不会传递到父面板上。

我能想到的解决办法就是把这个事件绑定到面板上每个控件的处理程序上。

但我觉得这样做不太优雅——要么我在创建每个子事件时都得明确绑定,这样会破坏封装性,要么面板要遍历所有子控件并进行绑定——这听起来有点危险,因为这些控件可能已经在用这个事件做其他事情。理想情况下,我希望面板上的控件不需要知道任何关于拖放的事情。

有没有人知道其他的解决方案?有没有什么命令事件可以用来开始拖动?或者有没有其他我没想到的办法?

2 个回答

1

在我看来,最好的方法是,面板应该绑定到控件的 wx.EVT_LEFT_DOWN 事件上,只有在按下特定的组合键,比如 Ctrl + Alt + D 时才进行拖动。

面板可以递归地处理这个事件,或者在面板中有一个函数,叫做 addControl,只有这样的控件会被绑定上这个事件。

你提到的“单个控件可能已经在用这个事件做其他事情”这个观点其实不成立,因为你要么可以用这个事件来拖动,要么不能,两者不能同时存在。

如果你希望在控件上拖动时能够拖动整个组,那么你必须重写这个事件,但如果只在特定的组合键或模式下进行拖动,就可以让子控件也保持它们自己的行为。

2

我知道这个回复有点晚了,但如果对某些人还有帮助,那就太好了:

我在我的相关问题的代码中做了这个:

import wx

app = wx.App(False)
d = {}

def wMouseDown(e):
    print "!!!", e.GetEventObject()

def MouseDown(e):   
    o           = e.GetEventObject()
    sx,sy       = panel.ScreenToClient(o.GetPositionTuple())
    dx,dy       = panel.ScreenToClient(wx.GetMousePosition())
    o._x,o._y   = (sx-dx, sy-dy)
    d['d'] = o

def MouseMove(e):
    try:
        if 'd' in d:
            o = d['d']
            x, y = wx.GetMousePosition()
            o.SetPosition(wx.Point(x+o._x,y+o._y))
    except: pass

def MouseUp(e):
    try:
        if 'd' in d: del d['d']
    except: pass

frame = wx.Frame(None, -1, 'simple.py')
panel = wx.Panel(frame)
box = wx.BoxSizer(wx.VERTICAL)
button1 = wx.Button(panel, -1, "foo")
box.Add(button1, 0, wx.ALL, 10)
button2 = wx.Button(panel, -1, "bar")
box.Add(button2, 0, wx.ALL, 10)

button1.Bind(wx.EVT_LEFT_DOWN, MouseDown)
button2.Bind(wx.EVT_LEFT_DOWN, MouseDown)

button1.Bind(wx.EVT_MOTION, MouseMove)
button2.Bind(wx.EVT_MOTION, MouseMove)

button1.Bind(wx.EVT_LEFT_UP, MouseUp)
button2.Bind(wx.EVT_LEFT_UP, MouseUp)

panel.Bind(wx.EVT_MOTION, MouseMove)
panel.Bind(wx.EVT_LEFT_UP, MouseUp)

panel.SetSizer(box)
panel.Layout()
frame.Show()

app.MainLoop()

撰写回答