add update dialog

This commit is contained in:
Ondrej Samohel 2021-08-20 17:57:28 +02:00 committed by Ondřej Samohel
parent 580e8ade0e
commit df310da141
No known key found for this signature in database
GPG key ID: 02376E18990A97C6
6 changed files with 274 additions and 3 deletions

View file

@ -31,8 +31,31 @@ def open_dialog():
return d.result()
def open_update_window(openpype_version):
"""Open update window."""
if os.getenv("OPENPYPE_HEADLESS_MODE"):
print("!!! Can't open dialog in headless mode. Exiting.")
sys.exit(1)
from Qt import QtWidgets, QtCore
from .update_window import UpdateWindow
scale_attr = getattr(QtCore.Qt, "AA_EnableHighDpiScaling", None)
if scale_attr is not None:
QtWidgets.QApplication.setAttribute(scale_attr)
app = QtWidgets.QApplication(sys.argv)
d = UpdateWindow(version=openpype_version)
d.open()
app.exec_()
version_path = d.get_version_path()
return version_path
__all__ = [
"BootstrapRepos",
"open_dialog",
"open_update_window",
"version"
]

View file

@ -966,6 +966,7 @@ class BootstrapRepos:
# test if destination directory already exist, if so lets delete it.
if destination.exists() and force:
self._print("removing existing directory")
try:
shutil.rmtree(destination)
except OSError as e:
@ -975,6 +976,7 @@ class BootstrapRepos:
raise OpenPypeVersionIOError(
f"cannot remove existing {destination}") from e
elif destination.exists() and not force:
self._print("destination directory already exists")
raise OpenPypeVersionExists(f"{destination} already exist.")
else:
# create destination parent directories even if they don't exist.
@ -984,6 +986,7 @@ class BootstrapRepos:
if openpype_version.path.is_dir():
# create zip inside temporary directory.
self._print("Creating zip from directory ...")
self._progress_callback(0)
with tempfile.TemporaryDirectory() as temp_dir:
temp_zip = \
Path(temp_dir) / f"openpype-v{openpype_version}.zip"
@ -1009,13 +1012,16 @@ class BootstrapRepos:
raise OpenPypeVersionInvalid("Invalid file format")
if not self.is_inside_user_data(openpype_version.path):
self._progress_callback(35)
openpype_version.path = self._copy_zip(
openpype_version.path, destination)
# extract zip there
self._print("extracting zip to destination ...")
with ZipFile(openpype_version.path, "r") as zip_ref:
self._progress_callback(75)
zip_ref.extractall(destination)
self._progress_callback(100)
return destination

61
igniter/update_thread.py Normal file
View file

@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
"""Working thread for update."""
from Qt.QtCore import QThread, Signal, QObject # noqa
from .bootstrap_repos import (
BootstrapRepos,
OpenPypeVersion
)
class UpdateThread(QThread):
"""Install Worker thread.
This class takes care of finding OpenPype version on user entered path
(or loading this path from database). If nothing is entered by user,
OpenPype will create its zip files from repositories that comes with it.
If path contains plain repositories, they are zipped and installed to
user data dir.
"""
progress = Signal(int)
message = Signal((str, bool))
def __init__(self, parent=None):
self._result = None
self._openpype_version = None
QThread.__init__(self, parent)
def set_version(self, openpype_version: OpenPypeVersion):
self._openpype_version = openpype_version
def result(self):
"""Result of finished installation."""
return self._result
def _set_result(self, value):
if self._result is not None:
raise AssertionError("BUG: Result was set more than once!")
self._result = value
def run(self):
"""Thread entry point.
Using :class:`BootstrapRepos` to either install OpenPype as zip files
or copy them from location specified by user or retrieved from
database.
"""
bs = BootstrapRepos(
progress_callback=self.set_progress, message=self.message)
version_path = bs.install_version(self._openpype_version)
self._set_result(version_path)
def set_progress(self, progress: int) -> None:
"""Helper to set progress bar.
Args:
progress (int): Progress in percents.
"""
self.progress.emit(progress)

173
igniter/update_window.py Normal file
View file

