pylance fixes and terminal plugin launching
This commit is contained in:
@@ -1337,7 +1337,7 @@ class MainApplication(QMainWindow):
|
|||||||
|
|
||||||
def terminal_gui(self):
|
def terminal_gui(self):
|
||||||
if self.terminal is None or not self.terminal.isVisible():
|
if self.terminal is None or not self.terminal.isVisible():
|
||||||
self.terminal = TerminalWindow(self)
|
self.terminal = TerminalWindow(self, self.plugin_manager)
|
||||||
self.terminal.show()
|
self.terminal.show()
|
||||||
|
|
||||||
def update_optode_positions(self):
|
def update_optode_positions(self):
|
||||||
|
|||||||
+8
-4
@@ -1,6 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
Filename: plugin_manager.py
|
Filename: plugin_manager.py
|
||||||
Description: Manager file for anything plugin related
|
Description: Manager file for anything plugin related
|
||||||
|
Note: Compliant with pylance strict type checking
|
||||||
|
|
||||||
Author: Tyler de Zeeuw
|
Author: Tyler de Zeeuw
|
||||||
License: GPL-3.0
|
License: GPL-3.0
|
||||||
@@ -117,7 +118,7 @@ class PluginManager(QObject):
|
|||||||
|
|
||||||
print(f"[PluginManager] {summary_msg}")
|
print(f"[PluginManager] {summary_msg}")
|
||||||
|
|
||||||
parent_widget = getattr(self, "main_window", None) or self
|
parent_widget = getattr(self, "main_window", None)
|
||||||
QMessageBox.warning(
|
QMessageBox.warning(
|
||||||
parent_widget,
|
parent_widget,
|
||||||
"Plugin Load Failures",
|
"Plugin Load Failures",
|
||||||
@@ -166,7 +167,9 @@ class PluginManager(QObject):
|
|||||||
for action in menubar.actions():
|
for action in menubar.actions():
|
||||||
clean_text = action.text().replace("&", "").strip().lower()
|
clean_text = action.text().replace("&", "").strip().lower()
|
||||||
if clean_text == "plugins":
|
if clean_text == "plugins":
|
||||||
plugins_menu = action.menu()
|
menu = action.menu()
|
||||||
|
if isinstance(menu, QMenu):
|
||||||
|
plugins_menu = menu
|
||||||
break
|
break
|
||||||
|
|
||||||
# If it doesn't exist yet, create it
|
# If it doesn't exist yet, create it
|
||||||
@@ -191,8 +194,9 @@ class PluginManager(QObject):
|
|||||||
plugins_menu.addSeparator()
|
plugins_menu.addSeparator()
|
||||||
manager_action = plugins_menu.addAction("Manage Plugins...")
|
manager_action = plugins_menu.addAction("Manage Plugins...")
|
||||||
|
|
||||||
if hasattr(self.main_window, "plugins_gui") and callable(self.main_window.plugins_gui):
|
plugins_gui_func = getattr(self.main_window, "plugins_gui", None)
|
||||||
manager_action.triggered.connect(self.main_window.plugins_gui)
|
if callable(plugins_gui_func):
|
||||||
|
manager_action.triggered.connect(plugins_gui_func)
|
||||||
|
|
||||||
def unload_plugins(self) -> None:
|
def unload_plugins(self) -> None:
|
||||||
"""Clears current active plugin instances."""
|
"""Clears current active plugin instances."""
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
src\analysis\participantfoldchannels.py 158
|
src\analysis\participantfoldchannels.py 84
|
||||||
src\shared\flaresbasewidget.py 1155
|
src\shared\flaresbasewidget.py 1155
|
||||||
flares.py 2900
|
flares.py 2900
|
||||||
main_unit_tests.py 152
|
main_unit_tests.py 152
|
||||||
|
|||||||
@@ -8,29 +8,31 @@ License: GPL-3.0
|
|||||||
|
|
||||||
# Built-in Imports
|
# Built-in Imports
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
from multiprocessing import Process, current_process, Manager
|
from multiprocessing import Process, current_process, Manager
|
||||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
||||||
|
|
||||||
# External library imports
|
# External library imports
|
||||||
from matplotlib.backend_bases import Event
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from pandas import DataFrame
|
||||||
|
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
import matplotlib.image as mpimg
|
import matplotlib.image as mpimg
|
||||||
from matplotlib.figure import Figure
|
from matplotlib.figure import Figure
|
||||||
|
from matplotlib.backend_bases import Event
|
||||||
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas
|
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas
|
||||||
|
|
||||||
from PySide6.QtWidgets import QFrame, QGridLayout, QHBoxLayout, QLabel, QLayout, QProgressBar, QPushButton, QScrollArea, QSizePolicy, QWidget, QDialog, QVBoxLayout
|
from PySide6.QtWidgets import QFrame, QGridLayout, QHBoxLayout, QLabel, QLayout, QProgressBar, QPushButton, QScrollArea, QSizePolicy, QWidget, QDialog, QVBoxLayout
|
||||||
from PySide6.QtCore import QThread, Qt, QSize, QTimer, QObject, Signal
|
from PySide6.QtCore import QThread, Qt, QSize, QTimer, QObject, Signal
|
||||||
from PySide6.QtGui import QCloseEvent, QMouseEvent, QPixmap, QImage
|
from PySide6.QtGui import QCloseEvent, QMouseEvent, QPixmap, QImage
|
||||||
from pandas import DataFrame
|
|
||||||
|
|
||||||
from mne.io.base import BaseRaw
|
from mne.io.base import BaseRaw
|
||||||
from src.shared.flaresbasewidget import FlaresBaseWidget
|
from src.shared.flaresbasewidget import FlaresBaseWidget
|
||||||
from src.shared.shareddata import APP_NAME, resource_path
|
from src.shared.shareddata import APP_NAME, resource_path
|
||||||
|
from flares import fold_channels
|
||||||
|
|
||||||
|
|
||||||
class MultiProgressDialog(QDialog):
|
class MultiProgressDialog(QDialog):
|
||||||
@@ -82,7 +84,6 @@ def single_participant_worker(
|
|||||||
""" Runs inside its own dedicated process """
|
""" Runs inside its own dedicated process """
|
||||||
p_name = os.path.basename(file_path)
|
p_name = os.path.basename(file_path)
|
||||||
try:
|
try:
|
||||||
from flares import fold_channels
|
|
||||||
# Perform the heavy fold_channels logic
|
# Perform the heavy fold_channels logic
|
||||||
channel_results = fold_channels(raw=raw_data, p_name=p_name, progress_queue=progress_queue)
|
channel_results = fold_channels(raw=raw_data, p_name=p_name, progress_queue=progress_queue)
|
||||||
|
|
||||||
@@ -284,7 +285,7 @@ class StaticChannelCanvas(FigureCanvas):
|
|||||||
|
|
||||||
|
|
||||||
class StandaloneLegendDialog(QWidget):
|
class StandaloneLegendDialog(QWidget):
|
||||||
def __init__(self, canvas_engine, title_prefix, parent=None):
|
def __init__(self, canvas_engine: Any, title_prefix: str, parent: QWidget | None = None) -> None:
|
||||||
super().__init__(None)
|
super().__init__(None)
|
||||||
self.setWindowTitle("Full View - Brodmann Legend")
|
self.setWindowTitle("Full View - Brodmann Legend")
|
||||||
self.setMinimumSize(500, 600)
|
self.setMinimumSize(500, 600)
|
||||||
@@ -302,7 +303,14 @@ class InteractiveParticipantGridCanvas(FigureCanvas):
|
|||||||
"""The Big Grid Canvas.
|
"""The Big Grid Canvas.
|
||||||
Dynamically scales row and column configurations to maintain a crisp 16:9 layout orientation.
|
Dynamically scales row and column configurations to maintain a crisp 16:9 layout orientation.
|
||||||
"""
|
"""
|
||||||
def __init__(self, channels_data, color_map, is_fullscreen_copy=False, parent=None):
|
def __init__(
|
||||||
|
self,
|
||||||
|
channels_data: dict[str, list[dict[str, Any]]],
|
||||||
|
color_map: dict[str, str],
|
||||||
|
is_fullscreen_copy: bool = False,
|
||||||
|
parent: QWidget | None = None
|
||||||
|
) -> None:
|
||||||
|
|
||||||
self.channels_data = channels_data
|
self.channels_data = channels_data
|
||||||
self.color_map = color_map
|
self.color_map = color_map
|
||||||
self.is_fullscreen_copy = is_fullscreen_copy
|
self.is_fullscreen_copy = is_fullscreen_copy
|
||||||
@@ -494,7 +502,7 @@ class InteractiveParticipantGridCanvas(FigureCanvas):
|
|||||||
self._fullscreen_refs.append(fullscreen_window)
|
self._fullscreen_refs.append(fullscreen_window)
|
||||||
|
|
||||||
|
|
||||||
def _calculate_total_brodmann_profile(self, channels_data: Dict[str, Any]):
|
def _calculate_total_brodmann_profile(self, channels_data: dict[str, list[dict[str, Any]]]) -> list[dict[str, Any]]:
|
||||||
"""Sums and normalizes the specificity profile across all channels."""
|
"""Sums and normalizes the specificity profile across all channels."""
|
||||||
totals = {}
|
totals = {}
|
||||||
num_channels = len(channels_data)
|
num_channels = len(channels_data)
|
||||||
@@ -503,7 +511,7 @@ class InteractiveParticipantGridCanvas(FigureCanvas):
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
# Sum up specificities across all channels
|
# Sum up specificities across all channels
|
||||||
for channel_name, data_list in channels_data.items():
|
for _, data_list in channels_data.items():
|
||||||
for entry in data_list:
|
for entry in data_list:
|
||||||
landmark = entry['Landmark']
|
landmark = entry['Landmark']
|
||||||
specificity = entry['Specificity']
|
specificity = entry['Specificity']
|
||||||
@@ -532,7 +540,7 @@ class InteractiveParticipantGridCanvas(FigureCanvas):
|
|||||||
|
|
||||||
return normalized_data_list
|
return normalized_data_list
|
||||||
|
|
||||||
def _open_expanded_view(self, channel_name, data_list):
|
def _open_expanded_view(self, channel_name: str, data_list: list[dict[str, Any]]) -> None:
|
||||||
# 1. Create a plain QWidget with NO parent (None)
|
# 1. Create a plain QWidget with NO parent (None)
|
||||||
# This instantly makes it a top-level desktop window
|
# This instantly makes it a top-level desktop window
|
||||||
popup = QWidget(None)
|
popup = QWidget(None)
|
||||||
@@ -746,11 +754,12 @@ class ProcessOrchestrator(QObject):
|
|||||||
setup_finished = Signal(object, object, object, list)
|
setup_finished = Signal(object, object, object, list)
|
||||||
setup_failed = Signal(str)
|
setup_failed = Signal(str)
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(
|
||||||
selected_files,
|
self,
|
||||||
haemo_dict: dict[str, BaseRaw],
|
selected_files: list[str],
|
||||||
worker_func
|
haemo_dict: dict[str, Any],
|
||||||
):
|
worker_func: Callable[..., Any]
|
||||||
|
) -> None:
|
||||||
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.selected_files = selected_files
|
self.selected_files = selected_files
|
||||||
@@ -810,9 +819,6 @@ class ParticipantFoldChannelsWidget(FlaresBaseWidget):
|
|||||||
|
|
||||||
self.index_texts = [
|
self.index_texts = [
|
||||||
"0 (Fold Channels)",
|
"0 (Fold Channels)",
|
||||||
# "1 (second image)",
|
|
||||||
# "2 (third image)",
|
|
||||||
# "3 (fourth image)",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
self.image_index_dropdown = self._create_multiselect_dropdown(self.index_texts)
|
self.image_index_dropdown = self._create_multiselect_dropdown(self.index_texts)
|
||||||
@@ -888,13 +894,8 @@ class ParticipantFoldChannelsWidget(FlaresBaseWidget):
|
|||||||
|
|
||||||
self.multi_progress.add_participant(os.path.basename(file_path), total_channels)
|
self.multi_progress.add_participant(os.path.basename(file_path), total_channels)
|
||||||
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
print(f"Before: {datetime.now()}")
|
|
||||||
self.multi_progress.show()
|
self.multi_progress.show()
|
||||||
|
|
||||||
print(f"After 1: {datetime.now()}")
|
|
||||||
|
|
||||||
if current_process().name == 'MainProcess':
|
if current_process().name == 'MainProcess':
|
||||||
# Create a clean background thread worker execution channel
|
# Create a clean background thread worker execution channel
|
||||||
self.orchestrator_thread = QThread()
|
self.orchestrator_thread = QThread()
|
||||||
@@ -914,7 +915,6 @@ class ParticipantFoldChannelsWidget(FlaresBaseWidget):
|
|||||||
self.orchestrator.setup_failed.connect(self.orchestrator.deleteLater)
|
self.orchestrator.setup_failed.connect(self.orchestrator.deleteLater)
|
||||||
|
|
||||||
self.orchestrator_thread.start()
|
self.orchestrator_thread.start()
|
||||||
print(f"After 4: {datetime.now()}")
|
|
||||||
|
|
||||||
def on_orchestration_success(
|
def on_orchestration_success(
|
||||||
self,
|
self,
|
||||||
@@ -1102,7 +1102,7 @@ class ParticipantFoldChannelsWidget(FlaresBaseWidget):
|
|||||||
vbox = QVBoxLayout(container)
|
vbox = QVBoxLayout(container)
|
||||||
|
|
||||||
title = QLabel("<b>Brodmann Area Legend</b>")
|
title = QLabel("<b>Brodmann Area Legend</b>")
|
||||||
title.setAlignment(Qt.AlignCenter)
|
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
vbox.addWidget(title)
|
vbox.addWidget(title)
|
||||||
|
|
||||||
pixmap = self._bytes_to_pixmap(legend_bytes)
|
pixmap = self._bytes_to_pixmap(legend_bytes)
|
||||||
@@ -1110,10 +1110,10 @@ class ParticipantFoldChannelsWidget(FlaresBaseWidget):
|
|||||||
# Legends are usually tall, so we scale it differently or keep it smaller
|
# Legends are usually tall, so we scale it differently or keep it smaller
|
||||||
legend_label.setPixmap(pixmap.scaled(
|
legend_label.setPixmap(pixmap.scaled(
|
||||||
self.thumb_size,
|
self.thumb_size,
|
||||||
Qt.KeepAspectRatio,
|
Qt.AspectRatioMode.KeepAspectRatio,
|
||||||
Qt.SmoothTransformation
|
Qt.TransformationMode.SmoothTransformation
|
||||||
))
|
))
|
||||||
legend_label.setAlignment(Qt.AlignCenter)
|
legend_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
legend_label.mousePressEvent = lambda e, p=pixmap: self._open_full_size(p, "Brodmann Legend")
|
legend_label.mousePressEvent = lambda e, p=pixmap: self._open_full_size(p, "Brodmann Legend")
|
||||||
|
|
||||||
vbox.addWidget(legend_label)
|
vbox.addWidget(legend_label)
|
||||||
|
|||||||
+32
-16
@@ -1,13 +1,15 @@
|
|||||||
"""
|
"""
|
||||||
Filename: plugins.py
|
Filename: plugins.py
|
||||||
Description: Plugins window
|
Description: Plugins window
|
||||||
|
Note: Compliant with pylance strict type checking
|
||||||
|
|
||||||
Author: Tyler de Zeeuw
|
Author: Tyler de Zeeuw
|
||||||
License: GPL-3.0
|
License: GPL-3.0
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Built-in imports
|
# Built-in imports
|
||||||
from typing import Any
|
from pathlib import Path
|
||||||
|
from typing import Any, cast
|
||||||
|
|
||||||
# External library imports
|
# External library imports
|
||||||
from PySide6.QtCore import Qt, QThread, Signal
|
from PySide6.QtCore import Qt, QThread, Signal
|
||||||
@@ -104,12 +106,14 @@ class PluginsWindow(QWidget):
|
|||||||
self._clear_details_panel()
|
self._clear_details_panel()
|
||||||
return
|
return
|
||||||
|
|
||||||
name = info.get("name", "Unknown")
|
plugin_info = cast(dict[str, Any], info)
|
||||||
version = info.get("version", "1.0.0")
|
|
||||||
author = info.get("author", "Unknown")
|
name = str(plugin_info.get("name", "Unknown"))
|
||||||
desc = info.get("description", "No description provided.")
|
version = str(plugin_info.get("version", "1.0.0"))
|
||||||
is_disabled = info.get("is_disabled", False)
|
author = str(plugin_info.get("author", "Unknown"))
|
||||||
path = info.get("path", "")
|
desc = str(plugin_info.get("description", "No description provided."))
|
||||||
|
is_disabled = bool(plugin_info.get("is_disabled", False))
|
||||||
|
path = str(plugin_info.get("path", ""))
|
||||||
|
|
||||||
self.lbl_plugin_title.setText(name)
|
self.lbl_plugin_title.setText(name)
|
||||||
self.lbl_plugin_meta.setText(f"<b>Version:</b> {version} | <b>Author:</b> {author}")
|
self.lbl_plugin_meta.setText(f"<b>Version:</b> {version} | <b>Author:</b> {author}")
|
||||||
@@ -134,25 +138,37 @@ class PluginsWindow(QWidget):
|
|||||||
selected = self.installed_list.currentItem()
|
selected = self.installed_list.currentItem()
|
||||||
if not selected:
|
if not selected:
|
||||||
return
|
return
|
||||||
info = selected.data(Qt.ItemDataRole.UserRole)
|
raw_info = selected.data(Qt.ItemDataRole.UserRole)
|
||||||
if isinstance(info, dict) and "path" in info:
|
if isinstance(raw_info, dict):
|
||||||
self.manager.toggle_plugin_state(info["path"])
|
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:
|
def uninstall_plugin(self) -> None:
|
||||||
selected = self.installed_list.currentItem()
|
selected = self.installed_list.currentItem()
|
||||||
if not selected:
|
if not selected:
|
||||||
return
|
return
|
||||||
info = selected.data(Qt.ItemDataRole.UserRole)
|
|
||||||
if not isinstance(info, dict) or "path" not in info:
|
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 isinstance(plugin_path, str) or not plugin_path:
|
||||||
return
|
return
|
||||||
|
|
||||||
reply = QMessageBox.question(
|
reply = QMessageBox.question(
|
||||||
self, "Confirm Uninstall",
|
self,
|
||||||
f"Are you sure you want to delete '{info['name']}'?",
|
"Confirm Uninstall",
|
||||||
|
f"Are you sure you want to delete '{plugin_name}'?",
|
||||||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
||||||
)
|
)
|
||||||
if reply == QMessageBox.StandardButton.Yes:
|
if reply == QMessageBox.StandardButton.Yes:
|
||||||
self.manager.uninstall_plugin(info["path"])
|
self.manager.uninstall_plugin(Path(plugin_path))
|
||||||
|
|
||||||
def fetch_remote_plugins(self) -> None:
|
def fetch_remote_plugins(self) -> None:
|
||||||
"""Asynchronously fetches remote plugins on a background thread."""
|
"""Asynchronously fetches remote plugins on a background thread."""
|
||||||
@@ -197,7 +213,7 @@ class PluginsWindow(QWidget):
|
|||||||
p_id = plugin.get("id", "")
|
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 = str(plugin.get("platforms", []))
|
||||||
min_v_str = plugin.get("min_app_version", "0.0.0")
|
min_v_str = plugin.get("min_app_version", "0.0.0")
|
||||||
|
|
||||||
is_platform_ok = not platforms or self.manager.current_platform in platforms
|
is_platform_ok = not platforms or self.manager.current_platform in platforms
|
||||||
|
|||||||
@@ -17,8 +17,10 @@ from PySide6.QtWidgets import QWidget, QVBoxLayout, QTextEdit, QLineEdit, QMainW
|
|||||||
from PySide6.QtCore import QProcess, Qt, QThread, Signal
|
from PySide6.QtCore import QProcess, Qt, QThread, Signal
|
||||||
|
|
||||||
from file_ext_registration import register_file_association, is_windows_admin
|
from file_ext_registration import register_file_association, is_windows_admin
|
||||||
|
from plugin_manager import PluginManager
|
||||||
from src.shared.shareddata import API_URL, API_URL_SECONDARY, APP_NAME, CURRENT_VERSION, PLATFORM_NAME
|
from src.shared.shareddata import API_URL, API_URL_SECONDARY, APP_NAME, CURRENT_VERSION, PLATFORM_NAME
|
||||||
from src.window.about import AboutWindow
|
from src.window.about import AboutWindow
|
||||||
|
from src.window.plugins import PluginsWindow
|
||||||
from updater import UpdateManager
|
from updater import UpdateManager
|
||||||
|
|
||||||
|
|
||||||
@@ -42,7 +44,7 @@ class _AssocWorker(QThread):
|
|||||||
|
|
||||||
|
|
||||||
class TerminalWindow(QWidget):
|
class TerminalWindow(QWidget):
|
||||||
def __init__(self, parent: QWidget | None = None) -> None:
|
def __init__(self, parent: QWidget | None, plugin_manager: PluginManager) -> None:
|
||||||
super().__init__(parent, Qt.WindowType.Window)
|
super().__init__(parent, Qt.WindowType.Window)
|
||||||
self.setWindowTitle(f"Terminal - {APP_NAME.upper()}")
|
self.setWindowTitle(f"Terminal - {APP_NAME.upper()}")
|
||||||
self.resize(320, 180)
|
self.resize(320, 180)
|
||||||
@@ -58,6 +60,7 @@ class TerminalWindow(QWidget):
|
|||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
|
|
||||||
self._process: QProcess | None = None
|
self._process: QProcess | None = None
|
||||||
|
self.plugin_manager = plugin_manager
|
||||||
|
|
||||||
self.commands: dict[str, Callable[..., Any]] = {
|
self.commands: dict[str, Callable[..., Any]] = {
|
||||||
"hello": self.cmd_hello,
|
"hello": self.cmd_hello,
|
||||||
@@ -66,6 +69,7 @@ class TerminalWindow(QWidget):
|
|||||||
"about": self.cmd_about,
|
"about": self.cmd_about,
|
||||||
"assoc": self.cmd_assoc,
|
"assoc": self.cmd_assoc,
|
||||||
"update": self.cmd_update,
|
"update": self.cmd_update,
|
||||||
|
"plugins": self.cmd_plugins,
|
||||||
"utest": self.cmd_utest,
|
"utest": self.cmd_utest,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,6 +126,10 @@ class TerminalWindow(QWidget):
|
|||||||
self.about = AboutWindow(self)
|
self.about = AboutWindow(self)
|
||||||
self.about.show()
|
self.about.show()
|
||||||
|
|
||||||
|
def cmd_plugins(self, *args: Any) -> None:
|
||||||
|
self.about = PluginsWindow(self, self.plugin_manager)
|
||||||
|
self.about.show()
|
||||||
|
|
||||||
def cmd_update(self, *args: Any) -> str:
|
def cmd_update(self, *args: Any) -> str:
|
||||||
main_win = self.parent()
|
main_win = self.parent()
|
||||||
if not isinstance(main_win, QMainWindow):
|
if not isinstance(main_win, QMainWindow):
|
||||||
|
|||||||
Reference in New Issue
Block a user