modified log viewer

This commit is contained in:
iLLiCiTiT 2021-08-31 14:28:19 +02:00
parent 08b00f3414
commit 649dbf44a2
6 changed files with 432 additions and 201 deletions

View file

@ -1,4 +1,4 @@
from .widgets import (
from .window import (
PublishLogViewerWindow
)

View file

@ -0,0 +1,19 @@
from Qt import QtCore
ITEM_ID_ROLE = QtCore.Qt.UserRole + 1
ITEM_IS_GROUP_ROLE = QtCore.Qt.UserRole + 2
ITEM_LABEL_ROLE = QtCore.Qt.UserRole + 3
PLUGIN_SKIPPED_ROLE = QtCore.Qt.UserRole + 4
PLUGIN_ERRORED_ROLE = QtCore.Qt.UserRole + 5
INSTANCE_REMOVED_ROLE = QtCore.Qt.UserRole + 6
__all__ = (
"ITEM_ID_ROLE",
"ITEM_IS_GROUP_ROLE",
"ITEM_LABEL_ROLE",
"PLUGIN_SKIPPED_ROLE",
"PLUGIN_ERRORED_ROLE",
"INSTANCE_REMOVED_ROLE"
)

View file

@ -0,0 +1,188 @@
import platform
from Qt import QtWidgets, QtCore, QtGui
from constants import (
ITEM_ID_ROLE,
ITEM_IS_GROUP_ROLE,
ITEM_LABEL_ROLE,
PLUGIN_SKIPPED_ROLE,
PLUGIN_ERRORED_ROLE,
INSTANCE_REMOVED_ROLE
)
colors = {
"error": QtGui.QColor("#ff4a4a"),
"warning": QtGui.QColor("#ff9900"),
"ok": QtGui.QColor("#77AE24"),
"active": QtGui.QColor("#99CEEE"),
"idle": QtCore.Qt.white,
"inactive": QtGui.QColor("#888"),
"hover": QtGui.QColor(255, 255, 255, 5),
"selected": QtGui.QColor(255, 255, 255, 10),
"outline": QtGui.QColor("#333"),
"group": QtGui.QColor("#21252B"),
"group-hover": QtGui.QColor("#3c3c3c"),
"group-selected-hover": QtGui.QColor("#555555")
}
class ItemDelegate(QtWidgets.QStyledItemDelegate):
pass
class GroupItemDelegate(QtWidgets.QStyledItemDelegate):
"""Generic delegate for instance header"""
def __init__(self, parent):
super(GroupItemDelegate, self).__init__(parent)
self.item_delegate = ItemDelegate(parent)
self._minus_pixmaps = {}
self._plus_pixmaps = {}
self._pix_offset_ratio = 1 / 3
self._pix_stroke_size_ratio = 1 / 7
path_stroker = QtGui.QPainterPathStroker()
path_stroker.setCapStyle(QtCore.Qt.RoundCap)
path_stroker.setJoinStyle(QtCore.Qt.RoundJoin)
self._path_stroker = path_stroker
def _get_plus_pixmap(self, size):
pix = self._minus_pixmaps.get(size)
if pix is not None:
return pix
pix = QtGui.QPixmap(size, size)
pix.fill(QtCore.Qt.transparent)
offset = int(size * self._pix_offset_ratio)
pnt_1 = QtCore.QPoint(offset, int(size / 2))
pnt_2 = QtCore.QPoint(size - offset, int(size / 2))
pnt_3 = QtCore.QPoint(int(size / 2), offset)
pnt_4 = QtCore.QPoint(int(size / 2), size - offset)
path_1 = QtGui.QPainterPath(pnt_1)
path_1.lineTo(pnt_2)
path_2 = QtGui.QPainterPath(pnt_3)
path_2.lineTo(pnt_4)
self._path_stroker.setWidth(size * self._pix_stroke_size_ratio)
stroked_path_1 = self._path_stroker.createStroke(path_1)
stroked_path_2 = self._path_stroker.createStroke(path_2)
pix = QtGui.QPixmap(size, size)
pix.fill(QtCore.Qt.transparent)
painter = QtGui.QPainter(pix)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.setPen(QtCore.Qt.transparent)
painter.setBrush(QtCore.Qt.white)
painter.drawPath(stroked_path_1)
painter.drawPath(stroked_path_2)
painter.end()
self._minus_pixmaps[size] = pix
return pix
def _get_minus_pixmap(self, size):
pix = self._plus_pixmaps.get(size)
if pix is not None:
return pix
offset = int(size / self._pix_offset_ratio)
pnt_1 = QtCore.QPoint(offset, int(size / 2))
pnt_2 = QtCore.QPoint(size - offset, int(size / 2))
path = QtGui.QPainterPath(pnt_1)
path.lineTo(pnt_2)
self._path_stroker.setWidth(size / self._pix_stroke_size_ratio)
stroked_path = self._path_stroker.createStroke(path)
pix = QtGui.QPixmap(size, size)
pix.fill(QtCore.Qt.transparent)
painter = QtGui.QPainter(pix)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.setPen(QtCore.Qt.transparent)
painter.setBrush(QtCore.Qt.white)
painter.drawPath(stroked_path)
painter.end()
self._plus_pixmaps[size] = pix
return pix
def paint(self, painter, option, index):
if index.data(ITEM_IS_GROUP_ROLE):
self.group_item_paint(painter, option, index)
else:
self.item_delegate.paint(painter, option, index)
def group_item_paint(self, painter, option, index):
"""Paint text
_
My label
"""
self.initStyleOption(option, index)
widget = option.widget
if widget:
style = widget.style()
else:
style = QtWidgets.QApplicaion.style()
_rect = style.proxy().subElementRect(
style.SE_ItemViewItemText, option, widget
)
bg_rect = QtCore.QRectF(option.rect)
bg_rect.setY(_rect.y())
bg_rect.setHeight(_rect.height())
expander_rect = QtCore.QRectF(bg_rect)
expander_rect.setWidth(expander_rect.height() + 5)
label_rect = QtCore.QRectF(
expander_rect.x() + expander_rect.width(),
expander_rect.y(),
bg_rect.width() - expander_rect.width(),
expander_rect.height()
)
bg_path = QtGui.QPainterPath()
radius = (bg_rect.height() / 2) - 0.01
bg_path.addRoundedRect(bg_rect, radius, radius)
painter.fillPath(bg_path, colors["group"])
selected = option.state & QtWidgets.QStyle.State_Selected
hovered = option.state & QtWidgets.QStyle.State_MouseOver
if selected and hovered:
painter.fillPath(bg_path, colors["selected"])
elif hovered:
painter.fillPath(bg_path, colors["hover"])
expanded = self.parent().isExpanded(index)
if expanded:
expander_icon = self._get_minus_pixmap(expander_rect.height())
else:
expander_icon = self._get_plus_pixmap(expander_rect.height())
label = index.data(QtCore.Qt.DisplayRole)
label = option.fontMetrics.elidedText(
label, QtCore.Qt.ElideRight, label_rect.width()
)
# Maintain reference to state, so we can restore it once we're done
painter.save()
pix_point = QtCore.QPoint(
expander_rect.center().x() - int(expander_icon.width() / 2),
expander_rect.top()
)
painter.drawPixmap(pix_point, expander_icon)
# Draw label
painter.setFont(option.font)
painter.drawText(label_rect, QtCore.Qt.AlignVCenter, label)
# Ok, we're done, tidy up.
painter.restore()

View file

@ -0,0 +1,197 @@
import uuid
from Qt import QtCore, QtGui
import pyblish.api
from .constants import (
ITEM_ID_ROLE,
ITEM_IS_GROUP_ROLE,
ITEM_LABEL_ROLE,
PLUGIN_SKIPPED_ROLE,
PLUGIN_ERRORED_ROLE,
INSTANCE_REMOVED_ROLE
)
class InstancesModel(QtGui.QStandardItemModel):
def __init__(self, *args, **kwargs):
super(InstancesModel, self).__init__(*args, **kwargs)
self._items_by_id = {}
self._plugin_items_by_id = {}
def get_items_by_id(self):
return self._items_by_id
def set_report(self, report_item):
self.clear()
self._items_by_id.clear()
self._plugin_items_by_id.clear()
root_item = self.invisibleRootItem()
families = set(report_item.instance_items_by_family.keys())
families.remove(None)
all_families = list(sorted(families))
all_families.insert(0, None)
family_items = []
for family in all_families:
items = []
instance_items = report_item.instance_items_by_family[family]
for instance_item in instance_items:
item = QtGui.QStandardItem(instance_item.label)
item.setData(instance_item.label, ITEM_LABEL_ROLE)
item.setData(instance_item.id, ITEM_ID_ROLE)
item.setData(instance_item.removed, INSTANCE_REMOVED_ROLE)
item.setData(False, ITEM_IS_GROUP_ROLE)
items.append(item)
self._items_by_id[instance_item.id] = item
self._plugin_items_by_id[instance_item.id] = item
if family is None:
family_items.extend(items)
continue
family_item = QtGui.QStandardItem(family)
family_item.setData(family, ITEM_LABEL_ROLE)
family_item.setFlags(QtCore.Qt.ItemIsEnabled)
family_id = uuid.uuid4()
family_item.setData(family_id, ITEM_ID_ROLE)
family_item.setData(True, ITEM_IS_GROUP_ROLE)
family_item.appendRows(items)
family_items.append(family_item)
self._items_by_id[family_id] = family_item
root_item.appendRows(family_items)
class InstanceProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, *args, **kwargs):
super(InstanceProxyModel, self).__init__(*args, **kwargs)
self._ignore_removed = True
@property
def ignore_removed(self):
return self._ignore_removed
def set_ignore_removed(self, value):
if value == self._ignore_removed:
return
self._ignore_removed = value
if self.sourceModel():
self.invalidateFilter()
def filterAcceptsRow(self, row, parent):
model = self.sourceModel()
source_index = model.index(row, 0, parent)
if source_index.data(ITEM_IS_GROUP_ROLE):
return model.rowCount(source_index) > 0
if self._ignore_removed and source_index.data(PLUGIN_SKIPPED_ROLE):
return False
return True
class PluginsModel(QtGui.QStandardItemModel):
order_label_mapping = (
(pyblish.api.CollectorOrder + 0.5, "Collect"),
(pyblish.api.ValidatorOrder + 0.5, "Validate"),
(pyblish.api.ExtractorOrder + 0.5, "Extract"),
(pyblish.api.IntegratorOrder + 0.5, "Integrate"),
(None, "Other")
)
def __init__(self, *args, **kwargs):
super(PluginsModel, self).__init__(*args, **kwargs)
self._items_by_id = {}
self._plugin_items_by_id = {}
def get_items_by_id(self):
return self._items_by_id
def set_report(self, report_item):
self.clear()
self._items_by_id.clear()
self._plugin_items_by_id.clear()
root_item = self.invisibleRootItem()
labels_iter = iter(self.order_label_mapping)
cur_order, cur_label = next(labels_iter)
cur_plugin_items = []
plugin_items_by_group_labels = []
plugin_items_by_group_labels.append((cur_label, cur_plugin_items))
for plugin_id in report_item.plugins_id_order:
plugin_item = report_item.plugins_items_by_id[plugin_id]
if cur_order is not None and plugin_item.order >= cur_order:
cur_order, cur_label = next(labels_iter)
cur_plugin_items = []
plugin_items_by_group_labels.append(
(cur_label, cur_plugin_items)
)
cur_plugin_items.append(plugin_item)
group_items = []
for group_label, plugin_items in plugin_items_by_group_labels:
group_id = uuid.uuid4()
group_item = QtGui.QStandardItem(group_label)
group_item.setData(group_label, ITEM_LABEL_ROLE)
group_item.setData(group_id, ITEM_ID_ROLE)
group_item.setData(True, ITEM_IS_GROUP_ROLE)
group_item.setFlags(QtCore.Qt.ItemIsEnabled)
group_items.append(group_item)
self._items_by_id[group_id] = group_item
if not plugin_items:
continue
items = []
for plugin_item in plugin_items:
item = QtGui.QStandardItem(plugin_item.label)
item.setData(False, ITEM_IS_GROUP_ROLE)
item.setData(plugin_item.label, ITEM_LABEL_ROLE)
item.setData(plugin_item.id, ITEM_ID_ROLE)
item.setData(plugin_item.skipped, PLUGIN_SKIPPED_ROLE)
item.setData(plugin_item.errored, PLUGIN_ERRORED_ROLE)
items.append(item)
self._items_by_id[plugin_item.id] = item
self._plugin_items_by_id[plugin_item.id] = item
group_item.appendRows(items)
root_item.appendRows(group_items)
class PluginProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, *args, **kwargs):
super(PluginProxyModel, self).__init__(*args, **kwargs)
self._ignore_skipped = True
@property
def ignore_skipped(self):
return self._ignore_skipped
def set_ignore_skipped(self, value):
if value == self._ignore_skipped:
return
self._ignore_skipped = value
if self.sourceModel():
self.invalidateFilter()
def filterAcceptsRow(self, row, parent):
model = self.sourceModel()
source_index = model.index(row, 0, parent)
if source_index.data(ITEM_IS_GROUP_ROLE):
return model.rowCount(source_index) > 0
if self._ignore_skipped and source_index.data(PLUGIN_SKIPPED_ROLE):
return False
return True

