From 9d3ec12695c13ad2c7bdf33ac8d85360177d262a Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sat, 5 Jan 2019 22:34:21 +0800 Subject: [PATCH] [Add] Add event engine --- .gitignore | 8 +-- vnpy/__init__.py | 0 vnpy/event/__init__.py | 1 + vnpy/event/engine.py | 139 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 144 insertions(+), 4 deletions(-) create mode 100644 vnpy/__init__.py create mode 100644 vnpy/event/__init__.py create mode 100644 vnpy/event/engine.py diff --git a/.gitignore b/.gitignore index 6a7585e7..9c033465 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,10 @@ +# Python +*.pyc + # IDE .vscode .idea -# Python -.pyc -.pyo - +# Temp build dist diff --git a/vnpy/__init__.py b/vnpy/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/vnpy/event/__init__.py b/vnpy/event/__init__.py new file mode 100644 index 00000000..d1f08b68 --- /dev/null +++ b/vnpy/event/__init__.py @@ -0,0 +1 @@ +from engine import Event, EventEngine, EVENT_TIMER \ No newline at end of file diff --git a/vnpy/event/engine.py b/vnpy/event/engine.py new file mode 100644 index 00000000..625c6222 --- /dev/null +++ b/vnpy/event/engine.py @@ -0,0 +1,139 @@ +from queue import Queue, Empty +from threading import Thread, Timer +from collections import defaultdict +from time import sleep +from typing import Any, Callable + +EVENT_TIMER = "eTimer" + + +class Event: + """ + Event object consists of a type string which is used + by event engine for distributing event, and a data + object which contains the real data. + """ + + def __init__(self, type: str, data: Any = None): + """""" + self.type = type + self.data = data + + +# Defines handler function to be used in event engine. +HandlerType = Callable[[Event], None] + + +class EventEngine: + """ + Event engine distributes event object based on its type + to those handlers registered. + + It also generates timer event by every interval seconds, + which can be used for timing purpose. + """ + + def __init__(self, interval: int = 1): + """ + Timer event is generated every 1 second by default, if + interval not specified. + """ + self._interval = interval + self._queue = Queue() + self._active = False + self._thread = Thread(target=self._run) + self._timer = Thread(target=self._run_timer) + self._handlers = defaultdict(list) + self._general_handlers = [] + + def _run(self): + """ + Get event from queue and then process it. + """ + while self._active: + try: + event = self._queue.get(block=True, timeout=1) + self._process(event) + except Empty: + pass + + def _process(self, event: Event): + """ + First ditribute event to those handlers registered listening + to this type. + + Then distrubute event to those general handlers which listens + to all types. + """ + if event.type in self._handlers: + [handler(event) for handler in self._handlers[event.type]] + + if self._general_handlers: + [handler(event) for handler in self._general_handlers] + + def _run_timer(self): + """ + Sleep by interval second(s) and then generate a timer event. + """ + while self._active: + sleep(self._interval) + event = Event(EVENT_TIMER) + self.put(event) + + def start(self): + """ + Start event engine to process events and generate timer events. + """ + self._active = True + self._thread.start() + self._timer.start() + + def stop(self): + """ + Stop event engine. + """ + self._active = False + self._timer.join() + self._thread.join() + + def put(self, event: Event): + """ + Put an event object into event queue. + """ + self._queue.put(event) + + def register(self, type: str, handler: HandlerType): + """ + Register a new handler function for a specific event type. Every + function can only be registered once for each event type. + """ + handler_list = self._handlers[type] + if handler not in handler_list: + handler_list.append(handler) + + def unregister(self, type: str, handler: HandlerType): + """ + Unregister an existing handler function from event engine. + """ + handler_list = self._handlers[type] + + if handler in handler_list: + handler_list.remove(handler) + + if not handler_list: + self._handlers.pop(type) + + def register_general(self, handler: HandlerType): + """ + Register a new handler function for all event types. Every + function can only be registered once for each event type. + """ + if handler not in self._general_handlers: + self._general_handlers.append(handler) + + def unregister_general(self, handler: HandlerType): + """ + Unregister an existing general handler function. + """ + if handler in self._general_handlers: + self._general_handlers.remove(handler)