def __init__(self, parent=None, logname=None, level=logging.NOTSET): QWidget.__init__(self, parent=parent) # Create Widgets self.label = QLabel('Minimum displayed log level: ', parent=self) self.combo = QComboBox(parent=self) self.text = QPlainTextEdit(parent=self) self.text.setReadOnly(True) self.clear_btn = QPushButton("Clear", parent=self) # Create layout layout = QVBoxLayout() level_control = QHBoxLayout() level_control.addWidget(self.label) level_control.addWidget(self.combo) layout.addLayout(level_control) layout.addWidget(self.text) layout.addWidget(self.clear_btn) self.setLayout(layout) # Allow QCombobox to control log level for log_level, value in LogLevels.as_dict().items(): self.combo.addItem(log_level, value) self.combo.currentIndexChanged[str].connect(self.setLevel) # Allow QPushButton to clear log text self.clear_btn.clicked.connect(self.clear) # Create a handler with the default format self.handler = GuiHandler(level=level, parent=self) self.logFormat = self.default_format self.handler.message.connect(self.write) # Create logger. Either as a root or given logname self.log = None self.level = None self.logName = logname or '' self.logLevel = level
def setup_ui(self): main = QHBoxLayout() sub = QVBoxLayout() for i in range(10): sub.addWidget(QLabel(str(i))) main.addLayout(sub) self.setLayout(main)
def __init__(self, device, parent=None): super().__init__(device, parent=parent) # Create button widget self.buttons = QWidget(parent=parent) self.button_layout = QHBoxLayout() self.buttons.setLayout(self.button_layout) # Create Insert PushButton if hasattr(device, 'insert'): self.insert_button = QPushButton('Insert', parent=parent) self.insert_button.setFont(self.font) self.button_layout.addWidget(self.insert_button) self.button_layout.addItem(QSpacerItem(10, 20)) # Create Remove PushButton if hasattr(device, 'remove'): self.remove_button = QPushButton('Remove', parent=parent) self.remove_button.setFont(self.font) self.button_layout.addWidget(self.remove_button) # Subscribe device to state changes try: # Wait for later to update widget self.device.subscribe(self.update_state, event_type=self.device.SUB_STATE, run=False) except Exception: logger.error("Widget is unable to subscribe to device %s", device.name)
def _add_row(self, read, name, write=None, is_enum=False): # Create label label = QLabel(self) label.setText(name) # Create signal display val_display = QHBoxLayout() # Add readback ro = TyphonLabel(init_channel=read, parent=self) val_display.addWidget(ro) # Add our write_pv if available if write: # Check whether our device is an enum or not if is_enum: edit = TyphonComboBox(init_channel=write, parent=self) else: logger.debug("Adding LineEdit for %s", name) edit = TyphonLineEdit(init_channel=write, parent=self) # Add our control widget to layout val_display.addWidget(edit) # Make sure they share space evenly val_display.setStretch(0, 1) val_display.setStretch(1, 1) # Add displays to panel loc = len(self.signals) self.layout().addWidget(label, loc, 0) self.layout().addLayout(val_display, loc, 1) # Store signal self.signals[name] = (read, write) return loc
def __init__(self, name, default, required=True, enum=None): self.name = name self.data_type = type(default) self.config = self.NO_CONFIG if not required or isinstance(default, bool): self.config += self.CHECK if not isinstance(default, bool): if enum is None: self.config += self.LINE else: self.config += self.COMBO self.layout = QHBoxLayout() if self.config & self.CHECK: self.check = QCheckBox() self.layout.addWidget(self.check) else: self.check = None if self.config == self.CHECK: self.check.setText('Enabled') if self.config & self.LINE: self.data = QLineEdit() self.data.setText(str(default)) if self.data_type == int: self.data.setValidator(QIntValidator()) elif self.data_type == float: self.data.setValidator(QDoubleValidator()) self.layout.addWidget(self.data) elif self.config & self.COMBO: self.data = QComboBox() for value in enum: self.data.addItem(str(value)) if default is not None: pass # TODO: pick correct default combo box item self.layout.addWidget(self.data) else: self.data = None if self.check is not None and self.data is not None: self.check.toggled.connect(self.data.setEnabled) if self.check is not None: if default in (None, False): self.check.setChecked(False) else: self.check.setChecked(True)
def __init__(self, parameter, default=inspect._empty, parent=None): super().__init__(parent=parent) # Store parameter information self.parameter = parameter self.default = default self.setLayout(QHBoxLayout()) # Create our label self.param_label = QLabel(parent=self) self.param_label.setText(clean_attr(parameter)) self.layout().addWidget(self.param_label) # Indicate required parameters in bold font if default == inspect._empty: logger.debug("Inferring that %s has no default", parameter) bold = QFont() bold.setBold(True) self.param_label.setFont(bold)
def __init__(self, methods=None, parent=None): # Initialize parent super().__init__("Functions", parent=parent) self.contents = QWidget() self.layout().addWidget(self.contents) # Create Layout self.contents.setLayout(QHBoxLayout()) # Add two spacers to center our functions without # expanding them self.contents.layout().addItem(QSpacerItem(10, 20)) self.contents.layout().addItem(QSpacerItem(10, 20)) # Add methods methods = methods or list() self.methods = dict() for method in methods: self.add_method(method)
def add_pv(self, read_pv, name, write_pv=None): """ Add PVs to the SignalPanel Parameters --------- read_pv : pyepics.PV name : str Name of signal to display write_pv : pyepics.PV, optional Returns ------- loc : int Row number that the signal information was added to in the `SignalPanel.layout()`` """ logger.debug("Adding PV %s", name) # Create label label = QLabel(self) label.setText(name) label_font = QFont() label.setFont(label_font) # Create signal display val_display = QHBoxLayout() # Add readback ro = TyphonLabel(init_channel=channel_name(read_pv.pvname), parent=self) val_display.addWidget(ro) # Add our write_pv if available if write_pv: ch = channel_name(write_pv.pvname) # Check whether our device is an enum or not if write_pv.enum_strs: edit = TyphonComboBox(init_channel=ch, parent=self) else: logger.debug("Adding LineEdit for %s", name) edit = PyDMLineEdit(init_channel=ch, parent=self) # Add our control widget to layout val_display.addWidget(edit) # Add displays to panel loc = len(self.pvs) self.layout().addWidget(label, loc, 0) self.layout().addLayout(val_display, loc, 1) # Store signal self.pvs[name] = (read_pv, write_pv) return loc
def _add_row(self, read, name, write=None): # Create label label = QLabel(self) label.setText(name) # Create signal display val_display = QHBoxLayout() # Add readback val_display.addWidget(read) # Add our write_pv if available if write is not None: # Add our control widget to layout val_display.addWidget(write) # Make sure they share space evenly val_display.setStretch(0, 1) val_display.setStretch(1, 1) # Add displays to panel loc = len(self.signals) self.layout().addWidget(label, loc, 0) self.layout().addLayout(val_display, loc, 1) # Store signal self.signals[name] = (read, write) return loc
class Setting: """ Abstraction of a Single user input setting. Handles what the ui should look like for just this setting and keeps the input value and display values in sync. Widgets are held in the "layout" attribute. Supports several different kinds of configurations and associated values: -Just a checkbox: Can be True (checked) or False (unchecked) Chosen if default is a boolean -Just a line edit: Can be any value the same type as default Chosen if required=True and no enum provided -Just a combo box Can be any value in the enum argument Chosen if required=True and enum list provided -A checkbox and a line edit or combo box Chosen if required=False and default is non-boolean If the checkbox is unchecked, the value is None """ NO_CONFIG = 0 CHECK = 1 LINE = 2 COMBO = 4 def __init__(self, name, default, required=True, enum=None): self.name = name self.data_type = type(default) self.config = self.NO_CONFIG if not required or isinstance(default, bool): self.config += self.CHECK if not isinstance(default, bool): if enum is None: self.config += self.LINE else: self.config += self.COMBO self.layout = QHBoxLayout() if self.config & self.CHECK: self.check = QCheckBox() self.layout.addWidget(self.check) else: self.check = None if self.config == self.CHECK: self.check.setText('Enabled') if self.config & self.LINE: self.data = QLineEdit() self.data.setText(str(default)) if self.data_type == int: self.data.setValidator(QIntValidator()) elif self.data_type == float: self.data.setValidator(QDoubleValidator()) self.layout.addWidget(self.data) elif self.config & self.COMBO: self.data = QComboBox() for value in enum: self.data.addItem(str(value)) if default is not None: pass # TODO: pick correct default combo box item self.layout.addWidget(self.data) else: self.data = None if self.check is not None and self.data is not None: self.check.toggled.connect(self.data.setEnabled) if self.check is not None: if default in (None, False): self.check.setChecked(False) else: self.check.setChecked(True) @property def value(self): if self.config == self.CHECK: return self.check.isChecked() elif self.config & self.CHECK: if not self.check.isChecked(): return None if self.config & self.LINE: raw = self.data.text() elif self.config & self.COMBO: raw = self.data.currentText() else: raw = None try: return self.data_type(raw) except Exception: return raw @value.setter def value(self, val): if self.config == self.CHECK: self.check.setChecked(bool(val)) return elif self.config & self.CHECK and val is None: self.check.setChecked(False) return else: try: val = self.data_type(val) except Exception: logger.exception('Invalid data type') return finally: txt = str(val) if self.config & self.LINE: self.data.setText(txt) elif self.config & self.COMBO: index = self.enum.index(txt) self.data.setCurrentIndex(index)
def __init__(self, parent=None, collumns=None, **settings): """ Parameters ---------- collumns: list, optional List of lists of headers included in each collumn The list at index 0 will be the first collumn, etc. settings: kwargs Mapping of header to list of Setting objects """ self.settings = {} self.window = QDialog(parent=parent) self.window.setWindowTitle('Skywalker Settings') main_layout = QVBoxLayout() self.window.setLayout(main_layout) self.window.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) all_cols_layout = QHBoxLayout() main_layout.addLayout(all_cols_layout) if collumns is None: collumns = [list(settings.keys())] for col in collumns: col_layout = QVBoxLayout() all_cols_layout.addLayout(col_layout) for header in col: title = QLabel() title.setText(header.capitalize()) title.setAlignment(Qt.AlignCenter) col_layout.addWidget(title) form = QFormLayout() col_layout.addLayout(form) for setting in settings[header]: self.settings[setting.name] = setting label = QLabel() label.setText(setting.name.capitalize()) label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) form.addRow(label, setting.layout) vertical_spacer = QSpacerItem(20, 10, QSizePolicy.Minimum, QSizePolicy.Expanding) col_layout.addItem(vertical_spacer) confirm_layout = QHBoxLayout() main_layout.addLayout(confirm_layout) horizontal_spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) cancel_button = QPushButton('Cancel') cancel_button.pressed.connect(self.window.reject) apply_button = QPushButton('Apply') apply_button.pressed.connect(self.window.accept) confirm_layout.addItem(horizontal_spacer) confirm_layout.addWidget(cancel_button) confirm_layout.addWidget(apply_button)
class LightRow(InactiveRow): """ Basic Widget to display LightDevice information The widget shows the device information and state, updating looking at the devices :attr:`.inserted` and :attr:`.removed` attributes. The :attr:`.remove_button` also allows the user to remove devices by calling the :meth:`.remove` method of the given device. The identical button is setup if the device is determined to have an `insert` method. Finally, PyDMRectangle is used to show the current path of the beam through the table Parameters ---------- device : obj path : BeamPath parent : QObject, optional """ def __init__(self, device, parent=None): super().__init__(device, parent=parent) # Create button widget self.buttons = QWidget(parent=parent) self.button_layout = QHBoxLayout() self.buttons.setLayout(self.button_layout) # Create Insert PushButton if hasattr(device, 'insert'): self.insert_button = QPushButton('Insert', parent=parent) self.insert_button.setFont(self.font) self.button_layout.addWidget(self.insert_button) self.button_layout.addItem(QSpacerItem(10, 20)) # Create Remove PushButton if hasattr(device, 'remove'): self.remove_button = QPushButton('Remove', parent=parent) self.remove_button.setFont(self.font) self.button_layout.addWidget(self.remove_button) # Subscribe device to state changes try: # Wait for later to update widget self.device.subscribe(self.update_state, event_type=self.device.SUB_STATE, run=False) except Exception: logger.error("Widget is unable to subscribe to device %s", device.name) def update_state(self, *args, **kwargs): """ Update the state label The displayed state can be one of ``Unknown`` , ``Inserted``, ``Removed`` or ``Error``, with ``Unknown`` being if the device is not inserted or removed, and error being if the device is reporting as both inserted and removed. The color of the label is also adjusted to either green or red to quickly """ states = Enum('states', ('Unknown', 'Inserted', 'Removed', 'Error')) # Interpret state try: state = 1 + int( self.device.inserted) + 2 * int(self.device.removed) except Exception as exc: logger.error(exc) state = states.Error.value # Set label to state description self.state_label.setText(states(state).name) # Set the color of the label if state == states.Removed.value: self.state_label.setStyleSheet("QLabel {color : rgb(124,252,0)}") elif state == states.Unknown.value: self.state_label.setStyleSheet("QLabel {color : rgb(255, 215, 0)}") else: self.state_label.setStyleSheet("QLabel {color : red}") # Disable buttons if necessary if hasattr(self, 'insert_button'): self.insert_button.setEnabled(state != states.Inserted.value) if hasattr(self, 'remove_button'): self.remove_button.setEnabled(state != states.Removed.value) @property def widgets(self): """ Ordered list of widgets to add to designer """ return [ self.indicator, self.name_label, self.prefix_label, self.spacer, self.state_label, self.buttons ] def clear_sub(self): """ Clear the subscription event """ self.device.clear_sub(self.update_state)
def setup_ui(self): # Create the main layout main_layout = QVBoxLayout() self.setLayout(main_layout) # Create a Label to be the title lbl_title = QLabel("Motors Diagnostic") # Add some StyleSheet to it lbl_title.setStyleSheet("\ QLabel {\ qproperty-alignment: AlignCenter;\ border: 1px solid #FF17365D;\ border-top-left-radius: 15px;\ border-top-right-radius: 15px;\ background-color: #FF17365D;\ padding: 5px 0px;\ color: rgb(255, 255, 255);\ max-height: 25px;\ font-size: 14px;\ }") # Add the title label to the main layout main_layout.addWidget(lbl_title) # Create the Search Panel layout search_layout = QHBoxLayout() # Create a GroupBox with "Filtering" as Title gb_search = QGroupBox(parent=self) gb_search.setTitle("Filtering") gb_search.setLayout(search_layout) # Create a label, line edit and button for filtering lbl_search = QLabel(text="Filter: ") self.txt_filter = QLineEdit() self.txt_filter.returnPressed.connect(self.do_search) btn_search = QPushButton() btn_search.setText("Search") btn_search.clicked.connect(self.do_search) # Add the created widgets to the layout search_layout.addWidget(lbl_search) search_layout.addWidget(self.txt_filter) search_layout.addWidget(btn_search) # Add the Groupbox to the main layout main_layout.addWidget(gb_search) # Create the Results Layout self.results_layout = QVBoxLayout() self.results_layout.setContentsMargins(0, 0, 0, 0) # Create a Frame to host the results of search self.frm_result = QFrame(parent=self) self.frm_result.setLayout(self.results_layout) # Create a ScrollArea so we can properly handle # many entries scroll_area = QScrollArea(parent=self) scroll_area.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) scroll_area.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) scroll_area.setWidgetResizable(True) # Add the Frame to the scroll area scroll_area.setWidget(self.frm_result) # Add the scroll area to the main layout main_layout.addWidget(scroll_area)