【问题标题】:With wxPython, How can I get an event from a popup menu to the calling window?使用 wxPython,如何从弹出菜单中获取事件到调用窗口?
【发布时间】:2014-01-04 23:40:25
【问题描述】:

我一直致力于将弹出菜单中的数据返回到另一个控件。我已经设置了一个自定义菜单类,并从那里捕获 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._eventReciever。关于为什么的任何想法?

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

【问题讨论】:

    标签: python-2.7 wxpython wxwidgets


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      您可以像这样将调用对象作为参数传递给类实例化。

      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()
      

      命令事件将向上传播到父级。即使 parent 以同样的方式设置 parent,frame 和 panel 将是 parent 和 child,但我们传入的 parent 仍然可以直接接收命令事件。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多