wxPython:尝试制作停靠窗口,绑定父级EVT_ACTIVATE导致无法调整大小

0 投票
2 回答
1709 浏览
提问于 2025-04-17 19:34

我正在尝试制作一个简单的停靠框架,它可以停靠在一个父窗口上,并跟随父窗口移动。目前我有以下代码:

class DockingFrame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, style=wx.CAPTION)

        parent.Bind(wx.EVT_MOVE, self.OnParentMove)
        parent.Bind(wx.EVT_ACTIVATE, self.OnParentActivate)
        parent.Bind(wx.EVT_SHOW, self.OnParentShow)

    def OnParentMove(self, moveEvent):
        print "Docked frame parent moved"
        pr = positioning.position(
            self.Rect,
            my='right_top', at='left_top', of=self.Parent.Rect)
        self.Move(pr.top_left)
        moveEvent.Skip()

    def OnParentActivate(self, event):
        print "Docked frame parent activated"
        self.Raise()
        event.Skip()

    def OnParentShow(self, event):
        print "Docked frame parent showed"
        self.Show(event.GetShow())
        event.Skip()

class MainFrame(wx.Frame):
    def __init__(self, title):
        wx.Frame.__init__(self, None, title=title)

        self.info_frame = DockingFrame(self)

        self.Show(True)

这个代码的效果是,当我移动父窗口时,停靠的窗口也会跟着移动;而且当我点击父窗口时,停靠窗口会自动抬起来。不过,这样做严重影响了父窗口的功能。我无法再关闭或调整父窗口的大小,每次点击父窗口时,都会出现很多“停靠框架父窗口被激活”的消息。看起来我对wxPython的一些基本概念理解得不太对。到底发生了什么,我该怎么解决呢?

我注意到aui似乎支持停靠功能,但相关文档很少,所以我还没有尝试过。如果有人能提供一个简单的代码示例,教我如何用aui让一个Frame停靠到另一个Frame上,我也可以尝试这种方法。

另外,我在这个应用中还使用了pygametwisted,这可能会有影响,也可能没有……

2 个回答

0

这个技巧在于,EVT_ACTIVATE 事件在激活和取消激活时都会触发。下面的代码就能正常工作。如果父窗口被激活,那么这个停靠窗口就会自己抬起来,并且紧接着会把父窗口也抬起来。

class DockingFrame(wx.Frame):
    def __init__(self, parent):
        self.handleParentActiveState = 'noTriggers'

        wx.Frame.__init__(self, parent, title="Last Hand", style=wx.CAPTION)

        parent.Bind(wx.EVT_MOVE, self.OnParentMove)
        parent.Bind(wx.EVT_ACTIVATE, self.OnParentActivate)
        self.Bind(wx.EVT_ACTIVATE, self.OnActivate)
        parent.Bind(wx.EVT_SHOW, self.OnParentShow)        

    def OnActivate(self, event):
        event.Skip()
        if not event.GetActive():
            return

        if self.handleParentActiveState == 'activateParentNext':
            self.handleParentActiveState = 'resetTriggers'
            self.Parent.Raise()

    def OnParentActivate(self, event):
        event.Skip()
        if not event.GetActive():
            return

        if self.handleParentActiveState == 'noTriggers':
            self.handleParentActiveState = 'activateParentNext'
            self.Raise()
        elif self.handleParentActiveState == 'resetTriggers':
            self.handleParentActiveState = 'noTriggers'

    def OnParentMove(self, moveEvent):
        pr = positioning.position(
            self.Rect,
            my='right_top', at='left_top', of=self.Parent.Rect)
        self.Move(pr.top_left)
        moveEvent.Skip()

    def OnParentShow(self, event):
        event.Skip()
        self.Show(event.GetShow())
2

当然,最简单的方法就是直接使用 wx.FRAME_FLOAT_ON_PARENT 这个样式...

class DockingFrame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, title="Last Hand",
                          style=wx.CAPTION  | wx.FRAME_FLOAT_ON_PARENT)

        parent.Bind(wx.EVT_MOVE, self.OnParentMove)
        parent.Bind(wx.EVT_SHOW, self.OnParentShow)

    def SnapToParent(self):
        print "*Snapping to parent"
        pr = positioning.position(
            self.Rect,
            my='right_top', at='left_top', of=self.Parent.Rect)
        self.Move(pr.top_left)

    def OnParentMove(self, moveEvent):
        moveEvent.Skip()
        self.SnapToParent()

    def OnParentShow(self, event):
        event.Skip()
        print "Parent %s" % ("Hide", "Show")[event.GetShow()]
        self.Show(event.GetShow())

撰写回答