Beispiel #1
0
class BaseDialog(QDialog):
    def __init__(self,
                 parent,
                 title,
                 ok_text,
                 cancel_text,
                 ok_button_column=0,
                 cancel_button_column=1,
                 buttons_to_new_row=True):
        super().__init__(parent)
        self.setWindowTitle(title)
        self.layout = QGridLayout(self)
        self.setLayout(self.layout)
        self.create_ui()
        buttons_row = self.layout.rowCount(
        ) if not buttons_to_new_row else self.layout.rowCount() + 1
        ok_button = QPushButton(ok_text, self)
        ok_button.setDefault(True)
        ok_button.clicked.connect(self.ok_clicked)
        self.layout.addWidget(ok_button, buttons_row, ok_button_column)
        cancel_button = QPushButton(cancel_text, self)
        cancel_button.clicked.connect(self.reject)
        self.layout.addWidget(cancel_button, buttons_row, cancel_button_column)

    def ok_clicked(self):
        self.accept()
Beispiel #2
0
class PasswordDialog(QDialog):
    """
    An add password Dialog
    """
    def __init__(self, optional_fields=[]):
        QDialog.__init__(self)
        self.setMinimumHeight(120)
        self.setMinimumWidth(380)
        self.setWindowTitle('Enter a Password')
        self.grid_layout = QGridLayout()
        self.setLayout(self.grid_layout)
        name_label = QLabel('Name:')
        pass_label = QLabel('Password:'******'Gen')
        generate_password_button.clicked.connect(self.generate_password)
        self.layout().addWidget(generate_password_button, 1, 2)
        self.grid_layout.addWidget(pass_label, 1, 0)
        self.grid_layout.addWidget(self.password_input, 1, 1)
        self.optional_fields = list()
        for field in optional_fields:
            self.__add_optional_field__(field)
        comment_label = QLabel('comments')
        self.comment_field = QTextEdit()
        self.layout().addWidget(comment_label, self.layout().rowCount() + 1, 0)
        self.layout().addWidget(self.comment_field,
                                self.layout().rowCount(), 1)
        self.confirm_button = QPushButton()
        self.confirm_button.setShortcut('Return')
        self.confirm_button.setText('OK')
        self.grid_layout.addWidget(self.confirm_button,
                                   self.grid_layout.rowCount() + 1, 1)
        self.confirm_button.clicked.connect(self.confirm)

    def generate_password(self):
        self.password_input.setText(random_password())

    def confirm(self):
        """
        confirms the add password dialog
        :return: None
        """
        self.accept()

    def __add_optional_field__(self, name):
        """
        adds an optional field to the Dialog
        :param name:
        :return:
        """
        next_row = self.grid_layout.rowCount() + 1
        label = QLabel('{name}:'.format(name=name))
        input_field = QLineEdit()
        self.grid_layout.addWidget(label, next_row, 0)
        self.grid_layout.addWidget(input_field, next_row, 1)
        self.optional_fields.append((label, input_field))
Beispiel #3
0
 def __init__(self, weapon: Weapon, parent: QWidget):
     # noinspection PyTypeChecker
     super(WeaponView, self).__init__(parent, Qt.WindowType.Dialog)
     layout = QGridLayout()
     layout.addWidget(
         QLabel(weapon.name.title()),
         0,
         0,
         1,
         3,
     )
     layout.addWidget(
         QLabel(weapon.type.value.title()),
         0,
         3,
     )
     if weapon.character is not None:
         character = f'{weapon.character.name.title()} only'
     else:
         character = 'All characters'
     layout.addWidget(
         QLabel(character),
         1,
         0,
         1,
         3,
     )
     # if weapon.game is not None:
     #     layout.addWidget(
     #         QLabel(f'{weapon.game}'),
     #         1, 3,
     #     )
     stats_row = layout.rowCount()
     for i, s in enumerate(weapon.stats):
         layout.addWidget(
             QLabel(f'{Stats.titles[i]}: {weapon.stats[i]}'),
             stats_row,
             i,
         )
     self.setWindowTitle(weapon.name.title())
     self.setWindowModality(Qt.WindowModality.WindowModal)
     self.setLayout(layout)
Beispiel #4
0
class _ExecuteTab(QTabWidget):
    """Tab used to execute modules or shell commands on the selected bot."""

    def __init__(self, responses_tab, model):
        """
        :type responses_tab: _ResponsesTab
        """
        super(_ExecuteTab, self).__init__()

        self._model = model
        self._current_layout = None
        self._current_bot = None

        self._layout = QGridLayout()
        self._sub_layout = QVBoxLayout()
        self._module_view = ModuleView(responses_tab)

        self._layout.setAlignment(Qt.AlignTop)
        self.setLayout(self._layout)
        self.set_empty_layout()

    def set_current_bot(self, bot):
        """Sets the connected bot this tab will interact with.

        :type bot: Bot
        """
        self._current_bot = bot

    def _clear_layout(self):
        while self._layout.count():
            child = self._layout.takeAt(0)

            if child.widget():
                child.widget().deleteLater()
        while self._sub_layout.count():
            child = self._sub_layout.takeAt(0)

            if child.widget():
                child.widget().deleteLater()

    def set_empty_layout(self):
        """Default layout shown when the user has not yet selected a row."""
        self._current_layout = "Empty"
        self._clear_layout()

        self._layout.addWidget(QLabel("Please select a bot in the table above."), 0, 0)

    def set_module_layout(self, module_name="screenshot"):
        """Sets the layout which can execute modules.

        :type module_name: str
        """
        self._current_layout = "Module"
        self._clear_layout()

        command_type_label = QLabel("Command type: ")
        command_type_combobox = QComboBox()

        command_type_combobox.addItem("Module")
        command_type_combobox.addItem("Shell")

        module_label = QLabel("Module name: ")
        module_combobox = QComboBox()

        for module_name in modules.get_names():
            module_combobox.addItem(module_name)

        module_combobox.currentTextChanged.connect(self._on_module_change)
        command_type_combobox.currentTextChanged.connect(self._on_command_type_change)

        self._layout.setColumnStretch(1, 1)
        self._layout.addWidget(command_type_label, 0, 0)
        self._layout.addWidget(command_type_combobox, 0, 1)
        self._layout.addWidget(module_label, 1, 0)
        self._layout.addWidget(module_combobox, 1, 1)

        # Module layout
        cached_module = modules.get_module(module_name)

        if not cached_module:
            cached_module = modules.load_module(module_name, self._module_view, self._model)

        input_fields = []

        for option_name in cached_module.get_setup_messages():
            input_field = QLineEdit()

            self._sub_layout.addWidget(QLabel(option_name))
            self._sub_layout.addWidget(input_field)
            input_fields.append(input_field)

        run_button = QPushButton("Run")
        run_button.setMaximumWidth(250)
        run_button.setMinimumHeight(25)

        run_button.pressed.connect(lambda: self._on_module_run(module_combobox.currentText(), input_fields))

        self._sub_layout.addWidget(QLabel(""))
        self._sub_layout.addWidget(run_button)
        self._sub_layout.setContentsMargins(0, 15, 0, 0)
        self._layout.addLayout(self._sub_layout, self._layout.rowCount() + 2, 0, 1, 2)

        self._on_module_change(module_combobox.currentText())

    def set_shell_layout(self):
        """Sets the layout which can execute shell commands."""
        self._current_layout = "Shell"
        self._clear_layout()

        command_type_label = QLabel("Command type: ")
        command_type_combobox = QComboBox()

        command_type_combobox.addItem("Shell")
        command_type_combobox.addItem("Module")

        command_label = QLabel("Command:")
        command_input = QLineEdit()

        run_button = QPushButton("Run")
        run_button.setMaximumWidth(250)
        run_button.setMinimumHeight(25)

        command_type_combobox.currentTextChanged.connect(self._on_command_type_change)
        run_button.pressed.connect(lambda: self._on_command_run(command_input))

        self._layout.addWidget(command_type_label, 0, 0)
        self._layout.addWidget(command_type_combobox, 0, 1)
        self._layout.addWidget(command_label, 1, 0)
        self._layout.addWidget(command_input, 1, 1)

        self._sub_layout.addWidget(QLabel(""))
        self._sub_layout.addWidget(run_button)
        self._sub_layout.setContentsMargins(0, 15, 0, 0)
        self._layout.addLayout(self._sub_layout, self._layout.rowCount() + 2, 0, 1, 2)

    def _on_command_type_change(self, text):
        """Handles the command type combobox change event.

        :type text: str
        """
        if text == "Module":
            self.set_module_layout()
        else:
            self.set_shell_layout()

    def _on_module_change(self, module_name):
        """Handles module combobox changes.

        :type module_name: str
        """
        while self._sub_layout.count():
            child = self._sub_layout.takeAt(0)

            if child.widget():
                child.widget().deleteLater()

        cached_module = modules.get_module(module_name)

        if not cached_module:
            cached_module = modules.load_module(module_name, self._module_view, self._model)

        input_fields = []

        for option_name in cached_module.get_setup_messages():
            input_field = QLineEdit()
            input_fields.append(input_field)

            self._sub_layout.addWidget(QLabel(option_name))
            self._sub_layout.addWidget(input_field)

        run_button = QPushButton("Run")
        run_button.setMaximumWidth(250)
        run_button.setMinimumHeight(25)

        run_button.pressed.connect(lambda: self._on_module_run(module_name, input_fields))

        self._sub_layout.addWidget(QLabel(""))
        self._sub_layout.addWidget(run_button)
        self._sub_layout.setContentsMargins(0, 15, 0, 0)

    def display_info(self, text):
        """
        :type text: str
        """
        message_box = QMessageBox()

        message_box.setIcon(QMessageBox.Information)
        message_box.setWindowTitle("Information")
        message_box.setText(text)
        message_box.setStandardButtons(QMessageBox.Ok)
        message_box.exec_()

    def _on_module_run(self, module_name, input_fields):
        """Handles running modules.

        :type module_name: str
        :type input_fields: list
        """
        set_options = []

        for input_field in input_fields:
            set_options.append(input_field.text())

        module = modules.get_module(module_name)

        if not module:
            module = modules.load_module(module_name, self._module_view, self._model)

        successful, options = module.setup(set_options)

        if successful:
            if module_name == "remove_bot":
                code = loaders.get_remove_code(self._current_bot.loader_name)
            elif module_name == "update_bot":
                code = loaders.get_update_code(self._current_bot.loader_name)
            else:
                code = modules.get_code(module_name)

            if not options:
                options = {}

            options["module_name"] = module_name

            self._model.add_command(self._current_bot.uid, Command(
                CommandType.MODULE, code, options
            ))

            self.display_info("Module added to the queue of:\n {}@{}".format(
                self._current_bot.username, self._current_bot.hostname)
            )

    def _on_command_run(self, command_input):
        """Handles running commands.

        :type command_input: QLineEdit
        """
        if command_input.text().strip() == "":
            return

        self._model.add_command(self._current_bot.uid, Command(CommandType.SHELL, command_input.text().encode()))

        command_input.clear()
        self.display_info("Command added to the queue of:\n {}@{}".format(
            self._current_bot.username, self._current_bot.hostname
        ))
