【问题标题】:pygtk gtk.Builder.connect_signals onto multiple objects?pygtk gtk.Builder.connect_signals 到多个对象?
【发布时间】:2011-01-09 05:07:20
【问题描述】:

我正在将一些代码从使用 libglade 更新到 GtkBuilder,这应该是未来的方式。

通过gtk.glade,您可以反复调用glade_xml.signal_autoconnect(...),将信号连接到程序中不同窗口对应的不同类的对象上。然而Builder.connect_signals 似乎只工作一次,并且(因此)对传入的第一个类中未定义的任何处理程序发出警告。

我意识到我可以手动连接它们,但这似乎有点费力。 (或者就此而言,我可以使用一些 getattr 黑客技术让它通过代理将它们连接到所有对象......)

没有跨多个对象连接处理程序的功能是一个错误吗?还是我错过了什么?

其他人也有类似的问题http://www.gtkforums.com/about1514.html 我认为这意味着无法完成。

【问题讨论】:

    标签: python gtk pygtk glade gtkbuilder


    【解决方案1】:

    这是我目前拥有的。随意使用它,或提出更好的建议:

    class HandlerFinder(object):
        """Searches for handler implementations across multiple objects.
        """
        # See <http://stackoverflow.com/questions/4637792> for why this is
        # necessary.
    
        def __init__(self, backing_objects):
            self.backing_objects = backing_objects
    
        def __getattr__(self, name):
            for o in self.backing_objects:
                if hasattr(o, name):
                    return getattr(o, name)
            else:
                raise AttributeError("%r not found on any of %r"
                    % (name, self.backing_objects))
    

    【讨论】:

    • 非常感谢,简单有效! :)
    【解决方案2】:

    一段时间以来,我一直在寻找解决方案,发现可以通过将所有处理程序的 dict 传递给 connect_signals 来完成。

    inspect 模块可以提取方法使用 inspect.getmembers(instance, predicate=inspect.ismethod 然后可以使用d.update(d3) 将它们连接到字典中,注意重复的函数,例如on_delete

    示例代码:

    import inspect
    ...    
    handlers = {}
    for c in [win2, win3, win4, self]:  # self is the main window
        methods = inspect.getmembers(c, predicate=inspect.ismethod)
        handlers.update(methods)
    builder.connect_signals(handlers)
    

    这不会获取使用@alias 声明的别名方法名称。有关如何执行此操作的示例,请参阅 Builder.py 的代码,地址为 def dict_from_callback_obj

    【讨论】:

      【解决方案3】:

      我只是一个新手,但这就是我所做的,也许它可以激发灵感;-)

      我从“控件”实例化主要组件并传递构建器对象,以便实例化的对象可以使用任何构建器对象(例如 mainwindow)或添加到构建器(aboutDialog 示例)。我还传递了一个字典 (dic),其中每个组件都向其中添加了“信号”。
      然后执行“connect_signals(dic)”。
      当然,当我需要将用户参数传递给回调方法时,我需要做一些手动信号连接,但这些很少。

      #modules.control.py
      class Control:
      
          def __init__(self):
      
              # Load the builder obj
              guibuilder = gtk.Builder()
              guibuilder.add_from_file("gui/mainwindow.ui")
              # Create a dictionnary to store signal from loaded components
              dic = {}
      
              # Instanciate the components...
              aboutdialog = modules.aboutdialog.AboutDialog(guibuilder, dic)           
              mainwin = modules.mainwindow.MainWindow(guibuilder, dic, self)
              ...
      
              guibuilder.connect_signals(dic)
              del dic
      
      
      #modules/aboutdialog.py
      class AboutDialog:
      
          def __init__(self, builder, dic):
              dic["on_OpenAboutWindow_activate"] = self.on_OpenAboutWindow_activate
              self.builder = builder
      
          def on_OpenAboutWindow_activate(self, menu_item):
              self.builder.add_from_file("gui/aboutdialog.ui")
              self.aboutdialog = self.builder.get_object("aboutdialog")
              self.aboutdialog.run()
      
              self.aboutdialog.destroy()
      
      #modules/mainwindow.py
      class MainWindow:
      
          def __init__(self, builder, dic, controller):
      
              self.control = controller
      
              # get gui xml and/or signals
              dic["on_file_new_activate"] = self.control.newFile
              dic["on_file_open_activate"] = self.control.openFile
              dic["on_file_save_activate"] = self.control.saveFile
              dic["on_file_close_activate"] = self.control.closeFile
              ...
      
              # get needed gui objects
              self.mainWindow = builder.get_object("mainWindow")
              ...
      

      编辑:将信号自动附加到回调的替代方法:
      未经测试的代码

      def start_element(name, attrs):
          if name == "signal":
              if attrs["handler"]:
                  handler = attrs["handler"]
                  #Insert code to verify if handler is part of the collection
                  #we want.
                  self.handlerList.append(handler)
      
      def extractSignals(uiFile)
          import xml.parsers.expat
          p = xml.parsers.expat.ParserCreate()
          p.StartElementHandler = self.start_element
          p.ParseFile(uiFile)
      
      self.handlerList = []
      extractSignals(uiFile)
      
      for handler in handlerList:
          dic[handler] = eval(''. join(["self.", handler, "_cb"]))
      

      【讨论】:

      • 感谢您的回答。我可以看到这会起作用,但它会重复处理程序名称,这正是我想要避免的:)
      • 我通过尝试回答来学习。我将尝试的东西...用一个约定来命名我的处理程序,该约定标识哪个类需要它。然后解析 ui 文件以找到所需的处理程序,或者如果 ui 文件特定于类,则它可以是所有处理程序。我的答案中添加了代码片段。不确定这是否对您有帮助,但我肯定会从中学习。 :-)
      【解决方案4】:
      builder.connect_signals
      ({ 
         "on_window_destroy" : gtk.main_quit, 
         "on_buttonQuit_clicked" : gtk.main_quit 
      })
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-30
        • 2019-02-22
        • 2021-07-14
        • 1970-01-01
        • 2019-02-06
        • 1970-01-01
        相关资源
        最近更新 更多