Ejemplo n.º 1
0
class RegionSeg(QGroupBox):
    """
    Region segmentation method panel

    """
    def __init__(
        self,
        parent,
        calculate_volumes_default=CALCULATE_VOLUMES_DEFAULT,
        summarise_volumes_default=SUMMARIZE_VOLUMES_DEFAULT,
        brush_size=BRUSH_SIZE,
        image_file_extension=IMAGE_FILE_EXT,
        num_colors=NUM_COLORS,
    ):

        super(RegionSeg, self).__init__()
        self.parent = parent

        self.calculate_volumes_default = calculate_volumes_default
        self.summarise_volumes_default = summarise_volumes_default

        # Brushes / ...
        self.brush_size_default = BRUSH_SIZE  # Keep track of default
        self.brush_size = brush_size  # Initialise
        self.num_colors = num_colors

        # File formats
        self.image_file_extension = image_file_extension

    def add_region_panel(self, row):
        self.region_panel = QGroupBox("Region analysis")
        region_layout = QGridLayout()

        add_button(
            "Add region",
            region_layout,
            self.add_region,
            2,
            0,
        )

        add_button(
            "Analyse regions",
            region_layout,
            self.run_region_analysis,
            2,
            1,
        )

        self.calculate_volumes_checkbox = add_checkbox(
            region_layout,
            self.calculate_volumes_default,
            "Calculate volumes",
            0,
        )

        self.summarise_volumes_checkbox = add_checkbox(
            region_layout,
            self.summarise_volumes_default,
            "Summarise volumes",
            1,
        )

        region_layout.setColumnMinimumWidth(1, COLUMN_WIDTH)
        self.region_panel.setLayout(region_layout)
        self.parent.layout.addWidget(self.region_panel, row, 0, 1, 2)
        self.region_panel.setVisible(False)

    def toggle_region_panel(self):
        # TODO: Change color scheme directly when theme is switched
        # TODO: "text-align" property should follow constant SEGM_METHODS_PANEL_ALIGN
        if self.region_panel.isVisible():
            self.region_panel.setVisible(False)
            if self.parent.viewer.theme == "dark":
                self.parent.show_regionseg_button.setStyleSheet(
                    f"QPushButton {{ background-color: #414851; text-align:{SEGM_METHODS_PANEL_ALIGN};}}"
                    f"QPushButton:pressed {{ background-color: #414851; text-align:{SEGM_METHODS_PANEL_ALIGN};}}"
                )
            else:
                self.parent.show_regionseg_button.setStyleSheet(
                    f"QPushButton {{ background-color: #d6d0ce; text-align:{SEGM_METHODS_PANEL_ALIGN};}}"
                    f"QPushButton:pressed {{ background-color: #d6d0ce; text-align:{SEGM_METHODS_PANEL_ALIGN};}}"
                )

        else:
            self.region_panel.setVisible(True)
            if self.parent.viewer.theme == "dark":
                self.parent.show_regionseg_button.setStyleSheet(
                    f"QPushButton {{ background-color: #7e868f; text-align:{SEGM_METHODS_PANEL_ALIGN};}}"
                    f"QPushButton:pressed {{ background-color: #7e868f; text-align:{SEGM_METHODS_PANEL_ALIGN};}}"
                )
            else:
                self.parent.show_regionseg_button.setStyleSheet(
                    f"QPushButton {{ background-color: #fdf194; text-align:{SEGM_METHODS_PANEL_ALIGN};}}"
                    f"QPushButton:pressed {{ background-color: #fdf194; text-align:{SEGM_METHODS_PANEL_ALIGN};}}"
                )

    def check_saved_region(self):
        add_existing_region_segmentation(
            self.parent.paths.regions_directory,
            self.parent.viewer,
            self.parent.label_layers,
            self.image_file_extension,
        )

    def add_region(self):
        print("Adding a new region\n")
        self.region_panel.setVisible(True)  # Should be visible by default!
        add_new_region_layer(
            self.parent.viewer,
            self.parent.label_layers,
            self.parent.base_layer.data,
            self.brush_size,
            self.num_colors,
        )

    def run_region_analysis(self):
        if self.parent.label_layers:
            print("Running region analysis")
            worker = region_analysis(
                self.parent.label_layers,
                self.parent.atlas_layer.data,
                self.parent.atlas,
                self.parent.paths.regions_directory,
                output_csv_file=self.parent.paths.region_summary_csv,
                volumes=self.calculate_volumes_checkbox.isChecked(),
                summarise=self.summarise_volumes_checkbox.isChecked(),
            )
            worker.start()
        else:
            print("No regions found")
