Skip to content

Commit

Permalink
Added code from PR AcademySoftwareFoundation#861 to solve gui freeze …
Browse files Browse the repository at this point in the history
…when using the log viewer.
  • Loading branch information
donalm committed Feb 6, 2021
1 parent 781d2de commit fe799f3
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 45 deletions.
16 changes: 16 additions & 0 deletions cuegui/cuegui/Main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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."""
Expand Down
135 changes: 90 additions & 45 deletions cuegui/cuegui/plugins/LogViewPlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
import os
import re
import string
import sys
import time
import traceback

from PySide2 import QtGui
from PySide2 import QtCore
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down

0 comments on commit fe799f3

Please sign in to comment.