Why was text=Text changed to text=Text() in Speech plugin?

Do you have questions about writing plugins or scripts in Python? Meet the coders here.

Why was text=Text changed to text=Text() in Speech plugin?

Postby pearbear » Sat Dec 31, 2016 7:05 am

I'm not super experienced in Python so this has me confused and I'm hoping someone might be able to explain to satisfy my curiosity and further my understanding of plugins. The following change was made to the Speech plugin last May:
https://github.com/EventGhost/EventGhost/pull/54
My understanding of how to use the Text class comes from the documentation:
http://www.eventghost.net/docs/internationalisation.html
which tells you to do text = Text. No other plugin included with EventGhost does text = Text(). The plugin works just the same for me either way(which is to say fine in 0.4.1.r1722 and not at all with the beta builds). I haven't been able to reproduce the bug that this is supposed to fix.

What is the difference?

Should this change be made in all plugins and the documentation updated?
pearbear
Experienced User
 
Posts: 102
Joined: Mon Apr 02, 2012 10:28 pm

Re: Why was text=Text changed to text=Text() in Speech plugi

Postby kgschlosser » Sat Dec 31, 2016 7:25 am

initializing the text class does nothing or that is what it is supposed to do.

there is no __init__ method. it's pointless.

The only purpose for the Text class is to be overridden if there is a language file that has been made and has a replacement class for the plugin. that is how it works. it is just a means to override the class with a translated version. it serves no other purpose other then a container.

the () returns an instance of Text class an initialized class that can perform a specific function.
without the () the class it's self gets is "returned" and i use that term loosley. not an instance. and since all of the attributes in the Text class are class level attributes (no self in front of them and defined outside of a method) you don't have to initialize it. Unless there is an __init__ method in the Text class. I would have to look
A loved one and Time, The 2 things that can never be replaced.

Family, The only thing you don't get to choose in life.
User avatar
kgschlosser
Site Admin
 
Posts: 1368
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: Why was text=Text changed to text=Text() in Speech plugi

Postby pearbear » Tue Jan 03, 2017 6:01 am

I found out how to reproduce the "infinite loop" bug that this change was made to fix:

  • Open a tree with the Speech plugin.
  • Open a tree with the Speech plugin.
  • The log fills with tons of these:
Code: Select all
17:29:32     File "C:\Program Files (x86)\EventGhost\eg\Utils.py", line 499, in SetDefault
17:29:32       SetDefault(targetDict[defaultKey], defaultValue)

After that the EventGhost application has issues and usually after exiting I have to kill some EventGhost processes that prevent it from restarting.

I found that my fix for a different problem with the plugin introduced by changes starting with the 0.5.0-beta1 release:
https://github.com/EventGhost/EventGhost/pull/156
makes the text = Text() thing unnecessary as it also fixes the infinite loop issue even if it's changed back to text = Text
pearbear
Experienced User
 
Posts: 102
Joined: Mon Apr 02, 2012 10:28 pm

Re: Why was text=Text changed to text=Text() in Speech plugi

Postby kgschlosser » Wed Jan 04, 2017 6:04 pm

*** PSEUDO CODE NOT TESTED***

the infinite Loop problem is because text-=Text is put into both the plugin and the action. it cannot be this way. text=Text has to be in the plugin and in the action you do nothing

but the Text class has to be done differently when this is done.

You cannot do

Code: Select all
class Text:
    var1 = 'blah'
    var2 = 'blah2'


and have the action be able to access var1 and var2

there are 2 ways around this.

if you only need to access var1 and var2 from that specific action you will need to do this

Code: Select all
class Text:
    class SAME_NAME_AS_ACTION:
        var1 = 'blah'
        var2 = 'blah2'


that will point to the action properly and can be accessed from inside of the action as self.text

now if you need to have access to var1 and var 2 from more then one action. you will have to subclass eg.TranslatableStrings this way the translation takes place before the plugin gets it's hands on it and that translation can be used elsewhere

Code: Select all
class Text(eg.TranslatableStrings):
    var1 = 'blah'
    var2 = 'blah2'


and to access this from the actions as self.text you will have to point to the Text class directly. But still
*****DO NOT**** set the text = Text in the class for the action. you can do this in a method. but not at the class level for the action

Code: Select all

class SOME_PLUGIN(eg.PluginClass):
    # NO NO NO NO
    text = Text()

    # YES YES YES YES
    text = Text

class SOME_ACTION(eg.AcrionClass):
    # NO NO NO NO
    text = Text

    def Configure(self):
        # YES YES YES YES
        text = self.text
        # or this if you subclassed eg.TranslatableStrings
        text = Text