Beispiel #5
0
class BasicFormCollapsible(CollapsibleBox):
    def __init__(self, parent, pynwb_class, metadata=None):
        """Basic Groupbox filling form."""
        super().__init__(title=pynwb_class.__name__, parent=parent)
        self.parent = parent
        self.group_type = pynwb_class.__name__
        self.metadata = metadata
        self.pynwb_class = pynwb_class
        self.groups_list = []
        from nwb_conversion_tools.gui.utils.name_references import name_to_gui_class
        self.name_to_gui_class = name_to_gui_class

        # Forms-creation basic info instructions
        self.fill_fields_info()
        # Updates specific fields from specific classes that inherit BasicFormCollapsible
        self.fields_info_update()
        # Constructs GUI forms using self.fields_info dictionary
        self.make_forms()

    def fill_fields_info(self):
        """Fills the fields info details dictionary."""
        # Loops through list of fields from class and store info in dictionary
        self.fields = self.pynwb_class.__init__.__docval__['args']
        self.fields_info = []
        for field in self.fields:
            # Required fields get a red star in their label
            if 'default' not in field:
                required = True
            # Optional fields
            else:
                required = False

            # Skip data types, continue looping
            if 'shape' in field:
                continue
            # Skip Iterable type, continue looping
            if field['type'] == Iterable:
                continue
            # String types
            if field['type'] is str:
                self.fields_info.append({
                    'name': field['name'],
                    'type': 'str',
                    'class': None,
                    'required': required,
                    'doc': field['doc']
                })
            # Float types
            elif field['type'] in ('float', float):
                self.fields_info.append({
                    'name': field['name'],
                    'type': 'float',
                    'class': None,
                    'required': required,
                    'doc': field['doc']
                })

    def fields_info_update(self):
        """Updates fields info with specific fields from the inheriting class."""
        pass

    def make_forms(self):
        """ Initializes forms."""
        # Forms grid, where each row: [label: form]
        self.grid = QGridLayout()
        self.grid.setColumnStretch(5, 1)
        validator_float = QDoubleValidator()

        # Loops through fields info to create a form entry for each
        for ii, field in enumerate(self.fields_info):
            # Required fields get a red star in their label
            if field['required']:
                field_label = field[
                    'name'] + "<span style='color:" + required_asterisk_color + ";'>*</span>:"
            else:
                field_label = field['name'] + ":"

            # String types
            if field['type'] == 'str':
                form = QLineEdit('')
            # Float types
            elif field['type'] == 'float':
                form = QLineEdit('')
                form.setValidator(validator_float)
            # Link types
            elif field['type'] == 'link':
                form = CustomComboBox()
            # Group types
            elif field['type'] == 'group':
                setattr(self, field['name'] + '_layout', QVBoxLayout())
                form = QGroupBox()
                form.setLayout(getattr(self, field['name'] + '_layout'))

            lbl = QLabel(field_label)
            setattr(self, 'lbl_' + field['name'], lbl)
            setattr(self, 'form_' + field['name'], form)
            getattr(self, 'form_' + field['name']).setToolTip(field['doc'])

            self.grid.addWidget(getattr(self, 'lbl_' + field['name']), ii, 0,
                                1, 2)
            self.grid.addWidget(getattr(self, 'form_' + field['name']), ii, 2,
                                1, 4)

    def refresh_objects_references(self, metadata=None):
        """
        Refreshes references with existing objects in parent / grandparent groups.
        Refreshes children's references.
        """
        # Refreshes self comboboxes
        for field in self.fields_info:
            if field['type'] == 'link':
                form = getattr(self, 'form_' + field['name'])
                form.clear()
                form_gui_class = self.name_to_gui_class[field['class']]
                # Search through parent
                for grp in self.parent.groups_list:
                    # Adds existing specfic groups to combobox
                    if isinstance(grp, form_gui_class):
                        getattr(self, 'form_' + field['name']).addItem(
                            grp.form_name.text())
                # Search through grandparent
                if hasattr(self.parent.parent, 'groups_list'):
                    for grp in self.parent.parent.groups_list:
                        # Adds existing specfic groups to combobox
                        if isinstance(grp, form_gui_class):
                            getattr(self, 'form_' + field['name']).addItem(
                                grp.form_name.text())
        # Refreshes children
        for child in self.groups_list:
            child.refresh_objects_references(metadata=metadata)

    def read_fields(self):
        """Reads fields and returns them structured in a dictionary."""
        metadata = {}
        n_fields = self.grid.rowCount()
        for i in range(n_fields):
            # Get field name
            name = self.grid.itemAtPosition(i, 0).widget().text()
            if '<' in name:
                name = name.split('<')[0]
            if ':' in name:
                name = name.replace(':', '')
            # Get field values
            group = self.grid.itemAtPosition(i, 2).widget()
            if isinstance(group, QLineEdit):
                try:
                    metadata[name] = float(group.text())
                except:
                    metadata[name] = group.text()
            if isinstance(group, QComboBox):
                metadata[name] = str(group.currentText())
            if isinstance(group, QGroupBox):
                metadata[name] = []
                for ii in range(group.children()[0].count()):
                    item = group.children()[0].itemAt(ii).widget()
                    metadata[name].append(item.read_fields())
        return metadata

    def write_fields(self, metadata={}):
        """Reads structured dictionary and write in form fields."""
        # Loops through fields info list
        for i, field in enumerate(self.fields_info):
            # Write metadata to field
            if field['name'] in metadata:
                group = self.grid.itemAtPosition(i, 2).widget()
                # If field form is a string or float
                if isinstance(group, QLineEdit):
                    group.setText(str(metadata[field['name']]))
                # If field form is a link
                if isinstance(group, QComboBox):
                    group.clear()
                    group.addItem(str(metadata[field['name']]))
                # If field form is a group
                if isinstance(group, QGroupBox):
                    n_items = group.children()[0].count()
                    for ind, sps in enumerate(metadata[field['name']]):
                        if ind >= n_items:
                            item_class = self.name_to_gui_class[field['class']]
                            item = item_class(self, metadata={})
                            item.write_fields(
                                metadata=metadata[field['name']][ind])
                            self.groups_list.append(item)
                            getattr(self,
                                    field['name'] + '_layout').addWidget(item)
        self.setContentLayout(self.grid)