Ejemplo n.º 2
0
class AnimationWindow(PyDialog):
    """
    +-------------------+
    | Animation         |
    +-------------------------+
    | icase   ______          |
    | scale   ______  Default |
    | time    ______  Default |
    |                         |
    | nframes ______  Default |
    | resolu. ______  Default |
    | Dir     ______  Browse  |
    | iFrame  ______          |
    |                         |
    | Animations:             |
    | o Scale, Phase, Time    |
    |                         |
    | x delete images         |
    | x repeat                |  # TODO: change to an integer
    | x make gif              |
    |                         |
    |      Step, RunAll       |
    |         Close           |
    +-------------------------+

    TODO: add key-frame support
    """
    def __init__(self, data, win_parent=None):
        PyDialog.__init__(self, data, win_parent)
        self.set_font_size(data['font_size'])
        self.istep = 0
        self._animate_type = 'time'

        self._updated_animation = False
        self._active_deformation = 0.
        self._icase_fringe = data['icase_fringe']
        self._icase_disp = data['icase_disp']
        self._icase_vector = data['icase_vector']

        self._default_name = data['name']
        self._default_time = data['time']
        self._default_fps = data['frames/sec']
        self._default_resolution = data['resolution']

        self._scale = data['scale']
        self._default_scale = data['default_scale']
        self._default_is_scale = data['is_scale']

        self._arrow_scale = data['arrow_scale']
        self._default_arrow_scale = data['default_arrow_scale']

        self._phase = data['phase']
        self._default_phase = data['default_phase']

        self._default_dirname = data['dirname']
        self._default_gif_name = os.path.join(self._default_dirname,
                                              data['name'] + '.gif')

        self.animation_types = [
            'Animate Scale',
        ]
        #'Animate Phase',
        #'Animate Time',
        #'Animate Frequency Sweep'
        #]

        self.setWindowTitle('Animate Model')
        self.create_widgets()
        self.create_layout()
        self.set_connections()

        self.is_gui = False
        if hasattr(self.win_parent, '_updated_legend'):
            self.win_parent.is_animate_open = True
            self.is_gui = True
        self.on_update_min_max_defaults()

    def create_widgets(self):
        """creates the menu objects"""
        self.box_scale = QGroupBox('Animate Scale')
        self.box_time = QGroupBox('Animate Time')

        icase_max = 1000  # TODO: update 1000

        self.checkbox_fringe = QCheckBox('Animate')
        self.checkbox_fringe.setToolTip(
            'Animate the fringe in addition to the deflection')
        #self.checkbox_disp = QCheckBox('Animate')
        self.checkbox_fringe.setEnabled(False)

        self.icase_fringe_label = QLabel("iCase (Fringe):")
        self.icase_fringe_edit = QSpinBox(self)
        self.icase_fringe_edit.setRange(0, icase_max)
        self.icase_fringe_edit.setSingleStep(1)
        if self._icase_fringe is not None:
            self.icase_fringe_edit.setValue(self._icase_fringe)
        self.icase_fringe_edit.setToolTip(
            'Case Number for the Scale/Phase Animation Type.\n'
            'Defaults to the result you had shown when you clicked "Create Animation".\n'
            'iCase can be seen by clicking "Apply" on a result.')

        self.icase_disp_label = QLabel("iCase (Disp):")
        self.icase_disp_edit = QSpinBox(self)
        self.icase_disp_edit.setRange(1, icase_max)
        self.icase_disp_edit.setSingleStep(1)
        if self._icase_disp is not None:
            self.icase_disp_edit.setValue(self._icase_disp)

        self.checkbox_vector = QCheckBox('Animate')
        self.checkbox_vector.setToolTip(
            'Animate the vector in addition to the deflection')
        self.checkbox_vector.hide()
        #self.checkbox_disp = QCheckBox('Animate')

        self.icase_vector_label = QLabel("iCase (Vector):")
        self.icase_vector_edit = QSpinBox(self)
        self.icase_vector_edit.setRange(1, icase_max)
        self.icase_vector_edit.setSingleStep(1)
        if self._icase_vector is not None:
            self.icase_vector_edit.setValue(self._icase_vector)

        self.scale_label = QLabel("True Scale:")
        self.scale_edit = QLineEdit(str(self._scale))
        self.scale_button = QPushButton("Default")
        self.scale_edit.setToolTip('Scale factor of the "deflection"')
        self.scale_button.setToolTip('Sets the scale factor of the gif to %s' %
                                     self._scale)

        self.arrow_scale_label = QLabel("Arrow Scale:")
        self.arrow_scale_edit = QLineEdit(str(self._scale))
        self.arrow_scale_button = QPushButton("Default")
        self.arrow_scale_edit.setToolTip('Scale factor of the "arrows"')
        self.arrow_scale_button.setToolTip(
            'Sets the arrow scale factor of the gif to %s' % self._arrow_scale)

        self.arrow_scale_label.setVisible(False)
        self.arrow_scale_edit.setVisible(False)
        self.arrow_scale_button.setVisible(False)

        self.time_label = QLabel("Total Time (sec):")
        self.time_edit = QDoubleSpinBox(self)
        self.time_edit.setValue(self._default_time)
        self.time_edit.setRange(0.1, 5. * 60.)
        self.time_edit.setDecimals(2)
        self.time_edit.setSingleStep(0.1)
        self.time_button = QPushButton("Default")
        self.time_edit.setToolTip("Total time of the gif")
        self.time_button.setToolTip('Sets the total time of the gif to %.2f' %
                                    self._default_time)

        self.fps_label = QLabel("Frames/Second:")
        self.fps_edit = QSpinBox(self)
        self.fps_edit.setRange(1, 60)
        self.fps_edit.setSingleStep(1)
        self.fps_edit.setValue(self._default_fps)
        self.fps_button = QPushButton("Default")
        self.fps_edit.setToolTip(
            "A higher FPS is smoother, but may not play well for large gifs")
        self.fps_button.setToolTip('Sets the FPS to %s' % self._default_fps)

        self.resolution_label = QLabel("Resolution Scale:")
        self.resolution_edit = QSpinBox(self)
        self.resolution_edit.setRange(1, 5)
        self.resolution_edit.setSingleStep(1)
        self.resolution_edit.setValue(self._default_resolution)
        self.resolution_button = QPushButton("Default")
        self.resolution_edit.setToolTip(
            'Scales the window resolution by an integer factor')
        self.resolution_button.setToolTip('Sets the resolution to %s' %
                                          self._default_resolution)

        #-----------------
        # Time plot
        self.fringe_label = QLabel("Fringe")

        self.icase_fringe_start_edit = QSpinBox(self)
        self.icase_fringe_start_edit.setRange(0, icase_max)
        self.icase_fringe_start_edit.setSingleStep(1)
        self.icase_fringe_start_edit.setValue(self._icase_fringe)
        self.icase_fringe_start_button = QPushButton("Default")

        self.icase_fringe_end_edit = QSpinBox(self)
        self.icase_fringe_end_edit.setRange(0, icase_max)
        self.icase_fringe_end_edit.setSingleStep(1)
        self.icase_fringe_end_edit.setValue(self._icase_fringe)
        self.icase_fringe_end_button = QPushButton("Default")

        self.icase_fringe_delta_edit = QSpinBox(self)
        self.icase_fringe_delta_edit.setRange(1, icase_max)
        self.icase_fringe_delta_edit.setSingleStep(1)
        self.icase_fringe_delta_edit.setValue(1)
        self.icase_fringe_delta_button = QPushButton("Default")

        self.displacement_label = QLabel("Displacement")
        self.icase_start = QLabel("iCase Start:")
        self.icase_disp_start_edit = QSpinBox(self)
        self.icase_disp_start_edit.setRange(0, icase_max)
        self.icase_disp_start_edit.setSingleStep(1)
        self.icase_disp_start_edit.setValue(self._icase_fringe)
        self.icase_disp_start_button = QPushButton("Default")

        self.icase_end_label = QLabel("iCase End:")
        self.icase_disp_end_edit = QSpinBox(self)
        self.icase_disp_end_edit.setRange(0, icase_max)
        self.icase_disp_end_edit.setSingleStep(1)
        self.icase_disp_end_edit.setValue(self._icase_fringe)
        self.icase_disp_end_button = QPushButton("Default")

        self.icase_delta_label = QLabel("iCase Delta:")
        self.icase_disp_delta_edit = QSpinBox(self)
        self.icase_disp_delta_edit.setRange(1, icase_max)
        self.icase_disp_delta_edit.setSingleStep(1)
        self.icase_disp_delta_edit.setValue(1)
        self.icase_disp_delta_button = QPushButton("Default")

        self.min_value_enable = QCheckBox()
        self.min_value_label = QLabel("Min Value:")
        self.min_value_edit = QLineEdit('')
        #self.min_value_edit.setRange(1, 1000)
        #self.min_value_edit.setSingleStep(1)
        #self.min_value_edit.setValue(1)
        self.min_value_button = QPushButton("Default")

        self.max_value_enable = QCheckBox()
        self.max_value_label = QLabel("Max Value:")
        self.max_value_edit = QLineEdit('')
        #self.min_value_edit.setRange(1, 1000)  # TODO: update 1000
        #self.min_value_edit.setSingleStep(1)
        #self.min_value_edit.setValue(1)
        self.max_value_button = QPushButton("Default")

        # TODO: enable this (uncomment) ------------------------------------------
        #self.min_value_enable.hide()
        #self.min_value.hide()
        #self.min_value_edit.hide()
        #self.min_value_button.hide()

        #self.max_value_enable.hide()
        #self.max_value.hide()
        #self.max_value_edit.hide()
        #self.max_value_button.hide()
        # TODO: enable this (uncomment) ------------------------------------------

        self.icase_disp_start_edit.setToolTip(
            'The first frame of the animation')
        self.icase_disp_end_edit.setToolTip(
            'The last frame of the animation\n'
            'Assumes icase_start + nframes * icase_delta = icase_end')
        self.icase_disp_delta_edit.setToolTip(
            'The frame step size (to skip non-consecutive results).\n'
            'Frame skipping can be used to:\n'
            "  - skip across results that you don't want to plot\n"
            '  - adjust the FPS')

        self.min_value_edit.setToolTip(
            'Min value of the legend (not supported)')
        self.max_value_edit.setToolTip(
            'Max value of the legend (not supported)')
        #'time' : 0.,
        #'default_time' : 0,
        #'icase_start' : 10,
        #'icase_delta' : 3,
        #'min_value' : 0.,
        #'max_value' : 1000.,

        self.browse_folder_label = QLabel('Output Directory:')
        self.browse_folder_edit = QLineEdit(str(self._default_dirname))
        self.browse_folder_button = QPushButton('Browse')
        self.browse_folder_edit.setToolTip(
            'Location to save the png/gif files')

        self.gif_label = QLabel("Gif Filename:")
        self.gif_edit = QLineEdit(str(self._default_name + '.gif'))
        self.gif_button = QPushButton('Default')
        self.gif_edit.setToolTip('Name of the gif')
        self.gif_button.setToolTip('Sets the name of the gif to %s.gif' %
                                   self._default_name)

        # scale / phase
        if 1:  # pragma: no cover
            self.animate_scale_radio = QRadioButton("Animate Scale")
            self.animate_phase_radio = QRadioButton("Animate Phase")
            self.animate_time_radio = QRadioButton("Animate Time")
            self.animate_freq_sweeep_radio = QRadioButton(
                "Animate Frequency Sweep")
            self.animate_scale_radio.setToolTip(
                'Animates the scale factor based on the "Animation Type"')
            self.animate_time_radio.setToolTip(
                'Animates the time/load/mode step')

            self.animate_scale_radio.setChecked(self._default_is_scale)
            self.animate_phase_radio.setChecked(not self._default_is_scale)
            self.animate_time_radio.setChecked(False)

            msg = 'Scale : Animates the scale factor based on the "Animation Profile"\n'
            if self._default_phase is None:
                self.animate_phase_radio.setDisabled(True)
                self.animate_phase_radio.setToolTip(
                    'Animates the phase angle '
                    '(only for complex results)')
                msg += 'Phase : Animates the phase angle (only for complex results)\n'
            else:
                self.animate_phase_radio.setToolTip("Animates the phase angle")
                msg += 'Phase : Animates the phase angle\n'
            msg += (
                'Time : Animates the time/load/mode step\n'
                'Freq Sweep : Animates a complex result across a range of frequencies '
                '(not supported)\n')

            self.animate_freq_sweeep_radio.setDisabled(True)
            self.animate_freq_sweeep_radio.setToolTip(
                'Animates a complex result across a range of frequencies (not supported)'
            )
        else:
            msg = 'Scale : Animates the scale factor based on the "Animation Profile"\n'
            if self._default_phase is None:
                #self.animate_phase_radio.setDisabled(True)
                #self.animate_phase_radio.setToolTip('Animates the phase angle '
                #'(only for complex results)')
                msg += 'Phase : Animates the phase angle (only for complex results)\n'
            else:
                #self.animate_phase_radio.setToolTip("Animates the phase angle")
                msg += 'Phase : Animates the phase angle\n'
            msg += (
                'Time : Animates the time/load/mode step\n'
                'Freq Sweep : Animates a complex result across a range of frequencies '
                '(not supported)\n')

        self.animation_type = QLabel("Animation Type:")
        animation_type = OrderedDict()
        #scale_msg = 'Scale\n'
        #phase_msg = 'Phase\n'
        #time_msg = 'Time\n'
        #animation_types = [
        #('Animate Scale', scale_msg),
        #('Animate Phase', phase_msg),
        #('Animate Time', time_msg),
        ##'Animate Frequency Sweep'
        #]

        if self._phase is not None:
            self.animation_types.append('Animate Phase')
        self.animation_types.append('Animate Time')

        self.animation_profile_label = QLabel("Animation Profile:")

        self.animation_profile_edit = QComboBox()
        for animation_profile in ANIMATION_PROFILES:
            self.animation_profile_edit.addItem(animation_profile)
        self.animation_profile_edit.setToolTip('The profile for a scaled GIF')

        self.animation_type_edit = QComboBox()
        # TODO: add a tooltip for each item
        for animation_type in self.animation_types:
            self.animation_type_edit.addItem(animation_type)
        #self.animation_type_edit.setToolTip('The profile for a scaled GIF')
        self.animation_type_edit.setToolTip(msg.rstrip())

        self.csv_profile_label = QLabel("CSV profile:")
        self.csv_profile_edit = QLineEdit()
        self.csv_profile_browse_button = QPushButton('Browse')
        self.csv_profile_edit.setToolTip(
            'The path to the CSV file of (Scale1, Scale2, Scale3, ...)')

        #widget = QWidget(self)
        #horizontal_vertical_group = QButtonGroup(widget)
        #horizontal_vertical_group.addButton(self.animate_scale_radio)
        #horizontal_vertical_group.addButton(self.animate_phase_radio)
        #horizontal_vertical_group.addButton(self.animate_time_radio)
        #horizontal_vertical_group.addButton(self.animate_freq_sweeep_radio)

        # animate in gui
        self.animate_in_gui_checkbox = QCheckBox("Animate In GUI?")
        self.animate_in_gui_checkbox.setChecked(True)

        # make images
        self.make_images_checkbox = QCheckBox("Make images?")
        self.make_images_checkbox.setChecked(True)

        # make images
        self.overwrite_images_checkbox = QCheckBox("Overwrite images?")
        self.overwrite_images_checkbox.setChecked(True)

        # delete images when finished
        self.delete_images_checkbox = QCheckBox("Delete images when finished?")
        self.delete_images_checkbox.setChecked(True)

        # endless loop
        self.repeat_checkbox = QCheckBox("Repeat?")
        self.repeat_checkbox.setChecked(True)
        self.repeat_checkbox.setToolTip(
            "Repeating creates an infinitely looping gif")

        # endless loop
        self.make_gif_checkbox = QCheckBox("Make Gif?")
        if IS_IMAGEIO:
            self.make_gif_checkbox.setChecked(True)
        else:
            self.make_gif_checkbox.setChecked(False)
            self.make_gif_checkbox.setEnabled(False)
            self.make_gif_checkbox.setToolTip(
                'imageio is not available; install it')

        # bottom buttons
        self.step_button = QPushButton("Step")
        self.wipe_button = QPushButton("Wipe Deformed Shape")
        self.stop_button = QPushButton("Stop")
        self.run_button = QPushButton("Run")

        self.step_button.setToolTip(
            'Steps through the animation (for testing)')
        self.wipe_button.setToolTip(
            'Removes the existing "deflecton" from the animation')
        self.stop_button.setToolTip('Stops the animation')
        self.run_button.setToolTip('Creates the animation')
        self.step_button.hide()
        self.wipe_button.hide()

        self.wipe_button.setEnabled(False)
        #self.wipe_button.hide()
        self.stop_button.setEnabled(False)

        self.cancel_button = QPushButton("Close")

        #self.set_grid_time(enabled=False)
        #self.set_grid_scale(enabled=self._default_is_scale)
        if self._default_phase:
            self.on_animate_phase(force=True)
            set_combo_box_text(self.animation_type_edit, 'Animate Phase')
        else:
            self.on_animate_scale(force=True)

    def set_connections(self):
        """creates button actions"""
        self.checkbox_vector.clicked.connect(self.on_checkbox_vector)

        self.scale_button.clicked.connect(self.on_default_scale)
        self.arrow_scale_button.clicked.connect(self.on_default_arrow_scale)
        self.time_button.clicked.connect(self.on_default_time)

        self.fps_button.clicked.connect(self.on_default_fps)
        self.resolution_button.clicked.connect(self.on_default_resolution)
        self.browse_folder_button.clicked.connect(self.on_browse_folder)
        self.csv_profile_browse_button.clicked.connect(self.on_browse_csv)
        self.gif_button.clicked.connect(self.on_default_name)

        self.step_button.clicked.connect(self.on_step)
        self.wipe_button.clicked.connect(self.on_wipe)
        self.stop_button.clicked.connect(self.on_stop)
        self.run_button.clicked.connect(self.on_run)
        self.min_value_enable.clicked.connect(self.on_min_value_enable)
        self.max_value_enable.clicked.connect(self.on_max_value_enable)

        self.min_value_button.clicked.connect(self.on_min_value_default)
        self.max_value_button.clicked.connect(self.on_max_value_default)
        self.icase_disp_start_button.clicked.connect(
            self.on_update_min_max_defaults)

        #self.animate_scale_radio.clicked.connect(self.on_animate_scale)
        #self.animate_phase_radio.clicked.connect(self.on_animate_phase)
        #self.animate_time_radio.clicked.connect(self.on_animate_time)
        self.animation_type_edit.currentIndexChanged.connect(self.on_animate)
        #self.animate_freq_sweeep_radio

        self.cancel_button.clicked.connect(self.on_cancel)

        self.animate_in_gui_checkbox.clicked.connect(self.on_animate_in_gui)
        self.animate_in_gui_checkbox.setChecked(True)
        self.on_animate_in_gui()

    def on_checkbox_vector(self):
        is_enabled = self.checkbox_vector.isEnabled()
        is_checked = self.checkbox_vector.isChecked()
        enable_edit = is_enabled and is_checked
        self.icase_vector_label.setEnabled(is_checked)
        self.icase_vector_edit.setEnabled(enable_edit)

    def on_animate_in_gui(self):
        animate_in_gui = self.animate_in_gui_checkbox.isChecked()
        enable = not animate_in_gui
        if HIDE_WHEN_INACTIVE:
            self.make_images_checkbox.setVisible(enable)
            self.delete_images_checkbox.setVisible(enable)
            self.make_gif_checkbox.setVisible(enable)
            self.repeat_checkbox.setVisible(enable)
            self.resolution_button.setVisible(enable)
            self.resolution_label.setVisible(enable)
            self.resolution_edit.setVisible(enable)
            self.gif_label.setVisible(enable)
            self.gif_edit.setVisible(enable)
            self.gif_button.setVisible(enable)
            self.browse_folder_label.setVisible(enable)
            self.browse_folder_button.setVisible(enable)
            self.browse_folder_edit.setVisible(enable)
            self.step_button.setEnabled(enable)

        self.make_images_checkbox.setEnabled(enable)
        self.delete_images_checkbox.setEnabled(enable)
        self.make_gif_checkbox.setEnabled(enable)
        self.repeat_checkbox.setEnabled(enable)
        self.resolution_button.setEnabled(enable)
        self.resolution_edit.setEnabled(enable)
        self.gif_edit.setEnabled(enable)
        self.gif_button.setEnabled(enable)
        self.browse_folder_button.setEnabled(enable)
        self.browse_folder_edit.setEnabled(enable)
        self.step_button.setEnabled(enable)
        #wipe_button

    def on_animate(self, value):
        """
        animate pulldown

        Parameters
        ----------
        value : int
            index in animation_types
        """
        #animation_types = ['Animate Scale', 'Animate Phase', 'Animate Time',
        #'Animate Frequency Sweep']
        animation_type = self.animation_types[value]
        if animation_type == 'Animate Scale':
            self.on_animate_scale()
        elif animation_type == 'Animate Phase':
            self.on_animate_phase()
        elif animation_type == 'Animate Time':
            self.on_animate_time()
        else:
            raise NotImplementedError('value = ', value)

    def on_animate_time(self, force=False):
        """enables the secondary input"""
        #print('on_animate_time')
        if self._animate_type == 'scale' or force:
            self.set_grid_scale(False, 'time')
        self.set_grid_time(True, 'time')
        self._animate_type = 'time'

    def on_animate_scale(self, force=False):
        """enables the secondary input"""
        #print('on_animate_scale')
        self.set_grid_scale(True, 'scale')
        if self._animate_type == 'time' or force:
            self.set_grid_time(False, 'scale')
        self._animate_type = 'scale'

    def on_animate_phase(self, force=False):
        """enables the secondary input"""
        #print('on_animate_phase')
        if self._animate_type == 'scale' or force:
            self.set_grid_scale(False, 'phase')
        if self._animate_type == 'time' or force:
            self.set_grid_time(False, 'phase')
        self._animate_type = 'phase'

    def set_grid_scale(self, enabled=True, word=''):
        """enables/disables the secondary input"""
        #print('%s-set_grid_scale; enabled = %r' % (word, enabled))
        if HIDE_WHEN_INACTIVE:
            self.box_scale.setVisible(enabled)
            self.animation_profile_label.setVisible(enabled)
            self.animation_profile_edit.setVisible(enabled)
            #self.min_value_enable.setVisible(enabled)
            #self.max_value_enable.setVisible(enabled)

        self.animation_profile_label.setEnabled(enabled)
        self.animation_profile_edit.setEnabled(enabled)

        # TODO: doesn't work...
        #self.csv_profile.setEnabled(enabled)
        #self.csv_profile_edit.setEnabled(enabled)
        #self.csv_profile_button.setEnabled(enabled)

        self.min_value_enable.setEnabled(enabled)
        self.max_value_enable.setEnabled(enabled)
        self.on_min_value_enable()
        self.on_max_value_enable()

    def set_grid_time(self, enabled=True, word=''):
        """enables/disables the secondary input"""
        #print('%s-set_grid_time; enabled = %r' % (word, enabled))
        if HIDE_WHEN_INACTIVE:
            self.box_time.setVisible(enabled)
            self.checkbox_fringe.setVisible(not enabled)
            self.icase_fringe_label.setVisible(not enabled)
            self.icase_fringe_edit.setVisible(not enabled)
            self.icase_disp_label.setVisible(not enabled)
            self.icase_disp_edit.setVisible(not enabled)
            self.icase_vector_label.setVisible(not enabled)
            self.icase_vector_edit.setVisible(not enabled)

            #self.icase_fringe_delta_edit.setVisible(enabled)
            self.icase_disp_delta_edit.setVisible(enabled)
            self.icase_disp_delta_edit.setVisible(enabled)
            self.fps_label.setVisible(enabled)
            self.fps_edit.setVisible(enabled)
            self.fps_button.setVisible(enabled)

        self.displacement_label.setEnabled(enabled)
        self.fringe_label.setEnabled(enabled)

        self.icase_start.setEnabled(enabled)
        self.icase_disp_start_edit.setEnabled(enabled)
        self.icase_disp_start_button.setEnabled(enabled)
        self.icase_fringe_start_edit.setEnabled(enabled)
        self.icase_fringe_start_button.setEnabled(enabled)

        self.icase_end_label.setEnabled(enabled)
        self.icase_disp_end_edit.setEnabled(enabled)
        self.icase_disp_end_button.setEnabled(enabled)
        self.icase_fringe_end_edit.setEnabled(enabled)
        self.icase_fringe_end_button.setEnabled(enabled)

        self.icase_delta_label.setEnabled(enabled)
        self.icase_disp_delta_edit.setEnabled(enabled)
        self.icase_disp_delta_button.setEnabled(enabled)
        self.icase_fringe_delta_edit.setEnabled(enabled)
        self.icase_fringe_delta_button.setEnabled(enabled)

        #-----------------------------------------------------------------------
        is_min_enabled = self.min_value_enable.isChecked()
        self.min_value_label.setEnabled(is_min_enabled)
        self.min_value_edit.setEnabled(is_min_enabled)
        self.min_value_button.setEnabled(is_min_enabled)

        is_max_enabled = self.max_value_enable.isChecked()
        self.max_value_label.setEnabled(is_max_enabled)
        self.max_value_edit.setEnabled(is_max_enabled)
        self.max_value_button.setEnabled(is_max_enabled)

        self.min_value_enable.setEnabled(enabled)
        self.on_min_value_enable()
        #self.min_value.setEnabled(enabled)
        #self.min_value_edit.setEnabled(enabled)
        #self.min_value_button.setEnabled(enabled)

        self.max_value_enable.setEnabled(enabled)
        self.on_max_value_enable()
        #self.max_value.setEnabled(enabled)
        #self.max_value_edit.setEnabled(enabled)
        #self.max_value_button.setEnabled(enabled)

        self.icase_fringe_label.setEnabled(not enabled)
        self.icase_fringe_edit.setEnabled(not enabled)
        self.checkbox_fringe.setEnabled(not enabled)

        self.icase_disp_label.setEnabled(not enabled)
        self.icase_disp_edit.setEnabled(not enabled)

        self.icase_vector_label.setEnabled(not enabled)
        self.icase_vector_edit.setEnabled(not enabled)
        self.checkbox_vector.setEnabled(not enabled)
        self.on_checkbox_vector()

        self.fps_label.setEnabled(not enabled)
        self.fps_edit.setEnabled(not enabled)
        self.fps_button.setEnabled(not enabled)

    def on_min_value_enable(self):
        """
        The min edit value box is enabled when we switch to time
        and the box is checked
        """
        is_min_enabled = self.min_value_enable.isChecked(
        ) and self.min_value_enable.isEnabled()
        self.min_value_label.setEnabled(is_min_enabled)
        self.min_value_edit.setEnabled(is_min_enabled)
        self.min_value_button.setEnabled(is_min_enabled)

    def on_max_value_enable(self):
        """
        The max edit value box is enabled when we switch to time
        and the box is checked
        """
        is_max_enabled = self.max_value_enable.isChecked(
        ) and self.max_value_enable.isEnabled()
        self.max_value_label.setEnabled(is_max_enabled)
        self.max_value_edit.setEnabled(is_max_enabled)
        self.max_value_button.setEnabled(is_max_enabled)

    def on_update_min_max_defaults(self):
        """
        When the icase is changed, the min/max value default message is changed
        """
        icase = self.icase_disp_start_edit.value()
        min_value, max_value = self.get_min_max(icase)
        self.min_value_button.setToolTip('Sets the min value to %g' %
                                         min_value)
        self.max_value_button.setToolTip('Sets the max value to %g' %
                                         max_value)

    def on_min_value_default(self):
        """When min default icase is pressued, update the value"""
        icase = self.icase_disp_start_edit.value()
        min_value = self.get_min_max(icase)[0]
        self.min_value_edit.setText(str(min_value))
        self.min_value_edit.setStyleSheet("QLineEdit{background: white;}")

    def on_max_value_default(self):
        """When max default icase is pressued, update the value"""
        icase = self.icase_disp_start_edit.value()
        max_value = self.get_min_max(icase)[1]
        self.max_value_edit.setText(str(max_value))
        self.max_value_edit.setStyleSheet("QLineEdit{background: white;}")

    def on_browse_folder(self):
        """opens a folder dialog"""
        dirname = getexistingdirectory(parent=self,
                                       caption='Select a Directory',
                                       basedir='',
                                       options=QFileDialog.ShowDirsOnly)
        if not dirname:
            return
        self.browse_folder_edit.setText(dirname)

    def on_browse_csv(self):
        """opens a file dialog"""
        default_filename = ''
        file_types = 'Delimited Text (*.txt; *.dat; *.csv)'
        dirname = open_file_dialog(self, 'Select a CSV File', default_filename,
                                   file_types)
        if not dirname:
            return
        self.csv_profile_browse_button.setText(dirname)

    def on_default_name(self):
        """sets the default gif name"""
        self.gif_edit.setText(self._default_name + '.gif')

    def on_default_scale(self):
        """sets the default displacement scale factor"""
        self.scale_edit.setText(str(self._default_scale))
        self.scale_edit.setStyleSheet("QLineEdit{background: white;}")

    def on_default_arrow_scale(self):
        """sets the default arrow scale factor"""
        self.arrow_scale_edit.setText(str(self._default_arrow_scale))
        self.arrow_scale_edit.setStyleSheet("QLineEdit{background: white;}")

    def on_default_time(self):
        """sets the default gif time"""
        self.time_edit.setValue(self._default_time)

    def on_default_fps(self):
        """sets the default FPS"""
        self.fps_edit.setValue(self._default_fps)

    def on_default_resolution(self):
        """sets the default image resolution scale factor"""
        self.resolution_edit.setValue(self._default_resolution)

    def create_layout(self):
        """displays the menu objects"""

        grid = QGridLayout()
        irow = 0
        grid.addWidget(self.icase_fringe_label, irow, 0)
        grid.addWidget(self.icase_fringe_edit, irow, 1)
        grid.addWidget(self.checkbox_fringe, irow, 2)
        irow += 1

        grid.addWidget(self.icase_disp_label, irow, 0)
        grid.addWidget(self.icase_disp_edit, irow, 1)
        #grid.addWidget(self.checkbox_disp, irow, 2)
        irow += 1

        grid.addWidget(self.icase_vector_label, irow, 0)
        grid.addWidget(self.icase_vector_edit, irow, 1)
        grid.addWidget(self.checkbox_vector, irow, 2)
        irow += 1

        grid.addWidget(self.scale_label, irow, 0)
        grid.addWidget(self.scale_edit, irow, 1)
        grid.addWidget(self.scale_button, irow, 2)
        irow += 1

        grid.addWidget(self.arrow_scale_label, irow, 0)
        grid.addWidget(self.arrow_scale_edit, irow, 1)
        grid.addWidget(self.arrow_scale_button, irow, 2)
        irow += 1

        grid.addWidget(self.time_label, irow, 0)
        grid.addWidget(self.time_edit, irow, 1)
        grid.addWidget(self.time_button, irow, 2)
        irow += 1

        # spacer
        spacer = QLabel('')

        grid.addWidget(self.fps_label, irow, 0)
        grid.addWidget(self.fps_edit, irow, 1)
        grid.addWidget(self.fps_button, irow, 2)
        irow += 1

        grid.addWidget(self.animation_type, irow, 0)
        grid.addWidget(self.animation_type_edit, irow, 1)
        irow += 1

        grid.addWidget(spacer, irow, 0)
        irow += 1

        #----------
        #Time
        grid_time = QGridLayout()
        jrow = 0

        self.fringe_label.setAlignment(Qt.AlignCenter)
        self.displacement_label.setAlignment(Qt.AlignCenter)

        if not IS_TIME_FRINGE:
            self.fringe_label.hide()
            self.icase_fringe_delta_edit.hide()
            self.icase_fringe_start_edit.hide()
            self.icase_fringe_end_edit.hide()
            self.icase_fringe_delta_button.hide()

        grid_time.addWidget(self.displacement_label, jrow, 1)
        grid_time.addWidget(self.fringe_label, jrow, 2)
        jrow += 1

        grid_time.addWidget(self.icase_start, jrow, 0)
        grid_time.addWidget(self.icase_disp_start_edit, jrow, 1)
        grid_time.addWidget(self.icase_fringe_start_edit, jrow, 2)
        #grid_time.addWidget(self.icase_disp_start_button, jrow, 2)
        jrow += 1

        grid_time.addWidget(self.icase_end_label, jrow, 0)
        grid_time.addWidget(self.icase_disp_end_edit, jrow, 1)
        grid_time.addWidget(self.icase_fringe_end_edit, jrow, 2)
        #grid_time.addWidget(self.icase_end_button, jrow, 2)
        jrow += 1

        grid_time.addWidget(self.icase_delta_label, jrow, 0)
        grid_time.addWidget(self.icase_disp_delta_edit, jrow, 1)
        grid_time.addWidget(self.icase_fringe_delta_edit, jrow, 2)
        #grid_time.addWidget(self.icase_delta_button, jrow, 2)
        jrow += 1

        hbox_min = QHBoxLayout()
        hbox_min.addWidget(self.min_value_enable)
        hbox_min.addWidget(self.min_value_label)
        grid_time.addLayout(hbox_min, jrow, 0)
        grid_time.addWidget(self.min_value_edit, jrow, 1)
        grid_time.addWidget(self.min_value_button, jrow, 2)
        jrow += 1

        hbox_max = QHBoxLayout()
        hbox_max.addWidget(self.max_value_enable)
        hbox_max.addWidget(self.max_value_label)
        grid_time.addLayout(hbox_max, jrow, 0)
        grid_time.addWidget(self.max_value_edit, jrow, 1)
        grid_time.addWidget(self.max_value_button, jrow, 2)
        jrow += 1

        grid_time.addWidget(spacer, jrow, 0)
        jrow += 1

        #--------------
        grid_scale = QGridLayout()
        grid_scale.addWidget(self.animation_profile_label, 0, 0)
        grid_scale.addWidget(self.animation_profile_edit, 0, 1)

        #grid_scale.addWidget(self.csv_profile, 1, 0)
        #grid_scale.addWidget(self.csv_profile_edit, 1, 1)
        #grid_scale.addWidget(self.csv_profile_browse_button, 1, 2)

        self.csv_profile = QLabel("CSV profile:")
        self.csv_profile_edit = QLineEdit()
        self.csv_profile_button = QPushButton('Browse')

        #box_time = QVBoxLayout()
        # TODO: It's super annoying that the animate time box doesn't
        #       line up with the previous box
        self.box_scale.setLayout(grid_scale)

        self.box_time.setLayout(grid_time)
        #----------

        grid2 = QGridLayout()
        irow = 0
        #grid2.addWidget(self.animate_scale_radio, 8, 0)
        #grid2.addWidget(self.animate_phase_radio, 8, 1)
        #grid2.addWidget(self.animate_time_radio, 8, 2)
        #grid2.addWidget(self.animate_freq_sweeep_radio, 8, 3)

        grid2.addWidget(self.animate_in_gui_checkbox, irow, 0)
        irow += 1

        grid2.addWidget(self.resolution_label, irow, 0)
        grid2.addWidget(self.resolution_edit, irow, 1)
        grid2.addWidget(self.resolution_button, irow, 2)
        irow += 1

        grid2.addWidget(self.browse_folder_label, irow, 0)
        grid2.addWidget(self.browse_folder_edit, irow, 1)
        grid2.addWidget(self.browse_folder_button, irow, 2)
        irow += 1

        grid2.addWidget(self.gif_label, irow, 0)
        grid2.addWidget(self.gif_edit, irow, 1)
        grid2.addWidget(self.gif_button, irow, 2)
        irow += 1

        grid2.addWidget(self.make_images_checkbox, irow, 0)
        #grid2.addWidget(self.overwrite_images_checkbox, irow, 0)
        grid2.addWidget(self.delete_images_checkbox, irow, 1)
        grid2.addWidget(self.make_gif_checkbox, irow, 2)
        irow += 1
        grid2.addWidget(self.repeat_checkbox, irow, 0)
        irow += 1
        grid2.addWidget(spacer, irow, 0)

        grid_hbox = QHBoxLayout()
        grid_hbox.addWidget(spacer)
        grid_hbox.addLayout(grid2)
        grid_hbox.addWidget(spacer)

        # bottom buttons
        step_run_box = QHBoxLayout()
        step_run_box.addWidget(self.step_button)
        step_run_box.addWidget(self.wipe_button)
        step_run_box.addWidget(self.stop_button)
        step_run_box.addWidget(self.run_button)

        ok_cancel_box = QHBoxLayout()
        ok_cancel_box.addWidget(self.cancel_button)

        vbox = QVBoxLayout()
        vbox.addLayout(grid)
        vbox.addWidget(self.box_scale)
        vbox.addWidget(self.box_time)
        #vbox.addLayout(checkboxes)
        vbox.addLayout(grid_hbox)
        vbox.addStretch()
        vbox.addLayout(step_run_box)
        vbox.addLayout(ok_cancel_box)
        self.setLayout(vbox)

    def on_step(self):
        """click the Step button"""
        passed, validate_out = self.on_validate()
        if passed:
            try:
                self._make_gif(validate_out, istep=self.istep)
                self.istep += 1
            except IndexError:
                self._make_gif(validate_out, istep=0)
                self.istep += 1
            self.wipe_button.setEnabled(True)

    def on_wipe(self):
        """click the Wipe button"""
        passed, validate_out = self.on_validate(wipe=True)
        if passed:
            self.istep = 0
            self._make_gif(validate_out, istep=self.istep)
            self.wipe_button.setEnabled(False)
            self.stop_button.setEnabled(False)

    def on_stop(self):
        """click the Stop button"""
        #passed, validate_out = self.on_validate()
        #if passed:
        #self._make_gif(validate_out, stop_animation=True)
        if self.is_gui:
            self.win_parent.win_parent.stop_animation()

        self.wipe_button.setEnabled(True)
        self.stop_button.setEnabled(False)

    def on_run(self):
        """click the Run button"""
        self.istep = 0
        self.wipe_button.setEnabled(False)
        self.stop_button.setEnabled(True)

        passed, validate_out = self.on_validate()
        if passed:
            self._make_gif(validate_out, istep=None)
        return passed

    def _make_gif(self, validate_out, istep=None, stop_animation=False):
        """interface for making the gif"""
        (icase_fringe, icase_disp, icase_vector, scale, time, fps,
         animate_in_gui, magnify, output_dir, gifbase, min_value,
         max_value) = validate_out
        fps = int(fps)

        gif_filename = None
        if not stop_animation and not animate_in_gui and gifbase is not None:
            if gifbase.lower().endswith('.gif'):
                gifbase = gifbase[:-4]
            gif_filename = os.path.join(output_dir, gifbase + '.gif')

        animate_fringe = self.checkbox_fringe.isChecked()
        animate_vector = self.checkbox_vector.isChecked()

        animate_scale = self.animate_scale_radio.isChecked()
        animate_phase = self.animate_phase_radio.isChecked()
        animate_time = self.animate_time_radio.isChecked()

        if not self.checkbox_vector.isEnabled():
            icase_vector = None

        animate_scale = False
        animate_phase = False
        animate_time = False
        if self._animate_type == 'scale':
            animate_scale = True
        elif self._animate_type == 'phase':
            animate_phase = True
        elif self._animate_type == 'time':
            animate_time = True
        else:
            raise NotImplementedError(self._animate_type)

        make_images = self.make_images_checkbox.isChecked()
        delete_images = self.delete_images_checkbox.isChecked()
        make_gif = self.make_gif_checkbox.isChecked()
        animation_profile = str(self.animation_profile_edit.currentText())

        icase_disp_start = self.icase_disp_start_edit.value()
        icase_disp_end = self.icase_disp_end_edit.value()
        icase_disp_delta = self.icase_disp_delta_edit.value()

        bool_repeat = self.repeat_checkbox.isChecked(
        )  # TODO: change this to an integer
        if bool_repeat:
            nrepeat = 0
        else:
            nrepeat = 1
        #self.out_data['is_shown'] = self.show_radio.isChecked()
        #icase = self._icase

        if self.is_gui:
            self.win_parent.win_parent.make_gif(
                gif_filename,
                scale,
                istep=istep,
                animate_scale=animate_scale,
                animate_phase=animate_phase,
                animate_time=animate_time,
                icase_fringe=icase_fringe,
                icase_disp=icase_disp,
                icase_vector=icase_vector,
                animate_fringe=animate_fringe,
                animate_vector=animate_vector,
                icase_start=icase_disp_start,
                icase_end=icase_disp_end,
                icase_delta=icase_disp_delta,
                time=time,
                animation_profile=animation_profile,
                nrepeat=nrepeat,
                fps=fps,
                magnify=magnify,
                make_images=make_images,
                delete_images=delete_images,
                make_gif=make_gif,
                stop_animation=stop_animation,
                animate_in_gui=animate_in_gui,
                min_value=min_value,
                max_value=max_value,
            )

        self.out_data['clicked_ok'] = True
        self.out_data['close'] = True

    def get_min_max(self, icase):
        if self.is_gui:
            (obj, (i, name)) = self.win_parent.win_parent.result_cases[icase]
            min_value, max_value = obj.get_min_max(i, name)
        else:
            return 0., 1.0
        return min_value, max_value

    def on_validate(self, wipe=False):
        """checks to see if the input is valid"""
        # requires no special validation
        icase_fringe, flag0 = check_int(self.icase_fringe_edit)
        icase_disp, unused_flaga = check_int(self.icase_disp_edit)
        icase_vector, unused_flagb = check_int(self.icase_vector_edit)
        #icase_disp = self._icase_disp
        #icase_vector = self._icase_vector

        scale, flag1 = check_float(self.scale_edit)
        time, flag2 = check_float(self.time_edit)
        fps, flag3 = check_float(self.fps_edit)

        min_value = max_value = None
        flag4 = flag5 = True
        if self.min_value_edit.isEnabled():
            min_value, flag4 = check_float(self.min_value_edit)
        if self.max_value_edit.isEnabled():
            max_value, flag5 = check_float(self.max_value_edit)

        if wipe:
            animate_in_gui = False
            scale = 0.
            flag1 = True
        else:
            animate_in_gui = self.animate_in_gui_checkbox.isChecked()

        if animate_in_gui or wipe:
            passed = all([flag0, flag1, flag2, flag3, flag4, flag5])
            magnify, output_dir, gifbase = None, None, None
        else:
            magnify, flag6 = check_int(self.resolution_edit)
            output_dir, flag7 = self.check_path(self.browse_folder_edit)
            gifbase, flag8 = self.check_name(self.gif_edit)
            passed = all([
                flag0, flag1, flag2, flag3, flag4, flag5, flag6, flag7, flag8
            ])
        return passed, (icase_fringe, icase_disp, icase_vector, scale, time,
                        fps, animate_in_gui, magnify, output_dir, gifbase,
                        min_value, max_value)

    @staticmethod
    def check_name(cell):
        """verifies that the data is string-able"""
        cell_value = cell.text()
        try:
            text = str(cell_value).strip()
        except UnicodeEncodeError:
            cell.setStyleSheet("QLineEdit{background: red;}")
            return None, False

        if len(text):
            cell.setStyleSheet("QLineEdit{background: white;}")
            return text, True
        else:
            cell.setStyleSheet("QLineEdit{background: red;}")
            return None, False

    def check_path(self, cell):
        """verifies that the path exists"""
        text, passed = self.check_name(cell)
        if not passed:
            return None, False

        if os.path.exists(text):
            cell.setStyleSheet("QLineEdit{background: white;}")
            return text, True
        else:
            cell.setStyleSheet("QLineEdit{background: red;}")
            return None, False

    #def on_ok(self):
    #"""click the OK button"""
    #passed = self.on_apply()
    #if passed:
    #self.win_parent._animation_window_shown = False
    #self.close()
    ##self.destroy()

    def on_cancel(self):
        """click the Cancel button"""
        self.on_stop()
        self.out_data['close'] = True
        self.close()
