diff --git a/eg/Classes/Document.py b/eg/Classes/Document.py index 9c46c59bf..fb0ece183 100644 --- a/eg/Classes/Document.py +++ b/eg/Classes/Document.py @@ -334,6 +334,8 @@ def Load(self, filePath): else: self.SetFilePath(filePath) eg.TreeLink.StartLoad() + eg.EventGhostEvent.ClearCachedEvents() + xmlTree = ElementTree.parse(filePath) node = xmlTree.getroot() root = self.RootItem(self, node) diff --git a/eg/Classes/EventGhostEvent.py b/eg/Classes/EventGhostEvent.py index fc4cd4ce5..de3e127f2 100644 --- a/eg/Classes/EventGhostEvent.py +++ b/eg/Classes/EventGhostEvent.py @@ -17,8 +17,11 @@ # with EventGhost. If not, see . from fnmatch import fnmatchcase -from threading import Event -from time import clock +from time import clock, localtime +from collections import deque +from copy import deepcopy as copy +import threading +import six # Local imports import eg @@ -32,7 +35,41 @@ config = eg.config #pylint: enable-msg=C0103 -class EventGhostEvent(object): + +class InstanceSingleton(type): + + def __init__(cls, name, bases, dct): + super(InstanceSingleton, cls).__init__(name, bases, dct) + cls._instances = {} + + def __call__(cls, suffix="", payload=None, prefix="Main", source=eg, new_thread=False, new_event=False): + if new_thread: + return super(InstanceSingleton, cls).__call__(new_thread=True) + + key = prefix + if suffix: + key += '.' + suffix + + if new_event: + instance = super(InstanceSingleton, cls).__call__(suffix, payload, prefix, source, new_event=new_event) + cls._instances[key] = instance + return instance + + if '.' in key: + key = key.rsplit('.', 1)[0] + + if key not in cls._instances: + suf = key.replace(prefix, '') + + instance = super(InstanceSingleton, cls).__call__(suf, None, prefix, source, new_event=True) + cls._instances[key] = instance + + instance = cls._instances[key] + return instance(suffix, payload, prefix, source) + + +@six.add_metaclass(InstanceSingleton) +class EventGhostEvent(threading.Thread): """ .. attribute:: string @@ -83,51 +120,262 @@ class EventGhostEvent(object): """ skipEvent = False - def __init__(self, suffix="", payload=None, prefix="Main", source=eg): - self.string = prefix + "." + suffix - self.prefix = prefix - self.suffix = suffix - self.payload = payload - self.source = source - self.time = clock() - self.isEnded = False - self.shouldEnd = Event() - self.upFuncList = [] + _instances = {} + + @classmethod + def ClearCachedEvents(cls): + cls._instances.clear() + + @classmethod + def RemoveCachedEvent(cls, eventItem): + if '*' in eventItem.name or '?' in eventItem.name: + for key in cls._instances.keys(): + if fnmatchcase(key, eventItem.name): + pattern = key + break + else: + pattern = [] + for item in eventItem.name.split('.'): + if '*' in item or '?' in item: + break + + pattern += [item] + + pattern = '.'.join(pattern) - def AddUpFunc(self, func, *args, **kwargs): - if self.isEnded: - func(*args, **kwargs) else: - self.upFuncList.append((func, args, kwargs)) + pattern = eventItem.name.split('.')[-1] + pattern = '.'.join(pattern) - def DoUpFuncs(self): - for func, args, kwargs in self.upFuncList: - func(*args, **kwargs) - del self.upFuncList[:] - self.isEnded = True + if pattern in cls._instances: + instance = cls._instances[pattern] + if eventItem in instance.eventHandlerList: + instance.eventHandlerList.remove(eventItem) - def Execute(self): - #start = clock() - eventString = self.string - if eventString in eg.notificationHandlers: - for listener in eg.notificationHandlers[eventString].listeners: - if listener(self) is True: + + @classmethod + def AddCachedEvent(cls, eventItem): + if '*' in eventItem.name or '?' in eventItem.name: + for key, instance in cls._instances.items(): + if fnmatchcase(key, eventItem.name): + instance.eventHandlerList.add(eventItem) return - eg.event = self - eg.eventString = eventString + pattern = [] + for item in eventItem.name.split('.'): + if '*' in item or '?' in item: + break + + pattern += [item] + + else: + pattern = eventItem.name.split('.')[-1] + + pattern = '.'.join(pattern) + + if pattern in cls._instances: + instance = cls._instances[pattern] + instance.eventHandlerList.add(eventItem) + return + + + if '.' in pattern: + prefix, suffix = pattern.split('.', 1) + else: + prefix = pattern + suffix = '' + + for plugin in eg.pluginList: + if plugin.info.eventPrefix == prefix: + source = plugin.info.plugin + break + else: + source = eg + + cls(suffix=suffix, prefix=prefix, source=source, new_event=True) + + def __init__( + self, + suffix="", + payload=None, + prefix="Main", + source=eg, + new_thread=False, + new_event=False + ): + eventString = prefix + if suffix: + eventString += '.' + suffix + + threading.Thread.__init__(self, name=eventString) + + if new_thread: + return + + self.cached_string = eventString + self.cached_prefix = prefix + self.cached_suffix = suffix + self.cached_source = source + + self.time = clock() + + self.eventHandlerList = set() + self.last_event_time = 0 + self.event_count = 0 + + self._queue = deque() + self._queue_event = threading.Event() + self._exit_event = threading.Event() + self.__event = None + + + @property + def string(self): + if self.__event is not None: + return self.__event.string + + return self.cached_string + + @property + def prefix(self): + if self.__event is not None: + return self.__event.prefix + + return self.cached_prefix + + @property + def prefix(self): + if self.__event is not None: + return self.__event.suffix + + return self.cached_suffix + + @property + def payload(self): + if self.__event is not None: + return self.__event.payload + + @property + def source(self): + if self.__event is not None: + return self.__event.source + + return self.cached_source + + def __call__(self, suffix, payload, prefix, source): + return Event(self, prefix, suffix, payload, source) + + def AddEvent(self, eventHolder): + self._queue.append(eventHolder) + self.start() + + @property + def result(self): + if self.__event is not None: + return self.__event.result - eventHandlerList = [] - for key, val in eg.eventTable.iteritems(): - if ( - eventString == key or - (("*" in key or "?" in key) and fnmatchcase(eventString, key)) - ): - eventHandlerList += val + @result.setter + def result(self, value): + if self.__event is not None: + self.__event.result = value + + @property + def eventString(self): + if self.__event is not None: + return self.__event.string + + @eventString.setter + def eventString(self, value): + pass + + @property + def programCounter(self): + if self.__event is not None: + return self.__event.programCounter + + @programCounter.setter + def programCounter(self, value): + if self.__event is not None: + self.__event.programCounter = value + + @property + def programReturnStack(self): + if self.__event is not None: + return self.__event.programReturnStack + + return [] + + @programReturnStack.setter + def programReturnStack(self, value): + if self.__event is not None: + self.__event.programReturnStack = value + + @property + def indent(self): + if self.__event is not None: + return self.__event.index + + return 0 + + @indent.setter + def indent(self, value): + if self.__event is not None: + self.__event.indent = value + + @property + def stopExecutionFlag(self): + if self.__event is not None: + return self.__event.stopEcevutionFlag + + return False + + @stopExecutionFlag.setter + def stopExecutionFlag(self, value): + if self.__event is not None: + self.__event.stopExecutionFlag = value + + @property + def lastFoundWindows(self): + if self.__event is not None: + return self.__event.lastFoundWindows + + return [] + + @lastFoundWindows.setter + def lastFoundWindows(self, value): + if self.__event is not None: + self.__event.lastFoundWindows = value + + @property + def currentItem(self): + if self.__event is not None: + return self.__event.currentItem + + @currentItem.setter + def currentItem(self, value): + if self.__event is not None: + self.__event.currentItem = value + + def __execute(self): + self.event_count += 1 + self.last_event_time = localtime() + + eventString = self.__event.string + + if eventString in eg.notificationHandlers: + for listener in eg.notificationHandlers[eventString].listeners: + if listener(self.__event) is True: + return activeHandlers = set() - for eventHandler in eventHandlerList: + for eventHandler in self.eventHandlerList: obj = eventHandler + if '*' in obj.name or '?' in obj.name: + if not fnmatchcase(eventString, obj.name): + continue + elif obj.name != eventString: + continue + while obj: if not obj.isEnabled: break @@ -136,18 +384,18 @@ def Execute(self): activeHandlers.add(eventHandler) for listener in eg.log.eventListeners: - listener.LogEvent(self) + listener.LogEvent(self.__event) if config.onlyLogAssigned and len(activeHandlers) == 0: - self.SetStarted() + self.__event.SetStarted() return # show the event in the logger - LogEvent(self) + LogEvent(self.__event) activeHandlers = sorted(activeHandlers, key=GetItemPath) - eg.SetProcessingState(2, self) + eg.SetProcessingState(2, self.__event) for eventHandler in activeHandlers: try: eg.programCounter = (eventHandler.parent, None) @@ -155,17 +403,120 @@ def Execute(self): RunProgram() except: eg.PrintTraceback() - if self.skipEvent: + if self.__event.skipEvent: break - self.SetStarted() - eg.SetProcessingState(1, self) + self.__event.SetStarted() + eg.SetProcessingState(1, self.__event) + + def run(self): + while not self._exit_event.is_set(): + while len(self._queue): + self.__event = self._queue.popleft() + self.__execute() + self.__event = None + + self._queue_event.clear() + self._queue_event.wait(3) + + if self._queue_event.is_set(): + self._queue_event.clear() + continue + else: + self._exit_event.set() + + self._exit_event.clear() + self._queue_event.clear() + + def start(self): + if self.is_alive(): + self._queue_event.set() + else: + try: + threading.Thread.start(self) + except RuntimeError: + instance = self.__class__(new_thread=True) + self.__dict__.update(instance.__dict__) + threading.Thread.start(self) + + def stop(self): + if self.is_alive(): + self._exit_event.set() + self._queue_event.set() + self._queue.clear() + self.join(3) + + def AddUpFunc(self, func, *args, **kwargs): + if self.__event is not None: + self.__event.AddUpFunc(func, *args, **kwargs) + + def DoUpFuncs(self): + if self.__event is not None: + self.__event.DoUpFuncs() + + def SetShouldEnd(self): + if self.__event is not None: + self.__event.SetShouldEnd() + + def SetStarted(self): + if self.__event is not None: + self.__event.SetStarted() + + +class Event(object): + def __init__(self, parent, prefix, suffix, payload, source): + self._parent = parent + self.string = prefix + if suffix: + self.string += '.' + suffix + + self.prefix = prefix + self.suffix = suffix + if isinstance(payload, (dict, list)): + try: + self.payload = copy(payload) + except: + self.payload = payload + else: + self.payload = payload + + self.source = source + self.skipEvent = False + + self.upFuncList = [] + self.isEnded = False + self.shouldEnd = threading.Event() + self.time = clock() + + self.result = None + self.eventString = self.string + self.programCounter = None + self.programReturnStack = [] + self.indent = 0 + self.stopExecutionFlag = False + self.lastFoundWindows = [] + self.currentItem = None + + def AddUpFunc(self, func, *args, **kwargs): + if self.isEnded: + func(*args, **kwargs) + else: + self.upFuncList.append((func, args, kwargs)) + + def DoUpFuncs(self): + for func, args, kwargs in self.upFuncList: + func(*args, **kwargs) + del self.upFuncList[:] + self.isEnded = True def SetShouldEnd(self): if not self.shouldEnd.isSet(): self.shouldEnd.set() eg.SetProcessingState(0, self) - actionThread.Call(self.DoUpFuncs) + self.DoUpFuncs() def SetStarted(self): if self.shouldEnd.isSet(): self.DoUpFuncs() + + def Execute(self): + self._parent.AddEvent(self) diff --git a/eg/Classes/EventItem.py b/eg/Classes/EventItem.py index 425a662dc..6b678e838 100644 --- a/eg/Classes/EventItem.py +++ b/eg/Classes/EventItem.py @@ -86,6 +86,7 @@ def RegisterEvent(self, eventString): if eventString not in eventTable: eventTable[eventString] = [] eventTable[eventString].append(self) + eg.EventGhostEvent.AddCachedEvent(eventString, self) @eg.AssertInActionThread def RenameTo(self, newName): @@ -110,3 +111,6 @@ def UnRegisterEvent(self, eventString): pass if len(eventTable[eventString]) == 0: del eventTable[eventString] + + eg.EventGhostEvent.RemoveCachedEvent(eventString, self) + diff --git a/eg/Core.py b/eg/Core.py index 55c48e4db..ccd209720 100644 --- a/eg/Core.py +++ b/eg/Core.py @@ -66,22 +66,16 @@ eg.systemEncoding = locale.getdefaultlocale()[1] eg.document = None eg.mainFrame = None -eg.result = None + eg.plugins = eg.Bunch() eg.globals = eg.Bunch() eg.globals.eg = eg -eg.event = None eg.eventTable = {} -eg.eventString = "" eg.notificationHandlers = {} -eg.programCounter = None -eg.programReturnStack = [] -eg.indent = 0 + eg.pluginList = [] eg.mainThread = threading.currentThread() -eg.stopExecutionFlag = False -eg.lastFoundWindows = [] -eg.currentItem = None + eg.actionGroup = eg.Bunch() eg.actionGroup.items = [] eg.GUID = eg.GUID() diff --git a/eg/__init__.py b/eg/__init__.py index 134cb8c40..7089a99d9 100644 --- a/eg/__init__.py +++ b/eg/__init__.py @@ -35,8 +35,164 @@ APP_NAME = "EventGhost" - class DynamicModule(object): + + _result = None + + @property + def result(self): + t = threading.current_thread() + if isinstance(t, eg.EventGhostEvent): + return t.result + return self._result + + @result.setter + def result(self, value): + t = threading.current_thread() + if isinstance(t, eg.EventGhostEvent): + t.result = value + + else: + self._result = value + + _event = None + + @property + def event(self): + t = threading.current_thread() + if isinstance(t, eg.EventGhostEvent): + return t + return self._event + + @event.setter + def event(self, value): + t = threading.current_thread() + if not isinstance(t, eg.EventGhostEvent): + self._event = value + + _eventString = '' + + @property + def eventString(self): + t = threading.current_thread() + if isinstance(t, eg.EventGhostEvent): + return t.eventString + return self._eventString + + @eventString.setter + def eventString(self, value): + t = threading.current_thread() + if not isinstance(t, eg.EventGhostEvent): + self._eventString = value + + _programCounter = None + + @property + def programCounter(self): + t = threading.current_thread() + if isinstance(t, eg.EventGhostEvent): + return t.programCounter + return self._programCounter + + @programCounter.setter + def programCounter(self, value): + t = threading.current_thread() + if isinstance(t, eg.EventGhostEvent): + t.programCounter = value + + else: + self._programCounter = value + + _programReturnStack = [] + + @property + def programReturnStack(self): + t = threading.current_thread() + if isinstance(t, eg.EventGhostEvent): + return t.programReturnStack + return self._programReturnStack + + @programReturnStack.setter + def programReturnStack(self, value): + t = threading.current_thread() + if isinstance(t, eg.EventGhostEvent): + t.programReturnStack = value + + else: + self._programReturnStack = value + + _indent = 0 + + @property + def indent(self): + t = threading.current_thread() + if isinstance(t, eg.EventGhostEvent): + return t.indent + return self._indent + + @indent.setter + def indent(self, value): + t = threading.current_thread() + if isinstance(t, eg.EventGhostEvent): + t.indent = value + + else: + self._indent = value + + _stopExecutionFlag = False + + @property + def stopExecutionFlag(self): + t = threading.current_thread() + if isinstance(t, eg.EventGhostEvent): + return t.stopExecutionFlag + return self._stopExecutionFlag + + @stopExecutionFlag.setter + def stopExecutionFlag(self, value): + t = threading.current_thread() + if isinstance(t, eg.EventGhostEvent): + t.stopExecutionFlag = value + + else: + self._stopExecutionFlag = value + + _lastFoundWindows = [] + + @property + def lastFoundWindows(self): + t = threading.current_thread() + if isinstance(t, eg.EventGhostEvent): + return t.lastFoundWindows + return self._lastFoundWindows + + @lastFoundWindows.setter + def lastFoundWindows(self, value): + t = threading.current_thread() + if isinstance(t, eg.EventGhostEvent): + t.lastFoundWindows = value + + else: + self._lastFoundWindows = value + + _currentItem = None + + @property + def currentItem(self): + t = threading.current_thread() + if isinstance(t, eg.EventGhostEvent): + return t.currentItem + + return self._currentItem + + @currentItem.setter + def currentItem(self, value): + t = threading.current_thread() + if isinstance(t, eg.EventGhostEvent): + t.currentItem = value + else: + self._currentItem = value + def __init__(self): mod = sys.modules[__name__] self.__dict__ = mod.__dict__