Source code for src.gui.instruments.cryomag_panels

"""Panels for controlling cryostat-magnet systems

This module provides a versatile panel for receiving data from and sending
data to a cryostat-magnet system, featuring an indicator to show received
values, a control for setting new values, and a button for sending commands.
"""

import string 
import wx

from src.gui import images as img

# pylint: disable=W0221,C0103,W0613
_BUTTON_SIZE_ADDITION = 6

_LABEL_OPTS = wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL

[docs]class GridPanel(wx.Panel): """A panel for displaying and setting values, allowing arbitrary rows. This panel features four columns: 1. A label to indicate what the row represents. 2. An indicator to show a measured value. 3. A control for specifying new setpoints. 4. A button for sending the setpoint to the appropriate handler. The rows which receive buttons is somewhat customizable. If the `buttonIDs` is set to `None`, no buttons will be created. If it is a single integer or a list of integers of length 1, a single button will be created in the last row. If it is a list of integers of the same length as the list of row labels, each row will receive a button. Even if no buttons are to be added, the space will be reserved for them to ensure that, when multiple instances of this panel are placed above and below one another, the controls will still line up aesthetically. Parameters ---------- parent : wx.Window The frame or panel which owns this panel. panelLabel : str The string which labels the panel (in the border). rowLabels : list of str A list of strings which label the rows in the grid. buttonIDs : list of int A list of integer IDs for the buttons so that the parent can bind actions to them. """ def __init__(self, parent, panelLabel, rowLabels, buttonIDs=None): """Create a new GridPanel.""" super(GridPanel, self).__init__(parent, wx.ID_ANY) # outerpanel = wx.StaticBox(self, wx.ID_ANY, panelLabel) # outersizer = wx.StaticBoxSizer(outerpanel, wx.VERTICAL) outersizer = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, panelLabel), wx.VERTICAL) mainpanel = wx.Panel(self) mainsizer = wx.FlexGridSizer(len(rowLabels)+1, 4, 5, 5) mainpanel.SetSizer(mainsizer) mainsizer.AddGrowableCol(0, 1) okBitmap = img.getOkBitmap() buttonSize = [x + _BUTTON_SIZE_ADDITION for x in okBitmap.GetSize()] #-- HEADER ROW --------------------------------------------------------- mainsizer.Add(wx.StaticText(mainpanel, wx.ID_ANY, label=''), 0, wx.ALIGN_CENTER_HORIZONTAL, 5) mainsizer.Add(wx.StaticText(mainpanel, wx.ID_ANY, label='Current'), 0, wx.ALIGN_CENTER_HORIZONTAL, 5) mainsizer.Add(wx.StaticText(mainpanel, wx.ID_ANY, label='Setpoint'), 0, wx.ALIGN_CENTER_HORIZONTAL, 5) if buttonIDs is not None: mainsizer.Add(wx.StaticText(mainpanel, wx.ID_ANY, label='Set'), 0, wx.ALIGN_CENTER_HORIZONTAL, 5) else: mainsizer.AddSpacer(buttonSize) #-- ITEMS -------------------------------------------------------------- self.currentCtrls = [] self.setpointCtrls = [] for index, label in enumerate(rowLabels): # Row label mainsizer.Add(wx.StaticText(mainpanel, wx.ID_ANY, label=label), 1, _LABEL_OPTS, 5) # Current value indicator indicator = wx.TextCtrl(mainpanel, wx.ID_ANY, style=(wx.TE_RIGHT|wx.TE_RICH2| wx.TE_READONLY), value='0.0') indicator.SetBackgroundColour(wx.WHITE) indicator.SetForegroundColour(wx.BLUE) mainsizer.Add(indicator, 0, wx.EXPAND, 5) self.currentCtrls.append(indicator) # Setpoint control control = wx.TextCtrl(mainpanel, wx.ID_ANY, style=wx.TE_RIGHT, validator=CharValidator(), value='0.0') mainsizer.Add(control, 0, wx.EXPAND, 5) self.setpointCtrls.append(control) # Button if isinstance(buttonIDs, int): buttonIDs = [buttonIDs] if buttonIDs is None: mainsizer.AddSpacer(buttonSize) elif len(buttonIDs) == 1: if index == len(rowLabels) - 1: mainsizer.Add(wx.BitmapButton(mainpanel, buttonIDs[0], okBitmap, size=buttonSize), 0, wx.EXPAND, 5) else: mainsizer.AddSpacer(buttonSize) elif len(buttonIDs) == len(rowLabels): mainsizer.Add(wx.BitmapButton(mainpanel, buttonIDs[index], okBitmap, size=buttonSize), 0, wx.EXPAND, 5) outersizer.Add(mainpanel, 1, wx.ALL|wx.EXPAND, 5) self.SetSizerAndFit(outersizer)
[docs] def getSetpoints(self): """Get the values of the setpoints. Returns ------- list of float The values of the setpoint controls. """ ans = [] for setpoint in self.setpointCtrls: try: value = float(setpoint.GetValue()) except ValueError: value = 0.0 setpoint.SetValue('0.0') ans.append(value) return ans
[docs] def setCurrents(self, values): """Set the values of the current indicators. Parameters ---------- values : list of float Set the indicators for the current values of the parameters contained in the panel. """ for (current, value) in zip(self.currentCtrls, values): current.SetValue(str(value))
[docs]class CharValidator(wx.PyValidator): """A validator to ensure that only digits are entered into a control.""" def __init__(self): """Create a new validator.""" super(CharValidator, self).__init__() self.Bind(wx.EVT_CHAR, self.OnChar)
[docs] def OnChar(self, event): """Make sure the character is a digit.""" key = event.GetKeyCode() if key in [wx.WXK_LEFT, wx.WXK_RIGHT]: event.Skip() else: try: key = chr(event.GetKeyCode()) win = self.GetWindow() if '.' in win.GetValue() and key == '.': return if key in string.letters or key == ' ': return except ValueError: pass event.Skip()
[docs] def Validate(self, win): """Nothing needs doing when the frame is closed.""" return True
[docs] def Clone(self): """Create a new validator.""" return CharValidator()
[docs] def TransferToWindow(self): """No data needs to be transferred.""" return True
[docs] def TransferFromWindow(self): """No data needs to be transferred.""" return True