Source code for siriushla.widgets.selection_matrix

"""Selection Widget."""

from functools import partial as _part

from qtpy.QtWidgets import QGridLayout, QHBoxLayout, QLabel, QCheckBox,\
    QScrollArea, QWidget, QPushButton, QSizePolicy as QSzPol
from qtpy.QtCore import Qt, QRect, QPoint, Signal
from qtpy.QtGui import QBrush, QColor, QPainter

from siriushla.widgets.led import SiriusLedAlert

from .QLed import QLed
from .pushbutton import CAPushButton


[docs] class SelectionMatrixWidget(QWidget): """Widget to perform component selection. Parameters ---------- parent : QWidget, optional The parent widget for the SelectionMatrixWidget. title: str, optional Widget title. has_bothplanes: bool, optional Whether to show button to send applyBothPlanesClicked signal. Default: False. toggle_all_false_text: string, optional Text to be displayed in toggleAllItems to False button. Default: 'Disable All'. show_toggle_all_false: bool, optional Whether to show button to send toggleAllItems to False. Default: True. toggle_all_true_text: string, optional Text to be displayed in toggleAllItems to True button. Default: 'Enable All'. show_toggle_all_true: bool, optional Whether to show button to send toggleAllItems to True. Default: True. use_scroll: bool, optional Whether to use or not QScrollArea. Default: True. rules: PyDM rules to be applied in write buttons. Default: None. Signals ------- applyChangesClicked: emitted when "Apply Changes" button is clicked applyBothPlanesClicked: emitted when "Apply Both Planes" button is clicked """ applyChangesClicked = Signal() applyBothPlanesClicked = Signal() def __init__( self, parent=None, title='', has_bothplanes=False, toggle_all_false_text='Disable All', show_toggle_all_false=True, toggle_all_true_text='Enable All', show_toggle_all_true=True, use_scroll=True, rules=None): """Init.""" super().__init__(parent) self.title = title self.has_bothplanes = has_bothplanes self.toggle_all_false_text = toggle_all_false_text self.show_toggle_all_false = show_toggle_all_false self.toggle_all_true_text = toggle_all_true_text self.show_toggle_all_true = show_toggle_all_true self.use_scroll = use_scroll self.enbl_rules = rules self.begin = QPoint() self.end = QPoint() self._top_headers, self._side_headers = self.get_headers() self._widgets = self.get_widgets() self._positions = self.get_positions() self._top_header_wids, self._side_header_wids = list(), list() self._setupUi() def _setupUi(self, side_header_size_em=2, top_header_size_em=2): lay = QGridLayout(self) if self.title: lab = QLabel(self.title, self, alignment=Qt.AlignCenter) lab.setStyleSheet("font-weight: bold;") lay.addWidget(lab, 0, 0, 1, 1) # scroll area + widgets matrix scr_ar_wid = QWidget() scr_ar_wid.setObjectName('scrollarea') scr_ar_wid.setStyleSheet( '#scrollarea {background-color: transparent;}') if self.use_scroll: scr_ar = QScrollArea(self) scr_ar.setWidget(scr_ar_wid) scr_ar.setWidgetResizable(True) scr_ar.setSizeAdjustPolicy(scr_ar.AdjustToContents) lay.addWidget(scr_ar, 1, 0, 1, 1) else: scr_ar_wid.setSizePolicy(QSzPol.Maximum, QSzPol.Maximum) lay.addWidget(scr_ar_wid, 1, 0, 1, 1) glay = QGridLayout(scr_ar_wid) glay.setContentsMargins(0, 0, 0, 0) for i, head in enumerate(self._top_headers): head_wid = QPushButton(head, self) head_wid.setStyleSheet(f'min-width:{top_header_size_em:f}em;') head_wid.clicked.connect( _part(self.selectWidgetsAt, i, isrow=False)) self._top_header_wids.append(head_wid) glay.addWidget(head_wid, 0, i+1) for i, head in enumerate(self._side_headers): head_wid = QPushButton(head, self) head_wid.setStyleSheet(f'min-width:{side_header_size_em:f}em;') head_wid.clicked.connect( _part(self.selectWidgetsAt, i, isrow=True)) self._side_header_wids.append(head_wid) glay.addWidget(head_wid, i+1, 0) for i, wid in enumerate(self._widgets): pos = self._positions[i] glay.addWidget(wid, pos[0]+1, pos[1]+1) led = wid.findChild(QLed) if not led: continue led.clicked.connect(led.toggleSelected) # action buttons hlay = QHBoxLayout() hlay.addStretch() self.btn_unsel_all = QPushButton('Undo Selection') self.btn_unsel_all.setDefault(True) self.btn_unsel_all.clicked.connect(self.undoItemsSelection) hlay.addWidget(self.btn_unsel_all) hlay.addStretch() self.btn_dsbl_all = QPushButton(self.toggle_all_false_text) self.btn_dsbl_all.clicked.connect(_part(self.toggleAllItems, False)) self.btn_dsbl_all.setVisible(self.show_toggle_all_false) hlay.addWidget(self.btn_dsbl_all) if self.show_toggle_all_false: hlay.addStretch() self.btn_enbl_all = QPushButton(self.toggle_all_true_text) self.btn_enbl_all.clicked.connect(_part(self.toggleAllItems, True)) self.btn_enbl_all.setVisible(self.show_toggle_all_true) hlay.addWidget(self.btn_enbl_all) if self.show_toggle_all_true: hlay.addStretch() self.btn_send = CAPushButton('Apply Changes') self.btn_send.rules = self.enbl_rules self.btn_send.clicked.connect(self.applyChangesClicked.emit) hlay.addWidget(self.btn_send) hlay.addStretch() if self.has_bothplanes: self.btn_send_otpl = CAPushButton('Apply Both Planes') self.btn_send_otpl.rules = self.enbl_rules self.btn_send_otpl.clicked.connect( self.applyBothPlanesClicked.emit) hlay.addWidget(self.btn_send_otpl) hlay.addStretch() lay.addLayout(hlay, 2, 0, 1, 1) lay.setSizeConstraint(lay.SetMinimumSize)
[docs] def paintEvent(self, _): """Paint event to draw selection rectangle.""" if self.begin == self.end: return painter = QPainter(self) brush = QBrush(QColor(100, 10, 10, 40)) painter.setBrush(brush) painter.drawRect(QRect(self.begin, self.end))
[docs] def mousePressEvent(self, event): """Mouse press event.""" self.begin = event.pos() self.end = event.pos() self.update()
[docs] def mouseMoveEvent(self, event): """Mouse move event.""" self.end = event.pos() self.update()
[docs] def mouseReleaseEvent(self, event): """Mouse release event.""" self.end = event.pos() self.selectItems() self.begin = event.pos() self.update()
[docs] def selectItems(self): """Select items.""" for wid in self._widgets: if not wid.isVisible(): continue led = wid.findChild(QLed) if led and self._is_within_area(led): led.toggleSelected() check = wid.findChild(QCheckBox) if check and self._is_within_area(check): check.toggle()
def _is_within_area(self, wid): pos = wid.mapTo(self, wid.pos()) _sz = wid.size() _x1 = pos.x()+_sz.width()/2 > self.begin.x() _x2 = pos.x()+_sz.width()/2 > self.end.x() _y1 = pos.y()+_sz.height()/2 > self.begin.y() _y2 = pos.y()+_sz.height()/2 > self.end.y() if _x1 != _x2 and _y1 != _y2: return True return False
[docs] def undoItemsSelection(self): """Undo items selection.""" for wid in self._widgets: led = wid.findChild(QLed) if led: led.setSelected(False) check = wid.findChild(QCheckBox) if check: check.setChecked(False)
[docs] def toggleAllItems(self, value): """Toggle all items.""" for wid in self._widgets: led = wid.findChild(QLed) if led: new_sel = (bool(led.state) != value) if isinstance(led, SiriusLedAlert): if not new_sel: led.setSelected(True) else: if new_sel: led.setSelected(True) check = wid.findChild(QCheckBox) if check: check.setChecked(value)
[docs] def selectWidgetsAt(self, idx, isrow=False): """Select widgets at idx (row or column).""" for i, wid in enumerate(self._widgets): row, col = self._positions[i] if (isrow and row == idx) or (not isrow and col == idx): led = wid.findChild(QLed) if led: led.toggleSelected() check = wid.findChild(QCheckBox) if check: check.toggle()
# --- properties --- @property def headers(self): """Return top and side header text lists, respectively.""" return self._top_headers, self._side_headers @property def header_widgets(self): """Return top and side header widget lists, respectively.""" return self._top_header_wids, self._side_header_wids @property def widgets(self): """Return widget list.""" return self._widgets @property def positions(self): """Return widget position list.""" return self._positions # --- specific methods, should be implemented in derivation ---
[docs] def get_headers(self): """ Should be implemented in class derivation. Return ------ top_headers: tuple or list A list of strings for top headers of the selection matrix widget. side_headers: tuple or list A list of strings for side headers of the selection matrix widget. """ raise NotImplementedError
[docs] def get_widgets(self): """ Should be implemented in class derivation. Return ------ widgets: tuple or list A tuple or list of widgets to be put in matrix. """ raise NotImplementedError
[docs] def get_positions(self): """ Should be implemented in class derivation. Return ------ positions: tuple or list A tuple or list of layout positions for each widget returned by get_widgets. """ raise NotImplementedError