229 lines
8.8 KiB
Python
229 lines
8.8 KiB
Python
from PySide6.QtWidgets import QComboBox, QInputDialog, QMessageBox, QSlider
|
|
|
|
|
|
# ScanSlicerParamsMixin: class definition for ScanSlicer behavior.
|
|
class ScanSlicerParamsMixin:
|
|
# set_autotune_profile: function definition for ScanSlicer behavior.
|
|
def set_autotune_profile(self, profile_name):
|
|
normalized = self.autoslicer.normalize_autotune_profile(profile_name)
|
|
self.autotune_profile = normalized
|
|
|
|
dropdown = getattr(self, "autotune_profile_dropdown", None)
|
|
if dropdown is not None and dropdown.currentText() != normalized:
|
|
dropdown.blockSignals(True)
|
|
dropdown.setCurrentText(normalized)
|
|
dropdown.blockSignals(False)
|
|
|
|
# set_show_advanced_parameters: function definition for ScanSlicer behavior.
|
|
def set_show_advanced_parameters(self, checked):
|
|
self.show_advanced_params = bool(checked)
|
|
self.update_parameter_visibility()
|
|
|
|
# sync_param_controls_from_model: function definition for ScanSlicer behavior.
|
|
def sync_param_controls_from_model(self):
|
|
self.updating_param_controls = True
|
|
for name, p in self.params.items():
|
|
control = self.param_controls[name]
|
|
if isinstance(control, QSlider):
|
|
control.setValue(p.get())
|
|
elif isinstance(control, QComboBox):
|
|
idx = control.findData(p.get())
|
|
if idx >= 0:
|
|
control.setCurrentIndex(idx)
|
|
self.updating_param_controls = False
|
|
self.update_parameter_visibility()
|
|
|
|
# update_parameter_visibility: function definition for ScanSlicer behavior.
|
|
def update_parameter_visibility(self):
|
|
pipeline = self.params.detect_pipeline.get()
|
|
method = self.params.bw_method.get()
|
|
morph_op = self.params.morph_op.get()
|
|
show_advanced = bool(getattr(self, "show_advanced_params", False))
|
|
advanced_params = set(getattr(self, "ADVANCED_PARAMS", set()))
|
|
|
|
visibility = {
|
|
"bw_method": pipeline == 0,
|
|
"bw_thresh": pipeline == 0 and method == 0,
|
|
"bw_gauss": pipeline == 0 and method == 1,
|
|
"bw_adaptive_c": pipeline == 0 and method == 1,
|
|
"canny_low": pipeline == 1,
|
|
"canny_high": pipeline == 1,
|
|
"dilate_kernel": morph_op != 0,
|
|
}
|
|
|
|
for param_name, block in self.param_blocks.items():
|
|
if block is None:
|
|
continue
|
|
is_visible = visibility.get(param_name, True)
|
|
if param_name in advanced_params and not show_advanced:
|
|
is_visible = False
|
|
block.setVisible(is_visible)
|
|
|
|
# load_default_parameters: function definition for ScanSlicer behavior.
|
|
def load_default_parameters(self):
|
|
mode = self.params.load_defaults()
|
|
self.sync_param_controls_from_model()
|
|
self.set_preset_dropdown_custom()
|
|
if mode == "saved":
|
|
self.update_statusbar("Loaded saved defaults.")
|
|
else:
|
|
self.update_statusbar("Loaded factory defaults (no saved defaults found).")
|
|
|
|
# save_default_parameters: function definition for ScanSlicer behavior.
|
|
def save_default_parameters(self):
|
|
try:
|
|
path = self.params.save_user_defaults()
|
|
except OSError as exc:
|
|
QMessageBox.critical(self, "Save defaults failed", str(exc))
|
|
self.update_statusbar("Failed to save defaults.")
|
|
return
|
|
|
|
self.update_statusbar("Saved defaults to " + path)
|
|
|
|
# autotune_parameters: function definition for ScanSlicer behavior.
|
|
def autotune_parameters(self):
|
|
if self.processing_busy:
|
|
return
|
|
if not self.autoslicer.image_loaded():
|
|
QMessageBox.warning(self, "No image loaded", "Load an image first")
|
|
return
|
|
self._start_autotune_worker()
|
|
|
|
# clear_autotune_history: function definition for ScanSlicer behavior.
|
|
def clear_autotune_history(self):
|
|
if self.processing_busy:
|
|
return
|
|
|
|
entry_count = len(self.autoslicer.feedback_entries)
|
|
prompt = (
|
|
"Clear auto-tune feedback history?\n\n"
|
|
"This removes learned examples from previous saves and resets auto-tune learning."
|
|
)
|
|
if entry_count > 0:
|
|
prompt += f"\n\nSaved examples: {entry_count}"
|
|
|
|
choice = QMessageBox.question(
|
|
self,
|
|
"Clear auto-tune history",
|
|
prompt,
|
|
QMessageBox.Yes | QMessageBox.No,
|
|
QMessageBox.No,
|
|
)
|
|
if choice != QMessageBox.Yes:
|
|
return
|
|
|
|
try:
|
|
result = self.autoslicer.clear_feedback_entries()
|
|
except OSError as exc:
|
|
QMessageBox.critical(self, "Clear history failed", str(exc))
|
|
self.update_statusbar("Failed to clear auto-tune history.")
|
|
return
|
|
|
|
self.append_status_log(
|
|
"Auto-tune feedback cleared: "
|
|
+ f"entries={result['cleared_count']}, path={result['path']}, removed_file={result['removed_file']}"
|
|
)
|
|
self.update_statusbar("Auto-tune history cleared.")
|
|
|
|
# set_preset_dropdown_custom: function definition for ScanSlicer behavior.
|
|
def set_preset_dropdown_custom(self):
|
|
if self.preset_dropdown.currentText() == "Custom":
|
|
return
|
|
self.preset_dropdown.blockSignals(True)
|
|
self.preset_dropdown.setCurrentText("Custom")
|
|
self.preset_dropdown.blockSignals(False)
|
|
|
|
# refresh_preset_dropdown: function definition for ScanSlicer behavior.
|
|
def refresh_preset_dropdown(self, selected_name=None):
|
|
if selected_name is None:
|
|
selected_name = self.preset_dropdown.currentText()
|
|
|
|
self.preset_dropdown.blockSignals(True)
|
|
self.preset_dropdown.clear()
|
|
self.preset_dropdown.addItem("Custom")
|
|
self.preset_dropdown.addItems(self.params.preset_names())
|
|
|
|
if selected_name and self.preset_dropdown.findText(selected_name) >= 0:
|
|
self.preset_dropdown.setCurrentText(selected_name)
|
|
else:
|
|
self.preset_dropdown.setCurrentText("Custom")
|
|
self.preset_dropdown.blockSignals(False)
|
|
|
|
# save_current_as_preset: function definition for ScanSlicer behavior.
|
|
def save_current_as_preset(self):
|
|
if self.processing_busy:
|
|
return
|
|
|
|
suggested_name = self.preset_dropdown.currentText()
|
|
if suggested_name == "Custom":
|
|
suggested_name = "My Preset"
|
|
|
|
preset_name, ok = QInputDialog.getText(
|
|
self,
|
|
"Save Preset",
|
|
"Preset name:",
|
|
text=suggested_name,
|
|
)
|
|
if not ok:
|
|
return
|
|
|
|
preset_name = preset_name.strip()
|
|
if len(preset_name) == 0:
|
|
QMessageBox.warning(self, "Invalid preset name", "Preset name cannot be empty.")
|
|
return
|
|
|
|
try:
|
|
saved_name = self.params.save_named_preset(preset_name)
|
|
except ValueError as exc:
|
|
QMessageBox.warning(self, "Invalid preset name", str(exc))
|
|
return
|
|
except OSError as exc:
|
|
QMessageBox.critical(self, "Save preset failed", str(exc))
|
|
return
|
|
|
|
self.refresh_preset_dropdown(selected_name=saved_name)
|
|
self.update_statusbar("Saved preset: " + saved_name)
|
|
|
|
# delete_current_preset: function definition for ScanSlicer behavior.
|
|
def delete_current_preset(self):
|
|
if self.processing_busy:
|
|
return
|
|
|
|
preset_name = self.preset_dropdown.currentText().strip()
|
|
if len(preset_name) == 0 or preset_name == "Custom":
|
|
QMessageBox.warning(self, "No preset selected", "Select a user preset to delete.")
|
|
return
|
|
|
|
choice = QMessageBox.question(
|
|
self,
|
|
"Delete Preset",
|
|
f"Delete preset '{preset_name}'?",
|
|
QMessageBox.Yes | QMessageBox.No,
|
|
QMessageBox.No,
|
|
)
|
|
if choice != QMessageBox.Yes:
|
|
return
|
|
|
|
try:
|
|
deleted_name = self.params.delete_named_preset(preset_name)
|
|
except ValueError as exc:
|
|
QMessageBox.warning(self, "Delete preset failed", str(exc))
|
|
return
|
|
except OSError as exc:
|
|
QMessageBox.critical(self, "Delete preset failed", str(exc))
|
|
return
|
|
|
|
self.refresh_preset_dropdown(selected_name="Custom")
|
|
self.update_statusbar("Deleted preset: " + deleted_name)
|
|
|
|
# apply_parameter_preset: function definition for ScanSlicer behavior.
|
|
def apply_parameter_preset(self, preset_name):
|
|
if preset_name == "Custom":
|
|
return
|
|
if not self.params.apply_preset(preset_name):
|
|
return
|
|
self.sync_param_controls_from_model()
|
|
self.preset_dropdown.blockSignals(True)
|
|
self.preset_dropdown.setCurrentText(preset_name)
|
|
self.preset_dropdown.blockSignals(False)
|
|
self.update_statusbar("Applied preset: " + preset_name)
|