{path}")
+ self.btn_update_plugin.setEnabled(update_available)
- def _clear_details_panel(self) -> None:
- """Clears details display when nothing is selected."""
- self.lbl_plugin_title.setText("Select a plugin")
- self.lbl_plugin_meta.setText("")
- self.lbl_plugin_status.setText("")
- self.txt_plugin_desc.clear()
- self.lbl_plugin_path.setText("")
+ def execute_plugin_installation(self, download_url: str, plugin_id: str, plugin_name: str, is_update: bool, plugin_path: Path | None) -> None:
+ """Sets up the progress dialog and starts the background installation/update worker."""
+ if self._install_worker is not None and self._install_worker.isRunning():
+ return
- def toggle_enable_disable(self) -> None:
+ self._installation_finished_flag = False
+ action_label = "Updating" if is_update else "Installing"
+
+ self._progress_dialog = QProgressDialog(f"{action_label} '{plugin_name}'...", "Cancel", 0, 100, self)
+ self._progress_dialog.setMinimumDuration(0) # Show the progress dialog instantly
+ self._progress_dialog.setWindowTitle(f"{action_label} Plugin")
+ self._progress_dialog.setWindowModality(Qt.WindowModality.WindowModal)
+ self._progress_dialog.setAutoClose(False)
+ self._progress_dialog.setAutoReset(False)
+ self._progress_dialog.canceled.connect(self._cancel_installation)
+ self._progress_dialog.show()
+
+ self._install_worker = PluginInstallWorker(self.manager, download_url, plugin_id, is_update, plugin_path)
+ self._install_worker.progress.connect(self._on_install_progress)
+ self._install_worker.status_message.connect(self._on_install_status)
+ self._install_worker.finished.connect(lambda success, msg: self._on_install_finished(success, msg, plugin_name, is_update))
+ self._install_worker.start()
+
+
+ def _cancel_installation(self) -> None:
+ """Handles manual user cancellation of the download/install progress."""
+ if getattr(self, "_installation_finished_flag", False):
+ return # Ignore if triggered by closing the dialog programmatically on success
+
+ if self._install_worker and self._install_worker.isRunning():
+ self._install_worker.terminate()
+ self._install_worker.wait()
+ QMessageBox.warning(self, "Cancelled", "Plugin installation was cancelled.")
+
+ def _on_install_finished(self, success: bool, message: str, plugin_name: str, is_update: bool) -> None:
+ self._installation_finished_flag = True # Prevent false cancellation triggers
+
+ if self._progress_dialog:
+ self._progress_dialog.close()
+ self._progress_dialog.deleteLater()
+ self._progress_dialog = None
+
+ action_word = "updated" if is_update else "installed"
+ action_verb = "update" if is_update else "install"
+
+ if success:
+ self.manager.reload_plugins()
+ QMessageBox.information(self, "Success", f"Successfully {action_word} '{plugin_name}'!")
+ self.refresh_installed_plugins()
+ if self._has_fetched_remote:
+ self._populate_browser_list()
+ else:
+ QMessageBox.critical(self, "Installation Failed", f"Could not {action_verb} plugin:\n{message}")
+
+ def _on_install_progress(self, downloaded: int, total: int) -> None:
+ if self._progress_dialog:
+ if total > 0:
+ self._progress_dialog.setMaximum(total)
+ self._progress_dialog.setValue(downloaded)
+ mb_down = downloaded / (1024 * 1024)
+ mb_total = total / (1024 * 1024)
+ self._progress_dialog.setLabelText(f"Downloading... ({mb_down:.2f} MB / {mb_total:.2f} MB)")
+ else:
+ self._progress_dialog.setMaximum(0) # Infinite marquee progress if size is unknown
+ mb_down = downloaded / (1024 * 1024)
+ self._progress_dialog.setLabelText(f"Downloading... ({mb_down:.2f} MB downloaded)")
+
+ def _on_install_status(self, message: str) -> None:
+ if self._progress_dialog:
+ self._progress_dialog.setLabelText(message)
+
+
+ def update_selected_plugin(self) -> None:
+ """Triggers the background update process for the selected installed plugin."""
selected = self.installed_list.currentItem()
if not selected:
return
raw_info = selected.data(Qt.ItemDataRole.UserRole)
- if isinstance(raw_info, dict):
- info = cast(dict[str, Any], raw_info)
- plugin_path = info.get("path")
- if isinstance(plugin_path, str) and plugin_path:
- self.manager.toggle_plugin_state(Path(plugin_path))
-
- def uninstall_plugin(self) -> None:
- selected = self.installed_list.currentItem()
- if not selected:
- return
-
- raw_info = selected.data(Qt.ItemDataRole.UserRole)
if not isinstance(raw_info, dict):
return
info = cast(dict[str, Any], raw_info)
+ download_url = info.get("download_url", "")
+ plugin_id = info.get("id", "unnamed")
+ plugin_name = info.get("name", "Plugin")
plugin_path = info.get("path")
- plugin_name = info.get("name", "this plugin")
- if not isinstance(plugin_path, str) or not plugin_path:
+ if not download_url:
+ QMessageBox.warning(self, "Update Failed", "No download URL provided for this update.")
return
- reply = QMessageBox.question(
- self,
- "Confirm Uninstall",
- f"Are you sure you want to delete '{plugin_name}'?",
- QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
- )
- if reply == QMessageBox.StandardButton.Yes:
- self.manager.uninstall_plugin(Path(plugin_path))
+ path_obj = Path(plugin_path) if isinstance(plugin_path, str) and plugin_path else (plugin_path if isinstance(plugin_path, Path) else None)
+ self.execute_plugin_installation(download_url, plugin_id, plugin_name, is_update=True, plugin_path=path_obj)
- def fetch_remote_plugins(self) -> None:
- """Asynchronously fetches remote plugins on a background thread."""
- if self._fetch_worker is not None and self._fetch_worker.isRunning():
+ def install_selected_plugin(self) -> None:
+ """Triggers the background installation/update process from the browser tab."""
+ selected = self.browser_list.currentItem()
+ if not selected:
+ return
+ data = selected.data(Qt.ItemDataRole.UserRole)
+ if not isinstance(data, dict):
return
- self.browser_list.clear()
- self.browser_list.addItem("Fetching remote repositories...")
- self.btn_install.setEnabled(False)
- self.btn_fetch.setEnabled(False)
+ plugin_data = cast(dict[str, Any], data)
- # Defer network call to background worker to avoid UI freeze
- self._fetch_worker = RemoteFetchWorker(self.manager, self.repository_urls)
- self._fetch_worker.fetched.connect(self._on_remote_fetched)
- self._fetch_worker.start()
+ update_available = bool(selected.data(Qt.ItemDataRole.UserRole + 3))
+ plugin_id = str(plugin_data.get("id", "unnamed"))
+ plugin_name = str(plugin_data.get("name", "Plugin"))
+ download_url = str(plugin_data.get("download_url", ""))
- def _on_remote_fetched(self, plugins: list[dict[str, Any]], all_failed: bool) -> None:
+ if not download_url:
+ QMessageBox.warning(self, "Error", "No download URL provided for this plugin.")
+ return
+
+ # Find existing local path if updating an installed plugin
+ existing_path: Path | None = None
+ installed_info = self.manager.get_installed_plugins_info()
+ for info in installed_info:
+ if info.get("id") == plugin_id or info.get("name") == plugin_name:
+ p = info.get("path")
+ if isinstance(p, Path):
+ existing_path = p
+ elif isinstance(p, str):
+ existing_path = Path(p)
+ break
+
+ self.execute_plugin_installation(download_url, plugin_id, plugin_name, is_update=update_available, plugin_path=existing_path)
+
+ def _on_remote_fetched(self, plugins: list[dict[str, Any]], all_failed: bool, fetch_type: str) -> None:
"""Callback executed on the main UI thread when remote fetching finishes."""
self.remote_plugins_data = plugins
self._has_fetched_remote = True
self.btn_fetch.setEnabled(True)
+ if fetch_type == "updates":
+ self.refresh_installed_plugins()
+ return
+
if plugins:
self._populate_browser_list()
elif all_failed:
@@ -200,13 +388,33 @@ class PluginsWindow(QWidget):
self.browser_list.clear()
self.browser_list.addItem("No plugins found across configured repositories.")
+ def fetch_remote_plugins(self) -> None:
+ """Asynchronously fetches remote plugins on a background thread for the browser."""
+ if self._fetch_worker is not None and self._fetch_worker.isRunning():
+ return
+
+ self.browser_list.clear()
+ self.browser_list.addItem("Fetching remote repositories...")
+ self.btn_install.setEnabled(False)
+ self.btn_fetch.setEnabled(False)
+
+ self._fetch_worker = RemoteFetchWorker(self.manager, self.repository_urls, fetch_type="browser")
+ self._fetch_worker.fetched.connect(self._on_remote_fetched)
+ self._fetch_worker.start()
+
def _populate_browser_list(self) -> None:
- """Populates the browser tab list and evaluates compatibility and installation state."""
+ """Populates the browser tab list and evaluates compatibility, installation, and updates."""
self.browser_list.clear()
installed_info = self.manager.get_installed_plugins_info()
- installed_ids = {p.get("id") for p in installed_info if p.get("id")}
- installed_names = {p.get("name") for p in installed_info if p.get("name")}
+ installed_map: dict[str, dict[str, Any]] = {}
+ for p in installed_info:
+ p_id = p.get("id")
+ p_name = p.get("name")
+ if p_id:
+ installed_map[str(p_id)] = p
+ if p_name:
+ installed_map[str(p_name)] = p
for plugin in self.remote_plugins_data:
name = plugin.get("name", "Unknown")
@@ -220,7 +428,6 @@ class PluginsWindow(QWidget):
is_version_ok = self.manager.current_app_version >= parse_version(min_v_str)
is_compatible = is_platform_ok and is_version_ok
- # Evaluate reasons for incompatibility
incompat_reasons: list[str] = []
if not is_platform_ok:
plat_str = ", ".join(platforms) if isinstance(platforms, list) else str(platforms)
@@ -228,10 +435,20 @@ class PluginsWindow(QWidget):
if not is_version_ok:
incompat_reasons.append(f"Requires App v{min_v_str}+")
- is_installed = (bool(p_id) and p_id in installed_ids) or (bool(name) and name in installed_names)
+ installed_match = installed_map.get(str(p_id)) or installed_map.get(str(name))
+ is_installed = installed_match is not None
+
+ update_available = False
+ remote_ver_str = str(plugin.get("version", "0.0.0"))
+ if is_installed:
+ local_ver_str = str(installed_match.get("version", "0.0.0"))
+ if parse_version(remote_ver_str) > parse_version(local_ver_str):
+ update_available = True
display_text = f"{name} (v{version}) - {desc}"
- if is_installed:
+ if update_available:
+ display_text += f" [Update Available: v{remote_ver_str}]"
+ elif is_installed:
display_text += " [Installed]"
elif incompat_reasons:
reason_str = "; ".join(incompat_reasons)
@@ -241,28 +458,14 @@ class PluginsWindow(QWidget):
item.setData(Qt.ItemDataRole.UserRole, plugin)
item.setData(Qt.ItemDataRole.UserRole + 1, is_compatible)
item.setData(Qt.ItemDataRole.UserRole + 2, is_installed)
+ item.setData(Qt.ItemDataRole.UserRole + 3, update_available)
- if is_installed:
- item.setForeground(QColor("#2e7d32")) # Green for installed
+ if update_available:
+ item.setForeground(QColor("#1976d2"))
+ elif is_installed:
+ item.setForeground(QColor("#2e7d32"))
elif not is_compatible:
- item.setForeground(QColor("#d32f2f")) # Red for incompatible
-
- def install_selected_plugin(self) -> None:
- selected = self.browser_list.currentItem()
- if not selected:
- return
- data = selected.data(Qt.ItemDataRole.UserRole)
- if not data:
- return
-
- try:
- target_path = self.manager.install_plugin_from_url(
- data.get("download_url", ""),
- data.get("id", "unnamed")
- )
- QMessageBox.information(self, "Success", f"Installed plugin to:\n{target_path}")
- except Exception as e:
- QMessageBox.critical(self, "Installation Failed", f"Could not install plugin:\n{e}")
+ item.setForeground(QColor("#d32f2f"))
def _on_tab_changed(self, index: int) -> None:
if index == 1 and not self._has_fetched_remote:
@@ -272,10 +475,19 @@ class PluginsWindow(QWidget):
if current:
is_compatible = bool(current.data(Qt.ItemDataRole.UserRole + 1))
is_installed = bool(current.data(Qt.ItemDataRole.UserRole + 2))
-
- # Button lights up ONLY if plugin is compatible and NOT yet installed
- self.btn_install.setEnabled(is_compatible and not is_installed)
+ update_available = bool(current.data(Qt.ItemDataRole.UserRole + 3))
+
+ if update_available:
+ self.btn_install.setText("Update Plugin")
+ self.btn_install.setEnabled(is_compatible)
+ elif is_installed:
+ self.btn_install.setText("Install Plugin")
+ self.btn_install.setEnabled(False)
+ else:
+ self.btn_install.setText("Install Plugin")
+ self.btn_install.setEnabled(is_compatible)
else:
+ self.btn_install.setText("Install Plugin")
self.btn_install.setEnabled(False)
def _on_add_repo(self) -> None:
@@ -334,14 +546,19 @@ class PluginsWindow(QWidget):
btn_layout = QHBoxLayout()
btn_refresh = QPushButton("Refresh List", tab)
+ self.btn_update_plugin = QPushButton("Update Plugin", tab)
+ self.btn_update_plugin.setEnabled(False)
+ self.btn_update_plugin.setStyleSheet("font-weight: bold; color: #1976d2;")
btn_enable = QPushButton("Enable / Disable", tab)
btn_uninstall = QPushButton("Uninstall", tab)
btn_refresh.clicked.connect(self.refresh_installed_plugins)
+ self.btn_update_plugin.clicked.connect(self.update_selected_plugin)
btn_enable.clicked.connect(self.toggle_enable_disable)
btn_uninstall.clicked.connect(self.uninstall_plugin)
btn_layout.addWidget(btn_refresh)
+ btn_layout.addWidget(self.btn_update_plugin)
btn_layout.addStretch()
btn_layout.addWidget(btn_enable)
btn_layout.addWidget(btn_uninstall)
@@ -388,4 +605,54 @@ class PluginsWindow(QWidget):
layout.addLayout(repo_layout)
layout.addWidget(self.browser_list)
layout.addLayout(btn_layout)
- return tab
\ No newline at end of file
+ return tab
+
+ def toggle_enable_disable(self) -> None:
+ selected = self.installed_list.currentItem()
+ if not selected:
+ return
+ raw_info = selected.data(Qt.ItemDataRole.UserRole)
+ if isinstance(raw_info, dict):
+ info = cast(dict[str, Any], raw_info)
+ plugin_path = info.get("path")
+ if isinstance(plugin_path, str) and plugin_path:
+ self.manager.toggle_plugin_state(Path(plugin_path))
+ elif isinstance(plugin_path, Path):
+ self.manager.toggle_plugin_state(plugin_path)
+
+ def uninstall_plugin(self) -> None:
+ selected = self.installed_list.currentItem()
+ if not selected:
+ return
+
+ raw_info = selected.data(Qt.ItemDataRole.UserRole)
+ if not isinstance(raw_info, dict):
+ return
+
+ info = cast(dict[str, Any], raw_info)
+ plugin_path = info.get("path")
+ plugin_name = info.get("name", "this plugin")
+
+ if not plugin_path:
+ return
+
+ reply = QMessageBox.question(
+ self,
+ "Confirm Uninstall",
+ f"Are you sure you want to delete '{plugin_name}'?",
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+ )
+ if reply == QMessageBox.StandardButton.Yes:
+ if isinstance(plugin_path, str):
+ self.manager.uninstall_plugin(Path(plugin_path))
+ elif isinstance(plugin_path, Path):
+ self.manager.uninstall_plugin(plugin_path)
+
+
+ def _clear_details_panel(self) -> None:
+ """Clears details display when nothing is selected."""
+ self.lbl_plugin_title.setText("Select a plugin")
+ self.lbl_plugin_meta.setText("")
+ self.lbl_plugin_status.setText("")
+ self.txt_plugin_desc.clear()
+ self.lbl_plugin_path.setText("")
\ No newline at end of file
diff --git a/src/window/terminal.py b/src/window/terminal.py
index 66b2775..46a7f6c 100644
--- a/src/window/terminal.py
+++ b/src/window/terminal.py
@@ -33,11 +33,11 @@ class _AssocWorker(QThread):
window. Used for the non-elevated path too for consistency.
"""
result_ready = Signal(bool, str)
-
+
def __init__(self, force_admin: bool, parent: QWidget | None = None) -> None:
super().__init__(parent)
self._force_admin = force_admin
-
+
def run(self) -> None:
ok, msg = register_file_association(force_admin=self._force_admin)
self.result_ready.emit(ok, msg)
@@ -92,7 +92,7 @@ class TerminalWindow(QWidget):
self._pending_assoc_confirmation = False
self._handle_assoc_confirmation(command_text.strip().lower())
return
-
+
parts = command_text.strip().split()
if not parts:
return
@@ -118,10 +118,10 @@ class TerminalWindow(QWidget):
def cmd_help(self, *args: Any) -> str:
available_cmds = [cmd for cmd in self.commands.keys() if cmd != "utest"]
return f"Available commands: {', '.join(available_cmds)}"
-
+
def cmd_version(self, *args: Any) -> str:
return f"{APP_NAME.upper()} is running version {CURRENT_VERSION}."
-
+
def cmd_about(self, *args: Any) -> None:
self.about = AboutWindow(self)
self.about.show()
@@ -134,7 +134,7 @@ class TerminalWindow(QWidget):
main_win = self.parent()
if not isinstance(main_win, QMainWindow):
return "[Error] Main window context not found."
-
+
self.updater = UpdateManager(
main_window=main_win,
api_url=API_URL,
@@ -145,7 +145,7 @@ class TerminalWindow(QWidget):
app_name=APP_NAME
)
self.output_area.append("Checking for updates...")
-
+
self.updater.manual_check_for_updates()
return "See status bar for update information."
@@ -155,10 +155,10 @@ class TerminalWindow(QWidget):
if PLATFORM_NAME != "windows" or is_windows_admin():
self._run_assoc(force_admin=False)
return None
-
+
self._pending_assoc_confirmation = True
return "Not running as admin. Register system-wide via UAC elevation? (y/n)"
-
+
def _handle_assoc_confirmation(self, answer: str) -> None:
if answer in ("y", "yes"):
self._run_assoc(force_admin=True)
@@ -166,15 +166,15 @@ class TerminalWindow(QWidget):
self._run_assoc(force_admin=False)
else:
self.output_area.append("Please answer 'y' or 'n'. Run 'assoc' again to retry.")
-
+
def _run_assoc(self, force_admin: bool) -> None:
if force_admin:
self.output_area.append("Requesting elevation. Check for a UAC prompt...")
-
+
self._assoc_worker = _AssocWorker(force_admin=force_admin, parent=self)
self._assoc_worker.result_ready.connect(self._on_assoc_result)
self._assoc_worker.start()
-
+
def _on_assoc_result(self, ok: bool, msg: str) -> None:
self.output_area.append(msg)
self._assoc_worker = None
@@ -190,7 +190,7 @@ class TerminalWindow(QWidget):
return f"[Error] Target script not found at: {target_script}"
self._process = QProcess(self)
-
+
# Stream stdout and stderr live to output_area
self._process.readyReadStandardOutput.connect(self._handle_stdout)
self._process.readyReadStandardError.connect(self._handle_stderr)
@@ -198,7 +198,7 @@ class TerminalWindow(QWidget):
# Use current Python interpreter executable. Works when packaged?
python_executable = sys.executable
-
+
self.output_area.append(f"Starting {target_script.name}...")
self._process.start(python_executable, [str(target_script)])
return None
diff --git a/src/window/updateevents.py b/src/window/updateevents.py
index c251ac3..1b2994d 100644
--- a/src/window/updateevents.py
+++ b/src/window/updateevents.py
@@ -102,7 +102,7 @@ def _align_boris_and_write_snirf_worker(
for raw_event in boris_events:
if not isinstance(raw_event, (list, tuple)):
continue
-
+
event = cast(Sequence[Any], raw_event)
if len(event) < 3:
@@ -179,7 +179,7 @@ def align_boris_and_write_snirf_isolated(
time_shift
)
return future.result()
-
+
class EventUpdateMode(Enum):
WRITE_SNIRF = auto() # destructive
@@ -217,7 +217,7 @@ class UpdateEventsWindow(QWidget):
self.label_events = QLabel("Events in selected observation:")
self.combo_events = QComboBox()
self.combo_events.setEnabled(False)
-
+
self.label_snirf_events = QLabel("Events in SNIRF file:")
self.combo_snirf_events = QComboBox()
self.combo_snirf_events.setEnabled(False)
@@ -227,13 +227,13 @@ class UpdateEventsWindow(QWidget):
self.btn_clear.clicked.connect(self.clear_files)
self.btn_go.clicked.connect(self.go_action)
- # ---
+ # ---
layout = QVBoxLayout()
self.description = QLabel()
self.description.setTextFormat(Qt.TextFormat.RichText)
self.description.setTextInteractionFlags(Qt.TextInteractionFlag.TextBrowserInteraction)
self.description.setOpenExternalLinks(True)
-
+
self.description.setText("The events that are present in a snirf file may not be the events that are to be studied and examined.Loading latest updates from server...
") @@ -49,17 +49,17 @@ class WelcomeDialog(QDialog): self.text_browser.setOpenLinks(False) self.text_browser.anchorClicked.connect(QDesktopServices.openUrl) layout.addWidget(self.text_browser) - + footer_layout = QHBoxLayout() - + ok_button = QPushButton("OK", self) ok_button.setDefault(True) ok_button.clicked.connect(self.accept) - + footer_layout.addStretch() footer_layout.addWidget(ok_button) layout.addLayout(footer_layout) - + # Fetch markdown from the web asynchronously self.network_manager = QNetworkAccessManager(self) self.network_manager.finished.connect(self._on_download_complete) diff --git a/startup_args.py b/startup_args.py index 3432c3e..1b0217d 100644 --- a/startup_args.py +++ b/startup_args.py @@ -30,7 +30,7 @@ def parse_startup_args(argv: list[str]) -> StartupArgs: a fixed position, so it survives regardless of whether --finish-update precedes it. """ - + finish_update = "--finish-update" in argv[1:] initial_file = None diff --git a/updater.py b/updater.py index 73aed27..15f80b8 100644 --- a/updater.py +++ b/updater.py @@ -37,7 +37,7 @@ class UpdateDownloadThread(QThread): download_url (str): URL of the update zip file to download. latest_version (str): Version string of the latest update. """ - + update_ready = Signal(str, str) error_occurred = Signal(str) @@ -48,7 +48,7 @@ class UpdateDownloadThread(QThread): platform_name: str, app_name: str, ) -> None: - + super().__init__() self.download_url = download_url self.latest_version = latest_version @@ -124,7 +124,7 @@ class UpdateCheckThread(QThread): platform_name: str, app_name: str, ) -> None: - + super().__init__() self.api_url = api_url self.api_url_sec = api_url_sec @@ -171,7 +171,7 @@ class UpdateCheckThread(QThread): if not latest: continue - + tag = latest["tag_name"].lstrip("v") for asset in latest.get("assets", []): @@ -203,7 +203,7 @@ class LocalPendingUpdateCheckThread(QThread): platform_name: str, app_name: str, ) -> None: - + super().__init__() self.current_version = current_version self.platform_suffix = platform_suffix @@ -237,7 +237,7 @@ class LocalPendingUpdateCheckThread(QThread): break except Exception: pass - + if not found: self.no_pending_update.emit() @@ -247,7 +247,7 @@ class UpdateManager(QObject): Orchestrates the update process. Main apps should instantiate this and call check_for_updates(). """ - + def __init__( self, main_window: QMainWindow, @@ -258,7 +258,7 @@ class UpdateManager(QObject): platform_suffix: str, app_name: str, ) -> None: - + super().__init__(main_window) self.main_window: QMainWindow = main_window self.api_url = api_url @@ -267,7 +267,7 @@ class UpdateManager(QObject): self.platform_name = platform_name self.platform_suffix = platform_suffix self.app_name = app_name - + self.pending_update_version: Optional[str] = None self.pending_update_path: Optional[str] = None @@ -331,7 +331,7 @@ class UpdateManager(QObject): except Exception as e: if self.main_window.statusBar(): self.main_window.statusBar().showMessage(f"Failed to delete old update folder: {e}") - + # Clear pending update info so new download proceeds self.pending_update_version = None self.pending_update_path = None @@ -471,7 +471,7 @@ def finish_update_if_needed(platform_name: str, app_name: str, cfg_path: str, fi return print("Finishing update...") - + # 1. Reset welcome dialog config flag update_cfg = configparser.ConfigParser() try: @@ -480,7 +480,7 @@ def finish_update_if_needed(platform_name: str, app_name: str, cfg_path: str, fi if not update_cfg.has_section("Options"): update_cfg.add_section("Options") update_cfg.set("Options", "show_welcome_dialog", "true") - + with open(cfg_path, "w") as f: update_cfg.write(f) print("Welcome dialog flag successfully reset to 'true' for next run.")