accessing button handlers in multiple python modules

More
01 Aug 2018 20:05 #115341 by tripwire
I've created a custom GUI in Glade (gladevcp). I have a main python module which contains handler functions for my gui widgets. this is all working fine. However, I copied in some gladevcp (xml) code from Serguei Glavatski's probe screen into my main glade file. there are lots of buttons and widgets associated with this new code who's handler functions reside in a separate file, probe_screen.py. I'm trying to keep this file separate from my main python file. I've done an
import probe_screen
at the top of my main python file. The module *is* being imported (as indicated by a diagnostic "print" statement.) But I am missing all the handlers for the probe_screen widgets.

There are get_handlers() functions in both python modules:
in my main python file:
def get_handlers(halcomp,builder,useropts):
    return [HandlerClass(halcomp,builder,useropts)]

In probe_screen.py:
def get_handlers(halcomp,builder,useropts):
    return [ProbeScreenClass(halcomp,builder,useropts)]

I've tried referencing the handler functions the following ways, all without success:
on_button_released
probe_screen.on_button_released
ProbeScreenClass.on_button_released
probe_screen.ProbeScreenClass.button_released

I'm stumped at this point. Anyone have any ideas?
Thanks!
Dave L.

Please Log in or Create an account to join the conversation.

More
01 Aug 2018 20:21 - 01 Aug 2018 20:23 #115343 by Grotius
A example :
from gmoccapy import widgets
The widgets location for this is : ...../lib/python/gmoccapy/widgets.py

So in your case copy your file probe_screen in that directory.

Then you can test the import once again for functionality.

You type
from gmoccapy import probe_screen

Good luck !
Last edit: 01 Aug 2018 20:23 by Grotius.

Please Log in or Create an account to join the conversation.

More
01 Aug 2018 22:27 - 01 Aug 2018 22:48 #115350 by Grotius
If you look in gscreen keybindings.py
You can see a lot of info.

It's a difficult item, i searched 5 mont's ago for the same issue. Then Norbert told me he
was going to program all the button's external. But i don't have a clear example for you right now, sorry.

In all the existing examples, there are no existing button handlers. So i am curious how the coding looks like.

If there is a main pyhon file that looks after the button handler, it works.
In external file, it is a problem. The error is: handler is missing...

So in the main file you have to do all your button handlers, then your problem is solved tonight.
I never seen a exernal button event, also on google there are no plug and play examples.....
Last edit: 01 Aug 2018 22:48 by Grotius.

Please Log in or Create an account to join the conversation.

More
02 Aug 2018 01:10 #115363 by tripwire
Thanks for your replies, Grotius. Yes, it seems complicated. I found the following code in gscreen.py which seems to be looking for widget handlers in external files. I'm just trying to decipher it:
def load_handlers(usermod,halcomp,builder,useropts,gscreen):
    hdl_func = 'get_handlers'
    mod = object = None
    def add_handler(method, f):
        if method in handlers:
            handlers[method].append(f)
        else:
            handlers[method] = [f]
    handlers = {}
    for u in usermod:
        (directory,filename) = os.path.split(u)
        (basename,extension) = os.path.splitext(filename)
        if directory == '':
            directory = '.'
        if directory not in sys.path:
            sys.path.insert(0,directory)
            print _('adding import dir %s' % directory)

        try:
            mod = __import__(basename)
        except ImportError,msg:
            print ("module '%s' skipped - import error: %s" %(basename,msg))
	    continue
        print _("module '%s' imported OK" % mod.__name__)
        try:
            # look for functions
            for temp in ("periodic","connect_signals","initialize_widgets"):
                h = getattr(mod,temp,None)
                if h and callable(h):
                    print ("module '%s' : '%s' function found" % (mod.__name__,temp))

            # look for 'get_handlers' function
            h = getattr(mod,hdl_func,None)
            if h and callable(h):
                print ("module '%s' : '%s' function found" % (mod.__name__,hdl_func))
                objlist = h(halcomp,builder,useropts,gscreen)
            else:
                # the module has no get_handlers() callable.
                # in this case we permit any callable except class Objects in the module to register as handler
                dbg("module '%s': no '%s' function - registering only functions as callbacks" % (mod.__name__,hdl_func))
                objlist =  [mod]
            # extract callback candidates
            for object in objlist:
                dbg("Registering handlers in module %s object %s" % (mod.__name__, object))
                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):
                        dbg("Register callback '%s' in %s" % (method, basename))
                        add_handler(method, f)
        except Exception, e:
            print ("**** GSCREEN ERROR: trouble looking for handlers in '%s': %s" %(basename, e))
            traceback.print_exc()

    # Wrap lists in Trampoline, unwrap single functions
    for n,v in list(handlers.items()):
        if len(v) == 1:
            handlers[n] = v[0]
        else:
            handlers[n] = Trampoline(v)

    return handlers,mod,object