Ejemplo n.º 3
0
class SegmentationWidget(QWidget):
    def __init__(
        self,
        viewer,
        boundaries_string=BOUNDARIES_STRING,
    ):
        super(SegmentationWidget, self).__init__()

        # general variables
        self.viewer = viewer

        # Disable / overwrite napari viewer functions
        # that either do not make sense or should be avoided by the user
        disable_napari_btns(self.viewer)
        disable_napari_key_bindings()
        # overwrite_napari_roll(self.viewer)

        # Main layers
        self.base_layer = []  # Contains registered brain / reference brain
        self.atlas_layer = []  # Contains annotations / region information

        # Track variables
        self.track_layers = []

        # Region variables
        self.label_layers = []

        # Atlas variables
        self.current_atlas_name = ""
        self.atlas = None

        self.boundaries_string = boundaries_string
        self.directory = ""
        # Set up segmentation methods
        self.region_seg = RegionSeg(self)
        self.track_seg = TrackSeg(self)

        # Generate main layout
        self.setup_main_layout()

        if DISPLAY_REGION_INFO:

            @self.viewer.mouse_move_callbacks.append
            def display_region_info(v, event):
                """
                Show brain region info on mouse over in status bar on the right
                """
                assert self.viewer == v
                if len(v.layers) and self.atlas_layer and self.atlas:
                    _, _, _, region_info = structure_from_viewer(
                        self.viewer.status, self.atlas_layer, self.atlas)
                    self.viewer.help = region_info

    def setup_main_layout(self):
        """
        Construct main layout of widget
        """
        self.layout = QGridLayout()
        self.layout.setContentsMargins(10, 10, 10, 10)
        self.layout.setAlignment(QtCore.Qt.AlignTop)
        self.layout.setSpacing(4)

        # 3 Steps:
        # - Loading panel
        # - Segmentation methods panel
        # -> Individual segmentation methods (which are invisible at first)
        # - Saving panel

        self.add_loading_panel(1)
        self.add_segmentation_methods_panel(1)
        self.track_seg.add_track_panel(2)  # Track segmentation subpanel
        self.region_seg.add_region_panel(3)  # Region segmentation subpanel
        self.add_saving_panel(4)

        # Take care of status label
        self.status_label = QLabel()
        self.status_label.setText("Ready")
        self.layout.addWidget(self.status_label, 5, 0)

        self.setLayout(self.layout)

    # PANELS ###############################################################

    def add_segmentation_methods_panel(self, row, column=1):
        """
        Segmentation methods chooser panel:
            Toggle visibility of segmentation
            methods
        """
        self.toggle_methods_panel = QGroupBox("Segmentation")
        self.toggle_methods_layout = QGridLayout()
        self.toggle_methods_layout.setContentsMargins(10, 10, 10, 10)
        self.toggle_methods_layout.setSpacing(5)
        self.toggle_methods_layout.setAlignment(QtCore.Qt.AlignBottom)

        self.show_trackseg_button = add_button(
            "Track tracing",
            self.toggle_methods_layout,
            self.track_seg.toggle_track_panel,
            0,
            1,
            minimum_width=COLUMN_WIDTH,
            alignment=SEGM_METHODS_PANEL_ALIGN,
        )
        self.show_trackseg_button.setEnabled(False)

        self.show_regionseg_button = add_button(
            "Region segmentation",
            self.toggle_methods_layout,
            self.region_seg.toggle_region_panel,
            1,
            1,
            minimum_width=COLUMN_WIDTH,
            alignment=SEGM_METHODS_PANEL_ALIGN,
        )
        self.show_regionseg_button.setEnabled(False)

        self.toggle_methods_layout.setColumnMinimumWidth(1, COLUMN_WIDTH)
        self.toggle_methods_panel.setLayout(self.toggle_methods_layout)
        self.toggle_methods_panel.setVisible(True)

        self.layout.addWidget(self.toggle_methods_panel, row, column, 1, 1)

    def add_loading_panel(self, row, column=0):
        """
        Loading panel:
            - Load project (sample space)
            - Load project (atlas space)
            - Atlas chooser
        """
        self.load_data_panel = QGroupBox("Load data")
        self.load_data_layout = QGridLayout()
        self.load_data_layout.setSpacing(15)
        self.load_data_layout.setContentsMargins(10, 10, 10, 10)
        self.load_data_layout.setAlignment(QtCore.Qt.AlignBottom)

        self.load_button = add_button(
            "Load project (sample space)",
            self.load_data_layout,
            self.load_brainreg_directory_sample,
            0,
            0,
            minimum_width=COLUMN_WIDTH,
            alignment=LOADING_PANEL_ALIGN,
        )

        self.load_button_standard = add_button(
            "Load project (atlas space)",
            self.load_data_layout,
            self.load_brainreg_directory_standard,
            1,
            0,
            minimum_width=COLUMN_WIDTH,
            alignment=LOADING_PANEL_ALIGN,
        )

        self.add_atlas_menu(self.load_data_layout)

        self.load_data_layout.setColumnMinimumWidth(0, COLUMN_WIDTH)
        self.load_data_panel.setLayout(self.load_data_layout)
        self.load_data_panel.setVisible(True)

        self.layout.addWidget(self.load_data_panel, row, column, 1, 1)

    def add_saving_panel(self, row):
        """
        Saving/Export panel
        """
        self.save_data_panel = QGroupBox()
        self.save_data_layout = QGridLayout()

        self.export_button = add_button(
            "To brainrender",
            self.save_data_layout,
            self.export_to_brainrender,
            0,
            0,
            visibility=False,
        )
        self.save_button = add_button("Save",
                                      self.save_data_layout,
                                      self.save,
                                      0,
                                      1,
                                      visibility=False)

        self.save_data_layout.setColumnMinimumWidth(1, COLUMN_WIDTH)
        self.save_data_panel.setLayout(self.save_data_layout)
        self.layout.addWidget(self.save_data_panel, row, 0, 1, 2)

        self.save_data_panel.setVisible(False)

    # ATLAS INTERACTION ####################################################

    def add_atlas_menu(self, layout):
        list_of_atlasses = ["Load atlas"]
        available_atlases = get_available_atlases()
        for atlas in available_atlases.keys():
            atlas_desc = f"{atlas} v{available_atlases[atlas]}"
            list_of_atlasses.append(atlas_desc)
            atlas_menu, _ = add_combobox(
                layout,
                None,
                list_of_atlasses,
                2,
                0,
                label_stack=True,
                callback=self.initialise_atlas,
                width=COLUMN_WIDTH,
            )

        self.atlas_menu = atlas_menu

    def initialise_atlas(self):
        atlas_string = self.atlas_menu.currentText()
        atlas_name = atlas_string.split(" ")[0].strip()
        if atlas_name != self.current_atlas_name:
            status = self.remove_layers()
            if not status:  # Something prevented deletion
                self.reset_atlas_menu()
                return
        else:
            print(f"{atlas_string} already selected for segmentation.")
            self.reset_atlas_menu()
            return

        # Get / set output directory
        self.set_output_directory()
        if not self.directory:
            self.reset_atlas_menu()
            return

        self.current_atlas_name = atlas_name
        # Instantiate atlas layers
        self.load_atlas()

        self.directory = self.directory / atlas_name
        self.paths = Paths(self.directory, atlas_space=True)

        self.status_label.setText("Ready")
        # Set window title
        self.viewer.title = f"Atlas: {self.current_atlas_name}"
        self.initialise_segmentation_interface()
        # Check / load previous regions and tracks
        self.region_seg.check_saved_region()
        self.track_seg.check_saved_track()
        self.reset_atlas_menu()

    def set_output_directory(self):
        self.status_label.setText("Loading...")
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        self.directory = QFileDialog.getExistingDirectory(
            self,
            "Select output directory",
            options=options,
        )
        if self.directory != "":
            self.directory = Path(self.directory)

    def load_atlas(self):
        atlas = BrainGlobeAtlas(self.current_atlas_name)
        self.atlas = atlas
        self.base_layer = self.viewer.add_image(
            self.atlas.reference,
            name="Reference",
        )
        self.atlas_layer = self.viewer.add_labels(
            self.atlas.annotation,
            name=self.atlas.atlas_name,
            blending="additive",
            opacity=0.3,
            visible=False,
        )
        self.standard_space = True

    def reset_atlas_menu(self):
        # Reset menu for atlas - show initial description
        self.atlas_menu.blockSignals(True)
        self.atlas_menu.setCurrentIndex(0)
        self.atlas_menu.blockSignals(False)

    # BRAINREG INTERACTION #################################################

    def load_brainreg_directory_sample(self):
        self.get_brainreg_directory(standard_space=False)

    def load_brainreg_directory_standard(self):
        self.get_brainreg_directory(standard_space=True)

    def get_brainreg_directory(self, standard_space):
        """
        Shows file dialog to choose output directory
        and sets global directory info
        """
        if standard_space:
            self.plugin = "brainreg_standard"
            self.standard_space = True
        else:
            self.plugin = "brainreg"
            self.standard_space = False

        self.status_label.setText("Loading...")
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        brainreg_directory = QFileDialog.getExistingDirectory(
            self,
            "Select brainreg directory",
            options=options,
        )

        if not brainreg_directory:
            return

        if self.directory != brainreg_directory:
            status = self.remove_layers()
            if not status:
                return  # Something prevented deletion
            self.directory = Path(brainreg_directory)
        else:
            print(f"{str(brainreg_directory)} already loaded.")
            return

        # Otherwise, proceed loading brainreg dir
        self.load_brainreg_directory()

    def load_brainreg_directory(self):
        """
        Opens brainreg folder in napari.
        Calls initialise_loaded_data to set up layers / info.
        Then checks for previously loaded data.

        """
        try:
            self.viewer.open(str(self.directory), plugin=self.plugin)
            self.paths = Paths(
                self.directory,
                standard_space=self.standard_space,
            )
            self.initialise_loaded_data()
        except ValueError:
            print(f"The directory ({self.directory}) does not appear to be "
                  f"a brainreg directory, please try again.")
            return

        # Check / load previous regions and tracks
        self.region_seg.check_saved_region()
        self.track_seg.check_saved_track()

    def initialise_loaded_data(self):
        """
        Set up brainreg layers in napari / fill with new data and info

        """
        try:
            self.viewer.layers.remove(self.boundaries_string)
        except KeyError:
            pass

        self.base_layer = self.viewer.layers["Registered image"]
        self.metadata = self.base_layer.metadata
        self.atlas = self.metadata["atlas_class"]
        self.atlas_layer = self.viewer.layers[self.metadata["atlas"]]
        self.initialise_segmentation_interface()

        # Set window title
        self.viewer.title = (
            f"Brainreg: {self.metadata['atlas']} ({self.plugin})")
        self.status_label.setText("Ready")

    # MORE LAYOUT COMPONENTS ###########################################

    def initialise_segmentation_interface(self):
        self.reset_variables()
        self.initialise_image_view()
        self.save_data_panel.setVisible(True)
        self.save_button.setVisible(True)
        self.export_button.setVisible(self.standard_space)
        self.show_regionseg_button.setEnabled(True)
        self.show_trackseg_button.setEnabled(True)
        self.status_label.setText("Ready")

    def initialise_image_view(self):
        self.set_z_position()

    def set_z_position(self):
        midpoint = int(round(len(self.base_layer.data) / 2))
        self.viewer.dims.set_point(0, midpoint)

    def reset_variables(self):
        """
        Reset atlas scale dependent variables
        - point_size (Track segmentation)
        - spline_size (Track segmentation)
        - brush_size (Region segmentation)
        """
        self.mean_voxel_size = int(
            np.sum(self.atlas.resolution) / len(self.atlas.resolution))
        self.track_seg.point_size = (self.track_seg.point_size_default /
                                     self.mean_voxel_size)
        self.track_seg.spline_size = (self.track_seg.spline_size_default /
                                      self.mean_voxel_size)
        self.region_seg.brush_size = (self.region_seg.brush_size_default /
                                      self.mean_voxel_size)
        return

    def display_delete_warning(self):
        """
        Display a warning in a pop up that informs
        about deletion of all annotation layers
        """
        message_reply = QMessageBox.question(
            self,
            "About to remove layers",
            "All layers are about to be deleted. Proceed?",
            QMessageBox.Yes | QMessageBox.Cancel,
        )
        if message_reply == QMessageBox.Yes:
            return True
        else:
            return False

    def remove_layers(self):
        """
        TODO: This needs work. Runs into an error currently
        when switching from a annotated project to another one
        """
        if len(self.viewer.layers) != 0:
            # Check with user if that is really what is wanted
            if self.track_layers or self.label_layers:
                choice = self.display_delete_warning()
                if not choice:
                    print('Preventing deletion because user chose "Cancel"')
                    return False

            # Remove old layers
            for layer in list(self.viewer.layers):
                try:
                    self.viewer.layers.remove(layer)
                except IndexError:  # no idea why this happens
                    pass

        # There seems to be a napari bug trying to access previously used slider
        # values. Trying to circument for now
        self.viewer.window.qt_viewer.dims._last_used = None

        self.track_layers = []
        self.label_layers = []
        return True

    def save(self):
        if self.label_layers or self.track_layers:
            print("Saving")
            worker = save_all(
                self.paths.regions_directory,
                self.paths.tracks_directory,
                self.label_layers,
                self.track_layers,
                track_file_extension=TRACK_FILE_EXT,
            )
            worker.start()

    def export_to_brainrender(self):
        print("Exporting")
        max_axis_2 = self.base_layer.shape[2]
        worker = export_all(
            self.paths.regions_directory,
            self.paths.tracks_directory,
            self.label_layers,
            self.track_seg.splines,
            self.track_seg.spline_names,
            self.atlas.resolution[0],
            max_axis_2,
        )
        worker.start()
Ejemplo n.º 4
0
class TrackSeg(QGroupBox):
    """
    Track segmentation method panel

    """
    def __init__(
        self,
        parent,
        point_size=POINT_SIZE,
        spline_size=SPLINE_SIZE,
        track_file_extension=TRACK_FILE_EXT,
        spline_points_default=SPLINE_POINTS_DEFAULT,
        spline_smoothing_default=SPLINE_SMOOTHING_DEFAULT,
        fit_degree_default=FIT_DEGREE_DEFAULT,
        summarise_track_default=SUMMARISE_TRACK_DEFAULT,
    ):

        super(TrackSeg, self).__init__()
        self.parent = parent
        self.tree = None

        self.summarise_track_default = summarise_track_default

        # Point / Spline fitting settings
        self.point_size_default = POINT_SIZE  # Keep track of default
        self.point_size = point_size  # Initialise
        self.spline_points_default = spline_points_default
        self.spline_size_default = SPLINE_SIZE  # Keep track of default
        self.spline_size = spline_size  # Initialise
        self.spline_smoothing_default = spline_smoothing_default
        self.fit_degree_default = fit_degree_default

        # File formats
        self.track_file_extension = track_file_extension

        # Initialise spline and spline names
        self.splines = None
        self.spline_names = None

    def add_track_panel(self, row):
        self.track_panel = QGroupBox("Track tracing")
        track_layout = QGridLayout()

        add_button(
            "Add surface points",
            track_layout,
            self.add_surface_points,
            5,
            1,
        )

        add_button(
            "Add track",
            track_layout,
            self.add_track,
            6,
            0,
        )

        add_button(
            "Trace tracks",
            track_layout,
            self.run_track_analysis,
            6,
            1,
        )

        self.summarise_track_checkbox = add_checkbox(
            track_layout,
            self.summarise_track_default,
            "Summarise",
            0,
        )

        self.fit_degree = add_int_box(
            track_layout,
            self.fit_degree_default,
            1,
            5,
            "Fit degree",
            1,
        )

        self.spline_smoothing = add_float_box(
            track_layout,
            self.spline_smoothing_default,
            0,
            1,
            "Spline smoothing",
            0.1,
            2,
        )

        self.spline_points = add_int_box(
            track_layout,
            self.spline_points_default,
            1,
            10000,
            "Spline points",
            3,
        )

        track_layout.setColumnMinimumWidth(1, COLUMN_WIDTH)
        self.track_panel.setLayout(track_layout)
        self.parent.layout.addWidget(self.track_panel, row, 0, 1, 2)
        self.track_panel.setVisible(False)

    def toggle_track_panel(self):
        # TODO: Change color scheme directly when theme is switched
        # TODO: "text-align" property should follow constant SEGM_METHODS_PANEL_ALIGN
        if self.track_panel.isVisible():
            self.track_panel.setVisible(False)
            if self.parent.viewer.theme == "dark":
                self.parent.show_trackseg_button.setStyleSheet(
                    f"QPushButton {{ background-color: #414851; text-align:{SEGM_METHODS_PANEL_ALIGN};}}"
                    f"QPushButton:pressed {{ background-color: #414851; text-align:{SEGM_METHODS_PANEL_ALIGN};}}"
                )
            else:
                self.parent.show_trackseg_button.setStyleSheet(
                    f"QPushButton {{ background-color: #d6d0ce; text-align:{SEGM_METHODS_PANEL_ALIGN};}}"
                    f"QPushButton:pressed {{ background-color: #d6d0ce; text-align:{SEGM_METHODS_PANEL_ALIGN};}}"
                )

        else:
            self.track_panel.setVisible(True)
            if self.parent.viewer.theme == "dark":
                self.parent.show_trackseg_button.setStyleSheet(
                    f"QPushButton {{ background-color: #7e868f; text-align:{SEGM_METHODS_PANEL_ALIGN};}}"
                    f"QPushButton:pressed {{ background-color: #7e868f; text-align:{SEGM_METHODS_PANEL_ALIGN};}}"
                )
            else:
                self.parent.show_trackseg_button.setStyleSheet(
                    f"QPushButton {{ background-color: #fdf194; text-align:{SEGM_METHODS_PANEL_ALIGN};}}"
                    f"QPushButton:pressed {{ background-color: #fdf194; text-align:{SEGM_METHODS_PANEL_ALIGN};}}"
                )

    def check_saved_track(self):
        track_files = glob(
            str(self.parent.paths.tracks_directory) + "/*" +
            self.track_file_extension)
        if self.parent.paths.tracks_directory.exists() and track_files != []:
            for track_file in track_files:
                self.parent.track_layers.append(
                    add_existing_track_layers(
                        self.parent.viewer,
                        track_file,
                        self.point_size,
                    ))

    def add_track(self):
        print("Adding a new track\n")
        self.splines = None
        self.spline_names = None
        self.track_panel.setVisible(True)  # Should be visible by default!
        add_new_track_layer(
            self.parent.viewer,
            self.parent.track_layers,
            self.point_size,
        )

    def add_surface_points(self):
        if self.parent.track_layers:
            print("Adding surface points (this may take a while)")
            if self.tree is None:
                self.create_brain_surface_tree()

            for track_layer in self.parent.track_layers:
                try:
                    _, index = self.tree.query(track_layer.data[0])
                except IndexError:
                    print(
                        f"{track_layer.name} does not appear to hold any data")
                    continue
                surface_point = self.tree.data[index]
                track_layer.data = np.vstack((surface_point, track_layer.data))
            print("Finished!\n")
        else:
            print("No tracks found.")

    def create_brain_surface_tree(self):
        self.tree = create_KDTree_from_image(self.parent.atlas_layer.data)

    def run_track_analysis(self, override=False):
        if self.parent.track_layers:
            if not override:
                choice = display_warning(
                    self.parent,
                    "About to analyse tracks",
                    "Existing files will be will be deleted. Proceed?",
                )
            else:
                choice = True  # for debugging

            if choice:
                print("Running track analysis")
                self.splines, self.spline_names = track_analysis(
                    self.parent.viewer,
                    self.parent.atlas,
                    self.parent.paths.tracks_directory,
                    self.parent.track_layers,
                    self.spline_size,
                    spline_points=self.spline_points.value(),
                    fit_degree=self.fit_degree.value(),
                    spline_smoothing=self.spline_smoothing.value(),
                    summarise_track=self.summarise_track_checkbox.isChecked(),
                )
                print("Finished!\n")
            else:
                print("Preventing analysis as user chose 'Cancel'")
        else:
            print("No tracks found.")
Ejemplo n.º 5
0
class MainLauncher(SiriusMainWindow):
    """Main Launcher."""

    showMonitor = Signal()
    showStatus = Signal()
    showEgun = Signal()

    def __init__(self, parent=None, prefix=VACA_PREFIX):
        """Init."""
        super().__init__(parent)
        self._prefix = prefix

        # window settings
        self.setObjectName('ASApp')
        self.setWindowTitle('Sirius Launcher')
        self.setWindowIcon(
            qta.icon('mdi.rocket', color=get_appropriate_color('AS')))

        screens = QApplication.screens()
        screen_idx = 3 if len(screens) == 8 else 0
        topleft = screens[screen_idx].geometry().topLeft()
        self.move(topleft.x(), topleft.y() + 20)
        self.setWindowFlags(Qt.WindowStaysOnTopHint)

        # menubar
        menubar = get_object(ismenubar=True)
        menubar.setNativeMenuBar(False)
        self.setMenuBar(menubar)
        self._setupUi()

        # connect window signals
        connect_newprocess(self,
                           'sirius-hla-as-ap-monitor.py',
                           parent=self,
                           signal=self.showMonitor)
        connect_newprocess(self,
                           'sirius-hla-si-ap-genstatus.py',
                           parent=self,
                           signal=self.showStatus)
        connect_newprocess(self,
                           'sirius-hla-li-eg-control.py',
                           parent=self,
                           signal=self.showEgun)

        # set focus policy
        self.setFocus(True)
        self.setFocusPolicy(Qt.StrongFocus)

    def _setupUi(self):
        # Machine Shift
        self.wid_shift = QGroupBox('Machine Shift')
        machshift_pvname = SiriusPVName(
            'AS-Glob:AP-MachShift:Mode-Sel').substitute(prefix=self._prefix)
        cbox_shift_mode = SiriusEnumComboBox(self, machshift_pvname)
        label_shift_mode = MachShiftLabel(self, self._prefix)
        label_shift_mode.label.setStyleSheet(
            'QLabel{max-height: 2em; min-width: 7em;}')
        lay_shift = QGridLayout(self.wid_shift)
        lay_shift.setVerticalSpacing(5)
        lay_shift.setAlignment(Qt.AlignCenter)
        lay_shift.addWidget(cbox_shift_mode, 1, 0)
        lay_shift.addWidget(label_shift_mode, 2, 0)

        # Injection System
        self.wid_injsys = QGroupBox('Inj.System')
        wid_injsys = InjSysStbyControlWidget(self,
                                             self._prefix,
                                             is_summary=True)
        lay_injsys = QGridLayout(self.wid_injsys)
        lay_injsys.setAlignment(Qt.AlignCenter)
        lay_injsys.setContentsMargins(0, 0, 0, 0)
        lay_injsys.addWidget(wid_injsys)

        # Egun triggers
        self.wid_egun = QGroupBox('Egun Trig.')
        self.wid_egun.setStyleSheet('min-width: 5em;')
        egun_dev = SiriusPVName('LI-01:EG-TriggerPS').substitute(
            prefix=self._prefix)
        but_egun_trigger = PyDMStateButton(
            self, egun_dev.substitute(propty_name='enable'))
        led_egun_trigger = SiriusLedState(
            self, egun_dev.substitute(propty_name='enablereal'))
        lay_egun = QGridLayout(self.wid_egun)
        lay_egun.setVerticalSpacing(5)
        lay_egun.addWidget(but_egun_trigger, 1, 0)
        lay_egun.addWidget(led_egun_trigger, 2, 0)

        # injection control
        injctrl_dev = SiriusPVName('AS-Glob:AP-InjCtrl')
        injctrl_dev = injctrl_dev.substitute(prefix=self._prefix)

        self.wid_inject = QGroupBox('Injection')
        self.ch_injmode = SiriusConnectionSignal(
            injctrl_dev.substitute(propty='Mode-Sts'))
        self.ch_injmode.new_value_signal[int].connect(
            self._handle_injmode_settings_vis)

        # # Settings
        label_sett = QLabel('<h4>Sett.</h4>', self, alignment=Qt.AlignCenter)
        led_sett = InjDiagLed(self)
        self.wid_injsett = QWidget()
        lay_injsett = QGridLayout(self.wid_injsett)
        lay_injsett.setContentsMargins(0, 0, 0, 0)
        lay_injsett.setAlignment(Qt.AlignTop)
        lay_injsett.addWidget(label_sett, 0, 0)
        lay_injsett.addWidget(led_sett, 1, 0)

        # # Auto Stop
        self.label_injauto = QLabel('<h4>AutoStop</h4>',
                                    self,
                                    alignment=Qt.AlignCenter)
        self.but_injauto = PyDMStateButton(
            self, injctrl_dev.substitute(propty='AutoStop-Sel'))
        self.led_injauto = SiriusLedState(
            self, injctrl_dev.substitute(propty='AutoStop-Sts'))
        self.wid_injauto = QWidget()
        self.wid_injauto.setObjectName('wid')
        self.wid_injauto.setStyleSheet("#wid{min-width: 8em; max-width: 8em;}")
        lay_injauto = QGridLayout(self.wid_injauto)
        lay_injauto.setContentsMargins(0, 0, 0, 0)
        lay_injauto.setAlignment(Qt.AlignTop)
        lay_injauto.addWidget(self.label_injauto, 0, 0)
        lay_injauto.addWidget(self.but_injauto, 1, 0)
        lay_injauto.addWidget(self.led_injauto, 2, 0)

        # # Top-up status
        label_tusts = QLabel('<h4>Status</h4>', self, alignment=Qt.AlignCenter)
        label_tunow = ClockLabel(self)
        label_tunow.setStyleSheet('max-height:2em;')
        label_tunxt = SiriusLabel(
            self, injctrl_dev.substitute(propty='TopUpNextInj-Mon'))
        label_tunxt.displayFormat = SiriusLabel.DisplayFormat.Time
        label_tunxt.setAlignment(Qt.AlignCenter)
        label_tunxt.setStyleSheet('max-height:2em;')
        but_round = PyDMPushButton(
            self, '', qta.icon('mdi.tilde'), 1, False,
            injctrl_dev.substitute(propty='TopUpNextInjRound-Cmd'))
        but_round.setObjectName('but')
        but_round.setStyleSheet(
            '#but{min-width:18px; max-width:18px; icon-size:16px;}')
        self.wid_tusts = QWidget()
        self.wid_tusts.setObjectName('wid')
        self.wid_tusts.setStyleSheet("#wid{min-width: 8em; max-width: 8em;}")
        lay_tusts = QGridLayout(self.wid_tusts)
        lay_tusts.setContentsMargins(0, 0, 0, 0)
        lay_tusts.setAlignment(Qt.AlignTop)
        lay_tusts.addWidget(label_tusts, 0, 0, 1, 2)
        lay_tusts.addWidget(QLabel('Now:', self), 1, 0)
        lay_tusts.addWidget(label_tunow, 1, 1)
        lay_tusts.addWidget(QLabel('Next:', self), 2, 0)
        lay_tusts.addWidget(label_tunxt, 2, 1)
        lay_tusts.addWidget(but_round, 2, 2)
        self.wid_tusts.setVisible(False)

        # # Control
        label_inj = QLabel('<h4>Control</h4>', self, alignment=Qt.AlignCenter)
        self.but_tiinj = EVGInjectionButton(self, self._prefix)
        self.but_topup = PyDMStateButton(
            self, injctrl_dev.substitute(propty='TopUpState-Sel'))
        self.but_topup.setVisible(False)
        lay_inject_sel = QGridLayout()
        lay_inject_sel.setAlignment(Qt.AlignCenter)
        lay_inject_sel.addWidget(self.but_tiinj, 0, 0)
        lay_inject_sel.addWidget(self.but_topup, 0, 0)
        led_injsts = EVGInjectionLed(self, self._prefix)
        label_injcnt = PyDMLabel(self)
        label_injcnt.setToolTip(
            'Count injection pulses when Egun Trigger is enabled.')
        label_injcnt.channel = SiriusPVName(
            'AS-Glob:AP-CurrInfo:InjCount-Mon').substitute(prefix=self._prefix)
        label_injcnt.setStyleSheet('QLabel{max-width: 3.5em;}')
        lay_inject_sts = QHBoxLayout()
        lay_inject_sts.setContentsMargins(0, 0, 0, 0)
        lay_inject_sts.setAlignment(Qt.AlignTop)
        lay_inject_sts.addWidget(led_injsts)
        lay_inject_sts.addWidget(label_injcnt)
        self.wid_injctrl = QWidget()
        lay_injctrl = QGridLayout(self.wid_injctrl)
        lay_injctrl.setContentsMargins(0, 0, 0, 0)
        lay_injctrl.setAlignment(Qt.AlignTop)
        lay_injctrl.addWidget(label_inj, 0, 2, alignment=Qt.AlignCenter)
        lay_injctrl.addLayout(lay_inject_sel, 1, 2, alignment=Qt.AlignCenter)
        lay_injctrl.addLayout(lay_inject_sts, 2, 2, alignment=Qt.AlignCenter)

        # # Injection Auxiliary section
        self.wid_injlog = QGroupBox('Injection Log')
        label_injlog = PyDMLogLabel(self,
                                    injctrl_dev.substitute(propty='Log-Mon'),
                                    replace=[
                                        'Remaining time',
                                    ])
        lay_injlog = QGridLayout(self.wid_injlog)
        lay_injlog.addWidget(label_injlog, 0, 0)
        self.wid_injlog.setVisible(False)

        self.wid_mon = MonitorSummaryWidget(self, self._prefix)
        self.wid_mon.setVisible(False)

        # # Target Current
        self._ld_currtgt = QLabel('<h4>Target Curr.</h4>',
                                  self,
                                  alignment=Qt.AlignCenter)
        self._sb_currtgt = SiriusSpinbox(
            self, injctrl_dev.substitute(propty='TargetCurrent-SP'))
        self._sb_currtgt.showStepExponent = False
        self._lb_currtgt = PyDMLabel(
            self, injctrl_dev.substitute(propty='TargetCurrent-RB'))
        self._lb_currtgt.showUnits = True
        self._wid_tcurr = QWidget()
        lay_tcurr = QGridLayout(self._wid_tcurr)
        lay_tcurr.setContentsMargins(0, 0, 0, 0)
        lay_tcurr.setAlignment(Qt.AlignTop)
        lay_tcurr.addWidget(self._ld_currtgt, 0, 0)
        lay_tcurr.addWidget(self._sb_currtgt, 1, 0)
        lay_tcurr.addWidget(self._lb_currtgt, 2, 0)

        # # Bucket List
        self._bucket_list = BucketList(self, prefix=self._prefix, min_size=15)

        self.wid_fill = QWidget()
        lay_fill = QGridLayout(self.wid_fill)
        lay_fill.setContentsMargins(0, 0, 0, 0)
        lay_fill.setHorizontalSpacing(9)
        lay_fill.addWidget(self._wid_tcurr, 0, 0)
        lay_fill.addWidget(self._bucket_list, 0, 1)
        self.wid_fill.setVisible(False)

        pbt_aux = QPushButton('v', self)
        pbt_aux.setToolTip('Show Injection Auxiliary section.')
        pbt_aux.clicked.connect(self._toggle_show_injaux)
        pbt_aux.setStyleSheet('QPushButton{max-width: 0.8em;}')
        pbt_bl = QPushButton('>', self)
        pbt_bl.setToolTip('Show bucket list and target current controls.')
        pbt_bl.clicked.connect(self._toggle_show_bucketlist)
        pbt_bl.setStyleSheet('QPushButton{max-width: 0.8em;}')

        lay_inj = QGridLayout(self.wid_inject)
        lay_inj.setAlignment(Qt.AlignTop)
        lay_inj.setVerticalSpacing(5)
        lay_inj.setHorizontalSpacing(12)
        lay_inj.addWidget(self.wid_injsett, 0, 0, 2, 1)
        lay_inj.addWidget(self.wid_injauto, 0, 1, 2, 1)
        lay_inj.addWidget(self.wid_tusts, 0, 1, 2, 1)
        lay_inj.addWidget(self.wid_injctrl, 0, 2, 2, 1)
        lay_inj.addWidget(pbt_bl, 0, 3, alignment=Qt.AlignTop)
        lay_inj.addWidget(pbt_aux, 1, 3, alignment=Qt.AlignBottom)
        lay_inj.addWidget(self.wid_fill, 0, 4, 2, 1)

        # Current
        curr_pvname = SiriusPVName(
            'SI-Glob:AP-CurrInfo:Current-Mon').substitute(prefix=self._prefix)
        self.label_curr = PyDMLabel(self, curr_pvname)
        self.label_curr.showUnits = True
        self.label_curr.setStyleSheet("""
            QLabel{
                font-size: 18pt; qproperty-alignment: AlignCenter;
                min-width: 6em; max-width: 6em;
            }""")
        self.wid_curr = QGroupBox('Current')
        lay_curr = QHBoxLayout(self.wid_curr)
        lay_curr.addWidget(self.label_curr)

        # RF Kill Beam
        self.wid_rfkill = QGroupBox('RF Kill Beam')
        self.wid_rfkill.setObjectName('RFKillBeam')
        rfkill_bt = RFKillBeamButton(self, self._prefix)
        rfkill_lay = QGridLayout(self.wid_rfkill)
        rfkill_lay.addWidget(rfkill_bt)

        # menu buttons
        self.wid_pbt = QPushButton('v', self)
        self.wid_pbt.clicked.connect(self._toggle_show_menubutton)
        self.wid_pbt.setStyleSheet('QPushButton{max-width: 0.8em;}')
        self._menubutton = get_object(ismenubar=False, parent=self)
        self._menubutton.layout().setContentsMargins(0, 0, 0, 0)
        self._menubutton.layout().setSpacing(4)
        self._menubutton.setVisible(False)

        hlay1 = QHBoxLayout()
        hlay1.setContentsMargins(0, 0, 0, 0)
        hlay1.addWidget(self.wid_shift)
        hlay1.addWidget(self.wid_injsys)
        hlay1.addWidget(self.wid_egun)
        hlay1.addWidget(self.wid_inject)
        hlay1.addWidget(self.wid_curr)
        hlay1.addWidget(self.wid_rfkill)

        hlay2 = QHBoxLayout()
        hlay2.setContentsMargins(0, 0, 0, 0)
        hlay2.addWidget(self.wid_injlog)
        hlay2.addWidget(self.wid_mon)

        cwid = QWidget(self)
        lay = QGridLayout(cwid)
        lay.addLayout(hlay1, 0, 0)
        lay.addLayout(hlay2, 1, 0)
        lay.addWidget(self._menubutton, 2, 0, 1, 2)
        lay.addWidget(self.wid_pbt,
                      0,
                      1,
                      2,
                      1,
                      alignment=Qt.AlignRight | Qt.AlignBottom)
        self.setCentralWidget(cwid)

    def _toggle_show_menubutton(self):
        self._menubutton.setVisible(self._menubutton.isHidden())
        text = 'v' if self._menubutton.isHidden() else '^'
        self.sender().setText(text)
        self.centralWidget().adjustSize()
        self.adjustSize()

    def _toggle_show_bucketlist(self):
        show = self.wid_fill.isHidden()
        self.wid_fill.setVisible(show)
        text = '<' if show else '>'
        tooltip = ('Hide' if show else 'Show') + ' bucket list controls.'
        self.sender().setText(text)
        self.sender().setToolTip(tooltip)
        self.sender().parent().adjustSize()
        self.centralWidget().adjustSize()
        self.adjustSize()

    def _toggle_show_injaux(self):
        show = self.wid_mon.isHidden()
        self.wid_mon.setVisible(show)
        self.wid_injlog.setVisible(show)
        text = '^' if show else 'v'
        tooltip = ('Hide'
                   if show else 'Show') + ' Injection Auxiliary section.'
        self.sender().setText(text)
        self.sender().setToolTip(tooltip)
        self.sender().parent().adjustSize()
        self.centralWidget().adjustSize()
        self.adjustSize()

    @Slot(int)
    def _handle_injmode_settings_vis(self, new_mode):
        is_topup = new_mode == _InjConst.InjMode.TopUp
        self.label_injauto.setVisible(not is_topup)
        self.but_injauto.setVisible(not is_topup)
        self.led_injauto.setVisible(not is_topup)
        self.wid_tusts.setVisible(is_topup)
        self.but_tiinj.setVisible(not is_topup)
        self.but_topup.setVisible(is_topup)

    def mouseDoubleClickEvent(self, event):
        """Implement mouseDoubleClickEvent."""
        if event.button() == Qt.LeftButton:
            if self.wid_curr.underMouse():
                self.showStatus.emit()
            elif self.wid_shift.underMouse():
                self.showStatus.emit()
            elif self.wid_egun.underMouse():
                self.showEgun.emit()
            elif self.wid_mon.underMouse():
                self.showMonitor.emit()
        super().mouseDoubleClickEvent(event)

    def changeEvent(self, event):
        """Implement changeEvent."""
        if event.type() == QEvent.FontChange:
            fontsize = self.app.font().pointSize()
            self.label_curr.setStyleSheet(
                'QLabel{'
                '    font-size: ' + str(fontsize + 8) + 'pt;'
                '    qproperty-alignment: AlignCenter;'
                '    min-width: 6em; max-width: 6em;'
                '}')
            self.ensurePolished()
