Notice: This forum has been recovered from an old backup, so some content, links, and dates may be outdated. The forum is currently read-only while we restore sign-in and registration functionality. Details

If you find this forum valuable and would like to help keep it online, donations to help cover hosting and domain costs are greatly appreciated, but never expected. You can support the forum through Buy Me a Coffee or Ko-fi. Thank you for helping preserve the EventGhost community.

Is it possible to split plugins into multiple files?

If you have a question or need help, this is the place to be.
WoLpH
Experienced User
Posts: 96
Joined: Mon Dec 10, 2012 3:57 am

Is it possible to split plugins into multiple files?

Post by WoLpH »

For some reason I seem to be completely unable to split plugins into multiple files without having them break EventGhost (freezes on startup and doesn't function anymore).
Is there any reason for this or am I doing something wrong?

The moment I put a class that inherits eg.PluginClass or something similar in a different file it breaks eventghost completely. The downside of this is that it's quite hard to create a single reusable base class for plugins which makes handling a bit easier. I've currently written this snippet which allows me to test locally as well but I have to keep it within the __init__.py for it to work right now:

Code: Select all

from __future__ import print_function

import wx
import abc
import sys

try:
    TESTING = False

    import eg
    _Panel = eg.ConfigPanel
    try:
        _Plugin = eg.PluginClass
    except ImportError:
        _Plugin = object

    try:
        _Action = eg.ActionBase
    except ImportError:
        _Action = object

    print_error = eg.PrintError
except ImportError:
    TESTING = True
    _Panel = wx.Dialog
    _Plugin = object
    _Action = object

    def print_error(message):
        print(message, file=sys.stderr)


class Panel(_Panel):
    if TESTING:
        def __init__(self, executable=None, resizable=True, showLine=True):
            _Panel.__init__(self, None, wx.ID_ANY,
                            '%s Test' % self.__class__.__name__,
                            style=wx.DEFAULT_DIALOG_STYLE | wx.THICK_FRAME
                                  | wx.RESIZE_BORDER | wx.TAB_TRAVERSAL)
            main_sizer = wx.BoxSizer(wx.VERTICAL)
            button_sizer = self.CreateButtonSizer(wx.OK | wx.CANCEL)

            self.sizer = wx.BoxSizer()
            self.SetSizer(main_sizer)
            main_sizer.Add(self.sizer, 1, wx.EXPAND)
            main_sizer.Add(button_sizer, 0, wx.ALIGN_CENTER | wx.BOTTOM)

        def Affirmed(self):
            self.resultCode = self.ShowModal()
            if self.resultCode == wx.ID_CANCEL:
                return

            return self.resultCode

        def SetResult(self, *args):
            import pprint
            print('Set results to:')
            pprint.pprint(args)


class Base(object):
    __metaclass__ = abc.ABCMeta

    def __init__(self, *args, **kwargs):
        self.sizer = wx.FlexGridSizer(0, 2)
        self.sizer.AddGrowableCol(1)
        self.widgets = dict()

    def add_field(self, label, key=None, widget=wx.TextCtrl, **kwargs):
        key = key or label

        self.widgets['label_%s' % key] = wx.StaticText(self.panel, wx.ID_ANY, label)
        self.widgets[key] = widget(self.panel, wx.ID_ANY, self.config.get(key, ''), **kwargs)
        self.sizer.Add(self.widgets['label_%s' % key], flag=wx.EXPAND)
        self.sizer.Add(self.widgets[key], proportion=1, flag=wx.CENTER | wx.EXPAND)

        return self.widgets[key]

    def Configure(self, config=None, *args):
        self.panel = Panel()
        self.config = config or {}
        return self.panel, self.config


class Action(Base, _Action):

    @abc.abstractmethod
    def __call__(self):
        raise NotImplementedError()


class Plugin(Base, _Plugin):

    def __start__(self, *args, **kwargs):
        pass

    def __stop__(self):
        pass

    def __close__(self):
        pass



if __name__ == '__main__':
    app = wx.App()
    plugin = YourPlugin()
    plugin.Configure()
[edit]Upon retrospect, this might not be the right forum for this question. Apologies, can any mod move it to the right location?
Author of the book Mastering Python. Got Python questions? Perhaps I can help :)
User avatar
kgschlosser
Site Admin
Posts: 5190
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: Is it possible to split plugins into multiple files?

