From 772c016b413a0faae64110d7a147bd0cfadb2a3f Mon Sep 17 00:00:00 2001 From: Tarashish Mishra Date: Wed, 5 Jun 2024 09:25:15 +0530 Subject: [PATCH] wait for websockify to start before starting qgis Related to https://github.com/jupyterhub/jupyter-remote-desktop-proxy/issues/105. When QGIS starts before VNC connection is established, the QGIS process doesn't get attached to the VNC session and runs in the background. We wait for the VNC connection to be established to make sure the QGIS GUI shows up in the VNC session. --- jupyter_remote_qgis_proxy/handlers.py | 19 +++---- jupyter_remote_qgis_proxy/logger.py | 5 ++ .../qgis/scripts/zoom.py | 1 + jupyter_remote_qgis_proxy/qgis/utils.py | 2 +- jupyter_remote_qgis_proxy/utils.py | 50 +++++++++++++++++++ 5 files changed, 65 insertions(+), 12 deletions(-) create mode 100644 jupyter_remote_qgis_proxy/logger.py create mode 100644 jupyter_remote_qgis_proxy/utils.py diff --git a/jupyter_remote_qgis_proxy/handlers.py b/jupyter_remote_qgis_proxy/handlers.py index e1d527b..a3ce473 100644 --- a/jupyter_remote_qgis_proxy/handlers.py +++ b/jupyter_remote_qgis_proxy/handlers.py @@ -1,23 +1,20 @@ -import os -import logging - from jupyter_remote_desktop_proxy.handlers import DesktopHandler from tornado import web +from tornado.ioloop import IOLoop -from .qgis.utils import open_qgis - -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) +from .utils import is_process_running, start_qgis class QgisHandler(DesktopHandler): @web.authenticated async def get(self): - logging.info("Starting QGIS") + await super().get() action = self.get_argument("action", None) url = self.get_argument("url", None) project_name = self.get_argument("project_name", "new project") layer_name = self.get_argument("layer_name", "Vector Layer") - if action and url: - open_qgis(action, url=url, project_name=project_name, layer_name=layer_name) - await super().get() + + if not is_process_running("websockify"): + IOLoop.current().call_later(3, lambda:start_qgis(action, url, project_name, layer_name)) + + diff --git a/jupyter_remote_qgis_proxy/logger.py b/jupyter_remote_qgis_proxy/logger.py new file mode 100644 index 0000000..f12c0c7 --- /dev/null +++ b/jupyter_remote_qgis_proxy/logger.py @@ -0,0 +1,5 @@ +import logging + + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) diff --git a/jupyter_remote_qgis_proxy/qgis/scripts/zoom.py b/jupyter_remote_qgis_proxy/qgis/scripts/zoom.py index 5a8d70b..1ee9981 100644 --- a/jupyter_remote_qgis_proxy/qgis/scripts/zoom.py +++ b/jupyter_remote_qgis_proxy/qgis/scripts/zoom.py @@ -5,6 +5,7 @@ # Get the QGIS interface iface = qgis.utils.iface +iface.mainWindow().showMaximized() # Get the active map canvas canvas = iface.mapCanvas() diff --git a/jupyter_remote_qgis_proxy/qgis/utils.py b/jupyter_remote_qgis_proxy/qgis/utils.py index 9f72865..9235ef8 100644 --- a/jupyter_remote_qgis_proxy/qgis/utils.py +++ b/jupyter_remote_qgis_proxy/qgis/utils.py @@ -28,4 +28,4 @@ def open_qgis(action="add_vector_layer", **kwargs): } project_file_content = action_template.format(**layer_args) f.write(project_file_content) - subprocess.Popen(["qgis", "--nologo", "--project", file_path, "--code", zoom_script_path, "--code", maximize_script_path]) + subprocess.Popen(["qgis", "--nologo", "--project", file_path, "--code", zoom_script_path]) diff --git a/jupyter_remote_qgis_proxy/utils.py b/jupyter_remote_qgis_proxy/utils.py new file mode 100644 index 0000000..70aecfc --- /dev/null +++ b/jupyter_remote_qgis_proxy/utils.py @@ -0,0 +1,50 @@ +import psutil +import time + +from .logger import logger +from .qgis.utils import open_qgis + + +def is_process_running(process_name): + """ + Checks if a process with the given name is running. + """ + for process in psutil.process_iter(): + try: + if process.name().lower() == process_name.lower(): + return True + except (psutil.NoSuchProcess, psutil.AccessDenied): + pass + return False + + +def wait_for_condition(condition, timeout=10): + """ + Waits for a condition to be true for a specified timeout. + + Args: + condition: A callable that returns True when the condition is met. + timeout: The maximum time in seconds to wait (default: 10). + + Returns: + True if the condition becomes true before the timeout, False otherwise. + """ + start_time = time.time() + while time.time() - start_time < timeout: + if condition(): + return True + time.sleep(0.5) + return False + + +def start_qgis(action, url, project_name, layer_name): + """Starts QGIS and waits for websockify to start if necessary.""" + logger.info("Waiting for websockify to start") + is_websockify_running = lambda: is_process_running("websockify") + running = wait_for_condition(is_websockify_running, timeout=10) + logger.info(f"Websockify is running: {running}") + logger.info("Starting QGIS") + if action and url: + open_qgis(action, url=url, project_name=project_name, layer_name=layer_name) + elif not is_process_running("qgis"): + open_qgis()