I really do hope this clears this up for everyone that is in confusion as to how the whole Text class things works.

But I am going to throw in a winger for ya

If you specify a Text class in the plugin. you cannot use the name and description in the action anymore.
It has to be done like so. you do how ever specify the icon in the action if you have an icon for the action.

Code: Select all
class Text:
    class SAME_NAME_AS_ACTION:
        name = 'Some Action Name
        description = 'Some Actions Description



so to wrap this up.
I will always put the eg.TranslatableStrings in the Text class. this is because I just never know if there is something i may want to access directly. and it's just a good habit to identify it as well.
because in the reality of things you can name the class HokeyPokey if you wanted

Code: Select all

class HokeyPokey:
   var1 = 'test'

class Some_Plugin(eg.PluginClass):

    text = HokeyPokey




but there is no means to Identify what HokeyPokey is or what it does. that is why it is good practice

here is a complete proper way to use this


Code: Select all
import eg

eg.RegisterPlugin(name='Text Class Demo')


class Text(eg.TranslatableStrings):
    pluginTest = 'test'
    action2Test = 'test'

    class Action1:
        name = 'Action Name 1'
        description = 'Action 1 Description'
        action1Test = 'test'

     class Action2:
        name = 'Action Name 2'
        description = 'Action 2 Description'

class PLUGIN(eg.PluginClass):

    text = Text

    def __init__(self):
        self.AddAction(Action1)
        self.AddAction(Action2)


    def __start__(self):
        text = self.Text

        print text.pluginTest

class Action1(eg.ActionClass):

    def __call__(self):

        text = self.Text
        print text.name
        print text.description
        print text.action1Test

class Action2(eg.ActionClass):

    def __call__(self):
        text = Text
        print text.action2Test

        text = self.Text
        print text.name
        print text.description
        print text.action2Test




Thank You all and Have a Nice Day :D :shock:
A loved one and Time, The 2 things that can never be replaced.

Family, The only thing you don't get to choose in life.
User avatar
kgschlosser
Site Admin
 
Posts: 1368
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: Why was text=Text changed to text=Text() in Speech plugi

Postby pearbear » Thu Jan 05, 2017 3:28 am

kgschlosser wrote:
Code: Select all
import eg

eg.RegisterPlugin(name='Text Class Demo')


class Text(eg.TranslatableStrings):
    pluginTest = 'test'
    action2Test = 'test'

    class Action1:
        name = 'Action Name 1'
        description = 'Action 1 Description'
        action1Test = 'test'

     class Action2:
        name = 'Action Name 2'
        description = 'Action 2 Description'

class PLUGIN(eg.PluginClass):

    text = Text

    def __init__(self):
        self.AddAction(Action1)
        self.AddAction(Action2)


    def __start__(self):
        text = self.Text

        print text.pluginTest

class Action1(eg.ActionClass):

    def __call__(self):

        text = self.Text
        print text.name
        print text.description
        print text.action1Test

class Action2(eg.ActionClass):

    def __call__(self):
        text = Text
        print text.action2Test

        text = self.Text
        print text.name
        print text.description
        print text.action2Test



I ran into a couple problems. First, there was an extra space on the line:
Code: Select all
     class Action2:

Fixed that and then ran into this error when trying to add the plugin:
Code: Select all
19:16:14        File "C:\Program Files (x86)\EventGhost\plugins\TextClassDemo\__init__.py", line 29, in __start__
19:16:14          text = self.Text
19:16:14      AttributeError: 'PLUGIN' object has no attribute 'Text'

So I took that to mean that
Code: Select all
        text = self.Text

needs to be changed to
Code: Select all
        text = self.text

Now I can add the plugin but if I try to run Action1 I get:
Code: Select all
19:18:00        File "C:\Program Files (x86)\EventGhost\plugins\TextClassDemo\__init__.py", line 37, in __call__
19:18:00          text = self.Text
19:18:00      AttributeError: 'Action1' object has no attribute 'Text'

so I make the same change there and Action1 is now working. Next I ran into the same problem with Action2 and made the same change. Now when I try to run Action2 I get:
Code: Select all
19:20:19        File "C:\Program Files (x86)\EventGhost\plugins\TextClassDemo\__init__.py", line 51, in __call__
19:20:19          print text.action2Test
19:20:19      AttributeError: class Action2 has no attribute 'action2Test'

So I'm passing the ball back to you at this point kgschlosser. Here's my updated code:
Code: Select all
import eg

eg.RegisterPlugin(name='Text Class Demo')