Post by kgschlosser »

sure is

just make sure that if it is part of the plugin class you do the import before the eg.RegisterPlugin is called.


example:


__init__.py

Code: Select all


import eg
from actions import NewAction

eg.RegisterPlugin(
...
..
..
)

class TestPlugin(eg.PluginBase):
    def __init__(self):
        self.newAction = self.AddAction(NewAction)
...
..
actions.py

Code: Select all


import eg

class NewAction(eg.ActionBase):
    def __init__(self):
        pass

    def Configure(self, param1='', param2='', param3=''):
        blah blah blah
        some more code

    def __call__(self, param1, param2, param3):
        and so on and so forth


this is all pseudo code of course

all other imports you want to do after the register plugin. this is for speed of loading because EG reads all of the plugins upon startup and throws an exception after the RegisterPlugin is called. that way it doesn't actually run the plugin. it's only to get the data contained within the RegisterPlugin to populate the PluginManager
If you like the work I have been doing then feel free to Image
WoLpH
Experienced User
Posts: 96
Joined: Mon Dec 10, 2012 3:57 am

Re: Is it possible to split plugins into multiple files?

Post by WoLpH »

Thanks for the quick response, that appears to be working. It's still odd how the entire application just freezes on startup if for some reason the order is different though.

For now it works however, I think I should be able to make a very simple and useful set of base-classes for creating plugins :)
Author of the book Mastering Python. Got Python questions? Perhaps I can help :)
User avatar
kgschlosser
Site Admin
Posts: 5190
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: Is it possible to split plugins into multiple files?

Post by kgschlosser »

if you are using eg 0.4 and run it from a command line with the -debug switch. it will make a fule in your appdata/roaming/eventghost folder called log.txt

in there you will get all kinds of extra information about what's happening.

unfortunately eg 0.5 doesn't set the debug variable until 1/2 way through the eventghost init process so if it's due to a plugin load problem then it will be to late to catch the information. so it's best to use 0.4 for this.

or you can simply edit the Core.py file in eg.0.5 and round abouts line 67 is where you want to change

Code: Select all

eg.debugLevel = 0
to this

Code: Select all

eg.debugLevel = 1
If you like the work I have been doing then feel free to Image
WoLpH
Experienced User
Posts: 96
Joined: Mon Dec 10, 2012 3:57 am

Re: Is it possible to split plugins into multiple files?

Post by WoLpH »

Great, I'll give that a try :)
Author of the book Mastering Python. Got Python questions? Perhaps I can help :)
User avatar
kgschlosser
Site Admin
Posts: 5190
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: Is it possible to split plugins into multiple files?

Post by kgschlosser »

the reason why eg hangs on load if try to call the subclass is because of the "lazy" import system EG has.


so how it works is this way. and this can be fixed as well if you go into the __ini__ file in the eg directory. and scroll to the bottom.

you will see if 'pylint' in sys.modules:
under that if you will see from StaticImports import *
this is typically only used if you build EG and have pylint installed so pylint doesn't throw a fit about not being able to resolve references. because the core of EG really doesn't import anything.

there is a __getattr__ that takes the module name from the eg.ConfigPanel for example.
because the file name is the same as the class name it does an import using __import__() and then stores it in self.__dict__ and passes the module instance back to whatever called it.


now when you build EG you can build the static imports which will create a file that contains all the imports and all of the setting of the attributes in EG.

so now back to the reason it hangs.

this is what i believe is happening

the eg.ActionClass is created in code and not programmatically. and i am pretty sure same goes for the Plugin Class.

if specific things aren't in order with the plugin files and it imports a file that has eg.PluginBase or eg.ActionBase

