Compare commits
1
Commits
d1c9346e57
..
v1.8.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2456daca7 |
No files matched your search
@@ -185,3 +185,4 @@ flares-*
|
|||||||
*.cfg
|
*.cfg
|
||||||
tempCodeRunnerFile.py
|
tempCodeRunnerFile.py
|
||||||
*.pkl
|
*.pkl
|
||||||
|
*.joblib
|
||||||
@@ -12,7 +12,10 @@
|
|||||||
- Export To CSV Viewer export options now perform the actions they claim rather than having no functionality
|
- Export To CSV Viewer export options now perform the actions they claim rather than having no functionality
|
||||||
- Logs on a per participant basis are now available to be viewed from within the application
|
- Logs on a per participant basis are now available to be viewed from within the application
|
||||||
- They can be accessed by right clicking a participant bubble and selecting "Show Logs". Logs will be empty if they have not been processed
|
- They can be accessed by right clicking a participant bubble and selecting "Show Logs". Logs will be empty if they have not been processed
|
||||||
|
- A notification now appears when processing completes even if the application is not focused
|
||||||
- Fixed a regression where opening a folder would no longer load all the files in that folder
|
- Fixed a regression where opening a folder would no longer load all the files in that folder
|
||||||
|
- Added inspirational quotes to the About window
|
||||||
|
- Licence is now clickable from the About window
|
||||||
- Improved code stability and functionality
|
- Improved code stability and functionality
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ from mne_nirs.statistics._glm_level_first import RegressionResults # type: igno
|
|||||||
from mne_connectivity.viz import plot_connectivity_circle # type: ignore
|
from mne_connectivity.viz import plot_connectivity_circle # type: ignore
|
||||||
from mne_connectivity import envelope_correlation, spectral_connectivity_epochs, spectral_connectivity_time # type: ignore
|
from mne_connectivity import envelope_correlation, spectral_connectivity_epochs, spectral_connectivity_time # type: ignore
|
||||||
|
|
||||||
from src.shared.shareddata import PLATFORM_NAME, resource_path, get_app_dir
|
from src.shared.shareddata import PLATFORM_NAME, resource_path, get_app_dir, METRIC_REGISTRY
|
||||||
|
|
||||||
|
|
||||||
PRIMARY_COLORS = {
|
PRIMARY_COLORS = {
|
||||||
@@ -7152,20 +7152,6 @@ def write_qc_excel_summary(
|
|||||||
wb.save(output_path)
|
wb.save(output_path)
|
||||||
|
|
||||||
|
|
||||||
METRIC_REGISTRY = {
|
|
||||||
'Peak_Amp': 'Peak_Amp',
|
|
||||||
'TTP': 'Time_to_Peak',
|
|
||||||
'AUC': 'AUC',
|
|
||||||
'Rising_Slope': 'Rising_Slope',
|
|
||||||
'Recovery_Slope': 'Recovery_Slope',
|
|
||||||
'FWHM': 'FWHM',
|
|
||||||
'Onset_Latency': 'Onset_Latency',
|
|
||||||
'P2P_Amp': 'Peak_to_Peak_Amp',
|
|
||||||
'Signal_Std': 'Signal_Std',
|
|
||||||
'RMS': 'RMS'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def plot_and_enqueue_waveform_metrics(
|
def plot_and_enqueue_waveform_metrics(
|
||||||
roi_curves,
|
roi_curves,
|
||||||
fir_delays,
|
fir_delays,
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ if sys.platform.startswith("linux"):
|
|||||||
import psutil
|
import psutil
|
||||||
|
|
||||||
from PySide6.QtWidgets import (
|
from PySide6.QtWidgets import (
|
||||||
QApplication, QWidget, QMessageBox, QVBoxLayout, QHBoxLayout, QTextEdit, QScrollArea, QComboBox, QGridLayout, QSplitter, QDialogButtonBox, QHeaderView,
|
QApplication, QStyle, QSystemTrayIcon, QWidget, QMessageBox, QVBoxLayout, QHBoxLayout, QTextEdit, QScrollArea, QComboBox, QGridLayout, QSplitter, QDialogButtonBox, QHeaderView,
|
||||||
QPushButton, QMainWindow, QLabel, QLineEdit, QGroupBox, QDialog, QMenu, QSpinBox, QTableWidget, QTableWidgetItem
|
QPushButton, QMainWindow, QLabel, QLineEdit, QGroupBox, QDialog, QMenu, QSpinBox, QTableWidget, QTableWidgetItem
|
||||||
)
|
)
|
||||||
from PySide6.QtCore import QEvent, QObject, Signal, Qt, QTimer
|
from PySide6.QtCore import QEvent, QObject, Signal, Qt, QTimer
|
||||||
@@ -738,6 +738,18 @@ class MainApplication(QMainWindow):
|
|||||||
|
|
||||||
self.plugin_manager.reload_plugins()
|
self.plugin_manager.reload_plugins()
|
||||||
|
|
||||||
|
self.tray_icon = QSystemTrayIcon(self)
|
||||||
|
stock_icon = self.style().standardIcon(QStyle.StandardPixmap.SP_ComputerIcon) # Or use your own QIcon("path_to_logo.png")
|
||||||
|
self.tray_icon.setIcon(stock_icon)
|
||||||
|
self.tray_icon.show()
|
||||||
|
|
||||||
|
def bring_to_front():
|
||||||
|
self.showNormal() # Restores if minimized
|
||||||
|
self.raise_() # Brings to top of window stack (macOS/Linux)
|
||||||
|
self.activateWindow() # Focuses the window (Windows)
|
||||||
|
|
||||||
|
self.tray_icon.messageClicked.connect(bring_to_front)
|
||||||
|
|
||||||
self.show()
|
self.show()
|
||||||
|
|
||||||
# Check if we should pop up the welcome screen
|
# Check if we should pop up the welcome screen
|
||||||
@@ -2361,8 +2373,15 @@ class MainApplication(QMainWindow):
|
|||||||
elapsed_str = self._format_elapsed(msg.get("elapsed", getattr(self, "_last_elapsed", 0)))
|
elapsed_str = self._format_elapsed(msg.get("elapsed", getattr(self, "_last_elapsed", 0)))
|
||||||
speedup = msg.get("speedup")
|
speedup = msg.get("speedup")
|
||||||
speedup_str = f" | Speedup: {speedup:.1f}x" if speedup else ""
|
speedup_str = f" | Speedup: {speedup:.1f}x" if speedup else ""
|
||||||
self.statusbar.showMessage(
|
status_text = f"Complete: {success_count} succeeded, {fail_count} failed. | Total time: {elapsed_str}{speedup_str}"
|
||||||
f"Complete: {success_count} succeeded, {fail_count} failed. | Total time: {elapsed_str}{speedup_str}"
|
self.statusbar.showMessage(status_text)
|
||||||
|
|
||||||
|
if hasattr(self, "tray_icon") and self.tray_icon.isSystemTrayAvailable():
|
||||||
|
self.tray_icon.showMessage(
|
||||||
|
"Process Completed",
|
||||||
|
status_text,
|
||||||
|
QSystemTrayIcon.MessageIcon.Information,
|
||||||
|
4000 # Duration in milliseconds
|
||||||
)
|
)
|
||||||
|
|
||||||
if success_count > 0:
|
if success_count > 0:
|
||||||
|
|||||||
@@ -0,0 +1,117 @@
|
|||||||
|
"""
|
||||||
|
Filename: inspiration.py
|
||||||
|
Description: Contains inspiration quotes to be shown at random
|
||||||
|
Note: Compliant with pylance strict type checking
|
||||||
|
|
||||||
|
Author: Tyler de Zeeuw
|
||||||
|
License: GPL-3.0
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Built-in imports
|
||||||
|
import random
|
||||||
|
|
||||||
|
FNIRS_QUOTES = [
|
||||||
|
"Understanding the brain through light and data.",
|
||||||
|
"Every signal offers another perspective on brain function.",
|
||||||
|
"Exploring the relationship between brain activity and behavior.",
|
||||||
|
"Using light to investigate the complexity of the human brain.",
|
||||||
|
"Turning neural measurements into meaningful insights.",
|
||||||
|
"A deeper understanding of the brain begins with better questions.",
|
||||||
|
"Studying the brain means looking beyond what we can see.",
|
||||||
|
"Connecting brain activity to the way we think, feel, and act.",
|
||||||
|
"Even small changes in brain signals can reveal meaningful patterns.",
|
||||||
|
"Advancing neuroscience through new ways of measuring brain activity.",
|
||||||
|
"The brain is complex, and every measurement adds another piece.",
|
||||||
|
"Using fNIRS to explore the connection between the brain and the world around us.",
|
||||||
|
"Good science begins with curiosity and grows through careful observation.",
|
||||||
|
"There is still so much to learn about how the brain works.",
|
||||||
|
"Measuring brain activity is only the beginning; understanding it is the goal.",
|
||||||
|
"Exploring what brain activity can tell us about the human experience.",
|
||||||
|
"Every experiment brings a new opportunity to understand the brain.",
|
||||||
|
"The more we measure, the more questions we learn to ask.",
|
||||||
|
"Neuroscience advances through careful measurements and thoughtful interpretation.",
|
||||||
|
"Understanding brain function requires looking at both signals and context.",
|
||||||
|
"Each dataset offers a different view of the brain in action.",
|
||||||
|
"Exploring the neural processes behind everyday human experiences.",
|
||||||
|
"Better measurements can lead to better questions about the brain.",
|
||||||
|
"The challenge is not just collecting data, but understanding what it means.",
|
||||||
|
"Brain activity reflects a complex interaction of processes and experiences.",
|
||||||
|
"Scientific progress begins with observing what we do not yet understand.",
|
||||||
|
"Each discovery builds on the questions that came before it.",
|
||||||
|
"There is more to brain activity than a single signal can reveal.",
|
||||||
|
"New tools create new opportunities to investigate old questions.",
|
||||||
|
"The goal is to connect what we measure with what we want to understand.",
|
||||||
|
"Neural signals provide clues to the processes underlying human behavior.",
|
||||||
|
"A better understanding of brain function starts with better evidence.",
|
||||||
|
"The value of a measurement lies in what it helps us understand.",
|
||||||
|
"Every experiment adds another perspective to a complex picture.",
|
||||||
|
"Scientific discovery often begins with noticing something unexpected.",
|
||||||
|
"Understanding the brain is an ongoing process of questioning and refinement.",
|
||||||
|
"The best insights emerge when data meets thoughtful interpretation.",
|
||||||
|
"Research moves forward when curiosity is matched with rigorous methods.",
|
||||||
|
"There is always another question worth exploring.",
|
||||||
|
"Investigating the brain means embracing complexity rather than avoiding it.",
|
||||||
|
"Progress comes from asking precise questions about complicated systems.",
|
||||||
|
"The brain does not work in isolation from the person or environment.",
|
||||||
|
"Understanding behavior requires more than observing behavior alone.",
|
||||||
|
"The smallest details can sometimes change how we interpret the bigger picture.",
|
||||||
|
"Every method offers a different window into brain function.",
|
||||||
|
"Different perspectives help build a more complete understanding of the brain.",
|
||||||
|
"Good research turns uncertainty into questions that can be investigated.",
|
||||||
|
"The process of discovery is as important as the result.",
|
||||||
|
"Meaningful findings begin with reliable data and careful reasoning.",
|
||||||
|
"The brain is not simple, and our methods should reflect that complexity.",
|
||||||
|
"Scientific understanding grows when evidence challenges our assumptions.",
|
||||||
|
"Research is a process of learning what the data can—and cannot—tell us.",
|
||||||
|
"The most useful findings are those that move understanding forward.",
|
||||||
|
"Each new method expands the questions we can ask about the brain.",
|
||||||
|
"The path to understanding is built from observations, questions, and evidence.",
|
||||||
|
"Neuroscience is about understanding the processes behind the patterns.",
|
||||||
|
"The aim is not simply to measure brain activity, but to interpret it meaningfully.",
|
||||||
|
"Better tools help us investigate the questions that matter.",
|
||||||
|
"Progress in neuroscience depends on both technical precision and human curiosity.",
|
||||||
|
"The brain is complex, but every careful observation helps clarify a part of it.",
|
||||||
|
"Understanding brain function means connecting measurements to meaningful outcomes.",
|
||||||
|
"Research turns curiosity into evidence and evidence into understanding.",
|
||||||
|
"There is a story behind every pattern, but evidence determines what we can conclude.",
|
||||||
|
"New insights emerge when we approach familiar questions from different angles.",
|
||||||
|
"The strongest conclusions begin with careful measurements.",
|
||||||
|
"Understanding the brain takes more than data; it takes context.",
|
||||||
|
"The questions we ask shape what we learn from the brain.",
|
||||||
|
"Good research is not about finding answers quickly, but finding them carefully.",
|
||||||
|
"Every result contributes to a larger conversation about how the brain works.",
|
||||||
|
"The human brain remains an evolving question for science.",
|
||||||
|
"Measuring brain activity gives us another way to investigate human cognition.",
|
||||||
|
"The connection between brain function and behavior is rarely straightforward.",
|
||||||
|
"Scientific progress comes from combining new methods with careful interpretation.",
|
||||||
|
"The more we learn about the brain, the better our questions become.",
|
||||||
|
"Research is the practice of making uncertainty more understandable.",
|
||||||
|
"Understanding the brain means being willing to revise what we think we know.",
|
||||||
|
"Each experiment helps define what we know and what remains uncertain.",
|
||||||
|
"The most interesting findings often begin with questions that have no easy answers.",
|
||||||
|
"The purpose of research is to move beyond assumptions toward evidence.",
|
||||||
|
"A measurement becomes useful when it helps answer a meaningful question.",
|
||||||
|
"Neuroscience brings together technology, observation, and the study of human experience.",
|
||||||
|
"fNIRS offers another way to investigate changes in brain hemodynamics.",
|
||||||
|
"Optical measurements provide a noninvasive approach to studying brain function.",
|
||||||
|
"Studying brain function outside the lab can reveal aspects of everyday cognition.",
|
||||||
|
"The flexibility of fNIRS opens new possibilities for human neuroscience research.",
|
||||||
|
"Brain measurements become more meaningful when considered alongside behavior.",
|
||||||
|
"fNIRS provides a useful perspective on the relationship between physiology and cognition.",
|
||||||
|
"Understanding the strengths and limitations of a method is part of good science.",
|
||||||
|
"The future of neuroscience depends on asking better questions, not just collecting more data.",
|
||||||
|
"Technology expands what we can measure, while critical thinking guides what we conclude.",
|
||||||
|
"The goal of neuroscience is not just to observe the brain, but to understand its function.",
|
||||||
|
"Every research method brings its own perspective, possibilities, and limitations.",
|
||||||
|
"The most meaningful discoveries connect technical advances to real-world questions.",
|
||||||
|
"Better ways to study the brain can lead to a better understanding of human experience.",
|
||||||
|
"Science progresses when we remain curious about what the evidence cannot yet explain.",
|
||||||
|
"Understanding the brain is a long-term effort built on many small discoveries.",
|
||||||
|
"Behind every useful finding is a question worth investigating.",
|
||||||
|
"The value of research lies in what we learn, not just what we measure.",
|
||||||
|
"Good science makes room for complexity, uncertainty, and new perspectives.",
|
||||||
|
"The next insight may come from looking at a familiar problem in a different way.",
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_quote() -> None:
|
||||||
|
return f"'{random.choice(FNIRS_QUOTES)}'"
|
||||||
@@ -8,12 +8,18 @@ License: GPL-3.0
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# Built-in imports
|
# Built-in imports
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import platform
|
import platform
|
||||||
|
|
||||||
|
try:
|
||||||
|
import winreg
|
||||||
|
except ImportError:
|
||||||
|
winreg = None
|
||||||
|
|
||||||
CURRENT_VERSION = "1.7.2"
|
CURRENT_VERSION = "1.8.0"
|
||||||
APP_NAME = "flares"
|
APP_NAME = "flares"
|
||||||
APP_NAME_EXPANDED = "fNIRS Lightweight Analysis, Research, & Evaluation Suite"
|
APP_NAME_EXPANDED = "fNIRS Lightweight Analysis, Research, & Evaluation Suite"
|
||||||
API_URL = f"https://git.research.dezeeuw.ca/api/v1/repos/tyler/{APP_NAME}/releases"
|
API_URL = f"https://git.research.dezeeuw.ca/api/v1/repos/tyler/{APP_NAME}/releases"
|
||||||
@@ -72,6 +78,63 @@ DATA_SCHEMA = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
METRIC_REGISTRY = {
|
||||||
|
"Peak_Amp": "Peak_Amp",
|
||||||
|
"TTP": "Time_to_Peak",
|
||||||
|
"AUC": "AUC",
|
||||||
|
"Rising_Slope": "Rising_Slope",
|
||||||
|
"Recovery_Slope": "Recovery_Slope",
|
||||||
|
"FWHM": "FWHM",
|
||||||
|
"Onset_Latency": "Onset_Latency",
|
||||||
|
"P2P_Amp": "Peak_to_Peak_Amp",
|
||||||
|
"Signal_Std": "Signal_Std",
|
||||||
|
"RMS": "RMS",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_cpu_model_name():
|
||||||
|
"""Detect detailed processor info, collapsing Windows registry whitespace padding."""
|
||||||
|
try:
|
||||||
|
system = platform.system()
|
||||||
|
|
||||||
|
if system == "Windows" and winreg is not None:
|
||||||
|
with winreg.OpenKey(
|
||||||
|
winreg.HKEY_LOCAL_MACHINE,
|
||||||
|
r"HARDWARE\DESCRIPTION\System\CentralProcessor\0",
|
||||||
|
) as key:
|
||||||
|
raw_cpu_name, _ = winreg.QueryValueEx(key, "ProcessorNameString")
|
||||||
|
cpu_name = " ".join(raw_cpu_name.split())
|
||||||
|
|
||||||
|
command = (
|
||||||
|
"Get-CimInstance Win32_Processor | "
|
||||||
|
"Select-Object NumberOfCores, NumberOfLogicalProcessors, MaxClockSpeed | "
|
||||||
|
"ConvertTo-Json -Compress"
|
||||||
|
)
|
||||||
|
cpu = json.loads(subprocess.check_output(
|
||||||
|
["powershell", "-NoProfile", "-Command", command],
|
||||||
|
text=True,
|
||||||
|
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
|
||||||
|
))
|
||||||
|
base_ghz = cpu["MaxClockSpeed"] / 1000
|
||||||
|
return (f"{cpu_name} @ {base_ghz:.1f}GHz "
|
||||||
|
f"{cpu['NumberOfCores']}c/{cpu['NumberOfLogicalProcessors']}t")
|
||||||
|
|
||||||
|
if system == "Darwin":
|
||||||
|
return subprocess.check_output(
|
||||||
|
["sysctl", "-n", "machdep.cpu.brand_string"], text=True
|
||||||
|
).strip()
|
||||||
|
|
||||||
|
if system == "Linux":
|
||||||
|
with open("/proc/cpuinfo") as f:
|
||||||
|
for line in f:
|
||||||
|
if "model name" in line:
|
||||||
|
return line.split(":")[1].strip()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return platform.processor() or "Multi-Core CPU"
|
||||||
|
|
||||||
|
|
||||||
def resource_path(relative_path: str) -> str:
|
def resource_path(relative_path: str) -> str:
|
||||||
"""Get absolute path to resource regardless of running directly or packaged using PyInstaller"""
|
"""Get absolute path to resource regardless of running directly or packaged using PyInstaller"""
|
||||||
base_path = getattr(sys, "_MEIPASS", os.path.abspath("."))
|
base_path = getattr(sys, "_MEIPASS", os.path.abspath("."))
|
||||||
|
|||||||
+11
-1
@@ -12,6 +12,7 @@ from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel
|
|||||||
from PySide6.QtCore import Qt
|
from PySide6.QtCore import Qt
|
||||||
|
|
||||||
from src.shared.shareddata import APP_NAME, APP_NAME_EXPANDED, CURRENT_VERSION
|
from src.shared.shareddata import APP_NAME, APP_NAME_EXPANDED, CURRENT_VERSION
|
||||||
|
from src.shared.inspiration import get_quote
|
||||||
|
|
||||||
|
|
||||||
class AboutWindow(QWidget):
|
class AboutWindow(QWidget):
|
||||||
@@ -30,12 +31,21 @@ class AboutWindow(QWidget):
|
|||||||
layout = QVBoxLayout()
|
layout = QVBoxLayout()
|
||||||
label = QLabel(f"About {APP_NAME.upper()}", self)
|
label = QLabel(f"About {APP_NAME.upper()}", self)
|
||||||
label2 = QLabel(f"{APP_NAME_EXPANDED}", self)
|
label2 = QLabel(f"{APP_NAME_EXPANDED}", self)
|
||||||
label3 = QLabel(f"{APP_NAME.upper()} is licensed under the GPL-3.0 licence. For more information, visit https://www.gnu.org/licenses/gpl-3.0.en.html", self)
|
label3 = QLabel(
|
||||||
|
f'{APP_NAME.upper()} is licensed under the GPL-3.0 licence. '
|
||||||
|
'For more information, visit '
|
||||||
|
'<a href="https://www.gnu.org/licenses/gpl-3.0.en.html">'
|
||||||
|
'GNU GPL-3.0</a>',
|
||||||
|
self,
|
||||||
|
)
|
||||||
|
label3.setOpenExternalLinks(True)
|
||||||
label4 = QLabel(f"Version v{CURRENT_VERSION}")
|
label4 = QLabel(f"Version v{CURRENT_VERSION}")
|
||||||
|
label5 = QLabel(f"{get_quote()}", self)
|
||||||
|
|
||||||
layout.addWidget(label)
|
layout.addWidget(label)
|
||||||
layout.addWidget(label2)
|
layout.addWidget(label2)
|
||||||
layout.addWidget(label3)
|
layout.addWidget(label3)
|
||||||
layout.addWidget(label4)
|
layout.addWidget(label4)
|
||||||
|
layout.addWidget(label5)
|
||||||
|
|
||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
Reference in new issue
Block a user