Beispiel #6
0
class QBaseDefenseGroupInfo(QGroupBox):
    def __init__(self, cp: ControlPoint, ground_object: TheaterGroundObject,
                 game):
        super(QBaseDefenseGroupInfo,
              self).__init__("Group : " + ground_object.obj_name)
        self.ground_object = ground_object
        self.cp = cp
        self.game = game
        self.buildings = game.theater.find_ground_objects_by_obj_name(
            self.ground_object.obj_name)

        self.main_layout = QVBoxLayout()
        self.unit_layout = QGridLayout()

        self.init_ui()

    def init_ui(self):

        self.buildLayout()
        self.main_layout.addLayout(self.unit_layout)
        if not self.cp.captured and not self.ground_object.is_dead:
            attack_button = QPushButton("Attack")
            attack_button.setProperty("style", "btn-danger")
            attack_button.setMaximumWidth(180)
            attack_button.clicked.connect(self.onAttack)
            self.main_layout.addWidget(attack_button, 0, Qt.AlignLeft)

        if self.cp.captured:
            manage_button = QPushButton("Manage")
            manage_button.setProperty("style", "btn-success")
            manage_button.setMaximumWidth(180)
            manage_button.clicked.connect(self.onManage)
            self.main_layout.addWidget(manage_button, 0, Qt.AlignLeft)

        self.setLayout(self.main_layout)

    def buildLayout(self):
        unit_dict = {}
        for i in range(self.unit_layout.rowCount()):
            for j in range(self.unit_layout.columnCount()):
                item = self.unit_layout.itemAtPosition(i, j)
                if item is not None and item.widget() is not None:
                    item.widget().setParent(None)
                    print("Remove " + str(i) + ", " + str(j))

        for g in self.ground_object.groups:
            for u in g.units:
                if u.type in unit_dict.keys():
                    unit_dict[u.type] = unit_dict[u.type] + 1
                else:
                    unit_dict[u.type] = 1
        i = 0
        for k, v in unit_dict.items():
            icon = QLabel()
            if k in VEHICLES_ICONS.keys():
                icon.setPixmap(VEHICLES_ICONS[k])
            else:
                icon.setText("<b>" + k[:8] + "</b>")
            icon.setProperty("style", "icon-armor")
            self.unit_layout.addWidget(icon, i, 0)
            unit_display_name = k
            unit_type = vehicles.vehicle_map.get(k)
            if unit_type is not None:
                unit_display_name = db.unit_get_expanded_info(
                    self.game.enemy_country, unit_type, 'name')
            self.unit_layout.addWidget(
                QLabel(
                    str(v) + " x " + "<strong>" + unit_display_name +
                    "</strong>"), i, 1)
            i = i + 1

        if len(unit_dict.items()) == 0:
            self.unit_layout.addWidget(QLabel("/"), 0, 0)

        self.setLayout(self.main_layout)

    def onAttack(self):
        Dialog.open_new_package_dialog(self.ground_object,
                                       parent=self.window())

    def onManage(self):
        self.edition_menu = QGroundObjectMenu(self.window(),
                                              self.ground_object,
                                              self.buildings, self.cp,
                                              self.game)
        self.edition_menu.show()
        self.edition_menu.changed.connect(self.onEdition)

    def onEdition(self):
        self.buildLayout()
Beispiel #7
0
 def add_update_row(description: str, count: int,
                    layout: QGridLayout) -> None:
     row = layout.rowCount()
     layout.addWidget(QLabel(f"<b>{description}</b>"), row, 0)
     layout.addWidget(QLabel(f"{count}"), row, 1)
