Publisher: Show instances in report page (#4915)

* renamed 'validations_widget.py' to 'report_page.py'

* Implemented base logic and widgets for logs

* make one report page

* added missing imports

* added missing constants

* move and rename 'VerticallScrollArea' to 'VerticalScrollArea'

* Validation erro item have id

* use 'ReportPageWidget' in window

* change 'bg-button-hover' key to 'bg-buttons-hover' in style colors

* move publish actions widgets

* Refactored how validation error title is showed

* remove item id from validation error item but add id to group items

* remove margins from actions widget

* shrink publish frame on finished publishing

* fix dash line draw

* add missing styles

* fix dash line in thumbnail widget

* added crash widget and changed layout a little

* added infor overlay message

* export and copy report happens in main window

* fix docstrings

* added per plugin filtering for validation errors

* added implementation of 'FlowLayout'

* actions buttons are in flow layout

* fix actions order

* implemented expanding text edit widget

* expand button has some signals and properties

* description and details are separated widgets

* fix typo

* added constans to '__all__'

* parse icon def is a function

* change layout of widgets

* fix log filtering

* added state icon to instances

* fix pyside6 issues

* implemented 'ClassicExpandBtnLabel' with arrow images

* modified details separator

* added some spacing to layouts

* fix syle of description inputs and progress color

* removed unused import

* add 'is_validation_error' to errored result

* validation error has different icon in logs view

* added plugin name to ValueError if happens

* spacer before detail inputs moved out of detals widget

* fix actions visible in craash report

* ignore pyblish base classes

* filter base plugins in discovery

* use 'is' comparison instead of '__eq__'

* fix action error handling

* Fix handling of 'None' values in comparison

* formatting fix

* Report instance card have same margins as in create mode

* publish instances are grouped by family

* log messages are rstripped
This commit is contained in:
Jakub Trllo 2023-05-23 18:16:05 +02:00 committed by GitHub
parent d55211c337
commit a73d19b612
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 2373 additions and 834 deletions

View file

@ -1,13 +1,16 @@
from .layouts import FlowLayout
from .widgets import (
FocusSpinBox,
FocusDoubleSpinBox,
ComboBox,
CustomTextComboBox,
PlaceholderLineEdit,
ExpandingTextEdit,
BaseClickableFrame,
ClickableFrame,
ClickableLabel,
ExpandBtn,
ClassicExpandBtn,
PixmapLabel,
IconButton,
PixmapButton,
@ -37,15 +40,19 @@ from .overlay_messages import (
__all__ = (
"FlowLayout",
"FocusSpinBox",
"FocusDoubleSpinBox",
"ComboBox",
"CustomTextComboBox",
"PlaceholderLineEdit",
"ExpandingTextEdit",
"BaseClickableFrame",
"ClickableFrame",
"ClickableLabel",
"ExpandBtn",
"ClassicExpandBtn",
"PixmapLabel",
"IconButton",
"PixmapButton",

View file

@ -0,0 +1,150 @@
from qtpy import QtWidgets, QtCore
class FlowLayout(QtWidgets.QLayout):
"""Layout that organize widgets by minimum size into a flow layout.
Layout is putting widget from left to right and top to bottom. When widget
can't fit a row it is added to next line. Minimum size matches widget with
biggest 'sizeHint' width and height using calculated geometry.
Content margins are part of calculations. It is possible to define
horizontal and vertical spacing.
Layout does not support stretch and spacing items.
Todos:
Unified width concept -> use width of largest item so all of them are
same. This could allow to have minimum columns option too.
"""
def __init__(self, parent=None):
super(FlowLayout, self).__init__(parent)
# spaces between each item
self._horizontal_spacing = 5
self._vertical_spacing = 5
self._items = []
def __del__(self):
while self.count():
self.takeAt(0, False)
def isEmpty(self):
for item in self._items:
if not item.isEmpty():
return False
return True
def setSpacing(self, spacing):
self._horizontal_spacing = spacing
self._vertical_spacing = spacing
self.invalidate()
def setHorizontalSpacing(self, spacing):
self._horizontal_spacing = spacing
self.invalidate()
def setVerticalSpacing(self, spacing):
self._vertical_spacing = spacing
self.invalidate()
def addItem(self, item):
self._items.append(item)
self.invalidate()
def count(self):
return len(self._items)
def itemAt(self, index):
if 0 <= index < len(self._items):
return self._items[index]
return None
def takeAt(self, index, invalidate=True):
if 0 <= index < len(self._items):
item = self._items.pop(index)
if invalidate:
self.invalidate()
return item
return None
def expandingDirections(self):
return QtCore.Qt.Orientations(QtCore.Qt.Vertical)
def hasHeightForWidth(self):
return True
def heightForWidth(self, width):
return self._setup_geometry(QtCore.QRect(0, 0, width, 0), True)
def setGeometry(self, rect):
super(FlowLayout, self).setGeometry(rect)
self._setup_geometry(rect)
def sizeHint(self):
return self.minimumSize()
def minimumSize(self):
size = QtCore.QSize(0, 0)
for item in self._items:
widget = item.widget()
if widget is not None:
parent = widget.parent()
if not widget.isVisibleTo(parent):
continue
size = size.expandedTo(item.minimumSize())
if size.width() < 1 or size.height() < 1:
return size
l_margin, t_margin, r_margin, b_margin = self.getContentsMargins()
size += QtCore.QSize(l_margin + r_margin, t_margin + b_margin)
return size
def _setup_geometry(self, rect, only_calculate=False):
h_spacing = self._horizontal_spacing
v_spacing = self._vertical_spacing
l_margin, t_margin, r_margin, b_margin = self.getContentsMargins()
left_x = rect.x() + l_margin
top_y = rect.y() + t_margin
pos_x = left_x
pos_y = top_y
row_height = 0
for item in self._items:
item_hint = item.sizeHint()
item_width = item_hint.width()
item_height = item_hint.height()
if item_width < 1 or item_height < 1:
continue
end_x = pos_x + item_width
wrap = (
row_height > 0
and (
end_x > rect.right()
or (end_x + r_margin) > rect.right()
)
)
if not wrap:
next_pos_x = end_x + h_spacing
else:
pos_x = left_x
pos_y += row_height + v_spacing
next_pos_x = pos_x + item_width + h_spacing
row_height = 0
if not only_calculate:
item.setGeometry(
QtCore.QRect(pos_x, pos_y, item_width, item_height)
)
pos_x = next_pos_x
row_height = max(row_height, item_height)
height = (pos_y - top_y) + row_height
if height > 0:
height += b_margin
return height

View file

@ -101,6 +101,46 @@ class PlaceholderLineEdit(QtWidgets.QLineEdit):
self.setPalette(filter_palette)
class ExpandingTextEdit(QtWidgets.QTextEdit):
"""QTextEdit which does not have sroll area but expands height."""
def __init__(self, parent=None):
super(ExpandingTextEdit, self).__init__(parent)
size_policy = self.sizePolicy()
size_policy.setHeightForWidth(True)
size_policy.setVerticalPolicy(QtWidgets.QSizePolicy.Preferred)
self.setSizePolicy(size_policy)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
doc = self.document()
doc.contentsChanged.connect(self._on_doc_change)
def _on_doc_change(self):
self.updateGeometry()
def hasHeightForWidth(self):
return True
def heightForWidth(self, width):
margins = self.contentsMargins()
document_width = 0
if width >= margins.left() + margins.right():
document_width = width - margins.left() - margins.right()
document = self.document().clone()
document.setTextWidth(document_width)
return margins.top() + document.size().height() + margins.bottom()
def sizeHint(self):
width = super(ExpandingTextEdit, self).sizeHint().width()
return QtCore.QSize(width, self.heightForWidth(width))
class BaseClickableFrame(QtWidgets.QFrame):
"""Widget that catch left mouse click and can trigger a callback.
@ -161,19 +201,34 @@ class ClickableLabel(QtWidgets.QLabel):
class ExpandBtnLabel(QtWidgets.QLabel):
"""Label showing expand icon meant for ExpandBtn."""
state_changed = QtCore.Signal()
def __init__(self, parent):
super(ExpandBtnLabel, self).__init__(parent)
self._source_collapsed_pix = QtGui.QPixmap(
get_style_image_path("branch_closed")
)
self._source_expanded_pix = QtGui.QPixmap(
get_style_image_path("branch_open")
)
self._source_collapsed_pix = self._create_collapsed_pixmap()
self._source_expanded_pix = self._create_expanded_pixmap()
self._current_image = self._source_collapsed_pix
self._collapsed = True
def set_collapsed(self, collapsed):
def _create_collapsed_pixmap(self):
return QtGui.QPixmap(
get_style_image_path("branch_closed")
)
def _create_expanded_pixmap(self):
return QtGui.QPixmap(
get_style_image_path("branch_open")
)
@property
def collapsed(self):
return self._collapsed
def set_collapsed(self, collapsed=None):
if collapsed is None:
collapsed = not self._collapsed
if self._collapsed == collapsed:
return
self._collapsed = collapsed
@ -182,6 +237,7 @@ class ExpandBtnLabel(QtWidgets.QLabel):
else:
self._current_image = self._source_expanded_pix
self._set_resized_pix()
self.state_changed.emit()
def resizeEvent(self, event):
self._set_resized_pix()
@ -203,21 +259,55 @@ class ExpandBtnLabel(QtWidgets.QLabel):
class ExpandBtn(ClickableFrame):
state_changed = QtCore.Signal()
def __init__(self, parent=None):
super(ExpandBtn, self).__init__(parent)
pixmap_label = ExpandBtnLabel(self)
pixmap_label = self._create_pix_widget(self)
layout = QtWidgets.QHBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(pixmap_label)
pixmap_label.state_changed.connect(self.state_changed)
self._pixmap_label = pixmap_label
def set_collapsed(self, collapsed):
def _create_pix_widget(self, parent=None):
if parent is None:
parent = self
return ExpandBtnLabel(parent)
@property
def collapsed(self):
return self._pixmap_label.collapsed
def set_collapsed(self, collapsed=None):
self._pixmap_label.set_collapsed(collapsed)
class ClassicExpandBtnLabel(ExpandBtnLabel):
def _create_collapsed_pixmap(self):
return QtGui.QPixmap(
get_style_image_path("right_arrow")
)
def _create_expanded_pixmap(self):
return QtGui.QPixmap(
get_style_image_path("down_arrow")
)
class ClassicExpandBtn(ExpandBtn):
"""Same as 'ExpandBtn' but with arrow images."""
def _create_pix_widget(self, parent=None):
if parent is None:
parent = self
return ClassicExpandBtnLabel(parent)
class ImageButton(QtWidgets.QPushButton):
"""PushButton with icon and size of font.