How to call a hotkey handler function from elsewhere

Asked by Greg Howell

I am setting a hot key to stop my scripts with the following:
Env.addHotkey(key.F12, KeyModifier.CTRL, self.breakscript)

And my breakscript definition:

def breakscript(self, event):
    App.focus("ArcMap")
    click(self.inside_sitmap)
    wheel(self.inside_sitmap, WHEEL_DOWN, 1)
    exit(1)

This code works great and I use it all the time to stop my scripts and have it return my ArcMap to the correct zoom level. But what if I wanted to call breakscript from anywhere else? I would need to create a SikuliEvent object to pass to it as "event". I've tried a couple ways to instantiate that object, but the compiler just keeps telling me that "AttributeError: 'module' object has no attribute: 'SikuliEvent'" or "NameError: global name 'SikuliEvent' is not defined". I have the following import at the top of my file:

from sikuli import *

So, is it possible to manually create an event object outside of a addHotKey or some kind of onAppear procedure? What is the correct syntax for doing so?

Thanks,

Question information

Language:
English Edit question
Status:
Solved
For:
SikuliX Edit question
Assignee:
No assignee Edit question
Solved by:
Greg Howell
Solved:
Last query:
Last reply:
Revision history for this message
RaiMan (raimund-hocke) said :
#1

If I understand you right:

since breakscript has a parameter self, I guess it is some method in a Python class (since I do not know the class name, I call it SomeClass here)

You might call this method from elsewhere by just using:
SomeClass(). breakscript(None)

Another possibility would be, to move the code out of breakscript into a global function
def myTearDown(SomeClassInstance)
    App.focus("ArcMap")
    click(SomeClassInstance.inside_sitmap)
    wheel(SomeClassInstance.inside_sitmap, WHEEL_DOWN, 1)
    exit(1)

and reduce break script to
def breakscript(self, event):
    myTearDown(self)

So you now can call myTearDown(SomeClassInstance) from anywhere.

--- Your initial question:
How to instantiate a SikuliEvent object

in the case of hotkeys, the event has no meaningful content and is not of type SikuliEvent but of type HotkeyEvent.

this is how the addHotkey is setup in sikuli.Env.py:

from org.sikuli.basics import HotkeyListener
from org.sikuli.basics import HotkeyManager
from org.sikuli.script import Env as JEnv
class Env(JEnv):
    @classmethod
    def addHotkey(cls, key, modifiers, handler):
        class AnonyListener(HotkeyListener):
            def hotkeyPressed(self, event):
                handler(event)
        return HotkeyManager.getInstance().addHotkey(key, modifiers, AnonyListener())

and this is the HotkeyEvent as defined on the Java level:

package org.sikuli.basics;
public class HotkeyEvent {
   public int keyCode;
   public int modifiers;
   public HotkeyEvent(int code_, int mod_){
      init(code_, mod_);
   }
   void init(int code_, int mod_){
      keyCode = code_;
      modifiers = mod_;
   }
}

... so it only contains the key information, for which it was defined (allowing to use the the same hotkey handler for different hotkeys - if that makes sense at all ;-)

This all is like it is, because including Java 7, functions are not first class objects like in Python and hence cannot passed around as callback functions (you always need some interface class, that defines a callback function, that you have to overwrite in an implementing class).
Only beginning with Java 8 you can have functions as parameters.

So with Python (and hence Sikuli scripting level) you should have a global utility class or even a module, that you just import to access your utility functions from where needed.

Revision history for this message
Greg Howell (ghowell) said :
#2

RaiMan,

This is great. Thank you. Your first couple of suggestions will work perfectly for me. You are right about the python class. All my code is within a large class so I can do unittest stuff. But I didn't even consider the option of passing "None". I don't really need the event anyway, so that is the best and easiest option for me. But creating another function that is called from breakscript is also a good option which I may use elsewhere.

In either case, thanks for the reply.

Greg