@ -0,0 +1,173 @@
# -*- coding: utf-8 -*-
"""Progress window to show when OpenPype is updating/installing locally."""
import os
import sys
from pathlib import Path
from .update_thread import UpdateThread
from Qt import QtCore, QtGui, QtWidgets # noqa
from .bootstrap_repos import OpenPypeVersion
def load_stylesheet(path: str = None) -> str:
"""Load css style sheet.
Args:
path (str, optional): Path to stylesheet. If none, `stylesheet.css` from
current package's path is used.
Returns:
str: content of the stylesheet
"""
if path:
stylesheet_path = Path(path)
else:
stylesheet_path = Path(os.path.dirname(__file__)) / "stylesheet.css"
return stylesheet_path.read_text()
class NiceProgressBar(QtWidgets.QProgressBar):
def __init__(self, parent=None):
super(NiceProgressBar, self).__init__(parent)
self._real_value = 0
def setValue(self, value):
self._real_value = value
if value != 0 and value < 11:
value = 11
super(NiceProgressBar, self).setValue(value)
def value(self):
return self._real_value
def text(self):
return "{} %".format(self._real_value)
class UpdateWindow(QtWidgets.QDialog):
"""OpenPype update window."""
_width = 500
_height = 100
def __init__(self, version: OpenPypeVersion, parent=None):
super(UpdateWindow, self).__init__(parent)
self._openpype_version = version
self._result_version_path = None
self.setWindowTitle(
f"OpenPype is updating ..."
)
self.setModal(True)
self.setWindowFlags(
QtCore.Qt.WindowMinimizeButtonHint
)
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)
for filename in os.listdir(poppins_font_path):
if os.path.splitext(filename)[1] == ".ttf":
QtGui.QFontDatabase.addApplicationFont(filename)
# Load logo
pixmap_openpype_logo = QtGui.QPixmap(icon_path)
# Set logo as icon of window
self.setWindowIcon(QtGui.QIcon(pixmap_openpype_logo))
self._pixmap_openpype_logo = pixmap_openpype_logo
self._update_thread = None
self.resize(QtCore.QSize(self._width, self._height))
self._init_ui()
# Set stylesheet
self.setStyleSheet(load_stylesheet())
self._run_update()
def _init_ui(self):
# Main info
# --------------------------------------------------------------------
main_label = QtWidgets.QLabel(
f"<b>OpenPype</b> is updating to {self._openpype_version}", self)
main_label.setWordWrap(True)
main_label.setObjectName("MainLabel")
# Progress bar
# --------------------------------------------------------------------
progress_bar = NiceProgressBar(self)
progress_bar.setAlignment(QtCore.Qt.AlignCenter)
progress_bar.setTextVisible(False)
# add all to main
main = QtWidgets.QVBoxLayout(self)
main.addSpacing(15)
main.addWidget(main_label, 0)
main.addSpacing(15)
main.addWidget(progress_bar, 0)
main.addSpacing(15)
self._progress_bar = progress_bar
def _run_update(self):
"""Start install process.
This will once again validate entered path and mongo if ok, start
working thread that will do actual job.
"""
# Check if install thread is not already running
if self._update_thread and self._update_thread.isRunning():
return
self._progress_bar.setRange(0, 0)
update_thread = UpdateThread(self)
update_thread.set_version(self._openpype_version)
update_thread.message.connect(self.update_console)
update_thread.progress.connect(self._update_progress)
update_thread.finished.connect(self._installation_finished)
self._update_thread = update_thread
update_thread.start()
def get_version_path(self):
return self._result_version_path
def _installation_finished(self):
status = self._update_thread.result()
self._result_version_path = status
self._progress_bar.setRange(0, 1)
self._update_progress(100)
QtWidgets.QApplication.processEvents()
self.done(0)
def _update_progress(self, progress: int):
# not updating progress as we are not able to determine it
# correctly now. Progress bar is set to un-deterministic mode
# until we are able to get progress in better way.
"""
self._progress_bar.setRange(0, 0)
self._progress_bar.setValue(progress)
text_visible = self._progress_bar.isTextVisible()
if progress == 0:
if text_visible:
self._progress_bar.setTextVisible(False)
elif not text_visible:
self._progress_bar.setTextVisible(True)
"""
return
def update_console(self, msg: str, error: bool = False) -> None:
"""Display message in console.
Args:
msg (str): message.
error (bool): if True, print it red.
"""
print(msg)

View file

@ -18,7 +18,7 @@ from .pype_commands import PypeCommands
@click.option("--list-versions", is_flag=True, expose_value=False,
help=("list all detected versions. Use With `--use-staging "
"to list staging versions."))
@click.option("--validate-version",
@click.option("--validate-version", expose_value=False,
help="validate given version integrity")
def main(ctx):
"""Pype is main command serving as entry point to pipeline system.

View file

@ -610,8 +610,16 @@ def _find_frozen_openpype(use_version: str = None,
if not is_inside:
# install latest version to user data dir
version_path = bootstrap.install_version(
openpype_version, force=True)
if not os.getenv("OPENPYPE_HEADLESS"):
import igniter
version_path = igniter.open_update_window(openpype_version)
else:
version_path = bootstrap.install_version(
openpype_version, force=True)
openpype_version.path = version_path
_initialize_environment(openpype_version)
return openpype_version.path
if openpype_version.path.is_file():
_print(">>> Extracting zip file ...")