Ejemplo n.º 6
0
    def _setupMeasSettingsWidget(self, mode):
        if mode == 'Normal':
            prefix = self.dcct_prefix
            visible = True
        elif mode == 'Fast':
            prefix = self.dcct_prefix.substitute(propty_name=mode)
            visible = False

        gbox_modesettings = QGroupBox(mode + ' Measurement Mode Settings',
                                      self)

        l_smpcnt = QLabel('Sample Count: ', self)
        spinbox_SampleCnt = PyDMSpinbox(
            self,
            prefix.substitute(propty=prefix.propty_name + 'SampleCnt-SP'))
        spinbox_SampleCnt.showStepExponent = False
        label_SampleCnt = PyDMLabel(
            self,
            prefix.substitute(propty=prefix.propty_name + 'SampleCnt-RB'))
        hlay_smpcnt = QHBoxLayout()
        hlay_smpcnt.addWidget(spinbox_SampleCnt)
        hlay_smpcnt.addWidget(label_SampleCnt)

        l_measperiod = QLabel('Period [s]: ', self)
        spinbox_MeasPeriod = PyDMSpinbox(
            self,
            prefix.substitute(propty=prefix.propty_name + 'MeasPeriod-SP'))
        spinbox_MeasPeriod.showStepExponent = False
        label_MeasPeriod = PyDMLabel(
            self,
            prefix.substitute(propty=prefix.propty_name + 'MeasPeriod-RB'))
        hlay_measperiod = QHBoxLayout()
        hlay_measperiod.addWidget(spinbox_MeasPeriod)
        hlay_measperiod.addWidget(label_MeasPeriod)

        l_measupdateperiod = QLabel('Measured Period [s]: ', self)
        label_MeasUpdatePeriod = PyDMLabel(
            self, self.dcct_prefix.substitute(propty='MeasUpdatePeriod-Mon'))

        l_imped = QLabel('Impedance: ', self)
        enumcombobox_Imped = PyDMEnumComboBox(
            self, prefix.substitute(propty=prefix.propty_name + 'Imped-Sel'))
        label_Imped = PyDMLabel(
            self, prefix.substitute(propty=prefix.propty_name + 'Imped-Sts'))
        hlay_imped = QHBoxLayout()
        hlay_imped.addWidget(enumcombobox_Imped)
        hlay_imped.addWidget(label_Imped)

        l_offset = QLabel('Relative Offset Enable: ', self)
        statebutton_RelEnbl = PyDMStateButton(
            self, prefix.substitute(propty=prefix.propty_name + 'RelEnbl-Sel'))
        statebutton_RelEnbl.shape = 1
        statebutton_RelEnbl.setStyleSheet('min-width:6em; max-width:6em;')
        label_RelEnbl = PyDMLabel(
            self, prefix.substitute(propty=prefix.propty_name + 'RelEnbl-Sts'))
        hlay_offset = QHBoxLayout()
        hlay_offset.addWidget(statebutton_RelEnbl)
        hlay_offset.addWidget(label_RelEnbl)

        l_rellvl = QLabel('Relative Offset Level [V]: ', self)
        spinbox_RelLvl = PyDMSpinbox(
            self, prefix.substitute(propty=prefix.propty_name + 'RelLvl-SP'))
        spinbox_RelLvl.showStepExponent = False
        label_RelLvl = PyDMLabel(
            self, prefix.substitute(propty=prefix.propty_name + 'RelLvl-RB'))
        label_RelLvl.precisionFromPV = False
        label_RelLvl.precision = 9
        pushbutton_RelAcq = PyDMPushButton(
            parent=self,
            label='Acquire Offset',
            pressValue=1,
            init_channel=prefix.substitute(propty=prefix.propty_name +
                                           'RelAcq-Cmd'))
        pushbutton_RelAcq.setAutoDefault(False)
        pushbutton_RelAcq.setDefault(False)
        hlay_rellvl = QHBoxLayout()
        hlay_rellvl.addWidget(spinbox_RelLvl)
        hlay_rellvl.addWidget(label_RelLvl)
        hlay_rellvl.addWidget(pushbutton_RelAcq)

        flay_modesettings = QFormLayout()
        flay_modesettings.setLabelAlignment(Qt.AlignRight)
        flay_modesettings.setFormAlignment(Qt.AlignHCenter)
        flay_modesettings.addRow(l_smpcnt, hlay_smpcnt)
        flay_modesettings.addRow(l_measperiod, hlay_measperiod)
        flay_modesettings.addRow(l_measupdateperiod, label_MeasUpdatePeriod)
        flay_modesettings.addRow(l_imped, hlay_imped)
        flay_modesettings.addRow(l_offset, hlay_offset)
        flay_modesettings.addRow(l_rellvl, hlay_rellvl)

        if mode == 'Normal':
            l_linesync = QLabel('Line Synchronization: ', self)
            statebutton_LineSync = PyDMStateButton(
                self,
                prefix.substitute(propty=prefix.propty_name + 'LineSync-Sel'))
            statebutton_LineSync.shape = 1
            statebutton_LineSync.setStyleSheet('min-width:6em; max-width:6em;')
            label_LineSync = PyDMLabel(
                self,
                prefix.substitute(propty=prefix.propty_name + 'LineSync-Sts'))
            hlay_linesync = QHBoxLayout()
            hlay_linesync.addWidget(statebutton_LineSync)
            hlay_linesync.addWidget(label_LineSync)

            label_avg = QLabel('<h4>Average Filter</h4>', self)
            l_avgenbl = QLabel('Enable: ', self)
            statebutton_AvgFilterEnbl = PyDMStateButton(
                self,
                prefix.substitute(propty=prefix.propty_name +
                                  'AvgFilterEnbl-Sel'))
            statebutton_AvgFilterEnbl.shape = 1
            label_AvgFilterEnbl = PyDMLabel(
                self,
                prefix.substitute(propty=prefix.propty_name +
                                  'AvgFilterEnbl-Sts'))
            hlay_avgenbl = QHBoxLayout()
            hlay_avgenbl.addWidget(statebutton_AvgFilterEnbl)
            hlay_avgenbl.addWidget(label_AvgFilterEnbl)

            l_avgcnt = QLabel('Samples: ', self)
            spinbox_AvgFilterCount = PyDMSpinbox(
                self,
                prefix.substitute(propty=prefix.propty_name +
                                  'AvgFilterCnt-SP'))
            spinbox_AvgFilterCount.showStepExponent = False
            label_AvgFilterCount = PyDMLabel(
                self,
                prefix.substitute(propty=prefix.propty_name +
                                  'AvgFilterCnt-RB'))
            hlay_avgcnt = QHBoxLayout()
            hlay_avgcnt.addWidget(spinbox_AvgFilterCount)
            hlay_avgcnt.addWidget(label_AvgFilterCount)

            l_avgtyp = QLabel('Type: ', self)
            enumcombobox_AvgFilterTyp = PyDMEnumComboBox(
                self, self.dcct_prefix.substitute(propty='AvgFilterTyp-Sel'))
            label_AvgFilterTyp = PyDMLabel(
                self, self.dcct_prefix.substitute(propty='AvgFilterTyp-Sts'))
            hlay_avgtyp = QHBoxLayout()
            hlay_avgtyp.addWidget(enumcombobox_AvgFilterTyp)
            hlay_avgtyp.addWidget(label_AvgFilterTyp)

            l_avgwin = QLabel('Noise window size [%]: ', self)
            spinbox_AvgFilterWind = PyDMSpinbox(
                self,
                prefix.substitute(propty=prefix.propty_name +
                                  'AvgFilterWind-SP'))
            spinbox_AvgFilterWind.showStepExponent = False
            label_AvgFilterWind = PyDMLabel(
                self,
                prefix.substitute(propty=prefix.propty_name +
                                  'AvgFilterWind-RB'))
            hlay_avgwin = QHBoxLayout()
            hlay_avgwin.addWidget(spinbox_AvgFilterWind)
            hlay_avgwin.addWidget(label_AvgFilterWind)

            flay_modesettings.addRow(l_linesync, hlay_linesync)
            flay_modesettings.addRow(QLabel(''))
            flay_modesettings.addRow(label_avg)
            flay_modesettings.addRow(l_avgenbl, hlay_avgenbl)
            flay_modesettings.addRow(l_avgcnt, hlay_avgcnt)
            flay_modesettings.addRow(l_avgtyp, hlay_avgtyp)
            flay_modesettings.addRow(l_avgwin, hlay_avgwin)

        gbox_modesettings.setLayout(flay_modesettings)
        gbox_modesettings.setVisible(visible)
        gbox_modesettings.setStyleSheet("""
            PyDMSpinbox, PyDMLabel{
                min-width:6em; max-width:6em;
                qproperty-alignment: AlignCenter;}
            PyDMLedMultiChannel, PyDMStateButton, PyDMEnumComboBox{
                min-width:6em; max-width:6em;}""")
        return gbox_modesettings
Ejemplo n.º 7
0
class General(QWidget):
    def __init__(
        self,
        viewer,
        point_size=30,
        spline_size=10,
        track_file_extension=".h5",
        image_file_extension=".nii",
        x_scaling=10,
        y_scaling=10,
        z_scaling=10,
        num_colors=10,
        brush_size=30,
        spline_points_default=1000,
        spline_smoothing_default=0.1,
        fit_degree_default=3,
        summarise_track_default=True,
        add_surface_point_default=False,
        calculate_volumes_default=True,
        summarise_volumes_default=True,
        region_alpha_default=0.8,
        structure_alpha_default=0.8,
        shading_default="flat",
        region_to_add_default="",
        vtkplotter_shading_types=["flat", "giroud", "phong"],
    ):
        super(General, self).__init__()
        self.point_size = point_size
        self.spline_size = spline_size

        # general variables
        self.viewer = viewer

        self.x_scaling = x_scaling
        self.y_scaling = y_scaling
        self.z_scaling = z_scaling

        # track variables
        self.track_layers = []
        self.track_file_extension = track_file_extension
        self.spline_points_default = spline_points_default
        self.spline_smoothing_default = spline_smoothing_default
        self.summarise_track_default = summarise_track_default
        self.add_surface_point_default = add_surface_point_default
        self.fit_degree_default = fit_degree_default
        self.napari_point_size = int(BRAINRENDER_TO_NAPARI_SCALE *
                                     self.point_size)
        self.napari_spline_size = int(BRAINRENDER_TO_NAPARI_SCALE *
                                      self.spline_size)

        # region variables
        self.label_layers = []
        self.image_file_extension = image_file_extension
        self.brush_size = brush_size
        self.num_colors = num_colors
        self.calculate_volumes_default = calculate_volumes_default
        self.summarise_volumes_default = summarise_volumes_default

        # atlas variables
        self.region_labels = []

        # brainrender variables
        self.region_alpha_default = region_alpha_default
        self.structure_alpha_default = structure_alpha_default
        self.shading_default = shading_default
        self.region_to_add_default = region_to_add_default
        self.vtkplotter_shading_types = vtkplotter_shading_types

        self.setup_layout()

    def setup_layout(self):
        self.instantiated = False
        layout = QGridLayout()

        self.load_button = add_button(
            "Load project",
            layout,
            self.load_amap_directory,
            0,
            0,
            minimum_width=200,
        )

        self.load_atlas_button = add_button("Load atlas",
                                            layout,
                                            self.load_atlas,
                                            0,
                                            1,
                                            visibility=False)
        self.save_button = add_button("Save",
                                      layout,
                                      self.save,
                                      7,
                                      1,
                                      visibility=False)

        self.status_label = QLabel()
        self.status_label.setText("Ready")

        layout.addWidget(self.status_label, 8, 0)
        layout.setAlignment(QtCore.Qt.AlignTop)
        layout.setSpacing(4)
        self.setLayout(layout)

        self.add_track_panel(layout)
        self.add_region_panel(layout)
        self.add_brainrender_panel(layout)

        self.setLayout(layout)

    def add_track_panel(self, layout):
        self.track_panel = QGroupBox("Track tracing")
        track_layout = QGridLayout()

        add_button(
            "Add track",
            track_layout,
            self.add_track,
            5,
            0,
        )
        add_button(
            "Trace tracks",
            track_layout,
            self.run_track_analysis,
            5,
            1,
        )

        self.summarise_track_checkbox = add_checkbox(
            track_layout,
            self.summarise_track_default,
            "Summarise",
            0,
        )

        self.add_surface_point_checkbox = add_checkbox(
            track_layout,
            self.add_surface_point_default,
            "Add surface point",
            1,
        )

        self.fit_degree = add_int_box(
            track_layout,
            self.fit_degree_default,
            1,
            5,
            "Fit degree",
            2,
        )

        self.spline_smoothing = add_float_box(
            track_layout,
            self.spline_smoothing_default,
            0,
            1,
            "Spline smoothing",
            0.1,
            3,
        )

        self.spline_points = add_int_box(
            track_layout,
            self.spline_points_default,
            1,
            10000,
            "Spline points",
            4,
        )

        track_layout.setColumnMinimumWidth(1, 150)
        self.track_panel.setLayout(track_layout)
        layout.addWidget(self.track_panel, 3, 0, 1, 2)

        layout.setAlignment(QtCore.Qt.AlignTop)
        layout.setSpacing(4)
        self.track_panel.setVisible(False)

    def add_region_panel(self, layout):
        self.region_panel = QGroupBox("Region analysis")
        region_layout = QGridLayout()

        add_button(
            "Add region",
            region_layout,
            self.add_new_region,
            2,
            0,
        )
        add_button(
            "Analyse regions",
            region_layout,
            self.run_region_analysis,
            2,
            1,
        )

        self.calculate_volumes_checkbox = add_checkbox(
            region_layout,
            self.calculate_volumes_default,
            "Calculate volumes",
            0,
        )

        self.summarise_volumes_checkbox = add_checkbox(
            region_layout,
            self.summarise_volumes_default,
            "Summarise volumes",
            1,
        )

        region_layout.setColumnMinimumWidth(1, 150)
        self.region_panel.setLayout(region_layout)
        layout.addWidget(self.region_panel, 5, 0, 1, 2)

        layout.setAlignment(QtCore.Qt.AlignTop)
        layout.setSpacing(4)
        self.region_panel.setVisible(False)

    def add_brainrender_panel(self, layout):
        self.initialise_brainrender()

        self.brainrender_panel = QGroupBox("brainrender")
        brainrender_layout = QGridLayout()

        add_button(
            "View in Brainrender",
            brainrender_layout,
            self.to_brainrender,
            4,
            1,
        )

        self.region_alpha = add_float_box(
            brainrender_layout,
            self.region_alpha_default,
            0,
            1,
            "Segmented region alpha",
            0.1,
            0,
        )

        self.structure_alpha = add_float_box(
            brainrender_layout,
            self.structure_alpha_default,
            0,
            1,
            "Atlas region alpha",
            0.1,
            1,
        )

        self.shading = add_combobox(
            brainrender_layout,
            "Segmented region shading",
            self.vtkplotter_shading_types,
            2,
        )

        self.region_to_render = add_combobox(
            brainrender_layout,
            "Region to render",
            self.available_meshes,
            3,
        )

        brainrender_layout.setColumnMinimumWidth(1, 150)
        self.brainrender_panel.setLayout(brainrender_layout)
        layout.addWidget(self.brainrender_panel, 6, 0, 1, 2)

        layout.setAlignment(QtCore.Qt.AlignTop)
        layout.setSpacing(4)
        self.brainrender_panel.setVisible(False)

    def load_atlas(self):
        if not self.region_labels:
            self.status_label.setText(f"Loading ...")
            self.region_labels = self.viewer.add_labels(
                prepare_load_nii(self.paths.annotations, memory=memory),
                name="Region labels",
                opacity=0.2,
            )
            self.region_labels.editable = False

            @self.region_labels.mouse_move_callbacks.append
            def display_region_name(layer, event):
                display_brain_region_name(layer, self.structures_df)

            self.status_label.setText(f"Ready")

    def initialise_image_view(self):
        self.set_z_position()

    def set_z_position(self):
        midpoint = int(round(len(self.base_layer.data) / 2))
        self.viewer.dims.set_point(0, midpoint)

    def load_amap_directory(self):
        self.select_nii_file()
        self.registration_directory = self.downsampled_file.parent
        self.status_label.setText(f"Loading ...")

        self.paths = Paths(self.registration_directory, self.downsampled_file)

        if not self.paths.tmp__inverse_transformed_image.exists():
            print(
                f"The image: '{self.downsampled_file}' has not been transformed into standard "
                f"space, and so must be transformed before segmentation.\n")
            transform_image_to_standard_space(
                self.registration_directory,
                image_to_transform_fname=self.downsampled_file,
                output_fname=self.paths.tmp__inverse_transformed_image,
                log_file_path=self.paths.tmp__inverse_transform_log_path,
                error_file_path=self.paths.tmp__inverse_transform_error_path,
            )
        else:
            print("Registered image exists, skipping registration\n")

        self.registered_image = prepare_load_nii(
            self.paths.tmp__inverse_transformed_image, memory=memory)
        self.base_layer = display_channel(
            self.viewer,
            self.registration_directory,
            self.paths.tmp__inverse_transformed_image,
            memory=memory,
            name="Image in standard space",
        )
        self.initialise_image_view()

        self.structures_df = load_structures_as_df(get_structures_path())

        self.load_button.setMinimumWidth(0)
        self.load_atlas_button.setVisible(True)
        self.save_button.setVisible(True)
        self.initialise_region_segmentation()
        self.initialise_track_tracing()
        self.status_label.setText(f"Ready")

    def select_nii_file(self):
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        file, _ = QFileDialog.getOpenFileName(
            self,
            "Select the downsampled image you wish to segment",
            "",
            "Nifti files (*.nii)",
            options=options,
        )
        self.downsampled_file = Path(file)

    def initialise_track_tracing(self):
        track_files = glob(
            str(self.paths.tracks_directory) + "/*" +
            self.track_file_extension)
        if self.paths.tracks_directory.exists() and track_files != []:
            for track_file in track_files:
                self.track_layers.append(
                    add_existing_track_layers(
                        self.viewer,
                        track_file,
                        self.napari_point_size,
                        self.x_scaling,
                        self.y_scaling,
                        self.z_scaling,
                    ))
        self.track_panel.setVisible(True)
        self.region_panel.setVisible(True)
        self.brainrender_panel.setVisible(True)

    def add_track(self):
        print("Adding a new track\n")
        add_new_track_layer(self.viewer, self.track_layers,
                            self.napari_point_size)

    def run_track_analysis(self):
        print("Running track analysis")
        self.scene, self.splines = track_analysis(
            self.viewer,
            self.base_layer,
            self.scene,
            self.paths.tracks_directory,
            self.x_scaling,
            self.y_scaling,
            self.z_scaling,
            self.napari_spline_size,
            add_surface_to_points=self.add_surface_point_checkbox.isChecked(),
            spline_points=self.spline_points.value(),
            fit_degree=self.fit_degree.value(),
            spline_smoothing=self.spline_smoothing.value(),
            point_size=self.point_size,
            spline_size=self.spline_size,
            summarise_track=self.summarise_track_checkbox.isChecked(),
            track_file_extension=self.track_file_extension,
        )
        print("Finished!\n")

    def initialise_region_segmentation(self):
        add_existing_region_segmentation(
            self.paths.regions_directory,
            self.viewer,
            self.label_layers,
            self.image_file_extension,
            memory=memory,
        )

    def add_new_region(self):
        print("Adding a new region\n")
        add_new_region_layer(
            self.viewer,
            self.label_layers,
            self.registered_image,
            self.brush_size,
            self.num_colors,
        )

    def run_region_analysis(self):
        print("Running region analysis")
        worker = region_analysis(
            self.label_layers,
            self.structures_df,
            self.paths.regions_directory,
            self.paths.annotations,
            self.paths.hemispheres,
            output_csv_file=self.paths.region_summary_csv,
            volumes=self.calculate_volumes_checkbox.isChecked(),
            summarise=self.summarise_volumes_checkbox.isChecked(),
        )
        worker.start()

    def initialise_brainrender(self):
        self.scene = Scene(add_root=True)
        self.splines = None
        self.available_meshes = [""] + self.scene.atlas.all_avaliable_meshes

    def to_brainrender(self):
        print("Closing viewer and viewing in brainrender.")
        QApplication.closeAllWindows()
        view_in_brainrender(
            self.scene,
            self.splines,
            self.paths.regions_directory,
            alpha=self.region_alpha.value(),
            shading=str(self.shading.currentText()),
            region_to_add=str(self.region_to_render.currentText()),
            region_alpha=self.structure_alpha.value(),
        )

    def save(self):
        print("Saving")
        worker = save_all(
            self.viewer,
            self.paths.regions_directory,
            self.paths.tracks_directory,
            self.label_layers,
            self.track_layers,
            self.paths.downsampled_image,
            self.x_scaling,
            self.y_scaling,
            self.z_scaling,
            track_file_extension=self.track_file_extension,
        )
        worker.start()
