Get source plugin for event

Do you have questions about writing plugins or scripts in Python? Meet the coders here.
Post Reply
Sem;colon
Experienced User
Posts: 609
Joined: Sat Feb 18, 2012 10:51 am
Location: Germany

Get source plugin for event

Post by Sem;colon » Sat Dec 01, 2018 10:04 am

Hello together,

I thought maybe someone know if or how to do that:
I try to get the instance of a plugin that triggered an event directly from the event object, does someone know if that's possible?
There is an object eg.event.source, but it seems to point to the system plugins where the TriggerEvent function is located..

User avatar
kgschlosser
Site Admin
Posts: 4313
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: Get source plugin for event

Post by kgschlosser » Sun Dec 02, 2018 7:21 pm

it is possible. provided that the plugin is using self.TriggerEvent and not eg.TriggerEvent. the source attribute in the event object is correct. this would be the source of the event.

it will always get set to object that called it. it has a default value of the "eg" object.
so if you look at the source for EG specifically Classes\PluginBase.py which is the base class for all plugins. scroll to the bottom you will see

Code: Select all

    def TriggerEvent(self, suffix, payload=None):
        """
        Trigger an event.

        If the plugin wants to trigger an event in EventGhost, it should call
        self.TriggerEvent with the event name as *suffix* parameter. It can
        also post optional additional data through the *payload* parameter.

        Keep in mind, that an event generated through this method will also
        automatically be ended immediately. If the plugin wants to generate
        an event with a longer duration, it has to use
        :meth:`!TriggerEnduringEvent`.
        """
        with gTriggerEventLock:
            info = self.info
            info.lastEvent.SetShouldEnd()
            event = eg.TriggerEvent(suffix, payload, info.eventPrefix, self)
            info.lastEvent = event
            return event
this is what gives you the ability to call self.TriggerEvent from inside of the plugin.
so here is the call to make the event

Code: Select all

 event = eg.TriggerEvent(suffix, payload, info.eventPrefix, self)
 
which translates down to

Code: Select all

 event = eg.TriggerEvent(
     suffix=suffix,
     payload=payload,
     prefix=info.eventPrefix,
     source=self
 )
 
as you can see the source parameter is set to self. which would be the plugin. so long as the author of the plugin uses the method in their plugin.
I have been guilty of not using the builtin method for the plugin.
If you like the work I have been doing then feel free to Image

User avatar
kgschlosser
Site Admin
Posts: 4313
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: Get source plugin for event

Post by kgschlosser » Sun Dec 02, 2018 8:14 pm

now if that is the case and the plugin author is using eg.TriggerEvent it gets a wee bit more complex to do this

here is an example of how this would work.

Code: Select all

import inspect

def test():
    prev_frame = inspect.currentframe().f_back
    if 'self' in prev_frame.f_locals:
        cls = prev_frame.f_locals["self"]
        print cls.__class__.__name__

        # call a method in the class to show that it worked.
        cls.found_class()

    the_method_or_function = prev_frame.f_code
    print the_method_or_function.co_name


def function_run_test():
    test()


class RunTest(object):

    def method_run_test(self):
        test()

    def found_class(self):
        print 'FOUND ME'


rtest = RunTest()

function_run_test()
rtest.method_run_test()
and here is some pseudo code for overriding TriggerEvent to obtain the plugin instance
I have not tested this and I am working from memory in the layout of the plugin instances in eg.plugins. and whether or not the actual classes contain the __package__ attribute. I do not remember if the module having this will get passed on to the actual class or not. if it does not it is still simple to handle. There are other things that would need to be done if there is more then a single copy of a plugin running. but that is something that I think you would have able to handle from there.
This gives you the basic idea of what to do if it does not work out of the box.

the class instance if it is a plugin would be stored in eg.globals.last_trigger_event_source, that variable will either be a None or a PluginBase instance
this would be a rare case which I have not account for in this example. it would be if the plugin author used a module level function to call TriggerEvent from. I feel this not done all that often and would not require a concession for.

if you need help in modifying the code let me know.

you will want to put this into a script that will only get run a single time at startup. be sure to place the script at the very top of you autostart group so it will get loaded before anything else in your tree

you can also use one of the new events that are generated at startup before the autostart macro gets processed to the do the. just in case you do not want to place a script into the autostart group. the Main.OnInit does not get triggered until after the autostart group gets processed so do not use that event.

i did add code to make sure it wouldn't get all goofed up in the event you accidentally ran it more then once.
so if you are making changes to the code you can run it more then once and the new changes will populate properly.

READ THE COMMENTS they provide some clues that can help to get the code running. as well as information
about tracebacks or things you may see in the log and the causes.

Code: Select all

import inspect

eg.globals.last_trigger_event_source = None


if not hasattr(eg.globals, 'original_trigger_event'):
    eg.globals.original_trigger_event = eg.TriggerEvent


