使用wxPython,如何从弹出菜单到调用窗口获取事件?

时间:2014-01-04 23:40:25

标签: python-2.7 wxpython wxwidgets

我一直在努力将弹出菜单中的数据恢复到另一个控件。我已经设置了一个自定义菜单类,从那里我抓住EVT_MENU,然后引发另一个自定义事件,这样我就可以从菜单中返回一些自定义数据,而不仅仅是菜单项id或文本。

我遇到的问题是我绑定到我正在创建的自定义事件的事件处理程序没有被调用。这是一个显示相关行为的简化示例。

import wx
import wx.lib.newevent

class Example(wx.Frame):
    def __init__(self, parent):
        super(Example, self).__init__(parent, wx.ID_ANY)
        self.panel = wx.Panel(self)
        self.panel.Bind(wx.EVT_CONTEXT_MENU, self._showMenu)

        self.Bind(RClickMenu.EVT_CHANGE, self._eventReciever)

    def _eventReciever(self, evt):
        print 'notified'

    def _showMenu(self, evt):
        pos = evt.GetPosition()
        pos = self.panel.ScreenToClient(pos)
        self.panel.PopupMenu(RClickMenu(), pos)

class RClickMenu(wx.Menu):
    ChangeEvent, EVT_CHANGE = wx.lib.newevent.NewEvent()
    vals = (('Item 1', 8),
            ('Item 2', None))

    def __init__(self):
        super(RClickMenu, self).__init__()
        for name, val in self.vals:
            item = self.Append(wx.ID_ANY, name)
            self.Bind(wx.EVT_MENU, self._notify, item)

    def _notify(self, event):
        item = self.FindItemById(event.GetId())
        text = item.GetText()
        for name, val in self.vals:
            if name == text:
                value = val
                break

        print 'do_notify'
        evt = self.ChangeEvent(value=value)
        wx.PostEvent(self, evt)

app = wx.App(False)
Example(None).Show()
app.MainLoop()

我所看到的是RClickMenu._notify正在被调用,但它引发的事件,RClickMenu.EVT_CHANGE从未在Example中被选中,因此从不调用Example._eventReciever。关于为什么的任何想法?

编辑:所以这似乎是一个育儿问题,其中RClickMenu或使用Panel.PopupMenu创建的弹出菜单在技术上不是Example的子代。我仍然宁愿能够在这个例子的本质上做一些事情,但是现在我只是创建了wx.Menu并在Example中填充它,并在那里捕获wx.EVT_MENU。不太理想,但它确实有效。

2 个答案:

答案 0 :(得分:0)

默认情况下,事件不会传播到父级,只会传播wxCommandEvent和派生类的对象。因此,要让另一个事件(例如您的自定义EVT_CHANGE)从菜单传播到其父框架,您需要设置event.m_propagationLevel,如果它由wxPython包装,或者调用ResumePropagation()

但最简单的解决方案可能是在父级中处理原始的EVT_MENU(这是一个命令事件并因此传播)。甚至完全抛弃事件并直接使用GetPopupMenuSelectionFromUser()

答案 1 :(得分:0)

您可以将调用对象作为参数传递给类实例化,如此。

import wx
import wx.lib.newevent

app_event_popup_item_activated, APP_EVENT_POPUP_ITEM_ACTIVATED = wx.lib.newevent.NewCommandEvent()

class Example(wx.Frame):

    def __init__(self, parent):
        super(Example, self).__init__(parent, wx.ID_ANY)

        self.panel = wx.Panel(self)
        self.panel.Bind(wx.EVT_CONTEXT_MENU, self._showMenu)
        self.Bind(APP_EVENT_POPUP_ITEM_ACTIVATED, self.on_popup_item_activated)


    def _showMenu(self, evt):
        pos = evt.GetPosition()
        pos = self.panel.ScreenToClient(pos)
        self.panel.PopupMenu(Popup(self), pos)        

    def on_popup_item_activated(self, event):
        print "Popup item clicked"

class Popup(wx.Menu):

    def __init__(self, parent):
        wx.Menu.__init__(self)
        self.parent = parent

        item = wx.MenuItem(self, wx.NewId(), "Click ME")
        self.AppendItem(item)

        self.Bind(wx.EVT_MENU, self.on_menu_item_activated, item)

    def on_menu_item_activated(self, event):
        post_event = app_event_popup_item_activated(APP_EVENT_POPUP_ITEM_ACTIVATED.typeId)
        wx.PostEvent(self.parent, post_event)

app = wx.App(False)
Example(None).Show()
app.MainLoop()

命令事件将传播到父级。即使父项以相同的方式设置父项,框架和面板也是父项和子项,我们传入的父项仍然可以直接接收命令事件。