class ChannelArithmeticDialog(ieb.ImarisExtensionBase):
    """
    Channel Arithmetic and Beyond
    =============================
    `View on GitHub <https://github.com/niaid/imaris_extensions>`_

    This program enables one to specify arithmetic expressions which are used to
    create new channels. The basic arithmetic operations are supported: +,-,*,/,**.
    More advanced operations that run short `SimpleITK <https://simpleitk.org/>`_
    code snippets are also supported.

    Channels are referenced using square brackets and the channel index, starting
    at **zero**. To apply an expression to all channels, use the channel index 'i'.

    When creating a single new channel, the arithmetic expression consists of literal
    channel numbers, one can select a name and color for the new channel. When
    creating multiple new channels, the arithmetic expression is applied to all channels,
    the postfix '_modified' is appended to the original channel names and the original
    color is copied over. Note that for all channels created by the program the
    channel description will include the arithmetic expression used to create that
    channel. This transparently supports your efforts to conduct reproducible
    research.

    Because an Imaris image has a specific pixel type (8, 16, 32 bit unsigned integer
    and 32 bit floating point) all computations are performed using a 32 bit floating
    point representation and then clamped to the range of the image's pixel type.

    The program allows you to use the same expression on multiple files. In this
    case literal channel values are limited by the number of shared channels. Thus,
    if working with two files one with three channels and one with four channels,
    the valid literal channel values are limited to 0, 1, 2. We cannot use 3 as it does not
    exist in all files. On the other hand, if our autofluorescence channel is one
    of these channels, e.g. channel 0, we can subtract it from all channels in
    both files, `[i]-[0]`.

    Basic Examples
    --------------

    Multiply channels zero and three:

    .. code-block:: Python

      [0]*[3]

    Multiply channels zero and three and subtract the result from channel four:

    .. code-block:: Python

      [4] - ([0]*[3])

    Duplicate all channels:

    .. code-block:: Python

      [i]

    Subtract channel zero from all channels:

    .. code-block:: Python

      [i]-[0]


    Advanced Examples
    -----------------

    Threshold channel one using a value of 100, resulting image is binary
    values in {0,1}:

    .. code-block:: Python

      [1]>100

    Threshold a specific channel to create a binary result using the Otsu
    filter:

    .. code-block:: Python

      sitk.OtsuThreshold([1], 0, 1)

    Threshold a specific channel retaining the values above the threshold:

    .. code-block:: Python

      sitk.Cast([1]>100, sitk.sitkFloat32)*[1]

    Threshold a specific channel, get all connected components, then
    sort the components according to size, discarding those smaller than a minimum
    size and create a binary mask corresponding to the largest component, which is
    the first label(second largest component label is 2 etc.)

    .. code-block:: Python

      sitk.RelabelComponent(sitk.ConnectedComponent([1]>100), minimumObjectSize = 50)==1

    Create a binary mask representing the colocalization of two channels,
    intensity values below 20 are considred noise:

    .. code-block:: Python

      ([1]>20)*([2]>20)

    Create a binary mask representing the colocalization of two channels.
    We are interested in all pixels in channel 2 that have a value above 20
    and that are less than 1.0um away from pixels in channel 1 that have a value
    above 100 (**note**: this operation yields different results when run using
    a slice-by-slice approach vs. a volumetric approach):

    .. code-block:: Python

        (sitk.Cast([2]>20, sitk.sitkFloat32) *
         sitk.Abs(sitk.SignedMaurerDistanceMap([1]>100, insideIsPositive=False, squaredDistance=False, useImageSpacing=True)))<=1.0

    Create a binary mask using thresholding and then perform morphological
    closing (dilation followed by erosion) with distance maps, useful
    for filling holes:

    .. code-block:: Python

      sitk.SignedMaurerDistanceMap(sitk.SignedMaurerDistanceMap([1]>100, insideIsPositive=False, squaredDistance=False, useImageSpacing=True) < 1.0, insideIsPositive=False, squaredDistance=False, useImageSpacing=True)<-1.0

    Create a binary mask using thresholding and then perform morphological
    opening (erosion followed by dilation) with distance maps, useful
    for removing small islands:

    .. code-block:: Python

      sitk.SignedMaurerDistanceMap(sitk.SignedMaurerDistanceMap([1]>100, insideIsPositive=False, squaredDistance=False, useImageSpacing=True) < -0.2, insideIsPositive=False, squaredDistance=False, useImageSpacing=True)<0.2
    """  # noqa

    def __init__(self):
        super(ChannelArithmeticDialog, self).__init__()
        # Channel indexes in the arithmetic calculator are denoted using a
        # regular expression: one or more digits in square brackets (e.g. [1234]).
        # First digit is zero and nothing afterwards or first digit is in [1-9] and
        # there are possibly more digits afterwards.
        # Starting index is zero.
        self.channel_pattern = re.compile(r"\[(0|[1-9]\d*)\]")

        # Use QT's global threadpool, documentation says: "This global thread pool
        # automatically maintains an optimal number of threads based on the
        # number of cores in the CPU."
        self.threadpool = QThreadPool.globalInstance()

        # Configure the help dialog.
        self.help_dialog = HelpDialog(w=700, h=500)
        self.help_dialog.setWindowTitle("Channel Arithmetic Help")
        self.help_dialog.set_rst_text(
            inspect.getdoc(self), pygments_css_file_name="pygments_dark.css")

        self.__create_gui()
        self.setWindowTitle("Channel Arithmetic")
        self.processing_error = False

        self.show()

    def __create_gui(self):
        menu_bar = self.menuBar()
        # Force menubar to be displayed in the application on OSX/Linux, otherwise it
        # is displayed in the system menubar
        menu_bar.setNativeMenuBar(False)
        self.help_button = QPushButton("Help")
        self.help_button.clicked.connect(self.help_dialog.show)
        menu_bar.setCornerWidget(self.help_button, Qt.TopLeftCorner)

        central_widget = QWidget(self)
        gui_layout = QVBoxLayout()
        central_widget.setLayout(gui_layout)
        self.setCentralWidget(central_widget)

        select_files_widget = self.__create_select_files_widget()
        arithmetic_widget = self.__create_arithmetic_widget()

        self.stack = QStackedWidget(self)
        self.stack.addWidget(select_files_widget)
        self.stack.addWidget(arithmetic_widget)
        gui_layout.addWidget(self.stack)

        self.status_bar = self.statusBar()

    def closeEvent(self, event):
        """
        Override the closeEvent method so that clicking the 'x' button also
        closes all of the dialogs.
        """
        self.help_dialog.close()
        event.accept()

    def __create_arithmetic_widget(self):
        wid = QWidget(self)
        arithmetic_layout = QVBoxLayout()
        wid.setLayout(arithmetic_layout)

        self.valid_indexes_label = QLabel("")
        arithmetic_layout.addWidget(self.valid_indexes_label)

        layout = QHBoxLayout()
        layout.setAlignment(Qt.AlignLeft)
        layout.addWidget(QLabel("Enter new channel arithmetic expression:"))
        arithmetic_layout.addLayout(layout)

        self.arithmetic_expression_text_edit = QTextEdit()
        arithmetic_layout.addWidget(self.arithmetic_expression_text_edit)

        self.slice_by_slice_checkbox = QCheckBox(
            "Slice by slice (smaller memory footprint).")
        arithmetic_layout.addWidget(self.slice_by_slice_checkbox)

        layout = QHBoxLayout()
        layout.addWidget(QLabel("New channel name:"))
        self.new_channel_name_line_edit = QLineEdit()
        layout.addWidget(self.new_channel_name_line_edit)
        arithmetic_layout.addLayout(layout)

        layout = QHBoxLayout()
        layout.addWidget(QLabel("New channel color:"))
        self.new_channel_color_button = QPushButton()
        self.new_channel_color_button.clicked.connect(
            self.__select_color_callback)
        layout.addWidget(self.new_channel_color_button)
        arithmetic_layout.addLayout(layout)

        self.apply_button = QPushButton("Apply")
        self.apply_button.clicked.connect(self.__channel_arithmetic_wrapper)
        arithmetic_layout.addWidget(self.apply_button)

        progress_wid = QWidget()
        self.progress_grid_layout = QGridLayout()
        progress_wid.setLayout(self.progress_grid_layout)
        scroll_area = QScrollArea()
        scroll_area.setWidget(progress_wid)
        scroll_area.setWidgetResizable(True)
        arithmetic_layout.addWidget(scroll_area)

        layout = QHBoxLayout()
        layout.setAlignment(Qt.AlignLeft)
        self.processing_prev_button = QPushButton("Prev")
        self.processing_prev_button.clicked.connect(
            lambda: self.stack.setCurrentIndex(0))
        layout.addWidget(self.processing_prev_button)
        arithmetic_layout.addLayout(layout)

        return wid

    def __configure_and_show_arithmetic_widget(self):
        file_names = self.input_files_edit.toPlainText().split("\n")
        num_channels = []
        problematic_images = []
        for file_name in file_names:
            try:
                meta_data = sio.read_metadata(file_name)
                num_channels.append(len(meta_data["channels_information"]))
            except Exception:
                problematic_images.append(file_name)
        if problematic_images:
            self._error_function(
                "Problem encountered reading the following file(s):\n" +
                "\n".join(problematic_images))
            return
        self.max_channel_index = min(num_channels) - 1
        self.valid_indexes_label.setText(
            f"Valid channel indexes: 0...{self.max_channel_index}, i")
        self.arithmetic_expression_text_edit.clear()
        self.slice_by_slice_checkbox.setChecked(False)
        self.new_channel_name_line_edit.clear()

        # Remove all widgets from layout, done in reverse order because
        # removing from the begining shifts the rest of the items
        for i in reversed(range(self.progress_grid_layout.count())):
            self.progress_grid_layout.itemAt(i).widget().setParent(None)

        for i, file_name in enumerate(file_names):
            self.progress_grid_layout.addWidget(
                QLabel(os.path.basename(file_name)), i, 0)
            progress_bar = QProgressBar()
            progress_bar.setMaximum(100)
            self.progress_grid_layout.addWidget(progress_bar, i, 1)

        self.stack.setCurrentIndex(1)

    def __create_select_files_widget(self):
        wid = QWidget()
        input_layout = QVBoxLayout()
        wid.setLayout(input_layout)

        layout = QHBoxLayout()
        layout.addWidget(QLabel("File names:"))
        layout.setAlignment(Qt.AlignLeft)
        button = QPushButton("Browse")
        button.setToolTip("Select input files for arithmetic operation.")
        button.clicked.connect(self.__browse_select_input_callback)
        layout.addWidget(button)
        input_layout.addLayout(layout)

        self.input_files_edit = QTextEdit()
        self.input_files_edit.setReadOnly(True)
        input_layout.addWidget(self.input_files_edit)

        layout = QHBoxLayout()
        layout.setAlignment(Qt.AlignRight)
        self.input_files_next_button = QPushButton("Next")
        self.input_files_next_button.setEnabled(False)
        self.input_files_next_button.clicked.connect(
            self.__configure_and_show_arithmetic_widget)
        layout.addWidget(self.input_files_next_button)
        input_layout.addLayout(layout)

        return wid

    def __browse_select_input_callback(self):
        file_names, _ = QFileDialog.getOpenFileNames(
            self,
            "QFileDialog.getOpenFileNames()",
            "",
            "Imaris Images (*.ims);;All Files (*)",
        )
        if file_names:
            self.input_files_edit.setText("\n".join(file_names))
            self.input_files_next_button.setEnabled(True)

    def __select_color_callback(self):
        color = QColorDialog.getColor()
        if color.isValid():
            self.new_channel_color_button.setStyleSheet(
                f"background-color :rgb({color.red()},{color.green()},{color.blue()})"
            )

    def __channel_arithmetic_wrapper(self):
        # Get the arithmetic expression after removing all whitespace
        arithmetic_expression = "".join(
            self.arithmetic_expression_text_edit.toPlainText().split())
        color = self.new_channel_color_button.palette().button().color()

        if arithmetic_expression:
            # Get the explicit channel indexes that appear in the expression and
            # check that they are in the valid range.
            channel_indexes = re.findall(self.channel_pattern,
                                         arithmetic_expression)
            invalid_channels = [
                ci for ci in channel_indexes
                if int(ci) not in range(self.max_channel_index + 1)
            ]
            if invalid_channels:
                self._error_function(
                    "The following channels specified in the arithmetic expression"
                    +
                    f" are outside the valid range [0,{self.max_channel_index}]: "
                    + ", ".join(invalid_channels))
                return

            # Disable the UI interaction during computation
            self.arithmetic_expression_text_edit.setReadOnly(True)
            self.slice_by_slice_checkbox.setEnabled(False)
            self.new_channel_name_line_edit.setReadOnly(True)
            self.new_channel_color_button.setEnabled(False)
            self.apply_button.setEnabled(False)
            self.processing_prev_button.setEnabled(False)

            QApplication.setOverrideCursor(Qt.WaitCursor)
            file_names = self.input_files_edit.toPlainText().split("\n")
            self.num_threads_left = len(file_names)
            for i, input_file_name in enumerate(file_names):
                # Configure and perform computation in another thread.
                arithmetic_calculator = ArithmeticCalculator(
                    self.channel_pattern)
                arithmetic_calculator.signals.finished.connect(
                    self.__arithmetic_finished)
                arithmetic_calculator.signals.processing_error.connect(
                    self._processing_error_function)
                arithmetic_calculator.signals.progress_signal.connect(
                    self.progress_grid_layout.itemAtPosition(
                        i, 1).widget().setValue)
                arithmetic_calculator.signals.update_state_signal.connect(
                    self.status_bar.showMessage)
                arithmetic_calculator.input_file_name = input_file_name
                arithmetic_calculator.arithmetic_expression = arithmetic_expression
                arithmetic_calculator.new_channel_color = [
                    color.red() / 255.0,
                    color.green() / 255.0,
                    color.blue() / 255.0,
                ]
                arithmetic_calculator.new_channel_alpha = color.alpha() / 255.0
                arithmetic_calculator.new_channel_name = (
                    self.new_channel_name_line_edit.text().strip())
                arithmetic_calculator.slice_by_slice = (
                    self.slice_by_slice_checkbox.isChecked())
                self.threadpool.start(arithmetic_calculator)
        else:
            self._error_function(
                "No action taken: arithmetic expression not set.")

    def __arithmetic_finished(self):
        self.num_threads_left = self.num_threads_left - 1
        if self.num_threads_left == 0:
            QApplication.restoreOverrideCursor()
            self.status_bar.clearMessage()
            for i in range(self.progress_grid_layout.rowCount()):
                self.progress_grid_layout.itemAtPosition(
                    i, 1).widget().setValue(0)
            # Enable the UI interaction after computation
            self.arithmetic_expression_text_edit.setReadOnly(False)
            self.slice_by_slice_checkbox.setEnabled(True)
            self.new_channel_name_line_edit.setReadOnly(False)
            self.new_channel_color_button.setEnabled(True)
            self.apply_button.setEnabled(True)
            self.processing_prev_button.setEnabled(True)

            # Inform the user that the calculations completed. If processing errors
            # occured then the desired operation may not have happened, but the
            # calculation was completed.
            QMessageBox().information(self, "Message",
                                      "Calculation completed.")
            self.processing_error = False