def TriggerEventWrapper(suffix, payload=None, prefix='Main', source=eg):

    if isinstance(source, eg.PluginBase):
        eg.globals.last_trigger_event_source = source

    else:
        prev_frame = inspect.currentframe().f_back
        if 'self' in prev_frame.f_locals:
            cls = prev_frame.f_locals["self"]

            # if you are trying to obtain the plugin instance the TriggerEvent
            # MUST be called from the code in the plugin. If it is called from
            # some other class in the plugin's code it gets a bit more complex.
            # but not impossible.
            if isinstance(cls, eg.PluginBase):
                eg.globals.last_trigger_event_source = cls
            else:
                # so here we do that additional legwork. we need to know if the
                # the calling class is in fact a plugin. we do this by
                # using the __package__ at the actual class level and not the
                # instance level. once we have that package name we can check
                # to see if it is a plugin from that point.
                # If it is a plugin then we go to the plugin list and locate
                # the plugin that has the matching package name.
                
                # it could be __package__ or possibly even __module__
                # you will need to test this. 
                # I think __package__ might be at the module level 
                # and __module__ is at the class level. I don't remember 
                # off hand

                package_name = cls.__class__.__package__
                if (
                    'CorePluginModule' in package_name or
                    'UserPluginModule' in package_name
                ):
                    for plugin in eg.plugins:
                        if (
                            package_name ==
                            plugin.info.plugin.__class.__.__package__
                        ):
                            eg.globals.last_trigger_event_source = (
                                plugin.info.plugin
                            )

                    else:
                        eg.globals.last_trigger_event_source = None
                else:
                    eg.globals.last_trigger_event_source = None
        else:
            eg.globals.last_trigger_event_source = None

    eg.globals.original_trigger_event(
        suffix=suffix,
        payload=payload,
        prefix=prefix,
        source=source
    )

# Now this is going to do one of 2 things. If you are running any release of EG
# prior to 0.5 RC5 and have debugging enabled it will produce a traceback and
# stop EG from running. if you have debugging turned off it will work just
# fine. if you are running EG 0.5 RC5 we changed this so if you have debugging
# enabled it will print out a warning message and not actually stop the program
eg.TriggerEvent = TriggerEventWrapper

If you like the work I have been doing then feel free to Image

Sem;colon
Experienced User
Posts: 609
Joined: Sat Feb 18, 2012 10:51 am
Location: Germany

Re: Get source plugin for event

Post by Sem;colon » Mon Dec 03, 2018 10:28 pm

Wow! Awesome Kevin, that helps me a lot! Thank you so much! :D

User avatar
kgschlosser
Site Admin
Posts: 4313
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: Get source plugin for event

Post by kgschlosser » Tue Dec 04, 2018 12:04 am

no worries m8.

There is always a solution. it just has to be discovered. Because the plugins were written by so many authors and not all of the authors knew all of the features of the API they may not have known about the method in the base class.

some day I will devote my time to actually writing some documentation on the API. as this is very lacking. I started work on a plugin generator that writes all of the necessary bits and pieces for the core of the plugin. things like the Text class, the plugin class, eg.RegisterPlugin, the actions. and even some threading examples.
If you like the work I have been doing then feel free to Image

Sem;colon
Experienced User
Posts: 609
Joined: Sat Feb 18, 2012 10:51 am
Location: Germany

Re: Get source plugin for event

Post by Sem;colon » Wed Dec 05, 2018 8:46 pm

Great idea! I think that would help a lot of people to get started with creating a plugin!

I remember that when I wrote my first plugin, I actually checked the wiki... And I looked at an existing plugin that had a similar functionality

User avatar
kgschlosser
Site Admin
Posts: 4313
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: Get source plugin for event

Post by kgschlosser » Thu Dec 06, 2018 8:01 am

That's the exact same thing I did. the WiKi wasn't a whole bunch of help. from a development standpoint I think it would be easier to add the docstring and then parse them. using sphinx for the docs makes it challenging because of all of the additional syntax and formatting. dealing with errors because of that is a big pain. and getting it to link to other areas of the documentation makes it one hell of a task to have to write all of that additional information into the docsting.

But that plugin thing i made. I use it all the time when i make a new plugin. even tho I know how to make one it does all of the basic plugin outline stuff for me. so I do not need to key in all the code for it. I simply enter the plugin name, type, version, help location, description a couple of check boxes for things like multiload and create macros on add. and if I want to add a threading class and the type of threading class. like a socket server or just a socket listener. the action names. and it builds the whole thing for me. well the basic outline of it. So it is really nice in that way. so I do not have to key all of that stuff in.

I think the hardest thing to learn was all the wxPython stuff. some day I might make a simple drag and drop config panel designer.
If you like the work I have been doing then feel free to Image

Post Reply