diff --git a/pype/tools/pyblish_pype/app.css b/pype/tools/pyblish_pype/app.css index b52d9efec8..3a2c05c1f3 100644 --- a/pype/tools/pyblish_pype/app.css +++ b/pype/tools/pyblish_pype/app.css @@ -491,3 +491,24 @@ QToolButton { #TerminalFilerBtn[type="log_critical"]:checked {color: rgb(255, 79, 117);} #TerminalFilerBtn[type="log_critical"] {color: rgba(255, 79, 117, 63);} + +#SuspendLogsBtn { + background: #444; + border: none; + border-top-right-radius: 7px; + border-bottom-right-radius: 7px; + border-top-left-radius: 0px; + border-bottom-left-radius: 0px; + font-family: "FontAwesome"; + font-size: 11pt; + color: white; + padding: 0px; +} + +#SuspendLogsBtn:hover { + background: #333; +} + +#SuspendLogsBtn:disabled { + background: #4c4c4c; +} diff --git a/pype/tools/pyblish_pype/control.py b/pype/tools/pyblish_pype/control.py index a078f0146d..5138b5cc4c 100644 --- a/pype/tools/pyblish_pype/control.py +++ b/pype/tools/pyblish_pype/control.py @@ -86,7 +86,6 @@ class Controller(QtCore.QObject): # - passing collectors order disables plugin/instance toggle self.collectors_order = None self.collect_state = 0 - self.collected = False # - passing validators order disables validate button and gives ability # to know when to stop on validate button press diff --git a/pype/tools/pyblish_pype/model.py b/pype/tools/pyblish_pype/model.py index 2c2661b5ec..b9257bfeea 100644 --- a/pype/tools/pyblish_pype/model.py +++ b/pype/tools/pyblish_pype/model.py @@ -32,7 +32,6 @@ from .awesome import tags as awesome import Qt from Qt import QtCore, QtGui from six import text_type -from six.moves import queue from .vendor import qtawesome from .constants import PluginStates, InstanceStates, GroupStates, Roles @@ -49,6 +48,7 @@ TerminalDetailType = QtGui.QStandardItem.UserType + 4 class QAwesomeTextIconFactory: icons = {} + @classmethod def icon(cls, icon_name): if icon_name not in cls.icons: @@ -58,6 +58,7 @@ class QAwesomeTextIconFactory: class QAwesomeIconFactory: icons = {} + @classmethod def icon(cls, icon_name, icon_color): if icon_name not in cls.icons: @@ -1009,7 +1010,7 @@ class ArtistProxy(QtCore.QAbstractProxyModel): return QtCore.QModelIndex() -class TerminalModel(QtGui.QStandardItemModel): +class TerminalDetailItem(QtGui.QStandardItem): key_label_record_map = ( ("instance", "Instance"), ("msg", "Message"), @@ -1022,6 +1023,57 @@ class TerminalModel(QtGui.QStandardItemModel): ("msecs", "Millis") ) + def __init__(self, record_item): + self.record_item = record_item + self.msg = None + msg = record_item.get("msg") + if msg is None: + msg = record_item["label"].split("\n")[0] + + super(TerminalDetailItem, self).__init__(msg) + + def data(self, role=QtCore.Qt.DisplayRole): + if role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole): + if self.msg is None: + self.msg = self.compute_detail_text(self.record_item) + return self.msg + return super(TerminalDetailItem, self).data(role) + + def compute_detail_text(self, item_data): + if item_data["type"] == "info": + return item_data["label"] + + html_text = "" + for key, title in self.key_label_record_map: + if key not in item_data: + continue + value = item_data[key] + text = ( + str(value) + .replace("<", "<") + .replace(">", ">") + .replace('\n', '
') + .replace(' ', ' ') + ) + + title_tag = ( + '{}: ' + ' color:#fff;\" >{}: ' + ).format(title) + + html_text += ( + '{}' + '{}' + ).format(title_tag, text) + + html_text = '{}
'.format( + html_text + ) + return html_text + + +class TerminalModel(QtGui.QStandardItemModel): item_icon_name = { "info": "fa.info", "record": "fa.circle", @@ -1053,38 +1105,38 @@ class TerminalModel(QtGui.QStandardItemModel): self.reset() def reset(self): - self.items_to_set_widget = queue.Queue() self.clear() - def prepare_records(self, result): + def prepare_records(self, result, suspend_logs): prepared_records = [] instance_name = None instance = result["instance"] if instance is not None: instance_name = instance.data["name"] - for record in result.get("records") or []: - if isinstance(record, dict): - record_item = record - else: - record_item = { - "label": text_type(record.msg), - "type": "record", - "levelno": record.levelno, - "threadName": record.threadName, - "name": record.name, - "filename": record.filename, - "pathname": record.pathname, - "lineno": record.lineno, - "msg": text_type(record.msg), - "msecs": record.msecs, - "levelname": record.levelname - } + if not suspend_logs: + for record in result.get("records") or []: + if isinstance(record, dict): + record_item = record + else: + record_item = { + "label": text_type(record.msg), + "type": "record", + "levelno": record.levelno, + "threadName": record.threadName, + "name": record.name, + "filename": record.filename, + "pathname": record.pathname, + "lineno": record.lineno, + "msg": text_type(record.msg), + "msecs": record.msecs, + "levelname": record.levelname + } - if instance_name is not None: - record_item["instance"] = instance_name + if instance_name is not None: + record_item["instance"] = instance_name - prepared_records.append(record_item) + prepared_records.append(record_item) error = result.get("error") if error: @@ -1140,49 +1192,14 @@ class TerminalModel(QtGui.QStandardItemModel): self.appendRow(top_item) - detail_text = self.prepare_detail_text(record_item) - detail_item = QtGui.QStandardItem(detail_text) + detail_item = TerminalDetailItem(record_item) detail_item.setData(TerminalDetailType, Roles.TypeRole) top_item.appendRow(detail_item) - self.items_to_set_widget.put(detail_item) def update_with_result(self, result): for record in result["records"]: self.append(record) - def prepare_detail_text(self, item_data): - if item_data["type"] == "info": - return item_data["label"] - - html_text = "" - for key, title in self.key_label_record_map: - if key not in item_data: - continue - value = item_data[key] - text = ( - str(value) - .replace("<", "<") - .replace(">", ">") - .replace('\n', '
') - .replace(' ', ' ') - ) - - title_tag = ( - '{}: ' - ' color:#fff;\" >{}: ' - ).format(title) - - html_text += ( - '{}' - '{}' - ).format(title_tag, text) - - html_text = '{}
'.format( - html_text - ) - return html_text - class TerminalProxy(QtCore.QSortFilterProxyModel): filter_buttons_checks = { diff --git a/pype/tools/pyblish_pype/view.py b/pype/tools/pyblish_pype/view.py index ada19bc7d9..03509604bb 100644 --- a/pype/tools/pyblish_pype/view.py +++ b/pype/tools/pyblish_pype/view.py @@ -1,5 +1,6 @@ from Qt import QtCore, QtWidgets from . import model +from . import widgets from .constants import Roles @@ -190,6 +191,23 @@ class TerminalView(QtWidgets.QTreeView): self.updateGeometry() self.scrollToBottom() + def expand(self, index): + """Wrapper to set widget for expanded index.""" + model = index.model() + row_count = model.rowCount(index) + is_new = False + for child_idx in range(row_count): + child_index = model.index(child_idx, index.column(), index) + widget = self.indexWidget(child_index) + if widget is None: + is_new = True + msg = child_index.data(QtCore.Qt.DisplayRole) + widget = widgets.TerminalDetail(msg) + self.setIndexWidget(child_index, widget) + super(TerminalView, self).expand(index) + if is_new: + self.updateGeometries() + def resizeEvent(self, event): super(self.__class__, self).resizeEvent(event) self.model().layoutChanged.emit() diff --git a/pype/tools/pyblish_pype/widgets.py b/pype/tools/pyblish_pype/widgets.py index e81633f7a3..880d4755ad 100644 --- a/pype/tools/pyblish_pype/widgets.py +++ b/pype/tools/pyblish_pype/widgets.py @@ -321,11 +321,6 @@ class PerspectiveWidget(QtWidgets.QWidget): data = {"records": records} self.terminal_model.reset() self.terminal_model.update_with_result(data) - while not self.terminal_model.items_to_set_widget.empty(): - item = self.terminal_model.items_to_set_widget.get() - widget = TerminalDetail(item.data(QtCore.Qt.DisplayRole)) - index = self.terminal_proxy.mapFromSource(item.index()) - self.terminal_view.setIndexWidget(index, widget) self.records.button_toggle_text.setText( "{} ({})".format(self.l_rec, len_records) diff --git a/pype/tools/pyblish_pype/window.py b/pype/tools/pyblish_pype/window.py index 5d22e5ac8f..9aa77a57a8 100644 --- a/pype/tools/pyblish_pype/window.py +++ b/pype/tools/pyblish_pype/window.py @@ -54,6 +54,7 @@ class Window(QtWidgets.QDialog): def __init__(self, controller, parent=None): super(Window, self).__init__(parent=parent) + self._suspend_logs = False # Use plastique style for specific ocations # TODO set style name via environment variable low_keys = { @@ -95,6 +96,18 @@ class Window(QtWidgets.QDialog): header_tab_terminal = QtWidgets.QRadioButton(header_tab_widget) header_spacer = QtWidgets.QWidget(header_tab_widget) + button_suspend_logs_widget = QtWidgets.QWidget() + button_suspend_logs_widget_layout = QtWidgets.QHBoxLayout( + button_suspend_logs_widget + ) + button_suspend_logs_widget_layout.setContentsMargins(0, 10, 0, 10) + button_suspend_logs = QtWidgets.QPushButton(header_widget) + button_suspend_logs.setFixedWidth(7) + button_suspend_logs.setSizePolicy( + QtWidgets.QSizePolicy.Preferred, + QtWidgets.QSizePolicy.Expanding + ) + button_suspend_logs_widget_layout.addWidget(button_suspend_logs) header_aditional_btns = QtWidgets.QWidget(header_tab_widget) aditional_btns_layout = QtWidgets.QHBoxLayout(header_aditional_btns) @@ -109,9 +122,11 @@ class Window(QtWidgets.QDialog): layout_tab.addWidget(header_tab_artist, 0) layout_tab.addWidget(header_tab_overview, 0) layout_tab.addWidget(header_tab_terminal, 0) + layout_tab.addWidget(button_suspend_logs_widget, 0) + # Compress items to the left layout_tab.addWidget(header_spacer, 1) - layout_tab.addWidget(header_aditional_btns, 1) + layout_tab.addWidget(header_aditional_btns, 0) layout = QtWidgets.QHBoxLayout(header_widget) layout.setContentsMargins(0, 0, 0, 0) @@ -226,6 +241,10 @@ class Window(QtWidgets.QDialog): footer_info = QtWidgets.QLabel(footer_widget) footer_spacer = QtWidgets.QWidget(footer_widget) + + footer_button_stop = QtWidgets.QPushButton( + awesome["stop"], footer_widget + ) footer_button_reset = QtWidgets.QPushButton( awesome["refresh"], footer_widget ) @@ -235,14 +254,12 @@ class Window(QtWidgets.QDialog): footer_button_play = QtWidgets.QPushButton( awesome["play"], footer_widget ) - footer_button_stop = QtWidgets.QPushButton( - awesome["stop"], footer_widget - ) layout = QtWidgets.QHBoxLayout() layout.setContentsMargins(5, 5, 5, 5) layout.addWidget(footer_info, 0) layout.addWidget(footer_spacer, 1) + layout.addWidget(footer_button_stop, 0) layout.addWidget(footer_button_reset, 0) layout.addWidget(footer_button_validate, 0) @@ -342,10 +359,11 @@ class Window(QtWidgets.QDialog): "TerminalView": terminal_view, # Buttons - "Play": footer_button_play, - "Validate": footer_button_validate, - "Reset": footer_button_reset, + "SuspendLogsBtn": button_suspend_logs, "Stop": footer_button_stop, + "Reset": footer_button_reset, + "Validate": footer_button_validate, + "Play": footer_button_play, # Misc "HeaderSpacer": header_spacer, @@ -370,10 +388,11 @@ class Window(QtWidgets.QDialog): overview_page, terminal_page, footer_widget, - footer_button_play, - footer_button_validate, + button_suspend_logs, footer_button_stop, footer_button_reset, + footer_button_validate, + footer_button_play, footer_spacer, closing_placeholder ): @@ -419,6 +438,7 @@ class Window(QtWidgets.QDialog): overview_instance_view.toggled.connect(self.on_instance_toggle) overview_plugin_view.toggled.connect(self.on_plugin_toggle) + button_suspend_logs.clicked.connect(self.on_suspend_clicked) footer_button_stop.clicked.connect(self.on_stop_clicked) footer_button_reset.clicked.connect(self.on_reset_clicked) footer_button_validate.clicked.connect(self.on_validate_clicked) @@ -442,10 +462,11 @@ class Window(QtWidgets.QDialog): self.terminal_filters_widget = terminal_filters_widget self.footer_widget = footer_widget + self.button_suspend_logs = button_suspend_logs + self.footer_button_stop = footer_button_stop self.footer_button_reset = footer_button_reset self.footer_button_validate = footer_button_validate self.footer_button_play = footer_button_play - self.footer_button_stop = footer_button_stop self.overview_instance_view = overview_instance_view self.overview_plugin_view = overview_plugin_view @@ -612,6 +633,13 @@ class Window(QtWidgets.QDialog): self.footer_button_play.setEnabled(False) self.footer_button_stop.setEnabled(False) + def on_suspend_clicked(self): + self._suspend_logs = not self._suspend_logs + if self.state["current_page"] == "terminal": + self.on_tab_changed("overview") + + self.tabs["terminal"].setVisible(not self._suspend_logs) + def on_comment_entered(self): """The user has typed a comment.""" self.controller.context.data["comment"] = self.comment_box.text() @@ -726,6 +754,8 @@ class Window(QtWidgets.QDialog): self.on_tab_changed(self.state["current_page"]) self.update_compatibility() + self.button_suspend_logs.setEnabled(False) + self.footer_button_validate.setEnabled(True) self.footer_button_reset.setEnabled(True) self.footer_button_stop.setEnabled(False) @@ -775,6 +805,12 @@ class Window(QtWidgets.QDialog): self.footer_widget.setProperty("success", 0) self.footer_widget.style().polish(self.footer_widget) + suspend_log_bool = ( + self.controller.collect_state == 1 + and not self.controller.stopped + ) + self.button_suspend_logs.setEnabled(suspend_log_bool) + def on_was_skipped(self, plugin): plugin_item = self.plugin_model.plugin_items[plugin.id] plugin_item.setData( @@ -834,17 +870,15 @@ class Window(QtWidgets.QDialog): if self.tabs["artist"].isChecked(): self.tabs["overview"].toggle() - result["records"] = self.terminal_model.prepare_records(result) + result["records"] = self.terminal_model.prepare_records( + result, + self._suspend_logs + ) plugin_item = self.plugin_model.update_with_result(result) instance_item = self.instance_model.update_with_result(result) self.terminal_model.update_with_result(result) - while not self.terminal_model.items_to_set_widget.empty(): - item = self.terminal_model.items_to_set_widget.get() - widget = widgets.TerminalDetail(item.data(QtCore.Qt.DisplayRole)) - index = self.terminal_proxy.mapFromSource(item.index()) - self.terminal_view.setIndexWidget(index, widget) self.update_compatibility() @@ -897,16 +931,19 @@ class Window(QtWidgets.QDialog): self.footer_button_validate.setEnabled(False) self.footer_button_play.setEnabled(False) + self.button_suspend_logs.setEnabled(False) + util.defer(5, self.controller.validate) def publish(self): self.info(self.tr("Preparing publish..")) - self.footer_button_stop.setEnabled(True) self.footer_button_reset.setEnabled(False) self.footer_button_validate.setEnabled(False) self.footer_button_play.setEnabled(False) + self.button_suspend_logs.setEnabled(False) + util.defer(5, self.controller.publish) def act(self, plugin_item, action): @@ -938,7 +975,10 @@ class Window(QtWidgets.QDialog): plugin_item = self.plugin_model.plugin_items[result["plugin"].id] action_state = plugin_item.data(Roles.PluginActionProgressRole) action_state |= PluginActionStates.HasFinished - result["records"] = self.terminal_model.prepare_records(result) + result["records"] = self.terminal_model.prepare_records( + result, + self._suspend_logs + ) error = result.get("error") if error: