269 lines
12 KiB
Python
269 lines
12 KiB
Python
import jwt
|
|
from PySide6.QtCore import Qt, Slot, Signal
|
|
from PySide6.QtGui import QAction
|
|
from PySide6.QtWidgets import (
|
|
QMainWindow,
|
|
QWidget,
|
|
QSplitter,
|
|
QHBoxLayout,
|
|
QVBoxLayout,
|
|
QMessageBox,
|
|
QApplication,
|
|
QDockWidget)
|
|
|
|
from aaredaqlib.coordinate import Coordinate, SmargonCoordinate
|
|
from aaredaqlib.diffraction_geometry import DiffractionGeometry
|
|
from aaredaqlib.models import SampleShortInfoList, TokenData
|
|
from aaredaqlib.sample_geometry import SampleGeometryModel
|
|
|
|
from aaregui.panels.beamline_controls import BeamlineControls
|
|
from aaregui.panels.data_collection_settings import DataCollectionSettings
|
|
from aaregui.panels.sample_queue_panel import SampleQueuePanel
|
|
from aaregui.panels.status_panel import StatusPanel
|
|
from aaregui.panels.tell_sample_panel import TellSamplePanel
|
|
from aaregui.scan_logic.raster_grid_manager import RasterGridManager
|
|
from aaregui.threads.camera_thread import SampleCameraThread
|
|
from aaregui.threads.daq_worker import DAQWorker
|
|
from aaregui.widgets.camera_image import SampleCameraImageLabel
|
|
from aaregui.widgets.no_wheel_scroll_area import NoWheelScrollArea
|
|
from aaregui.widgets.status_bar import StatusBar
|
|
|
|
|
|
class MainWindow(QMainWindow):
|
|
sample_geometry = Signal(SampleGeometryModel)
|
|
|
|
def __init__(self, base_url: str | None,
|
|
token: str,
|
|
default_image: str | None,
|
|
zmq_addr: str | None):
|
|
super().__init__()
|
|
self.__base_url = base_url
|
|
self.__token = token
|
|
|
|
# Decode the JWT without signature verification
|
|
self.__decoded_token = TokenData(**jwt.decode(token, options={"verify_signature": False}))
|
|
print(self.__decoded_token)
|
|
|
|
self.setStyleSheet("background-color: rgb(216, 228, 253);")
|
|
|
|
top_widget = QWidget(parent=self)
|
|
top_widget_layout = QHBoxLayout(top_widget)
|
|
top_widget.setLayout(top_widget_layout)
|
|
|
|
diffraction = DiffractionGeometry(
|
|
energy_keV=12.4,
|
|
dtz_mm=100,
|
|
detector_size_pxl=(1553,1630),
|
|
pixel_size_mm=0.150, #PILATUS 4
|
|
beam_center_pxl=(750, 750)
|
|
)
|
|
|
|
geom = SampleGeometryModel(beam_location_pxl=Coordinate(x=1000,y=1000),
|
|
pixel_in_mm=0.001,
|
|
aerotech=Coordinate(),
|
|
smargon=SmargonCoordinate(sh_mm=Coordinate(), phi_deg=0, chi_deg=0),
|
|
omega_deg=0,
|
|
beam_size_mm=Coordinate(x=0.01, y=0.01),
|
|
aerotech_meas=Coordinate()
|
|
)
|
|
|
|
self.raster = RasterGridManager(geom=geom)
|
|
|
|
collection_controls_scroll = NoWheelScrollArea(top_widget)
|
|
|
|
self.data_collection = DataCollectionSettings(s=geom,
|
|
parent=top_widget,
|
|
raster_mgr=self.raster,
|
|
diffraction=diffraction)
|
|
|
|
|
|
top_widget_layout.addWidget(collection_controls_scroll)
|
|
collection_controls_scroll.setWidget(self.data_collection)
|
|
collection_controls_scroll.setHorizontalScrollBarPolicy(
|
|
Qt.ScrollBarPolicy.ScrollBarAlwaysOff
|
|
)
|
|
collection_controls_scroll.setFixedWidth(self.data_collection.set_width + 10)
|
|
|
|
|
|
self.camera_image = SampleCameraImageLabel(geom=geom, raster=self.raster, parent=top_widget, default_image=default_image)
|
|
|
|
top_widget_layout.addWidget(self.camera_image)
|
|
|
|
beamline_controls_scroll = NoWheelScrollArea(top_widget)
|
|
|
|
self.beamline = BeamlineControls(beamline_controls_scroll, staff=self.__decoded_token.staff)
|
|
top_widget_layout.addWidget(beamline_controls_scroll)
|
|
beamline_controls_scroll.setWidget(self.beamline)
|
|
beamline_controls_scroll.setHorizontalScrollBarPolicy(
|
|
Qt.ScrollBarPolicy.ScrollBarAlwaysOff
|
|
)
|
|
beamline_controls_scroll.setFixedWidth(self.beamline.set_width + 10)
|
|
|
|
self.tell_samples = TellSamplePanel(samples=SampleShortInfoList(s = []))
|
|
self.job_list_panel = SampleQueuePanel()
|
|
self.status_panel = StatusPanel()
|
|
|
|
self.tell_samples_dock = QDockWidget("Sample List", self)
|
|
self.tell_samples_dock.setWidget(self.tell_samples)
|
|
self.tell_samples_dock.setAllowedAreas(Qt.DockWidgetArea.BottomDockWidgetArea)
|
|
self.addDockWidget(Qt.DockWidgetArea.BottomDockWidgetArea, self.tell_samples_dock)
|
|
|
|
self.job_list_dock = QDockWidget("Automation list", self)
|
|
self.job_list_dock.setWidget(self.job_list_panel)
|
|
self.job_list_dock.setAllowedAreas(Qt.DockWidgetArea.BottomDockWidgetArea)
|
|
self.addDockWidget(Qt.DockWidgetArea.BottomDockWidgetArea, self.job_list_dock)
|
|
|
|
self.status_panel_dock = QDockWidget("Beamline status", self)
|
|
self.status_panel_dock.setWidget(self.status_panel)
|
|
self.status_panel_dock.setAllowedAreas(Qt.DockWidgetArea.BottomDockWidgetArea)
|
|
self.addDockWidget(Qt.DockWidgetArea.BottomDockWidgetArea, self.status_panel_dock)
|
|
|
|
self.setCentralWidget(top_widget)
|
|
|
|
self.setWindowTitle("AareGUI")
|
|
self.create_menu_bar()
|
|
|
|
self.status_bar = StatusBar(self.__decoded_token, parent=self)
|
|
self.setStatusBar(self.status_bar)
|
|
|
|
self.daq = DAQWorker(base_url=self.__base_url, token=self.__token)
|
|
self.daq.spreadsheet.connect(self.tell_samples.new_sample_list)
|
|
self.daq.update.connect(self.beamline.omega_panel.update_daq_status)
|
|
self.daq.update.connect(self.beamline.smargon_panel.update_daq_status)
|
|
self.daq.update.connect(self.data_collection.update_daq_status)
|
|
self.daq.update.connect(self.beamline.illumination_panel.update_daq_status)
|
|
self.daq.update.connect(self.status_panel.update_daq_status)
|
|
self.daq.update.connect(self.camera_image.update_daq_status)
|
|
self.daq.update.connect(self.tell_samples.update_daq_status)
|
|
self.daq.update.connect(self.beamline.abr_tweak.update_daq_status)
|
|
self.daq.update.connect(self.status_bar.update_daq_status)
|
|
self.daq.update.connect(self.beamline.samcam.update_daq_status)
|
|
self.beamline.samcam.changed.connect(self.daq.samcam_settings)
|
|
self.beamline.state_panel.sample_exchange.clicked.connect(self.daq.sample_exchange)
|
|
self.beamline.state_panel.sample_alignment.clicked.connect(self.daq.sample_alignment)
|
|
self.beamline.state_panel.dewar_exchange.clicked.connect(self.daq.dewar_exchange)
|
|
self.beamline.loopctr.background.clicked.connect(self.daq.alc_background)
|
|
self.beamline.loopctr.find_tip.clicked.connect(self.daq.center_loop)
|
|
self.camera_image.smargon.connect(self.daq.move_smargon)
|
|
self.beamline.smargon_panel.smargon.connect(self.daq.move_smargon)
|
|
|
|
self.beamline.omega_panel.set_omega.connect(self.daq.set_omega)
|
|
self.camera_image.set_omega.connect(self.daq.set_omega)
|
|
self.beamline.zoom_panel.zoom.connect(self.daq.zoom)
|
|
self.beamline.illumination_panel.light.connect(self.daq.light)
|
|
self.beamline.abr_tweak.abr_tweak.connect(self.daq.abr_tweak)
|
|
self.beamline.abr_tweak.abr_save.connect(self.daq.abr_save)
|
|
|
|
self.beamline.beam_mark.beam_mark_clear.connect(self.daq.beam_mark_clear)
|
|
self.camera_image.update_beam_center.connect(self.daq.beam_mark_add)
|
|
self.beamline.beam_center.beam_center.connect(self.daq.beam_center)
|
|
|
|
if zmq_addr is not None:
|
|
self.camera_thread = SampleCameraThread(zmq_url=zmq_addr)
|
|
self.camera_thread.camera_image.connect(self.camera_image.update_pixmap)
|
|
self.camera_thread.start()
|
|
else:
|
|
self.camera_thread = None
|
|
|
|
QApplication.instance().aboutToQuit.connect(self.cleanup)
|
|
|
|
#
|
|
# self.data_collection.helical.helical_scan.connect(self.worker.helical_scan)
|
|
# self.data_collection.helical.update_bookmarks.connect(
|
|
# self.camera_image.update_bookmarks
|
|
# )
|
|
#
|
|
|
|
self.camera_image.autofocus.connect(self.daq.autofocus)
|
|
|
|
self.camera_image.evaluate_grid.connect(self.raster.run_grid_scan)
|
|
self.data_collection.raster.evaluate_grid.connect(self.raster.run_grid_scan)
|
|
|
|
self.camera_image.clear_grid.connect(self.raster.clear_active_grid)
|
|
self.daq.run_number_incremented.connect(self.data_collection.file_path_panel.increment_run_number)
|
|
|
|
self.job_list_panel.auto_scan.connect(self.daq.automated_scan)
|
|
self.daq.automated_scan_done.connect(self.job_list_panel.automated_scan_done)
|
|
self.job_list_panel.unmount.connect(self.daq.unmount)
|
|
|
|
self.tell_samples.mount.connect(self.daq.mount)
|
|
self.tell_samples.unmount.connect(self.daq.unmount)
|
|
|
|
self.data_collection.raster.grid_size_updated.connect(self.raster.update_grid_size)
|
|
self.data_collection.raster.exp_time_updated.connect(self.raster.update_exposure_time)
|
|
self.data_collection.raster.transmission_updated.connect(self.raster.update_transmission)
|
|
self.data_collection.raster.dtz_updated.connect(self.raster.update_dtz)
|
|
|
|
self.raster.grid_scan.connect(self.daq.raster_scan)
|
|
self.data_collection.screening.rotation_scan.connect(self.daq.standard_scan)
|
|
|
|
self.raster.grid_scan_size_changed.connect(self.data_collection.raster.grid_scan_size_change)
|
|
self.status_bar.set_pgroup.connect(self.daq.set_pgroup)
|
|
self.status_bar.end_session.connect(self.daq.end_session)
|
|
self.status_bar.force_session.connect(self.daq.force_session)
|
|
self.status_bar.dewar_exchange.connect(self.daq.dewar_exchange)
|
|
self.status_bar.sample_exchange.connect(self.daq.sample_exchange)
|
|
self.status_bar.sample_alignment.connect(self.daq.sample_alignment)
|
|
self.status_bar.close_shutter.connect(self.daq.close_shutter)
|
|
|
|
def create_menu_bar(self):
|
|
"""Create a menu bar with File->Quit and Help->About."""
|
|
# Main menu bar
|
|
menu_bar = self.menuBar()
|
|
|
|
# File menu
|
|
file_menu = menu_bar.addMenu("File")
|
|
quit_action = QAction("&Quit", self) # Action for 'Quit'
|
|
quit_action.triggered.connect(self.close) # Connect to window's `close` method
|
|
file_menu.addAction(quit_action)
|
|
|
|
view_menu = menu_bar.addMenu("View")
|
|
show_samples_action = QAction("Show Sample List", self)
|
|
show_samples_action.setCheckable(True)
|
|
show_samples_action.setChecked(True)
|
|
show_samples_action.triggered.connect(lambda: self.tell_samples_dock.setVisible(show_samples_action.isChecked()))
|
|
|
|
# When dock visibility changes, update the action's checked state
|
|
self.tell_samples_dock.visibilityChanged.connect(show_samples_action.setChecked)
|
|
view_menu.addAction(show_samples_action)
|
|
|
|
show_job_list_action = QAction("Show job List", self)
|
|
show_job_list_action.setCheckable(True)
|
|
show_job_list_action.setChecked(True)
|
|
show_job_list_action.triggered.connect(self.job_list_dock.setVisible)
|
|
# When dock visibility changes, update the action's checked state
|
|
show_job_list_action.triggered.connect(lambda: self.job_list_dock.setVisible(show_job_list_action.isChecked()))
|
|
view_menu.addAction(show_job_list_action)
|
|
|
|
show_beamline_status_action = QAction("Show beamline status", self)
|
|
show_beamline_status_action.setCheckable(True)
|
|
show_beamline_status_action.setChecked(True)
|
|
show_beamline_status_action.triggered.connect(self.status_panel_dock.setVisible)
|
|
# When dock visibility changes, update the action's checked state
|
|
show_beamline_status_action.triggered.connect(lambda: self.status_panel_dock.setVisible(show_beamline_status_action.isChecked()))
|
|
self.status_panel_dock.visibilityChanged.connect(show_beamline_status_action.setChecked)
|
|
|
|
view_menu.addAction(show_beamline_status_action)
|
|
|
|
# Help menu
|
|
help_menu = menu_bar.addMenu("Help")
|
|
about_action = QAction("About", self) # Action for 'About'
|
|
about_action.triggered.connect(self.show_about_dialog)
|
|
help_menu.addAction(about_action)
|
|
|
|
def show_about_dialog(self):
|
|
QMessageBox.about(
|
|
self,
|
|
"About",
|
|
"Aare Macromolecular Crystallography GUI\nVersion: 1.0\nCopyright: Paul Scherrer Instiute 2024-2025",
|
|
)
|
|
|
|
@Slot(str)
|
|
def display_error(self, msg: str):
|
|
self.status_bar.setStyleSheet("color: red;")
|
|
self.status_bar.showMessage(f'<span style="color: red; "> {msg} </span>', 10000)
|
|
|
|
def cleanup(self):
|
|
if self.camera_thread is not None:
|
|
self.camera_thread.stop()
|