Files

156 lines
4.8 KiB
Python
Raw Permalink Normal View History

"""
Reference-line management: a side-panel list of custom horizontal markers plus a
properties dialog.
`RefLineControlWidget` is a pure view over a list of `RefLineProps` owned by the
main window: it renders the list and emits intents (add / edit / remove / clear).
`RefLineDialog` edits one line's value, colour, line style, and tag.
"""
import logging
from PyQt5.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, QListWidget, QPushButton,
QDialog, QFormLayout, QDoubleSpinBox, QComboBox, QLineEdit, QColorDialog,
QDialogButtonBox,
)
from PyQt5.QtGui import QColor
from PyQt5.QtCore import pyqtSignal
from plotspec import RefLineProps
_STYLE_CHOICES = [("Solid", "solid"), ("Dashed", "dash"), ("Dotted", "dot")]
class RefLineDialog(QDialog):
"""Edit one reference line's properties. Read the result via `result_props`."""
def __init__(self, parent, props: RefLineProps, value_units: str = ""):
super().__init__(parent)
self.setWindowTitle("Reference line")
self._color = props.color
form = QFormLayout(self)
self.value_spin = QDoubleSpinBox()
self.value_spin.setRange(-1e6, 1e6)
self.value_spin.setDecimals(2)
self.value_spin.setValue(props.value)
if value_units:
self.value_spin.setSuffix(f" {value_units}")
form.addRow("Value:", self.value_spin)
self.color_button = QPushButton()
self.color_button.clicked.connect(self._pick_color)
self._refresh_color_button()
form.addRow("Colour:", self.color_button)
self.style_combo = QComboBox()
for label, key in _STYLE_CHOICES:
self.style_combo.addItem(label, key)
idx = self.style_combo.findData(props.style)
if idx >= 0:
self.style_combo.setCurrentIndex(idx)
form.addRow("Line style:", self.style_combo)
self.label_edit = QLineEdit(props.label)
self.label_edit.setPlaceholderText("(optional tag)")
form.addRow("Tag:", self.label_edit)
buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
buttons.accepted.connect(self.accept)
buttons.rejected.connect(self.reject)
form.addRow(buttons)
def _pick_color(self):
chosen = QColorDialog.getColor(QColor(self._color), self, "Reference line colour")
if chosen.isValid():
self._color = chosen.name()
self._refresh_color_button()
def _refresh_color_button(self):
self.color_button.setText(self._color)
# Show the colour as the button's background for a quick read.
self.color_button.setStyleSheet(f"background-color: {self._color};")
def result_props(self) -> RefLineProps:
return RefLineProps(
value=float(self.value_spin.value()),
color=self._color,
style=self.style_combo.currentData(),
label=self.label_edit.text().strip(),
)
class RefLineControlWidget(QWidget):
"""List of reference lines with Add / Edit / Remove / Clear controls."""
addRequested = pyqtSignal()
editRequested = pyqtSignal(int)
removeRequested = pyqtSignal(int)
clearRequested = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
self.logger = logging.getLogger(__name__)
self.initUI()
def initUI(self):
layout = QVBoxLayout(self)
layout.setContentsMargins(5, 5, 5, 5)
group_box = QGroupBox("Reference lines")
group_layout = QVBoxLayout(group_box)
self.line_list = QListWidget()
self.line_list.setMaximumHeight(110)
self.line_list.itemDoubleClicked.connect(self._on_double_click)
group_layout.addWidget(self.line_list)
row = QHBoxLayout()
self.add_button = QPushButton("Add")
self.add_button.clicked.connect(self.addRequested.emit)
row.addWidget(self.add_button)
self.edit_button = QPushButton("Edit…")
self.edit_button.clicked.connect(self._emit_edit)
row.addWidget(self.edit_button)
self.remove_button = QPushButton("Remove")
self.remove_button.clicked.connect(self._emit_remove)
row.addWidget(self.remove_button)
self.clear_button = QPushButton("Clear")
self.clear_button.clicked.connect(self.clearRequested.emit)
row.addWidget(self.clear_button)
group_layout.addLayout(row)
layout.addWidget(group_box)
def set_lines(self, lines: list[RefLineProps]):
"""Repopulate the list display from the current props (preserving selection)."""
current = self.line_list.currentRow()
self.line_list.clear()
for p in lines:
tag = f" {p.label}" if p.label else ""
self.line_list.addItem(f"{p.value:.2f}{tag}")
if 0 <= current < self.line_list.count():
self.line_list.setCurrentRow(current)
def _selected_row(self) -> int:
return self.line_list.currentRow()
def _emit_edit(self):
row = self._selected_row()
if row >= 0:
self.editRequested.emit(row)
def _emit_remove(self):
row = self._selected_row()
if row >= 0:
self.removeRequested.emit(row)
def _on_double_click(self, _item):
self._emit_edit()