Exemple #1
0
 def setup_ui(self):
     main = QHBoxLayout()
     sub = QVBoxLayout()
     for i in range(10):
         sub.addWidget(QLabel(str(i)))
     main.addLayout(sub)
     self.setLayout(main)
Exemple #2
0
 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
Exemple #3
0
 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)
Exemple #4
0
 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()
Exemple #5
0
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)
Exemple #6
0
    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)
Exemple #7
0
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)
Exemple #8
0
    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)