package dependency updates

This commit is contained in:
2026-09-01 18:02:31 -07:00
parent e9ef78c872
commit 4d00ef86e4
4 changed files with 123 additions and 11 deletions
+1
View File
@@ -1184,6 +1184,7 @@ class MainApplication(QMainWindow):
# for stat in stats[:10]: # for stat in stats[:10]:
# print(stat) # print(stat)
# print("Top 10 growing object types in RAM:") # print("Top 10 growing object types in RAM:")
# objgraph has been removed so it will need to be reinstalled for this to work
# objgraph.show_most_common_types(limit=10) # objgraph.show_most_common_types(limit=10)
def check_if_app_is_dirty(self): def check_if_app_is_dirty(self):
+49
View File
@@ -0,0 +1,49 @@
Package Version Latest Type
------------------------- -------- ------------ -----
certifi 2026.1.4 2026.7.22 wheel
charset-normalizer 3.4.4 3.5.1 wheel
decorator 5.2.1 5.3.1 wheel
fonttools 4.61.1 4.64.0 wheel
h5py 3.15.1 3.16.0 wheel
idna 3.11 3.19 wheel
joblib 1.5.3 1.6.0 wheel
kiwisolver 1.4.9 1.5.1 wheel
lazy_loader 0.4 0.5 wheel
lxml 6.0.2 6.1.2 wheel
matplotlib 3.10.8 3.11.1 wheel
mne 1.11.0 1.12.1 wheel
mne-connectivity 0.7.0 0.9.0 wheel
neurokit2 0.2.12 0.2.13 wheel
nibabel 5.3.3 5.4.2 wheel
nilearn 0.13.0 0.14.0 wheel
numpy 2.4.1 2.5.2 wheel
packaging 25.0 26.3 wheel
pandas 2.3.3 3.0.5 wheel
patsy 1.0.2 1.0.3 wheel
pillow 12.1.0 12.3.0 wheel
pip 25.3 26.2.1 wheel
platformdirs 4.5.1 4.11.7 wheel
pooch 1.8.2 1.9.0 wheel
psutil 7.2.1 7.2.2 wheel
pyinstaller 6.19.0 6.22.2 wheel
pyinstaller-hooks-contrib 2026.3 2026.7 wheel
pyparsing 3.3.1 3.3.2 wheel
PySide6 6.10.1 6.11.2 wheel
PySide6_Addons 6.10.1 6.11.2 wheel
PySide6_Essentials 6.10.1 6.11.2 wheel
pytz 2025.2 2026.3.post1 wheel
pyvista 0.46.5 0.48.4 wheel
pyvistaqt 0.11.3 0.12.0 wheel
requests 2.32.5 2.34.2 wheel
scikit-learn 1.8.0 1.9.0 wheel
scipy 1.17.0 1.18.1 wheel
scooby 0.11.0 0.11.2 wheel
setuptools 82.0.1 84.0.0 wheel
shiboken6 6.10.1 6.11.2 wheel
statsmodels 0.14.6 0.15.0 wheel
tqdm 4.67.1 4.70.0 wheel
typing_extensions 4.15.0 4.16.0 wheel
tzdata 2025.3 2026.3 wheel
urllib3 2.6.3 2.7.0 wheel
vtk 9.5.2 9.7.0 wheel
xarray 2026.1.0 2026.7.0 wheel
BIN
View File
Binary file not shown.
+73 -11
View File
@@ -1,6 +1,6 @@
""" """
Filename: plugins.py Filename: plugins.py
Description: Plugins window Description: Plugins window with deferred remote fetching and installation state checks.
Author: Tyler de Zeeuw Author: Tyler de Zeeuw
License: GPL-3.0 License: GPL-3.0
@@ -10,7 +10,7 @@ License: GPL-3.0
from typing import Any from typing import Any
# External library imports # External library imports
from PySide6.QtCore import Qt from PySide6.QtCore import Qt, QThread, Signal
from PySide6.QtGui import QColor from PySide6.QtGui import QColor
from PySide6.QtWidgets import ( from PySide6.QtWidgets import (
QHBoxLayout, QLabel, QLineEdit, QListWidget, QListWidgetItem, QHBoxLayout, QLabel, QLineEdit, QListWidget, QListWidgetItem,
@@ -21,6 +21,20 @@ from plugin_manager import PluginManager, parse_version
from src.shared.shareddata import APP_NAME, CURRENT_VERSION, PLUGINS_URL from src.shared.shareddata import APP_NAME, CURRENT_VERSION, PLUGINS_URL
class RemoteFetchWorker(QThread):
"""Background thread to fetch remote repository data without lagging the main UI."""
fetched = Signal(list, bool) # (plugins_data, all_failed)
def __init__(self, manager: PluginManager, urls: list[str]) -> None:
super().__init__()
self.manager = manager
self.urls = urls
def run(self) -> None:
plugins, all_failed = self.manager.fetch_remote_repositories(self.urls)
self.fetched.emit(plugins, all_failed)
class PluginsWindow(QWidget): class PluginsWindow(QWidget):
def __init__(self, parent: QWidget | None, plugin_manager: PluginManager) -> None: def __init__(self, parent: QWidget | None, plugin_manager: PluginManager) -> None:
@@ -29,12 +43,13 @@ class PluginsWindow(QWidget):
self.main_app_window = parent self.main_app_window = parent
self.manager: PluginManager = plugin_manager self.manager: PluginManager = plugin_manager
self.setWindowTitle(f"{APP_NAME.upper()} - Plugins") self.setWindowTitle(f"Plugins - {APP_NAME.upper()}")
self.resize(750, 500) self.resize(750, 500)
self.repository_urls: list[str] = [PLUGINS_URL] self.repository_urls: list[str] = [PLUGINS_URL]
self.remote_plugins_data: list[dict[str, Any]] = [] self.remote_plugins_data: list[dict[str, Any]] = []
self._has_fetched_remote: bool = False self._has_fetched_remote: bool = False
self._fetch_worker: RemoteFetchWorker | None = None
# Refresh UI automatically if manager updates state # Refresh UI automatically if manager updates state
self.manager.plugins_changed.connect(self.refresh_installed_plugins) self.manager.plugins_changed.connect(self.refresh_installed_plugins)
@@ -74,6 +89,10 @@ class PluginsWindow(QWidget):
self.installed_list.addItem("No plugins installed.") self.installed_list.addItem("No plugins installed.")
self._clear_details_panel() self._clear_details_panel()
# Re-populate browser list if remote data was already fetched to reflect newly installed/uninstalled plugins
if self._has_fetched_remote and self.remote_plugins_data:
self._populate_browser_list()
def _on_installed_item_changed(self, current: QListWidgetItem | None, _: Any) -> None: def _on_installed_item_changed(self, current: QListWidgetItem | None, _: Any) -> None:
"""Updates the right-hand details panel when a plugin is selected.""" """Updates the right-hand details panel when a plugin is selected."""
if not current: if not current:
@@ -136,13 +155,25 @@ class PluginsWindow(QWidget):
self.manager.uninstall_plugin(info["path"]) self.manager.uninstall_plugin(info["path"])
def fetch_remote_plugins(self) -> None: 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():
return # Fetch operation already in progress
self.browser_list.clear() self.browser_list.clear()
self.browser_list.addItem("Fetching remote repositories...") self.browser_list.addItem("Fetching remote repositories...")
self.btn_install.setEnabled(False) self.btn_install.setEnabled(False)
self.btn_fetch.setEnabled(False)
plugins, all_failed = self.manager.fetch_remote_repositories(self.repository_urls) # 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()
def _on_remote_fetched(self, plugins: list[dict[str, Any]], all_failed: bool) -> None:
"""Callback executed on the main UI thread when remote fetching finishes."""
self.remote_plugins_data = plugins self.remote_plugins_data = plugins
self._has_fetched_remote = True self._has_fetched_remote = True
self.btn_fetch.setEnabled(True)
if plugins: if plugins:
self._populate_browser_list() self._populate_browser_list()
@@ -154,9 +185,17 @@ class PluginsWindow(QWidget):
self.browser_list.addItem("No plugins found across configured repositories.") self.browser_list.addItem("No plugins found across configured repositories.")
def _populate_browser_list(self) -> None: def _populate_browser_list(self) -> None:
"""Populates the browser tab list and checks if each plugin is already installed."""
self.browser_list.clear() self.browser_list.clear()
# Gather set of currently installed identifiers (by ID and Name)
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")}
for plugin in self.remote_plugins_data: for plugin in self.remote_plugins_data:
name = plugin.get("name", "Unknown") name = plugin.get("name", "Unknown")
p_id = plugin.get("id", "")
version = plugin.get("version", "v0.0") version = plugin.get("version", "v0.0")
desc = plugin.get("description", "") desc = plugin.get("description", "")
platforms = plugin.get("platforms", []) platforms = plugin.get("platforms", [])
@@ -166,16 +205,24 @@ class PluginsWindow(QWidget):
is_version_ok = self.manager.current_app_version >= parse_version(min_v_str) is_version_ok = self.manager.current_app_version >= parse_version(min_v_str)
is_compatible = is_platform_ok and is_version_ok is_compatible = is_platform_ok and is_version_ok
# Check if plugin is already installed locally
is_installed = (bool(p_id) and p_id in installed_ids) or (bool(name) and name in installed_names)
display_text = f"{name} (v{version}) - {desc}" display_text = f"{name} (v{version}) - {desc}"
if not is_compatible: if is_installed:
display_text += " [Installed]"
elif not is_compatible:
display_text += " [Incompatible]" display_text += " [Incompatible]"
item = QListWidgetItem(display_text, self.browser_list) item = QListWidgetItem(display_text, self.browser_list)
item.setData(Qt.ItemDataRole.UserRole, plugin) item.setData(Qt.ItemDataRole.UserRole, plugin)
item.setData(Qt.ItemDataRole.UserRole + 1, is_compatible) item.setData(Qt.ItemDataRole.UserRole + 1, is_compatible)
item.setData(Qt.ItemDataRole.UserRole + 2, is_installed)
if not is_compatible: if is_installed:
item.setForeground(QColor("#d32f2f")) item.setForeground(QColor("#2e7d32")) # Green for installed
elif not is_compatible:
item.setForeground(QColor("#d32f2f")) # Red for incompatible
def install_selected_plugin(self) -> None: def install_selected_plugin(self) -> None:
selected = self.browser_list.currentItem() selected = self.browser_list.currentItem()
@@ -200,7 +247,21 @@ class PluginsWindow(QWidget):
def _on_browser_item_changed(self, current: QListWidgetItem | None, _: Any) -> None: def _on_browser_item_changed(self, current: QListWidgetItem | None, _: Any) -> None:
if current: if current:
self.btn_install.setEnabled(bool(current.data(Qt.ItemDataRole.UserRole + 1))) is_compatible = bool(current.data(Qt.ItemDataRole.UserRole + 1))
is_installed = bool(current.data(Qt.ItemDataRole.UserRole + 2))
# Disable install button if plugin is incompatible OR ALREADY INSTALLED
self.btn_install.setEnabled(is_compatible and not is_installed)
else:
self.btn_install.setEnabled(False)
def _on_add_repo(self) -> None:
"""Adds a custom repository URL and re-fetches plugins."""
url = self.repo_input.text().strip()
if url and url not in self.repository_urls:
self.repository_urls.append(url)
self.repo_input.clear()
self.fetch_remote_plugins()
def _create_installed_tab(self) -> QWidget: def _create_installed_tab(self) -> QWidget:
tab = QWidget() tab = QWidget()
@@ -284,6 +345,7 @@ class PluginsWindow(QWidget):
self.repo_input = QLineEdit(tab) self.repo_input = QLineEdit(tab)
self.repo_input.setPlaceholderText("Enter custom plugins.json URL...") self.repo_input.setPlaceholderText("Enter custom plugins.json URL...")
btn_add = QPushButton("Add Repo", tab) btn_add = QPushButton("Add Repo", tab)
btn_add.clicked.connect(self._on_add_repo)
repo_layout.addWidget(self.repo_input) repo_layout.addWidget(self.repo_input)
repo_layout.addWidget(btn_add) repo_layout.addWidget(btn_add)
@@ -292,14 +354,14 @@ class PluginsWindow(QWidget):
self.browser_list.currentItemChanged.connect(self._on_browser_item_changed) self.browser_list.currentItemChanged.connect(self._on_browser_item_changed)
btn_layout = QHBoxLayout() btn_layout = QHBoxLayout()
btn_fetch = QPushButton("Fetch Remote Lists", tab) self.btn_fetch = QPushButton("Fetch Remote Lists", tab)
self.btn_install = QPushButton("Install Plugin", tab) self.btn_install = QPushButton("Install Plugin", tab)
self.btn_install.setEnabled(False) self.btn_install.setEnabled(False)
btn_fetch.clicked.connect(self.fetch_remote_plugins) self.btn_fetch.clicked.connect(self.fetch_remote_plugins)
self.btn_install.clicked.connect(self.install_selected_plugin) self.btn_install.clicked.connect(self.install_selected_plugin)
btn_layout.addWidget(btn_fetch) btn_layout.addWidget(self.btn_fetch)
btn_layout.addStretch() btn_layout.addStretch()
btn_layout.addWidget(self.btn_install) btn_layout.addWidget(self.btn_install)