class Text(eg.TranslatableStrings):
    pluginTest = 'test'
    action2Test = 'test'

    class Action1:
        name = 'Action Name 1'
        description = 'Action 1 Description'
        action1Test = 'test'

    class Action2:
        name = 'Action Name 2'
        description = 'Action 2 Description'

class PLUGIN(eg.PluginClass):

    text = Text

    def __init__(self):
        self.AddAction(Action1)
        self.AddAction(Action2)


    def __start__(self):
        text = self.text

        print text.pluginTest

class Action1(eg.ActionClass):

    def __call__(self):

        text = self.text
        print text.name
        print text.description
        print text.action1Test

class Action2(eg.ActionClass):

    def __call__(self):
        text = Text
        print self.plugin.text.action2Test

        text = self.text
        print text.name
        print text.description
        print text.action2Test
pearbear
Experienced User
 
Posts: 102
Joined: Mon Apr 02, 2012 10:28 pm

Re: Why was text=Text changed to text=Text() in Speech plugi

Postby pearbear » Thu Jan 05, 2017 3:44 am

OK, I figured it out. I just needed to change:
Code: Select all
text = Text

to:
Code: Select all
baseText = Text

and
Code: Select all
print text.action2Test

to:
Code: Select all
print baseText.action2Test

but I have a question about that. Why not just do:
Code: Select all
print Text.action2Test

instead?
pearbear
Experienced User
 
Posts: 102
Joined: Mon Apr 02, 2012 10:28 pm

Re: Why was text=Text changed to text=Text() in Speech plugi

Postby kgschlosser » Thu Jan 05, 2017 9:07 am

kgschlosser wrote:*** PSEUDO CODE NOT TESTED***



:-D

Code: Select all
import eg

eg.RegisterPlugin(name='Text Class Demo')


class Text(eg.TranslatableStrings):
    pluginTest = 'Plugin Test'
    action2Test = 'Action 2 Test'

    class Action1:
        name = 'Action 1 Name'
        description = 'Action 1 Description'
        action1Test = 'Action 1 Test'

    class Action2:
        name = 'Action 2 Name'
        description = 'Action 2 Description'


class PLUGIN(eg.PluginClass):

    text = Text

    def __init__(self):
        self.AddAction(Action1)
        self.AddAction(Action2)

    def __start__(self):
        text = self.text
        print text.pluginTest


class Action1(eg.ActionClass):

    def __call__(self):

        text = self.text
        print text.name
        print text.description
        print text.action1Test


class Action2(eg.ActionClass):

    def __call__(self):
        text = Text
        print text.action2Test

        text = self.text
        print text.name
        print text.description
        print text.action2Test



ok I am sorry for the typos. To many friggen Texts and texts and self.texts

this does work and has been tested


this is the reason you CANNOT use baseText = text unless you are not concerned with having it translated
the whole purpose to this is to allow for a translation replacement class to be made. this translation class has all the exact same attribute names just has a different language as the values

this replacement class is stored in the language files (If one has been made)

when the plugin gets installed EG check to see if plugin.text has been set. if it has been, it will go and do a look see if there is a language.plugin.pluginname in there. and if so it will replace plugin.text with language.plugin.pluginname.

so by using baseText = Text you completely disable any language translation possibilities. because you are now not letting EG do it's thing.

this is why it is important not to set a text = Text in the actions. because if this is done properly and you have a class name inside of the Text class that has the same name as the action. this too will also be in the language.plugin.pluginname and EG will automatically set action.text to language.plugin.pluginname.actionname where all translations for that action will be located.


the code below will show the proper use. and by doing it this way it will also make sure that everything is compatible with EventGhost languages

But if you look at the Text class you will see there is no name and description that has been set for the plugin. but you will see it print out. so when you monkey around with it you get things like infinite looping. incorrect information. or the language editor cannot do it's job. This is one of those things that falls into the group "just is" i didn't make it. but this is how it works and this is how it has to be set up to function properly. any deviation can cause issues. so it's best not to.

I do not know how else to explain it. except for add classes inside of the Text class that have the same names as your actions. make sure you have name and description in each one of these. if you have a need to share the information. then you can access that shared information using self.plugin.text from inside of the action. and put the shared attributes at the class level for Text. and only add text = Text to the plugin at class level.

that's it. no more, no less. It is very specific on how it has to be used to work. There are other things that can be done with it. but for someone just starting out with making plugins. use this method. it is the easiest and will cause no issues


Code: Select all
import eg

eg.RegisterPlugin(
    name='Proper Text Class Demo',
    description='This is a sample description'
)