And then in the Gscreen class __init__() function the above gets called as follows:
# look for custom handler files:
        HANDLER_FN = "%s_handler.py"%skinname
        local_handler_path = os.path.join(CONFIGPATH,HANDLER_FN)
        if os.path.exists(local_handler_path):
            temp = [HANDLER_FN]
        else:
            temp = []
        handlers,self.handler_module,self.handler_instance = load_handlers(temp,self.halcomp,self.xml,[],self)
        self.xml.connect_signals(handlers)

There should be an easier way to accomplish this for external modules in known locations.

I'll probably just resort to what you suggested and put all the widget handlers in the main python file.

Thanks again!

Please Log in or Create an account to join the conversation.

More
02 Aug 2018 01:45 #115365 by Grotius
Hi Tripwire,

Try to figure this out please. You can ask this question on the Python forum to specialists. They will have the ansfer within 2 day's
I would also like to know how the code looks like. Please post your solution over here.

Please Log in or Create an account to join the conversation.

More
02 Aug 2018 02:58 - 02 Aug 2018 03:00 #115367 by cmorley
If you are building your own GUI then there is no requirement to use a handler file.
Handler files isa way to extend GladeVCP without having to add to the source code. Gmoccapy and gscreen use it for the same reason.

If you are creating the source code and don't need to be able to extend it then yes just import the file like normally.

you need to instantiate the class, then reference it through that name.
something like this:

from probe import HandlerClass as Probe # import the class changing the name
self. probe = Probe() # instantiate the class and giving a reference name
self.probe.someFunctionName() # calling the functions using the reference name

By importing while changing the class name you avoid the problem of the two file both being name HandlerClass ( I am assuming that is true)

Hope this helps

Chris M
Last edit: 02 Aug 2018 03:00 by cmorley.

Please Log in or Create an account to join the conversation.

More
02 Aug 2018 03:32 #115369 by tripwire
Thanks for your input, Chris M. Yes, I am doing what you suggested and it works for accessing, from the main python file, functions in the class in the external python module.

But I was trying to have widget signals (those specified in the Glade XML file) "find" their handler functions in the external python module without having to write additional code in the main python file. (So far I'm getting "missing handler" error messages for all the external handlers.)

It seems to hinge on the gladevcp get_handlers() and gtkbuilder.connect_signals() functions. But I haven't worked it out yet.

Please Log in or Create an account to join the conversation.

More
02 Aug 2018 19:27 #115430 by tripwire
OK. Here's a working example. (I don't consider it the final solution as the widget handlers, here are added manually which would be laborious with a lot of widgets. But this code does show how widget handlers can be located in different python modules...

This can be tested in Terminal with:
python multi-module-test.py
multi-module-test.glade:
<?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>
      </object>
    </child>
  </object>
</interface>

multi-module-test.py:
#!/usr/bin/env python

import gtk
# the following is an external module containing a button handler:
import external_module 

class Handler:
    def onDestroy(self, *args):
        print("quit app")
        gtk.main_quit()

   # local button handler:
    def onButton1Pressed(self, button):
        print("Button1 Pressed")

    def __init__(self):
        self.builder = gtk.Builder()
        self.builder.add_from_file("multi-module-test.glade")
       
       # get an instance of the handler class in the external module
        self.xmod = external_module.externalHandler()

       # create a dictionary of handler pairs from both this module and the external module
        self.handlers = {"onButton1Pressed": self.onButton1Pressed, "onDestroy": self.onDestroy, "onButton2Pressed": self.xmod.onButton2Pressed}

       # pass the dictionary to the connect_signals() function
        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()

external_module.py:
#!/usr/bin/env python

import gtk

class externalHandler:

    def onButton2Pressed(self, button):
        print("Button2 Pressed")

I'll work on iterating through the handler classes to more easily create the dictionary for connect_signals(). Will keep you posted...

Please Log in or Create an account to join the conversation.

More
02 Aug 2018 21:07 #115441 by Grotius
Hi Tripwire,

Nice that it works now. If i look at the code, it looks like you have more work now then keeping all the button handlers in the same file.
The following user(s) said Thank You: tripwire

Please Log in or Create an account to join the conversation.

More
02 Aug 2018 22:22 #115458 by cmorley

But I was trying to have widget signals (those specified in the Glade XML file) "find" their handler functions in the external python module without having to write additional code in the main python file. (So far I'm getting "missing handler" error messages for all the external handlers.)

It seems to hinge on the gladevcp get_handlers() and gtkbuilder.connect_signals() functions. But I haven't worked it out yet.


Ahh I see. Yes then in that case IIRC that is a limitation of GTK's connect_signals() one can't add module reference to it.
So In that case that is where the get_handlers() was a solution. Again IIRC if adds the handler's function calls to the main python program. Then connects the signals with .connect_signals() so GTK can find the functions.

i'll point out that gscreen and gmocapy's routines are derivatives of gladevcp's own handler function.
gladevcp's functions may be easier to follow the flow as gladevcp is simpler
src/hal/user_comps/gladevcp.py

Chris M
The following user(s) said Thank You: tripwire

Please Log in or Create an account to join the conversation.

Moderators: HansU
Time to create page: 0.176 seconds
Powered by Kunena Forum