fix for xlsx and upgrade dependencies
This commit is contained in:
@@ -4,6 +4,8 @@
|
|||||||
- The BLAZES option will assign events that are exported directly from the software [BLAZES](https://git.research.dezeeuw.ca/tyler/blazes)
|
- The BLAZES option will assign events that are exported directly from the software [BLAZES](https://git.research.dezeeuw.ca/tyler/blazes)
|
||||||
- Moved the updating logic to a seperate file for better reusability and generalization
|
- Moved the updating logic to a seperate file for better reusability and generalization
|
||||||
- Fixed 'Toggle Status Bar' having no effect on the visibility of the status bar
|
- Fixed 'Toggle Status Bar' having no effect on the visibility of the status bar
|
||||||
|
- Fixed a bug when updating optode positions that would prevent .txt files from being selected. Fixes [Issue 54](https://git.research.dezeeuw.ca/tyler/flares/issues/54)
|
||||||
|
- Fixed a missing dependency in the standalone application when attempting to use an .xlsx file to update optode positions
|
||||||
|
|
||||||
|
|
||||||
# Version 1.2.1
|
# Version 1.2.1
|
||||||
|
|||||||
72
main.py
72
main.py
@@ -190,8 +190,7 @@ SECTIONS = [
|
|||||||
{"name": "DRIFT_MODEL", "default": ["cosine"], "type": list, "options": ["cosine", "polynomial"], "help": "Specifies the desired drift model."},
|
{"name": "DRIFT_MODEL", "default": ["cosine"], "type": list, "options": ["cosine", "polynomial"], "help": "Specifies the desired drift model."},
|
||||||
{"name": "HIGH_PASS", "default": 0.01, "type": float, "help": "High-pass frequency in case of a cosine model (in Hz)."},
|
{"name": "HIGH_PASS", "default": 0.01, "type": float, "help": "High-pass frequency in case of a cosine model (in Hz)."},
|
||||||
{"name": "DRIFT_ORDER", "default": 1, "type": int, "help": "Order of the drift model (in case it is polynomial)"},
|
{"name": "DRIFT_ORDER", "default": 1, "type": int, "help": "Order of the drift model (in case it is polynomial)"},
|
||||||
{"name": "FIR_DELAYS", "default": "None", "type": range, "depends_on": "HRF_MODEL", "depends_value": "fir", "help": "In case of FIR design, yields the array of delays used in the FIR model (in scans)."},
|
{"name": "FIR_DELAYS", "default": 15, "type": range, "depends_on": "HRF_MODEL", "depends_value": "fir", "help": "In case of FIR design, yields the array of delays used in the FIR model (in scans)."}, {"name": "MIN_ONSET", "default": -24, "type": int, "help": "Minimal onset relative to frame times (in seconds)"},
|
||||||
{"name": "MIN_ONSET", "default": -24, "type": int, "help": "Minimal onset relative to frame times (in seconds)"},
|
|
||||||
{"name": "OVERSAMPLING", "default": 50, "type": int, "help": "Oversampling factor used in temporal convolutions."},
|
{"name": "OVERSAMPLING", "default": 50, "type": int, "help": "Oversampling factor used in temporal convolutions."},
|
||||||
{"name": "REMOVE_EVENTS", "default": "None", "type": list, "help": "Remove events matching the names provided before generating the Design Matrix"},
|
{"name": "REMOVE_EVENTS", "default": "None", "type": list, "help": "Remove events matching the names provided before generating the Design Matrix"},
|
||||||
{"name": "SHORT_CHANNEL_REGRESSION", "default": True, "type": bool, "depends_on": "SHORT_CHANNEL", "help": "Should short channel regression be used to create the design matrix? This will use the 'signal' from the short channel and regress it out of all other channels."},
|
{"name": "SHORT_CHANNEL_REGRESSION", "default": True, "type": bool, "depends_on": "SHORT_CHANNEL", "help": "Should short channel regression be used to create the design matrix? This will use the 'signal' from the short channel and regress it out of all other channels."},
|
||||||
@@ -543,7 +542,7 @@ class UpdateOptodesWindow(QWidget):
|
|||||||
self.line_edit_file_a.setText(file_path)
|
self.line_edit_file_a.setText(file_path)
|
||||||
|
|
||||||
def browse_file_b(self):
|
def browse_file_b(self):
|
||||||
file_path, _ = QFileDialog.getOpenFileName(self, "Select File", "", "Text Files (*.txt), Excel Files (*.xlsx)")
|
file_path, _ = QFileDialog.getOpenFileName(self, "Select File", "", "Text Files (*.txt);;Excel Files (*.xlsx)")
|
||||||
if file_path:
|
if file_path:
|
||||||
self.line_edit_file_b.setText(file_path)
|
self.line_edit_file_b.setText(file_path)
|
||||||
|
|
||||||
@@ -1730,26 +1729,31 @@ class ParamSection(QWidget):
|
|||||||
h_layout.addWidget(label)
|
h_layout.addWidget(label)
|
||||||
h_layout.setStretch(1, 3) # Set the stretch factor for label (40%)
|
h_layout.setStretch(1, 3) # Set the stretch factor for label (40%)
|
||||||
|
|
||||||
|
default_val = param["default"]
|
||||||
|
|
||||||
# Create input widget based on type
|
# Create input widget based on type
|
||||||
if param["type"] == bool:
|
if param["type"] == bool:
|
||||||
widget = QComboBox()
|
widget = QComboBox()
|
||||||
widget.addItems(["True", "False"])
|
widget.addItems(["True", "False"])
|
||||||
widget.setCurrentText(str(param["default"]))
|
widget.setCurrentText(str(default_val))
|
||||||
|
widget.currentTextChanged.connect(lambda val, p=param["name"]: self.check_if_changed(p, val))
|
||||||
widget.currentTextChanged.connect(self.notify_global_update)
|
widget.currentTextChanged.connect(self.notify_global_update)
|
||||||
elif param["type"] == int:
|
elif param["type"] == int:
|
||||||
widget = QLineEdit()
|
widget = QLineEdit()
|
||||||
widget.setValidator(QIntValidator())
|
widget.setValidator(QIntValidator())
|
||||||
widget.setText(str(param["default"]))
|
widget.setText(str(default_val))
|
||||||
|
widget.textChanged.connect(lambda val, p=param["name"]: self.check_if_changed(p, val))
|
||||||
elif param["type"] == float:
|
elif param["type"] == float:
|
||||||
widget = QLineEdit()
|
widget = QLineEdit()
|
||||||
widget.setValidator(QDoubleValidator())
|
widget.setValidator(QDoubleValidator())
|
||||||
widget.setText(str(param["default"]))
|
widget.setText(str(default_val))
|
||||||
|
widget.textChanged.connect(lambda val, p=param["name"]: self.check_if_changed(p, val))
|
||||||
elif param["type"] == list:
|
elif param["type"] == list:
|
||||||
if param.get("exclusive", True):
|
if param.get("exclusive", True):
|
||||||
widget = QComboBox()
|
widget = QComboBox()
|
||||||
widget.addItems(param.get("options", []))
|
widget.addItems(param.get("options", []))
|
||||||
widget.setCurrentText(str(param.get("default", "<None Selected>")))
|
widget.setCurrentText(str(default_val))
|
||||||
|
widget.currentTextChanged.connect(lambda val, p=param["name"]: self.check_if_changed(p, val))
|
||||||
widget.currentTextChanged.connect(self.notify_global_update)
|
widget.currentTextChanged.connect(self.notify_global_update)
|
||||||
else:
|
else:
|
||||||
widget = self._create_multiselect_dropdown(None)
|
widget = self._create_multiselect_dropdown(None)
|
||||||
@@ -1757,16 +1761,16 @@ class ParamSection(QWidget):
|
|||||||
widget = QSpinBox()
|
widget = QSpinBox()
|
||||||
widget.setRange(0, 999) # Set a sensible maximum
|
widget.setRange(0, 999) # Set a sensible maximum
|
||||||
# If default is "None" or range(15), handle it gracefully:
|
# If default is "None" or range(15), handle it gracefully:
|
||||||
default_val = param["default"]
|
|
||||||
if isinstance(default_val, range):
|
if isinstance(default_val, range):
|
||||||
widget.setValue(default_val.stop)
|
widget.setValue(default_val.stop)
|
||||||
elif str(default_val).isdigit():
|
elif str(default_val).isdigit():
|
||||||
widget.setValue(int(default_val))
|
widget.setValue(int(default_val))
|
||||||
else:
|
else:
|
||||||
widget.setValue(15) # Default fallback
|
widget.setValue(15) # Default fallback
|
||||||
|
widget.valueChanged.connect(lambda val, p=param["name"]: self.check_if_changed(p, val))
|
||||||
else:
|
else:
|
||||||
widget = QLineEdit()
|
widget = QLineEdit()
|
||||||
widget.setText(str(param["default"]))
|
widget.setText(str(default_val))
|
||||||
|
|
||||||
if "depends_on" in param:
|
if "depends_on" in param:
|
||||||
self.dependencies.append({
|
self.dependencies.append({
|
||||||
@@ -1783,12 +1787,62 @@ class ParamSection(QWidget):
|
|||||||
layout.addLayout(h_layout)
|
layout.addLayout(h_layout)
|
||||||
self.widgets[param["name"]] = {
|
self.widgets[param["name"]] = {
|
||||||
"widget": widget,
|
"widget": widget,
|
||||||
|
"label": label,
|
||||||
|
"default": default_val,
|
||||||
"type": param["type"],
|
"type": param["type"],
|
||||||
"h_layout": h_layout
|
"h_layout": h_layout
|
||||||
}
|
}
|
||||||
|
|
||||||
self.update_dependencies()
|
self.update_dependencies()
|
||||||
|
|
||||||
|
def check_if_changed(self, param_name, current_value):
|
||||||
|
"""Toggles bold font on the label if the value differs from default."""
|
||||||
|
info = self.widgets.get(param_name)
|
||||||
|
if not info:
|
||||||
|
return
|
||||||
|
|
||||||
|
label = info["label"]
|
||||||
|
default = info["default"]
|
||||||
|
|
||||||
|
is_changed = False
|
||||||
|
|
||||||
|
if info["type"] == list:
|
||||||
|
# If it's an exclusive ComboBox, current_value is a string.
|
||||||
|
# We wrap it in a list to compare it to the default list.
|
||||||
|
if isinstance(current_value, str):
|
||||||
|
normalized_current = [current_value]
|
||||||
|
else:
|
||||||
|
normalized_current = current_value # Already a list from multi-select
|
||||||
|
|
||||||
|
# Ensure default is a list for comparison
|
||||||
|
normalized_default = default if isinstance(default, list) else [default]
|
||||||
|
|
||||||
|
# Use sorted to ensure order doesn't matter
|
||||||
|
is_changed = sorted(normalized_current) != sorted(normalized_default)
|
||||||
|
|
||||||
|
# 2. Handle Range (SpinBox)
|
||||||
|
elif info["type"] == range:
|
||||||
|
ref = default.stop if isinstance(default, range) else default
|
||||||
|
try:
|
||||||
|
is_changed = int(current_value) != int(ref)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
is_changed = True
|
||||||
|
|
||||||
|
# 3. Standard Comparison (bool, int, float, str)
|
||||||
|
else:
|
||||||
|
is_changed = str(current_value) != str(default)
|
||||||
|
|
||||||
|
# Update Font
|
||||||
|
font = label.font()
|
||||||
|
font.setBold(is_changed)
|
||||||
|
label.setFont(font)
|
||||||
|
|
||||||
|
# Optional: Change color to make it even more obvious
|
||||||
|
if is_changed:
|
||||||
|
label.setStyleSheet("color: #3498db; font-weight: bold;") # Nice Blue
|
||||||
|
else:
|
||||||
|
label.setStyleSheet("color: none; font-weight: normal;")
|
||||||
|
|
||||||
def notify_global_update(self):
|
def notify_global_update(self):
|
||||||
"""
|
"""
|
||||||
Since dependencies can cross sections, we need to tell
|
Since dependencies can cross sections, we need to tell
|
||||||
|
|||||||
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
Reference in New Issue
Block a user