class Text:
    pluginTest = 'Plugin Test'

    class Action1:
        name = 'Action 1 Name'
        description = 'Action 1 Description'
        action1Test = 'Action 1 Test'

    class Action2:
        name = 'Action 2 Name'
        description = 'Action 2 Description'
        action2Test = 'Action 2 Test'


class ProperText(eg.PluginClass):

    text = Text

    def __init__(self):
        self.AddAction(Action1)
        self.AddAction(Action2)

    def __start__(self):
        text = self.text
        print 'Printing text.pluginTest    ', text.pluginTest


class Action1(eg.ActionClass):

    def __call__(self):

        text = self.text

        print 'Printing text.name       ', text.name
        print 'Printing text.description     ', text.description
        print 'Printing text.action1Test     ', text.action1Test

        print 'Printing self.plugin.text.pluginTest    ', self.plugin.text.pluginTest

        print 'Printing self.plugin.text.Action1.name   ', self.plugin.text.Action1.name
        print 'Printing self.plugin.text.Action1.description    ', self.plugin.text.Action1.description
        print 'Printing self.plugin.text.Action1.action1Test    ', self.plugin.text.Action1.action1Test

        print 'Printing self.plugin.text.Action2.name   ', self.plugin.text.Action2.name
        print 'Printing self.plugin.text.Action2.description    ', self.plugin.text.Action2.description
        print 'Printing self.plugin.text.Action2.action2Test    ', self.plugin.text.Action2.action2Test

        info = (
            'Now we are going to pass the translated text class\n'
            'to another class that is not related to the plugin.\n\n'
            'This can be done one of 2 ways. You can either pass\n'
            'an instance of the plugin to the class and have the\n'
            'class then access the text.\n'
            'Like So:\n'
            'testClass = TestClass(self)\n'
            '    if you initialize the outside class from the plugin\n'
            '    and want it to access the whole text class you can\n'
            '    pass an instance of self (which is the instance of the plugin)\n'
            '    and you will access the text class using plugin.text\n\n'
            'testClass = TestClass(self.plugin)\n'
            '    if you start the class from an action but want to access\n'
            '    the whole text class then you would need to pass an instance\n'
            '    of the plugin and access it using plugin.text\n\n'
            'Or you can simply pass an instance of the text class.\n'
            'testClass = TestClass(self.text)\n'
            '    By using this from the plugin you will have access to the\n'
            '    whole text class. But if you do this from inside of the\n'
            '    action you will only have access to the actions text class\n\n'
        )

        print info

        print 'Passing the actions text class\n\n'
        TestClass(self.text)
        print '\n\n'
        print 'Passing the plugins text class\n\n'
        TestClass(self.plugin.text)


class Action2(eg.ActionClass):

    def __call__(self):
        print 'Use Action1'


class TestClass:

    def __init__(self, text):

        print 'Printing text.name   ', text.name
        print 'Printing text.description   ', text.description


A loved one and Time, The 2 things that can never be replaced.

Family, The only thing you don't get to choose in life.
User avatar
kgschlosser
Site Admin
 
Posts: 1368
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: Why was text=Text changed to text=Text() in Speech plugi

Postby kgschlosser » Thu Jan 05, 2017 9:18 am

pearbear wrote:but I have a question about that. Why not just do:
Code: Select all
print Text.action2Test

instead?


You can. if and only if you have subclassed eg.TranslatableStrings

Code: Select all

class Text(eg.TranslatableStrings):



now I have never used the Language Editor and do not know how doing this functions instead of doing

Code: Select all
class Text:

and adding the text = Text to the plugin

I have seen the subclassed method used all throughout the core code. and I would imagine it shows up in the language editor. now in what context it show up I am unsure of

I am going to tinker with it now and see
A loved one and Time, The 2 things that can never be replaced.

Family, The only thing you don't get to choose in life.
User avatar
kgschlosser
Site Admin
 
Posts: 1368
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: Why was text=Text changed to text=Text() in Speech plugi

Postby kgschlosser » Thu Jan 05, 2017 9:39 am

ok this is what I have found.

You can use the subclass method and it will show up in the editor.

but.....If you do not do text = Text in the plugin you will have to add name = 'action name' and description = 'action description' in each action at the class level. if you do specify the text = Text you will have to add a class in the Text class with the name name as the action with name and description under that class.

doing the above allows you to access the text class directly using Text.someattributename and have the language translation gobbly gook work properly.

I was correct in how it works.
A loved one and Time, The 2 things that can never be replaced.

Family, The only thing you don't get to choose in life.
User avatar
kgschlosser
Site Admin
 
Posts: 1368
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA


Return to Coding Corner

Who is online

Users browsing this forum: No registered users and 3 guests