pygtk gtk.Builder.connect_signals 到多个对象上?

发布于 2024-10-11 05:34:44 字数 490 浏览 10 评论 0原文

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

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

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

这是一个错误,没有功能可以跨多个对象连接处理程序吗?或者我错过了什么?

其他人也有类似的问题 http://www.gtkforums.com/about1514.html 我假设意味着这是不可能完成的。

I am updating some code from using libglade to GtkBuilder, which is supposed to be the way of the future.

With gtk.glade, you could call glade_xml.signal_autoconnect(...) repeatedly to connect signals onto objects of different classes corresponding to different windows in the program. However Builder.connect_signals seems to work only once, and (therefore) to give warnings about any handlers that aren't defined in the first class that's passed in.

I realize I can connect them manually but this seems a bit laborious. (Or for that matter I could use some getattr hackery to let it connect them through a proxy to all the objects...)

Is it a bug there's no function to hook up handlers across multiple objects? Or am I missing something?

Someone else has a similar problem http://www.gtkforums.com/about1514.html which I assume means this can't be done.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

初懵 2024-10-18 05:34:45

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

我从“控件”实例化主要组件并传递构建器对象,以便实例化对象可以使用任何构建器对象( 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"]))

I'm only a novice but this is what I do, maybe it can inspire;-)

I instantiate the major components from a 'control' and pass the builder object so that the instantiated object can make use of any of the builder objects (mainwindow in example) or add to the builder (aboutDialog example). I also pass a dictionary (dic) where each component adds "signals" to it.
Then the 'connect_signals(dic)' is executed.
Of course I need to do some manual signal connecting when I need to pass user arguments to the callback method, but those are few.

#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")
        ...

Edit: alternative to auto attach signals to callbacks:
Untested code

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"]))
空心↖ 2024-10-18 05:34:45
builder.connect_signals
({ 
   "on_window_destroy" : gtk.main_quit, 
   "on_buttonQuit_clicked" : gtk.main_quit 
})
builder.connect_signals
({ 
   "on_window_destroy" : gtk.main_quit, 
   "on_buttonQuit_clicked" : gtk.main_quit 
})
梓梦 2024-10-18 05:34:44

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

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

Here's what I currently have. Feel free to use it, or to suggest something better:

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))
野味少女 2024-10-18 05:34:44

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

检查模块可以使用提取方法
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 声明的别名方法名称。有关如何执行此操作的示例,请参阅 def dict_from_callback_obj 处的 Builder.py 代码。

I have been looking for a solution to this for some time and found that it can be done by passing a dict of all the handlers to connect_signals.

The inspect module can extract methods using
inspect.getmembers(instance, predicate=inspect.ismethod
These can then be concatenated into a dictionary using d.update(d3), watching out for duplicate functions such as on_delete.

Example code:

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)

This will not pick up alias method names declared using @alias. For an example of how to do that, see the code for Builder.py, at def dict_from_callback_obj.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文