diff --git a/spinetoolbox/config.py b/spinetoolbox/config.py
index 9b556add4..a31424bf6 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.8.3"
+REQUIRED_SPINE_OPT_VERSION = "0.9.0"
# Invalid characters for directory names
# NOTE: "." is actually valid in a directory name but this is
diff --git a/spinetoolbox/helpers.py b/spinetoolbox/helpers.py
index 771ba3c63..469d83aaa 100644
--- a/spinetoolbox/helpers.py
+++ b/spinetoolbox/helpers.py
@@ -1062,24 +1062,26 @@ def file_is_valid(parent, file_path, msgbox_title, extra_check=None):
return True
-def dir_is_valid(parent, dir_path, msgbox_title):
+def dir_is_valid(parent, dir_path, msgbox_title, msg=None):
"""Checks that given path is a directory. Needed in
- SettingsWdiget and KernelEditor because the QLineEdits
+ SettingsWidget and KernelEditor because the QLineEdits
are editable. Returns True when dir_path is an empty string so that
- we can use default values (e.g. from line edit place holder text)
+ we can use default values (e.g. from line edit placeholder text)
Args:
parent (QWidget): Parent widget for the message box
dir_path (str): Directory path to check
msgbox_title (str): Message box title
+ msg (str): Warning message
Returns:
bool: True if given path is an empty string or if path is an existing directory, False otherwise
"""
+ if not msg:
+ msg = "Please select a valid directory"
if dir_path == "":
return True
if not os.path.isdir(dir_path):
- msg = "Please select a valid directory"
# noinspection PyCallByClass, PyArgumentList
QMessageBox.warning(parent, msgbox_title, msg)
return False
diff --git a/spinetoolbox/widgets/add_up_spine_opt_wizard.py b/spinetoolbox/widgets/add_up_spine_opt_wizard.py
index 4b385b38a..e06bb90a5 100644
--- a/spinetoolbox/widgets/add_up_spine_opt_wizard.py
+++ b/spinetoolbox/widgets/add_up_spine_opt_wizard.py
@@ -13,7 +13,7 @@
"""Classes for custom QDialogs for julia setup."""
from enum import IntEnum, auto
from PySide6.QtCore import Qt, Slot
-from PySide6.QtGui import QCursor
+from PySide6.QtGui import QCursor, QTextCursor
from PySide6.QtWidgets import (
QCheckBox,
QFileDialog,
@@ -72,6 +72,7 @@ def __init__(self, parent, julia_exe, julia_project):
self.setPage(_PageId.ADD_UP_SPINE_OPT_AGAIN, AddUpSpineOptAgainPage(self))
self.setPage(_PageId.TOTAL_FAILURE, TotalFailurePage(self))
self.setStartId(_PageId.INTRO)
+ self.setOption(QWizard.WizardOption.NoCancelButtonOnLastPage)
class IntroPage(QWizardPage):
@@ -238,11 +239,10 @@ def initializePage(self):
processing, code, process = {
"add": (
"Installing",
- 'using Pkg; pkg"registry add General https://github.com/spine-tools/SpineJuliaRegistry.git"; '
- 'pkg"add SpineOpt"',
+ 'using Pkg; Pkg.Registry.add("General"); Pkg.add("SpineOpt")',
"installation",
),
- "update": ("Updating", 'using Pkg; pkg"up SpineOpt"', "update"),
+ "update": ("Updating", 'using Pkg; Pkg.update("SpineOpt")', "update"),
}[self.wizard().required_action]
self.setTitle(f"{processing} SpineOpt")
julia_exe = self.field("julia_exe")
@@ -331,10 +331,23 @@ def __init__(self, parent):
self.setTitle("Troubleshooting")
msg = "Select your problem from the list."
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(
+ self._button2 = QRadioButton("Installing SpineOpt fails with the following message (or similar):")
+ msg2 = MonoSpaceFontTextBrowser(self)
+ msg2.append(
+ """
+ \u22ee
+ error: GitError(Code:ERROR, Class:SSL, Your Julia is built with a SSL/TLS engine that libgit2
+ doesn't know how to configure to use a file or directory of certificate authority roots,
+ but your environment specifies one via the SSL_CERT_FILE variable. If you believe your
+ system's root certificates are safe to use, you can `export JULIA_SSL_CA_ROOTS_PATH=""`
+ in your environment to use those instead.
+ \u22ee
+ """
+ )
+ self._button3 = QRadioButton("Installing SpineOpt fails with one of the following messages (or similar):")
+ msg3a = MonoSpaceFontTextBrowser(self)
+ msg3b = MonoSpaceFontTextBrowser(self)
+ msg3a.append(
"""
\u22ee
Updating git-repo `https://github.com/spine-tools/SpineJuliaRegistry`
@@ -343,7 +356,7 @@ def __init__(self, parent):
\u22ee
"""
)
- msg2b.append(
+ msg3b.append(
"""
\u22ee
Updating git-repo `https://github.com/spine-tools/SpineJuliaRegistry`
@@ -352,9 +365,9 @@ def __init__(self, parent):
\u22ee
"""
)
- self._button3 = QRadioButton("On Windows 7, installing SpineOpt fails with the following message (or similar):")
- msg3 = MonoSpaceFontTextBrowser(self)
- msg3.append(
+ self._button4 = QRadioButton("On Windows 7, installing SpineOpt fails with the following message (or similar):")
+ msg4 = MonoSpaceFontTextBrowser(self)
+ msg4.append(
"""
\u22ee
Downloading artifact: OpenBLAS32
@@ -375,11 +388,14 @@ def __init__(self, parent):
layout.addWidget(self._button1)
layout.addStretch()
layout.addWidget(self._button2)
- layout.addWidget(msg2a)
- layout.addWidget(msg2b)
+ layout.addWidget(msg2)
layout.addStretch()
layout.addWidget(self._button3)
- layout.addWidget(msg3)
+ layout.addWidget(msg3a)
+ layout.addWidget(msg3b)
+ layout.addStretch()
+ layout.addWidget(self._button4)
+ layout.addWidget(msg4)
layout.addStretch()
button_view_log = QPushButton("View log")
widget_view_log = QWidget()
@@ -388,16 +404,24 @@ def __init__(self, parent):
layout_view_log.addWidget(button_view_log)
layout.addWidget(widget_view_log)
layout.addStretch()
+ cursor = QTextCursor()
+ cursor.movePosition(QTextCursor.MoveOperation.Start, QTextCursor.MoveMode.MoveAnchor)
+ msg2.setTextCursor(cursor) # Scroll to the beginning of the document
+ msg3a.setTextCursor(cursor)
+ msg3b.setTextCursor(cursor)
+ msg4.setTextCursor(cursor)
self.registerField("problem1", self._button1)
self.registerField("problem2", self._button2)
self.registerField("problem3", self._button3)
+ self.registerField("problem4", self._button4)
self._button1.toggled.connect(lambda _: self.completeChanged.emit())
self._button2.toggled.connect(lambda _: self.completeChanged.emit())
self._button3.toggled.connect(lambda _: self.completeChanged.emit())
+ self._button4.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") or self.field("problem3")
+ return self.field("problem1") or self.field("problem2") or self.field("problem3") or self.field("problem4")
@Slot(bool)
def _show_log(self, _=False):
@@ -426,8 +450,11 @@ def initializePage(self):
self._initialize_page_solution2()
elif self.field("problem3"):
self._initialize_page_solution3()
+ elif self.field("problem4"):
+ self._initialize_page_solution4()
def _initialize_page_solution1(self):
+ self.setFinalPage(False)
action = {"add": "Install SpineOpt", "update": "Update SpineOpt"}[self.wizard().required_action]
julia = self.field("julia_exe")
env = self.field("julia_project")
@@ -435,7 +462,6 @@ def _initialize_page_solution1(self):
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"""
@@ -443,7 +469,6 @@ def _initialize_page_solution1(self):
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 = """
@@ -477,7 +502,42 @@ def _initialize_page_solution1(self):
self.setButtonText(QWizard.WizardButton.CommitButton, action)
def _initialize_page_solution2(self):
- action = {"add": "Install SpineOpt", "update": "Update SpineOpt"}[self.wizard().required_action]
+ self.setFinalPage(True)
+ julia = self.field("julia_exe")
+ env = self.field("julia_project")
+ self.setTitle("JULIA_SSL_CA_ROOTS_PATH environment variable missing")
+ description = (
+ "
You are most likely running Toolbox in a Conda environment and the issue " + "you're facing is due to a missing environment variable. The simplest solution " + "is to open the Julia REPL from the Anaconda Prompt, add the environment variable, " + "and then install SpineOpt.
" + "To do this, open your Anaconda prompt and start the Julia REPL using "
+ f"command:
{julia}
In the Julia REPL, enter the commands below (gray text, "
+ "not the green one). After entering the commands, SpineOpt should be installed. If you run into "
+ "other problems, please open an issue "
+ "with SpineOpt.