class Application: """cRIO GUI application class. Parses command line arguments. Runs application including splash screen (can be disabled by command line option). Splash screen is shown during SAL initialization. Parameters ---------- eui_class : `class` Class, ideally child of QMainWindow, which will be instantiated after wait for SAL/DDS initialization. It's parameters are SALComm created with addComm method. Usage ----- .. code-block:: python from PySide2.QtWidgets import QApplication class EUI(QMainWindow): ... if __name__ == "__main__": app = Application(EUI) app.addComm("MTM1M3") app.addComm("MTMount", include=["azimuth", "elevation"]) app.run() """ def __init__(self, eui_class): self._eui_class = eui_class self._app = QApplication(sys.argv) parser = QCommandLineParser() parser.addHelpOption() parser.addVersionOption() noSplash = QCommandLineOption(["n", "no-splash"], "don't show splash screen") parser.addOption(noSplash) parser.process(self._app) self._loop = QEventLoop(self._app) asyncio.set_event_loop(self._loop) self._comms = [] self._splash = not (parser.isSet(noSplash)) self._eui = None def addComm(self, name, manual=None, **kwargs): """Adds SALComm object to parameters of QMainWindow class. Parameters ---------- name : `str` Remote name. manual : `hash` Events and telemetry topics created with optional arguments. Keys are events and telemetry names, values is a hash of additional arguments. **kwargs : `dict` Optional parameters passed to remote. """ self._comms.append(create(name, manual=manual, **kwargs)) def run(self): """Runs the application. Creates splash screen, display it if requested. Creates and display main window after SAL/DDS is initialized.""" class AppSplashScreen(SplashScreen): def started(splash, *comms): self._eui = self._eui_class(*comms) splash.finish(self._eui) self._eui.show() splash = AppSplashScreen(*self._comms, show=self._splash) if self._splash: splash.show() def handler(signum, frame): print(f"Catching signal {signum}, exiting") self._loop.call_soon(splash.stop) self._loop.call_soon(self._app.closeAllWindows) for signum in [signal.SIGINT, signal.SIGHUP, signal.SIGTERM]: signal.signal(signum, handler) # Run the main Qt loop with self._loop: self._loop.run_forever()
class Application(QtApplication): """ Add asyncio support . Seems like a complete hack compared to twisted but whatever. """ loop = Instance(QEventLoop) def __init__(self): super().__init__() #: Set event loop policy for windows if sys.platform == 'win32': asyncio.set_event_loop_policy( asyncio.WindowsSelectorEventLoopPolicy()) self.loop = QEventLoop(self._qapp) asyncio.set_event_loop(self.loop) for name in ('asyncqt._unix._Selector', 'asyncqt._QEventLoop', 'asyncqt._SimpleTimer'): log = logging.getLogger(name) log.setLevel(logging.WARN) def start(self): """ Run using the event loop """ log.info("Application starting") with self.loop: self.loop.run_forever() def deferred_call(self, callback, *args, **kwargs): """ Invoke a callable on the next cycle of the main event loop thread. Parameters ---------- callback : callable The callable object to execute at some point in the future. args, kwargs Any additional positional and keyword arguments to pass to the callback. """ if asyncio.iscoroutinefunction(callback): task = lambda: asyncio.ensure_future(callback(*args, **kwargs)) return self.loop.call_soon(task) return super().deferred_call(callback, *args, **kwargs) def timed_call(self, ms, callback, *args, **kwargs): """ Invoke a callable on the main event loop thread at a specified time in the future. Parameters ---------- ms : int The time to delay, in milliseconds, before executing the callable. callback : callable The callable object to execute at some point in the future. args, kwargs Any additional positional and keyword arguments to pass to the callback. """ if asyncio.iscoroutinefunction(callback): task = lambda: asyncio.ensure_future(callback(*args, **kwargs)) return self.loop.call_later(ms / 1000, task) return super().timed_call(ms, callback, *args, **kwargs)