class GroupNwbfile(CollapsibleBox):
    def __init__(self, parent, metadata):
        """Groupbox for NWBFile fields filling form."""
        super().__init__(title="NWBFile", parent=parent)
        self.parent = parent
        self.metadata = metadata
        #self.setTitle('NWBFile')
        self.group_type = 'NWBFile'
        self.groups_list = []

        self.grid = QGridLayout()
        self.grid.setColumnStretch(2, 1)
        self.grid.setColumnStretch(4, 1)

        self.lbl_session_description = QLabel(
            'session_description<span style="color:' +
            required_asterisk_color + ';">*</span>:')
        self.form_session_description = QLineEdit("session_description")
        self.form_session_description.setToolTip(
            "A description of the session where this data was generated")
        self.grid.addWidget(self.lbl_session_description, 0, 0, 1, 2)
        self.grid.addWidget(self.form_session_description, 0, 2, 1, 4)

        self.lbl_identifier = QLabel('identifier<span style="color:' +
                                     required_asterisk_color + ';">*</span>:')
        self.form_identifier = QLineEdit("ABC123")
        self.form_identifier.setToolTip(
            "a unique text identifier for the file")
        self.grid.addWidget(self.lbl_identifier, 1, 0, 1, 2)
        self.grid.addWidget(self.form_identifier, 1, 2, 1, 4)

        self.lbl_session_start_time = QLabel(
            'session_start_time<span style="color:' + required_asterisk_color +
            ';">*</span>:')
        self.form_session_start_time1 = QLineEdit("")
        self.form_session_start_time1.setPlaceholderText("dd/mm/yyyy")
        self.form_session_start_time1.setToolTip(
            "the start date and time of the recording session")
        self.form_session_start_time2 = QLineEdit("")
        self.form_session_start_time2.setPlaceholderText("hh:mm")
        self.form_session_start_time2.setToolTip(
            "the start date and time of the recording session")
        self.grid.addWidget(self.lbl_session_start_time, 2, 0, 1, 2)
        self.grid.addWidget(self.form_session_start_time1, 2, 2, 1, 2)
        self.grid.addWidget(self.form_session_start_time2, 2, 4, 1, 2)

        self.lbl_experimenter = QLabel('experimenter:')
        self.form_experimenter = QLineEdit('')
        self.form_experimenter.setPlaceholderText(
            "Alan Lloyd Hodgkin, Andrew Fielding Huxley")
        self.form_experimenter.setToolTip(
            "Comma-separated list of names of persons who performed experiment"
        )
        nWidgetsGrid = self.grid.rowCount()
        self.grid.addWidget(self.lbl_experimenter, nWidgetsGrid, 0, 1, 2)
        self.grid.addWidget(self.form_experimenter, nWidgetsGrid, 2, 1, 4)

        self.lbl_experiment_description = QLabel('experiment_description:')
        self.form_experiment_description = QLineEdit('')
        self.form_experiment_description.setPlaceholderText(
            "propagation of action potentials in the squid giant axon")
        self.form_experiment_description.setToolTip(
            "general description of the experiment")
        nWidgetsGrid = self.grid.rowCount()
        self.grid.addWidget(self.lbl_experiment_description, nWidgetsGrid, 0,
                            1, 2)
        self.grid.addWidget(self.form_experiment_description, nWidgetsGrid, 2,
                            1, 4)

        self.lbl_session_id = QLabel('session_id:')
        self.form_session_id = QLineEdit('')
        self.form_session_id.setPlaceholderText("LAB 0123")
        self.form_session_id.setToolTip("lab-specific ID for the session")
        nWidgetsGrid = self.grid.rowCount()
        self.grid.addWidget(self.lbl_session_id, nWidgetsGrid, 0, 1, 2)
        self.grid.addWidget(self.form_session_id, nWidgetsGrid, 2, 1, 4)

        self.lbl_institution = QLabel('institution:')
        self.form_institution = QLineEdit('')
        self.form_institution.setPlaceholderText("institution")
        self.form_institution.setToolTip(
            "institution(s) where experiment is performed")
        nWidgetsGrid = self.grid.rowCount()
        self.grid.addWidget(self.lbl_institution, nWidgetsGrid, 0, 1, 2)
        self.grid.addWidget(self.form_institution, nWidgetsGrid, 2, 1, 4)

        self.lbl_lab = QLabel("lab:")
        self.form_lab = QLineEdit('')
        self.form_lab.setPlaceholderText("lab name")
        self.form_lab.setToolTip("lab where experiment was performed")
        nWidgetsGrid = self.grid.rowCount()
        self.grid.addWidget(self.lbl_lab, nWidgetsGrid, 0, 1, 2)
        self.grid.addWidget(self.form_lab, nWidgetsGrid, 2, 1, 4)

        if 'lab_meta_data' in metadata.keys():
            self.lbl_lab_meta_data = QLabel("lab_meta_data:")
            self.lab_meta_data = GroupCustomExtension(
                parent=self, metadata=metadata['lab_meta_data'])
            self.lab_meta_data.setToolTip(
                "an extension that contains lab-specific meta-data")
            nWidgetsGrid = self.grid.rowCount()
            self.grid.addWidget(self.lbl_lab_meta_data, nWidgetsGrid, 0, 1, 2)
            self.grid.addWidget(self.lab_meta_data, nWidgetsGrid, 2, 1, 4)

        self.lbl_keywords = QLabel('keywords:')
        self.form_keywords = QLineEdit('')
        self.form_keywords.setPlaceholderText(
            "action potential, ion channels, mathematical model")
        self.form_keywords.setToolTip(
            "comma-separated list of terms to search over")
        nWidgetsGrid = self.grid.rowCount()
        self.grid.addWidget(self.lbl_keywords, nWidgetsGrid, 0, 1, 2)
        self.grid.addWidget(self.form_keywords, nWidgetsGrid, 2, 1, 4)

        self.lbl_notes = QLabel("notes:")
        self.form_notes = QLineEdit('')
        self.form_notes.setPlaceholderText("")
        self.form_notes.setToolTip("Notes about the experiment")
        nWidgetsGrid = self.grid.rowCount()
        self.grid.addWidget(self.lbl_notes, nWidgetsGrid, 0, 1, 2)
        self.grid.addWidget(self.form_notes, nWidgetsGrid, 2, 1, 4)

        self.lbl_pharmacology = QLabel("pharmacology:")
        self.form_pharmacology = QLineEdit('')
        self.form_pharmacology.setPlaceholderText("")
        self.form_pharmacology.setToolTip(
            "Description of drugs used, including how and when they were administered.\n"
            "Anesthesia(s), painkiller(s), etc., plus dosage, concentration, etc."
        )
        nWidgetsGrid = self.grid.rowCount()
        self.grid.addWidget(self.lbl_pharmacology, nWidgetsGrid, 0, 1, 2)
        self.grid.addWidget(self.form_pharmacology, nWidgetsGrid, 2, 1, 4)

        self.lbl_protocol = QLabel("protocol:")
        self.form_protocol = QLineEdit('')
        self.form_protocol.setPlaceholderText("")
        self.form_protocol.setToolTip(
            "Experimental protocol, if applicable. E.g. include IACUC protocol"
        )
        nWidgetsGrid = self.grid.rowCount()
        self.grid.addWidget(self.lbl_protocol, nWidgetsGrid, 0, 1, 2)
        self.grid.addWidget(self.form_protocol, nWidgetsGrid, 2, 1, 4)

        self.lbl_related_publications = QLabel("related publications:")
        self.form_related_publications = QLineEdit('')
        self.form_related_publications.setPlaceholderText("")
        self.form_related_publications.setToolTip(
            "Publication information. PMID, DOI, URL, etc. If multiple, concatenate "
            "together \nand describe which is which")
        nWidgetsGrid = self.grid.rowCount()
        self.grid.addWidget(self.lbl_related_publications, nWidgetsGrid, 0, 1,
                            2)
        self.grid.addWidget(self.form_related_publications, nWidgetsGrid, 2, 1,
                            4)

        self.lbl_slices = QLabel("slices:")
        self.form_slices = QLineEdit('')
        self.form_slices.setPlaceholderText("")
        self.form_slices.setToolTip(
            "Description of slices, including information about preparation thickness,"
            "\norientation, temperature and bath solution")
        nWidgetsGrid = self.grid.rowCount()
        self.grid.addWidget(self.lbl_slices, nWidgetsGrid, 0, 1, 2)
        self.grid.addWidget(self.form_slices, nWidgetsGrid, 2, 1, 4)

        self.lbl_data_collection = QLabel("data_collection:")
        self.form_data_collection = QLineEdit('')
        self.form_data_collection.setPlaceholderText("")
        self.form_data_collection.setToolTip(
            "Notes about data collection and analysis")
        nWidgetsGrid = self.grid.rowCount()
        self.grid.addWidget(self.lbl_data_collection, nWidgetsGrid, 0, 1, 2)
        self.grid.addWidget(self.form_data_collection, nWidgetsGrid, 2, 1, 4)

        self.lbl_surgery = QLabel("surgery:")
        self.form_surgery = QLineEdit('')
        self.form_surgery.setPlaceholderText("")
        self.form_surgery.setToolTip(
            "Narrative description about surgery/surgeries, including date(s) and who performed surgery."
        )
        nWidgetsGrid = self.grid.rowCount()
        self.grid.addWidget(self.lbl_surgery, nWidgetsGrid, 0, 1, 2)
        self.grid.addWidget(self.form_surgery, nWidgetsGrid, 2, 1, 4)

        self.lbl_virus = QLabel("virus:")
        self.form_virus = QLineEdit('')
        self.form_virus.setPlaceholderText("")
        self.form_virus.setToolTip(
            "Information about virus(es) used in experiments, including virus ID, source, "
            "date made, injection location, volume, etc.")
        nWidgetsGrid = self.grid.rowCount()
        self.grid.addWidget(self.lbl_virus, nWidgetsGrid, 0, 1, 2)
        self.grid.addWidget(self.form_virus, nWidgetsGrid, 2, 1, 4)

        self.lbl_stimulus_notes = QLabel("stimulus_notes:")
        self.form_stimulus_notes = QLineEdit('')
        self.form_stimulus_notes.setPlaceholderText("")
        self.form_stimulus_notes.setToolTip(
            "Notes about stimuli, such as how and where presented.")
        nWidgetsGrid = self.grid.rowCount()
        self.grid.addWidget(self.lbl_stimulus_notes, nWidgetsGrid, 0, 1, 2)
        self.grid.addWidget(self.form_stimulus_notes, nWidgetsGrid, 2, 1, 4)
        #self.setLayout(self.grid)
        self.setContentLayout(self.grid)

    def read_fields(self):
        """Reads fields and returns them structured in a dictionary."""
        error = None
        data = {}
        data['session_description'] = self.form_session_description.text()
        data['identifier'] = self.form_identifier.text()
        str_datetime = self.form_session_start_time1.text(
        ) + ", " + self.form_session_start_time2.text()
        try:
            data['session_start_time'] = datetime.strptime(
                str_datetime, '%d/%m/%Y, %H:%M')
        except Exception as error:
            self.parent.write_to_logger(str(error))
            self.parent.write_to_logger(
                "ERROR: Invalid 'session_start_time' format. "
                "Please fill in correct format.")
            return None, error
        if self.form_experimenter.text() != '':
            data['experimenter'] = self.form_experimenter.text()
        else:
            data['experimenter'] = None
        if self.form_experiment_description.text() != '':
            data[
                'experiment_description'] = self.form_experiment_description.text(
                )
        else:
            data['experiment_description'] = None
        if self.form_session_id.text() != '':
            data['session_id'] = self.form_session_id.text()
        else:
            data['session_id'] = None
        if self.form_institution.text() != '':
            data['institution'] = self.form_institution.text()
        else:
            data['institution'] = None
        if self.form_lab.text() != '':
            data['lab'] = self.form_lab.text()
        else:
            data['lab'] = None
        if 'lab_meta_data' in self.metadata.keys():
            data['lab_meta_data'], error = self.lab_meta_data.read_fields()
        if len(self.form_keywords.text()) > 0:
            keywords = self.form_keywords.text()
            data['keywords'] = [kw.strip() for kw in keywords.split(',')]
        else:
            data['keywords'] = None
        if self.form_notes.text() != '':
            data['notes'] = self.form_notes.text()
        else:
            data['notes'] = None
        if self.form_pharmacology.text() != '':
            data['pharmacology'] = self.form_pharmacology.text()
        else:
            data['pharmacology'] = None
        if self.form_protocol.text() != '':
            data['protocol'] = self.form_protocol.text()
        else:
            data['protocol'] = None
        if self.form_related_publications.text() != '':
            data['related_publications'] = self.form_related_publications.text(
            )
        else:
            data['related_publications'] = None
        if self.form_slices.text() != '':
            data['slices'] = self.form_slices.text()
        else:
            data['slices'] = None
        if self.form_data_collection.text() != '':
            data['data_collection'] = self.form_data_collection.text()
        else:
            data['data_collection'] = None
        if self.form_surgery.text() != '':
            data['surgery'] = self.form_surgery.text()
        else:
            data['surgery'] = None
        if self.form_virus.text() != '':
            data['virus'] = self.form_virus.text()
        else:
            data['virus'] = None
        if self.form_stimulus_notes.text() != '':
            data['stimulus_notes'] = self.form_stimulus_notes.text()
        else:
            data['stimulus_notes'] = None
        return data, error

    def write_fields(self, data={}):
        """Reads structured dictionary and write in form fields."""
        self.form_session_description.setText(data['session_description'])
        self.form_identifier.setText(data['identifier'])
        if 'session_start_time' in data and data['session_start_time']:
            str_datetime = data['session_start_time'].strftime(
                '%d/%m/%Y, %H:%M')
            self.form_session_start_time1.setText(str_datetime.split(',')[0])
            self.form_session_start_time2.setText(
                str_datetime.split(',')[1].strip())
        if 'experimenter' in data and data['experimenter'] is not None:
            if isinstance(data['experimenter'], list):
                self.form_experimenter.setText(','.join(
                    str(x) for x in data['experimenter']))
            else:
                self.form_experimenter.setText(data['experimenter'])
        if 'experiment_description' in data:
            self.form_experiment_description.setText(
                data['experiment_description'])
        if 'session_id' in data:
            self.form_session_id.setText(data['session_id'])
        if 'institution' in data:
            self.form_institution.setText(data['institution'])
        if 'lab' in data:
            self.form_lab.setText(data['lab'])
        if 'keywords' in data and data['keywords'] is not None:
            self.form_keywords.setText(','.join(
                str(x) for x in data['keywords']))
        if 'notes' in data:
            self.form_notes.setText(data['notes'])
        if 'pharmacology' in data:
            self.form_pharmacology.setText(data['pharmacology'])
        if 'protocol' in data:
            self.form_protocol.setText(data['protocol'])
        if 'related_publications' in data:
            self.form_related_publications.setText(
                data['related_publications'])
        if 'slices' in data:
            self.form_slices.setText(data['slices'])
        if 'data_collection' in data:
            self.form_data_collection.setText(data['data_collection'])
        if 'surgery' in data:
            self.form_surgery.setText(data['surgery'])
        if 'virus' in data:
            self.form_virus.setText(data['virus'])
        if 'stimulus_notes' in data:
            self.form_stimulus_notes.setText(data['stimulus_notes'])