Ejemplo n.º 8
0
class CurationWidget(QWidget):
    def __init__(
        self,
        viewer,
    ):
        super(CurationWidget, self).__init__()

        self.viewer = viewer

        self.background_layer = []
        self.background_path = ""

        self.signal_layer = []
        self.signal_path = ""

        self.directory = ""
        self.output_directory = None

        self.setup_main_layout()

    def setup_main_layout(self):
        """
        Construct main layout of widget
        """
        self.layout = QGridLayout()
        self.layout.setContentsMargins(10, 10, 10, 10)
        self.layout.setAlignment(QtCore.Qt.AlignTop)
        self.layout.setSpacing(4)

        self.add_loading_panel(1)

        self.status_label = QLabel()
        self.status_label.setText("Ready")
        self.layout.addWidget(self.status_label, 5, 0)

        self.setLayout(self.layout)

    def add_loading_panel(self, row, column=0):
        """
        Loading panel:
            - Load project (sample space)
            - Load project (atlas space)
            - Atlas chooser
        """
        self.load_data_panel = QGroupBox("Load data")
        self.load_data_layout = QGridLayout()
        self.load_data_layout.setSpacing(15)
        self.load_data_layout.setContentsMargins(10, 10, 10, 10)
        self.load_data_layout.setAlignment(QtCore.Qt.AlignBottom)

        # self.load_cellfinder_dir_button = add_button(
        #     "Load cellfinder project",
        #     self.load_data_layout,
        #     self.get_cellfinder_directory,
        #     0,
        #     0,
        #     minimum_width=COLUMN_WIDTH,
        # )

        # self.load_background_button = add_button(
        #     "Load background",
        #     self.load_data_layout,
        #     self.get_background,
        #     1,
        #     0,
        #     minimum_width=COLUMN_WIDTH,
        # )

        self.load_signal_button = add_button(
            "Load signal",
            self.load_data_layout,
            self.get_signal,
            2,
            0,
            minimum_width=COLUMN_WIDTH,
        )

        self.add_cells_button = add_button(
            "Add cell count",
            self.load_data_layout,
            self.add_cell_count,
            3,
            0,
            minimum_width=COLUMN_WIDTH,
        )

        self.add_cells_button = add_button(
            "Load cell count",
            self.load_data_layout,
            self.load_cells,
            4,
            0,
            minimum_width=COLUMN_WIDTH,
        )

        self.save_cells_button = add_button(
            "Save cells",
            self.load_data_layout,
            self.save_cell_count,
            5,
            0,
            minimum_width=COLUMN_WIDTH,
        )
        self.analyse_cells_button = add_button(
            "Analyse cells",
            self.load_data_layout,
            self.analyse_cells,
            6,
            0,
            minimum_width=COLUMN_WIDTH,
        )

        self.load_data_layout.setColumnMinimumWidth(0, COLUMN_WIDTH)
        self.load_data_panel.setLayout(self.load_data_layout)
        self.load_data_panel.setVisible(True)
        self.layout.addWidget(self.load_data_panel, row, column, 1, 1)

    def get_cellfinder_directory(self):
        self.status_label.setText("Loading...")
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        cellfinder_directory = QFileDialog.getExistingDirectory(
            self,
            "Select cellfinder directory",
            options=options,
        )

        if not cellfinder_directory:
            return

        if self.directory != cellfinder_directory:
            self.directory = Path(cellfinder_directory)
        else:
            print(f"{str(cellfinder_directory)} already loaded.")
            return

        # Otherwise, proceed loading brainreg dir
        self.load_cellfinder_directory()
        self.status_label.setText("Ready...")

    def load_cellfinder_directory(self):
        try:
            self.viewer.open(str(self.directory), plugin="cellfinder")
        except ValueError:
            print(f"The directory ({self.directory}) does not appear to be "
                  f"a cellfinder directory, please try again.")
            return

    def get_background(self):
        self.background_layer, self.background_path = self.get_data(
            name="background", visible=False)

    def get_signal(self):
        self.signal_layer, self.signal_path = self.get_data(name="signal")
        self.status_label.setText("Ready")

    def get_data(self, name="", visible=True):
        self.status_label.setText("Loading...")
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        directory = QFileDialog.getExistingDirectory(
            self,
            f"Select {name} channel",
            options=options,
        )

        if not directory:
            return

        if self.directory != directory:
            self.directory = Path(directory)
        else:
            print(f"{str(directory)} already loaded.")
            return

        return self.load_data(name=name, visible=visible), directory

    def load_data(self, name="", visible=True):
        try:
            img_paths = get_sorted_file_paths(self.directory,
                                              file_extension=".tif")
            images = magic_imread(img_paths, use_dask=True, stack=True)
            return self.viewer.add_image(images)
            # return self.viewer.open(
            #     str(self.directory), name=name, visible=visible
            # )
        except ValueError:
            print(f"The directory ({self.directory}) cannot be "
                  f"loaded, please try again.")
            return

    def add_cell_count(self):
        self.cell_layer = self.viewer.add_points(
            np.empty((0, 3)),
            symbol="ring",
            n_dimensional=True,
            size=10,
            opacity=0.6,
            face_color="lightgoldenrodyellow",
            name="cells",
        )

    def load_cells(self):
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        filename = QFileDialog.getOpenFileName(
            self,
            "Select cell file...",
            filter="cellfinder xml files (*.xml)",
            options=options,
        )
        cells, _ = get_cell_arrays(filename[0])

        self.cell_layer = self.viewer.add_points(
            cells,
            symbol="ring",
            n_dimensional=True,
            size=10,
            opacity=0.6,
            face_color="lightgoldenrodyellow",
            name="cells",
        )

    def save_cell_count(self):
        self.status_label.setText("Saving cells")
        print("Saving cells")
        self.get_output_directory()
        filename = self.output_directory / "cells.xml"

        cells_to_save = []
        for idx, point in enumerate(self.cell_layer.data):
            cell = Cell([point[2], point[1], point[0]], Cell.CELL)
            cells_to_save.append(cell)

        save_cells(cells_to_save, str(filename))

        self.status_label.setText("Ready")
        print("Done!")

    def get_output_directory(self):
        if self.output_directory is None:
            options = QFileDialog.Options()
            options |= QFileDialog.DontUseNativeDialog
            self.output_directory = QFileDialog.getExistingDirectory(
                self,
                "Select output directory",
                options=options,
            )
            self.output_directory = Path(self.output_directory)

    def analyse_cells(self):
        self.status_label.setText("Analysing cells")
        print("Analysing cells")

        self.get_output_directory()
        self.get_brainreg_directory()

        analyse_cell_positions(
            self.cell_layer.data,
            self.brainreg_directory,
            self.signal_path,
            self.output_directory,
        )
        self.status_label.setText("Ready")

    def get_brainreg_directory(self):
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        self.brainreg_directory = QFileDialog.getExistingDirectory(
            self,
            "Select brainreg directory",
            options=options,
        )
