Skip to content

Commit

Permalink
segment handling
Browse files Browse the repository at this point in the history
  • Loading branch information
JoschD committed Dec 4, 2023
1 parent f4d3dc5 commit 8b29b49
Show file tree
Hide file tree
Showing 11 changed files with 426 additions and 115 deletions.
2 changes: 1 addition & 1 deletion omc3_gui/segment_by_segment.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import sys
from omc3_gui.segment_by_segment.controller import SbSController
from omc3_gui.segment_by_segment.main_controller import SbSController
from omc3_gui.utils.log_handler import init_logging
import logging

Expand Down
69 changes: 0 additions & 69 deletions omc3_gui/segment_by_segment/controller.py

This file was deleted.

8 changes: 8 additions & 0 deletions omc3_gui/segment_by_segment/defaults.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@


DEFAULT_SEGMENTS =(
("IP1", "BPM.12L1", "BPM.12R1"),
("IP2", "BPM.12L2", "BPM.12R2"),
("IP5", "BPM.12L5", "BPM.12R5"),
("IP8", "BPM.12L8", "BPM.12R8"),
)
163 changes: 163 additions & 0 deletions omc3_gui/segment_by_segment/main_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
from pathlib import Path
from typing import List, Sequence, Tuple
from omc3_gui.segment_by_segment.segment_model import SegmentModel
from omc3_gui.utils.base_classes import Controller
from omc3_gui.utils.file_dialogs import OpenDirectoriesDialog, OpenDirectoryDialog
from omc3_gui.segment_by_segment.main_view import SbSWindow
from omc3_gui.segment_by_segment.main_model import SegmentTableModel, Settings
from qtpy.QtCore import Qt, Signal, Slot
from qtpy.QtWidgets import QFileDialog
from omc3_gui.segment_by_segment.measurement_model import OpticsMeasurement
import logging
from omc3_gui.segment_by_segment.measurement_view import OpticsMeasurementDialog
from omc3_gui.segment_by_segment.defaults import DEFAULT_SEGMENTS

LOGGER = logging.getLogger(__name__)

class SbSController(Controller):

settings: Settings
_view: SbSWindow # for the IDE

def __init__(self):
super().__init__(SbSWindow())
self.connect_signals()
self.settings = Settings()
self._last_selected_optics_path = None


def add_measurement(self, measurement: OpticsMeasurement):
self._view.get_measurement_list().add_item(measurement)


def connect_signals(self):
self._view.sig_load_button_clicked.connect(self.open_measurements)
self._view.sig_edit_optics_button_clicked.connect(self.edit_measurement)
self._view.sig_list_optics_double_clicked.connect(self.edit_measurement)
self._view.sig_list_optics_selected.connect(self.view_measurement)

self._view.sig_new_segment_button_clicked.connect(self.new_segment)
self._view.sig_copy_segment_button_clicked.connect(self.copy_segment)
# self._view.sig_run_segment_button_clicked.connect(self.run_segment)
# self._view.sig_remove_segment_button_clicked.connect(self.remove_segment)
# self._view.sig_remove_button_clicked.connect(self.remove_measurement)
self._view.sig_default_segments_button_clicked.connect(self.add_default_segments)
# self._view.sig_load_segments_button_clicked.connect(self.load_segments)

self._view.sig_table_segments_selected.connect(self.view_segment)

# Measurements ---
@Slot()
def open_measurements(self):
LOGGER.debug("Opening new optics measurement. Asking for folder paths.")
filenames = OpenDirectoriesDialog(
parent=self._view,
caption="Select Optics Folders",
directory=str(self._last_selected_optics_path) if self._last_selected_optics_path else None,
).run_selection_dialog()

loaded_measurements = self._view.get_measurement_list()
measurement_indices = []

LOGGER.debug(f"User selected {len(filenames)} files.")
for filename in filenames:
LOGGER.debug(f"adding: {filename!s}")
optics_measurement = OpticsMeasurement.from_path(filename)
try:
loaded_measurements.add_item(optics_measurement)
except ValueError as e:
LOGGER.error(str(e))
else:
measurement_indices.append(loaded_measurements.get_index(optics_measurement))