before eg has had the chance to make those 2 attributes. this can happen several ways. I do know that EG does scan all of the plugins. at exactly what point in the process of the startup it does this i am not sure. but i believe it happens before those 2 attributes are set. and that is what actually causes the hang if there is anything related before the eg.RegisterPlugin. because the ActionClass and ActionBase have not been set. and when eg scans the plugins it is only supposed to go as far as the eg.registerplugin before throwing a handeled exception to stop the plugin from loading all of the way.

if you want a copy of the static imports this would be a start to find a solution. if you do an import of that right after the eg instance is created. this way everything is hard coded imports. and then you can move the ActionClass and PluginClass instance creations to right after the staticimports import.

this might solve that issue.

i don't know i have not honestly messed around with that aspect of EG.

these are not sure fire things but it could be a place to look

I have attached a copy of the static imports never the less in case you want to have it.


K
If you like the work I have been doing then feel free to Image
WoLpH
Experienced User
Posts: 96
Joined: Mon Dec 10, 2012 3:57 am

Re: Is it possible to split plugins into multiple files?

Post by WoLpH »

Thank you so much for all of the background information. That explains quite a lot about the workings :)
Author of the book Mastering Python. Got Python questions? Perhaps I can help :)
User avatar
kgschlosser
Site Admin
Posts: 5190
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: Is it possible to split plugins into multiple files?

Post by kgschlosser »

It has taken me quite some time to figure out how eg works. no one to explain it to me. many hours reading code and jumping through the files using print statements to follow the trail of breadcrumbs if you will to see how the process works and what data is passed from one thing to another. because of how eg runs and how it receives win32 messages it stops me from being able to run eg from an ide to debug it. I am not sure how all of that portion works and as to why it stops being able to run eg from an ide but it does. so the print statements are all i have and that takes a hell of a lot of time.

it's this bit of code here that causes the issue when you run EG from inside of an IDE

Code: Select all

self.windowName = windowName
self.messageProcs = {
    WM_SIZE: [self.WmSizeHandler],
}
eg.ThreadWorker.__init__(self)
wndclass = WNDCLASS(
    lpfnWndProc = WNDPROC(self.WindowProc),
    hInstance = GetModuleHandle(None),
    lpszMenuName = None,
    lpszClassName = self.windowName + "MessageReceiver",
)
self.classAtom = RegisterClass(byref(wndclass))
if not self.classAtom:
    raise WinError()
but if you run EG from inside of an IDE the classAtom causes the raise of WinError. don't know why or if there is a better way to handle how this is done.
If you like the work I have been doing then feel free to Image
WoLpH
Experienced User
Posts: 96
Joined: Mon Dec 10, 2012 3:57 am

Re: Is it possible to split plugins into multiple files?

Post by WoLpH »

I'm personally working around EG completely while doing most of my testing. Just take a look at my Domoticz plugin: https://github.com/WoLpH/eventghost-domoticz
You can easily test the dialogs by executing __init__.py even if you don't have EG available.

Not a perfect solution, but it does the trick for me right now.
Author of the book Mastering Python. Got Python questions? Perhaps I can help :)
User avatar
kgschlosser
Site Admin
Posts: 5190
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: Is it possible to split plugins into multiple files?

Post by kgschlosser »

cook. but may i make one suggestion???


instead of

Code: Select all

if not eg_base.TESTING:
you could do.

Code: Select all

if __name__ != __main__:

    import eg
    eg.RegisterPlugin()

i think that would work.
you may have to import __main__ dunno never tried it.

that would eliminate the need for setting a variable if you just want to run the plugin dialogs for testing because if you run the script from python instead of eg __name__ would be __main__ yes?

i am making a suggestion and asking a question at the same time i guess lol


and i didn't really look through the code for your plugin as of yet. i will do so later. and I might be able to point out some of the eg wrappers you may not know about yet.
If you like the work I have been doing then feel free to Image
WoLpH
Experienced User
Posts: 96
Joined: Mon Dec 10, 2012 3:57 am

Re: Is it possible to split plugins into multiple files?

Post by WoLpH »

kgschlosser wrote:cook. but may i make one suggestion???


instead of

Code: Select all

if not eg_base.TESTING:
you could do.

Code: Select all

