pygtk gtk.Builder.connect_signals sur plusieurs objets?
-
08-10-2019 - |
Question
Je suis mise à jour du code de l'utilisation libglade à GtkBuilder, qui est censé être la voie de l'avenir.
Avec gtk.glade, vous pouvez appeler glade_xml.signal_autoconnect(...)
à plusieurs reprises pour des signaux de connexion sur des objets de différentes classes correspondant aux différentes fenêtres du programme. Cependant semble Builder.connect_signals
au travail qu'une seule fois, et (donc) pour donner des avertissements au sujet des gestionnaires qui ne sont pas définis dans la première classe qui est passé dans.
Je sais que je peux les connecter manuellement, mais cela semble un laborieux bits. (Ou pour cette matière que je pourrais utiliser une getattr pour carriole laisser les relier par un proxy pour tous les objets ...)
Est-ce un bug il n'y a pas de fonction de brancher des gestionnaires sur plusieurs objets? Ou suis-je manque quelque chose?
Quelqu'un d'autre a un problème similaire http://www.gtkforums.com/about1514.html que je suppose que ce moyen ne peut se faire.
La solution
Voici ce que j'ai actuellement. Ne hésitez pas à utiliser, ou de suggérer quelque chose de mieux:
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))
Autres conseils
Je suis à la recherche d'une solution à ce pendant un certain temps et a constaté que cela peut être fait en passant un dict de tous les gestionnaires à connect_signals
.
Le module peut inspecter extraire en utilisant des méthodes
inspect.getmembers(instance, predicate=inspect.ismethod
Ceux-ci peuvent alors être concaténés dans un dictionnaire en utilisant d.update(d3)
, en faisant attention pour les fonctions en double telles que on_delete
.
Exemple de 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)
Ce ne captera pas les noms des méthodes d'alias déclarés en utilisant @alias. Pour un exemple de la façon de le faire, voir le code pour Builder.py, à def dict_from_callback_obj
.
Je ne suis qu'un novice mais c'est ce que je fais, peut-être qu'il peut inspirer ;-)
J'instancier les composants principaux d'un « contrôle » et passer l'objet constructeur afin que l'objet instancié peut utiliser l'un des objets de constructeur (de mainWindow dans l'exemple) ou ajouter au constructeur (exemple aboutDialog). Je passe aussi un dictionnaire (CIVD) où chaque composant ajoute des « signaux » à elle.
Ensuite, les « connect_signals (CIVD) » est exécutée.
Bien sûr, je dois faire quelque signal manuel de connexion lorsque je dois passer des arguments de l'utilisateur à la méthode de rappel, mais ce sont quelques-uns.
#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 à l'auto attacher des signaux à callbacks:
code non testé
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"]))
builder.connect_signals
({
"on_window_destroy" : gtk.main_quit,
"on_buttonQuit_clicked" : gtk.main_quit
})