diff --git a/openpype/hosts/nuke/plugins/inventory/repair_old_loaders.py b/openpype/hosts/nuke/plugins/inventory/repair_old_loaders.py index 49405fd213..5f834be557 100644 --- a/openpype/hosts/nuke/plugins/inventory/repair_old_loaders.py +++ b/openpype/hosts/nuke/plugins/inventory/repair_old_loaders.py @@ -1,4 +1,4 @@ -from avalon import api, style +from avalon import api from openpype.api import Logger from openpype.hosts.nuke.api.lib import set_avalon_knob_data @@ -7,7 +7,7 @@ class RepairOldLoaders(api.InventoryAction): label = "Repair Old Loaders" icon = "gears" - color = style.colors.alert + color = "#cc0000" log = Logger.get_logger(__name__) diff --git a/openpype/hosts/nuke/plugins/load/load_backdrop.py b/openpype/hosts/nuke/plugins/load/load_backdrop.py index 6619cfb414..58ebcc7d49 100644 --- a/openpype/hosts/nuke/plugins/load/load_backdrop.py +++ b/openpype/hosts/nuke/plugins/load/load_backdrop.py @@ -1,4 +1,4 @@ -from avalon import api, style, io +from avalon import api, io import nuke import nukescripts @@ -23,7 +23,7 @@ class LoadBackdropNodes(api.Loader): label = "Iport Nuke Nodes" order = 0 icon = "eye" - color = style.colors.light + color = "white" node_color = "0x7533c1ff" def load(self, context, name, namespace, data): diff --git a/openpype/hosts/nuke/plugins/load/load_effects.py b/openpype/hosts/nuke/plugins/load/load_effects.py index f636c6b510..4d83da1a78 100644 --- a/openpype/hosts/nuke/plugins/load/load_effects.py +++ b/openpype/hosts/nuke/plugins/load/load_effects.py @@ -1,7 +1,8 @@ import json from collections import OrderedDict import nuke -from avalon import api, style, io +from avalon import api, io + from openpype.hosts.nuke.api import ( containerise, update_container, @@ -18,7 +19,7 @@ class LoadEffects(api.Loader): label = "Load Effects - nodes" order = 0 icon = "cc" - color = style.colors.light + color = "white" ignore_attr = ["useLifetime"] diff --git a/openpype/hosts/nuke/plugins/load/load_effects_ip.py b/openpype/hosts/nuke/plugins/load/load_effects_ip.py index 990bce54f1..4d30e0f93c 100644 --- a/openpype/hosts/nuke/plugins/load/load_effects_ip.py +++ b/openpype/hosts/nuke/plugins/load/load_effects_ip.py @@ -3,7 +3,8 @@ from collections import OrderedDict import nuke -from avalon import api, style, io +from avalon import api, io + from openpype.hosts.nuke.api import lib from openpype.hosts.nuke.api import ( containerise, @@ -21,7 +22,7 @@ class LoadEffectsInputProcess(api.Loader): label = "Load Effects - Input Process" order = 0 icon = "eye" - color = style.colors.alert + color = "#cc0000" ignore_attr = ["useLifetime"] def load(self, context, name, namespace, data): diff --git a/openpype/hosts/nuke/plugins/load/load_gizmo.py b/openpype/hosts/nuke/plugins/load/load_gizmo.py index 659977d789..9c726d8fe6 100644 --- a/openpype/hosts/nuke/plugins/load/load_gizmo.py +++ b/openpype/hosts/nuke/plugins/load/load_gizmo.py @@ -1,5 +1,6 @@ import nuke -from avalon import api, style, io +from avalon import api, io + from openpype.hosts.nuke.api.lib import ( maintained_selection, get_avalon_knob_data, @@ -21,7 +22,7 @@ class LoadGizmo(api.Loader): label = "Load Gizmo" order = 0 icon = "dropbox" - color = style.colors.light + color = "white" node_color = "0x75338eff" def load(self, context, name, namespace, data): diff --git a/openpype/hosts/nuke/plugins/load/load_gizmo_ip.py b/openpype/hosts/nuke/plugins/load/load_gizmo_ip.py index 240bfd467d..78d2625758 100644 --- a/openpype/hosts/nuke/plugins/load/load_gizmo_ip.py +++ b/openpype/hosts/nuke/plugins/load/load_gizmo_ip.py @@ -1,5 +1,6 @@ -from avalon import api, style, io +from avalon import api, io import nuke + from openpype.hosts.nuke.api.lib import ( maintained_selection, create_backdrop, @@ -22,7 +23,7 @@ class LoadGizmoInputProcess(api.Loader): label = "Load Gizmo - Input Process" order = 0 icon = "eye" - color = style.colors.alert + color = "#cc0000" node_color = "0x7533c1ff" def load(self, context, name, namespace, data): diff --git a/openpype/hosts/nuke/plugins/load/load_script_precomp.py b/openpype/hosts/nuke/plugins/load/load_script_precomp.py index aa48b631c5..48bf0b889f 100644 --- a/openpype/hosts/nuke/plugins/load/load_script_precomp.py +++ b/openpype/hosts/nuke/plugins/load/load_script_precomp.py @@ -1,5 +1,6 @@ import nuke -from avalon import api, style, io +from avalon import api, io + from openpype.hosts.nuke.api.lib import get_avalon_knob_data from openpype.hosts.nuke.api import ( containerise, @@ -17,7 +18,7 @@ class LinkAsGroup(api.Loader): label = "Load Precomp" order = 0 icon = "file" - color = style.colors.alert + color = "#cc0000" def load(self, context, name, namespace, data): # for k, v in context.items(): diff --git a/openpype/plugins/load/copy_file.py b/openpype/plugins/load/copy_file.py index eaf5853035..bdcb4fec79 100644 --- a/openpype/plugins/load/copy_file.py +++ b/openpype/plugins/load/copy_file.py @@ -1,4 +1,5 @@ -from avalon import api, style +from avalon import api +from openpype.style import get_default_entity_icon_color class CopyFile(api.Loader): @@ -10,7 +11,7 @@ class CopyFile(api.Loader): label = "Copy File" order = 10 icon = "copy" - color = style.colors.default + color = get_default_entity_icon_color() def load(self, context, name=None, namespace=None, data=None): self.log.info("Added copy to clipboard: {0}".format(self.fname)) diff --git a/openpype/plugins/load/delete_old_versions.py b/openpype/plugins/load/delete_old_versions.py index e8612745fb..fb8be0ed33 100644 --- a/openpype/plugins/load/delete_old_versions.py +++ b/openpype/plugins/load/delete_old_versions.py @@ -8,9 +8,10 @@ import ftrack_api import qargparse from Qt import QtWidgets, QtCore -from avalon import api, style +from avalon import api from avalon.api import AvalonMongoDB import avalon.pipeline +from openpype import style from openpype.api import Anatomy diff --git a/openpype/resources/images/spinner-200.svg b/openpype/resources/images/spinner-200.svg new file mode 100644 index 0000000000..73d8ae2890 --- /dev/null +++ b/openpype/resources/images/spinner-200.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/openpype/style/__init__.py b/openpype/style/__init__.py index ea88b342ee..b2a1a4ce6c 100644 --- a/openpype/style/__init__.py +++ b/openpype/style/__init__.py @@ -7,13 +7,19 @@ from openpype import resources from .color_defs import parse_color - -_STYLESHEET_CACHE = None -_FONT_IDS = None - current_dir = os.path.dirname(os.path.abspath(__file__)) +class _Cache: + stylesheet = None + font_ids = None + + tools_icon_color = None + default_entity_icon_color = None + disabled_entity_icon_color = None + deprecated_entity_font_color = None + + def get_style_image_path(image_name): # All filenames are lowered image_name = image_name.lower() @@ -125,21 +131,19 @@ def _load_font(): """Load and register fonts into Qt application.""" from Qt import QtGui - global _FONT_IDS - # Check if font ids are still loaded - if _FONT_IDS is not None: - for font_id in tuple(_FONT_IDS): + if _Cache.font_ids is not None: + for font_id in tuple(_Cache.font_ids): font_families = QtGui.QFontDatabase.applicationFontFamilies( font_id ) # Reset font if font id is not available if not font_families: - _FONT_IDS = None + _Cache.font_ids = None break - if _FONT_IDS is None: - _FONT_IDS = [] + if _Cache.font_ids is None: + _Cache.font_ids = [] fonts_dirpath = os.path.join(current_dir, "fonts") font_dirs = [] font_dirs.append(os.path.join(fonts_dirpath, "Noto_Sans")) @@ -157,7 +161,7 @@ def _load_font(): continue full_path = os.path.join(font_dir, filename) font_id = QtGui.QFontDatabase.addApplicationFont(full_path) - _FONT_IDS.append(font_id) + _Cache.font_ids.append(font_id) font_families = QtGui.QFontDatabase.applicationFontFamilies( font_id ) @@ -167,11 +171,11 @@ def _load_font(): def load_stylesheet(): """Load and return OpenPype Qt stylesheet.""" - global _STYLESHEET_CACHE - if _STYLESHEET_CACHE is None: - _STYLESHEET_CACHE = _load_stylesheet() + + if _Cache.stylesheet is None: + _Cache.stylesheet = _load_stylesheet() _load_font() - return _STYLESHEET_CACHE + return _Cache.stylesheet def get_app_icon_path(): @@ -182,3 +186,63 @@ def get_app_icon_path(): def app_icon_path(): # Backwards compatibility return get_app_icon_path() + + +def get_default_tools_icon_color(): + """Default color used in tool icons. + + Color must be possible to parse using QColor. + + Returns: + str: Color as a string. + """ + if _Cache.tools_icon_color is None: + color_data = get_colors_data() + _Cache.tools_icon_color = color_data["icon-tools"] + return _Cache.tools_icon_color + + +def get_default_entity_icon_color(): + """Default color of entities icons. + + Color must be possible to parse using QColor. + + Returns: + str: Color as a string. + """ + if _Cache.default_entity_icon_color is None: + color_data = get_colors_data() + _Cache.default_entity_icon_color = color_data["icon-entity-default"] + return _Cache.default_entity_icon_color + + +def get_disabled_entity_icon_color(): + """Default color of entities icons. + + TODO: Find more suitable function name. + + Color must be possible to parse using QColor. + + Returns: + str: Color as a string. + """ + if _Cache.disabled_entity_icon_color is None: + color_data = get_colors_data() + _Cache.disabled_entity_icon_color = color_data["icon-entity-disabled"] + return _Cache.disabled_entity_icon_color + + +def get_deprecated_entity_font_color(): + """Font color for deprecated entities. + + Color must be possible to parse using QColor. + + Returns: + str: Color as a string. + """ + if _Cache.deprecated_entity_font_color is None: + color_data = get_colors_data() + _Cache.deprecated_entity_font_color = ( + color_data["font-entity-deprecated"] + ) + return _Cache.deprecated_entity_font_color diff --git a/openpype/style/data.json b/openpype/style/data.json index b8ccef8bbd..a76a77015b 100644 --- a/openpype/style/data.json +++ b/openpype/style/data.json @@ -56,6 +56,12 @@ "delete-btn-bg": "rgb(201, 54, 54)", "delete-btn-bg-disabled": "rgba(201, 54, 54, 64)", + "icon-tools": "#ffffff", + "icon-alert-tools": "#AA5050", + "icon-entity-default": "#bfccd6", + "icon-entity-disabled": "#808080", + "font-entity-deprecated": "#666666", + "tab-widget": { "bg": "#21252B", "bg-selected": "#434a56", diff --git a/openpype/tools/launcher/models.py b/openpype/tools/launcher/models.py index 9036c9cbd5..85d553fca4 100644 --- a/openpype/tools/launcher/models.py +++ b/openpype/tools/launcher/models.py @@ -14,7 +14,10 @@ from openpype.lib.applications import ( CUSTOM_LAUNCH_APP_GROUPS, ApplicationManager ) -from openpype.tools.utils.lib import DynamicQThread +from openpype.tools.utils.lib import ( + DynamicQThread, + get_project_icon, +) from openpype.tools.utils.assets_widget import ( AssetModel, ASSET_NAME_ROLE @@ -400,6 +403,7 @@ class LauncherModel(QtCore.QObject): self._dbcon = dbcon # Available project names self._project_names = set() + self._project_docs_by_name = {} # Context data self._asset_docs = [] @@ -460,6 +464,9 @@ class LauncherModel(QtCore.QObject): """Available project names.""" return self._project_names + def get_project_doc(self, project_name): + return self._project_docs_by_name.get(project_name) + @property def asset_filter_data_by_id(self): """Prepared filter data by asset id.""" @@ -516,9 +523,13 @@ class LauncherModel(QtCore.QObject): """Refresh projects.""" current_project = self.project_name project_names = set() + project_docs_by_name = {} for project_doc in self._dbcon.projects(only_active=True): - project_names.add(project_doc["name"]) + project_name = project_doc["name"] + project_names.add(project_name) + project_docs_by_name[project_name] = project_doc + self._project_docs_by_name = project_docs_by_name self._project_names = project_names self.projects_refreshed.emit() if ( @@ -694,6 +705,11 @@ class LauncherTaskModel(TasksModel): self._launcher_model = launcher_model super(LauncherTaskModel, self).__init__(*args, **kwargs) + def _refresh_project_doc(self): + self._project_doc = self._launcher_model.get_project_doc( + self._launcher_model.project_name + ) + def set_asset_id(self, asset_id): asset_doc = None if self._context_is_valid(): @@ -718,7 +734,6 @@ class AssetRecursiveSortFilterModel(QtCore.QSortFilterProxyModel): self._assignee_filter = self._launcher_model.assignee_filters self.invalidateFilter() - """Filters to the regex if any of the children matches allow parent""" def filterAcceptsRow(self, row, parent): if ( not self._name_filter @@ -818,7 +833,6 @@ class ProjectModel(QtGui.QStandardItemModel): super(ProjectModel, self).__init__(parent=parent) self._launcher_model = launcher_model - self.project_icon = qtawesome.icon("fa.map", color="white") self._project_names = set() launcher_model.projects_refreshed.connect(self._on_refresh) @@ -863,7 +877,11 @@ class ProjectModel(QtGui.QStandardItemModel): for row in reversed(sorted(row_counts.keys())): items = [] for project_name in row_counts[row]: - item = QtGui.QStandardItem(self.project_icon, project_name) + project_doc = self._launcher_model.get_project_doc( + project_name + ) + icon = get_project_icon(project_doc) + item = QtGui.QStandardItem(icon, project_name) items.append(item) self.invisibleRootItem().insertRows(row, items) diff --git a/openpype/tools/loader/model.py b/openpype/tools/loader/model.py index baee569239..1007355989 100644 --- a/openpype/tools/loader/model.py +++ b/openpype/tools/loader/model.py @@ -3,15 +3,13 @@ import re import math from uuid import uuid4 -from avalon import ( - style, - schema -) from Qt import QtCore, QtGui import qtawesome +from avalon import schema from avalon.lib import HeroVersionType +from openpype.style import get_default_entity_icon_color from openpype.tools.utils.models import TreeModel, Item from openpype.tools.utils import lib @@ -180,7 +178,10 @@ class SubsetsModel(TreeModel, BaseRepresentationModel): self._sorter = None self._grouping = grouping self._icons = { - "subset": qtawesome.icon("fa.file-o", color=style.colors.default) + "subset": qtawesome.icon( + "fa.file-o", + color=get_default_entity_icon_color() + ) } self._items_by_id = {} @@ -1066,8 +1067,10 @@ class RepresentationModel(TreeModel, BaseRepresentationModel): self._docs = {} self._icons = lib.get_repre_icons() - self._icons["repre"] = qtawesome.icon("fa.file-o", - color=style.colors.default) + self._icons["repre"] = qtawesome.icon( + "fa.file-o", + color=get_default_entity_icon_color() + ) self._items_by_id = {} def set_version_ids(self, version_ids): @@ -1165,7 +1168,7 @@ class RepresentationModel(TreeModel, BaseRepresentationModel): "remote_site_name": self.remote_site, "icon": qtawesome.icon( "fa.folder", - color=style.colors.default + color=get_default_entity_icon_color() ) }) self._items_by_id[item_id] = group_item diff --git a/openpype/tools/mayalookassigner/__init__.py b/openpype/tools/mayalookassigner/__init__.py index 616a3e94d0..5e40777741 100644 --- a/openpype/tools/mayalookassigner/__init__.py +++ b/openpype/tools/mayalookassigner/__init__.py @@ -1,9 +1,9 @@ from .app import ( - App, + MayaLookAssignerWindow, show ) __all__ = [ - "App", + "MayaLookAssignerWindow", "show"] diff --git a/openpype/tools/mayalookassigner/app.py b/openpype/tools/mayalookassigner/app.py index 31bb455f95..0e633a21e3 100644 --- a/openpype/tools/mayalookassigner/app.py +++ b/openpype/tools/mayalookassigner/app.py @@ -4,10 +4,10 @@ import logging from Qt import QtWidgets, QtCore -from openpype.hosts.maya.api.lib import assign_look_by_version - -from avalon import style, io +from avalon import io +from openpype import style from openpype.tools.utils.lib import qt_app_context +from openpype.hosts.maya.api.lib import assign_look_by_version from maya import cmds # old api for MFileIO @@ -28,10 +28,10 @@ module = sys.modules[__name__] module.window = None -class App(QtWidgets.QWidget): +class MayaLookAssignerWindow(QtWidgets.QWidget): def __init__(self, parent=None): - QtWidgets.QWidget.__init__(self, parent=parent) + super(MayaLookAssignerWindow, self).__init__(parent=parent) self.log = logging.getLogger(__name__) @@ -56,30 +56,41 @@ class App(QtWidgets.QWidget): def setup_ui(self): """Build the UI""" + main_splitter = QtWidgets.QSplitter(self) + # Assets (left) - asset_outliner = AssetOutliner() + asset_outliner = AssetOutliner(main_splitter) # Looks (right) - looks_widget = QtWidgets.QWidget() - looks_layout = QtWidgets.QVBoxLayout(looks_widget) + looks_widget = QtWidgets.QWidget(main_splitter) - look_outliner = LookOutliner() # Database look overview + look_outliner = LookOutliner(looks_widget) # Database look overview - assign_selected = QtWidgets.QCheckBox("Assign to selected only") + assign_selected = QtWidgets.QCheckBox( + "Assign to selected only", looks_widget + ) assign_selected.setToolTip("Whether to assign only to selected nodes " "or to the full asset") - remove_unused_btn = QtWidgets.QPushButton("Remove Unused Looks") + remove_unused_btn = QtWidgets.QPushButton( + "Remove Unused Looks", looks_widget + ) + looks_layout = QtWidgets.QVBoxLayout(looks_widget) looks_layout.addWidget(look_outliner) looks_layout.addWidget(assign_selected) looks_layout.addWidget(remove_unused_btn) + main_splitter.addWidget(asset_outliner) + main_splitter.addWidget(looks_widget) + main_splitter.setSizes([350, 200]) + # Footer - status = QtWidgets.QStatusBar() + status = QtWidgets.QStatusBar(self) status.setSizeGripEnabled(False) status.setFixedHeight(25) - warn_layer = QtWidgets.QLabel("Current Layer is not " - "defaultRenderLayer") + warn_layer = QtWidgets.QLabel( + "Current Layer is not defaultRenderLayer", self + ) warn_layer.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) warn_layer.setStyleSheet("color: #DD5555; font-weight: bold;") warn_layer.setFixedHeight(25) @@ -92,11 +103,6 @@ class App(QtWidgets.QWidget): # Build up widgets main_layout = QtWidgets.QVBoxLayout(self) main_layout.setSpacing(0) - main_splitter = QtWidgets.QSplitter() - main_splitter.setStyleSheet("QSplitter{ border: 0px; }") - main_splitter.addWidget(asset_outliner) - main_splitter.addWidget(looks_widget) - main_splitter.setSizes([350, 200]) main_layout.addWidget(main_splitter) main_layout.addLayout(footer) @@ -124,6 +130,8 @@ class App(QtWidgets.QWidget): self.remove_unused = remove_unused_btn self.assign_selected = assign_selected + self._first_show = True + def setup_connections(self): """Connect interactive widgets with actions""" if self._connections_set_up: @@ -147,11 +155,14 @@ class App(QtWidgets.QWidget): def showEvent(self, event): self.setup_connections() - super(App, self).showEvent(event) + super(MayaLookAssignerWindow, self).showEvent(event) + if self._first_show: + self._first_show = False + self.setStyleSheet(style.load_stylesheet()) def closeEvent(self, event): self.remove_connection() - super(App, self).closeEvent(event) + super(MayaLookAssignerWindow, self).closeEvent(event) def _on_renderlayer_switch(self, *args): """Callback that updates on Maya renderlayer switch""" @@ -267,8 +278,7 @@ def show(): if widget.objectName() == "MayaWindow") with qt_app_context(): - window = App(parent=mainwindow) - window.setStyleSheet(style.load_stylesheet()) + window = MayaLookAssignerWindow(parent=mainwindow) window.show() module.window = window diff --git a/openpype/tools/mayalookassigner/models.py b/openpype/tools/mayalookassigner/models.py index 386b7d7e1e..77a3c8a590 100644 --- a/openpype/tools/mayalookassigner/models.py +++ b/openpype/tools/mayalookassigner/models.py @@ -3,14 +3,19 @@ from collections import defaultdict from Qt import QtCore import qtawesome -from avalon.style import colors from openpype.tools.utils import models +from openpype.style import get_default_entity_icon_color class AssetModel(models.TreeModel): Columns = ["label"] + def __init__(self, *args, **kwargs): + super(AssetModel, self).__init__(*args, **kwargs) + + self._icon_color = get_default_entity_icon_color() + def add_items(self, items): """ Add items to model with needed data @@ -65,8 +70,10 @@ class AssetModel(models.TreeModel): node = index.internalPointer() icon = node.get("icon") if icon: - return qtawesome.icon("fa.{0}".format(icon), - color=colors.default) + return qtawesome.icon( + "fa.{0}".format(icon), + color=self._icon_color + ) return super(AssetModel, self).data(index, role) diff --git a/openpype/tools/mayalookassigner/views.py b/openpype/tools/mayalookassigner/views.py index 993023bb45..8e676ebc7f 100644 --- a/openpype/tools/mayalookassigner/views.py +++ b/openpype/tools/mayalookassigner/views.py @@ -1,9 +1,6 @@ from Qt import QtWidgets, QtCore -DEFAULT_COLOR = "#fb9c15" - - class View(QtWidgets.QTreeView): data_changed = QtCore.Signal() diff --git a/openpype/tools/mayalookassigner/widgets.py b/openpype/tools/mayalookassigner/widgets.py index e546ee705d..10e573342a 100644 --- a/openpype/tools/mayalookassigner/widgets.py +++ b/openpype/tools/mayalookassigner/widgets.py @@ -14,7 +14,7 @@ from .models import ( LookModel ) from . import commands -from . import views +from .views import View from maya import cmds @@ -24,25 +24,28 @@ class AssetOutliner(QtWidgets.QWidget): selection_changed = QtCore.Signal() def __init__(self, parent=None): - QtWidgets.QWidget.__init__(self, parent) + super(AssetOutliner, self).__init__(parent) - layout = QtWidgets.QVBoxLayout() - - title = QtWidgets.QLabel("Assets") + title = QtWidgets.QLabel("Assets", self) title.setAlignment(QtCore.Qt.AlignCenter) title.setStyleSheet("font-weight: bold; font-size: 12px") model = AssetModel() - view = views.View() + view = View(self) view.setModel(model) view.customContextMenuRequested.connect(self.right_mouse_menu) view.setSortingEnabled(False) view.setHeaderHidden(True) view.setIndentation(10) - from_all_asset_btn = QtWidgets.QPushButton("Get All Assets") - from_selection_btn = QtWidgets.QPushButton("Get Assets From Selection") + from_all_asset_btn = QtWidgets.QPushButton( + "Get All Assets", self + ) + from_selection_btn = QtWidgets.QPushButton( + "Get Assets From Selection", self + ) + layout = QtWidgets.QVBoxLayout(self) layout.addWidget(title) layout.addWidget(from_all_asset_btn) layout.addWidget(from_selection_btn) @@ -58,8 +61,6 @@ class AssetOutliner(QtWidgets.QWidget): self.view = view self.model = model - self.setLayout(layout) - self.log = logging.getLogger(__name__) def clear(self): @@ -188,15 +189,10 @@ class LookOutliner(QtWidgets.QWidget): menu_apply_action = QtCore.Signal() def __init__(self, parent=None): - QtWidgets.QWidget.__init__(self, parent) - - # look manager layout - layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(10) + super(LookOutliner, self).__init__(parent) # Looks from database - title = QtWidgets.QLabel("Looks") + title = QtWidgets.QLabel("Looks", self) title.setAlignment(QtCore.Qt.AlignCenter) title.setStyleSheet("font-weight: bold; font-size: 12px") title.setAlignment(QtCore.Qt.AlignCenter) @@ -207,13 +203,17 @@ class LookOutliner(QtWidgets.QWidget): proxy = QtCore.QSortFilterProxyModel() proxy.setSourceModel(model) - view = views.View() + view = View(self) view.setModel(proxy) view.setMinimumHeight(180) view.setToolTip("Use right mouse button menu for direct actions") view.customContextMenuRequested.connect(self.right_mouse_menu) view.sortByColumn(0, QtCore.Qt.AscendingOrder) + # look manager layout + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(10) layout.addWidget(title) layout.addWidget(view) diff --git a/openpype/tools/project_manager/project_manager/multiselection_combobox.py b/openpype/tools/project_manager/project_manager/multiselection_combobox.py index 890567de6d..f776831298 100644 --- a/openpype/tools/project_manager/project_manager/multiselection_combobox.py +++ b/openpype/tools/project_manager/project_manager/multiselection_combobox.py @@ -1,4 +1,4 @@ -from Qt import QtCore, QtGui, QtWidgets +from Qt import QtCore, QtWidgets class ComboItemDelegate(QtWidgets.QStyledItemDelegate): diff --git a/openpype/tools/publisher/widgets/tasks_widget.py b/openpype/tools/publisher/widgets/tasks_widget.py index 8a913b7114..aa239f6334 100644 --- a/openpype/tools/publisher/widgets/tasks_widget.py +++ b/openpype/tools/publisher/widgets/tasks_widget.py @@ -1,7 +1,7 @@ from Qt import QtCore, QtGui from openpype.tools.utils.tasks_widget import TasksWidget, TASK_NAME_ROLE -from openpype.tools.utils.lib import get_task_icon +from openpype.tools.utils.lib import get_default_task_icon class TasksModel(QtGui.QStandardItemModel): @@ -120,7 +120,7 @@ class TasksModel(QtGui.QStandardItemModel): item = QtGui.QStandardItem(task_name) item.setData(task_name, TASK_NAME_ROLE) if task_name: - item.setData(get_task_icon(), QtCore.Qt.DecorationRole) + item.setData(get_default_task_icon(), QtCore.Qt.DecorationRole) self._items_by_name[task_name] = item new_items.append(item) diff --git a/openpype/tools/sceneinventory/model.py b/openpype/tools/sceneinventory/model.py index cba60be355..6ec3601705 100644 --- a/openpype/tools/sceneinventory/model.py +++ b/openpype/tools/sceneinventory/model.py @@ -6,8 +6,9 @@ from collections import defaultdict from Qt import QtCore, QtGui import qtawesome -from avalon import api, io, style, schema +from avalon import api, io, schema from avalon.lib import HeroVersionType +from openpype.style import get_default_entity_icon_color from openpype.tools.utils.models import TreeModel, Item from .lib import ( @@ -38,6 +39,8 @@ class InventoryModel(TreeModel): self._hierarchy_view = False + self._default_icon_color = get_default_entity_icon_color() + manager = ModulesManager() sync_server = manager.modules_by_name["sync_server"] self.sync_enabled = sync_server.enabled @@ -131,7 +134,7 @@ class InventoryModel(TreeModel): if role == QtCore.Qt.DecorationRole: if index.column() == 0: # Override color - color = item.get("color", style.colors.default) + color = item.get("color", self._default_icon_color) if item.get("isGroupNode"): # group-item return qtawesome.icon("fa.folder", color=color) if item.get("isNotSet"): diff --git a/openpype/tools/sceneinventory/view.py b/openpype/tools/sceneinventory/view.py index 32c1883de6..fb93faefd6 100644 --- a/openpype/tools/sceneinventory/view.py +++ b/openpype/tools/sceneinventory/view.py @@ -5,9 +5,10 @@ from functools import partial from Qt import QtWidgets, QtCore import qtawesome -from avalon import io, api, style +from avalon import io, api from avalon.lib import HeroVersionType +from openpype import style from openpype.modules import ModulesManager from openpype.tools.utils.lib import ( get_progress_for_repre, diff --git a/openpype/tools/standalonepublish/widgets/model_asset.py b/openpype/tools/standalonepublish/widgets/model_asset.py index 6d764eff9f..a7316a2aa7 100644 --- a/openpype/tools/standalonepublish/widgets/model_asset.py +++ b/openpype/tools/standalonepublish/widgets/model_asset.py @@ -1,10 +1,15 @@ import logging import collections + from Qt import QtCore, QtGui import qtawesome -from . import TreeModel, Node -from avalon import style +from openpype.style import ( + get_default_entity_icon_color, + get_deprecated_entity_font_color, +) + +from . import TreeModel, Node log = logging.getLogger(__name__) @@ -49,6 +54,14 @@ class AssetModel(TreeModel): def __init__(self, dbcon, parent=None): super(AssetModel, self).__init__(parent=parent) self.dbcon = dbcon + + self._default_asset_icon_color = QtGui.QColor( + get_default_entity_icon_color() + ) + self._deprecated_asset_font_color = QtGui.QColor( + get_deprecated_entity_font_color() + ) + self.refresh() def _add_hierarchy(self, assets, parent=None, silos=None): @@ -163,7 +176,7 @@ class AssetModel(TreeModel): icon = data.get("icon", None) if icon is None and node.get("type") == "silo": icon = "database" - color = data.get("color", style.colors.default) + color = data.get("color", self._default_asset_icon_color) if icon is None: # Use default icons if no custom one is specified. @@ -189,7 +202,7 @@ class AssetModel(TreeModel): if role == QtCore.Qt.ForegroundRole: # font color if "deprecated" in node.get("tags", []): - return QtGui.QColor(style.colors.light).darker(250) + return QtGui.QColor(self._deprecated_asset_font_color) if role == self.ObjectIdRole: return node.get("_id", None) diff --git a/openpype/tools/standalonepublish/widgets/model_tasks_template.py b/openpype/tools/standalonepublish/widgets/model_tasks_template.py index 1f36eaa39d..648f7ed479 100644 --- a/openpype/tools/standalonepublish/widgets/model_tasks_template.py +++ b/openpype/tools/standalonepublish/widgets/model_tasks_template.py @@ -1,7 +1,9 @@ from Qt import QtCore import qtawesome + +from openpype.style import get_default_entity_icon_color + from . import Node, TreeModel -from avalon import style class TasksTemplateModel(TreeModel): @@ -14,7 +16,7 @@ class TasksTemplateModel(TreeModel): self.selectable = selectable self.icon = qtawesome.icon( 'fa.calendar-check-o', - color=style.colors.default + color=get_default_entity_icon_color() ) def set_tasks(self, tasks): diff --git a/openpype/tools/standalonepublish/widgets/widget_asset.py b/openpype/tools/standalonepublish/widgets/widget_asset.py index d929f227f9..e6b74f8f82 100644 --- a/openpype/tools/standalonepublish/widgets/widget_asset.py +++ b/openpype/tools/standalonepublish/widgets/widget_asset.py @@ -4,7 +4,7 @@ import qtawesome from openpype.tools.utils import PlaceholderLineEdit -from avalon import style +from openpype.style import get_default_tools_icon_color from . import RecursiveSortFilterProxyModel, AssetModel from . import TasksTemplateModel, DeselectableTreeView @@ -165,7 +165,9 @@ class AssetWidget(QtWidgets.QWidget): # Header header = QtWidgets.QHBoxLayout() - icon = qtawesome.icon("fa.refresh", color=style.colors.light) + icon = qtawesome.icon( + "fa.refresh", color=get_default_tools_icon_color() + ) refresh = QtWidgets.QPushButton(icon, "") refresh.setToolTip("Refresh items") diff --git a/openpype/tools/utils/__init__.py b/openpype/tools/utils/__init__.py index 6ab9e75b52..ea1133c442 100644 --- a/openpype/tools/utils/__init__.py +++ b/openpype/tools/utils/__init__.py @@ -42,6 +42,7 @@ __all__ = ( "set_style_property", "DynamicQThread", "qt_app_context", + "get_asset_icon", "RecursiveSortFilterProxyModel", ) diff --git a/openpype/tools/utils/assets_widget.py b/openpype/tools/utils/assets_widget.py index 4c77b81c0e..3d4efcdd4d 100644 --- a/openpype/tools/utils/assets_widget.py +++ b/openpype/tools/utils/assets_widget.py @@ -5,9 +5,10 @@ import Qt from Qt import QtWidgets, QtCore, QtGui import qtawesome -from avalon import style - -from openpype.style import get_objected_colors +from openpype.style import ( + get_objected_colors, + get_default_tools_icon_color, +) from openpype.tools.flickcharm import FlickCharm from .views import ( @@ -512,7 +513,7 @@ class AssetModel(QtGui.QStandardItemModel): item.setData(asset_label, ASSET_LABEL_ROLE) has_children = item.rowCount() > 0 - icon = get_asset_icon(asset_data, has_children) + icon = get_asset_icon(asset_doc, has_children) item.setData(icon, QtCore.Qt.DecorationRole) def _threaded_fetch(self): @@ -589,7 +590,7 @@ class AssetsWidget(QtWidgets.QWidget): view.setModel(proxy) current_asset_icon = qtawesome.icon( - "fa.arrow-down", color=style.colors.light + "fa.arrow-down", color=get_default_tools_icon_color() ) current_asset_btn = QtWidgets.QPushButton(self) current_asset_btn.setIcon(current_asset_icon) @@ -597,7 +598,9 @@ class AssetsWidget(QtWidgets.QWidget): # Hide by default current_asset_btn.setVisible(False) - refresh_icon = qtawesome.icon("fa.refresh", color=style.colors.light) + refresh_icon = qtawesome.icon( + "fa.refresh", color=get_default_tools_icon_color() + ) refresh_btn = QtWidgets.QPushButton(self) refresh_btn.setIcon(refresh_icon) refresh_btn.setToolTip("Refresh items") diff --git a/openpype/tools/utils/host_tools.py b/openpype/tools/utils/host_tools.py index 6ce9e818d9..2d9733ec94 100644 --- a/openpype/tools/utils/host_tools.py +++ b/openpype/tools/utils/host_tools.py @@ -224,20 +224,18 @@ class HostToolsHelper: def get_look_assigner_tool(self, parent): """Create, cache and return look assigner tool window.""" if self._look_assigner_tool is None: - import mayalookassigner + from openpype.tools.mayalookassigner import MayaLookAssignerWindow - mayalookassigner_window = mayalookassigner.App(parent) + mayalookassigner_window = MayaLookAssignerWindow(parent) self._look_assigner_tool = mayalookassigner_window return self._look_assigner_tool def show_look_assigner(self, parent=None): """Look manager is Maya specific tool for look management.""" - from avalon import style with qt_app_context(): look_assigner_tool = self.get_look_assigner_tool(parent) look_assigner_tool.show() - look_assigner_tool.setStyleSheet(style.load_stylesheet()) def get_experimental_tools_dialog(self, parent=None): """Dialog of experimental tools. diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py index 042ceaab88..93b156bef8 100644 --- a/openpype/tools/utils/lib.py +++ b/openpype/tools/utils/lib.py @@ -7,8 +7,8 @@ from Qt import QtWidgets, QtCore, QtGui import qtawesome import avalon.api -from avalon import style +from openpype.style import get_default_entity_icon_color from openpype.api import ( get_project_settings, Logger @@ -92,10 +92,6 @@ def qt_app_context(): yield app -# Backwards compatibility -application = qt_app_context - - class SharedObjects: jobs = {} icons = {} @@ -126,30 +122,74 @@ def get_qta_icon_by_name_and_color(icon_name, icon_color): return icon +def get_project_icon(project_doc): + if project_doc: + icon_name = project_doc.get("data", {}).get("icon") + icon = get_qta_icon_by_name_and_color(icon_name, "white") + if icon: + return icon + + return get_qta_icon_by_name_and_color( + "fa.map", get_default_entity_icon_color() + ) + + +def get_asset_icon_name(asset_doc, has_children=True): + icon_name = asset_doc["data"].get("icon") + if icon_name: + return icon_name + + if has_children: + return "folder" + return "folder-o" + + +def get_asset_icon_color(asset_doc): + icon_color = asset_doc["data"].get("color") + if icon_color: + return icon_color + return get_default_entity_icon_color() + + def get_asset_icon(asset_doc, has_children=False): - asset_data = asset_doc.get("data") or {} - icon_color = asset_data.get("color") or style.colors.default - icon_name = asset_data.get("icon") - if not icon_name: - # Use default icons if no custom one is specified. - # If it has children show a full folder, otherwise - # show an open folder - if has_children: - icon_name = "folder" - else: - icon_name = "folder-o" + icon_name = get_asset_icon_name(asset_doc, has_children) + icon_color = get_asset_icon_color(asset_doc) return get_qta_icon_by_name_and_color(icon_name, icon_color) -def get_task_icon(): - """Get icon for a task. +def get_default_task_icon(color=None): + if color is None: + color = get_default_entity_icon_color() + return get_qta_icon_by_name_and_color("fa.male", color) - TODO: Get task icon based on data in database. + +def get_task_icon(project_doc, asset_doc, task_name): + """Get icon for a task. Icon should be defined by task type which is stored on project. """ - return get_qta_icon_by_name_and_color("fa.male", style.colors.default) + + color = get_default_entity_icon_color() + + tasks_info = asset_doc.get("data", {}).get("tasks") or {} + task_info = tasks_info.get(task_name) or {} + task_icon = task_info.get("icon") + if task_icon: + icon = get_qta_icon_by_name_and_color(task_icon, color) + if icon is not None: + return icon + + task_type = task_info.get("type") + task_types = project_doc["config"]["tasks"] + + task_type_info = task_types.get(task_type) or {} + task_type_icon = task_type_info.get("icon") + if task_type_icon: + icon = get_qta_icon_by_name_and_color(task_icon, color) + if icon is not None: + return icon + return get_default_task_icon(color) def schedule(func, time, channel="default"): @@ -412,6 +452,7 @@ class GroupsConfig: def __init__(self, dbcon): self.dbcon = dbcon self.groups = {} + self._default_group_color = get_default_entity_icon_color() @classmethod def default_group_config(cls): @@ -419,7 +460,7 @@ class GroupsConfig: cls._default_group_config = { "icon": qtawesome.icon( "fa.object-group", - color=style.colors.default + color=get_default_entity_icon_color() ), "order": 0 } @@ -453,7 +494,7 @@ class GroupsConfig: for config in group_configs: name = config["name"] icon = "fa." + config.get("icon", "object-group") - color = config.get("color", style.colors.default) + color = config.get("color", self._default_group_color) order = float(config.get("order", 0)) self.groups[name] = { diff --git a/openpype/tools/utils/tasks_widget.py b/openpype/tools/utils/tasks_widget.py index 7619f59974..eab183d5f3 100644 --- a/openpype/tools/utils/tasks_widget.py +++ b/openpype/tools/utils/tasks_widget.py @@ -1,7 +1,8 @@ from Qt import QtWidgets, QtCore, QtGui import qtawesome -from avalon import style +from openpype.style import get_disabled_entity_icon_color +from openpype.tools.utils.lib import get_task_icon from .views import DeselectableTreeView @@ -21,53 +22,35 @@ class TasksModel(QtGui.QStandardItemModel): self.setHeaderData( 0, QtCore.Qt.Horizontal, "Tasks", QtCore.Qt.DisplayRole ) - self._default_icon = qtawesome.icon( - "fa.male", - color=style.colors.default - ) + self._no_tasks_icon = qtawesome.icon( "fa.exclamation-circle", - color=style.colors.mid + color=get_disabled_entity_icon_color() ) self._cached_icons = {} - self._project_task_types = {} + self._project_doc = {} self._empty_tasks_item = None self._last_asset_id = None self._loaded_project_name = None def _context_is_valid(self): - if self.dbcon.Session.get("AVALON_PROJECT"): + if self._get_current_project(): return True return False def refresh(self): - self._refresh_task_types() + self._refresh_project_doc() self.set_asset_id(self._last_asset_id) - def _refresh_task_types(self): + def _refresh_project_doc(self): # Get the project configured icons from database - task_types = {} + project_doc = {} if self._context_is_valid(): - project = self.dbcon.find_one( - {"type": "project"}, - {"config.tasks"} - ) - task_types = project["config"].get("tasks") or task_types - self._project_task_types = task_types + project_doc = self.dbcon.find_one({"type": "project"}) - def _try_get_awesome_icon(self, icon_name): - icon = None - if icon_name: - try: - icon = qtawesome.icon( - "fa.{}".format(icon_name), - color=style.colors.default - ) - - except Exception: - pass - return icon + self._loaded_project_name = self._get_current_project() + self._project_doc = project_doc def headerData(self, section, orientation, role=None): if role is None: @@ -82,28 +65,8 @@ class TasksModel(QtGui.QStandardItemModel): return super(TasksModel, self).headerData(section, orientation, role) - def _get_icon(self, task_icon, task_type_icon): - if task_icon in self._cached_icons: - return self._cached_icons[task_icon] - - icon = self._try_get_awesome_icon(task_icon) - if icon is not None: - self._cached_icons[task_icon] = icon - return icon - - if task_type_icon in self._cached_icons: - icon = self._cached_icons[task_type_icon] - self._cached_icons[task_icon] = icon - return icon - - icon = self._try_get_awesome_icon(task_type_icon) - if icon is None: - icon = self._default_icon - - self._cached_icons[task_icon] = icon - self._cached_icons[task_type_icon] = icon - - return icon + def _get_current_project(self): + return self.dbcon.Session.get("AVALON_PROJECT") def set_asset_id(self, asset_id): asset_doc = None @@ -128,6 +91,9 @@ class TasksModel(QtGui.QStandardItemModel): Arguments: asset_doc (dict): Asset document from MongoDB. """ + if self._loaded_project_name != self._get_current_project(): + self._refresh_project_doc() + asset_tasks = {} self._last_asset_id = None if asset_doc: @@ -138,13 +104,11 @@ class TasksModel(QtGui.QStandardItemModel): root_item.removeRows(0, root_item.rowCount()) items = [] + for task_name, task_info in asset_tasks.items(): - task_icon = task_info.get("icon") task_type = task_info.get("type") task_order = task_info.get("order") - task_type_info = self._project_task_types.get(task_type) or {} - task_type_icon = task_type_info.get("icon") - icon = self._get_icon(task_icon, task_type_icon) + icon = get_task_icon(self._project_doc, asset_doc, task_name) task_assignees = set() assignees_data = task_info.get("assignees") or [] diff --git a/openpype/tools/utils/views.py b/openpype/tools/utils/views.py index 97aaf622a4..a2f1f15b95 100644 --- a/openpype/tools/utils/views.py +++ b/openpype/tools/utils/views.py @@ -1,5 +1,5 @@ import os -from avalon import style +from openpype.resources import get_image_path from Qt import QtWidgets, QtCore, QtGui, QtSvg @@ -24,11 +24,8 @@ class TreeViewSpinner(QtWidgets.QTreeView): def __init__(self, parent=None): super(TreeViewSpinner, self).__init__(parent=parent) - loading_image_path = os.path.join( - os.path.dirname(os.path.abspath(style.__file__)), - "svg", - "spinner-200.svg" - ) + loading_image_path = get_image_path("spinner-200.svg") + self.spinner = QtSvg.QSvgRenderer(loading_image_path) self.is_loading = False diff --git a/openpype/tools/workfiles/model.py b/openpype/tools/workfiles/model.py index b3cf5063e7..e9184842fc 100644 --- a/openpype/tools/workfiles/model.py +++ b/openpype/tools/workfiles/model.py @@ -4,7 +4,11 @@ import logging from Qt import QtCore import qtawesome -from avalon import style +from openpype.style import ( + get_default_entity_icon_color, + get_disabled_entity_icon_color, +) + from openpype.tools.utils.models import TreeModel, Item log = logging.getLogger(__name__) @@ -25,7 +29,10 @@ class FilesModel(TreeModel): self._root = None self._file_extensions = file_extensions self._icons = { - "file": qtawesome.icon("fa.file-o", color=style.colors.default) + "file": qtawesome.icon( + "fa.file-o", + color=get_default_entity_icon_color() + ) } def set_root(self, root): @@ -64,7 +71,10 @@ class FilesModel(TreeModel): "date": None, "filepath": None, "enabled": False, - "icon": qtawesome.icon("fa.times", color=style.colors.mid) + "icon": qtawesome.icon( + "fa.times", + color=get_disabled_entity_icon_color() + ) }) self.add_child(item) self.endResetModel()