diff --git a/cuegui/cuegui/Main.py b/cuegui/cuegui/Main.py index 12575c82b..52b1b6b8f 100644 --- a/cuegui/cuegui/Main.py +++ b/cuegui/cuegui/Main.py @@ -118,6 +118,9 @@ def startup(app_name, app_version, argv): mainWindow.displayStartupNotice() mainWindow.show() + # Custom qt message handler to ignore known warnings + QtCore.qInstallMessageHandler(warning_handler) + # Open all windows that were open when the app was last closed for name in mainWindow.windows_names[1:]: if settings.value("%s/Open" % name, False): @@ -132,6 +135,19 @@ def startup(app_name, app_version, argv): app.aboutToQuit.connect(closingTime) app.exec_() +def warning_handler(msg_type, msg_log_context, msg_string): + """ + Ignore known warning messages (from LogViewPlugin for now) that + happens when multi-threaded/multiple updates in a short span + """ + if ('QTextCursor::setPosition:' in msg_string or + 'SelectionRequest too old' in msg_string): + return + else: + # Todo: write to a log file + print ('{}: {}, Message: {}'.format( + str(msg_type), str(msg_log_context), str(msg_string))) + def closingTime(): """Window close callback.""" diff --git a/cuegui/cuegui/plugins/LogViewPlugin.py b/cuegui/cuegui/plugins/LogViewPlugin.py index f49957538..009e65255 100644 --- a/cuegui/cuegui/plugins/LogViewPlugin.py +++ b/cuegui/cuegui/plugins/LogViewPlugin.py @@ -22,7 +22,9 @@ import os import re import string +import sys import time +import traceback from PySide2 import QtGui from PySide2 import QtCore @@ -284,12 +286,37 @@ def line_number_area_paint_event(self, event): bottom = top + self.blockBoundingRect(block).height() block_number += 1 +class LogLoadSignals(QtCore.QObject): + SIG_LOG_LOAD_ERROR = QtCore.Signal(tuple) + SIG_LOG_LOAD_RESULT = QtCore.Signal(str, str) + SIG_LOG_LOAD_FINISHED = QtCore.Signal() + +class LogLoader(QtCore.QRunnable): + def __init__(self, fn, *args, **kwargs): + super(LogLoader, self).__init__() + self.fn = fn + self.args = args + self.kwargs = kwargs + self.signals = LogLoadSignals() + + @QtCore.Slot() + def run(self): + try: + content, log_mtime = self.fn(*self.args, **self.kwargs) + except: + exctype, value = sys.exc_info()[:2] + self.signals.SIG_LOG_LOAD_ERROR.emit( + (exctype, value, traceback.format_exc())) + else: + self.signals.SIG_LOG_LOAD_RESULT.emit(content, log_mtime) + finally: + self.signals.SIG_LOG_LOAD_FINISHED.emit() class LogViewWidget(QtWidgets.QWidget): """ Displays the log file for the selected frame """ - + SIG_CONTENT_UPDATED = QtCore.Signal(str, str) def __init__(self, parent=None): """ Create the UI elements @@ -445,6 +472,9 @@ def __init__(self, parent=None): self._current_match = 0 self._content_box.mousePressedSignal.connect(self._on_mouse_pressed) + self.SIG_CONTENT_UPDATED.connect(self._update_log_content) + self.log_thread_pool = QtCore.QThreadPool() + def _on_mouse_pressed(self, pos): """ Mouse press event, to be called when the user scrolls by hand or moves @@ -511,7 +541,7 @@ def _set_log_file(self): log_index_txt = ('Log %s of %s%s' % (str(self._current_log_index+1), len(self._log_files), - (' (current)' if self._current_log_index == 0 + (' (current)' if self._current_log_index == 0 else ''))) self._log_index_label.setText(log_index_txt) @@ -772,12 +802,55 @@ def _display_log_content(self): """ try: - self._update_log() - self._new_log = False + if not os.path.exists(self._log_file): + self._log_file_exists = False + content = 'Log file does not exist: %s' % self._log_file + self._content_timestamp = time.time() + self._update_log_content(content, self._log_mtime) + else: + # Creating the load logs process as qrunnables so + # that they don't block the ui while loading + log_loader = LogLoader(self._load_log, self._log_file, + self._new_log, self._log_mtime) + log_loader.signals.SIG_LOG_LOAD_RESULT.connect( + self._receive_log_results) + log_loader.setAutoDelete(True) + self.log_thread_pool.start(log_loader) + self.log_thread_pool.waitForDone() + self._new_log = False finally: QtCore.QTimer.singleShot(5000, self._display_log_content) + + @QtCore.Slot() + def _load_log(self, log_file, new_log, curr_log_mtime): + content = None + log_size = int(os.stat(log_file).st_size) + if log_size > 1 * 1e6: + content = ('Log file size (%0.1f MB) exceeds the size ' + 'threshold (1.0 MB).' + % float(log_size / (1024 * 1024))) + elif not new_log and os.path.exists(log_file): + log_mtime = os.path.getmtime(log_file) + if log_mtime > curr_log_mtime: + curr_log_mtime = log_mtime # no new updates + content = '' - def _update_log(self): + if content is None: + content = '' + try: + with open(log_file, 'r') as f: + content = f.read() + except IOError: + content = 'Can not access log file: %s' % log_file + + return content, curr_log_mtime + + @QtCore.Slot() + def _receive_log_results(self, content, log_mtime): + self.SIG_CONTENT_UPDATED.emit(content, log_mtime) + + @QtCore.Slot(str, str) + def _update_log_content(self, content, log_mtime): """ Updates the content of the content box with the content of the log file, if necessary. The full path to the log file will be populated in @@ -797,51 +870,23 @@ def _update_log(self): (if necessary) """ - # Get the content of the log file - if not self._log_file: - return # There's no log file, nothing to do here! - self._path.setText(self._log_file) - content = None - if not os.path.exists(self._log_file): - self._log_file_exists = False - content = 'Log file does not exist: %s' % self._log_file - self._content_timestamp = time.time() - else: - log_size = int(os.stat(self._log_file).st_size) - if log_size > 5 * 1e6: - content = ('Log file size (%0.1f MB) exceeds the size ' - 'threshold (5.0 MB).' - % float(log_size / (1024 * 1024))) - elif not self._new_log and os.path.exists(self._log_file): - log_mtime = os.path.getmtime(self._log_file) - if log_mtime > self._log_mtime: - self._log_mtime = log_mtime # no new updates - content = '' + self._log_mtime = log_mtime + QtGui.qApp.processEvents() - if content is None: - content = '' - try: - with open(self._log_file, 'r') as f: - content = f.read() - except IOError: - content = 'Can not access log file: %s' % self._log_file + if self._new_log: + self._content_box.setPlainText(content) + else: + current_text = (self._content_box.toPlainText() or '') + new_text = content.lstrip(str(current_text)) + [x for x in new_text if x in PRINTABLE] + if new_text: + self._content_box.appendPlainText(new_text) + self._content_timestamp = time.time() + self._path.setText(self._log_file) - # Do we need to scroll to the end? scroll_to_end = (self._scrollbar_max == self._scrollbar_value or self._new_log) - # Update the content in the gui (if necessary) - current_text = (self._content_box.toPlainText() or '') - new_text = content.lstrip(str(current_text)) - [x for x in new_text if x in PRINTABLE] - if new_text: - if self._new_log: - self._content_box.setPlainText(content) - else: - self._content_box.appendPlainText(new_text) - self._content_timestamp = time.time() - QtGui.qApp.processEvents() - # Adjust scrollbar value (if necessary) self._scrollbar_max = self._log_scrollbar.maximum() val = self._scrollbar_max if scroll_to_end else self._scrollbar_value