Access Class B's function inside Class A?



  • Hi,

    Is there a way I can access Class A's function inside Class B?
    My case scenario is Class A is a treeview layout. Class B is the main GeDialog.
    You can see it here: https://www.dropbox.com/s/zua8k8xi8zw05g6/c4d263_access_class_a_function_inside_class_b.jpg?dl=0

    I want Class B's refresh_layout function to trigger when I changed a variable in the treeview layout specifically the global variable folder. Is this possible?

    A mock code would be something like this

    
    class A(c4d.gui.TreeViewFunction):
    
        def Select(self, root, userdata, obj, mode):
            if mode == c4d.SELECTION_NEW:
                for node in self.node_list :
                    node.selected = False
                    if node == obj:
                        node.selected = True
                        
                        folder =  obj.name
                        # Trigger Class B's refresh layout() function
    
    class B (gui.GeDialog):
    
        def InitValues():
            global folder
        def refresh_layout():
            # refreshes Class B's layout
    

    The other alternative would be to make the refresh_layout function live outside the class but since the function has already a lot of variables using Class B, I want it to be inside Class B too.

    Is this possible?



  • Not in the way you may imagine it, but yes.

    For a class function to be called, you need an object to call it on (self). Since many classes may have functions of the same name, it is necessary to know what class you're even addressing here.
    For example, x.doSomething() and y.doSomething() may call totally different functions doSomething because x and y belong to different classes, each with its own definition of doSomething.
    That means, to call refresh_layout() you need to have an object to invoke it on. Like, thatDialog.refresh_layout().

    So, what you need to do is to pass the dialog object of class B to class A in some way. Then, class A can invoke the refresh by just calling the function on that object. You normally pass such connecting objects in the init function of the class A (but that's really a design question that I can't answer for you).

    (To cover all cases: it is possible to define functions that do not have a self parameter; read up on your Python class definitions for that... it doesn't apply for your case here, since a refresh wll need the dialog object anyway.)



  • Hi,

    everything is an object in Python, which is leaves you with many options to do what you want. Here are two of the more popular variants.

    class Item(object):
        """A type to invoke some_function on.
        """
        def __init__(self, name): self._name = name
    
        def __repr__(self): return self._name
    
        def some_function(self, *args, **kwargs):
            """
            """
            msg = "{} has been invoked with the args: {} and the kwargs: {}."
            print msg.format(self.some_function, args, kwargs)
    
    class Invoker(object):
        """Invokes an anonymous callable by binding that callable.
        """
        def __init__(self, func): self._callable = func
    
        def invoke(self, *args, **kwargs):
            """Invoke the stuff we want to invoke.
            """
            print "\nInvoking callable."
            # We are invoking an anonymous function that has been bound
            # to the class. We are not really aware that this is actually
            # a method. This could also be a function, a lambda, everything
            # that is callable.
            self._callable(*args, **kwargs)
    
    class AlsoInvoker(object):
        """Invokes a specific method by binding objects and calling the class
        implementation on them.
        """
        def __init__(self, objects): self._objects = objects
    
        def invoke(self, *args, **kwargs):
            """Invoke the stuff we want to invoke.
            """
            print "\nInvoking class implementation."
            for obj in self._objects:
                # We are calling the class object for a known method and 
                # are just filling in the "self" part of the call (obj).
                Item.some_function(obj, *args, **kwargs) 
    
    # Some Items with methods we want to invoke.
    a, b = Item("a"), Item("b")
    
    # Option A: bind the instance of a function object directly.
    invoker = Invoker(func=a.some_function)
    invoker.invoke(42, pi_equals_e=True)
    
    # Option B: bind instances of an object and then call the implementation of
    # a method on the class object with these objects.
    invoker = AlsoInvoker(objects=[a, b])
    invoker.invoke("bob is your uncle", pi=3, e=3)
    
    
    Invoking callable.
    <bound method Item.some_function of a> has been invoked with the args: (42,) and the kwargs: {'pi_equals_e': True}.
    
    Invoking class implementation.
    <bound method Item.some_function of a> has been invoked with the args: ('bob is your uncle',) and the kwargs: {'pi': 3, 'e': 3}.
    <bound method Item.some_function of b> has been invoked with the args: ('bob is your uncle',) and the kwargs: {'pi': 3, 'e': 3}.
    [Finished in 0.1s]
    

    Cheers,
    zipit



  • Another solution, in your specific scenario, where you don't really need to access a method but want to trigger the GeDialog in some way, is to message the dialog as discussed here.

    In short: your treeview sends a message, and the dialog reacts to the message by performing the refresh layout.



  • @Cairyn @zipit @C4DS

    Thank you all for the responses.
    I ended up with @C4DS suggestion in using SpecialEventAdd() and CoreMessage for simplicity sake.

    Have a great day ahead!


Log in to reply