View file

@ -5,11 +5,16 @@ from Qt import QtWidgets, QtCore, QtGui
import pyblish.api
ITEM_ID_ROLE = QtCore.Qt.UserRole + 1
ITEM_IS_GROUP_ROLE = QtCore.Qt.UserRole + 2
PLUGIN_SKIPPED_ROLE = QtCore.Qt.UserRole + 3
PLUGIN_ERRORED_ROLE = QtCore.Qt.UserRole + 4
INSTANCE_REMOVED_ROLE = QtCore.Qt.UserRole + 5
from .constants import (
ITEM_ID_ROLE
)
from .delegates import GroupItemDelegate
from .model import (
InstancesModel,
InstanceProxyModel,
PluginsModel,
PluginProxyModel
)
class PluginItem:
@ -96,154 +101,6 @@ class PublishReport:
self.logs = all_logs
class InstancesModel(QtGui.QStandardItemModel):
def set_report(self, report_item):
self.clear()
root_item = self.invisibleRootItem()
families = set(report_item.instance_items_by_family.keys())
families.remove(None)
all_families = list(sorted(families))
all_families.insert(0, None)
family_items = []
for family in all_families:
items = []
instance_items = report_item.instance_items_by_family[family]
for instance_item in instance_items:
item = QtGui.QStandardItem(instance_item.label)
item.setData(instance_item.id, ITEM_ID_ROLE)
item.setData(instance_item.removed, INSTANCE_REMOVED_ROLE)
item.setData(False, ITEM_IS_GROUP_ROLE)
items.append(item)
if family is None:
family_items.extend(items)
continue
family_item = QtGui.QStandardItem(family)
family_item.setFlags(QtCore.Qt.ItemIsEnabled)
family_item.setData(True, ITEM_IS_GROUP_ROLE)
family_item.appendRows(items)
family_items.append(family_item)
root_item.appendRows(family_items)
class InstanceProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, *args, **kwargs):
super(InstanceProxyModel, self).__init__(*args, **kwargs)
self._ignore_removed = True
@property
def ignore_removed(self):
return self._ignore_removed
def set_ignore_removed(self, value):
if value == self._ignore_removed:
return
self._ignore_removed = value
if self.sourceModel():
self.invalidateFilter()
def filterAcceptsRow(self, row, parent):
model = self.sourceModel()
source_index = model.index(row, 0, parent)
if source_index.data(ITEM_IS_GROUP_ROLE):
return model.rowCount(source_index) > 0
if self._ignore_removed and source_index.data(PLUGIN_SKIPPED_ROLE):
return False
return True
class PluginsModel(QtGui.QStandardItemModel):
order_label_mapping = (
(pyblish.api.CollectorOrder + 0.5, "Collect"),
(pyblish.api.ValidatorOrder + 0.5, "Validate"),
(pyblish.api.ExtractorOrder + 0.5, "Extract"),
(pyblish.api.IntegratorOrder + 0.5, "Integrate"),
(None, "Other")
)
def set_report(self, report_item):
self.clear()
root_item = self.invisibleRootItem()
labels_iter = iter(self.order_label_mapping)
cur_order, cur_label = next(labels_iter)
cur_plugin_items = []
plugin_items_by_group_labels = []
plugin_items_by_group_labels.append((cur_label, cur_plugin_items))
for plugin_id in report_item.plugins_id_order:
plugin_item = report_item.plugins_items_by_id[plugin_id]
if cur_order is not None and plugin_item.order >= cur_order:
cur_order, cur_label = next(labels_iter)
cur_plugin_items = []
plugin_items_by_group_labels.append(
(cur_label, cur_plugin_items)
)
cur_plugin_items.append(plugin_item)
group_items = []
for group_label, plugin_items in plugin_items_by_group_labels:
group_item = QtGui.QStandardItem(group_label)
group_item.setData(True, ITEM_IS_GROUP_ROLE)
group_item.setFlags(QtCore.Qt.ItemIsEnabled)
group_items.append(group_item)
if not plugin_items:
continue
items = []
for plugin_item in plugin_items:
item = QtGui.QStandardItem(plugin_item.label)
item.setData(False, ITEM_IS_GROUP_ROLE)
item.setData(False, ITEM_IS_GROUP_ROLE)
item.setData(plugin_item.id, ITEM_ID_ROLE)
item.setData(plugin_item.skipped, PLUGIN_SKIPPED_ROLE)
item.setData(plugin_item.errored, PLUGIN_ERRORED_ROLE)
items.append(item)
group_item.appendRows(items)
root_item.appendRows(group_items)
class PluginProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, *args, **kwargs):
super(PluginProxyModel, self).__init__(*args, **kwargs)
self._ignore_skipped = True
@property
def ignore_skipped(self):
return self._ignore_skipped
def set_ignore_skipped(self, value):
if value == self._ignore_skipped:
return
self._ignore_skipped = value
if self.sourceModel():
self.invalidateFilter()
def filterAcceptsRow(self, row, parent):
model = self.sourceModel()
source_index = model.index(row, 0, parent)
if source_index.data(ITEM_IS_GROUP_ROLE):
return model.rowCount(source_index) > 0
if self._ignore_skipped and source_index.data(PLUGIN_SKIPPED_ROLE):
return False
return True
class DetailsWidget(QtWidgets.QWidget):
def __init__(self, parent):
super(DetailsWidget, self).__init__(parent)
@ -297,28 +154,38 @@ class PublishLogViewerWidget(QtWidgets.QWidget):
removed_instances_check.setChecked(instances_proxy.ignore_removed)
instances_view = QtWidgets.QTreeView(self)
instances_view.setObjectName("PublishDetailViews")
instances_view.setModel(instances_proxy)
# instances_view.setIndentation(0)
instances_view.setIndentation(0)
instances_view.setHeaderHidden(True)
instances_view.setEditTriggers(QtWidgets.QTreeView.NoEditTriggers)
instances_delegate = GroupItemDelegate(instances_view)
instances_view.setItemDelegate(instances_delegate)
skipped_plugins_check = QtWidgets.QCheckBox(
"Hide skipped plugins", self
)
skipped_plugins_check.setChecked(plugins_proxy.ignore_skipped)
plugins_view = QtWidgets.QTreeView(self)
plugins_view.setObjectName("PublishDetailViews")
plugins_view.setModel(plugins_proxy)
# plugins_view.setIndentation(0)
plugins_view.setIndentation(0)
plugins_view.setHeaderHidden(True)
plugins_view.setEditTriggers(QtWidgets.QTreeView.NoEditTriggers)
plugins_delegate = GroupItemDelegate(plugins_view)
plugins_view.setItemDelegate(plugins_delegate)
details_widget = DetailsWidget(self)
layout = QtWidgets.QGridLayout(self)
# Row 1
layout.addWidget(removed_instances_check, 0, 0)
layout.addWidget(instances_view, 1, 0)
layout.addWidget(skipped_plugins_check, 0, 1)
# Row 2
layout.addWidget(instances_view, 1, 0)
layout.addWidget(plugins_view, 1, 1)
layout.addWidget(details_widget, 1, 2)
@ -345,6 +212,9 @@ class PublishLogViewerWidget(QtWidgets.QWidget):
self._instances_model = instances_model
self._instances_proxy = instances_proxy
self._instances_delegate = instances_delegate
self._plugins_delegate = plugins_delegate
self._skipped_plugins_check = skipped_plugins_check
self._plugins_view = plugins_view
self._plugins_model = plugins_model
@ -358,7 +228,6 @@ class PublishLogViewerWidget(QtWidgets.QWidget):
self._instances_model.set_report(report_item)
self._plugins_model.set_report(report_item)
self._details_widget.set_logs(report_item.logs)
self._ignore_selection_changes = False