Beispiel #10
0
class SequenceRecordsWindow(QWidget):
    def __init__(self, parent):
        super(SequenceRecordsWindow, self).__init__(parent)
        self.grid_layout = QGridLayout()
        self.grid_layout.setContentsMargins(0, 0, 0, 0)
        self.grid_layout.setSpacing(0)
        self.setLayout(self.grid_layout)

        self.seq_font = QFont()
        self.seq_font.setFamily("Noto Sans Mono")
        self.seq_font.setPointSize(12)
        self.seq_font.setFixedPitch(True)
        self.seq_font.setStyleHint(QFont.Monospace)

        self.seq_h_scroll_bar = QScrollBar(self, self.parent())
        self.seq_h_scroll_bar.setOrientation(Qt.Horizontal)
        self.seq_h_scroll_bar.setMinimum(0)
        self.seq_h_scroll_bar.setMaximum(self.longest_seq_len - self.char_nb)
        self.seq_h_scroll_bar.valueChanged.connect(self.move_seqs)
        self.grid_layout.addWidget(self.seq_h_scroll_bar,
                                   self.grid_layout.rowCount(), 5)

        self.lower_spacer_item = QSpacerItem(1, 1, QSizePolicy.Minimum,
                                             QSizePolicy.MinimumExpanding)
        self.grid_layout.addItem(self.lower_spacer_item)

        self.seq_record_items = []

    def sizeHint(self):  # Workaroud QTBUG-70305
        return self.parent().parent().size()

    def populate(self, seq_records):
        self.grid_layout.removeWidget(self.seq_h_scroll_bar)
        self.grid_layout.removeItem(self.lower_spacer_item)

        for seq_record in seq_records:
            new_row = self.grid_layout.rowCount()
            self.seq_record_items.append(
                SequenceRecordItem(self, seq_record, self.seq_font))
            for widget_index in range(0,
                                      len(self.seq_record_items[-1].widgets)):
                col = widget_index
                self.seq_record_items[-1].seqLabel.installEventFilter(self)
                self.grid_layout.addWidget(
                    self.seq_record_items[-1].widgets[widget_index], new_row,
                    col)

            if len(seq_record) > self.longest_seq_len:
                self.longest_seq_len = len(seq_record)

        self.update_char_nb()
        self.grid_layout.addWidget(self.seq_h_scroll_bar,
                                   self.grid_layout.rowCount(), 5)
        self.grid_layout.addItem(self.lower_spacer_item)
        self.display_all_seq()

    def clear(self):
        # TODO
        pass

    def eventFilter(self, watched, event):
        if event.type() == QEvent.Resize:
            self.update_char_nb()
            self.update_scrollbar()
            self.display_all_seq()
        return super(SequenceRecordsWindow, self).eventFilter(watched, event)

    def display_all_seq(self):
        for seq_record_item in self.seq_record_items:
            seq_record_item.seqLabel.display_seq(
                seq_record_item.seq_record.seq, self.display_begin,
                self.char_nb)

    def update_char_nb(self):
        font_metrics = QFontMetrics(self.seq_font)
        px_wide_char = font_metrics.width("A")
        label_width = self.seq_record_items[0].seqLabel.width(
        )  # width of first seq label = all seq labels
        approx_char_nb = label_width // px_wide_char

        test_str = "A" * approx_char_nb

        while font_metrics.width(
                test_str) < label_width:  # fontMetrics not precise at all...
            test_str += "A"

        while font_metrics.width(
                test_str) >= label_width:  # fontMetrics not precise at all...
            test_str = test_str[:-1]

        self.char_nb = len(test_str)

    def update_scrollbar(self):
        self.seq_h_scroll_bar.setMaximum(self.longest_seq_len - self.char_nb +
                                         12)

    def move_seqs(self, value):
        print(value)
        self.display_begin = value
        self.display_all_seq()

    char_nb = 0
    longest_seq_len = 0
    display_begin = 0
