diff --git a/spinetoolbox/config.py b/spinetoolbox/config.py
index e3882ee35..9b556add4 100644
--- a/spinetoolbox/config.py
+++ b/spinetoolbox/config.py
@@ -18,7 +18,7 @@
LATEST_PROJECT_VERSION = 13
# For the Add/Update SpineOpt wizard
-REQUIRED_SPINE_OPT_VERSION = "0.6.9"
+REQUIRED_SPINE_OPT_VERSION = "0.8.3"
# Invalid characters for directory names
# NOTE: "." is actually valid in a directory name but this is
diff --git a/spinetoolbox/ui_main.py b/spinetoolbox/ui_main.py
index 16a0afd6b..dfbcec7dd 100644
--- a/spinetoolbox/ui_main.py
+++ b/spinetoolbox/ui_main.py
@@ -128,7 +128,7 @@ class ToolboxUI(QMainWindow):
def __init__(self):
from .ui.mainwindow import Ui_MainWindow # pylint: disable=import-outside-toplevel
- super().__init__(flags=Qt.Window)
+ super().__init__(flags=Qt.WindowType.Window)
self.set_app_style()
self.set_error_mode()
self._qsettings = QSettings("SpineProject", "Spine Toolbox", self)
@@ -227,16 +227,16 @@ def __init__(self):
def eventFilter(self, obj, ev):
# Save/restore splitter states when hiding/showing execution lists
if obj == self.ui.listView_console_executions:
- if ev.type() == QEvent.Hide:
+ if ev.type() == QEvent.Type.Hide:
self._qsettings.setValue("mainWindow/consoleSplitterPosition", self.ui.splitter_console.saveState())
- elif ev.type() == QEvent.Show:
+ elif ev.type() == QEvent.Type.Show:
splitter_state = self._qsettings.value("mainWindow/consoleSplitterPosition", defaultValue="false")
if splitter_state != "false":
self.ui.splitter_console.restoreState(splitter_state)
return super().eventFilter(obj, ev)
def _setup_properties_title(self):
- self.label_item_name.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
+ self.label_item_name.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter)
self.label_item_name.setMinimumHeight(28)
self._button_item_dir.setIcon(QIcon(":icons/folder-open-regular.svg"))
layout = QHBoxLayout(self._properties_title)
@@ -316,7 +316,9 @@ def connect_signals(self):
# Consoles
self.jupyter_console_requested.connect(self._setup_jupyter_console)
self.kernel_shutdown.connect(self._handle_kernel_shutdown)
- self.persistent_console_requested.connect(self._setup_persistent_console, Qt.BlockingQueuedConnection)
+ self.persistent_console_requested.connect(
+ self._setup_persistent_console, Qt.ConnectionType.BlockingQueuedConnection
+ )
@staticmethod
def set_app_style():
diff --git a/spinetoolbox/widgets/add_up_spine_opt_wizard.py b/spinetoolbox/widgets/add_up_spine_opt_wizard.py
index fe96373eb..96c470909 100644
--- a/spinetoolbox/widgets/add_up_spine_opt_wizard.py
+++ b/spinetoolbox/widgets/add_up_spine_opt_wizard.py
@@ -26,6 +26,7 @@
QWidget,
QWizard,
QWizardPage,
+ QApplication,
)
from ..config import REQUIRED_SPINE_OPT_VERSION
from ..execution_managers import QProcessExecutionManager
@@ -42,7 +43,6 @@ class _PageId(IntEnum):
FAILURE = auto()
TROUBLESHOOT_PROBLEMS = auto()
TROUBLESHOOT_SOLUTION = auto()
- RESET_REGISTRY = auto()
ADD_UP_SPINE_OPT_AGAIN = auto()
TOTAL_FAILURE = auto()
@@ -69,7 +69,6 @@ def __init__(self, parent, julia_exe, julia_project):
self.setPage(_PageId.FAILURE, FailurePage(self))
self.setPage(_PageId.TROUBLESHOOT_PROBLEMS, TroubleshootProblemsPage(self))
self.setPage(_PageId.TROUBLESHOOT_SOLUTION, TroubleshootSolutionPage(self))
- self.setPage(_PageId.RESET_REGISTRY, ResetRegistryPage(self))
self.setPage(_PageId.ADD_UP_SPINE_OPT_AGAIN, AddUpSpineOptAgainPage(self))
self.setPage(_PageId.TOTAL_FAILURE, TotalFailurePage(self))
self.setStartId(_PageId.INTRO)
@@ -94,7 +93,7 @@ def nextId(self):
class SelectJuliaPage(QWizardPage):
def __init__(self, parent, julia_exe, julia_project):
super().__init__(parent)
- self.setTitle("Select Julia project")
+ self.setTitle("Select Julia")
self._julia_exe = julia_exe
self._julia_project = julia_project
self._julia_exe_line_edit = QLineEdit()
@@ -103,14 +102,14 @@ def __init__(self, parent, julia_exe, julia_project):
self.registerField("julia_exe*", self._julia_exe_line_edit)
self.registerField("julia_project", self._julia_project_line_edit)
layout = QVBoxLayout(self)
- layout.addWidget(QLabel("Julia executable:"))
+ layout.addWidget(QLabel("Julia executable"))
julia_exe_widget = QWidget()
julia_exe_layout = QHBoxLayout(julia_exe_widget)
julia_exe_layout.addWidget(self._julia_exe_line_edit)
julia_exe_button = QPushButton("Browse")
julia_exe_layout.addWidget(julia_exe_button)
layout.addWidget(julia_exe_widget)
- layout.addWidget(QLabel("Julia project (directory):"))
+ layout.addWidget(QLabel("Julia project/environment (directory)"))
julia_project_widget = QWidget()
julia_project_layout = QHBoxLayout(julia_project_widget)
julia_project_layout.addWidget(self._julia_project_line_edit)
@@ -134,7 +133,7 @@ def _select_julia_exe(self, _):
@Slot(bool)
def _select_julia_project(self, _):
julia_project = QFileDialog.getExistingDirectory(
- self, "Select Julia project (directory)", self.field("julia_project")
+ self, "Select Julia project/environment (directory)", self.field("julia_project")
)
if not julia_project:
return
@@ -179,11 +178,11 @@ def initializePage(self):
self._exec_mngr = QProcessExecutionManager(self, julia_exe, args, silent=True)
self.completeChanged.emit()
self._exec_mngr.execution_finished.connect(self._handle_check_install_finished)
- qApp.setOverrideCursor(QCursor(Qt.BusyCursor)) # pylint: disable=undefined-variable
+ QApplication.setOverrideCursor(QCursor(Qt.CursorShape.BusyCursor)) # pylint: disable=undefined-variable
self._exec_mngr.start_execution()
def _handle_check_install_finished(self, ret):
- qApp.restoreOverrideCursor() # pylint: disable=undefined-variable
+ QApplication.restoreOverrideCursor() # pylint: disable=undefined-variable
self._exec_mngr.execution_finished.disconnect(self._handle_check_install_finished)
if self.wizard().currentPage() is not self:
return
@@ -212,20 +211,20 @@ def _handle_check_install_finished(self, ret):
return
msg = (
f"SpineOpt version {spine_opt_version} is installed, "
- f"but version {REQUIRED_SPINE_OPT_VERSION} is required."
+ f"but version {REQUIRED_SPINE_OPT_VERSION} or higher is required."
)
self.layout().addWidget(WrapLabel(msg))
self.wizard().required_action = "update"
self.setFinalPage(False)
self.setCommitPage(True)
- self.setButtonText(QWizard.CommitButton, "Update SpineOpt")
+ self.setButtonText(QWizard.WizardButton.CommitButton, "Update SpineOpt")
self.completeChanged.emit()
return
self.layout().addWidget(QLabel("SpineOpt is not installed."))
self.wizard().required_action = "add"
self.setFinalPage(False)
self.setCommitPage(True)
- self.setButtonText(QWizard.CommitButton, "Install SpineOpt")
+ self.setButtonText(QWizard.WizardButton.CommitButton, "Install SpineOpt")
self.completeChanged.emit()
def nextId(self):
@@ -255,11 +254,11 @@ def initializePage(self):
self.msg_success.emit(f"SpineOpt {process} started")
cmd = julia_exe + " " + " ".join(args)
self.msg.emit(f"$ {cmd}")
- qApp.setOverrideCursor(QCursor(Qt.BusyCursor)) # pylint: disable=undefined-variable
+ QApplication.setOverrideCursor(QCursor(Qt.CursorShape.BusyCursor)) # pylint: disable=undefined-variable
self._exec_mngr.start_execution()
def _handle_spine_opt_add_up_finished(self, ret):
- qApp.restoreOverrideCursor() # pylint: disable=undefined-variable
+ QApplication.restoreOverrideCursor() # pylint: disable=undefined-variable
self._exec_mngr.execution_finished.disconnect(self._handle_spine_opt_add_up_finished)
if self.wizard().currentPage() is not self:
return
@@ -270,9 +269,10 @@ def _handle_spine_opt_add_up_finished(self, ret):
configured = {"add": "installed", "update": "updated"}[self.wizard().required_action]
self.msg_success.emit(f"SpineOpt successfully {configured}")
return
- process = {"add": "installation", "update": "updatee"}[self.wizard().required_action]
+ process = {"add": "installation", "update": "update"}[self.wizard().required_action]
self.msg_error.emit(f"SpineOpt {process} failed")
self.wizard().process_log = self._log.toHtml()
+ self.wizard().process_log_plain = self._log.toPlainText()
def nextId(self):
if self._successful:
@@ -296,13 +296,16 @@ def nextId(self):
class FailurePage(QWizardPage):
- def __init__(self, parent):
- super().__init__(parent)
+ def initializePage(self):
+ process = {"add": "Installation", "update": "Update"}[self.wizard().required_action]
+ self.setTitle(f"{process} failed")
check_box = QCheckBox("Troubleshoot problems")
check_box.setChecked(True)
self.registerField("troubleshoot", check_box)
layout = QVBoxLayout(self)
- msg = "Apologies."
+ msg = ("Apologies. Please see the Troubleshoot problems section "
+ "by clicking Next or click Cancel to close "
+ "the wizard.")
layout.addWidget(WrapLabel(msg))
layout.addStretch()
layout.addWidget(check_box)
@@ -314,10 +317,6 @@ def __init__(self, parent):
def _handle_check_box_clicked(self, checked=False):
self.setFinalPage(not checked)
- def initializePage(self):
- process = {"add": "Installation", "update": "Update"}[self.wizard().required_action]
- self.setTitle(f"{process} failed")
-
def nextId(self):
if self.field("troubleshoot"):
return _PageId.TROUBLESHOOT_PROBLEMS
@@ -329,10 +328,11 @@ def __init__(self, parent):
super().__init__(parent)
self.setTitle("Troubleshooting")
msg = "Select your problem from the list."
- self._button1 = QRadioButton("Installing SpineOpt fails with one of the following messages (or similar):")
- msg1a = MonoSpaceFontTextBrowser(self)
- msg1b = MonoSpaceFontTextBrowser(self)
- msg1a.append(
+ self._button1 = QRadioButton("None of the below")
+ self._button2 = QRadioButton("Installing SpineOpt fails with one of the following messages (or similar):")
+ msg2a = MonoSpaceFontTextBrowser(self)
+ msg2b = MonoSpaceFontTextBrowser(self)
+ msg2a.append(
"""
\u22ee
Updating git-repo `https://github.com/spine-tools/SpineJuliaRegistry`
@@ -341,7 +341,7 @@ def __init__(self, parent):
\u22ee
"""
)
- msg1b.append(
+ msg2b.append(
"""
\u22ee
Updating git-repo `https://github.com/spine-tools/SpineJuliaRegistry`
@@ -350,9 +350,9 @@ def __init__(self, parent):
\u22ee
"""
)
- self._button2 = QRadioButton("On Windows 7, installing SpineOpt fails with the following message (or similar):")
- msg2 = MonoSpaceFontTextBrowser(self)
- msg2.append(
+ self._button3 = QRadioButton("On Windows 7, installing SpineOpt fails with the following message (or similar):")
+ msg3 = MonoSpaceFontTextBrowser(self)
+ msg3.append(
"""
\u22ee
Downloading artifact: OpenBLAS32
@@ -371,13 +371,15 @@ def __init__(self, parent):
layout.addWidget(WrapLabel(msg))
layout.addStretch()
layout.addWidget(self._button1)
- layout.addWidget(msg1a)
- layout.addWidget(msg1b)
layout.addStretch()
layout.addWidget(self._button2)
- layout.addWidget(msg2)
+ layout.addWidget(msg2a)
+ layout.addWidget(msg2b)
+ layout.addStretch()
+ layout.addWidget(self._button3)
+ layout.addWidget(msg3)
layout.addStretch()
- button_view_log = QPushButton("View process log")
+ button_view_log = QPushButton("View log")
widget_view_log = QWidget()
layout_view_log = QHBoxLayout(widget_view_log)
layout_view_log.addStretch()
@@ -386,16 +388,18 @@ def __init__(self, parent):
layout.addStretch()
self.registerField("problem1", self._button1)
self.registerField("problem2", self._button2)
+ self.registerField("problem3", self._button3)
self._button1.toggled.connect(lambda _: self.completeChanged.emit())
self._button2.toggled.connect(lambda _: self.completeChanged.emit())
+ self._button3.toggled.connect(lambda _: self.completeChanged.emit())
button_view_log.clicked.connect(self._show_log)
def isComplete(self):
- return self.field("problem1") or self.field("problem2")
+ return self.field("problem1") or self.field("problem2") or self.field("problem3")
@Slot(bool)
def _show_log(self, _=False):
- log_widget = QWidget(self, f=Qt.Window)
+ log_widget = QWidget(self, f=Qt.WindowType.Window)
layout = QVBoxLayout(log_widget)
log = MonoSpaceFontTextBrowser(log_widget)
log.append(self.wizard().process_log)
@@ -412,97 +416,108 @@ def __init__(self, parent):
self.setCommitPage(True)
QVBoxLayout(self)
- def cleanupPage(self):
- super().cleanupPage()
- self.wizard().reset_registry = False
-
def initializePage(self):
_clear_layout(self.layout())
if self.field("problem1"):
self._initialize_page_solution1()
- elif self.field("problem2"):
+ if self.field("problem2"):
self._initialize_page_solution2()
+ elif self.field("problem3"):
+ self._initialize_page_solution3()
def _initialize_page_solution1(self):
- self.wizard().reset_registry = True
+ action = {"add": "Install SpineOpt", "update": "Update SpineOpt"}[self.wizard().required_action]
+ julia = self.field("julia_exe")
+ env = self.field("julia_project")
+ if not env:
+ install_cmds = f"""
+ julia> import Pkg
+ julia> Pkg.Registry.add("General")
+ julia> Pkg.Registry.add(Pkg.RegistrySpec(url="https://github.com/spine-tools/SpineJuliaRegistry"))
+ julia> Pkg.add("SpineOpt")
"""
+ else:
+ install_cmds = f"""
+ julia> import Pkg
+ julia> cd("{env}")
+ julia> Pkg.activate(".")
+ julia> Pkg.Registry.add("General")
+ julia> Pkg.Registry.add(Pkg.RegistrySpec(url="https://github.com/spine-tools/SpineJuliaRegistry"))
+ julia> Pkg.add("SpineOpt")
"""
+ if not env:
+ update_cmds = """
+ julia> import Pkg
+ julia> Pkg.update("SpineOpt")
"""
+ else:
+ update_cmds = f"""
+ julia> import Pkg
+ julia> cd("{env}")
+ julia> Pkg.activate(".")
+ julia> Pkg.update("SpineOpt")
"""
+ action_cmds = {"Install SpineOpt": install_cmds, "Update SpineOpt": update_cmds}
+ self.setTitle("What now?")
+ msg_browser = MonoSpaceFontTextBrowser(self)
+ msg_browser.append(action_cmds[action])
+ label1_txt = (
+ "
See also up-to-date " + "installation " + "instructions in SpineOpt documentation.
" + ) + self.layout().addWidget(HyperTextLabel(label1_txt)) + self.layout().addWidget(msg_browser) + self.layout().addWidget(HyperTextLabel(label2_txt)) + self.setButtonText(QWizard.WizardButton.CommitButton, action) + + def _initialize_page_solution2(self): + action = {"add": "Install SpineOpt", "update": "Update SpineOpt"}[self.wizard().required_action] + julia = self.field("julia_exe") self.setTitle("Reset Julia General Registry") description = ( "The issue you're facing can be due to an error in the installation of the Julia General registry " - "from the Julia Package Server.
" - "The simplest solution is to delete any trace of the registry and install it again, from GitHub.
" - "However, this will also remove all your installed packages.
" + "from the Julia Package Server. The simplest solution is to delete any trace of the registry and install " + "it again from GitHub." + "To do this, open your favorite terminal (ie. Command prompt) and start the Julia REPL using "
+ f"command:
{julia}
In the Julia REPL, enter the commands below (gray text, "
+ "not the green one). Afterwards, try Add/Update SpineOpt again.
NOTE: this will also remove all your installed packages.
" ) + cmds = f""" + julia> import PkgThe issue you're facing can be solved by installing Windows Managemet Framework 3 or greater, " + "
The issue you're facing can be solved by updating Windows Management Framework to 5.1 or greater, " "as follows:
Please open an issue with SpineOpt." + msg = ("
Please open an issue with SpineOpt."
+ "
Copy the log and paste it into the issue description.