if __name__ != __main__:

    import eg
    eg.RegisterPlugin()

i think that would work.
you may have to import __main__ dunno never tried it.

that would eliminate the need for setting a variable if you just want to run the plugin dialogs for testing because if you run the script from python instead of eg __name__ would be __main__ yes?

i am making a suggestion and asking a question at the same time i guess lol
True, if I would import it from a different script that would work as well for the __init__.py at least.

But... I've incorporated the "if TESTING" in multiple parts of the code (see the other files as well) so that solution can't fully work. Unless __init__.py would import the other files and the other files would import __init__.py again. Recursive imports tend to go wrong :)
and i didn't really look through the code for your plugin as of yet. i will do so later. and I might be able to point out some of the eg wrappers you may not know about yet.
Please do :) I found the writing of the plugin quite awkward. A large part of that is due to wx layout weirdness but the eg handling of creating dialogs didn't help either.
Author of the book Mastering Python. Got Python questions? Perhaps I can help :)
User avatar
kgschlosser
Site Admin
Posts: 5190
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: Is it possible to split plugins into multiple files?

Post by kgschlosser »

I'mma take a look at it now. I could use a break from working on my house.
If you like the work I have been doing then feel free to Image
User avatar
kgschlosser
Site Admin
Posts: 5190
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: Is it possible to split plugins into multiple files?

Post by kgschlosser »

HA it's kinda funny. but you got me tinkering with something and I am sure i can get it done. but when running a plugin from outside of EG for testing import sys and check sys.modules for an instance of 'eg' and if not there add it pointing to a class you made that provides the same mechanism for the lazy import system

but i do have a question is there a reason for all of the subclassing other than just the testing purposes??

it makes the code very difficult to follow.

typically what happens is any repeating code that may be in an actionclass would be put into the pluginclass and can be accessed using the self.plugin.


now. you also do have this ability when adding the action to your plugin via the AddAction() which has to be placed in the init of the plugin does to how the EG UI loads the tree data because if it is not put in the init the tree item for your actions will not function properly.

but if you did this


plugin init

Code: Select all

def __init__(self, param1, param2, param3):
    self.Action1 = AddAction(SomeAction1)
    self.Action2 = AddAction(SomeAction2)

plugin actions

Code: Select all

class SomeAction1(eg.ActionBase):

    def __init__(self):
        self.action = None

    def SetAction(self, action):
        self.action = action

    def Configure(self, *args):
        if self.action is None:
            self.action = self
        panel = eg.ConfigPanel()
        self.widget1 = somewidget()
        self.widget2 = somewidget()
        self.action.CreateDialog(*args)

        while panel.Affirmed():
            panel.SetResults(*self.action.GetResults())

        self.action = None
        
    def GetResults(self):
        return (
            self.widget1.GetValue(),
            self.widget2.GetValue(),
            self.widget3.GetValue()
        )
   
    def CreateDialog(self, param1):
        self.widget3 = somewidget(param1)
        

class SomeAction2(eg.ActionBase):
    def __init__(self)
        def ConfigureWrapper(*args):
            self.plugin.Action1.SetAction(self)
            self.plugin.Action1.Configure(*args)
            
        self.Configure = ConfigureWrapper

    def CreateDialog(*args):
        self.widget3 = somewidget()

    def GetResults(self):
        return (
            self.plugin.Action1.widget1.GetValue(),
            self.plugin.Action1.widget2.GetValue(),
            self.widget3.GetValue()
        )


i have done the whole sharing dialog code as well but that has caused me grief in following code. and it's bets to just repeat the code. but if you want to this is a more readable method.

but you do also have an ability to add "Events" kinda like wx events to eg. using the eg.Bind() and eg.Notify() the bind you would set the event name and a single parameter of which function/method to call and the notify would be the same event name along with any parameters you wish to pass to that function/method.

this is all pseudo code of course but it gives a means to share dialog code if needed. and I think it's a little more readable. this gives a general idea.