View file

@ -1,27 +1,4 @@
import os
import sys
import json
import copy
import uuid
import collections
openpype_dir = r"C:\Users\jakub.trllo\Desktop\pype\pype3"
mongo_url = "mongodb://localhost:2707"
os.environ["OPENPYPE_MONGO"] = mongo_url
os.environ["AVALON_MONGO"] = mongo_url
os.environ["OPENPYPE_DATABASE_NAME"] = "openpype"
os.environ["AVALON_CONFIG"] = "openpype"
os.environ["AVALON_TIMEOUT"] = "1000"
os.environ["AVALON_DB"] = "avalon"
for path in [
openpype_dir,
r"{}\repos\avalon-core".format(openpype_dir),
r"{}\.venv\Lib\site-packages".format(openpype_dir)
]:
sys.path.append(path)
from Qt import QtWidgets, QtCore, QtGui
from Qt import QtWidgets
from openpype import style
if __package__:
@ -50,22 +27,3 @@ class PublishLogViewerWindow(QtWidgets.QWidget):
def set_report(self, report_data):
self._main_widget.set_report(report_data)
def main():
"""Main function for testing purposes."""
app = QtWidgets.QApplication([])
window = PublishLogViewerWindow()
log_path = os.path.join(os.path.dirname(__file__), "logs.json")
with open(log_path, "r") as file_stream:
report_data = json.load(file_stream)
window.set_report(report_data)
window.show()
app.exec_()
if __name__ == "__main__":
main()