self._last_selected_optics_path = filename.parent

self._view.set_selected_measurements(measurement_indices)

@Slot(OpticsMeasurement)
def edit_measurement(self, measurement: OpticsMeasurement):
LOGGER.debug(f"Opening edit dialog for {measurement.display()}.")
dialog = OpticsMeasurementDialog(
parent=self._view,
optics_measurement=measurement,
)
if dialog.exec_():
LOGGER.debug("Edit dialog closed. Updating measurement.")

@Slot(tuple)
def view_measurement(self, measurements: Sequence[OpticsMeasurement]):
LOGGER.debug(f"Showing {len(measurements)} measurements.")
if len(measurements) > 1:
LOGGER.debug("More than one measurement selected.")
return

measurement = measurements[0]
segment_model = SegmentTableModel()
segment_model.add_items(measurement.segments.values())
self._view.set_segments(segment_model)


# Segments ---
@Slot(tuple)
def view_segment(self, segments: Sequence[SegmentModel]):
LOGGER.debug(f"Showing {len(segments)} segments.")
if len(segments) > 1:
LOGGER.debug("More than one segment selected. Clearing Plots.")
return

segment = segments[0]
# Plot segements

@Slot()
def add_default_segments(self):
LOGGER.debug("Adding default segments.")
selected_measurements = self._view.get_selected_measurements()
if not selected_measurements:
LOGGER.error("Please select at least one measurement.")
return

for measurement in selected_measurements:
beam = measurement.beam
if beam is None:
LOGGER.error(f"No beam found in measurement {measurement.display()}. Cannot add default segments.")
continue

for segment_tuple in DEFAULT_SEGMENTS:
segment = SegmentModel(*segment_tuple)
segment.start = f"{segment.start}.B{beam}"
segment.end = f"{segment.end}.B{beam}"
measurement.segments[segment.name] = segment

self.view_measurement(selected_measurements)

@Slot()
def new_segment(self):
LOGGER.debug("Creating new segment.")
selected_measurements = self._view.get_selected_measurements()
if not selected_measurements:
LOGGER.error("Please select at least one measurement.")
return

# add the same segment to all selected measurements!
# This allows for renaming it in one, and having the same name in all.
# Might have unexpected side-effects. (jdilly)
new_segment = SegmentModel("New Segment")
for measurement in selected_measurements:
measurement.segments[new_segment.name] = new_segment

self.view_measurement(selected_measurements)

@Slot(tuple)
def copy_segment(self, segments: Sequence[SegmentModel]):
LOGGER.debug(f"Copying {len(segments)} segments.")
selected_measurements = self._view.get_selected_measurements()
if not selected_measurements:
LOGGER.error("Please select at least one measurement.")
return

for measurement in selected_measurements:
for segment in segments:
new_segment = SegmentModel(f"{segment.name} - Copy", start=segment.start, end=segment.end)
measurement.segments[new_segment.name] = new_segment

self.view_measurement(selected_measurements)
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import enum
import types
from dataclasses import dataclass, fields
from pathlib import Path
from typing import Any, ClassVar, Dict, List, Optional, Sequence, Union
from dataclasses import dataclass
import logging
from typing import Any, Dict, Sequence

from qtpy import QtCore
from qtpy.QtCore import Qt

import pyqtgraph as pg
from accwidgets.graph import StaticPlotWidget
from omc3.segment_by_segment.segments import Segment
from qtpy import QtCore, QtWidgets
from qtpy.QtCore import QModelIndex, Qt

from omc3_gui.segment_by_segment.measurement_model import OpticsMeasurement
from omc3_gui.segment_by_segment.segment_model import SegmentModel

LOGGER = logging.getLogger(__name__)


@dataclass
Expand All @@ -30,9 +30,18 @@ def try_emit(self, emit: bool = True):