and on a side note. EG will only pass *args to the call of the plugin or the call of an action or it's configure. it does not have the mechanism in place to use keywords. this is one of those things i would like to get working as that could make doing something like this a whole lot easier. and i wish that instead of having EG create the dialog then call the Configure it would create the dialog if the call to eg.ConfigPanel took place. it would have been better if there were convince classes for frame and dialog that added the Affirmed and SetResult attributes that way it was a little more flexible
If you like the work I have been doing then feel free to Image
WoLpH
Experienced User
Posts: 96
Joined: Mon Dec 10, 2012 3:57 am

Re: Is it possible to split plugins into multiple files?

Post by WoLpH »

kgschlosser wrote:HA it's kinda funny. but you got me tinkering with something and I am sure i can get it done. but when running a plugin from outside of EG for testing import sys and check sys.modules for an instance of 'eg' and if not there add it pointing to a class you made that provides the same mechanism for the lazy import system
True, essentially it's the same but I find catching the ImportError somewhat more intuitive. I personally think it's weird (and wrong) that eg just puts eg in the global scope. Imho all plugins should explicitly import the module.
but i do have a question is there a reason for all of the subclassing other than just the testing purposes??
The idea for the base classes such as panels is to create a single reusable base-class that can be used in multiple extensions. Truthfully, it's not really working out that way just yet but that's the ultimate goal. To create a single class that's easy and convenient to use without having to worry about the wx and eg quirks too much :)
it makes the code very difficult to follow.
Agreed... ideally I'll release the base classes as a separate package with some documentation to make it very easy to create a new plugin. The added advantage of such an approach would be that migrating to a newer eg API would require a single change to the base class instead of changes to all of the plugins. But that's just dreaming of the future for the time being.
typically what happens is any repeating code that may be in an actionclass would be put into the pluginclass and can be accessed using the self.plugin.

now. you also do have this ability when adding the action to your plugin via the AddAction() which has to be placed in the init of the plugin does to how the EG UI loads the tree data because if it is not put in the init the tree item for your actions will not function properly.

but if you did this
[snip]
i have done the whole sharing dialog code as well but that has caused me grief in following code. and it's bets to just repeat the code. but if you want to this is a more readable method.
That makes sense, I didn't think of that but I'll change the code :)
Actually... I'll just modify the actions to function as a metaclass making it auto-registering.
but you do also have an ability to add "Events" kinda like wx events to eg. using the eg.Bind() and eg.Notify() the bind you would set the event name and a single parameter of which function/method to call and the notify would be the same event name along with any parameters you wish to pass to that function/method.

this is all pseudo code of course but it gives a means to share dialog code if needed. and I think it's a little more readable. this gives a general idea.
True, that would be better for testing. The current code is mostly the result of frustration because my initial approaches didn't function as planned ;)
and on a side note. EG will only pass *args to the call of the plugin or the call of an action or it's configure. it does not have the mechanism in place to use keywords. this is one of those things i would like to get working as that could make doing something like this a whole lot easier. and i wish that instead of having EG create the dialog then call the Configure it would create the dialog if the call to eg.ConfigPanel took place. it would have been better if there were convince classes for frame and dialog that added the Affirmed and SetResult attributes that way it was a little more flexible
Yep. That's why I'm using the config dictionary. Not ideal either but at least a bit more flexible
Author of the book Mastering Python. Got Python questions? Perhaps I can help :)
krambriw
Plugin Developer
Posts: 2570
Joined: Sat Jun 30, 2007 2:51 pm
Location: Stockholm, Sweden
Contact:

Re: Is it possible to split plugins into multiple files?

Post by krambriw »

Hi guys,

This is a topic that I have interest in but have not found a solution. I have earlier experimented in having actions in separate files and that worked ok, I could import them without problems. But now to the test, I have a rather large plugin with a lot of code in a eg.RawReceiverPlugin class. It is really ugly, it has a lot of functions and I would like to tidy up the creature. I would like to break out functions and actions into separate files for maintenance purpose. The problem is that one or more functions are related to one or more actions so ideally, I would like to have one file for each set holding the functions and related actions where I can import from.

The plugin is the RFXtrx

Any ideas???

Kind regards, Walter
Post Reply