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, 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 __init__(self, title, parent=None): super().__init__(parent=parent) # Create Widget Infrastructure self.title = title self.setLayout(QVBoxLayout()) self.layout().setContentsMargins(2, 2, 2, 2) self.layout().setSpacing(5) # Create button control # Assuming widget is visible, set the button as checked self.contents = None self.hide_button = QPushButton(self.title) self.hide_button.setCheckable(True) self.hide_button.setChecked(True) self.layout().addWidget(self.hide_button) self.hide_button.clicked.connect(self.show_contents)
def __init__(self, func, name=None, annotations=None, hide_params=None, parent=None): # Function information self.func = func self.signature = inspect.signature(func) self.name = name or self.func.__name__ # Initialize parent super().__init__('{} Parameters'.format(clean_attr(self.name)), parent=parent) # Ignore certain parameters, args and kwargs by default self.hide_params = ['self', 'args', 'kwargs'] if hide_params: self.hide_params.extend(hide_params) # Create basic layout self._layout = QVBoxLayout() self._layout.setSpacing(2) self.setLayout(self._layout) # Create an empty list to fill later with parameter widgets self.param_controls = list() # Add our button to execute the function self.execute_button = QPushButton() self.execute_button.setText(clean_attr(self.name)) self.execute_button.clicked.connect(self.execute) self._layout.addWidget(self.execute_button) # Add a panel for the optional parameters self.optional = TogglePanel("Optional Parameters") self.optional.contents = QWidget() self.optional.contents.setLayout(QVBoxLayout()) self.optional.contents.layout().setSpacing(2) self.optional.layout().addWidget(self.optional.contents) self.optional.show_contents(False) self._layout.addWidget(self.optional) self._layout.addItem(QSpacerItem(10, 5, vPolicy=QSizePolicy.Expanding)) # Create parameters from function signature annotations = annotations or dict() for param in [ param for param in self.signature.parameters.values() if param.name not in self.hide_params ]: logger.debug("Adding parameter %s ", param.name) # See if we received a manual annotation for this parameter if param.name in annotations: _type = annotations[param.name] logger.debug("Found manually specified type %r", _type.__name__) # Try and get the type from the function annotation elif param.annotation != inspect._empty: _type = param.annotation logger.debug("Found annotated type %r ", _type.__name__) # Try and get the type from the default value elif param.default != inspect._empty: _type = type(param.default) logger.debug("Gathered type %r from parameter default ", _type.__name__) # If we don't have a default value or an annotation, # we can not make a widget for this parameter. Since # this is a required variable (no default), the function # will not work without it. Raise an Exception else: raise TypeError("Parameter {} has an unspecified " "type".format(param.name)) # Add our parameter self.add_parameter(param.name, _type, default=param.default) # Hide optional parameter widget if there are no such parameters if not self.optional_params: self.optional.hide()
class FunctionDisplay(QGroupBox): """ Display controls for an annotated function in a QGroupBox In order to display function arguments in the user interface, the class must be aware of what the type is of each of the parameters. Instead of requiring a user to enter this information manually, the class takes advantage of the function annotation language described in PEP 3107. This allows us to quickly create the appropriate widget for the given parameter based on the type. If a function parameter is not given an annotation, we will attempt to infer it from the default value if available. If this is not possible, and the type is not specified in the ``annotations`` dictionary an exception will be raised. The created user interface consists of a button to execute the function, the required parameters are always displayed beneath the button, and a :class:`.TogglePanel` object that toggles the view of the optional parameters below. Attributes ---------- accepted_types : list List of types FunctionDisplay can create widgets for Parameters ---------- func : callable name : str, optional Name to label the box with, by default this will be the function meeting annotations : dict, optional If the function your are creating a display for is not annotated, you may manually supply types for parameters by passing in a dictionary of name to type mapping hide_params : list, optional List of parameters to exclude from the display. These should have appropriate defaults. By default, ``self``, ``args`` and ``kwargs`` are all excluded parent : QWidget, optional """ accepted_types = [bool, str, int, float] def __init__(self, func, name=None, annotations=None, hide_params=None, parent=None): # Function information self.func = func self.signature = inspect.signature(func) self.name = name or self.func.__name__ # Initialize parent super().__init__('{} Parameters'.format(clean_attr(self.name)), parent=parent) # Ignore certain parameters, args and kwargs by default self.hide_params = ['self', 'args', 'kwargs'] if hide_params: self.hide_params.extend(hide_params) # Create basic layout self._layout = QVBoxLayout() self._layout.setSpacing(2) self.setLayout(self._layout) # Create an empty list to fill later with parameter widgets self.param_controls = list() # Add our button to execute the function self.execute_button = QPushButton() self.execute_button.setText(clean_attr(self.name)) self.execute_button.clicked.connect(self.execute) self._layout.addWidget(self.execute_button) # Add a panel for the optional parameters self.optional = TogglePanel("Optional Parameters") self.optional.contents = QWidget() self.optional.contents.setLayout(QVBoxLayout()) self.optional.contents.layout().setSpacing(2) self.optional.layout().addWidget(self.optional.contents) self.optional.show_contents(False) self._layout.addWidget(self.optional) self._layout.addItem(QSpacerItem(10, 5, vPolicy=QSizePolicy.Expanding)) # Create parameters from function signature annotations = annotations or dict() for param in [ param for param in self.signature.parameters.values() if param.name not in self.hide_params ]: logger.debug("Adding parameter %s ", param.name) # See if we received a manual annotation for this parameter if param.name in annotations: _type = annotations[param.name] logger.debug("Found manually specified type %r", _type.__name__) # Try and get the type from the function annotation elif param.annotation != inspect._empty: _type = param.annotation logger.debug("Found annotated type %r ", _type.__name__) # Try and get the type from the default value elif param.default != inspect._empty: _type = type(param.default) logger.debug("Gathered type %r from parameter default ", _type.__name__) # If we don't have a default value or an annotation, # we can not make a widget for this parameter. Since # this is a required variable (no default), the function # will not work without it. Raise an Exception else: raise TypeError("Parameter {} has an unspecified " "type".format(param.name)) # Add our parameter self.add_parameter(param.name, _type, default=param.default) # Hide optional parameter widget if there are no such parameters if not self.optional_params: self.optional.hide() @property def required_params(self): """ Required parameters """ parameters = self.signature.parameters return [ param.parameter for param in self.param_controls if parameters[param.parameter].default == inspect._empty ] @property def optional_params(self): """ Optional parameters """ parameters = self.signature.parameters return [ param.parameter for param in self.param_controls if parameters[param.parameter].default != inspect._empty ] @pyqtSlot() def execute(self): """ Execute :attr:`.func` This takes the parameters configured by the :attr:`.param_controls` widgets and passes them into the given callable. All generated exceptions are captured and logged """ logger.info("Executing %s ...", self.name) # If our function does not take any argument # just pass it on. Otherwise, collect information # from the appropriate widgets if not self.signature.parameters: func = self.func else: kwargs = dict() # Gather information from parameter widgets for button in self.param_controls: logger.debug("Gathering parameters for %s ...", button.parameter) val = button.get_param_value() logger.debug("Received %s", val) # Watch for NaN values returned from widgets # This indicates that there was improper information given if np.isnan(val): logger.error( "Invalid information supplied for %s " "parameter", button.parameter) return kwargs[button.parameter] = val # Button up function call with partial to try below func = partial(self.func, **kwargs) try: # Execute our function func() except Exception as exc: logger.exception("Exception while executing function") else: logger.info("Operation Complete") def add_parameter(self, name, _type, default=inspect._empty): """ Add a parameter to the function display Parameters ---------- name : str Parameter name _type : type Type of variable that we are expecting the user to input default : optional Default value for the parameter """ # Create our parameter control widget # QCheckBox field if _type == bool: cntrl = ParamCheckBox(name, default=default) else: # Check if this is a valid type if _type not in self.accepted_types: raise TypeError("Parameter {} has type {} which can not " "be represented in a widget" "".format(name, _type.__name__)) # Create our QLineEdit cntrl = ParamLineEdit(name, default=default, _type=_type) # Add our button to the widget # If it is required add it above the panel so that it is always # visisble. Otherwise, add it to the hideable panel self.param_controls.append(cntrl) if default == inspect._empty: self.layout().insertWidget(len(self.required_params), cntrl) else: # If this is the first optional parameter, # show the whole optional panel if self.optional.isHidden(): self.optional.show() # Add the control widget to our contents self.optional.contents.layout().addWidget(cntrl) def sizeHint(self): """Suggested size""" return QSize(175, 100)
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 AllMotorsDisplay(Display): def __init__(self, parent=None, args=[], macros=None): super(AllMotorsDisplay, self).__init__(parent=parent, args=args, macros=None) # Placeholder for data to filter self.data = [] # Reference to the PyDMApplication self.app = QApplication.instance() # Assemble the Widgets self.setup_ui() # Load data from file self.load_data() def minimumSizeHint(self): # This is the default recommended size # for this screen return QtCore.QSize(750, 120) def ui_filepath(self): # No UI file is being used return None 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) def load_data(self): # Extract the directory of this file... base_dir = os.path.dirname(os.path.realpath(__file__)) # Concatenate the directory with the file name... data_file = os.path.join(base_dir, "motor_db.txt") # Open the file so we can read the data... with open(data_file, 'r') as f: # For each line in the file... for entry in f.readlines(): # Append to the list of data... self.data.append(entry[:-1]) def do_search(self): # First lets clear the old connections self.app.close_widget_connections(self.frm_result) # For each widget inside the results frame, lets destroy them for widget in self.frm_result.findChildren(QWidget): widget.setParent(None) widget.deleteLater() # Grab the filter text filter_text = self.txt_filter.text() # For every entry in the dataset... for entry in self.data: # Check if they match our filter if filter_text.upper() not in entry.upper(): continue # Create a PyDMEmbeddedDisplay for this entry disp = PyDMEmbeddedDisplay(parent=self) disp.macros = json.dumps({"MOTOR":entry}) disp.filename = 'inline_motor.ui' disp.setMinimumWidth(700) disp.setMinimumHeight(40) disp.setMaximumHeight(100) # Add the Embedded Display to the Results Layout self.results_layout.addWidget(disp) # Recursively establish the connection for widgets # inside the Results Frame self.app.establish_widget_connections(self.frm_result)
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)