mostly finish rewrite
This commit is contained in:
@@ -1,3 +1,11 @@
|
||||
"""
|
||||
Filename: about.py
|
||||
Description: About window for FLARES
|
||||
|
||||
Author: Tyler de Zeeuw
|
||||
License: GPL-3.0
|
||||
"""
|
||||
|
||||
from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
"""
|
||||
Filename: terminal.py
|
||||
Description: Terminal window for FLARES
|
||||
|
||||
Author: Tyler de Zeeuw
|
||||
License: GPL-3.0
|
||||
"""
|
||||
|
||||
from PySide6.QtWidgets import QWidget, QVBoxLayout, QTextEdit, QLineEdit
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
"""
|
||||
Filename: updateevents.py
|
||||
Description: Methods to update snirf events for FLARES
|
||||
|
||||
Author: Tyler de Zeeuw
|
||||
License: GPL-3.0
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
from enum import Enum, auto
|
||||
@@ -26,7 +34,7 @@ class UpdateEventsWindow(QWidget):
|
||||
|
||||
self.mode = mode
|
||||
self.caller = caller or self.__class__.__name__
|
||||
self.setWindowTitle("Update event markers")
|
||||
self.setWindowTitle(f"Update event markers - {APP_NAME.upper()}")
|
||||
self.resize(760, 200)
|
||||
|
||||
print("INIT MODE:", mode)
|
||||
@@ -844,6 +852,4 @@ class UpdateEventsBlazesWindow(QWidget):
|
||||
QMessageBox.information(self, "Success",
|
||||
f"Aligned {len(onsets)} events.\n(Filtered out {skipped_count} short events)")
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Error", f"Failed to update SNIRF file:\n{e}")
|
||||
|
||||
|
||||
QMessageBox.critical(self, "Error", f"Failed to update SNIRF file:\n{e}")
|
||||
@@ -1,3 +1,11 @@
|
||||
"""
|
||||
Filename: updateoptodes.py
|
||||
Description: Methods to update optode locations for FLARES
|
||||
|
||||
Author: Tyler de Zeeuw
|
||||
License: GPL-3.0
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
@@ -9,6 +17,7 @@ from PySide6.QtCore import Qt
|
||||
|
||||
from mne.io import read_raw_snirf
|
||||
from mne_nirs.io import write_raw_snirf
|
||||
from mne.channels import make_dig_montage
|
||||
|
||||
from src.shared.shareddata import APP_NAME
|
||||
|
||||
@@ -17,7 +26,7 @@ class UpdateOptodesWindow(QWidget):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent, Qt.WindowType.Window)
|
||||
self.setWindowTitle("Update optode positions")
|
||||
self.setWindowTitle(f"Update optode positions - {APP_NAME.upper()}")
|
||||
self.resize(760, 200)
|
||||
|
||||
self.label_file_a = QLabel("SNIRF file:")
|
||||
@@ -136,7 +145,7 @@ class UpdateOptodesWindow(QWidget):
|
||||
|
||||
def show_help_popup(self, text):
|
||||
msg = QMessageBox(self)
|
||||
msg.setWindowTitle(f"Parameter Info - {APP_NAME.upper}")
|
||||
msg.setWindowTitle(f"Parameter Info - {APP_NAME.upper()}")
|
||||
msg.setText(text)
|
||||
msg.exec()
|
||||
|
||||
@@ -284,4 +293,4 @@ class UpdateOptodesWindow(QWidget):
|
||||
# Read the SNIRF file, set the montage, and write it back
|
||||
raw = read_raw_snirf(file_a, preload=True)
|
||||
raw.set_montage(initial_montage)
|
||||
write_raw_snirf(raw, save_path)
|
||||
write_raw_snirf(raw, save_path)
|
||||
+10
-2
@@ -1,7 +1,15 @@
|
||||
"""
|
||||
Filename: userguide.py
|
||||
Description: User guide for FLARES
|
||||
|
||||
Author: Tyler de Zeeuw
|
||||
License: GPL-3.0
|
||||
"""
|
||||
|
||||
from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
from src.shared.shareddata import APP_NAME, PIPELINE_STAGES
|
||||
from src.shared.shareddata import APP_NAME, PIPELINE_STAGES, WIKI_URL
|
||||
|
||||
|
||||
class UserGuideWindow(QWidget):
|
||||
@@ -22,7 +30,7 @@ class UserGuideWindow(QWidget):
|
||||
label2_text = "\n".join(f"Stage {idx + 1}: {name}" for idx, name in enumerate(PIPELINE_STAGES)) + "\n"
|
||||
label2 = QLabel(label2_text, self)
|
||||
|
||||
label3 = QLabel(f"For more information, visit the Git wiki page <a href='https://git.research.dezeeuw.ca/tyler/{APP_NAME}/wiki'>here</a>.", self)
|
||||
label3 = QLabel(f"For more information, visit the Git wiki page <a href='{WIKI_URL}'>here</a>.", self)
|
||||
label3.setTextFormat(Qt.TextFormat.RichText)
|
||||
label3.setTextInteractionFlags(Qt.TextInteractionFlag.TextBrowserInteraction)
|
||||
label3.setOpenExternalLinks(True)
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
"""
|
||||
Filename: viewerlauncher.py
|
||||
Description: Analysis options launcher for FLARES
|
||||
|
||||
Author: Tyler de Zeeuw
|
||||
License: GPL-3.0
|
||||
"""
|
||||
|
||||
from PySide6.QtWidgets import QPushButton, QWidget, QVBoxLayout
|
||||
from PySide6.QtCore import QTimer
|
||||
|
||||
from src.analysis.exportcsv import ExportDataAsCSVViewerWidget
|
||||
from src.analysis.group import GroupViewerWidget
|
||||
from src.analysis.groupbrain import GroupBrainViewerWidget
|
||||
from src.analysis.groupfunctionalconnectivity import GroupFunctionalConnectivityWidget
|
||||
from src.analysis.participant import ParticipantViewerWidget
|
||||
from src.analysis.participantbrain import ParticipantBrainViewerWidget
|
||||
from src.analysis.participantfoldchannels import ParticipantFoldChannelsWidget
|
||||
from src.analysis.participantfunctionalconnectivity import ParticipantFunctionalConnectivityWidget
|
||||
from src.shared.shareddata import APP_NAME
|
||||
|
||||
|
||||
class ViewerLauncherWidget(QWidget):
|
||||
def __init__(self, haemo_dict, config_dict, fig_bytes_dict, cha_dict, contrast_results_dict, df_ind, design_matrix, epochs_dict, folding_bypass):
|
||||
super().__init__()
|
||||
self.setWindowTitle(f"Viewer Launcher - {APP_NAME.upper()}")
|
||||
|
||||
group_dict = {
|
||||
file_path: config.get("GROUP", "Unknown")
|
||||
for file_path, config in config_dict.items()
|
||||
}
|
||||
|
||||
def launch(func, btn, *args):
|
||||
func(*args)
|
||||
self._trigger_success(btn)
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
|
||||
btn1 = QPushButton("Open Participant Viewer")
|
||||
btn1.clicked.connect(lambda: launch(self.open_participant_viewer, btn1, haemo_dict, fig_bytes_dict))
|
||||
btn1.setEnabled(not folding_bypass)
|
||||
|
||||
btn2 = QPushButton("Open Participant Brain Viewer")
|
||||
btn2.clicked.connect(lambda: launch(self.open_participant_brain_viewer, btn2, haemo_dict, cha_dict))
|
||||
btn2.setEnabled(not folding_bypass)
|
||||
|
||||
btn3 = QPushButton("Open Participant Fold Channels Viewer")
|
||||
btn3.clicked.connect(lambda: launch(self.open_participant_fold_channels_viewer, btn3, haemo_dict, cha_dict))
|
||||
|
||||
btn7 = QPushButton("Open Functional Connectivity Viewer [BETA]")
|
||||
btn7.clicked.connect(lambda: launch(self.open_participant_functional_connectivity_viewer, btn7, haemo_dict, epochs_dict))
|
||||
btn7.setEnabled(not folding_bypass)
|
||||
|
||||
btn8 = QPushButton("Open Group Functional Connectivity Viewer [BETA]")
|
||||
btn8.clicked.connect(lambda: launch(self.open_group_functional_connectivity_viewer, btn8, haemo_dict, group_dict, config_dict))
|
||||
btn8.setEnabled(not folding_bypass)
|
||||
|
||||
btn4 = QPushButton("Open Inter-Group Viewer")
|
||||
btn4.clicked.connect(lambda: launch(self.open_group_viewer, btn4, haemo_dict, cha_dict, df_ind, design_matrix, contrast_results_dict, group_dict))
|
||||
btn4.setEnabled(not folding_bypass)
|
||||
|
||||
btn5 = QPushButton("Open Cross Group Brain Viewer")
|
||||
btn5.clicked.connect(lambda: launch(self.open_group_brain_viewer, btn5, haemo_dict, df_ind, design_matrix, group_dict, contrast_results_dict))
|
||||
btn5.setEnabled(not folding_bypass)
|
||||
|
||||
btn6 = QPushButton("Open Export Data As CSV Viewer")
|
||||
btn6.clicked.connect(lambda: launch(self.open_export_data_as_csv_viewer, btn6, haemo_dict, cha_dict, df_ind, design_matrix, group_dict, contrast_results_dict))
|
||||
btn6.setEnabled(not folding_bypass)
|
||||
|
||||
layout.addWidget(btn1)
|
||||
layout.addWidget(btn2)
|
||||
layout.addWidget(btn3)
|
||||
layout.addWidget(btn7)
|
||||
layout.addWidget(btn8)
|
||||
layout.addWidget(btn4)
|
||||
layout.addWidget(btn5)
|
||||
layout.addWidget(btn6)
|
||||
|
||||
def open_participant_viewer(self, haemo_dict, fig_bytes_dict):
|
||||
self.participant_viewer = ParticipantViewerWidget(haemo_dict, fig_bytes_dict)
|
||||
self.participant_viewer.show()
|
||||
|
||||
def open_participant_brain_viewer(self, haemo_dict, cha_dict):
|
||||
self.participant_brain_viewer = ParticipantBrainViewerWidget(haemo_dict, cha_dict)
|
||||
self.participant_brain_viewer.show()
|
||||
|
||||
def open_participant_fold_channels_viewer(self, haemo_dict, cha_dict):
|
||||
self.participant_fold_channels_viewer = ParticipantFoldChannelsWidget(haemo_dict, cha_dict)
|
||||
self.participant_fold_channels_viewer.show()
|
||||
|
||||
def open_participant_functional_connectivity_viewer(self, haemo_dict, epochs_dict):
|
||||
self.participant_brain_viewer = ParticipantFunctionalConnectivityWidget(haemo_dict, epochs_dict)
|
||||
self.participant_brain_viewer.show()
|
||||
|
||||
def open_group_functional_connectivity_viewer(self, haemo_dict, group, config_dict):
|
||||
self.participant_brain_viewer = GroupFunctionalConnectivityWidget(haemo_dict, group, config_dict)
|
||||
self.participant_brain_viewer.show()
|
||||
|
||||
def open_group_viewer(self, haemo_dict, cha_dict, df_ind, design_matrix, contrast_results_dict, group):
|
||||
self.participant_brain_viewer = GroupViewerWidget(haemo_dict, cha_dict, df_ind, design_matrix, contrast_results_dict, group)
|
||||
self.participant_brain_viewer.show()
|
||||
|
||||
def open_group_brain_viewer(self, haemo_dict, df_ind, design_matrix, group, contrast_results_dict):
|
||||
self.participant_brain_viewer = GroupBrainViewerWidget(haemo_dict, df_ind, design_matrix, group, contrast_results_dict)
|
||||
self.participant_brain_viewer.show()
|
||||
|
||||
def open_export_data_as_csv_viewer(self, haemo_dict, cha_dict, df_ind, design_matrix, group, contrast_results_dict):
|
||||
self.export_data_as_csv_viewer = ExportDataAsCSVViewerWidget(haemo_dict, cha_dict, df_ind, design_matrix, group, contrast_results_dict)
|
||||
self.export_data_as_csv_viewer.show()
|
||||
|
||||
def _trigger_success(self, button):
|
||||
"""Temporarily adds a green checkmark to the button text."""
|
||||
original_text = button.text()
|
||||
button.setText(f"{original_text} ✔")
|
||||
button.setStyleSheet("color: green; font-weight: bold;")
|
||||
|
||||
# Revert after 1 second
|
||||
QTimer.singleShot(1000, lambda: self._revert_button(button, original_text))
|
||||
|
||||
def _revert_button(self, button, original_text):
|
||||
button.setText(original_text)
|
||||
button.setStyleSheet("")
|
||||
+18
-12
@@ -1,9 +1,17 @@
|
||||
"""
|
||||
Filename: welcome.py
|
||||
Description: Welcome dialog for FLARES
|
||||
|
||||
Author: Tyler de Zeeuw
|
||||
License: GPL-3.0
|
||||
"""
|
||||
|
||||
from PySide6.QtWidgets import QTextBrowser, QVBoxLayout, QLabel, QDialog, QHBoxLayout, QPushButton
|
||||
from PySide6.QtGui import QDesktopServices, QIcon
|
||||
from PySide6.QtCore import QUrl
|
||||
from PySide6.QtNetwork import QNetworkAccessManager, QNetworkRequest
|
||||
|
||||
from src.shared.shareddata import APP_NAME, CURRENT_VERSION, resource_path
|
||||
from src.shared.shareddata import APP_NAME, CURRENT_VERSION, CHANGELOG_URL, resource_path
|
||||
|
||||
|
||||
class WelcomeDialog(QDialog):
|
||||
@@ -13,13 +21,13 @@ class WelcomeDialog(QDialog):
|
||||
self.setMinimumSize(550, 450)
|
||||
self.resize(800, 500)
|
||||
|
||||
# Main Layout
|
||||
layout = QVBoxLayout(self)
|
||||
|
||||
# Header Layout (Logo + App Name)
|
||||
header_layout = QHBoxLayout()
|
||||
logo_label = QLabel(self)
|
||||
logo_label.setPixmap(QIcon(resource_path("icons/main.ico")).pixmap(48, 48)) # Fits cleanly in a header
|
||||
|
||||
# NOTE: might not work on mac and need the icns file
|
||||
logo_label.setPixmap(QIcon(resource_path("icons/main.ico")).pixmap(48, 48))
|
||||
if direct:
|
||||
title_label = QLabel(f"<h2>{APP_NAME.upper()} has been sucessfully updated to version {CURRENT_VERSION}!</h2>", self)
|
||||
else:
|
||||
@@ -30,19 +38,19 @@ class WelcomeDialog(QDialog):
|
||||
header_layout.addStretch()
|
||||
layout.addLayout(header_layout)
|
||||
|
||||
# Text Browser Area (Automatically converts Markdown syntax into clean formatted UI text)
|
||||
self.text_browser = QTextBrowser(self)
|
||||
self.text_browser.setHtml("<p style='color: gray;'>Loading latest updates from server...</p>")
|
||||
self.text_browser.setOpenLinks(False) # Don't open links inside the viewer
|
||||
|
||||
# Ensure links open in the default web browser and not in this window
|
||||
self.text_browser.setOpenLinks(False)
|
||||
self.text_browser.anchorClicked.connect(QDesktopServices.openUrl)
|
||||
layout.addWidget(self.text_browser)
|
||||
|
||||
# Footer Controls Layout
|
||||
footer_layout = QHBoxLayout()
|
||||
|
||||
ok_button = QPushButton("OK", self)
|
||||
ok_button.setDefault(True)
|
||||
ok_button.clicked.connect(self.accept) # Closes the dialog with a success signal
|
||||
ok_button.clicked.connect(self.accept)
|
||||
|
||||
footer_layout.addStretch()
|
||||
footer_layout.addWidget(ok_button)
|
||||
@@ -51,18 +59,16 @@ class WelcomeDialog(QDialog):
|
||||
# Fetch markdown from the web asynchronously
|
||||
self.network_manager = QNetworkAccessManager(self)
|
||||
self.network_manager.finished.connect(self._on_download_complete)
|
||||
|
||||
md_url = "https://git.research.dezeeuw.ca/tyler/flares/raw/branch/main/changelog_major.md"
|
||||
self.network_manager.get(QNetworkRequest(QUrl(md_url)))
|
||||
self.network_manager.get(QNetworkRequest(QUrl(CHANGELOG_URL)))
|
||||
|
||||
|
||||
def _on_download_complete(self, reply):
|
||||
"""Processes the downloaded markdown and drops it into the view frame."""
|
||||
if reply.error() == reply.NetworkError.NoError:
|
||||
raw_bytes = reply.readAll()
|
||||
|
||||
# Convert raw bytes to standard text string
|
||||
markdown_text = str(raw_bytes, encoding='utf-8')
|
||||
# Qt's QTextBrowser natively renders markdown arrays beautifully!
|
||||
self.text_browser.setMarkdown(markdown_text)
|
||||
else:
|
||||
self.text_browser.setHtml(
|
||||
|
||||
Reference in New Issue
Block a user