diff --git a/pype/modules/settings_action.py b/pype/modules/settings_action.py index 0d56a6c5ae..c1fa8a68bc 100644 --- a/pype/modules/settings_action.py +++ b/pype/modules/settings_action.py @@ -45,8 +45,16 @@ class SettingsAction(PypeModule, ITrayAction): if not self.settings_window: raise AssertionError("Window is not initialized.") + # Store if was visible + was_visible = self.settings_window.isVisible() + + # Show settings gui self.settings_window.show() # Pull window to the front. self.settings_window.raise_() self.settings_window.activateWindow() + + # Reset content if was not visible + if not was_visible: + self.settings_window.reset() diff --git a/pype/tools/settings/__init__.py b/pype/tools/settings/__init__.py index 89abd262e8..3090adcf0a 100644 --- a/pype/tools/settings/__init__.py +++ b/pype/tools/settings/__init__.py @@ -20,6 +20,7 @@ def main(user_role=None): widget = MainWidget(user_role) widget.show() + widget.reset() sys.exit(app.exec_()) diff --git a/pype/tools/settings/settings/style/style.css b/pype/tools/settings/settings/style/style.css index 82313d5cfa..f3eb3a258e 100644 --- a/pype/tools/settings/settings/style/style.css +++ b/pype/tools/settings/settings/style/style.css @@ -193,6 +193,9 @@ QPushButton[btn-type="expand-toggle"] { background-color: #21252B; } +#ShadowWidget { + font-size: 36pt; +} QTabWidget::pane { border-top-style: none; } diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index 8e16c3614c..0a788e7684 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -1,6 +1,7 @@ import os import copy import json +from enum import Enum from Qt import QtWidgets, QtCore, QtGui from pype.settings.constants import ( SYSTEM_SETTINGS_KEY, @@ -26,27 +27,60 @@ from pype.settings.lib import ( save_project_anatomy, apply_overrides, + get_system_settings, find_environments, DuplicatedEnvGroups ) from .widgets import UnsavedChangesDialog from . import lib -from avalon import io +from avalon.mongodb import ( + AvalonMongoConnection, + AvalonMongoDB +) from avalon.vendor import qtawesome +class CategoryState(Enum): + Idle = object() + Working = object() + + class SettingsCategoryWidget(QtWidgets.QWidget): schema_category = None initial_schema_name = None + state_changed = QtCore.Signal() + saved = QtCore.Signal(QtWidgets.QWidget) + def __init__(self, user_role, parent=None): super(SettingsCategoryWidget, self).__init__(parent) self.user_role = user_role + self._state = CategoryState.Idle + self.initialize_attributes() self.create_ui() - self.reset() + + @property + def state(self): + return self._state + + @state.setter + def state(self, value): + self.set_state(value) + + def set_state(self, state): + if self._state == state: + return + + self._state = state + self.state_changed.emit() + + # Process events so emitted signal is processed + app = QtWidgets.QApplication.instance() + if app: + app.processEvents() def initialize_attributes(self): self._hide_studio_overrides = False @@ -84,7 +118,9 @@ class SettingsCategoryWidget(QtWidgets.QWidget): scroll_widget.setWidgetResizable(True) scroll_widget.setWidget(content_widget) - footer_widget = QtWidgets.QWidget() + configurations_widget = QtWidgets.QWidget(self) + + footer_widget = QtWidgets.QWidget(configurations_widget) footer_layout = QtWidgets.QHBoxLayout(footer_widget) if self.user_role == "developer": @@ -95,7 +131,6 @@ class SettingsCategoryWidget(QtWidgets.QWidget): footer_layout.addWidget(spacer_widget, 1) footer_layout.addWidget(save_btn, 0) - configurations_widget = QtWidgets.QWidget() configurations_layout = QtWidgets.QVBoxLayout(configurations_widget) configurations_layout.setContentsMargins(0, 0, 0, 0) configurations_layout.setSpacing(0) @@ -186,12 +221,15 @@ class SettingsCategoryWidget(QtWidgets.QWidget): input_field.hierarchical_style_update() def reset(self): + self.set_state(CategoryState.Working) + reset_default_settings() self.keys.clear() self.input_fields.clear() while self.content_layout.count() != 0: widget = self.content_layout.itemAt(0).widget() + widget.setVisible(False) self.content_layout.removeWidget(widget) widget.deleteLater() @@ -203,8 +241,11 @@ class SettingsCategoryWidget(QtWidgets.QWidget): self.add_children_gui(self.schema) self._update_values() + self.hierarchical_style_update() + self.set_state(CategoryState.Idle) + def items_are_valid(self): has_invalid = False for item in self.input_fields: @@ -232,13 +273,21 @@ class SettingsCategoryWidget(QtWidgets.QWidget): first_invalid_item.setFocus(True) return False + def on_saved(self, saved_tab_widget): + """Callback on any tab widget save.""" + return + def _save(self): - if not self.items_are_valid(): - return + self.set_state(CategoryState.Working) - self.save() + if self.items_are_valid(): + self.save() - self._update_values() + self._update_values() + + self.set_state(CategoryState.Idle) + + self.saved.emit(self) def _on_refresh(self): self.reset() @@ -433,7 +482,7 @@ class ProjectListWidget(QtWidgets.QWidget): self.project_list = project_list - self.refresh() + self.dbcon = None def on_item_clicked(self, new_index): new_project_name = new_index.data(QtCore.Qt.DisplayRole) @@ -501,10 +550,32 @@ class ProjectListWidget(QtWidgets.QWidget): model = self.project_list.model() model.clear() + items = [self.default] - io.install() - for project_doc in tuple(io.projects()): - items.append(project_doc["name"]) + + system_settings = get_system_settings() + mongo_url = system_settings["modules"]["avalon"]["AVALON_MONGO"] + if not mongo_url: + mongo_url = os.environ["PYPE_MONGO"] + + # Force uninstall of whole avalon connection if url does not match + # to current environment and set it as environment + if mongo_url != os.environ["AVALON_MONGO"]: + AvalonMongoConnection.uninstall(self.dbcon, force=True) + os.environ["AVALON_MONGO"] = mongo_url + self.dbcon = None + + if not self.dbcon: + try: + self.dbcon = AvalonMongoDB() + self.dbcon.install() + except Exception: + self.dbcon = None + self.current_project = None + + if self.dbcon: + for project_doc in tuple(self.dbcon.projects()): + items.append(project_doc["name"]) for item in items: model.appendRow(QtGui.QStandardItem(item)) @@ -527,6 +598,7 @@ class ProjectWidget(SettingsCategoryWidget): def ui_tweaks(self): project_list_widget = ProjectListWidget(self) + project_list_widget.refresh() self.main_layout.insertWidget(0, project_list_widget, 0) @@ -541,7 +613,26 @@ class ProjectWidget(SettingsCategoryWidget): # Projects does not have any specific validations return True + def on_saved(self, saved_tab_widget): + """Callback on any tab widget save. + + Check if AVALON_MONGO is still same. + """ + if self is saved_tab_widget: + return + + system_settings = get_system_settings() + mongo_url = system_settings["modules"]["avalon"]["AVALON_MONGO"] + if not mongo_url: + mongo_url = os.environ["PYPE_MONGO"] + + # If mongo url is not the same as was then refresh projects + if mongo_url != os.environ["AVALON_MONGO"]: + self.project_list_widget.refresh() + def _on_project_change(self): + self.set_state(CategoryState.Working) + project_name = self.project_list_widget.project_name() if project_name is None: _project_overrides = lib.NOT_SET @@ -566,6 +657,8 @@ class ProjectWidget(SettingsCategoryWidget): item.apply_overrides(overrides) self.ignore_value_changes = False + self.set_state(CategoryState.Idle) + def save(self): data = {} studio_overrides = bool(self.project_name is None) diff --git a/pype/tools/settings/settings/widgets/item_types.py b/pype/tools/settings/settings/widgets/item_types.py index ca970ab138..2e40a627d9 100644 --- a/pype/tools/settings/settings/widgets/item_types.py +++ b/pype/tools/settings/settings/widgets/item_types.py @@ -430,7 +430,7 @@ class SettingObject: return self.mouseReleaseEvent(event) return - menu = QtWidgets.QMenu() + menu = QtWidgets.QMenu(self) actions_mapping = {} if self.child_modified: diff --git a/pype/tools/settings/settings/widgets/widgets.py b/pype/tools/settings/settings/widgets/widgets.py index 092591c165..e9fa6d0326 100644 --- a/pype/tools/settings/settings/widgets/widgets.py +++ b/pype/tools/settings/settings/widgets/widgets.py @@ -2,6 +2,48 @@ from Qt import QtWidgets, QtCore, QtGui from avalon.vendor import qtawesome +class ShadowWidget(QtWidgets.QWidget): + def __init__(self, message, parent): + super(ShadowWidget, self).__init__(parent) + self.setObjectName("ShadowWidget") + + self.parent_widget = parent + self.message = message + + def wrapper(func): + def wrapped(*args, **kwarg): + result = func(*args, **kwarg) + self._update_geometry() + return result + return wrapped + + parent.resizeEvent = wrapper(parent.resizeEvent) + parent.moveEvent = wrapper(parent.moveEvent) + parent.showEvent = wrapper(parent.showEvent) + + def set_message(self, message): + self.message = message + if self.isVisible(): + self.repaint() + + def _update_geometry(self): + self.setGeometry(self.parent_widget.rect()) + + def paintEvent(self, event): + painter = QtGui.QPainter(self) + painter.setRenderHint(QtGui.QPainter.Antialiasing) + painter.fillRect( + event.rect(), QtGui.QBrush(QtGui.QColor(0, 0, 0, 127)) + ) + if self.message: + painter.drawText( + event.rect(), + QtCore.Qt.AlignCenter | QtCore.Qt.AlignCenter, + self.message + ) + painter.end() + + class IconButton(QtWidgets.QPushButton): def __init__(self, icon_name, color, hover_color, *args, **kwargs): super(IconButton, self).__init__(*args, **kwargs) diff --git a/pype/tools/settings/settings/widgets/window.py b/pype/tools/settings/settings/widgets/window.py index 2dd5111d74..3afa47e3db 100644 --- a/pype/tools/settings/settings/widgets/window.py +++ b/pype/tools/settings/settings/widgets/window.py @@ -1,5 +1,6 @@ from Qt import QtWidgets, QtGui -from .base import SystemWidget, ProjectWidget +from .base import CategoryState, SystemWidget, ProjectWidget +from .widgets import ShadowWidget from .. import style @@ -22,6 +23,12 @@ class MainWidget(QtWidgets.QWidget): studio_widget = SystemWidget(user_role, header_tab_widget) project_widget = ProjectWidget(user_role, header_tab_widget) + + tab_widgets = [ + studio_widget, + project_widget + ] + header_tab_widget.addTab(studio_widget, "System") header_tab_widget.addTab(project_widget, "Project") @@ -31,3 +38,39 @@ class MainWidget(QtWidgets.QWidget): layout.addWidget(header_tab_widget) self.setLayout(layout) + + self._shadow_widget = ShadowWidget("Working...", self) + + for tab_widget in tab_widgets: + tab_widget.saved.connect(self._on_tab_save) + tab_widget.state_changed.connect(self._on_state_change) + + self.tab_widgets = tab_widgets + + def _on_tab_save(self, source_widget): + for tab_widget in self.tab_widgets: + tab_widget.on_saved(source_widget) + + def _on_state_change(self): + any_working = False + for widget in self.tab_widgets: + if widget.state is CategoryState.Working: + any_working = True + break + + if ( + (any_working and self._shadow_widget.isVisible()) + or (not any_working and not self._shadow_widget.isVisible()) + ): + return + + self._shadow_widget.setVisible(any_working) + + # Process events to apply shadow widget visibility + app = QtWidgets.QApplication.instance() + if app: + app.processEvents() + + def reset(self): + for tab_widget in self.tab_widgets: + tab_widget.reset()