accessing button handlers in multiple python modules

More
8 years 2 months ago #115462 by cmorley
You might look at stepconf and mulitfilebuilder.py.
Stepconf loads multiple GLADE files.
Now stepconf only uses one python files for call backs but I think that is by design rather then by obligation.

Chris M

Please Log in to join the conversation.

More
8 years 2 months ago #115524 by tripwire
Thanks for everyone's replies and suggestions.

It looked like the get_handlers() function would be the solution, because that function can be placed in each python module, but I couldn't get it to work because it seems the connect_signals() is the only place to actually connect the signals with handlers and I don't understand how get_handlers() helps with that.

But anyway, I did write a simpler version of the load_handlers() function in gscreen.py that gathers signal handlers from multiple python files and places them in a dictionary suitable for connect_signals(). I will post it later today...

Please Log in to join the conversation.

More
8 years 2 months ago #115552 by tripwire
Here is a working version of a single Glade (xml) file referencing 2 python modules for it's signal handlers. The "magic" function is mainly a simplification of the load_handlers() function in gscreen.py.

The Glade file:
Code:
<?xml version="1.0" encoding="UTF-8"?> <interface> <requires lib="gtk+" version="2.18"/> <!-- interface-naming-policy toplevel-contextual --> <object class="GtkWindow" id="window1"> <property name="can_focus">False</property> <signal name="destroy" handler="onDestroy" swapped="no"/> <child> <object class="GtkHBox" id="hbox1"> <property name="visible">True</property> <property name="can_focus">False</property> <child> <object class="GtkButton" id="button1"> <property name="label" translatable="yes">button w. local handler</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <signal name="pressed" handler="onButton1Pressed" swapped="no"/> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkButton" id="button2"> <property name="label" translatable="yes">button w. external handler</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <signal name="pressed" handler="onButton2Pressed" swapped="no"/> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">1</property> </packing> </child> <child> <object class="GtkLabel" id="label1"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="label" translatable="yes">label</property> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">2</property> </packing> </child> <child> <object class="GtkLabel" id="label2"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="label" translatable="yes">label</property> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">3</property> </packing> </child> </object> </child> </object> </interface>

The "main" python file:
Code:
#!/usr/bin/env python import gtk import external_module # trampoline and load_handlers are used for custom displays # (Copied from gscreen.py. I don't know how it works but seems necessary. Guru help?) # class Trampoline(object): def __init__(self,methods): self.methods = methods print("Trampoline instance") def __call__(self, *a, **kw): for m in self.methods: m(*a, **kw) class Handler: def onDestroy(self, *args): print("quit app") gtk.main_quit() def onButton1Pressed(self, button): print("Button1 Pressed") self.builder.get_object("label1").set_text("Button 1 pressed") # This function scans the given class object for signal handlers # and adds them to a (class-global) dictionary, which we'll pass to # connect_signals(). # # This allows us to add signal handlers from "self" as well as from classes in external python modules # def _load_handlers(self, object): #print("Registering handlers in object %s" % (object)) #for object in objects: if isinstance(object, dict): methods = dict.items() else: methods = map(lambda n: (n, getattr(object, n, None)), dir(object)) for method,f in methods: if method.startswith('_'): continue if callable(f): print("Registering callback %s" % (method)) if method in self.handlers: # Note, I would sometimes get an error here but it would occur at the end of the iteration process. # I found that I could replace the following line with a simple print warning. self.handlers[method].append(f) else: self.handlers[method] = [f] # Wrap lists in Trampoline, unwrap single functions # (Copied from gscreen.py. I don't know how it or the Trampoline class works # but it seems necessary.) Python guru help? for n,v in list(self.handlers.items()): try: if len(v) == 1: self.handlers[n] = v[0] else: self.handlers[n] = Trampoline(v) except: continue # I kept getting the error, "no len(v) for type instancemethod" but that was always after the iteration was complete # so I'm simply trapping and ignoring the error. (Guru help needed...) def __init__(self): self.builder = gtk.Builder() self.builder.add_from_file("multi-module-test.glade") # Get an instance of the external python module self.xmod = external_module.externalHandler(self.builder) # Create a dictionary to hold the signal-handler pairs self.handlers = {} # Call the magic function to load signal handlers from this and any other class module # self._load_handlers(self.xmod) self._load_handlers(self) #self.handlers = {"onButton1Pressed": self.onButton1Pressed, "onDestroy": self.onDestroy, "onButton2Pressed": self.xmod.onButton2Pressed} self.builder.connect_signals(self.handlers) self.window = self.builder.get_object("window1") self.window.show_all() try: app = Handler() except KeyboardInterrupt: sys.exit(0) gtk.main()

The "external" python module:
Code:
#!/usr/bin/env python import gtk class externalHandler: def onButton2Pressed(self, button): self.builder.get_object("label2").set_text("Button 2 pressed") print("Button2 Pressed") def __init__(self, builder): self.builder = builder

There are a few areas of uncertainty but it basically works. I'm certainly open to suggestions/clarifications to improve the code.

Please Log in to join the conversation.

More
8 years 2 months ago #115553 by cmorley
Glad you got something working.
In General, the magic function searches for callable functions, some are inside a class some are outside. It adjusts the function call signature to suit. Then passes that to connect_signals() to parse so GTK can find the functions.


I'm curious - why don't you use gscreen to load your GUI code - thats what it was made for.

Chris M

Please Log in to join the conversation.

More
8 years 2 months ago #115565 by tripwire

cmorley wrote: I'm curious - why don't you use gscreen to load your GUI code - thats what it was made for.


I was under the impression that gscreen is a pre-built GUI. I wanted to build one from scratch.

Please Log in to join the conversation.

More
8 years 2 months ago #115570 by cmorley
Gscreen is infrastructure for controlling linuxcnc with a GTK2 interface.

There are many example screens -
gscreen
gaxis
spartan
industrial
etc.
all of them are gscreen based

Tere is a very basic example in the maunal:
linuxcnc.org/docs/2.7/html/gui/gscreen.h..._sheet_custom_screen

Chris M

Please Log in to join the conversation.

More
8 years 2 months ago #115572 by tripwire

Gscreen is infrastructure for controlling linuxcnc with a GTK2 interface.


OK, thanks. I'll check that out...

Please Log in to join the conversation.

Time to create page: 0.098 seconds
Powered by Kunena Forum