Beispiel #11
0
class NetworkDevice():
    def __init__(self, jsonDict, warningHandler: WarningHandler,
                 tabWidget: QTabWidget):
        """
        Base class for any object on the MQTT network
        """

        self.ipAddr = jsonDict['ip']
        self.mac = jsonDict['mac']
        self.name = jsonDict['name']
        self.apiVersion = jsonDict['api']
        self.warningHandler = warningHandler
        self.uptime = ""
        self.linkSpeed = jsonDict['link']
        self._tabWidget = tabWidget
        self.updated = False  # Flag if any info has been updated
        self.warnings = []
        self.errors = []
        self.state = None

        # Create tab in the tab widget for this device
        self._tab = QWidget(self._tabWidget)
        if (self.mac == get_mac()):
            # Ensure that the controller in use goes first
            self._tabWidget.insertTab(0, self._tab,
                                      f"{self.name}\n{self.ipAddr}")
        else:
            # Other device, put at end
            self._tabWidget.addTab(self._tab, f"{self.name}\n{self.ipAddr}")
        self._horizontalLayout = QHBoxLayout(self._tab)
        self._grid = QGridLayout()
        self._iconLayout = QVBoxLayout()
        self._horizontalLayout.addLayout(self._grid)
        self._horizontalLayout.addLayout(self._iconLayout)
        self._horizontalLayout.setStretchFactor(self._iconLayout, 1)

        self._ipLabel = self.add_value_row("IP Address:", self.ipAddr)

        # These are the definition of a unique device so never need updating
        self.add_value_row("Type:", self.get_type())
        self.add_value_row("MAC Address:", self.mac)

        self._apiLabel = self.add_value_row("MQTT API Version:",
                                            self.apiVersion)

        self._uptimeLabel = self.add_value_row("Uptime:", self.uptime)
        self._onlineLabel = self.add_value_row("Online:", "")
        self._update_online_state(True)
        self._linkSpeedLabel = self.add_value_row(
            "Link Speed:",
            str(self.linkSpeed) + " Mbps")

        self._grid.setRowStretch(self._grid.rowCount(), 1)
        self._tab.setLayout(self._horizontalLayout)

    def add_value_row(self, labelString, initialValue):
        """
        Adds a row to self._tab with two QLabels horizontally laid
        out. First contains the identifying string, second contains
        value.

        Args:
            labelString (str): Label for this item
            intialValue (str / int): value for this item

        Returns:
            QLabel: label item created for the value so it can be updated

        Raises:
            None
        """
        labelWidget = QLabel(labelString)
        labelWidget.setStyleSheet(
            "font: Waree; font-size: 32px; font-weight: bold")
        valueWidget = QLabel(str(initialValue))
        valueWidget.setStyleSheet("font: Waree; font-size: 32px")
        rowCount = self._grid.rowCount()
        # Should just be disable stretch on last row but seems
        # to need to be last two rows. Can't be bothered fixing
        self._grid.setRowStretch(rowCount - 1, 0)
        self._grid.setRowStretch(rowCount - 2, 0)
        self._grid.addWidget(labelWidget, rowCount, 0)
        self._grid.addWidget(valueWidget, rowCount, 1)
        self._grid.setRowStretch(self._grid.rowCount(), 1)
        return valueWidget

    def update_discovery_info(self, jsonDict: dict) -> None:
        """
        Updates the discovery information for an already existing
        entry in self._tab. None of this should be changing -
        discovery information is intended to be static
        """
        self._update_online_state(True)
        if self.ipAddr != jsonDict['ip']:
            self.warningHandler.add_warning(
                self.name, "Network",
                f"Device {self.name} ({self.mac}) changed IP address"
                f"from {self.ipAddr} to {jsonDict['ip']}")
            self.ipAddr = jsonDict['ip']
            self.updated = True

        if self.name != jsonDict['name']:
            self.warningHandler.add_warning(
                self.name, "Network",
                f"Device {self.name} ({self.mac}) changed name"
                f"from {self.name} to {jsonDict['name']}")
            self.name = jsonDict['name']
            self.updated = True

        if self.apiVersion != jsonDict['api']:
            self.warningHandler.add_warning(
                self.name, "Network",
                f"Device {self.name} ({self.mac}) changed API"
                f"from {self.apiVersion} to {jsonDict['api']}")
            self.apiVersion = jsonDict['api']
            self.updated = True

        if self.linkSpeed != jsonDict['link']:
            self.warningHandler.add_warning(
                self.name, "Network",
                f"Device {self.name} ({self.mac}) changed speed"
                f"from {self.linkSpeed}Mbps to {jsonDict['link']} Mbps")
            self.name = jsonDict['name']
            self.updated = True

        self._ipLabel.setText(self.ipAddr)
        self._apiLabel.setText(self.apiVersion)
        self._linkSpeedLabel.setText(str(self.linkSpeed) + " Mbps")

        # Online is a bit special as we want a tick box
        self._update_online_state(True)

        # Update tab name
        self._tabWidget.setTabText(self._tabWidget.indexOf(self._tab),
                                   f"{self.name}\n{self.ipAddr}")

        if self.updated:
            logging.info(f"Updated discovery info for device "
                         f"{self.mac} ({self.name})")

    def update_status_info(self, jsonDict: dict) -> None:
        """
        Updates the discovery information for an already existing
        entry in self._tab.
        """

        # Online is a bit special as we want a tick box
        self._update_online_state(True)

        self._uptimeLabel.setText(jsonDict['uptime'])

        if self.updated:
            logging.debug(f"Updated status info for device "
                          f"{self.mac} ({self.name})")

    def _update_online_state(self, online: bool):
        """
        Updates online status icon
        """
        if not isinstance(online, bool):
            raise TypeError(f"{online} not in (True, False)")
        self.online = online
        if online:
            icon = QIcon("resources/img/icon_tick.png")
        else:
            icon = QIcon("resources/img/icon_cross.png")
        pixmap = icon.pixmap(QSize(32, 32))
        self._onlineLabel.setPixmap(pixmap)
Beispiel #12
0
    else:
        controller = None
        modbusUpdateTimer.stop()
        graphUpdateTimer.stop()


connectButton = QPushButton("Connect")
connectButton.setCheckable(True)
connectButton.setFont(appFont)
connectButton.toggled.connect(connectToPort)

tab1Layout = QGridLayout()
actionLayout = QVBoxLayout()
portSelectLayout = QHBoxLayout()
tab1Layout.columnCount = 2
tab1Layout.rowCount = 2
tab1Layout.setColumnStretch(0, 2)
tab1Layout.setColumnStretch(1, 1)

# actionLayout.setSizeConstraint()
actionLayout.addWidget(startStopButton)
portSelectLayout.setStretch(0, 1)
portSelectLayout.addWidget(refreshPortsButton)
portSelectLayout.setStretch(1, 4)
portSelectLayout.addWidget(portSelectBox)
portSelectLayout.setStretch(2, 2)
portSelectLayout.addWidget(connectButton)
actionLayout.addLayout(portSelectLayout)

tab1Layout.addWidget(graph, 0, 0)
tab1Layout.addLayout(actionLayout, 0, 1)