diff --git a/openpype/hosts/harmony/api/lib.py b/openpype/hosts/harmony/api/lib.py
index d6fa1f30d8..134f670dc4 100644
--- a/openpype/hosts/harmony/api/lib.py
+++ b/openpype/hosts/harmony/api/lib.py
@@ -51,8 +51,8 @@ class ProcessContext:
callback()
if cls.process is not None and cls.process.poll() is not None:
log.info("Server is not running, closing")
- ProcessContext.stdout_broker.exit()
- sys.exit()
+ ProcessContext.stdout_broker.stop()
+ QtWidgets.QApplication.quit()
def signature(postfix="func") -> str:
@@ -88,7 +88,7 @@ def main(*subprocess_args):
app.setWindowIcon(icon)
ProcessContext.stdout_broker = StdOutBroker('harmony')
-
+ ProcessContext.stdout_broker.start()
launch(*subprocess_args)
loop_timer = QtCore.QTimer()
@@ -620,4 +620,3 @@ def find_node_by_name(name, node_type):
return node
return None
-
diff --git a/openpype/modules/webserver/host_console_listener.py b/openpype/modules/webserver/host_console_listener.py
index 19428adce3..6138f9f097 100644
--- a/openpype/modules/webserver/host_console_listener.py
+++ b/openpype/modules/webserver/host_console_listener.py
@@ -34,8 +34,7 @@ class HostListener:
webserver.add_route('*', "/ws/host_listener", self.websocket_handler)
def _host_is_connecting(self, host_name, label):
- from openpype.tools.stdout_broker.app import ConsoleDialog
-
+ from openpype.tools.stdout_broker.window import ConsoleDialog
""" Initialize dialog, adds to submenu. """
services_submenu = self.module._services_submenu
action = QtWidgets.QAction(label, services_submenu)
diff --git a/openpype/tools/stdout_broker/app.py b/openpype/tools/stdout_broker/app.py
index f714071b31..a42d93dab4 100644
--- a/openpype/tools/stdout_broker/app.py
+++ b/openpype/tools/stdout_broker/app.py
@@ -1,18 +1,14 @@
import os
import sys
-import re
+import threading
import collections
import websocket
import json
from datetime import datetime
-from avalon import style
from openpype_modules.webserver.host_console_listener import MsgAction
-
from openpype.api import Logger
-from Qt import QtWidgets, QtCore
-
log = Logger.get_logger(__name__)
@@ -21,53 +17,12 @@ class StdOutBroker:
Application showing console in Services tray for non python hosts
instead of cmd window.
"""
- callback_queue = None
- process = None
- webserver_client = None
- _instance = None
-
MAX_LINES = 10000
-
- sdict = {
- r">>> ":
- ' >>> ',
- r"!!!(?!\sCRI|\sERR)":
- ' !!! ',
- r"\-\-\- ":
- ' --- ',
- r"\*\*\*(?!\sWRN)":
- ' *** ',
- r"\*\*\* WRN":
- ' *** WRN',
- r" \- ":
- ' - ',
- r"\[ ":
- '[',
- r"\]":
- ']',
- r"{":
- '{',
- r"}":
- r"}",
- r"\(":
- '(',
- r"\)":
- r")",
- r"^\.\.\. ":
- ' ... ',
- r"!!! ERR: ":
- ' !!! ERR: ',
- r"!!! CRI: ":
- ' !!! CRI: ',
- r"(?i)failed":
- ' FAILED ',
- r"(?i)error":
- ' ERROR '
- }
+ TIMER_TIMEOUT = 0.200
def __init__(self, host_name):
self.host_name = host_name
- self.websocket_server = None
+ self.webserver_client = None
self.original_stdout_write = None
self.original_stderr_write = None
@@ -77,25 +32,55 @@ class StdOutBroker:
self.host_id = "{}_{}".format(self.host_name, date_str)
self._std_available = False
+ self._is_running = False
self._catch_std_outputs()
- self._connect_to_tray()
- loop_timer = QtCore.QTimer()
- loop_timer.setInterval(200)
- loop_timer.timeout.connect(self._process_queue)
- self.loop_timer = loop_timer
-
- @property
- def websocket_server_is_running(self):
- if self.websocket_server is not None:
- return self.websocket_server.is_running
- return False
+ self._timer = None
@property
def send_to_tray(self):
"""Checks if connected to tray and have access to logs."""
return self.webserver_client and self._std_available
+ def start(self):
+ """Start app, create and start timer"""
+ if not self._std_available or self._is_running:
+ return
+ self._is_running = True
+ self._create_timer()
+ self._connect_to_tray()
+
+ def stop(self):
+ """Disconnect from Tray, process last logs"""
+ if not self._is_running:
+ return
+ self._is_running = False
+ self._process_queue()
+ self._disconnect_from_tray()
+
+ def host_connected(self):
+ """Send to Tray console that host is ready - icon change. """
+ log.info("Host {} connected".format(self.host_id))
+
+ payload = {
+ "host": self.host_id,
+ "action": MsgAction.INITIALIZED,
+ "text": "Integration with {}".format(
+ str.capitalize(self.host_name))
+ }
+ self._send(payload)
+
+ def _create_timer(self):
+ timer = threading.Timer(self.TIMER_TIMEOUT, self._timer_callback)
+ timer.start()
+ self._timer = timer
+
+ def _timer_callback(self):
+ if not self._is_running:
+ return
+ self._process_queue()
+ self._create_timer()
+
def _connect_to_tray(self):
"""Connect to Tray webserver to pass console output. """
if not self._std_available: # not content to log
@@ -135,50 +120,26 @@ class StdOutBroker:
self._send(payload)
self.webserver_client.close()
- def host_connected(self):
- """Send to Tray console that host is ready - icon change. """
- log.info("Host {} connected".format(self.host_id))
-
- payload = {
- "host": self.host_id,
- "action": MsgAction.INITIALIZED,
- "text": "Integration with {}".format(
- str.capitalize(self.host_name))
- }
- self._send(payload)
- self.loop_timer.start()
-
- def restart_server(self):
- if self.websocket_server:
- self.websocket_server.stop_server(restart=True)
-
- def exit(self):
- """Exit whole application. """
- self._disconnect_from_tray()
-
- if self.websocket_server:
- self.websocket_server.stop()
-
def _catch_std_outputs(self):
"""Redirects standard out and error to own functions"""
if sys.stdout:
self.original_stdout_write = sys.stdout.write
- sys.stdout.write = self.my_stdout_write
+ sys.stdout.write = self._my_stdout_write
self._std_available = True
if sys.stderr:
self.original_stderr_write = sys.stderr.write
- sys.stderr.write = self.my_stderr_write
+ sys.stderr.write = self._my_stderr_write
self._std_available = True
- def my_stdout_write(self, text):
+ def _my_stdout_write(self, text):
"""Appends outputted text to queue, keep writing to original stdout"""
if self.original_stdout_write is not None:
self.original_stdout_write(text)
if self.send_to_tray:
self.log_queue.append(text)
- def my_stderr_write(self, text):
+ def _my_stderr_write(self, text):
"""Appends outputted text to queue, keep writing to original stderr"""
if self.original_stderr_write is not None:
self.original_stderr_write(text)
@@ -210,68 +171,3 @@ class StdOutBroker:
self.webserver_client.send(json.dumps(payload))
except ConnectionResetError: # Tray closed
self._connect_to_tray()
-
- @staticmethod
- def _multiple_replace(text, adict):
- """Replace multiple tokens defined in dict.
-
- Find and replace all occurrences of strings defined in dict is
- supplied string.
-
- Args:
- text (str): string to be searched
- adict (dict): dictionary with `{'search': 'replace'}`
-
- Returns:
- str: string with replaced tokens
-
- """
- for r, v in adict.items():
- text = re.sub(r, v, text)
-
- return text
-
- @staticmethod
- def color(message):
- """Color message with html tags. """
- message = StdOutBroker._multiple_replace(message,
- StdOutBroker.sdict)
-
- return message
-
-
-class ConsoleDialog(QtWidgets.QDialog):
- """Qt dialog to show stdout instead of unwieldy cmd window"""
- WIDTH = 720
- HEIGHT = 450
- MAX_LINES = 10000
-
- def __init__(self, text, parent=None):
- super(ConsoleDialog, self).__init__(parent)
- layout = QtWidgets.QHBoxLayout(parent)
-
- plain_text = QtWidgets.QPlainTextEdit(self)
- plain_text.setReadOnly(True)
- plain_text.resize(self.WIDTH, self.HEIGHT)
- plain_text.maximumBlockCount = self.MAX_LINES
-
- while text:
- plain_text.appendPlainText(text.popleft().strip())
-
- layout.addWidget(plain_text)
-
- self.setWindowTitle("Console output")
-
- self.plain_text = plain_text
-
- self.setStyleSheet(style.load_stylesheet())
-
- self.resize(self.WIDTH, self.HEIGHT)
-
- def append_text(self, new_text):
- if isinstance(new_text, str):
- new_text = collections.deque(new_text.split("\n"))
- while new_text:
- text = new_text.popleft()
- if text:
- self.plain_text.appendHtml(StdOutBroker.color(text))
diff --git a/openpype/tools/stdout_broker/window.py b/openpype/tools/stdout_broker/window.py
new file mode 100644
index 0000000000..a2190e0491
--- /dev/null
+++ b/openpype/tools/stdout_broker/window.py
@@ -0,0 +1,103 @@
+from avalon import style
+from Qt import QtWidgets, QtCore
+import collections
+import re
+
+
+class ConsoleDialog(QtWidgets.QDialog):
+ """Qt dialog to show stdout instead of unwieldy cmd window"""
+ WIDTH = 720
+ HEIGHT = 450
+ MAX_LINES = 10000
+
+ sdict = {
+ r">>> ":
+ ' >>> ',
+ r"!!!(?!\sCRI|\sERR)":
+ ' !!! ',
+ r"\-\-\- ":
+ ' --- ',
+ r"\*\*\*(?!\sWRN)":
+ ' *** ',
+ r"\*\*\* WRN":
+ ' *** WRN',
+ r" \- ":
+ ' - ',
+ r"\[ ":
+ '[',
+ r"\]":
+ ']',
+ r"{":
+ '{',
+ r"}":
+ r"}",
+ r"\(":
+ '(',
+ r"\)":
+ r")",
+ r"^\.\.\. ":
+ ' ... ',
+ r"!!! ERR: ":
+ ' !!! ERR: ',
+ r"!!! CRI: ":
+ ' !!! CRI: ',
+ r"(?i)failed":
+ ' FAILED ',
+ r"(?i)error":
+ ' ERROR '
+ }
+
+ def __init__(self, text, parent=None):
+ super(ConsoleDialog, self).__init__(parent)
+ layout = QtWidgets.QHBoxLayout(parent)
+
+ plain_text = QtWidgets.QPlainTextEdit(self)
+ plain_text.setReadOnly(True)
+ plain_text.resize(self.WIDTH, self.HEIGHT)
+ plain_text.maximumBlockCount = self.MAX_LINES
+
+ while text:
+ plain_text.appendPlainText(text.popleft().strip())
+
+ layout.addWidget(plain_text)
+
+ self.setWindowTitle("Console output")
+
+ self.plain_text = plain_text
+
+ self.setStyleSheet(style.load_stylesheet())
+
+ self.resize(self.WIDTH, self.HEIGHT)
+
+ def append_text(self, new_text):
+ if isinstance(new_text, str):
+ new_text = collections.deque(new_text.split("\n"))
+ while new_text:
+ text = new_text.popleft()
+ if text:
+ self.plain_text.appendHtml(self.color(text))
+
+ def _multiple_replace(self, text, adict):
+ """Replace multiple tokens defined in dict.
+
+ Find and replace all occurrences of strings defined in dict is
+ supplied string.
+
+ Args:
+ text (str): string to be searched
+ adict (dict): dictionary with `{'search': 'replace'}`
+
+ Returns:
+ str: string with replaced tokens
+
+ """
+ for r, v in adict.items():
+ text = re.sub(r, v, text)
+
+ return text
+
+ def color(self, message):
+ """Color message with html tags. """
+ message = self._multiple_replace(message, self.sdict)
+
+ return message