mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-25 05:14:40 +01:00
Merge pull request #2363 from pypeclub/enhancement/OP-2075_version-handling
Version handling
This commit is contained in:
commit
1964e30246
23 changed files with 1322 additions and 185 deletions
|
|
@ -6,9 +6,18 @@ import sys
|
|||
|
||||
os.chdir(os.path.dirname(__file__)) # for override sys.path in Deadline
|
||||
|
||||
from .bootstrap_repos import BootstrapRepos
|
||||
from .bootstrap_repos import (
|
||||
BootstrapRepos,
|
||||
OpenPypeVersion
|
||||
)
|
||||
from .version import __version__ as version
|
||||
|
||||
# Store OpenPypeVersion to 'sys.modules'
|
||||
# - this makes it available in OpenPype processes without modifying
|
||||
# 'sys.path' or 'PYTHONPATH'
|
||||
if "OpenPypeVersion" not in sys.modules:
|
||||
sys.modules["OpenPypeVersion"] = OpenPypeVersion
|
||||
|
||||
|
||||
def open_dialog():
|
||||
"""Show Igniter dialog."""
|
||||
|
|
@ -22,7 +31,9 @@ def open_dialog():
|
|||
if scale_attr is not None:
|
||||
QtWidgets.QApplication.setAttribute(scale_attr)
|
||||
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
app = QtWidgets.QApplication.instance()
|
||||
if not app:
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
|
||||
d = InstallDialog()
|
||||
d.open()
|
||||
|
|
@ -43,7 +54,9 @@ def open_update_window(openpype_version):
|
|||
if scale_attr is not None:
|
||||
QtWidgets.QApplication.setAttribute(scale_attr)
|
||||
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
app = QtWidgets.QApplication.instance()
|
||||
if not app:
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
|
||||
d = UpdateWindow(version=openpype_version)
|
||||
d.open()
|
||||
|
|
@ -53,9 +66,32 @@ def open_update_window(openpype_version):
|
|||
return version_path
|
||||
|
||||
|
||||
def show_message_dialog(title, message):
|
||||
"""Show dialog with a message and title to user."""
|
||||
if os.getenv("OPENPYPE_HEADLESS_MODE"):
|
||||
print("!!! Can't open dialog in headless mode. Exiting.")
|
||||
sys.exit(1)
|
||||
from Qt import QtWidgets, QtCore
|
||||
from .message_dialog import MessageDialog
|
||||
|
||||
scale_attr = getattr(QtCore.Qt, "AA_EnableHighDpiScaling", None)
|
||||
if scale_attr is not None:
|
||||
QtWidgets.QApplication.setAttribute(scale_attr)
|
||||
|
||||
app = QtWidgets.QApplication.instance()
|
||||
if not app:
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
|
||||
dialog = MessageDialog(title, message)
|
||||
dialog.open()
|
||||
|
||||
app.exec_()
|
||||
|
||||
|
||||
__all__ = [
|
||||
"BootstrapRepos",
|
||||
"open_dialog",
|
||||
"open_update_window",
|
||||
"show_message_dialog",
|
||||
"version"
|
||||
]
|
||||
|
|
|
|||
|
|
@ -22,7 +22,10 @@ from .user_settings import (
|
|||
OpenPypeSecureRegistry,
|
||||
OpenPypeSettingsRegistry
|
||||
)
|
||||
from .tools import get_openpype_path_from_db
|
||||
from .tools import (
|
||||
get_openpype_path_from_db,
|
||||
get_expected_studio_version_str
|
||||
)
|
||||
|
||||
|
||||
LOG_INFO = 0
|
||||
|
|
@ -60,6 +63,7 @@ class OpenPypeVersion(semver.VersionInfo):
|
|||
staging = False
|
||||
path = None
|
||||
_VERSION_REGEX = re.compile(r"(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$") # noqa: E501
|
||||
_installed_version = None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Create OpenPype version.
|
||||
|
|
@ -232,6 +236,390 @@ class OpenPypeVersion(semver.VersionInfo):
|
|||
else:
|
||||
return hash(str(self))
|
||||
|
||||
@staticmethod
|
||||
def is_version_in_dir(
|
||||
dir_item: Path, version: OpenPypeVersion) -> Tuple[bool, str]:
|
||||
"""Test if path item is OpenPype version matching detected version.
|
||||
|
||||
If item is directory that might (based on it's name)
|
||||
contain OpenPype version, check if it really does contain
|
||||
OpenPype and that their versions matches.
|
||||
|
||||
Args:
|
||||
dir_item (Path): Directory to test.
|
||||
version (OpenPypeVersion): OpenPype version detected
|
||||
from name.
|
||||
|
||||
Returns:
|
||||
Tuple: State and reason, True if it is valid OpenPype version,
|
||||
False otherwise.
|
||||
|
||||
"""
|
||||
try:
|
||||
# add one 'openpype' level as inside dir there should
|
||||
# be many other repositories.
|
||||
version_str = OpenPypeVersion.get_version_string_from_directory(
|
||||
dir_item) # noqa: E501
|
||||
version_check = OpenPypeVersion(version=version_str)
|
||||
except ValueError:
|
||||
return False, f"cannot determine version from {dir_item}"
|
||||
|
||||
version_main = version_check.get_main_version()
|
||||
detected_main = version.get_main_version()
|
||||
if version_main != detected_main:
|
||||
return False, (f"dir version ({version}) and "
|
||||
f"its content version ({version_check}) "
|
||||
"doesn't match. Skipping.")
|
||||
return True, "Versions match"
|
||||
|
||||
@staticmethod
|
||||
def is_version_in_zip(
|
||||
zip_item: Path, version: OpenPypeVersion) -> Tuple[bool, str]:
|
||||
"""Test if zip path is OpenPype version matching detected version.
|
||||
|
||||
Open zip file, look inside and parse version from OpenPype
|
||||
inside it. If there is none, or it is different from
|
||||
version specified in file name, skip it.
|
||||
|
||||
Args:
|
||||
zip_item (Path): Zip file to test.
|
||||
version (OpenPypeVersion): Pype version detected
|
||||
from name.
|
||||
|
||||
Returns:
|
||||
Tuple: State and reason, True if it is valid OpenPype version,
|
||||
False otherwise.
|
||||
|
||||
"""
|
||||
# skip non-zip files
|
||||
if zip_item.suffix.lower() != ".zip":
|
||||
return False, "Not a zip"
|
||||
|
||||
try:
|
||||
with ZipFile(zip_item, "r") as zip_file:
|
||||
with zip_file.open(
|
||||
"openpype/version.py") as version_file:
|
||||
zip_version = {}
|
||||
exec(version_file.read(), zip_version)
|
||||
try:
|
||||
version_check = OpenPypeVersion(
|
||||
version=zip_version["__version__"])
|
||||
except ValueError as e:
|
||||
return False, str(e)
|
||||
|
||||
version_main = version_check.get_main_version() #
|
||||
# noqa: E501
|
||||
detected_main = version.get_main_version()
|
||||
# noqa: E501
|
||||
|
||||
if version_main != detected_main:
|
||||
return False, (f"zip version ({version}) "
|
||||
f"and its content version "
|
||||
f"({version_check}) "
|
||||
"doesn't match. Skipping.")
|
||||
except BadZipFile:
|
||||
return False, f"{zip_item} is not a zip file"
|
||||
except KeyError:
|
||||
return False, "Zip does not contain OpenPype"
|
||||
return True, "Versions match"
|
||||
|
||||
@staticmethod
|
||||
def get_version_string_from_directory(repo_dir: Path) -> Union[str, None]:
|
||||
"""Get version of OpenPype in given directory.
|
||||
|
||||
Note: in frozen OpenPype installed in user data dir, this must point
|
||||
one level deeper as it is:
|
||||
`openpype-version-v3.0.0/openpype/version.py`
|
||||
|
||||
Args:
|
||||
repo_dir (Path): Path to OpenPype repo.
|
||||
|
||||
Returns:
|
||||
str: version string.
|
||||
None: if OpenPype is not found.
|
||||
|
||||
"""
|
||||
# try to find version
|
||||
version_file = Path(repo_dir) / "openpype" / "version.py"
|
||||
if not version_file.exists():
|
||||
return None
|
||||
|
||||
version = {}
|
||||
with version_file.open("r") as fp:
|
||||
exec(fp.read(), version)
|
||||
|
||||
return version['__version__']
|
||||
|
||||
@classmethod
|
||||
def get_openpype_path(cls):
|
||||
"""Path to openpype zip directory.
|
||||
|
||||
Path can be set through environment variable 'OPENPYPE_PATH' which
|
||||
is set during start of OpenPype if is not available.
|
||||
"""
|
||||
return os.getenv("OPENPYPE_PATH")
|
||||
|
||||
@classmethod
|
||||
def openpype_path_is_set(cls):
|
||||
"""Path to OpenPype zip directory is set."""
|
||||
if cls.get_openpype_path():
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def openpype_path_is_accessible(cls):
|
||||
"""Path to OpenPype zip directory is accessible.
|
||||
|
||||
Exists for this machine.
|
||||
"""
|
||||
# First check if is set
|
||||
if not cls.openpype_path_is_set():
|
||||
return False
|
||||
|
||||
# Validate existence
|
||||
if Path(cls.get_openpype_path()).exists():
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def get_local_versions(
|
||||
cls, production: bool = None, staging: bool = None
|
||||
) -> List:
|
||||
"""Get all versions available on this machine.
|
||||
|
||||
Arguments give ability to specify if filtering is needed. If both
|
||||
arguments are set to None all found versions are returned.
|
||||
|
||||
Args:
|
||||
production (bool): Return production versions.
|
||||
staging (bool): Return staging versions.
|
||||
"""
|
||||
# Return all local versions if arguments are set to None
|
||||
if production is None and staging is None:
|
||||
production = True
|
||||
staging = True
|
||||
|
||||
elif production is None and not staging:
|
||||
production = True
|
||||
|
||||
elif staging is None and not production:
|
||||
staging = True
|
||||
|
||||
# Just return empty output if both are disabled
|
||||
if not production and not staging:
|
||||
return []
|
||||
|
||||
dir_to_search = Path(user_data_dir("openpype", "pypeclub"))
|
||||
versions = OpenPypeVersion.get_versions_from_directory(
|
||||
dir_to_search
|
||||
)
|
||||
filtered_versions = []
|
||||
for version in versions:
|
||||
if version.is_staging():
|
||||
if staging:
|
||||
filtered_versions.append(version)
|
||||
elif production:
|
||||
filtered_versions.append(version)
|
||||
return list(sorted(set(filtered_versions)))
|
||||
|
||||
@classmethod
|
||||
def get_remote_versions(
|
||||
cls, production: bool = None, staging: bool = None
|
||||
) -> List:
|
||||
"""Get all versions available in OpenPype Path.
|
||||
|
||||
Arguments give ability to specify if filtering is needed. If both
|
||||
arguments are set to None all found versions are returned.
|
||||
|
||||
Args:
|
||||
production (bool): Return production versions.
|
||||
staging (bool): Return staging versions.
|
||||
"""
|
||||
# Return all local versions if arguments are set to None
|
||||
if production is None and staging is None:
|
||||
production = True
|
||||
staging = True
|
||||
|
||||
elif production is None and not staging:
|
||||
production = True
|
||||
|
||||
elif staging is None and not production:
|
||||
staging = True
|
||||
|
||||
# Just return empty output if both are disabled
|
||||
if not production and not staging:
|
||||
return []
|
||||
|
||||
dir_to_search = None
|
||||
if cls.openpype_path_is_accessible():
|
||||
dir_to_search = Path(cls.get_openpype_path())
|
||||
else:
|
||||
registry = OpenPypeSettingsRegistry()
|
||||
try:
|
||||
registry_dir = Path(str(registry.get_item("openPypePath")))
|
||||
if registry_dir.exists():
|
||||
dir_to_search = registry_dir
|
||||
|
||||
except ValueError:
|
||||
# nothing found in registry, we'll use data dir
|
||||
pass
|
||||
|
||||
if not dir_to_search:
|
||||
return []
|
||||
|
||||
versions = cls.get_versions_from_directory(dir_to_search)
|
||||
filtered_versions = []
|
||||
for version in versions:
|
||||
if version.is_staging():
|
||||
if staging:
|
||||
filtered_versions.append(version)
|
||||
elif production:
|
||||
filtered_versions.append(version)
|
||||
return list(sorted(set(filtered_versions)))
|
||||
|
||||
@staticmethod
|
||||
def get_versions_from_directory(openpype_dir: Path) -> List:
|
||||
"""Get all detected OpenPype versions in directory.
|
||||
|
||||
Args:
|
||||
openpype_dir (Path): Directory to scan.
|
||||
|
||||
Returns:
|
||||
list of OpenPypeVersion
|
||||
|
||||
Throws:
|
||||
ValueError: if invalid path is specified.
|
||||
|
||||
"""
|
||||
if not openpype_dir.exists() and not openpype_dir.is_dir():
|
||||
raise ValueError("specified directory is invalid")
|
||||
|
||||
_openpype_versions = []
|
||||
# iterate over directory in first level and find all that might
|
||||
# contain OpenPype.
|
||||
for item in openpype_dir.iterdir():
|
||||
|
||||
# if file, strip extension, in case of dir not.
|
||||
name = item.name if item.is_dir() else item.stem
|
||||
result = OpenPypeVersion.version_in_str(name)
|
||||
|
||||
if result:
|
||||
detected_version: OpenPypeVersion
|
||||
detected_version = result
|
||||
|
||||
if item.is_dir() and not OpenPypeVersion.is_version_in_dir(
|
||||
item, detected_version
|
||||
)[0]:
|
||||
continue
|
||||
|
||||
if item.is_file() and not OpenPypeVersion.is_version_in_zip(
|
||||
item, detected_version
|
||||
)[0]:
|
||||
continue
|
||||
|
||||
detected_version.path = item
|
||||
_openpype_versions.append(detected_version)
|
||||
|
||||
return sorted(_openpype_versions)
|
||||
|
||||
@staticmethod
|
||||
def get_installed_version_str() -> str:
|
||||
"""Get version of local OpenPype."""
|
||||
|
||||
version = {}
|
||||
path = Path(os.environ["OPENPYPE_ROOT"]) / "openpype" / "version.py"
|
||||
with open(path, "r") as fp:
|
||||
exec(fp.read(), version)
|
||||
return version["__version__"]
|
||||
|
||||
@classmethod
|
||||
def get_installed_version(cls):
|
||||
"""Get version of OpenPype inside build."""
|
||||
if cls._installed_version is None:
|
||||
installed_version_str = cls.get_installed_version_str()
|
||||
if installed_version_str:
|
||||
cls._installed_version = OpenPypeVersion(
|
||||
version=installed_version_str,
|
||||
path=Path(os.environ["OPENPYPE_ROOT"])
|
||||
)
|
||||
return cls._installed_version
|
||||
|
||||
@staticmethod
|
||||
def get_latest_version(
|
||||
staging: bool = False,
|
||||
local: bool = None,
|
||||
remote: bool = None
|
||||
) -> OpenPypeVersion:
|
||||
"""Get latest available version.
|
||||
|
||||
The version does not contain information about path and source.
|
||||
|
||||
This is utility version to get latest version from all found. Build
|
||||
version is not listed if staging is enabled.
|
||||
|
||||
Arguments 'local' and 'remote' define if local and remote repository
|
||||
versions are used. All versions are used if both are not set (or set
|
||||
to 'None'). If only one of them is set to 'True' the other is disabled.
|
||||
It is possible to set both to 'True' (same as both set to None) and to
|
||||
'False' in that case only build version can be used.
|
||||
|
||||
Args:
|
||||
staging (bool, optional): List staging versions if True.
|
||||
local (bool, optional): List local versions if True.
|
||||
remote (bool, optional): List remote versions if True.
|
||||
"""
|
||||
if local is None and remote is None:
|
||||
local = True
|
||||
remote = True
|
||||
|
||||
elif local is None and not remote:
|
||||
local = True
|
||||
|
||||
elif remote is None and not local:
|
||||
remote = True
|
||||
|
||||
installed_version = OpenPypeVersion.get_installed_version()
|
||||
local_versions = []
|
||||
remote_versions = []
|
||||
if local:
|
||||
local_versions = OpenPypeVersion.get_local_versions(
|
||||
staging=staging
|
||||
)
|
||||
if remote:
|
||||
remote_versions = OpenPypeVersion.get_remote_versions(
|
||||
staging=staging
|
||||
)
|
||||
all_versions = local_versions + remote_versions
|
||||
if not staging:
|
||||
all_versions.append(installed_version)
|
||||
|
||||
if not all_versions:
|
||||
return None
|
||||
|
||||
all_versions.sort()
|
||||
return all_versions[-1]
|
||||
|
||||
@classmethod
|
||||
def get_expected_studio_version(cls, staging=False, global_settings=None):
|
||||
"""Expected OpenPype version that should be used at the moment.
|
||||
|
||||
If version is not defined in settings the latest found version is
|
||||
used.
|
||||
|
||||
Using precached global settings is needed for usage inside OpenPype.
|
||||
|
||||
Args:
|
||||
staging (bool): Staging version or production version.
|
||||
global_settings (dict): Optional precached global settings.
|
||||
|
||||
Returns:
|
||||
OpenPypeVersion: Version that should be used.
|
||||
"""
|
||||
result = get_expected_studio_version_str(staging, global_settings)
|
||||
if not result:
|
||||
return None
|
||||
return OpenPypeVersion(version=result)
|
||||
|
||||
|
||||
class BootstrapRepos:
|
||||
"""Class for bootstrapping local OpenPype installation.
|
||||
|
|
@ -301,16 +689,6 @@ class BootstrapRepos:
|
|||
return v.path
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_local_live_version() -> str:
|
||||
"""Get version of local OpenPype."""
|
||||
|
||||
version = {}
|
||||
path = Path(os.environ["OPENPYPE_ROOT"]) / "openpype" / "version.py"
|
||||
with open(path, "r") as fp:
|
||||
exec(fp.read(), version)
|
||||
return version["__version__"]
|
||||
|
||||
@staticmethod
|
||||
def get_version(repo_dir: Path) -> Union[str, None]:
|
||||
"""Get version of OpenPype in given directory.
|
||||
|
|
@ -358,7 +736,7 @@ class BootstrapRepos:
|
|||
# version and use it as a source. Otherwise repo_dir is user
|
||||
# entered location.
|
||||
if not repo_dir:
|
||||
version = self.get_local_live_version()
|
||||
version = OpenPypeVersion.get_installed_version_str()
|
||||
repo_dir = self.live_repo_dir
|
||||
else:
|
||||
version = self.get_version(repo_dir)
|
||||
|
|
@ -734,6 +1112,65 @@ class BootstrapRepos:
|
|||
|
||||
os.environ["PYTHONPATH"] = os.pathsep.join(paths)
|
||||
|
||||
@staticmethod
|
||||
def find_openpype_version(version, staging):
|
||||
if isinstance(version, str):
|
||||
version = OpenPypeVersion(version=version)
|
||||
|
||||
installed_version = OpenPypeVersion.get_installed_version()
|
||||
if installed_version == version:
|
||||
return installed_version
|
||||
|
||||
local_versions = OpenPypeVersion.get_local_versions(
|
||||
staging=staging, production=not staging
|
||||
)
|
||||
zip_version = None
|
||||
for local_version in local_versions:
|
||||
if local_version == version:
|
||||
if local_version.path.suffix.lower() == ".zip":
|
||||
zip_version = local_version
|
||||
else:
|
||||
return local_version
|
||||
|
||||
if zip_version is not None:
|
||||
return zip_version
|
||||
|
||||
remote_versions = OpenPypeVersion.get_remote_versions(
|
||||
staging=staging, production=not staging
|
||||
)
|
||||
for remote_version in remote_versions:
|
||||
if remote_version == version:
|
||||
return remote_version
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def find_latest_openpype_version(staging):
|
||||
installed_version = OpenPypeVersion.get_installed_version()
|
||||
local_versions = OpenPypeVersion.get_local_versions(
|
||||
staging=staging
|
||||
)
|
||||
remote_versions = OpenPypeVersion.get_remote_versions(
|
||||
staging=staging
|
||||
)
|
||||
all_versions = local_versions + remote_versions
|
||||
if not staging:
|
||||
all_versions.append(installed_version)
|
||||
|
||||
if not all_versions:
|
||||
return None
|
||||
|
||||
all_versions.sort()
|
||||
latest_version = all_versions[-1]
|
||||
if latest_version == installed_version:
|
||||
return latest_version
|
||||
|
||||
if not latest_version.path.is_dir():
|
||||
for version in local_versions:
|
||||
if version == latest_version and version.path.is_dir():
|
||||
latest_version = version
|
||||
break
|
||||
return latest_version
|
||||
|
||||
def find_openpype(
|
||||
self,
|
||||
openpype_path: Union[Path, str] = None,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ from Qt.QtCore import QTimer # noqa
|
|||
from .install_thread import InstallThread
|
||||
from .tools import (
|
||||
validate_mongo_connection,
|
||||
get_openpype_path_from_db
|
||||
get_openpype_path_from_db,
|
||||
get_openpype_icon_path
|
||||
)
|
||||
|
||||
from .nice_progress_bar import NiceProgressBar
|
||||
|
|
@ -187,7 +188,6 @@ class InstallDialog(QtWidgets.QDialog):
|
|||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
roboto_font_path = os.path.join(current_dir, "RobotoMono-Regular.ttf")
|
||||
poppins_font_path = os.path.join(current_dir, "Poppins")
|
||||
icon_path = os.path.join(current_dir, "openpype_icon.png")
|
||||
|
||||
# Install roboto font
|
||||
QtGui.QFontDatabase.addApplicationFont(roboto_font_path)
|
||||
|
|
@ -196,6 +196,7 @@ class InstallDialog(QtWidgets.QDialog):
|
|||
QtGui.QFontDatabase.addApplicationFont(filename)
|
||||
|
||||
# Load logo
|
||||
icon_path = get_openpype_icon_path()
|
||||
pixmap_openpype_logo = QtGui.QPixmap(icon_path)
|
||||
# Set logo as icon of window
|
||||
self.setWindowIcon(QtGui.QIcon(pixmap_openpype_logo))
|
||||
|
|
|
|||
44
igniter/message_dialog.py
Normal file
44
igniter/message_dialog.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
from Qt import QtWidgets, QtGui
|
||||
|
||||
from .tools import (
|
||||
load_stylesheet,
|
||||
get_openpype_icon_path
|
||||
)
|
||||
|
||||
|
||||
class MessageDialog(QtWidgets.QDialog):
|
||||
"""Simple message dialog with title, message and OK button."""
|
||||
def __init__(self, title, message):
|
||||
super(MessageDialog, self).__init__()
|
||||
|
||||
# Set logo as icon of window
|
||||
icon_path = get_openpype_icon_path()
|
||||
pixmap_openpype_logo = QtGui.QPixmap(icon_path)
|
||||
self.setWindowIcon(QtGui.QIcon(pixmap_openpype_logo))
|
||||
|
||||
# Set title
|
||||
self.setWindowTitle(title)
|
||||
|
||||
# Set message
|
||||
label_widget = QtWidgets.QLabel(message, self)
|
||||
|
||||
ok_btn = QtWidgets.QPushButton("OK", self)
|
||||
btns_layout = QtWidgets.QHBoxLayout()
|
||||
btns_layout.addStretch(1)
|
||||
btns_layout.addWidget(ok_btn, 0)
|
||||
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
layout.addWidget(label_widget, 1)
|
||||
layout.addLayout(btns_layout, 0)
|
||||
|
||||
ok_btn.clicked.connect(self._on_ok_clicked)
|
||||
|
||||
self._label_widget = label_widget
|
||||
self._ok_btn = ok_btn
|
||||
|
||||
def _on_ok_clicked(self):
|
||||
self.close()
|
||||
|
||||
def showEvent(self, event):
|
||||
super(MessageDialog, self).showEvent(event)
|
||||
self.setStyleSheet(load_stylesheet())
|
||||
|
|
@ -16,6 +16,11 @@ from pymongo.errors import (
|
|||
)
|
||||
|
||||
|
||||
class OpenPypeVersionNotFound(Exception):
|
||||
"""OpenPype version was not found in remote and local repository."""
|
||||
pass
|
||||
|
||||
|
||||
def should_add_certificate_path_to_mongo_url(mongo_url):
|
||||
"""Check if should add ca certificate to mongo url.
|
||||
|
||||
|
|
@ -182,6 +187,28 @@ def get_openpype_path_from_db(url: str) -> Union[str, None]:
|
|||
return None
|
||||
|
||||
|
||||
def get_expected_studio_version_str(
|
||||
staging=False, global_settings=None
|
||||
) -> str:
|
||||
"""Version that should be currently used in studio.
|
||||
|
||||
Args:
|
||||
staging (bool): Get current version for staging.
|
||||
global_settings (dict): Optional precached global settings.
|
||||
|
||||
Returns:
|
||||
str: OpenPype version which should be used. Empty string means latest.
|
||||
"""
|
||||
mongo_url = os.environ.get("OPENPYPE_MONGO")
|
||||
if global_settings is None:
|
||||
global_settings = get_openpype_global_settings(mongo_url)
|
||||
if staging:
|
||||
key = "staging_version"
|
||||
else:
|
||||
key = "production_version"
|
||||
return global_settings.get(key) or ""
|
||||
|
||||
|
||||
def load_stylesheet() -> str:
|
||||
"""Load css style sheet.
|
||||
|
||||
|
|
@ -192,3 +219,11 @@ def load_stylesheet() -> str:
|
|||
stylesheet_path = Path(__file__).parent.resolve() / "stylesheet.css"
|
||||
|
||||
return stylesheet_path.read_text()
|
||||
|
||||
|
||||
def get_openpype_icon_path() -> str:
|
||||
"""Path to OpenPype icon png file."""
|
||||
return os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"openpype_icon.png"
|
||||
)
|
||||
|
|
|
|||
85
openpype/lib/openpype_version.py
Normal file
85
openpype/lib/openpype_version.py
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
"""Lib access to OpenPypeVersion from igniter.
|
||||
|
||||
Access to logic from igniter is available only for OpenPype processes.
|
||||
Is meant to be able check OpenPype versions for studio. The logic is dependent
|
||||
on igniter's inner logic of versions.
|
||||
|
||||
Keep in mind that all functions except 'get_installed_version' does not return
|
||||
OpenPype version located in build but versions available in remote versions
|
||||
repository or locally available.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
def get_OpenPypeVersion():
|
||||
"""Access to OpenPypeVersion class stored in sys modules."""
|
||||
return sys.modules.get("OpenPypeVersion")
|
||||
|
||||
|
||||
def op_version_control_available():
|
||||
"""Check if current process has access to OpenPypeVersion."""
|
||||
if get_OpenPypeVersion() is None:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def get_installed_version():
|
||||
"""Get OpenPype version inside build.
|
||||
|
||||
This version is not returned by any other functions here.
|
||||
"""
|
||||
if op_version_control_available():
|
||||
return get_OpenPypeVersion().get_installed_version()
|
||||
return None
|
||||
|
||||
|
||||
def get_available_versions(*args, **kwargs):
|
||||
"""Get list of available versions."""
|
||||
if op_version_control_available():
|
||||
return get_OpenPypeVersion().get_available_versions(
|
||||
*args, **kwargs
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
def openpype_path_is_set():
|
||||
"""OpenPype repository path is set in settings."""
|
||||
if op_version_control_available():
|
||||
return get_OpenPypeVersion().openpype_path_is_set()
|
||||
return None
|
||||
|
||||
|
||||
def openpype_path_is_accessible():
|
||||
"""OpenPype version repository path can be accessed."""
|
||||
if op_version_control_available():
|
||||
return get_OpenPypeVersion().openpype_path_is_accessible()
|
||||
return None
|
||||
|
||||
|
||||
def get_local_versions(*args, **kwargs):
|
||||
"""OpenPype versions available on this workstation."""
|
||||
if op_version_control_available():
|
||||
return get_OpenPypeVersion().get_local_versions(*args, **kwargs)
|
||||
return None
|
||||
|
||||
|
||||
def get_remote_versions(*args, **kwargs):
|
||||
"""OpenPype versions in repository path."""
|
||||
if op_version_control_available():
|
||||
return get_OpenPypeVersion().get_remote_versions(*args, **kwargs)
|
||||
return None
|
||||
|
||||
|
||||
def get_latest_version(*args, **kwargs):
|
||||
"""Get latest version from repository path."""
|
||||
if op_version_control_available():
|
||||
return get_OpenPypeVersion().get_latest_version(*args, **kwargs)
|
||||
return None
|
||||
|
||||
|
||||
def get_expected_studio_version(staging=False):
|
||||
"""Expected production or staging version in studio."""
|
||||
if op_version_control_available():
|
||||
return get_OpenPypeVersion().get_expected_studio_version(staging)
|
||||
return None
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
"studio_name": "Studio name",
|
||||
"studio_code": "stu",
|
||||
"admin_password": "",
|
||||
"production_version": "",
|
||||
"staging_version": "",
|
||||
"environment": {
|
||||
"__environment_keys__": {
|
||||
"global": []
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ from .exceptions import (
|
|||
SchemaError,
|
||||
DefaultsNotDefined,
|
||||
StudioDefaultsNotDefined,
|
||||
BaseInvalidValueType,
|
||||
BaseInvalidValue,
|
||||
InvalidValueType,
|
||||
InvalidKeySymbols,
|
||||
SchemaMissingFileInfo,
|
||||
|
|
@ -106,7 +106,7 @@ from .enum_entity import (
|
|||
ToolsEnumEntity,
|
||||
TaskTypeEnumEntity,
|
||||
DeadlineUrlEnumEntity,
|
||||
AnatomyTemplatesEnumEntity
|
||||
AnatomyTemplatesEnumEntity,
|
||||
)
|
||||
|
||||
from .list_entity import ListEntity
|
||||
|
|
@ -122,12 +122,15 @@ from .dict_conditional import (
|
|||
)
|
||||
|
||||
from .anatomy_entities import AnatomyEntity
|
||||
|
||||
from .op_version_entity import (
|
||||
ProductionVersionsInputEntity,
|
||||
StagingVersionsInputEntity
|
||||
)
|
||||
|
||||
__all__ = (
|
||||
"DefaultsNotDefined",
|
||||
"StudioDefaultsNotDefined",
|
||||
"BaseInvalidValueType",
|
||||
"BaseInvalidValue",
|
||||
"InvalidValueType",
|
||||
"InvalidKeySymbols",
|
||||
"SchemaMissingFileInfo",
|
||||
|
|
@ -181,5 +184,8 @@ __all__ = (
|
|||
"DictConditionalEntity",
|
||||
"SyncServerProviders",
|
||||
|
||||
"AnatomyEntity"
|
||||
"AnatomyEntity",
|
||||
|
||||
"ProductionVersionsInputEntity",
|
||||
"StagingVersionsInputEntity"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from .lib import (
|
|||
)
|
||||
|
||||
from .exceptions import (
|
||||
BaseInvalidValueType,
|
||||
BaseInvalidValue,
|
||||
InvalidValueType,
|
||||
SchemeGroupHierarchyBug,
|
||||
EntitySchemaError
|
||||
|
|
@ -437,7 +437,7 @@ class BaseItemEntity(BaseEntity):
|
|||
|
||||
try:
|
||||
new_value = self.convert_to_valid_type(value)
|
||||
except BaseInvalidValueType:
|
||||
except BaseInvalidValue:
|
||||
new_value = NOT_SET
|
||||
|
||||
if new_value is not NOT_SET:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from .lib import STRING_TYPE
|
||||
from .input_entities import InputEntity
|
||||
from .exceptions import (
|
||||
BaseInvalidValueType,
|
||||
BaseInvalidValue,
|
||||
InvalidValueType
|
||||
)
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ class ColorEntity(InputEntity):
|
|||
reason = "Color entity expect 4 items in list got {}".format(
|
||||
len(value)
|
||||
)
|
||||
raise BaseInvalidValueType(reason, self.path)
|
||||
raise BaseInvalidValue(reason, self.path)
|
||||
|
||||
new_value = []
|
||||
for item in value:
|
||||
|
|
@ -60,7 +60,7 @@ class ColorEntity(InputEntity):
|
|||
reason = (
|
||||
"Color entity expect 4 integers in range 0-255 got {}"
|
||||
).format(value)
|
||||
raise BaseInvalidValueType(reason, self.path)
|
||||
raise BaseInvalidValue(reason, self.path)
|
||||
new_value.append(item)
|
||||
|
||||
# Make sure
|
||||
|
|
|
|||
|
|
@ -15,14 +15,14 @@ class StudioDefaultsNotDefined(Exception):
|
|||
super(StudioDefaultsNotDefined, self).__init__(msg)
|
||||
|
||||
|
||||
class BaseInvalidValueType(Exception):
|
||||
class BaseInvalidValue(Exception):
|
||||
def __init__(self, reason, path):
|
||||
msg = "Path \"{}\". {}".format(path, reason)
|
||||
self.msg = msg
|
||||
super(BaseInvalidValueType, self).__init__(msg)
|
||||
super(BaseInvalidValue, self).__init__(msg)
|
||||
|
||||
|
||||
class InvalidValueType(BaseInvalidValueType):
|
||||
class InvalidValueType(BaseInvalidValue):
|
||||
def __init__(self, valid_types, invalid_type, path):
|
||||
joined_types = ", ".join(
|
||||
[str(valid_type) for valid_type in valid_types]
|
||||
|
|
|
|||
|
|
@ -441,6 +441,16 @@ class TextEntity(InputEntity):
|
|||
# GUI attributes
|
||||
self.multiline = self.schema_data.get("multiline", False)
|
||||
self.placeholder_text = self.schema_data.get("placeholder")
|
||||
self.value_hints = self.schema_data.get("value_hints") or []
|
||||
|
||||
def schema_validations(self):
|
||||
if self.multiline and self.value_hints:
|
||||
reason = (
|
||||
"TextEntity entity can't use value hints"
|
||||
" for multiline input (yet)."
|
||||
)
|
||||
raise EntitySchemaError(self, reason)
|
||||
super(TextEntity, self).schema_validations()
|
||||
|
||||
def _convert_to_valid_type(self, value):
|
||||
# Allow numbers converted to string
|
||||
|
|
|
|||
85
openpype/settings/entities/op_version_entity.py
Normal file
85
openpype/settings/entities/op_version_entity.py
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
from openpype.lib.openpype_version import (
|
||||
get_remote_versions,
|
||||
get_OpenPypeVersion,
|
||||
get_installed_version
|
||||
)
|
||||
from .input_entities import TextEntity
|
||||
from .lib import (
|
||||
OverrideState,
|
||||
NOT_SET
|
||||
)
|
||||
from .exceptions import BaseInvalidValue
|
||||
|
||||
|
||||
class OpenPypeVersionInput(TextEntity):
|
||||
"""Entity to store OpenPype version to use.
|
||||
|
||||
Settings created on another machine may affect available versions
|
||||
on current user's machine. Text input element is provided to explicitly
|
||||
set version not yet showing up the user's machine.
|
||||
|
||||
It is possible to enter empty string. In that case is used any latest
|
||||
version. Any other string must match regex of OpenPype version semantic.
|
||||
"""
|
||||
def _item_initialization(self):
|
||||
super(OpenPypeVersionInput, self)._item_initialization()
|
||||
self.multiline = False
|
||||
self.placeholder_text = "Latest"
|
||||
self.value_hints = []
|
||||
|
||||
def _get_openpype_versions(self):
|
||||
"""This is abstract method returning version hints for UI purposes."""
|
||||
raise NotImplementedError((
|
||||
"{} does not have implemented '_get_openpype_versions'"
|
||||
).format(self.__class__.__name__))
|
||||
|
||||
def set_override_state(self, state, *args, **kwargs):
|
||||
"""Update value hints for UI purposes."""
|
||||
value_hints = []
|
||||
if state is OverrideState.STUDIO:
|
||||
versions = self._get_openpype_versions()
|
||||
for version in versions:
|
||||
version_str = str(version)
|
||||
if version_str not in value_hints:
|
||||
value_hints.append(version_str)
|
||||
|
||||
self.value_hints = value_hints
|
||||
|
||||
super(OpenPypeVersionInput, self).set_override_state(
|
||||
state, *args, **kwargs
|
||||
)
|
||||
|
||||
def convert_to_valid_type(self, value):
|
||||
"""Add validation of version regex."""
|
||||
if value and value is not NOT_SET:
|
||||
OpenPypeVersion = get_OpenPypeVersion()
|
||||
if OpenPypeVersion is not None:
|
||||
try:
|
||||
OpenPypeVersion(version=value)
|
||||
except Exception:
|
||||
raise BaseInvalidValue(
|
||||
"Value \"{}\"is not valid version format.".format(
|
||||
value
|
||||
),
|
||||
self.path
|
||||
)
|
||||
return super(OpenPypeVersionInput, self).convert_to_valid_type(value)
|
||||
|
||||
|
||||
class ProductionVersionsInputEntity(OpenPypeVersionInput):
|
||||
"""Entity meant only for global settings to define production version."""
|
||||
schema_types = ["production-versions-text"]
|
||||
|
||||
def _get_openpype_versions(self):
|
||||
versions = get_remote_versions(staging=False, production=True)
|
||||
versions.append(get_installed_version())
|
||||
return sorted(versions)
|
||||
|
||||
|
||||
class StagingVersionsInputEntity(OpenPypeVersionInput):
|
||||
"""Entity meant only for global settings to define staging version."""
|
||||
schema_types = ["staging-versions-text"]
|
||||
|
||||
def _get_openpype_versions(self):
|
||||
versions = get_remote_versions(staging=True, production=False)
|
||||
return sorted(versions)
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
},
|
||||
{
|
||||
"type": "label",
|
||||
"label": "This is <b>NOT a securely stored password!</b>. It only acts as a simple barrier to stop users from accessing studio wide settings."
|
||||
"label": "This is <b>NOT a securely stored password!</b> It only acts as a simple barrier to stop users from accessing studio wide settings."
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
|
|
@ -30,6 +30,23 @@
|
|||
{
|
||||
"type": "splitter"
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
"label": "Define explicit OpenPype version that should be used. Keep empty to use latest available version."
|
||||
},
|
||||
{
|
||||
"type": "production-versions-text",
|
||||
"key": "production_version",
|
||||
"label": "Production version"
|
||||
},
|
||||
{
|
||||
"type": "staging-versions-text",
|
||||
"key": "staging_version",
|
||||
"label": "Staging version"
|
||||
},
|
||||
{
|
||||
"type": "splitter"
|
||||
},
|
||||
{
|
||||
"key": "environment",
|
||||
"label": "Environment",
|
||||
|
|
|
|||
|
|
@ -168,7 +168,13 @@ class CacheValues:
|
|||
|
||||
class MongoSettingsHandler(SettingsHandler):
|
||||
"""Settings handler that use mongo for storing and loading of settings."""
|
||||
global_general_keys = ("openpype_path", "admin_password", "disk_mapping")
|
||||
global_general_keys = (
|
||||
"openpype_path",
|
||||
"admin_password",
|
||||
"disk_mapping",
|
||||
"production_version",
|
||||
"staging_version"
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
# Get mongo connection
|
||||
|
|
|
|||
|
|
@ -111,7 +111,9 @@
|
|||
"focus-border": "#839caf",
|
||||
"image-btn": "#bfccd6",
|
||||
"image-btn-hover": "#189aea",
|
||||
"image-btn-disabled": "#bfccd6"
|
||||
"image-btn-disabled": "#bfccd6",
|
||||
"version-exists": "#458056",
|
||||
"version-not-found": "#ffc671"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -536,6 +536,27 @@ QAbstractItemView::branch:!has-children:!has-siblings:adjoins-item {
|
|||
background: transparent;
|
||||
}
|
||||
|
||||
CompleterView {
|
||||
border: 1px solid #555555;
|
||||
background: {color:bg-inputs};
|
||||
}
|
||||
|
||||
CompleterView::item:selected {
|
||||
background: {color:bg-view-hover};
|
||||
}
|
||||
|
||||
CompleterView::item:selected:hover {
|
||||
background: {color:bg-view-hover};
|
||||
}
|
||||
|
||||
CompleterView::right-arrow {
|
||||
min-width: 10px;
|
||||
}
|
||||
CompleterView::separator {
|
||||
background: {color:bg-menu-separator};
|
||||
height: 2px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
/* Progress bar */
|
||||
QProgressBar {
|
||||
|
|
@ -1158,6 +1179,14 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
|
|||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#OpenPypeVersionLabel[state="success"] {
|
||||
color: {color:settings:version-exists};
|
||||
}
|
||||
|
||||
#OpenPypeVersionLabel[state="warning"] {
|
||||
color: {color:settings:version-not-found};
|
||||
}
|
||||
|
||||
#ShadowWidget {
|
||||
font-size: 36pt;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,15 @@ class BaseWidget(QtWidgets.QWidget):
|
|||
self.label_widget = None
|
||||
self.create_ui()
|
||||
|
||||
@staticmethod
|
||||
def set_style_property(obj, property_name, property_value):
|
||||
"""Change QWidget property and polish it's style."""
|
||||
if obj.property(property_name) == property_value:
|
||||
return
|
||||
|
||||
obj.setProperty(property_name, property_value)
|
||||
obj.style().polish(obj)
|
||||
|
||||
def scroll_to(self, widget):
|
||||
self.category_widget.scroll_to(widget)
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ from openpype.settings.entities import (
|
|||
StudioDefaultsNotDefined,
|
||||
SchemaError
|
||||
)
|
||||
from openpype.settings.entities.op_version_entity import (
|
||||
OpenPypeVersionInput
|
||||
)
|
||||
|
||||
from openpype.settings import SaveWarningExc
|
||||
from .widgets import ProjectListWidget
|
||||
|
|
@ -47,6 +50,7 @@ from .item_widgets import (
|
|||
BoolWidget,
|
||||
DictImmutableKeysWidget,
|
||||
TextWidget,
|
||||
OpenPypeVersionText,
|
||||
NumberWidget,
|
||||
RawJsonWidget,
|
||||
EnumeratorWidget,
|
||||
|
|
@ -118,6 +122,9 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
|||
elif isinstance(entity, BoolEntity):
|
||||
return BoolWidget(*args)
|
||||
|
||||
elif isinstance(entity, OpenPypeVersionInput):
|
||||
return OpenPypeVersionText(*args)
|
||||
|
||||
elif isinstance(entity, TextEntity):
|
||||
return TextWidget(*args)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@ import json
|
|||
|
||||
from Qt import QtWidgets, QtCore, QtGui
|
||||
|
||||
from openpype.widgets.sliders import NiceSlider
|
||||
from openpype.tools.settings import CHILD_OFFSET
|
||||
from openpype.settings.entities.exceptions import BaseInvalidValue
|
||||
|
||||
from .widgets import (
|
||||
ExpandingWidget,
|
||||
NumberSpinBox,
|
||||
|
|
@ -22,9 +26,6 @@ from .base import (
|
|||
InputWidget
|
||||
)
|
||||
|
||||
from openpype.widgets.sliders import NiceSlider
|
||||
from openpype.tools.settings import CHILD_OFFSET
|
||||
|
||||
|
||||
class DictImmutableKeysWidget(BaseWidget):
|
||||
def create_ui(self):
|
||||
|
|
@ -378,6 +379,16 @@ class TextWidget(InputWidget):
|
|||
self.input_field.focused_in.connect(self._on_input_focus)
|
||||
self.input_field.textChanged.connect(self._on_value_change)
|
||||
|
||||
self._refresh_completer()
|
||||
|
||||
def _refresh_completer(self):
|
||||
# Multiline entity can't have completer
|
||||
# - there is not space for this UI component
|
||||
if self.entity.multiline:
|
||||
return
|
||||
|
||||
self.input_field.update_completer_values(self.entity.value_hints)
|
||||
|
||||
def _on_input_focus(self):
|
||||
self.focused_in()
|
||||
|
||||
|
|
@ -406,6 +417,86 @@ class TextWidget(InputWidget):
|
|||
self.entity.set(self.input_value())
|
||||
|
||||
|
||||
class OpenPypeVersionText(TextWidget):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._info_widget = None
|
||||
super(OpenPypeVersionText, self).__init__(*args, **kwargs)
|
||||
|
||||
def create_ui(self):
|
||||
super(OpenPypeVersionText, self).create_ui()
|
||||
info_widget = QtWidgets.QLabel(self)
|
||||
info_widget.setObjectName("OpenPypeVersionLabel")
|
||||
self.content_layout.addWidget(info_widget, 1)
|
||||
|
||||
self._info_widget = info_widget
|
||||
|
||||
def _update_info_widget(self):
|
||||
value = self.input_value()
|
||||
|
||||
message = ""
|
||||
tooltip = ""
|
||||
state = None
|
||||
if self._is_invalid:
|
||||
message = "Invalid OpenPype version format"
|
||||
|
||||
elif value == "":
|
||||
message = "Use latest available version"
|
||||
tooltip = (
|
||||
"Latest version from OpenPype zip repository will be used"
|
||||
)
|
||||
|
||||
elif value in self.entity.value_hints:
|
||||
state = "success"
|
||||
message = "Version {} will be used".format(value)
|
||||
|
||||
else:
|
||||
state = "warning"
|
||||
message = (
|
||||
"Version {} not found in listed versions".format(value)
|
||||
)
|
||||
if self.entity.value_hints:
|
||||
tooltip = "Listed versions: {}".format(", ".join(
|
||||
['"{}"'.format(hint) for hint in self.entity.value_hints]
|
||||
))
|
||||
else:
|
||||
tooltip = "No versions were listed"
|
||||
|
||||
self._info_widget.setText(message)
|
||||
self._info_widget.setToolTip(tooltip)
|
||||
self.set_style_property(self._info_widget, "state", state)
|
||||
|
||||
def set_entity_value(self):
|
||||
super(OpenPypeVersionText, self).set_entity_value()
|
||||
self._invalidate()
|
||||
self._update_info_widget()
|
||||
|
||||
def _on_value_change_timer(self):
|
||||
value = self.input_value()
|
||||
self._invalidate()
|
||||
if not self.is_invalid:
|
||||
self.entity.set(value)
|
||||
self.update_style()
|
||||
else:
|
||||
# Manually trigger hierachical style update
|
||||
self.ignore_input_changes.set_ignore(True)
|
||||
self.ignore_input_changes.set_ignore(False)
|
||||
|
||||
self._update_info_widget()
|
||||
|
||||
def _invalidate(self):
|
||||
value = self.input_value()
|
||||
try:
|
||||
self.entity.convert_to_valid_type(value)
|
||||
is_invalid = False
|
||||
except BaseInvalidValue:
|
||||
is_invalid = True
|
||||
self._is_invalid = is_invalid
|
||||
|
||||
def _on_entity_change(self):
|
||||
super(OpenPypeVersionText, self)._on_entity_change()
|
||||
self._refresh_completer()
|
||||
|
||||
|
||||
class NumberWidget(InputWidget):
|
||||
_slider_widget = None
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import os
|
||||
import copy
|
||||
from Qt import QtWidgets, QtCore, QtGui
|
||||
from avalon.vendor import qtawesome
|
||||
from avalon.mongodb import (
|
||||
|
|
@ -25,13 +26,235 @@ from .constants import (
|
|||
)
|
||||
|
||||
|
||||
class CompleterFilter(QtCore.QSortFilterProxyModel):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CompleterFilter, self).__init__(*args, **kwargs)
|
||||
|
||||
self.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
|
||||
|
||||
self._text_filter = ""
|
||||
|
||||
def set_text_filter(self, text):
|
||||
if self._text_filter == text:
|
||||
return
|
||||
self._text_filter = text
|
||||
self.invalidateFilter()
|
||||
|
||||
def filterAcceptsRow(self, row, parent_index):
|
||||
if not self._text_filter:
|
||||
return True
|
||||
model = self.sourceModel()
|
||||
index = model.index(row, self.filterKeyColumn(), parent_index)
|
||||
value = index.data(QtCore.Qt.DisplayRole)
|
||||
if self._text_filter in value:
|
||||
if self._text_filter == value:
|
||||
return False
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class CompleterView(QtWidgets.QListView):
|
||||
row_activated = QtCore.Signal(str)
|
||||
|
||||
def __init__(self, parent):
|
||||
super(CompleterView, self).__init__(parent)
|
||||
|
||||
self.setWindowFlags(
|
||||
QtCore.Qt.FramelessWindowHint
|
||||
| QtCore.Qt.Tool
|
||||
)
|
||||
delegate = QtWidgets.QStyledItemDelegate()
|
||||
self.setItemDelegate(delegate)
|
||||
|
||||
model = QtGui.QStandardItemModel()
|
||||
filter_model = CompleterFilter()
|
||||
filter_model.setSourceModel(model)
|
||||
self.setModel(filter_model)
|
||||
|
||||
# self.installEventFilter(parent)
|
||||
|
||||
self.clicked.connect(self._on_activated)
|
||||
|
||||
self._last_loaded_values = None
|
||||
self._model = model
|
||||
self._filter_model = filter_model
|
||||
self._delegate = delegate
|
||||
|
||||
def _on_activated(self, index):
|
||||
if index.isValid():
|
||||
value = index.data(QtCore.Qt.DisplayRole)
|
||||
self.row_activated.emit(value)
|
||||
|
||||
def set_text_filter(self, text):
|
||||
self._filter_model.set_text_filter(text)
|
||||
self._update_geo()
|
||||
|
||||
def sizeHint(self):
|
||||
result = super(CompleterView, self).sizeHint()
|
||||
if self._filter_model.rowCount() == 0:
|
||||
result.setHeight(0)
|
||||
|
||||
return result
|
||||
|
||||
def showEvent(self, event):
|
||||
super(CompleterView, self).showEvent(event)
|
||||
self._update_geo()
|
||||
|
||||
def _update_geo(self):
|
||||
size_hint = self.sizeHint()
|
||||
self.resize(size_hint.width(), size_hint.height())
|
||||
|
||||
def update_values(self, values):
|
||||
if not values:
|
||||
values = []
|
||||
|
||||
if self._last_loaded_values == values:
|
||||
return
|
||||
self._last_loaded_values = copy.deepcopy(values)
|
||||
|
||||
root_item = self._model.invisibleRootItem()
|
||||
existing_values = {}
|
||||
for row in reversed(range(root_item.rowCount())):
|
||||
child = root_item.child(row)
|
||||
value = child.data(QtCore.Qt.DisplayRole)
|
||||
if value not in values:
|
||||
root_item.removeRows(child.row())
|
||||
else:
|
||||
existing_values[value] = child
|
||||
|
||||
for row, value in enumerate(values):
|
||||
if value in existing_values:
|
||||
item = existing_values[value]
|
||||
if item.row() == row:
|
||||
continue
|
||||
else:
|
||||
item = QtGui.QStandardItem(value)
|
||||
item.setEditable(False)
|
||||
|
||||
root_item.setChild(row, item)
|
||||
|
||||
self._update_geo()
|
||||
|
||||
def _get_selected_row(self):
|
||||
indexes = self.selectionModel().selectedIndexes()
|
||||
if not indexes:
|
||||
return -1
|
||||
return indexes[0].row()
|
||||
|
||||
def _select_row(self, row):
|
||||
index = self._filter_model.index(row, 0)
|
||||
self.setCurrentIndex(index)
|
||||
|
||||
def move_up(self):
|
||||
rows = self._filter_model.rowCount()
|
||||
if rows == 0:
|
||||
return
|
||||
|
||||
selected_row = self._get_selected_row()
|
||||
if selected_row < 0:
|
||||
new_row = rows - 1
|
||||
else:
|
||||
new_row = selected_row - 1
|
||||
if new_row < 0:
|
||||
new_row = rows - 1
|
||||
|
||||
if new_row != selected_row:
|
||||
self._select_row(new_row)
|
||||
|
||||
def move_down(self):
|
||||
rows = self._filter_model.rowCount()
|
||||
if rows == 0:
|
||||
return
|
||||
|
||||
selected_row = self._get_selected_row()
|
||||
if selected_row < 0:
|
||||
new_row = 0
|
||||
else:
|
||||
new_row = selected_row + 1
|
||||
if new_row >= rows:
|
||||
new_row = 0
|
||||
|
||||
if new_row != selected_row:
|
||||
self._select_row(new_row)
|
||||
|
||||
def enter_pressed(self):
|
||||
selected_row = self._get_selected_row()
|
||||
if selected_row < 0:
|
||||
return
|
||||
index = self._filter_model.index(selected_row, 0)
|
||||
self._on_activated(index)
|
||||
|
||||
|
||||
class SettingsLineEdit(PlaceholderLineEdit):
|
||||
focused_in = QtCore.Signal()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SettingsLineEdit, self).__init__(*args, **kwargs)
|
||||
|
||||
self._completer = None
|
||||
|
||||
self.textChanged.connect(self._on_text_change)
|
||||
|
||||
def _on_text_change(self, text):
|
||||
if self._completer is not None:
|
||||
self._completer.set_text_filter(text)
|
||||
|
||||
def _update_completer(self):
|
||||
if self._completer is None or not self._completer.isVisible():
|
||||
return
|
||||
point = self.frameGeometry().bottomLeft()
|
||||
new_point = self.mapToGlobal(point)
|
||||
self._completer.move(new_point)
|
||||
|
||||
def focusInEvent(self, event):
|
||||
super(SettingsLineEdit, self).focusInEvent(event)
|
||||
self.focused_in.emit()
|
||||
|
||||
if self._completer is None:
|
||||
return
|
||||
self._completer.show()
|
||||
self._update_completer()
|
||||
|
||||
def focusOutEvent(self, event):
|
||||
super(SettingsLineEdit, self).focusOutEvent(event)
|
||||
if self._completer is not None:
|
||||
self._completer.hide()
|
||||
|
||||
def paintEvent(self, event):
|
||||
super(SettingsLineEdit, self).paintEvent(event)
|
||||
self._update_completer()
|
||||
|
||||
def update_completer_values(self, values):
|
||||
if not values and self._completer is None:
|
||||
return
|
||||
|
||||
self._create_completer()
|
||||
|
||||
self._completer.update_values(values)
|
||||
|
||||
def _create_completer(self):
|
||||
if self._completer is None:
|
||||
self._completer = CompleterView(self)
|
||||
self._completer.row_activated.connect(self._completer_activated)
|
||||
|
||||
def _completer_activated(self, text):
|
||||
self.setText(text)
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
if self._completer is None:
|
||||
super(SettingsLineEdit, self).keyPressEvent(event)
|
||||
return
|
||||
|
||||
key = event.key()
|
||||
if key == QtCore.Qt.Key_Up:
|
||||
self._completer.move_up()
|
||||
elif key == QtCore.Qt.Key_Down:
|
||||
self._completer.move_down()
|
||||
elif key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return):
|
||||
self._completer.enter_pressed()
|
||||
else:
|
||||
super(SettingsLineEdit, self).keyPressEvent(event)
|
||||
|
||||
|
||||
class SettingsPlainTextEdit(QtWidgets.QPlainTextEdit):
|
||||
focused_in = QtCore.Signal()
|
||||
|
|
|
|||
303
start.py
303
start.py
|
|
@ -196,7 +196,8 @@ from igniter import BootstrapRepos # noqa: E402
|
|||
from igniter.tools import (
|
||||
get_openpype_global_settings,
|
||||
get_openpype_path_from_db,
|
||||
validate_mongo_connection
|
||||
validate_mongo_connection,
|
||||
OpenPypeVersionNotFound
|
||||
) # noqa
|
||||
from igniter.bootstrap_repos import OpenPypeVersion # noqa: E402
|
||||
|
||||
|
|
@ -467,23 +468,41 @@ def _process_arguments() -> tuple:
|
|||
use_version = None
|
||||
use_staging = False
|
||||
commands = []
|
||||
for arg in sys.argv:
|
||||
if arg == "--use-version":
|
||||
_print("!!! Please use option --use-version like:")
|
||||
_print(" --use-version=3.0.0")
|
||||
sys.exit(1)
|
||||
|
||||
if arg.startswith("--use-version="):
|
||||
m = re.search(
|
||||
r"--use-version=(?P<version>\d+\.\d+\.\d+(?:\S*)?)", arg)
|
||||
if m and m.group('version'):
|
||||
use_version = m.group('version')
|
||||
_print(">>> Requested version [ {} ]".format(use_version))
|
||||
sys.argv.remove(arg)
|
||||
if "+staging" in use_version:
|
||||
use_staging = True
|
||||
break
|
||||
# OpenPype version specification through arguments
|
||||
use_version_arg = "--use-version"
|
||||
|
||||
for arg in sys.argv:
|
||||
if arg.startswith(use_version_arg):
|
||||
# Remove arg from sys argv
|
||||
sys.argv.remove(arg)
|
||||
# Extract string after use version arg
|
||||
use_version_value = arg[len(use_version_arg):]
|
||||
|
||||
if (
|
||||
not use_version_value
|
||||
or not use_version_value.startswith("=")
|
||||
):
|
||||
_print("!!! Please use option --use-version like:")
|
||||
_print(" --use-version=3.0.0")
|
||||
sys.exit(1)
|
||||
|
||||
version_str = use_version_value[1:]
|
||||
use_version = None
|
||||
if version_str.lower() == "latest":
|
||||
use_version = "latest"
|
||||
else:
|
||||
m = re.search(
|
||||
r"(?P<version>\d+\.\d+\.\d+(?:\S*)?)", version_str
|
||||
)
|
||||
if m and m.group('version'):
|
||||
use_version = m.group('version')
|
||||
_print(">>> Requested version [ {} ]".format(use_version))
|
||||
if "+staging" in use_version:
|
||||
use_staging = True
|
||||
break
|
||||
|
||||
if use_version is None:
|
||||
_print("!!! Requested version isn't in correct format.")
|
||||
_print((" Use --list-versions to find out"
|
||||
" proper version string."))
|
||||
|
|
@ -521,7 +540,7 @@ def _process_arguments() -> tuple:
|
|||
if os.getenv("OPENPYPE_HEADLESS_MODE") == "1":
|
||||
_print("!!! Cannot open Igniter dialog in headless mode.")
|
||||
sys.exit(1)
|
||||
import igniter
|
||||
|
||||
return_code = igniter.open_dialog()
|
||||
|
||||
# this is when we want to run OpenPype without installing anything.
|
||||
|
|
@ -646,67 +665,62 @@ def _find_frozen_openpype(use_version: str = None,
|
|||
(if requested).
|
||||
|
||||
"""
|
||||
version_path = None
|
||||
openpype_version = None
|
||||
openpype_versions = bootstrap.find_openpype(include_zips=True,
|
||||
staging=use_staging)
|
||||
# Collect OpenPype versions
|
||||
installed_version = OpenPypeVersion.get_installed_version()
|
||||
# Expected version that should be used by studio settings
|
||||
# - this option is used only if version is not explictly set and if
|
||||
# studio has set explicit version in settings
|
||||
studio_version = OpenPypeVersion.get_expected_studio_version(use_staging)
|
||||
|
||||
if use_version is not None:
|
||||
# Specific version is defined
|
||||
if use_version.lower() == "latest":
|
||||
# Version says to use latest version
|
||||
_print("Finding latest version defined by use version")
|
||||
openpype_version = bootstrap.find_latest_openpype_version(
|
||||
use_staging
|
||||
)
|
||||
else:
|
||||
_print("Finding specified version \"{}\"".format(use_version))
|
||||
openpype_version = bootstrap.find_openpype_version(
|
||||
use_version, use_staging
|
||||
)
|
||||
|
||||
if openpype_version is None:
|
||||
raise OpenPypeVersionNotFound(
|
||||
"Requested version \"{}\" was not found.".format(
|
||||
use_version
|
||||
)
|
||||
)
|
||||
|
||||
elif studio_version is not None:
|
||||
# Studio has defined a version to use
|
||||
_print("Finding studio version \"{}\"".format(studio_version))
|
||||
openpype_version = bootstrap.find_openpype_version(
|
||||
studio_version, use_staging
|
||||
)
|
||||
if openpype_version is None:
|
||||
raise OpenPypeVersionNotFound((
|
||||
"Requested OpenPype version \"{}\" defined by settings"
|
||||
" was not found."
|
||||
).format(studio_version))
|
||||
|
||||
else:
|
||||
# Default behavior to use latest version
|
||||
_print("Finding latest version")
|
||||
openpype_version = bootstrap.find_latest_openpype_version(
|
||||
use_staging
|
||||
)
|
||||
if openpype_version is None:
|
||||
if use_staging:
|
||||
reason = "Didn't find any staging versions."
|
||||
else:
|
||||
reason = "Didn't find any versions."
|
||||
raise OpenPypeVersionNotFound(reason)
|
||||
|
||||
# get local frozen version and add it to detected version so if it is
|
||||
# newer it will be used instead.
|
||||
local_version_str = bootstrap.get_version(
|
||||
Path(os.environ["OPENPYPE_ROOT"]))
|
||||
if local_version_str:
|
||||
local_version = OpenPypeVersion(
|
||||
version=local_version_str,
|
||||
path=Path(os.environ["OPENPYPE_ROOT"]))
|
||||
if local_version not in openpype_versions:
|
||||
openpype_versions.append(local_version)
|
||||
openpype_versions.sort()
|
||||
# if latest is currently running, ditch whole list
|
||||
# and run from current without installing it.
|
||||
if local_version == openpype_versions[-1]:
|
||||
os.environ["OPENPYPE_TRYOUT"] = "1"
|
||||
openpype_versions = []
|
||||
else:
|
||||
_print("!!! Warning: cannot determine current running version.")
|
||||
|
||||
if not os.getenv("OPENPYPE_TRYOUT"):
|
||||
try:
|
||||
# use latest one found (last in the list is latest)
|
||||
openpype_version = openpype_versions[-1]
|
||||
except IndexError:
|
||||
# no OpenPype version found, run Igniter and ask for them.
|
||||
_print('*** No OpenPype versions found.')
|
||||
if os.getenv("OPENPYPE_HEADLESS_MODE") == "1":
|
||||
_print("!!! Cannot open Igniter dialog in headless mode.")
|
||||
sys.exit(1)
|
||||
_print("--- launching setup UI ...")
|
||||
import igniter
|
||||
return_code = igniter.open_dialog()
|
||||
if return_code == 2:
|
||||
os.environ["OPENPYPE_TRYOUT"] = "1"
|
||||
if return_code == 3:
|
||||
# run OpenPype after installation
|
||||
|
||||
_print('>>> Finding OpenPype again ...')
|
||||
openpype_versions = bootstrap.find_openpype(
|
||||
staging=use_staging)
|
||||
try:
|
||||
openpype_version = openpype_versions[-1]
|
||||
except IndexError:
|
||||
_print(("!!! Something is wrong and we didn't "
|
||||
"found it again."))
|
||||
sys.exit(1)
|
||||
elif return_code != 2:
|
||||
_print(f" . finished ({return_code})")
|
||||
sys.exit(return_code)
|
||||
|
||||
if not openpype_versions:
|
||||
# no openpype versions found anyway, lets use then the one
|
||||
# shipped with frozen OpenPype
|
||||
if not os.getenv("OPENPYPE_TRYOUT"):
|
||||
_print("*** Still no luck finding OpenPype.")
|
||||
_print(("*** We'll try to use the one coming "
|
||||
"with OpenPype installation."))
|
||||
if installed_version == openpype_version:
|
||||
version_path = _bootstrap_from_code(use_version, use_staging)
|
||||
openpype_version = OpenPypeVersion(
|
||||
version=BootstrapRepos.get_version(version_path),
|
||||
|
|
@ -714,22 +728,6 @@ def _find_frozen_openpype(use_version: str = None,
|
|||
_initialize_environment(openpype_version)
|
||||
return version_path
|
||||
|
||||
# get path of version specified in `--use-version`
|
||||
local_version = bootstrap.get_version(OPENPYPE_ROOT)
|
||||
if use_version and use_version != local_version:
|
||||
# force the one user has selected
|
||||
openpype_version = None
|
||||
openpype_versions = bootstrap.find_openpype(include_zips=True,
|
||||
staging=use_staging)
|
||||
v: OpenPypeVersion
|
||||
found = [v for v in openpype_versions if str(v) == use_version]
|
||||
if found:
|
||||
openpype_version = sorted(found)[-1]
|
||||
if not openpype_version:
|
||||
_print(f"!!! Requested version {use_version} was not found.")
|
||||
list_versions(openpype_versions, local_version)
|
||||
sys.exit(1)
|
||||
|
||||
# test if latest detected is installed (in user data dir)
|
||||
is_inside = False
|
||||
try:
|
||||
|
|
@ -742,12 +740,12 @@ def _find_frozen_openpype(use_version: str = None,
|
|||
|
||||
if not is_inside:
|
||||
# install latest version to user data dir
|
||||
if os.getenv("OPENPYPE_HEADLESS_MODE", "0") != "1":
|
||||
import igniter
|
||||
version_path = igniter.open_update_window(openpype_version)
|
||||
else:
|
||||
if os.getenv("OPENPYPE_HEADLESS_MODE") == "1":
|
||||
version_path = bootstrap.install_version(
|
||||
openpype_version, force=True)
|
||||
openpype_version, force=True
|
||||
)
|
||||
else:
|
||||
version_path = igniter.open_update_window(openpype_version)
|
||||
|
||||
openpype_version.path = version_path
|
||||
_initialize_environment(openpype_version)
|
||||
|
|
@ -783,6 +781,13 @@ def _bootstrap_from_code(use_version, use_staging):
|
|||
# run through repos and add them to `sys.path` and `PYTHONPATH`
|
||||
# set root
|
||||
_openpype_root = OPENPYPE_ROOT
|
||||
# Unset use version if latest should be used
|
||||
# - when executed from code then code is expected as latest
|
||||
# - when executed from build then build is already marked as latest
|
||||
# in '_find_frozen_openpype'
|
||||
if use_version and use_version.lower() == "latest":
|
||||
use_version = None
|
||||
|
||||
if getattr(sys, 'frozen', False):
|
||||
local_version = bootstrap.get_version(Path(_openpype_root))
|
||||
switch_str = f" - will switch to {use_version}" if use_version else ""
|
||||
|
|
@ -790,47 +795,45 @@ def _bootstrap_from_code(use_version, use_staging):
|
|||
assert local_version
|
||||
else:
|
||||
# get current version of OpenPype
|
||||
local_version = bootstrap.get_local_live_version()
|
||||
local_version = OpenPypeVersion.get_installed_version_str()
|
||||
|
||||
version_to_use = None
|
||||
openpype_versions = bootstrap.find_openpype(
|
||||
include_zips=True, staging=use_staging)
|
||||
if use_staging and not use_version:
|
||||
try:
|
||||
version_to_use = openpype_versions[-1]
|
||||
except IndexError:
|
||||
_print("!!! No staging versions are found.")
|
||||
list_versions(openpype_versions, local_version)
|
||||
sys.exit(1)
|
||||
# All cases when should be used different version than build
|
||||
if (use_version and use_version != local_version) or use_staging:
|
||||
if use_version:
|
||||
# Explicit version should be used
|
||||
version_to_use = bootstrap.find_openpype_version(
|
||||
use_version, use_staging
|
||||
)
|
||||
if version_to_use is None:
|
||||
raise OpenPypeVersionNotFound(
|
||||
"Requested version \"{}\" was not found.".format(
|
||||
use_version
|
||||
)
|
||||
)
|
||||
else:
|
||||
# Staging version should be used
|
||||
version_to_use = bootstrap.find_latest_openpype_version(
|
||||
use_staging
|
||||
)
|
||||
if version_to_use is None:
|
||||
if use_staging:
|
||||
reason = "Didn't find any staging versions."
|
||||
else:
|
||||
# This reason is backup for possible bug in code
|
||||
reason = "Didn't find any versions."
|
||||
raise OpenPypeVersionNotFound(reason)
|
||||
|
||||
# Start extraction of version if needed
|
||||
if version_to_use.path.is_file():
|
||||
version_to_use.path = bootstrap.extract_openpype(
|
||||
version_to_use)
|
||||
version_to_use.path = bootstrap.extract_openpype(version_to_use)
|
||||
bootstrap.add_paths_from_directory(version_to_use.path)
|
||||
os.environ["OPENPYPE_VERSION"] = str(version_to_use)
|
||||
os.environ["OPENPYPE_VERSION"] = use_version
|
||||
version_path = version_to_use.path
|
||||
os.environ["OPENPYPE_REPOS_ROOT"] = (version_path / "openpype").as_posix() # noqa: E501
|
||||
os.environ["OPENPYPE_REPOS_ROOT"] = (
|
||||
version_path / "openpype"
|
||||
).as_posix()
|
||||
_openpype_root = version_to_use.path.as_posix()
|
||||
|
||||
elif use_version and use_version != local_version:
|
||||
v: OpenPypeVersion
|
||||
found = [v for v in openpype_versions if str(v) == use_version]
|
||||
if found:
|
||||
version_to_use = sorted(found)[-1]
|
||||
|
||||
if version_to_use:
|
||||
# use specified
|
||||
if version_to_use.path.is_file():
|
||||
version_to_use.path = bootstrap.extract_openpype(
|
||||
version_to_use)
|
||||
bootstrap.add_paths_from_directory(version_to_use.path)
|
||||
os.environ["OPENPYPE_VERSION"] = use_version
|
||||
version_path = version_to_use.path
|
||||
os.environ["OPENPYPE_REPOS_ROOT"] = (version_path / "openpype").as_posix() # noqa: E501
|
||||
_openpype_root = version_to_use.path.as_posix()
|
||||
else:
|
||||
_print(f"!!! Requested version {use_version} was not found.")
|
||||
list_versions(openpype_versions, local_version)
|
||||
sys.exit(1)
|
||||
else:
|
||||
os.environ["OPENPYPE_VERSION"] = local_version
|
||||
version_path = Path(_openpype_root)
|
||||
|
|
@ -889,16 +892,6 @@ def boot():
|
|||
# ------------------------------------------------------------------------
|
||||
_startup_validations()
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# Play animation
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
# from igniter.terminal_splash import play_animation
|
||||
|
||||
# don't play for silenced commands
|
||||
# if all(item not in sys.argv for item in silent_commands):
|
||||
# play_animation()
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# Process arguments
|
||||
# ------------------------------------------------------------------------
|
||||
|
|
@ -940,7 +933,7 @@ def boot():
|
|||
if getattr(sys, 'frozen', False):
|
||||
local_version = bootstrap.get_version(Path(OPENPYPE_ROOT))
|
||||
else:
|
||||
local_version = bootstrap.get_local_live_version()
|
||||
local_version = OpenPypeVersion.get_installed_version_str()
|
||||
|
||||
if "validate" in commands:
|
||||
_print(f">>> Validating version [ {use_version} ]")
|
||||
|
|
@ -969,7 +962,6 @@ def boot():
|
|||
)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if not openpype_path:
|
||||
_print("*** Cannot get OpenPype path from database.")
|
||||
|
||||
|
|
@ -989,7 +981,7 @@ def boot():
|
|||
if getattr(sys, 'frozen', False):
|
||||
local_version = bootstrap.get_version(Path(_openpype_root))
|
||||
else:
|
||||
local_version = bootstrap.get_local_live_version()
|
||||
local_version = OpenPypeVersion.get_installed_version_str()
|
||||
|
||||
list_versions(openpype_versions, local_version)
|
||||
sys.exit(1)
|
||||
|
|
@ -1003,6 +995,15 @@ def boot():
|
|||
# find versions of OpenPype to be used with frozen code
|
||||
try:
|
||||
version_path = _find_frozen_openpype(use_version, use_staging)
|
||||
except OpenPypeVersionNotFound as exc:
|
||||
message = str(exc)
|
||||
_print(message)
|
||||
if os.environ.get("OPENPYPE_HEADLESS_MODE") == "1":
|
||||
list_versions(openpype_versions, local_version)
|
||||
else:
|
||||
igniter.show_message_dialog("Version not found", message)
|
||||
sys.exit(1)
|
||||
|
||||
except RuntimeError as e:
|
||||
# no version to run
|
||||
_print(f"!!! {e}")
|
||||
|
|
@ -1015,7 +1016,17 @@ def boot():
|
|||
sys.exit(1)
|
||||
_print(f"--- version is valid")
|
||||
else:
|
||||
version_path = _bootstrap_from_code(use_version, use_staging)
|
||||
try:
|
||||
version_path = _bootstrap_from_code(use_version, use_staging)
|
||||
|
||||
except OpenPypeVersionNotFound as exc:
|
||||
message = str(exc)
|
||||
_print(message)
|
||||
if os.environ.get("OPENPYPE_HEADLESS_MODE") == "1":
|
||||
list_versions(openpype_versions, local_version)
|
||||
else:
|
||||
igniter.show_message_dialog("Version not found", message)
|
||||
sys.exit(1)
|
||||
|
||||
# set this to point either to `python` from venv in case of live code
|
||||
# or to `openpype` or `openpype_console` in case of frozen code
|
||||
|
|
|
|||
|
|
@ -140,9 +140,10 @@ def test_search_string_for_openpype_version(printer):
|
|||
]
|
||||
for ver_string in strings:
|
||||
printer(f"testing {ver_string[0]} should be {ver_string[1]}")
|
||||
assert OpenPypeVersion.version_in_str(ver_string[0]) == \
|
||||
ver_string[1]
|
||||
|
||||
assert isinstance(
|
||||
OpenPypeVersion.version_in_str(ver_string[0]),
|
||||
OpenPypeVersion if ver_string[1] else type(None)
|
||||
)
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_install_live_repos(fix_bootstrap, printer, monkeypatch, pytestconfig):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue