Merge pull request #861 from pypeclub/feature/settings_shadow_widget

Settings shadow widget when working
This commit is contained in:
Milan Kolar 2021-01-08 12:00:06 +01:00 committed by GitHub
commit 4a952d0225
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 204 additions and 14 deletions

View file

@ -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()

View file

@ -20,6 +20,7 @@ def main(user_role=None):
widget = MainWidget(user_role)
widget.show()
widget.reset()
sys.exit(app.exec_())

View file

@ -193,6 +193,9 @@ QPushButton[btn-type="expand-toggle"] {
background-color: #21252B;
}
#ShadowWidget {
font-size: 36pt;
}
QTabWidget::pane {
border-top-style: none;
}

View file

@ -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)

View file

@ -430,7 +430,7 @@ class SettingObject:
return self.mouseReleaseEvent(event)
return
menu = QtWidgets.QMenu()
menu = QtWidgets.QMenu(self)
actions_mapping = {}
if self.child_modified:

View file

@ -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)

View file

@ -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()