import os
import webbrowser
from PyQt6.QtWidgets import QDialog, QMessageBox, QTextEdit, QVBoxLayout
from cosedaUI.export import ExportMtzWindow
from cosedaUI.merging_window import MergingSettingsWindow
from cosedaUI.metadata_window import MetadataWindow
from cosedaUI.strip_file_window import StripFilesWindow
from cosedaUI.unitcell_window import UnitCellHistogramWindow
from cosedaUI.workspace_unitcell_fit_window import WorkspaceUnitCellFitWindow
[docs]
class WorkflowMixin:
[docs]
def open_strip_files_window(self):
if not self.full_ini_file_path or not os.path.exists(self.full_ini_file_path):
QMessageBox.warning(self, "No INI File", "Please load an INI file first.")
return
# Prevent multiple instances
if hasattr(self, 'strip_files_window') and self.strip_files_window.isVisible():
self.strip_files_window.raise_()
self.strip_files_window.activateWindow()
else:
self.strip_files_window = StripFilesWindow(
parent=self,
ini_directory=self.ini_directory,
ini_file_path=self.full_ini_file_path
)
self.strip_files_window.show()
[docs]
def open_index_window(self):
QMessageBox.information(self, "Not Implemented", "The Index function is not implemented yet.")
[docs]
def open_index_integrate_window(self):
from cosedaUI.indexintegrate_window import IndexingControlWindow
# Pass the ini_directory and the HDF5 file path instead of the INI file path
self.index_integrate_window = IndexingControlWindow(self, self.ini_directory, self.hdf5_path)
# Two-way synchronization of file selection
w = self.index_integrate_window
# Main -> Index window (try common handler names)
for slot_name in ("on_mainwindow_ini_changed", "_sync_from_mainwindow_selection", "handle_mainwindow_ini_changed"):
if hasattr(w, slot_name):
try:
self.ini_selection_changed.connect(getattr(w, slot_name))
break
except Exception:
pass
# Index window -> Main (try common signal names)
for sig_name in ("ini_selection_changed", "ini_selected", "request_select_ini", "file_selection_changed", "file_selected"):
if hasattr(w, sig_name):
try:
getattr(w, sig_name).connect(self.on_indexintegrate_requested_select_ini)
break
except Exception:
pass
# Immediately sync current selection to the Index window
if self.full_ini_file_path:
self.ini_selection_changed.emit(self.full_ini_file_path)
self.index_integrate_window.show()
[docs]
def open_merge_window(self):
if not self.full_ini_file_path or not os.path.exists(self.full_ini_file_path):
QMessageBox.warning(self, "No INI File", "Please load an INI file first.")
return
# Prevent multiple instances
if hasattr(self, 'merging_window') and self.merging_window.isVisible():
self.merging_window.raise_()
self.merging_window.activateWindow()
else:
# Pass the workspace file path (fallback to current INI if not available)
workspace_path = self.workspace_file_path or self.full_ini_file_path
self.merging_window = MergingSettingsWindow(
parent=self,
ini_directory=self.ini_directory,
ini_file_path=self.full_ini_file_path,
workspace_path=workspace_path,
)
self.merging_window.show()
[docs]
def open_export_window(self):
"""
Open the Export window which lists available CrystFEL HKL files
and converts them to SHELX HKLF4, and can also write RES/P4P
from CrystFEL cell files.
"""
# Determine a sensible workspace root to scan for *_merge_* folders.
workspace_path = self.workspace_file_path or self.full_ini_file_path
if not workspace_path or not os.path.exists(workspace_path):
QMessageBox.warning(self, "No Workspace", "Please open or create a workspace first.")
return
# Prevent multiple instances
if hasattr(self, 'export_mtz_window') and self.export_mtz_window.isVisible():
self.export_mtz_window.raise_()
self.export_mtz_window.activateWindow()
else:
try:
self.export_mtz_window = ExportMtzWindow(workspace_path=workspace_path, parent=self)
self.export_mtz_window.show()
except Exception as e:
QMessageBox.critical(self, "Export HKLF4", f"Failed to open Export HKLF4 window:\n{e}")
[docs]
def open_unit_cell_hist_window(self, auto_refresh: bool = False):
"""
Open the unit cell histogram viewer for the current stream.
"""
# Prefer current stream from the indexing manager but allow manual selection in the dialog.
stream_path = None
try:
stream_path = getattr(self, "indexing_manager", None).stream_path
except Exception:
stream_path = None
# Reuse existing window if open
if hasattr(self, "unit_cell_hist_window") and self.unit_cell_hist_window.isVisible():
self.unit_cell_hist_window.raise_()
self.unit_cell_hist_window.activateWindow()
if auto_refresh:
try:
self.unit_cell_hist_window.start_auto_refresh()
except Exception:
pass
return
self.unit_cell_hist_window = UnitCellHistogramWindow(
parent=self,
stream_path=stream_path,
indexing_manager=getattr(self, "indexing_manager", None),
)
# Show dialog unless it immediately closed itself (e.g., missing parser)
if self.unit_cell_hist_window is not None:
if auto_refresh:
try:
self.unit_cell_hist_window.start_auto_refresh()
except Exception:
pass
self.unit_cell_hist_window.show()
[docs]
def open_workspace_unit_cell_fit_window(self):
"""Open the workspace-wide unit-cell Gaussian fit tool."""
workspace_ini_paths = list(getattr(self, "workspace_ini_paths", {}).values())
workspace_path = getattr(self, "workspace_file_path", None) or getattr(self, "full_ini_file_path", None)
if hasattr(self, "workspace_unit_cell_fit_window") and self.workspace_unit_cell_fit_window.isVisible():
self.workspace_unit_cell_fit_window.set_workspace_context(workspace_ini_paths, workspace_path)
self.workspace_unit_cell_fit_window.raise_()
self.workspace_unit_cell_fit_window.activateWindow()
return
self.workspace_unit_cell_fit_window = WorkspaceUnitCellFitWindow(
parent=self,
workspace_ini_paths=workspace_ini_paths,
workspace_path=workspace_path,
)
self.workspace_unit_cell_fit_window.show()
[docs]
def report_issue(self):
recipient_email = "contact-project+kristallorakel-coseda-reportissue@incoming.gitlab.com"
subject = "Issue Report for " + self.window_title
body = "Please describe your issue or feedback."
mailto_link = f"mailto:{recipient_email}?subject={subject}&body={body}"
webbrowser.open(mailto_link)
[docs]
def display_log_file(self):
if not self.log_file_path or not os.path.exists(self.log_file_path):
QMessageBox.information(self, "Log File", "Log file not found.")
return
with open(self.log_file_path, 'r') as file:
log_contents = file.read()
# Create a popup window
dialog = QDialog(self)
dialog.setWindowTitle("Log File")
dialog.resize(600, 400)
layout = QVBoxLayout(dialog)
text_edit = QTextEdit()
text_edit.setPlainText(log_contents)
text_edit.setReadOnly(True) # Make it read-only
layout.addWidget(text_edit)
dialog.exec()