if hasattr(self, "dataChanged"):
# TODO: return which data has actually changed?
self.dataChanged.emit(self.index(0), self.index(len(self.items)-1), [Qt.EditRole])
try:
idx_start = self.index(0) # list
idx_end = self.index(len(self.items)-1)
except TypeError:
idx_start = self.index(0, 0) # table
idx_end = self.index(self.rowCount()-1, self.columnCount()-1)
self.headerDataChanged.emit(Qt.Horizontal, 0, self.columnCount()-1)
self.dataChanged.emit(idx_start, idx_end)

def update_item(self, item):
def update_item(self, item, old_name: str = None):
if old_name is not None:
self.items.pop(old_name)
self.items[str(item)] = item
self.try_emit()

Expand Down Expand Up @@ -70,6 +79,13 @@ def remove_items_at(self, indices: Sequence):
def get_item_at(self, index: int) -> Any:
return list(self.items.values())[index]

def get_index(self, item: Any) -> QtCore.QModelIndex:
idx_item = list(self.items.keys()).index(str(item))
try:
return self.index(idx_item)
except TypeError:
return self.index(idx_item, 0)


class MeasurementListModel(QtCore.QAbstractListModel, ItemDictModel):

Expand Down Expand Up @@ -101,7 +117,8 @@ def __init__(self, *args, **kwargs):
def data(self, index: QtCore.QModelIndex, role: int = Qt.DisplayRole):

meas: OpticsMeasurement = self.get_item_at(index.row())
if role == Qt.DisplayRole: # https://doc.qt.io/qt-5/qt.html#ItemDataRole-enum
# https://doc.qt.io/qt-5/qt.html#ItemDataRole-enum
if role == Qt.DisplayRole:
return meas.display()

if role == Qt.ToolTipRole:
Expand All @@ -122,10 +139,9 @@ class SegmentTableModel(QtCore.QAbstractTableModel, ItemDictModel):
_COLUMNS = {0: "Segment", 1: "Start", 2: "End"}
_COLUMNS_MAP = {0: "name", 1: "start", 2: "end"}

items: Dict[str, Segment]
items: Dict[str, SegmentModel]

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
super(QtCore.QAbstractTableModel, self).__init__(*args, **kwargs)
super(ItemDictModel, self).__init__()

Expand All @@ -143,13 +159,43 @@ def columnCount(self, parent=QtCore.QModelIndex()):
def data(self, index: QtCore.QModelIndex, role=QtCore.Qt.DisplayRole):
i = index.row()
j = index.column()
segment: Segment = self.get_item_at(i)
segment: SegmentModel = self.get_item_at(i)

if role == QtCore.Qt.DisplayRole:
return str(getattr(segment, self._COLUMNS_MAP[j]))

if role == Qt.EditRole:
return segment
return segment

def setData(self, index, value, role):
i = index.row()
j = index.column()
segment: SegmentModel = self.get_item_at(i)
old_name = segment.name

if role == Qt.EditRole:
if value is None or value == "":
return False

setattr(segment, self._COLUMNS_MAP[j], value)
if segment.name != old_name:
self.update_item(segment, old_name=old_name)
else:
self.dataChanged.emit(index, index)
return True

def toggle_row(self, index):
i = index.row()
segment: SegmentModel = self.get_item_at(i)
segment.enabled = not segment.enabled
self.headerDataChanged.emit(Qt.Horizontal, 0, self.rowCount() - 1)
self.dataChanged.emit(self.index(i, 0), self.index(i, self.rowCount() - 1))

def flags(self, index):
return QtCore.Qt.ItemIsEnabled
i = index.row()
j = index.column()
segment: SegmentModel = self.get_item_at(i)
if segment.enabled:
return Qt.ItemIsEnabled | Qt.ItemIsEditable | Qt.ItemIsSelectable

return Qt.ItemIsEditable | Qt.ItemIsSelectable
Loading

0 comments on commit 8b29b49

Please sign in to comment.