diff --git a/noisicaa/core/process_manager.py b/noisicaa/core/process_manager.py index b2ab3f55..9997fe56 100644 --- a/noisicaa/core/process_manager.py +++ b/noisicaa/core/process_manager.py @@ -281,8 +281,12 @@ class ProcessImpl(object): self.event_loop = self.create_event_loop() asyncio.set_event_loop(self.event_loop) - self.event_loop.run_until_complete( - self.main_async(ready_callback, *args, **kwargs)) + try: + self.event_loop.run_until_complete( + self.main_async(ready_callback, *args, **kwargs)) + finally: + self.event_loop.stop() + self.event_loop.close() async def main_async(self, ready_callback, *args, **kwargs): self.manager = ManagerStub(self.event_loop, self.manager_address) diff --git a/noisicaa/editor.py b/noisicaa/editor.py index 7e1effff..db18c04d 100644 --- a/noisicaa/editor.py +++ b/noisicaa/editor.py @@ -1,5 +1,8 @@ #!/usr/bin/python3 +import pyximport +pyximport.install() + import argparse import asyncio import os @@ -9,6 +12,43 @@ import quamash from . import logging from .runtime_settings import RuntimeSettings +from .ui import ui_process +from .core import ipc + +class ProcessImpl(object): + async def setup(self): + pass + + async def cleanup(self): + pass + + def main(self, *args, **kwargs): + # Create a new event loop to replace the one we inherited. + self.event_loop = self.create_event_loop() + asyncio.set_event_loop(self.event_loop) + + try: + self.event_loop.run_until_complete( + self.main_async(*args, **kwargs)) + finally: + pass + #self.event_loop.stop() + #self.event_loop.close() + + async def main_async(self, *args, **kwargs): + self.server = ipc.Server(self.event_loop, 'ui') + async with self.server: + try: + await self.setup() + + return await self.run(*args, **kwargs) + finally: + await self.cleanup() + + +class UIProcess(ui_process.UIProcessMixin, ProcessImpl): + pass + def main(argv): runtime_settings = RuntimeSettings() @@ -31,17 +71,8 @@ def main(argv): logging.info("RuntimeSettings: %s", runtime_settings.to_json()) - from .ui.editor_app import EditorApp - app = EditorApp(runtime_settings, args.path) - event_loop = quamash.QEventLoop(app) - asyncio.set_event_loop(event_loop) - - with event_loop: - app.setup() - try: - event_loop.run_forever() - finally: - app.cleanup() + proc = UIProcess() + proc.main() return 0 diff --git a/noisicaa/ui/editor_app.py b/noisicaa/ui/editor_app.py index 2bdeb912..dd6e5d04 100644 --- a/noisicaa/ui/editor_app.py +++ b/noisicaa/ui/editor_app.py @@ -56,9 +56,11 @@ class ExceptHook(object): class BaseEditorApp(QApplication): - def __init__(self, runtime_settings, settings=None): + def __init__(self, process, runtime_settings, settings=None): super().__init__(['noisicaƤ']) + self.process = process + self.runtime_settings = runtime_settings if settings is None: @@ -68,6 +70,8 @@ class BaseEditorApp(QApplication): self.settings = settings self.dumpSettings() + self.setQuitOnLastWindowClosed(False) + self._projects = [] self._exit_code = None @@ -112,13 +116,16 @@ class BaseEditorApp(QApplication): self.sequencer.close() self.sequencer = None + def quit(self): + self.process.quit() + def createSequencer(self): return None def createMidiHub(self): return devices.MidiHub(self.sequencer) - def exit(self, exit_code): + def exit(self, exit_code=0): logger.info("exit(%d) received", exit_code) self._exit_code = exit_code super().exit(exit_code) @@ -191,8 +198,8 @@ class BaseEditorApp(QApplication): class EditorApp(BaseEditorApp): - def __init__(self, runtime_settings, paths, settings=None): - super().__init__(runtime_settings, settings) + def __init__(self, process, runtime_settings, paths, settings=None): + super().__init__(process, runtime_settings, settings) self.paths = paths @@ -225,23 +232,18 @@ class EditorApp(BaseEditorApp): self.aboutToQuit.connect(self.shutDown) - def cleanup(self): - super().cleanup() - - if self._sequencer is not None: - self._sequencer.close() - self._sequencer = None - def shutDown(self): logger.info("Shutting down.") - self.win.storeState() - self.settings.sync() - self.dumpSettings() + if self.win is not None: + self.win.storeState() + self.settings.sync() + self.dumpSettings() def cleanup(self): - self.win.closeAll() - self.win = None + if self.win is not None: + self.win.closeAll() + self.win = None super().cleanup() diff --git a/noisicaa/ui/ui_process.py b/noisicaa/ui/ui_process.py new file mode 100644 index 00000000..c91732a8 --- /dev/null +++ b/noisicaa/ui/ui_process.py @@ -0,0 +1,56 @@ +#!/usr/bin/python3 + +import functools +import asyncio +import logging +import threading +import time +import uuid + +import quamash +from PyQt5.QtCore import Qt +from PyQt5 import QtCore +from PyQt5 import QtWidgets +from PyQt5 import QtGui + +from noisicaa import core +from noisicaa.core import ipc + +from . import editor_app +from .. import runtime_settings + +logger = logging.getLogger(__name__) + + +class UIProcessMixin(object): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.app = self.create_app( + self, runtime_settings.RuntimeSettings(), []) + + def create_app(self, *args, **kwargs): + return editor_app.EditorApp(*args, **kwargs) + + def create_event_loop(self): + return quamash.QEventLoop(self.app) + + async def setup(self): + self._shutting_down = asyncio.Event() + + await super().setup() + self.app.setup() + + async def cleanup(self): + self.app.cleanup() + await super().cleanup() + + async def run(self): + await self._shutting_down.wait() + + def quit(self): + self._shutting_down.set() + + +class UIProcess(UIProcessMixin, core.ProcessImpl): + pass diff --git a/noisicaa/ui/uitest_utils.py b/noisicaa/ui/uitest_utils.py index 8890cf49..bb33b30a 100644 --- a/noisicaa/ui/uitest_utils.py +++ b/noisicaa/ui/uitest_utils.py @@ -56,7 +56,7 @@ class MockSequencer(object): class MockApp(BaseEditorApp): def __init__(self): - super().__init__(RuntimeSettings(), MockSettings()) + super().__init__(None, RuntimeSettings(), MockSettings()) def createSequencer(self): return MockSequencer()