Ejemplo n.º 9
0
class InjCtrlWindow(SiriusMainWindow):
    """InjCtrl Main Window."""

    showMonitor = Signal()
    showStatus = Signal()
    showEgun = Signal()

    def __init__(self, parent=None, prefix=''):
        """Init."""
        super().__init__(parent)
        self._prefix = prefix
        self._inj_dev = SiriusPVName('AS-Glob:AP-InjCtrl')
        self._inj_prefix = self._inj_dev.substitute(prefix=prefix)
        self.setWindowTitle('Injection Controls')
        self.setObjectName('ASApp')
        self.setWindowIcon(
            qta.icon('fa5s.syringe', color=get_appropriate_color('AS')))

        self._setupUi()

        self.setFocus(True)
        self.setFocusPolicy(Qt.StrongFocus)

    def _setupUi(self):
        self.title = QLabel('<h3>Injection Control<h3>',
                            self,
                            alignment=Qt.AlignCenter)
        self.title.setStyleSheet('QLabel{max-height:1.6em;}')

        self.wid_comm = self._setupMainBarWidget()
        self.wid_comm.setSizePolicy(QSzPlcy.Preferred, QSzPlcy.Fixed)

        self.wid_sett = self._setupSettingsWidget()
        self.wid_sett.setSizePolicy(QSzPlcy.Preferred,
                                    QSzPlcy.MinimumExpanding)

        self.wid_mon = self._setupMonitorWidget()
        self.wid_log = self._setupLogWidget()
        wid_row = QWidget()
        wid_row.setSizePolicy(QSzPlcy.Preferred, QSzPlcy.Fixed)
        hbox_row = QHBoxLayout(wid_row)
        hbox_row.setContentsMargins(0, 0, 0, 0)
        hbox_row.setStretch(0, 3)
        hbox_row.setStretch(1, 5)
        hbox_row.addWidget(self.wid_mon)
        hbox_row.addWidget(self.wid_log)

        wid = QWidget(self)
        lay = QVBoxLayout(wid)
        lay.addWidget(self.title)
        lay.addWidget(self.wid_comm)
        lay.addWidget(self.wid_comm)
        lay.addWidget(self.wid_sett)
        lay.addWidget(wid_row)
        lay.setStretch(1, 1)
        lay.setStretch(2, 3)
        lay.setStretch(3, 2)
        self.setCentralWidget(wid)

        self._ch_injmode = SiriusConnectionSignal(
            self._inj_prefix.substitute(propty='Mode-Sel'))
        self._ch_injmode.new_value_signal[int].connect(
            self._handle_injmode_settings_vis)

        # connect window signals
        connect_newprocess(self,
                           'sirius-hla-as-ap-monitor.py',
                           parent=self,
                           signal=self.showMonitor)
        connect_newprocess(self,
                           'sirius-hla-si-ap-genstatus.py',
                           parent=self,
                           signal=self.showStatus)
        connect_newprocess(self,
                           'sirius-hla-li-eg-control.py',
                           parent=self,
                           signal=self.showEgun)

    def _setupMainBarWidget(self):
        # Shift
        machshift_pvname = SiriusPVName(
            'AS-Glob:AP-MachShift:Mode-Sel').substitute(prefix=self._prefix)
        self._cb_shift = SiriusEnumComboBox(self, machshift_pvname)
        self._lb_shift = MachShiftLabel(self, prefix=self._prefix)
        self._lb_shift.setStyleSheet(
            'QLabel{max-height: 2em; min-width: 7em;}')
        self.wid_shift = QGroupBox('Mach.Shift', self)
        lay_shift = QVBoxLayout(self.wid_shift)
        lay_shift.addWidget(self._cb_shift)
        lay_shift.addWidget(self._lb_shift)

        # Injection System
        self.wid_is_summ = InjSysStbyControlWidget(self, is_summary=True)
        self.wid_injsys = QGroupBox('Inj.System', self)
        lay_injsys = QGridLayout(self.wid_injsys)
        lay_injsys.setContentsMargins(0, 0, 0, 0)
        lay_injsys.addWidget(self.wid_is_summ, 0, 0)
        # self.wid_is_full = InjSysStbyControlWidget(self, is_summary=False)
        # self.wid_is_full.setVisible(False)
        # lay_injsys.addWidget(self.wid_is_full, 0, 0)
        # self._icon_expd = qta.icon('fa5s.plus')
        # self._icon_comp = qta.icon('fa5s.minus')
        # self.bt_is_tgl = QPushButton(self._icon_expd, '', self)
        # self.bt_is_tgl.clicked.connect(self._handle_injsys_details_vis)
        # self.bt_is_tgl.setObjectName('bt')
        # self.bt_is_tgl.setStyleSheet("""
        #     #bt{
        #         min-width: 0.8em; max-width: 0.8em;
        #         min-height: 0.8em; max-height: 0.8em;
        #         icon-size:12px;}
        # """)
        # lay_injsys.addWidget(
        #     self.bt_is_tgl, 0, 1, alignment=Qt.AlignRight | Qt.AlignBottom)

        # EGun
        egun_dev = SiriusPVName('LI-01:EG-TriggerPS').substitute(
            prefix=self._prefix)
        self._sb_eguntrg = PyDMStateButton(
            self, egun_dev.substitute(propty_name='enable'))
        self._led_eguntrg = SiriusLedState(
            self, egun_dev.substitute(propty_name='enablereal'))
        self._led_eguntrg.setStyleSheet(
            'QLed{min-width: 1.29em; max-width: 1.29em;}')
        self.wid_egun = self._create_groupwidget('EGun Trig.', [
            self._sb_eguntrg,
        ], [
            self._led_eguntrg,
        ])

        # Injection
        self._pb_tiinj = EVGInjectionButton(self, self._prefix)
        self._pb_topup = PyDMStateButton(
            self,
            init_channel=self._inj_prefix.substitute(propty='TopUpState-Sel'))
        self._pb_topup.setVisible(False)
        self._led_injti = EVGInjectionLed(self, self._prefix)
        self._lb_injcnt = PyDMLabel(self)
        self._lb_injcnt.setToolTip(
            'Count injection pulses when Egun Trigger is enabled.')
        ch_injcnt = SiriusPVName(
            'AS-Glob:AP-CurrInfo:InjCount-Mon').substitute(prefix=self._prefix)
        self._lb_injcnt.channel = ch_injcnt
        self._lb_injcnt.setStyleSheet('QLabel{max-width: 3.5em;}')
        hbox_injsts = QHBoxLayout()
        hbox_injsts.setContentsMargins(0, 0, 0, 0)
        hbox_injsts.addWidget(self._led_injti)
        hbox_injsts.addWidget(self._lb_injcnt)
        self.wid_inj = QGroupBox('Injection', self)
        lay_inj = QGridLayout(self.wid_inj)
        lay_inj.setAlignment(Qt.AlignCenter)
        lay_inj.addWidget(self._pb_tiinj, 0, 0)
        lay_inj.addWidget(self._pb_topup, 0, 0)
        lay_inj.addLayout(hbox_injsts, 1, 0)

        # Current
        curr_pvname = SiriusPVName(
            'SI-Glob:AP-CurrInfo:Current-Mon').substitute(prefix=self._prefix)
        self._lb_curr = PyDMLabel(self, curr_pvname)
        self._lb_curr.showUnits = True
        self._lb_curr.setStyleSheet("""
            QLabel{
                font-size: 18pt; qproperty-alignment: AlignCenter;
                min-width: 5.5em; max-width: 5.5em;
        }""")
        self.wid_curr = QGroupBox('Current', self)
        lay_curr = QHBoxLayout(self.wid_curr)
        lay_curr.addWidget(self._lb_curr)

        # TopUp status
        self._lb_tusts = PyDMLabel(
            self, self._inj_prefix.substitute(propty='TopUpState-Sts'))
        self._lb_tusts.setAlignment(Qt.AlignCenter)
        self._lb_tusts.setStyleSheet('QLabel{max-height:2em;}')
        self._ld_tunow = QLabel('Now:',
                                self,
                                alignment=Qt.AlignRight | Qt.AlignVCenter)
        self._lb_tunow = ClockLabel(self)
        self._lb_tunow.setStyleSheet('QLabel{max-height:2em;}')
        self._ld_tunxt = QLabel('Next:',
                                self,
                                alignment=Qt.AlignRight | Qt.AlignVCenter)
        self._lb_tunxt = SiriusLabel(
            self, self._inj_prefix.substitute(propty='TopUpNextInj-Mon'))
        self._lb_tunxt.displayFormat = SiriusLabel.DisplayFormat.Time
        self._lb_tunxt.setAlignment(Qt.AlignCenter)
        self._lb_tunxt.setStyleSheet('QLabel{max-height:2em;}')
        self._pb_round = PyDMPushButton(
            self,
            label='',
            icon=qta.icon('mdi.tilde'),
            pressValue=1,
            init_channel=self._inj_prefix.substitute(
                propty='TopUpNextInjRound-Cmd'))
        self._pb_round.setObjectName('but')
        self._pb_round.setStyleSheet(
            '#but{min-width:18px; max-width:18px; icon-size:16px;}')
        self.wid_tusts = QGroupBox('Top-up status')
        self.wid_tusts.setVisible(False)
        lay_tusts = QGridLayout(self.wid_tusts)
        lay_tusts.addWidget(self._lb_tusts, 0, 0, 1, 2)
        lay_tusts.addWidget(self._ld_tunow, 1, 0)
        lay_tusts.addWidget(self._lb_tunow, 1, 1)
        lay_tusts.addWidget(self._ld_tunxt, 2, 0)
        lay_tusts.addWidget(self._lb_tunxt, 2, 1)
        lay_tusts.addWidget(self._pb_round, 2, 2)

        wid = QWidget()
        lay = QGridLayout(wid)
        lay.setContentsMargins(0, 0, 0, 0)
        lay.addWidget(self.wid_shift, 0, 0)
        lay.addWidget(self.wid_injsys, 0, 1)
        lay.addWidget(self.wid_egun, 0, 2)
        lay.addWidget(self.wid_inj, 0, 3)
        lay.addWidget(self.wid_tusts, 0, 4)
        lay.addWidget(self.wid_curr, 0, 5)
        lay.setColumnStretch(0, 3)
        lay.setColumnStretch(1, 2)
        lay.setColumnStretch(2, 2)
        lay.setColumnStretch(3, 2)
        lay.setColumnStretch(4, 4)
        lay.setColumnStretch(5, 3)
        wid.setStyleSheet('.QLabel{min-height: 1em; max-height: 1em;}')
        return wid

    def _setupSettingsWidget(self):
        # group of labels to set the same stylesheet
        labelsdesc, labelsmon = list(), list()

        # Mode
        self._ld_injmode = QLabel('Mode', self)
        labelsdesc.append(self._ld_injmode)
        self._cb_injmode = SiriusEnumComboBox(
            self, self._inj_prefix.substitute(propty='Mode-Sel'))
        self._lb_injmode = PyDMLabel(
            self, self._inj_prefix.substitute(propty='Mode-Sts'))
        self._lb_injmode.showUnits = True
        labelsmon.append(self._lb_injmode)

        # Target current
        self._ld_currtgt = QLabel('Target Curr.', self)
        labelsdesc.append(self._ld_currtgt)
        self._sb_currtgt = SiriusSpinbox(
            self, self._inj_prefix.substitute(propty='TargetCurrent-SP'))
        self._sb_currtgt.showStepExponent = False
        self._lb_currtgt = PyDMLabel(
            self, self._inj_prefix.substitute(propty='TargetCurrent-RB'))
        self._lb_currtgt.showUnits = True
        labelsmon.append(self._lb_currtgt)

        # mode specific configurations
        self.wid_dcdtls = self._setupDecayModeWidget()
        self.wid_tudtls = self._setupTopUpModeWidget()
        self.wid_tudtls.setVisible(False)

        # Mon
        self._ld_injset = QLabel('Setup ok', self)
        labelsdesc.append(self._ld_injset)
        self._led_injset = InjDiagLed(self)

        # Type
        self._ld_injtype = QLabel('Type', self)
        labelsdesc.append(self._ld_injtype)
        self._cb_injtype = SiriusEnumComboBox(
            self, self._inj_prefix.substitute(propty='Type-Sel'))
        self._lb_injtype = PyDMLabel(
            self, self._inj_prefix.substitute(propty='Type-Sts'))
        labelsmon.append(self._lb_injtype)
        self._lb_injtype_mon = PyDMLabel(
            self, self._inj_prefix.substitute(propty='Type-Mon'))
        labelsmon.append(self._lb_injtype_mon)
        self._ch_injtype = SiriusConnectionSignal(
            self._inj_prefix.substitute(propty='Type-Sel'))
        self._ch_injtype.new_value_signal[int].connect(
            self._handle_injtype_settings_vis)

        # Single bunch bias voltage
        self._ld_sbbias = QLabel('SB Bias Voltage', self)
        labelsdesc.append(self._ld_sbbias)
        self._sb_sbbias = SiriusSpinbox(
            self, self._inj_prefix.substitute(propty='SglBunBiasVolt-SP'))
        self._sb_sbbias.showStepExponent = False
        self._lb_sbbias = PyDMLabel(
            self, self._inj_prefix.substitute(propty='SglBunBiasVolt-RB'))
        self._lb_sbbias.showUnits = True
        labelsmon.append(self._lb_sbbias)
        self._ld_sbbias.setVisible(False)
        self._sb_sbbias.setVisible(False)
        self._lb_sbbias.setVisible(False)

        # Multi bunch bias voltage
        self._ld_mbbias = QLabel('MB Bias Volt.', self)
        labelsdesc.append(self._ld_mbbias)
        self._sb_mbbias = SiriusSpinbox(
            self, self._inj_prefix.substitute(propty='MultBunBiasVolt-SP'))
        self._sb_mbbias.showStepExponent = False
        self._lb_mbbias = PyDMLabel(
            self, self._inj_prefix.substitute(propty='MultBunBiasVolt-RB'))
        self._lb_mbbias.showUnits = True
        labelsmon.append(self._lb_mbbias)

        # bias voltage mon
        ch_bias_mon = SiriusPVName('LI-01:EG-BiasPS').substitute(
            prefix=self._prefix, propty_name='voltinsoft')
        self._lb_bias_mon = PyDMLabel(self, ch_bias_mon)
        self._lb_bias_mon.showUnits = True
        labelsmon.append(self._lb_bias_mon)

        # Filament current op value
        self._ld_filaopcurr = QLabel('Fila.Op. Curr.', self)
        labelsdesc.append(self._ld_filaopcurr)
        self._sb_filaopcurr = SiriusSpinbox(
            self, self._inj_prefix.substitute(propty='FilaOpCurr-SP'))
        self._sb_filaopcurr.showStepExponent = False
        self._lb_filaopcurr = PyDMLabel(
            self, self._inj_prefix.substitute(propty='FilaOpCurr-RB'))
        self._lb_filaopcurr.showUnits = True
        labelsmon.append(self._lb_filaopcurr)
        ch_filacurr_mon = SiriusPVName('LI-01:EG-FilaPS').substitute(
            prefix=self._prefix, propty_name='currentinsoft')
        self._lb_filaopcurr_mon = PyDMLabel(self, ch_filacurr_mon)
        self._lb_filaopcurr_mon.showUnits = True
        labelsmon.append(self._lb_filaopcurr_mon)

        # High voltage op value
        self._ld_hvopvolt = QLabel('HV.Op. Volt.', self)
        labelsdesc.append(self._ld_hvopvolt)
        self._sb_hvopvolt = SiriusSpinbox(
            self, self._inj_prefix.substitute(propty='HVOpVolt-SP'))
        self._sb_hvopvolt.showStepExponent = False
        self._lb_hvopvolt = PyDMLabel(
            self, self._inj_prefix.substitute(propty='HVOpVolt-RB'))
        self._lb_hvopvolt.showUnits = True
        labelsmon.append(self._lb_hvopvolt)
        ch_hvvolt_mon = SiriusPVName('LI-01:EG-HVPS').substitute(
            prefix=self._prefix, propty_name='voltinsoft')
        self._lb_hvopvolt_mon = PyDMLabel(self, ch_hvvolt_mon)
        self._lb_hvopvolt_mon.showUnits = True
        labelsmon.append(self._lb_hvopvolt_mon)

        # header
        ld_sp = QLabel('<h4>SP</h4>', self, alignment=Qt.AlignCenter)
        ld_rb = QLabel('<h4>RB</h4>', self, alignment=Qt.AlignCenter)
        ld_mon = QLabel('<h4>Mon</h4>', self, alignment=Qt.AlignCenter)

        # Bucket list
        self._wid_bl = BucketList(self,
                                  prefix=self._prefix,
                                  min_size=15,
                                  show_graph=True)
        self._wid_bl.setSizePolicy(QSzPlcy.Preferred, QSzPlcy.MinimumExpanding)

        wid1 = QWidget()
        wid1.setSizePolicy(QSzPlcy.Preferred, QSzPlcy.Fixed)
        glay1 = QGridLayout(wid1)
        glay1.setAlignment(Qt.AlignTop)
        glay1.addWidget(self._ld_injset, 0, 0)
        glay1.addWidget(self._led_injset, 0, 1)
        glay1.addWidget(self._ld_injmode, 1, 0)
        glay1.addWidget(self._cb_injmode, 1, 1)
        glay1.addWidget(self._lb_injmode, 1, 2)
        glay1.addWidget(self._ld_currtgt, 2, 0)
        glay1.addWidget(self._sb_currtgt, 2, 1)
        glay1.addWidget(self._lb_currtgt, 2, 2)
        glay1.addWidget(self.wid_tudtls, 3, 0, 2, 3)
        glay1.addWidget(self.wid_dcdtls, 3, 0, 2, 3)
        glay1.setColumnStretch(0, 3)
        glay1.setColumnStretch(1, 2)
        glay1.setColumnStretch(2, 2)

        wid2 = QWidget()
        wid2.setSizePolicy(QSzPlcy.Preferred, QSzPlcy.Fixed)
        glay2 = QGridLayout(wid2)
        glay2.setAlignment(Qt.AlignTop)
        glay2.addWidget(ld_sp, 0, 1)
        glay2.addWidget(ld_rb, 0, 2)
        glay2.addWidget(ld_mon, 0, 3)
        glay2.addWidget(self._ld_injtype, 1, 0)
        glay2.addWidget(self._cb_injtype, 1, 1)
        glay2.addWidget(self._lb_injtype, 1, 2)
        glay2.addWidget(self._lb_injtype_mon, 1, 3)
        glay2.addWidget(self._ld_sbbias, 2, 0)
        glay2.addWidget(self._sb_sbbias, 2, 1)
        glay2.addWidget(self._lb_sbbias, 2, 2)
        glay2.addWidget(self._ld_mbbias, 2, 0)
        glay2.addWidget(self._sb_mbbias, 2, 1)
        glay2.addWidget(self._lb_mbbias, 2, 2)
        glay2.addWidget(self._lb_bias_mon, 2, 3)
        glay2.addWidget(self._ld_filaopcurr, 4, 0)
        glay2.addWidget(self._sb_filaopcurr, 4, 1)
        glay2.addWidget(self._lb_filaopcurr, 4, 2)
        glay2.addWidget(self._lb_filaopcurr_mon, 4, 3)
        glay2.addWidget(self._ld_hvopvolt, 5, 0)
        glay2.addWidget(self._sb_hvopvolt, 5, 1)
        glay2.addWidget(self._lb_hvopvolt, 5, 2)
        glay2.addWidget(self._lb_hvopvolt_mon, 5, 3)
        glay2.setColumnStretch(0, 3)
        glay2.setColumnStretch(1, 2)
        glay2.setColumnStretch(2, 2)
        glay2.setColumnStretch(3, 2)

        wid = QGroupBox('Settings')
        lay = QGridLayout(wid)
        lay.addWidget(wid1, 0, 0, alignment=Qt.AlignTop)
        lay.addWidget(wid2, 0, 1, alignment=Qt.AlignTop)
        lay.addWidget(self._wid_bl, 1, 0, 1, 2)
        lay.setColumnStretch(0, 3)
        lay.setColumnStretch(1, 4)

        for lbl in labelsdesc:
            lbl.setStyleSheet("""
                QLabel{
                    min-width: 6.5em; max-width: 6.5em; min-height: 1.5em;
                    qproperty-alignment: 'AlignRight | AlignVCenter';
                }""")
        for lbl in labelsmon:
            lbl.setStyleSheet("PyDMLabel{qproperty-alignment: AlignCenter;}")

        return wid

    def _setupTopUpModeWidget(self):
        self._ld_tuperd = QLabel('Period', self)
        self._sb_tuperd = SiriusSpinbox(
            self, self._inj_prefix.substitute(propty='TopUpPeriod-SP'))
        self._sb_tuperd.showStepExponent = False
        self._lb_tuperd = PyDMLabel(
            self, self._inj_prefix.substitute(propty='TopUpPeriod-RB'))
        self._lb_tuperd.showUnits = True

        self._ld_tumaxpu = QLabel('Max.Nr.Pulses', self)
        self._sb_tumaxpu = SiriusSpinbox(
            self, self._inj_prefix.substitute(propty='TopUpMaxNrPulses-SP'))
        self._sb_tumaxpu.showStepExponent = False
        self._lb_tumaxpu = PyDMLabel(
            self, self._inj_prefix.substitute(propty='TopUpMaxNrPulses-RB'))
        self._lb_tumaxpu.showUnits = True

        wid = QWidget()
        lay = QGridLayout(wid)
        lay.setContentsMargins(0, 6, 0, 0)
        lay.setAlignment(Qt.AlignTop)
        lay.addWidget(self._ld_tuperd, 0, 0)
        lay.addWidget(self._sb_tuperd, 0, 1)
        lay.addWidget(self._lb_tuperd, 0, 2)
        lay.addWidget(self._ld_tumaxpu, 1, 0)
        lay.addWidget(self._sb_tumaxpu, 1, 1)
        lay.addWidget(self._lb_tumaxpu, 1, 2)
        lay.setColumnStretch(0, 3)
        lay.setColumnStretch(1, 2)
        lay.setColumnStretch(2, 2)

        wid.setStyleSheet("""
            .QLabel{
                min-width: 6.5em; max-width: 6.5em; min-height: 1.5em;
                qproperty-alignment: 'AlignRight | AlignVCenter';
            }
            PyDMLabel{
                qproperty-alignment: AlignCenter;
            }""")
        return wid

    def _setupDecayModeWidget(self):
        self._ld_autostop = QLabel('Auto Stop', self)
        self._cb_autostop = PyDMStateButton(
            self, self._inj_prefix.substitute(propty='AutoStop-Sel'))
        self._cb_autostop.shape = 1
        self._led_autostop = SiriusLedState(
            self, self._inj_prefix.substitute(propty='AutoStop-Sts'))

        wid = QWidget()
        lay = QGridLayout(wid)
        lay.setAlignment(Qt.AlignTop)
        lay.setContentsMargins(0, 6, 0, 0)
        lay.addWidget(self._ld_autostop, 0, 0)
        lay.addWidget(self._cb_autostop, 0, 1)
        lay.addWidget(self._led_autostop, 0, 2)
        lay.setColumnStretch(0, 3)
        lay.setColumnStretch(1, 2)
        lay.setColumnStretch(2, 2)

        wid.setStyleSheet("""
            .QLabel{
                min-width: 6.5em; max-width: 6.5em; min-height: 1.5em;
                qproperty-alignment: 'AlignRight | AlignVCenter';
            }
            PyDMLabel{
                qproperty-alignment: AlignCenter;
            }""")
        return wid

    def _setupLogWidget(self):
        self._log = PyDMLogLabel(self,
                                 self._inj_prefix.substitute(propty='Log-Mon'),
                                 [
                                     'Remaining time',
                                 ])

        wid = QGroupBox('Log')
        lay = QHBoxLayout(wid)
        lay.addWidget(self._log)
        return wid

    def _setupMonitorWidget(self):
        self.wid_mon = MonitorSummaryWidget(self)

        wid = QWidget(self)
        lay = QGridLayout(wid)
        lay.setContentsMargins(0, 0, 0, 0)
        lay.addWidget(self.wid_mon, 0, 0)
        return wid

    # ---- auxiliary commands ----

    @Slot(int)
    def _handle_injtype_settings_vis(self, new_type):
        is_sb = new_type == _Const.InjType.SingleBunch
        self._ld_sbbias.setVisible(is_sb)
        self._sb_sbbias.setVisible(is_sb)
        self._lb_sbbias.setVisible(is_sb)
        self._ld_mbbias.setVisible(not is_sb)
        self._sb_mbbias.setVisible(not is_sb)
        self._lb_mbbias.setVisible(not is_sb)
        self.centralWidget().adjustSize()
        self.adjustSize()

    @Slot(int)
    def _handle_injmode_settings_vis(self, new_mode):
        is_topoup = new_mode == _Const.InjMode.TopUp
        self.wid_tudtls.setVisible(is_topoup)
        self._pb_topup.setVisible(is_topoup)
        self.wid_dcdtls.setVisible(not is_topoup)
        self._pb_tiinj.setVisible(not is_topoup)
        self.wid_tusts.setVisible(is_topoup)

    def _handle_injsys_details_vis(self):
        exp = self.wid_is_summ.isVisible()
        icon = self._icon_comp if exp else self._icon_expd
        self.sender().setIcon(icon)
        self.wid_is_summ.setVisible(not exp)
        self.wid_is_full.setVisible(exp)
        self.centralWidget().adjustSize()
        self.adjustSize()

    def _create_groupwidget(self, title, sp_wids, rb_wids, aux_wids=None):
        hbox_sp = QHBoxLayout()
        hbox_sp.setAlignment(Qt.AlignCenter)
        hbox_sp.setContentsMargins(0, 0, 0, 0)
        for wid in sp_wids:
            hbox_sp.addWidget(wid)

        hbox_rb = QHBoxLayout()
        hbox_rb.setAlignment(Qt.AlignCenter)
        hbox_rb.setContentsMargins(0, 0, 0, 0)
        for wid in rb_wids:
            hbox_rb.addWidget(wid)

        box = QGroupBox(title, self) if title else QWidget(self)
        lay = QVBoxLayout(box)
        lay.setAlignment(Qt.AlignCenter)
        if not isinstance(box, QGroupBox):
            lay.setContentsMargins(0, 0, 0, 0)
        lay.addLayout(hbox_sp)
        lay.addLayout(hbox_rb)

        if aux_wids:
            hbox_aux = QHBoxLayout()
            hbox_aux.setAlignment(Qt.AlignCenter)
            hbox_aux.setContentsMargins(0, 0, 0, 0)
            for wid in aux_wids:
                hbox_aux.addWidget(wid)
            lay.addLayout(hbox_aux)
        return box

    # ---- events ----

    def mouseDoubleClickEvent(self, event):
        """Implement mouseDoubleClickEvent."""
        if event.button() == Qt.LeftButton:
            if self.wid_curr.underMouse():
                self.showStatus.emit()
            elif self.wid_shift.underMouse():
                self.showStatus.emit()
            elif self.wid_egun.underMouse():
                self.showEgun.emit()
            elif self.wid_mon.underMouse():
                self.showMonitor.emit()
        super().mouseDoubleClickEvent(event)

    def changeEvent(self, event):
        """Implement changeEvent."""
        if event.type() == QEvent.FontChange:
            fontsize = self.app.font().pointSize()
            self._lb_curr.setStyleSheet('QLabel{'
                                        '    font-size: ' + str(fontsize + 8) +
                                        'pt;'
                                        '    qproperty-alignment: AlignCenter;'
                                        '    min-width: 6em; max-width: 6em;'
                                        '}')
            self.ensurePolished()