Example #1
0
    def _add_img(self, source, name=None, header=None, view_min=None,
                 view_max=None, alpha=255, colormap='gray'):
        """
        Add image.

        """
        # If model is NULL, then re-initialize it.
        if not self.model:
            self._init_label_config_center()
            self.model = VolumeListModel([], self._label_config_center)
            self.model.set_scale_factor(self.default_grid_scale_factor, 'grid')
            self.model.set_scale_factor(self.default_orth_scale_factor, 'orth')
            self.painter_status = PainterStatus(ViewSettings())

        # Save previous opened directory (except `standard` directory)
        file_path = str(source)
        temp_dir = os.path.dirname(file_path)
        if sys.platform == 'win32':
            if not os.stat(temp_dir) == os.stat(os.path.join(self.label_path,
                                                             'standard')):
                self._temp_dir = temp_dir
        else:
            if not os.path.samefile(temp_dir, os.path.join(self.label_path,
                                                           'standard')):
                self._temp_dir = temp_dir

        if self.model.addItem(file_path, None, name, header, view_min,
                              view_max, alpha, colormap):
            # Take different acions in different case.
            # If only one data in VolumeList, then initialize views.
            if self.model.rowCount() == 1:
                # initialize views
                self.list_view = LayerView(self._label_config_center)
                self.list_view.setModel(self.model)
                self._init_roi_dialog()
                self.image_view = GridView(self.model, self.painter_status)
                # initialize display layout
                central_widget = QWidget()
                layout = QHBoxLayout()
                central_widget.setLayout(layout)
                central_widget.layout().addWidget(self.list_view)
                central_widget.layout().addWidget(self.image_view)
                self.setCentralWidget(central_widget)
                # add a toolbar
                self._add_toolbar()
                #self.setUnifiedTitleAndToolBarOnMac(True)
                # change button status
                self._actions['save_image'].setEnabled(True)
                #self._actions['ld_lbl'].setEnabled(True)
                #self._actions['ld_glbl'].setEnabled(True)
                self._actions['new_image'].setEnabled(True)
                self._actions['close'].setEnabled(True)
                self._actions['orth_view'].setEnabled(True)
                self._actions['cross_hover_view'].setEnabled(True)
                self._actions['original_view'].setEnabled(True)
                self._actions['undo'].setEnabled(False)
                self._actions['redo'].setEnabled(False)
                self._functional_module_set_enabled(True)
                # connect signals with slots
                self.list_view.current_changed.connect(self._update_undo)
                self.list_view.current_changed.connect(self._update_redo)
                self.model.rowsInserted.connect(self._update_remove_image)
                self.model.undo_stack_changed.connect(self._update_undo)
                self.model.redo_stack_changed.connect(self._update_redo)
                # set current volume index
                self.list_view.setCurrentIndex(self.model.index(0))
                # set crosshair as the center of the data
                self.model.set_cross_pos([self.model.getY()/2,
                                          self.model.getX()/2,
                                          self.model.getZ()/2])
                ## Enable cursor tracking
                # self.list_view._list_view.selectionModel().currentChanged.connect(
                #                self._switch_cursor_status)
            elif self.model.rowCount() > 1:
                self._actions['remove_image'].setEnabled(True)
                # set current volume index
                self.list_view.setCurrentIndex(self.model.index(0))
            self.is_save_configure = True
        else:
            QMessageBox.information(self,'FreeROI', 'Cannot load ' + "filename" + '.')
Example #2
0
class BpMainWindow(QMainWindow):
    """Class BpMainWindow provides UI interface of FreeROI.

    Example:
    --------

    >>> from PyQt4.QtGui import QApplication
    >>> import main
    >>> app = QApplication([])
    >>> win = main.BpMainWindow()
    ......
    >>> win.show()
    >>> app.exec_()

    """

    def __init__(self, parent=None):
        """
        Initialize an instance of BpMainWindow.
        
        """
        # Inherited from QMainWindow
        if sys.platform == 'darwin':
            # Workaround for Qt issue on OS X that causes QMainWindow to
            # hide when adding QToolBar, see
            # https://bugreports.qt-project.org/browse/QTBUG-4300
            super(BpMainWindow, self).__init__(parent,
                                               Qt.MacWindowToolBarButtonHint)
        else:
            super(BpMainWindow, self).__init__(parent)

        # temporary variable
        self._temp_dir = None
        self.is_save_configure = False

        # pre-define a model variable
        self.model = None

    def config_extra_settings(self, data_dir):
        """
        Set data directory and update some configurations.

        """
        # load data directory configuration
        self.label_path = data_dir
        self.label_config_dir = os.path.join(self.label_path, 'labelconfig')
        self.label_config_suffix = 'lbl'

        # set icon configuration
        self._icon_dir = get_icon_dir()

        # set window title
        self.setWindowTitle('FreeROI')
        #self.resize(1280, 1000)
        self.center()
        # set window icon
        self.setWindowIcon(QIcon(os.path.join(self._icon_dir,'icon.png')))

        self._init_configuration()

        # create actions
        self._create_actions()

        # create menus
        self._create_menus()

    def center(self):
        """
        Display main window in the center of screen

        """
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    def _init_configuration(self):
        """
        Load configuration for GUI.

        """
        config_file = os.path.expanduser('~/.pybp.conf')
        if os.path.exists(config_file):
            config = ConfigParser.RawConfigParser()
            config.read(config_file)
            self.window_width = config.getint('width', 'int')
            self.window_height = config.getint('height', 'int')
            self.orth_scale_factor = config.getint('orth_scale', 'int')
            self.grid_scale_factor = config.getint('grid_scale', 'int')
            self.window_xpos = config.getint('xpos', 'int')
            self.window_ypos = config.getint('ypos', 'int')

            self.resize(self.window_width, self.window_height)
            self.move(self.window_xpos, self.window_ypos)
            self.default_orth_scale_factor = float(self.orth_scale_factor) / 100
            self.default_grid_scale_factor = float(self.grid_scale_factor) / 100
        else:
            self.setWindowState(Qt.WindowMaximized)
            self.default_orth_scale_factor = 1.0
            self.default_grid_scale_factor = 2.0

    def _save_configuration(self):
        """
        Save GUI configuration to a file.

        """
        config_file = os.path.expanduser('~/.pybp.conf')

        config = ConfigParser.RawConfigParser()
        config.add_section('width')
        config.add_section('height')
        config.add_section('orth_scale')
        config.add_section('grid_scale')
        config.add_section('xpos')
        config.add_section('ypos')
        config.set('width', 'int', self.width())
        config.set('height', 'int', self.height())
        config.set('xpos', 'int', self.x())
        config.set('ypos', 'int', self.y())
        if hasattr(self, 'model') and isinstance(self.model, VolumeListModel):
            config.set('orth_scale', 'int',
                       int(self.model.get_scale_factor('orth')*100))
            config.set('grid_scale', 'int',
                       int(self.model.get_scale_factor('grid')*100))
        else:
            config.set('orth_scale', 'int',
                       int(self.default_orth_scale_factor * 100))
            config.set('grid_scale', 'int',
                       int(self.default_grid_scale_factor * 100))

        with open(config_file, 'wb') as conf:
            config.write(conf)

    def closeEvent(self, e):
        if self.is_save_configure:
           self._save_configuration()
        e.accept()

    def _create_actions(self):
        """
        Create actions.

        """
        # create a dictionary to store actions info
        self._actions = {}

        # Open template action
        self._actions['add_template'] = QAction(QIcon(os.path.join(
                                                self._icon_dir, 'open.png')),
                                            self.tr("&Open standard volume"),
                                            self)
        self._actions['add_template'].setShortcut(self.tr("Ctrl+O"))
        self._actions['add_template'].triggered.connect(self._add_template)
        self._actions['add_template'].setEnabled(True)

        # Add a new image action
        self._actions['add_image'] = QAction(QIcon(os.path.join(
                                                self._icon_dir, 'add.png')),
                                            self.tr("&Add volume"),
                                            self)
        self._actions['add_image'].setShortcut(self.tr("Ctrl+A"))
        self._actions['add_image'].triggered.connect(self._add_image)
        self._actions['add_image'].setEnabled(True)

        # Remove an image
        self._actions['remove_image'] = QAction(QIcon(os.path.join(
                                                self._icon_dir, 'remove.png')),
                                            self.tr("&Remove volume"),
                                            self)
        self._actions['remove_image'].setShortcut(self.tr("Ctrl+R"))
        self._actions['remove_image'].triggered.connect(self._remove_image)
        self._actions['remove_image'].setEnabled(False)

        # New image
        self._actions['new_image'] = QAction(QIcon(os.path.join(
                                                self._icon_dir, 'create.png')),
                                             self.tr("&New volume"),
                                             self)
        self._actions['new_image'].setShortcut(self.tr("Ctrl+N"))
        self._actions['new_image'].triggered.connect(self.__new_image)
        self._actions['new_image'].setEnabled(False)

        # Save image
        self._actions['save_image'] = QAction(QIcon(os.path.join(
                                                self._icon_dir, 'save.png')),
                                              self.tr("&Save volume as..."),
                                              self)
        self._actions['save_image'].setShortcut(self.tr("Ctrl+S"))
        self._actions['save_image'].triggered.connect(self._save_image)
        self._actions['save_image'].setEnabled(False)

        ## Load Label Config
        #self._actions['ld_lbl'] = QAction('Load Label', self)
        #self._actions['ld_lbl'].triggered.connect(self._ld_lbl)
        #self._actions['ld_lbl'].setEnabled(False)

        ## Load Global Label Config
        #self._actions['ld_glbl'] = QAction('Load Global Label', self)
        #self._actions['ld_glbl'].triggered.connect(self._ld_glbl)
        #self._actions['ld_glbl'].setEnabled(False)

        # Close display
        self._actions['close'] = QAction(self.tr("Close"), self)
        self._actions['close'].setShortcut(self.tr("Ctrl+W"))
        self._actions['close'].triggered.connect(self._close_display)
        self._actions['close'].setEnabled(False)

        # Quit action
        self._actions['quit'] = QAction(QIcon(os.path.join(
                                            self._icon_dir, 'quit.png')),
                                        self.tr("&Quit"),
                                        self)
        self._actions['quit'].setShortcut(self.tr("Ctrl+Q"))
        self._actions['quit'].triggered.connect(self.close)

        # Grid view action
        self._actions['grid_view'] = QAction(QIcon(os.path.join(
                                            self._icon_dir, 'gridview.png')),
                                             self.tr("Lightbox"),
                                             self)
        self._actions['grid_view'].triggered.connect(self._grid_view)
        self._actions['grid_view'].setEnabled(False)

        # Orth view action
        self._actions['orth_view'] = QAction(QIcon(os.path.join(
                                            self._icon_dir, 'orthview.png')),
                                             self.tr("Orthographic"),
                                             self)
        self._actions['orth_view'].triggered.connect(self._orth_view)
        self._actions['orth_view'].setEnabled(False)

        # return original size
        self._actions['original_view'] = QAction(QIcon(os.path.join(
                                        self._icon_dir, 'original_size.png')),
                                                 self.tr("Reset view"),
                                                 self)
        self._actions['original_view'].triggered.connect(self._reset_view)
        self._actions['original_view'].setEnabled(False)

        # whether display the cross hover
        self._actions['cross_hover_view'] = QAction(QIcon(os.path.join(
                                    self._icon_dir, 'cross_hover_enable.png')),
                                                self.tr("Disable cross hover"),
                                                self)
        self._actions['cross_hover_view'].triggered.connect(
                                            self._display_cross_hover)
        self._actions['cross_hover_view'].setEnabled(False)

        # Binaryzation view action
        self._actions['binarization'] = QAction(QIcon(os.path.join(
                                        self._icon_dir, 'binarization.png')),
                                                self.tr("Binarization"),
                                                self)
        self._actions['binarization'].triggered.connect(self._binarization)
        self._actions['binarization'].setEnabled(False)

        # Intersection action
        self._actions['intersect'] = QAction(QIcon(os.path.join(
                                        self._icon_dir, 'intersect.png')),
                                             self.tr("Intersection"),
                                             self)
        self._actions['intersect'].triggered.connect(self._intersect)
        self._actions['intersect'].setEnabled(False)

        # Extract mean time course
        self._actions['meants'] = QAction(QIcon(os.path.join(self._icon_dir,
                                                            'voxel_curve.png')),
                                          self.tr("Extract Mean Time Course"),
                                          self)
        self._actions['meants'].triggered.connect(self._meants)
        self._actions['meants'].setEnabled(False)
        
        # Local Max action
        self._actions['localmax'] = QAction(QIcon(os.path.join(
                                        self._icon_dir, 'localmax.png')),
                                             self.tr("Local Max"),
                                             self)
        self._actions['localmax'].triggered.connect(self._local_max)
        self._actions['localmax'].setEnabled(False)

        # Inversion action
        self._actions['inverse'] = QAction(QIcon(os.path.join(
                                        self._icon_dir, 'inverse.icon')),
                                             self.tr("Inversion"),
                                             self)
        self._actions['inverse'].triggered.connect(self._inverse)
        self._actions['inverse'].setEnabled(False)

        # Smoothing action
        self._actions['smoothing'] = QAction(QIcon(os.path.join(
                                        self._icon_dir, 'smoothing.png')),
                                             self.tr("Smoothing"),
                                             self)
        self._actions['smoothing'].triggered.connect(self._smooth)
        self._actions['smoothing'].setEnabled(False)

        # Region Growing action
        self._actions['region_grow'] = QAction(QIcon(os.path.join(
                                        self._icon_dir, 'grow.png')),
                                             self.tr("Region Growing"),
                                             self)
        self._actions['region_grow'].triggered.connect(self._region_grow)
        self._actions['region_grow'].setEnabled(False)

        # Watershed action
        self._actions['watershed'] = QAction(QIcon(os.path.join(
                                        self._icon_dir, 'watershed.png')),
                                             self.tr("Watershed"),
                                             self)
        self._actions['watershed'].triggered.connect(self._watershed)
        self._actions['watershed'].setEnabled(False)

        # Cluster action
        self._actions['cluster'] = QAction(QIcon(os.path.join(
                                        self._icon_dir, 'cluster.png')),
                                             self.tr("Cluster"),
                                             self)
        self._actions['cluster'].triggered.connect(self._cluster)
        self._actions['cluster'].setEnabled(False)

        # Opening
        self._actions['opening'] = QAction(self.tr("Opening"), self)
        self._actions['opening'].triggered.connect(self._opening)
        self._actions['opening'].setEnabled(False)

        # Binary_erosion view action
        self._actions['binaryerosion'] = QAction(self.tr("Binary Erosion"),
                                                 self)
        self._actions['binaryerosion'].triggered.connect(self._binaryerosion)
        self._actions['binaryerosion'].setEnabled(False)

        # Binary_dilation view action
        self._actions['binarydilation'] = QAction(self.tr("Binary Dilation"),
                                                  self)
        self._actions['binarydilation'].triggered.connect(self._binarydilation)
        self._actions['binarydilation'].setEnabled(False)

        # grey_erosion view action
        self._actions['greyerosion'] = QAction(self.tr("Grey Erosion"),
                                               self)
        self._actions['greyerosion'].triggered.connect(self._greyerosion)
        self._actions['greyerosion'].setEnabled(False)

        # grey_dilation view action
        self._actions['greydilation'] = QAction(self.tr("Grey Dilation"),
                                                self)
        self._actions['greydilation'].triggered.connect(self._greydilation)
        self._actions['greydilation'].setEnabled(False)

        # About software
        self._actions['about_pybp'] = QAction(self.tr("About FreeROI"), self)
        self._actions['about_pybp'].triggered.connect(self._about_pybp)

        # About Qt
        self._actions['about_qt'] = QAction(QIcon(os.path.join(
                                                self._icon_dir, 'qt.png')),
                                            self.tr("About Qt"),
                                            self)
        self._actions['about_qt'].triggered.connect(qApp.aboutQt)

        # Hand
        self._actions['hand'] = QAction(QIcon(os.path.join(
                                            self._icon_dir, 'hand.png')),
                                        self.tr("Hand"),
                                        self)
        self._actions['hand'].triggered.connect(self._hand_enable)
        self._actions['hand'].setCheckable(True)
        self._actions['hand'].setChecked(False)
        self._actions['hand'].setEnabled(False)

        # Cursor
        self._actions['cursor'] = QAction(QIcon(os.path.join(
                                            self._icon_dir, 'cursor.png')),
                                          self.tr("Cursor"),
                                          self)
        self._actions['cursor'].triggered.connect(self._cursor_enable)
        self._actions['cursor'].setCheckable(True)
        self._actions['cursor'].setChecked(True)
        self._actions['cursor'].setEnabled(True)

        # Edit
        self._actions['edit'] = QAction(QIcon(os.path.join(
                                            self._icon_dir, 'edit.png')),
                                         self.tr("Edit"), self)
        self._actions['edit'].triggered.connect(self._roidialog_enable)
        self._actions['edit'].setCheckable(True)
        self._actions['edit'].setChecked(False)

        # Undo
        self._actions['undo'] = QAction(QIcon(os.path.join(
                                            self._icon_dir, 'undo.png')),
                                        self.tr("Undo"),
                                        self)
        self._actions['undo'].triggered.connect(self._undo)

        # Redo
        self._actions['redo'] = QAction(QIcon(os.path.join(
                                            self._icon_dir, 'redo.png')),
                                        self.tr("Redo"),
                                        self)
        self._actions['redo'].triggered.connect(self._redo)

        # sphere and cube roi
        self._actions['regular_roi'] = QAction(QIcon(os.path.join(
                                                self._icon_dir,
                                                'sphere_and_cube.png')),
                                               self.tr("Regular ROI"), self)
        self._actions['regular_roi'].triggered.connect(self._regular_roi)
        self._actions['regular_roi'].setEnabled(False)

        # ROI to Interface
        self._actions['r2i'] = QAction(QIcon(os.path.join(
                                        self._icon_dir, 'r2i.png')),
                                       self.tr("ROI2Interface"), self)
        self._actions['r2i'].triggered.connect(self._r2i)
        self._actions['r2i'].setEnabled(False)

        # Edge detection for ROI
        self._actions['edge_dete'] = QAction(QIcon(os.path.join(
                                                self._icon_dir,
                                                'edge_detection.png')),
                                             self.tr("Edge Detection"), self)
        self._actions['edge_dete'].triggered.connect(self._edge_detection)
        self._actions['edge_dete'].setEnabled(False)

        # ROI Merging
        self._actions['roi_merge'] = QAction(QIcon(os.path.join(
                                                self._icon_dir, 'merging.png')),
                                             self.tr("ROI Merging"), self)
        self._actions['roi_merge'].triggered.connect(self._roi_merge)
        self._actions['roi_merge'].setEnabled(False)

    def _add_toolbar(self):
        """
        Add toolbar.

        """
        # Initialize a spinbox for zoom-scale selection
        self._spinbox = QSpinBox()
        self._spinbox.setMaximum(500)
        self._spinbox.setMinimum(50)
        self._spinbox.setSuffix('%')
        self._spinbox.setSingleStep(10)
        self._spinbox.setValue(self.default_grid_scale_factor * 100)
        self._spinbox.valueChanged.connect(self._set_scale_factor)

        # Add a toolbar
        self._toolbar = self.addToolBar("Tools")
        #self._toolbar.setIconSize(QSize(38,38))
        # Add file actions
        self._toolbar.addAction(self._actions['add_image'])
        self._toolbar.addAction(self._actions['remove_image'])
        self._toolbar.addAction(self._actions['new_image'])
        self._toolbar.addAction(self._actions['save_image'])
        # Add view actions
        self._toolbar.addSeparator()
        self._toolbar.addAction(self._actions['grid_view'])
        self._toolbar.addAction(self._actions['orth_view'])
        self._toolbar.addAction(self._actions['original_view'])
        self._toolbar.addAction(self._actions['cross_hover_view'])
        # Add cursor status
        self._toolbar.addSeparator()
        self._toolbar.addAction(self._actions['hand'])
        self._toolbar.addAction(self._actions['cursor'])
        self._toolbar.addAction(self._actions['edit'])
        # Add undo redo
        self._toolbar.addSeparator()
        self._toolbar.addAction(self._actions['undo'])
        self._toolbar.addAction(self._actions['redo'])

        self._toolbar.addSeparator()
        self._toolbar.addWidget(self._spinbox)

    def _set_scale_factor(self, value):
        """
        Set scale factor.

        """
        value = float(value) / 100
        self.model.set_scale_factor(value, self.image_view.display_type())

    def _add_template(self):
        """
        Open a dialog window and select a template file.

        """
        template_dir = os.path.join(self.label_path, 'standard',
                                    'MNI152_T1_2mm_brain.nii.gz')
        template_name = QFileDialog.getOpenFileName(
            self,
            'Open standard file',
            template_dir,
            'Nifti files (*.nii.gz *.nii)')
        if not template_name.isEmpty():
            template_path = str(template_name)
            self._add_img(template_path)

    def _add_image(self):
        """
        Add new item.

        """
        if self._temp_dir == None:
            temp_dir = QDir.currentPath()
        else:
            temp_dir = self._temp_dir
        file_name = QFileDialog.getOpenFileName(self,
                                                'Add new file',
                                                temp_dir,
                                                'Nifti files (*.nii.gz *.nii)')
        if not file_name.isEmpty():
            file_path = str(file_name)
            self._add_img(file_path)

    def _add_img(self, source, name=None, header=None, view_min=None,
                 view_max=None, alpha=255, colormap='gray'):
        """
        Add image.

        """
        # If model is NULL, then re-initialize it.
        if not self.model:
            self._init_label_config_center()
            self.model = VolumeListModel([], self._label_config_center)
            self.model.set_scale_factor(self.default_grid_scale_factor, 'grid')
            self.model.set_scale_factor(self.default_orth_scale_factor, 'orth')
            self.painter_status = PainterStatus(ViewSettings())

        # Save previous opened directory (except `standard` directory)
        file_path = str(source)
        temp_dir = os.path.dirname(file_path)
        if sys.platform == 'win32':
            if not os.stat(temp_dir) == os.stat(os.path.join(self.label_path,
                                                             'standard')):
                self._temp_dir = temp_dir
        else:
            if not os.path.samefile(temp_dir, os.path.join(self.label_path,
                                                           'standard')):
                self._temp_dir = temp_dir

        if self.model.addItem(file_path, None, name, header, view_min,
                              view_max, alpha, colormap):
            # Take different acions in different case.
            # If only one data in VolumeList, then initialize views.
            if self.model.rowCount() == 1:
                # initialize views
                self.list_view = LayerView(self._label_config_center)
                self.list_view.setModel(self.model)
                self._init_roi_dialog()
                self.image_view = GridView(self.model, self.painter_status)
                # initialize display layout
                central_widget = QWidget()
                layout = QHBoxLayout()
                central_widget.setLayout(layout)
                central_widget.layout().addWidget(self.list_view)
                central_widget.layout().addWidget(self.image_view)
                self.setCentralWidget(central_widget)
                # add a toolbar
                self._add_toolbar()
                #self.setUnifiedTitleAndToolBarOnMac(True)
                # change button status
                self._actions['save_image'].setEnabled(True)
                #self._actions['ld_lbl'].setEnabled(True)
                #self._actions['ld_glbl'].setEnabled(True)
                self._actions['new_image'].setEnabled(True)
                self._actions['close'].setEnabled(True)
                self._actions['orth_view'].setEnabled(True)
                self._actions['cross_hover_view'].setEnabled(True)
                self._actions['original_view'].setEnabled(True)
                self._actions['undo'].setEnabled(False)
                self._actions['redo'].setEnabled(False)
                self._functional_module_set_enabled(True)
                # connect signals with slots
                self.list_view.current_changed.connect(self._update_undo)
                self.list_view.current_changed.connect(self._update_redo)
                self.model.rowsInserted.connect(self._update_remove_image)
                self.model.undo_stack_changed.connect(self._update_undo)
                self.model.redo_stack_changed.connect(self._update_redo)
                # set current volume index
                self.list_view.setCurrentIndex(self.model.index(0))
                # set crosshair as the center of the data
                self.model.set_cross_pos([self.model.getY()/2,
                                          self.model.getX()/2,
                                          self.model.getZ()/2])
                ## Enable cursor tracking
                # self.list_view._list_view.selectionModel().currentChanged.connect(
                #                self._switch_cursor_status)
            elif self.model.rowCount() > 1:
                self._actions['remove_image'].setEnabled(True)
                # set current volume index
                self.list_view.setCurrentIndex(self.model.index(0))
            self.is_save_configure = True
        else:
            QMessageBox.information(self,'FreeROI', 'Cannot load ' + "filename" + '.')

    def __new_image(self):
        self._new_image()

    def _update_remove_image(self):
        if self.model.rowCount() == 1:
            self._actions['remove_image'].setEnabled(False)
        else:
            self._actions['remove_image'].setEnabled(True)

    def _new_image(self, data=None, name=None, colormap=None):
        """
        Create a new volume for brain parcellation.

        """
        if colormap is None:
            colormap = self._label_config_center.get_first_label_config()
        self.model.new_image(data, name, None, colormap)
        self.list_view.setCurrentIndex(self.model.index(0))

        # change button status
        self._actions['remove_image'].setEnabled(True)

    def new_image_action(self):
        self._actions['remove_image'].setEnabled(True)

    def _remove_image(self):
        """
        Remove current image.

        """
        row = self.list_view.currentRow()
        self.model.delItem(row)
        if self.model.rowCount() == 1:
            self._actions['remove_image'].setEnabled(False)

    def _save_image(self):
        """
        Save image as a nifti file.

        """
        index = self.model.currentIndex()
        if self._temp_dir == None:
            temp_dir = str(QDir.currentPath())
        else:
            temp_dir = self._temp_dir
        file_path = os.path.join(temp_dir,
                                 str(self.model.data(index, Qt.DisplayRole))+'.nii.gz')
        path = QFileDialog.getSaveFileName(
            self,
            'Save image as...',
            file_path,
            'Nifti files (*.nii.gz *.nii)')
        if not path.isEmpty():
            self._temp_dir = os.path.dirname(str(path))
            self.model._data[index.row()].save2nifti(str(path))

    def _close_display(self):
        """
        Close current display.

        """
        self.setCentralWidget(QWidget())
        self._set_scale_factor(self.default_grid_scale_factor)
        self.removeToolBar(self._toolbar)
        self.model = None
        self._actions['add_template'].setEnabled(True)
        self._actions['add_image'].setEnabled(True)
        self._actions['remove_image'].setEnabled(False)
        self._actions['new_image'].setEnabled(False)
        self._actions['save_image'].setEnabled(False)
        #self._actions['ld_glbl'].setEnabled(False)
        #self._actions['ld_lbl'].setEnabled(False)
        self._actions['close'].setEnabled(False)
        self._actions['grid_view'].setEnabled(False)
        self._actions['orth_view'].setEnabled(False)
        self._actions['cross_hover_view'].setEnabled(False)
        self._actions['original_view'].setEnabled(False)
        self._functional_module_set_enabled(False)

    def _about_pybp(self):
        """
        About software.

        """
        QMessageBox.about(self, self.tr("About FreeROI"),
                          self.tr("<p><b>FreeROI</b> is a versatile image "
                                  "processing software developed for "
                                  "neuroimaging data.</p>"
                                  "<p>Its goal is to provide a user-friendly "
                                  "interface for neuroimaging researchers "
                                  "to visualize and analyze their data, "
                                  "especially in defining region of interest "
                                  "(ROI) for ROI analysis.</p>"
                                  "<p>Written by: Lijie Huang, Zetian Yang, "
                                  "Guangfu Zhou, Zhaoguo Liu, Xiaobin Dang, "
                                  "Xiangzhen Kong, Xu Wang, and Zonglei Zhen."
                                  "</p>"
                                  "<p><b>FreeROI</b> is under Revised BSD "
                                  "License.</p>"
                                  "<p>Version: " + __version__ + "</p>"
                                  "<p>Copyright(c) 2012-2014 "
                                  "Neuroinformatic Team in LiuLab "
                                  "from Beijing Normal University</p>"
                                  "<p></p>"
                                  "<p>Please report bugs to:</p>"
                                  "<p><b>[email protected]</b></p>"))

    def _create_menus(self):
        """Create menus."""
        self.file_menu = self.menuBar().addMenu(self.tr("File"))
        self.file_menu.addAction(self._actions['add_image'])
        self.file_menu.addAction(self._actions['add_template'])
        self.file_menu.addAction(self._actions['save_image'])
        #self.file_menu.addSeparator()
        #self.file_menu.addAction(self._actions['ld_lbl'])
        #self.file_menu.addAction(self._actions['ld_glbl'])
        self.file_menu.addSeparator()
        self.file_menu.addAction(self._actions['close'])
        self.file_menu.addAction(self._actions['quit'])

        self.volume_menu = self.menuBar().addMenu(self.tr("Volume"))
        self.volume_menu.addAction(self._actions['new_image'])
        self.volume_menu.addAction(self._actions['remove_image'])

        self.view_menu = self.menuBar().addMenu(self.tr("View"))
        self.view_menu.addAction(self._actions['grid_view'])
        self.view_menu.addAction(self._actions['orth_view'])
        self.view_menu.addAction(self._actions['original_view'])
        self.view_menu.addAction(self._actions['cross_hover_view'])

        self.tool_menu = self.menuBar().addMenu(self.tr("Tools"))
        basic_tools = self.tool_menu.addMenu(self.tr("Basic Tools"))
        basic_tools.addAction(self._actions['binarization'])
        basic_tools.addAction(self._actions['intersect'])
        basic_tools.addAction(self._actions['localmax'])
        basic_tools.addAction(self._actions['inverse'])
        basic_tools.addAction(self._actions['smoothing'])
        basic_tools.addAction(self._actions['meants'])
        segment_tools = self.tool_menu.addMenu(self.tr("Segmentation"))
        segment_tools.addAction(self._actions['region_grow'])
        segment_tools.addAction(self._actions['watershed'])
        segment_tools.addAction(self._actions['cluster'])
        roi_tools = self.tool_menu.addMenu(self.tr("ROI Tools"))
        roi_tools.addAction(self._actions['edge_dete'])
        roi_tools.addAction(self._actions['roi_merge'])
        roi_tools.addAction(self._actions['regular_roi'])
        roi_tools.addAction(self._actions['r2i'])
        morphological_tools = self.tool_menu.addMenu(
                                    self.tr("Morphological Processing"))
        morphological_tools.addAction(self._actions['opening'])
        morphological_tools.addAction(self._actions['binarydilation'])
        morphological_tools.addAction(self._actions['binaryerosion'])
        morphological_tools.addAction(self._actions['greydilation'])
        morphological_tools.addAction(self._actions['greyerosion'])

        self.help_menu = self.menuBar().addMenu(self.tr("Help"))
        self.help_menu.addAction(self._actions['about_pybp'])
        self.help_menu.addAction(self._actions['about_qt'])

    def _cursor_enable(self):
        """
        Cursor enabled.

        """
        if self._actions['cursor'].isChecked():
            self._actions['cursor'].setChecked(True)
            if isinstance(self.image_view, OrthView):
                self._actions['hand'].setChecked(False)
            if self.roidialog.isVisible():
                self._roidialog_disable()

            self.painter_status.set_draw_settings(ViewSettings())
            self.image_view.set_cursor(Qt.ArrowCursor)
            self.image_view.set_label_mouse_tracking(True)
        else:
            self._actions['cursor'].setChecked(True)

    def _voxel_edit_enable(self):
        """
        Brush enabled.

        """
        self._label_config_center.set_is_roi_edit(False)
        self.painter_status.set_draw_settings(self._label_config_center)
        self.image_view.set_cursor(Qt.CrossCursor)
        self.image_view.set_label_mouse_tracking(False)

    def _roi_edit_enable(self):
        """
        ROI brush enabled.

        """
        self._label_config_center.set_is_roi_edit(True)
        self.painter_status.set_draw_settings(self._label_config_center)
        self.image_view.set_cursor(Qt.CrossCursor)
        self.image_view.set_label_mouse_tracking(False)

    def _roidialog_enable(self):
        if self._actions['edit'].isChecked():
            self._actions['cursor'].setChecked(False)
            if isinstance(self.image_view, OrthView):
                self._actions['hand'].setChecked(False)
            self._actions['edit'].setChecked(True)
            self.roidialog._voxel_clicked()
            self.roidialog.show()
        else:
            self._actions['edit'].setChecked(True)

    def _roi_batch_enable(self):
        self.image_view.set_label_mouse_tracking(False)
        self._label_config_center.set_is_roi_edit(False)
        self.painter_status.set_draw_settings(self.roidialog)

    def _roidialog_disable(self):
        self.roidialog.hide()
        self._actions['edit'].setChecked(False)

    def _hand_enable(self):
        """
        Hand enabled.

        """
        if self._actions['hand'].isChecked():
            self._actions['cursor'].setChecked(False)
            self._actions['hand'].setChecked(True)
 
            if hasattr(self, 'roidialog'):
                self._roidialog_disable()

            self.painter_status.set_draw_settings(MoveSettings())
            self.image_view.set_cursor(Qt.OpenHandCursor)
            self.image_view.set_label_mouse_tracking(True)
        else:
            self._actions['hand'].setChecked(True)

    def _switch_cursor_status(self):
        self._actions['cursor'].setChecked(True)
        self._cursor_enable()

    def _update_undo(self):
        if self.model.current_undo_available():
            self._actions['undo'].setEnabled(True)
        else:
            self._actions['undo'].setEnabled(False)

    def _update_redo(self):
        if self.model.current_redo_available():
            self._actions['redo'].setEnabled(True)
        else:
            self._actions['redo'].setEnabled(False)

    def _init_roi_dialog(self):
        """
        Initialize ROI Dialog

        """
        self.roidialog = ROIDialog(self.model, self._label_config_center)
        self.roidialog.voxel_edit_enabled.connect(self._voxel_edit_enable)
        self.roidialog.roi_edit_enabled.connect(self._roi_edit_enable)
        self.roidialog.roi_batch_enabled.connect(self._roi_batch_enable)
        self.list_view._list_view.selectionModel().currentChanged.connect(
                self.roidialog.clear_rois)

    def _init_label_config_center(self):
        """
        Initialize LabelConfigCenter

        """
        lbl_path = os.path.join(self.label_config_dir,
                                '*.' + self.label_config_suffix)
        label_configs = glob.glob(lbl_path)
        self.label_configs = map(LabelConfig, label_configs)
        self._label_config_center = LabelConfigCenter(self.label_configs)

    def _get_label_config(self, file_path):
        """
        Get label config file.

        """
        # Get label config file
        dir = os.path.dirname(file_path)
        file = os.path.basename(file_path)
        split_list = file.split('.')
        nii_index = split_list.index('nii')
        file = ''.join(split_list[:nii_index])
        config_file = os.path.join(file, 'lbl')
        if os.path.isfile(config_file):
            label_config = LabelConfig(config_file, False)
        else:
            label_config = self.label_config

        return label_config

    def _undo(self):
        self.model.undo_current_image()

    def _redo(self):
        self.model.redo_current_image()

    def _regular_roi(self):
        regular_roi_dialog = RegularROIDialog(self.model)
        regular_roi_dialog.exec_()

    def _edge_detection(self):
        new_dialog = Edge_detectionDialog(self.model)
        new_dialog.exec_()

    def _roi_merge(self):
        new_dialog = ROIMergeDialog(self.model)
        new_dialog.exec_()

    def _r2i(self):
        new_dialog = Roi2gwmiDialog(self.model)
        new_dialog.exec_()

    def _opening(self):
        new_dialog = OpenDialog(self.model)
        new_dialog.exec_()

    def _ld_lbl(self):
        file_name = QFileDialog.getOpenFileName(self,
                                                'Load Label File',
                                                QDir.currentPath(),
                                                "Label files (*.lbl)")
        if file_name:
            label_config = LabelConfig(str(file_name), False)
            self.model.set_cur_label(label_config)

    def _ld_glbl(self):
        file_name = QFileDialog.getOpenFileName(self,
                                                'Load Label File',
                                                QDir.currentPath(),
                                                "Label files (*.lbl)")
        if file_name:
            label_config = LabelConfig(str(file_name), True)
            self.model.set_global_label(label_config)

    def _grid_view(self):
        """
        Grid view option.

        """
        self._actions['grid_view'].setEnabled(False)
        self._actions['orth_view'].setEnabled(True)
        self._actions['hand'].setEnabled(False)
        self._actions['cursor'].trigger()

        self.centralWidget().layout().removeWidget(self.image_view)
        self.image_view.set_display_type('grid')
        self.model.scale_changed.disconnect()
        self.model.repaint_slices.disconnect()
        self.model.cross_pos_changed.disconnect(self.image_view.update_cross_pos)
        self.image_view.deleteLater()
        self._spinbox.setValue(100 * self.model.get_scale_factor('grid'))
        self.image_view = GridView(self.model, self.painter_status,
                                   self._gridview_vertical_scrollbar_position)
        self.centralWidget().layout().addWidget(self.image_view)

    def _orth_view(self):
        """
        Orth view option.
        """


        self._actions['orth_view'].setEnabled(False)
        self._actions['grid_view'].setEnabled(True)
        self._actions['hand'].setEnabled(True)
        self._actions['cursor'].trigger()

        self._gridview_vertical_scrollbar_position = \
            self.image_view.get_vertical_srollbar_position()
        self.centralWidget().layout().removeWidget(self.image_view)
        self.image_view.set_display_type('orth')
        self.model.scale_changed.disconnect()
        self.model.repaint_slices.disconnect()
        self.model.cross_pos_changed.disconnect(self.image_view.update_cross_pos)
        self.image_view.deleteLater()
        self._spinbox.setValue(100 * self.model.get_scale_factor('orth'))
        self.image_view = OrthView(self.model, self.painter_status)
        self.centralWidget().layout().addWidget(self.image_view)

    def _display_cross_hover(self):
        if self.model._display_cross:
            self.model.set_cross_status(False)
            self._actions['cross_hover_view'].setText('Enable cross hover')
            self._actions['cross_hover_view'].setIcon(QIcon(os.path.join(self._icon_dir,'cross_hover_disable.png')))
        else:
            self.model.set_cross_status(True)
            self._actions['cross_hover_view'].setText('Disable cross hover')
            self._actions['cross_hover_view'].setIcon(QIcon(os.path.join(self._icon_dir,'cross_hover_enable.png')))

    def _reset_view(self):
        """
        Reset view parameters.

        """
        if self.image_view.display_type() == 'orth':
            if not self.model.get_scale_factor('orth') == \
                    self.default_orth_scale_factor:
                self._spinbox.setValue(100 * self.default_orth_scale_factor)
            self.image_view.reset_view()
        elif self.image_view.display_type() == 'grid':
            if not self.model.get_scale_factor('grid') == \
                    self.default_grid_scale_factor:
                self._spinbox.setValue(100 * self.default_grid_scale_factor)

    def _binarization(self):
        binarization_dialog = BinarizationDialog(self.model)
        binarization_dialog.exec_()

    def _binaryerosion(self):
        binaryerosion_dialog = BinaryerosionDialog(self.model)
        binaryerosion_dialog.exec_()

    def _binarydilation(self):
        binarydilation_dialog = BinarydilationDialog(self.model)
        binarydilation_dialog.exec_()

    def _greyerosion(self):
        greyerosiondialog = GreyerosionDialog(self.model)
        greyerosiondialog.exec_()

    def _greydilation(self):
        greydilation_dialog = GreydilationDialog(self.model)
        greydilation_dialog.exec_()

    def _intersect(self):
        intersect_dialog = IntersectDialog(self.model)
        intersect_dialog.exec_()

    def _meants(self):
        new_dialog = MeanTSDialog(self.model)
        new_dialog.exec_()

    def _local_max(self):
        new_dialog = LocalMaxDialog(self.model, self)
        new_dialog.exec_()

    def _inverse(self):
        inverse_image(self.model)

    def _smooth(self):
        new_dialog = SmoothingDialog(self.model)
        new_dialog.exec_()

    def _region_grow(self):
        new_dialog = GrowDialog(self.model, self)
        new_dialog.exec_()

    def _watershed(self):
        new_dialog = WatershedDialog(self.model, self)
        new_dialog.exec_()

    def _cluster(self):
        new_dialog = ClusterDialog(self.model)
        new_dialog.exec_()

    def _functional_module_set_enabled(self, status):
        self._actions['binarization'].setEnabled(status)
        self._actions['intersect'].setEnabled(status)
        self._actions['meants'].setEnabled(status)
        self._actions['localmax'].setEnabled(status)
        self._actions['inverse'].setEnabled(status)
        self._actions['smoothing'].setEnabled(status)
        self._actions['region_grow'].setEnabled(status)
        self._actions['watershed'].setEnabled(status)
        self._actions['cluster'].setEnabled(status)
        self._actions['opening'].setEnabled(status)
        self._actions['binarydilation'].setEnabled(status)
        self._actions['binaryerosion'].setEnabled(status)
        self._actions['greydilation'].setEnabled(status)
        self._actions['greyerosion'].setEnabled(status)
        self._actions['regular_roi'].setEnabled(status)
        self._actions['r2i'].setEnabled(status)
        self._actions['edge_dete'].setEnabled(status)
        self._actions['roi_merge'].setEnabled(status)
Example #3
0
    def _add_img(self, source, name=None, header=None, view_min=None,
                 view_max=None, alpha=255, colormap='gray'):
        """
        Add image.

        """
        # If model is NULL, then re-initialize it.
        if not self.model:
            self._init_label_config_center()
            self.model = VolumeListModel([], self._label_config_center)
            self.model.set_scale_factor(self.default_grid_scale_factor, 'grid')
            self.model.set_scale_factor(self.default_orth_scale_factor, 'orth')
            self.painter_status = PainterStatus(ViewSettings())

        # Save previous opened directory (except `standard` directory)
        file_path = source
        if sys.platform == 'win32':
            temp_dir = os.path.dirname(unicode(file_path, 'gb2312'))
            if not os.stat(temp_dir) == os.stat(os.path.join(self.label_path,
                                                             'standard')):
                self._temp_dir = temp_dir
        else:
            temp_dir = os.path.dirname(file_path)
            if not os.path.samefile(temp_dir, os.path.join(self.label_path,
                                                           'standard')):
                self._temp_dir = temp_dir

        if self.model.addItem(file_path, None, name, header, view_min,
                              view_max, alpha, colormap):
            # Take different acions in different case.
            # If only one data in VolumeList, then initialize views.
            if self.model.rowCount() == 1:
                # initialize views
                self.list_view = LayerView(self._label_config_center)
                self.list_view.setModel(self.model)
                self._init_roi_dialog()
                self.image_view = GridView(self.model, self.painter_status)
                # initialize display layout
                central_widget = QWidget()
                layout = QHBoxLayout()
                central_widget.setLayout(layout)
                central_widget.layout().addWidget(self.list_view)
                central_widget.layout().addWidget(self.image_view)
                self.setCentralWidget(central_widget)
                # add a toolbar
                self._add_toolbar()
                #self.setUnifiedTitleAndToolBarOnMac(True)
                # change button status
                self._actions['save_image'].setEnabled(True)
                #self._actions['ld_lbl'].setEnabled(True)
                #self._actions['ld_glbl'].setEnabled(True)
                self._actions['new_image'].setEnabled(True)
                self._actions['close'].setEnabled(True)
                self._actions['orth_view'].setEnabled(True)
                self._actions['cross_hover_view'].setEnabled(True)
                self._actions['original_view'].setEnabled(True)
                self._actions['undo'].setEnabled(False)
                self._actions['redo'].setEnabled(False)
                self._functional_module_set_enabled(True)
                # connect signals with slots
                self.list_view.current_changed.connect(self._update_undo)
                self.list_view.current_changed.connect(self._update_redo)
                self.model.rowsInserted.connect(self._update_remove_image)
                self.model.undo_stack_changed.connect(self._update_undo)
                self.model.redo_stack_changed.connect(self._update_redo)
                # set current volume index
                self.list_view.setCurrentIndex(self.model.index(0))
                # set crosshair as the center of the data
                self.model.set_cross_pos([self.model.getY()/2,
                                          self.model.getX()/2,
                                          self.model.getZ()/2])
                ## Enable cursor tracking
                # self.list_view._list_view.selectionModel().currentChanged.connect(
                #                self._switch_cursor_status)
            elif self.model.rowCount() > 1:
                self._actions['remove_image'].setEnabled(True)
                # set current volume index
                self.list_view.setCurrentIndex(self.model.index(0))
            self.is_save_configure = True
        else:
            QMessageBox.information(self,'FreeROI', 'Cannot load ' + \
                                    file_path + ': due to mismatch data size.')
Example #4
0
File: main.py Project: xtao/FreeROI
class BpMainWindow(QMainWindow):
    """Class BpMainWindow provides UI interface of FreeROI.

    Example:
    --------

    >>> from PyQt4.QtGui import QApplication
    >>> import main
    >>> app = QApplication([])
    >>> win = main.BpMainWindow()
    ......
    >>> win.show()
    >>> app.exec_()

    """

    def __init__(self, parent=None):
        """
        Initialize an instance of BpMainWindow.
        
        """
        # Inherited from QMainWindow
        if sys.platform == 'darwin':
            # Workaround for Qt issue on OS X that causes QMainWindow to
            # hide when adding QToolBar, see
            # https://bugreports.qt-project.org/browse/QTBUG-4300
            super(BpMainWindow, self).__init__(parent, Qt.MacWindowToolBarButtonHint)
        else:
            super(BpMainWindow, self).__init__(parent)

        # temporary variable
        self._temp_dir = None
        self.is_save_configure = False

        # pre-define a model variable
        self.model = None

    def config_extra_settings(self, data_dir):
        """
        Set data directory and update some configurations.

        """
        # load data directory configuration
        self.label_path = data_dir
        self.label_config_dir = os.path.join(self.label_path, 'labelconfig')
        self.label_config_suffix = 'lbl'

        # set icon configuration
        self._icon_dir=get_icon_dir()

        # set window title
        self.setWindowTitle('FreeROI')
        #self.resize(1280, 1000)
        self.center()
        # set window icon
        self.setWindowIcon(QIcon(os.path.join(self._icon_dir,'icon.png')))

        self._init_configuration()
        self._init_label_config_center()

        # create actions
        self._create_actions()

        # create menus
        self._create_menus()

    def center(self):
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    def _init_configuration(self):
        config_file = os.path.expanduser('~/.pybp.conf')
        if os.path.exists(config_file):
            config = ConfigParser.RawConfigParser()
            config.read(config_file)
            self.window_width = config.getint('width', 'int')
            self.window_height = config.getint('height', 'int')
            self.orth_scale_factor = config.getint('orth_scale', 'int')
            self.grid_scale_factor = config.getint('grid_scale', 'int')
            self.window_xpos = config.getint('xpos', 'int')
            self.window_ypos = config.getint('ypos', 'int')

            self.resize(self.window_width, self.window_height)
            self.move(self.window_xpos, self.window_ypos)
            self.default_orth_scale_factor = float(self.orth_scale_factor) / 100
            self.default_grid_scale_factor = float(self.grid_scale_factor) / 100
        else:
            self.setWindowState(Qt.WindowMaximized)
            self.default_orth_scale_factor = 1.0
            self.default_grid_scale_factor = 2.0

    def _save_configuration(self):
        config_file = os.path.expanduser('~/.pybp.conf')

        config = ConfigParser.RawConfigParser()
        config.add_section('width')
        config.add_section('height')
        config.add_section('orth_scale')
        config.add_section('grid_scale')
        config.add_section('xpos')
        config.add_section('ypos')
        config.set('width', 'int', self.width())
        config.set('height', 'int', self.height())
        config.set('xpos', 'int', self.x())
        config.set('ypos', 'int', self.y())
        if hasattr(self, 'model'):
            config.set('orth_scale', 'int', int(self.model.get_scale_factor('orth')*100))
            config.set('grid_scale', 'int', int(self.model.get_scale_factor('grid')*100))
        else:
            config.set('orth_scale', 'int', int(self.default_orth_scale_factor * 100))
            config.set('grid_scale', 'int', int(self.default_grid_scale_factor * 100))

        with open(config_file, 'wb') as conf:
            config.write(conf)

    def closeEvent(self, e):
        if self.is_save_configure:
           self._save_configuration()
        e.accept()

    def _create_actions(self):
        """
        Create actions.

        """
        # create a dictionary to store actions info
        self._actions = {}

        # Open template action
        self._actions['add_template'] = QAction(QIcon(os.path.join(
            self._icon_dir, 'open.png')),
                                                self.tr("&Open standard volume"),
                                                self)
        self._actions['add_template'].setShortcut(self.tr("Ctrl+O"))
        self._actions['add_template'].triggered.connect(self._add_template)
        self._actions['add_template'].setEnabled(True)

        # Add a new image action
        self._actions['add_image'] = QAction(QIcon(os.path.join(
            self._icon_dir,'add.png')),
                                             self.tr("&Add volume"), self)
        self._actions['add_image'].setShortcut(self.tr("Ctrl+A"))
        self._actions['add_image'].triggered.connect(self._add_image)
        self._actions['add_image'].setEnabled(True)

        # Remove an image
        self._actions['remove_image'] = QAction(QIcon(os.path.join(
            self._icon_dir, 'remove.png')),
                                                self.tr("&Remove volume"),
                                                self)
        self._actions['remove_image'].setShortcut(self.tr("Ctrl+R"))
        self._actions['remove_image'].triggered.connect(self._remove_image)
        self._actions['remove_image'].setEnabled(False)

        # New image
        self._actions['new_image'] = QAction(QIcon(os.path.join(
            self._icon_dir, 'create.png')),
                                             self.tr("&New volume"), self)
        self._actions['new_image'].setShortcut(self.tr("Ctrl+N"))
        self._actions['new_image'].triggered.connect(self.__new_image)
        self._actions['new_image'].setEnabled(False)

        # Save image
        self._actions['save_image'] = QAction(QIcon(os.path.join(
            self._icon_dir, 'save.png')),
                                              self.tr("&Save volume as..."),
                                              self)
        self._actions['save_image'].setShortcut(self.tr("Ctrl+S"))
        self._actions['save_image'].triggered.connect(self._save_image)
        self._actions['save_image'].setEnabled(False)

        # Load Label Config
        self._actions['ld_lbl'] = QAction('Load Label', self)
        self._actions['ld_lbl'].triggered.connect(self._ld_lbl)
        self._actions['ld_lbl'].setEnabled(False)

        # Load Global Label Config
        self._actions['ld_glbl'] = QAction('Load Global Label', self)
        self._actions['ld_glbl'].triggered.connect(self._ld_glbl)
        self._actions['ld_glbl'].setEnabled(False)

        # Close display
        self._actions['close'] = QAction(self.tr("Close"), self)
        #self._actions['close'].setShortcut(self.tr("Ctrl+W"))
        self._actions['close'].triggered.connect(self._close_display)
        self._actions['close'].setEnabled(False)

        # Quit action
        self._actions['quit'] = QAction(QIcon(os.path.join(
            self._icon_dir, 'quit.png')),
                                        self.tr("&Quit"), self)
        self._actions['quit'].setShortcut(self.tr("Ctrl+Q"))
        self._actions['quit'].triggered.connect(self.close)

        # Grid view action
        self._actions['grid_view'] = QAction(QIcon(os.path.join(
            self._icon_dir, 'gridview.png')),
                                             self.tr("Lightbox"), self)
        self._actions['grid_view'].triggered.connect(self._grid_view)
        self._actions['grid_view'].setEnabled(False)

        # Orth view action
        self._actions['orth_view'] = QAction(QIcon(os.path.join(
            self._icon_dir, 'orthview.png')),
                                             self.tr("Orthographic"), self)
        self._actions['orth_view'].triggered.connect(self._orth_view)
        self._actions['orth_view'].setEnabled(False)

        # return original size
        self._actions['original_view'] = QAction(QIcon(os.path.join(
            self._icon_dir,
            'original_size.png')),
                                                 self.tr("Reset view"), self)
        self._actions['original_view'].triggered.connect(self._reset_view)
        self._actions['original_view'].setEnabled(False)

        # dwhether display the cross hover
        self._actions['cross_hover_view'] = QAction(QIcon(os.path.join(
            self._icon_dir,
            'cross_hover_enable.png')),
                                                    self.tr("Disable cross hover"), self)
        self._actions['cross_hover_view'].triggered.connect(self._display_cross_hover)
        self._actions['cross_hover_view'].setEnabled(False)

        #-- generated by zgf
        # Binaryzation view action
        self._actions['binaryzation'] = QAction(QIcon(os.path.join(
            self._icon_dir, 'binaryzation.png')),
                                                self.tr("Binaryzation"), self)
        self._actions['binaryzation'].triggered.connect(self._binaryzation)
        self._actions['binaryzation'].setEnabled(False)

        # Binary_erosion view action
        self._actions['binaryerosion'] = QAction(QIcon(os.path.join(
            self._icon_dir, 'binary_erosion.png')),
                                                 self.tr("Binaryerosion"), self)
        self._actions['binaryerosion'].triggered.connect(self._binaryerosion)
        self._actions['binaryerosion'].setEnabled(False)

        # Binary_dilation view action
        self._actions['binarydilation'] = QAction(QIcon(os.path.join(
            self._icon_dir, 'binary_dilation.png')),
                                                  self.tr("Binarydilation"), self)
        self._actions['binarydilation'].triggered.connect(self._binarydilation)
        self._actions['binarydilation'].setEnabled(False)

        # grey_erosion view action
        self._actions['greyerosion'] = QAction(QIcon(os.path.join(
            self._icon_dir, 'grey_erosion.png')),
                                               self.tr("Greyerosion"), self)
        self._actions['greyerosion'].triggered.connect(self._greyerosion)
        self._actions['greyerosion'].setEnabled(False)

        # grey_dilation view action
        self._actions['greydilation'] = QAction(QIcon(os.path.join(
            self._icon_dir, 'grey_dilation.png')),
                                                self.tr("Greydilation"), self)
        self._actions['greydilation'].triggered.connect(self._greydilation)
        self._actions['greydilation'].setEnabled(False)

        # voxel time point curve view action
        self._actions['roiorvoxelcurve'] = QAction(QIcon(os.path.join(
            self._icon_dir, 'voxel_time_point_curve.png')),
                                                       self.tr("Roiorvoxelcurve"), self)
        self._actions['roiorvoxelcurve'].triggered.connect(self._roiorvoxelcurve)
        self._actions['roiorvoxelcurve'].setEnabled(False)

        # three demension intensity view action
        self._actions['volumeintensity'] = QAction(QIcon(os.path.join(
            self._icon_dir, 'volumeintensity.png')),
                                                   self.tr("Volume Intensity"), self)
        self._actions['volumeintensity'].triggered.connect(self._volumeintensity)
        self._actions['volumeintensity'].setEnabled(False)
        #-- generated by zgf


        # About software
        self._actions['about_pybp'] = QAction(self.tr("About FreeROI"), self)
        self._actions['about_pybp'].triggered.connect(self._about_pybp)

        # About Qt
        self._actions['about_qt'] = QAction(QIcon(os.path.join(
            self._icon_dir, 'qt.png')),
                                            self.tr("About Qt"), self)
        self._actions['about_qt'].triggered.connect(qApp.aboutQt)

        # Hand
        self._actions['hand'] = QAction(QIcon(os.path.join(self._icon_dir,
                                                           'hand.png')),
                                        self.tr("Hand"), self)
        self._actions['hand'].triggered.connect(self._hand_enable)
        self._actions['hand'].setCheckable(True)
        self._actions['hand'].setChecked(False)
        self._actions['hand'].setEnabled(False)

        # Cursor
        self._actions['cursor'] = QAction(QIcon(os.path.join(
            self._icon_dir, 'cursor.png')),
                                          self.tr("Cursor"), self)
        self._actions['cursor'].triggered.connect(self._cursor_enable)
        self._actions['cursor'].setCheckable(True)
        self._actions['cursor'].setChecked(True)
        self._actions['cursor'].setEnabled(True)

        # Brush
        self._actions['brush'] = QAction(QIcon(os.path.join(
            self._icon_dir, 'brush.png')),
                                         self.tr("Voxel Edit"), self)
        self._actions['brush'].triggered.connect(self._brush_enable)
        self._actions['brush'].setCheckable(True)
        self._actions['brush'].setChecked(False)

        # ROI Brush
        self._actions['roibrush'] = QAction(QIcon(os.path.join(
            self._icon_dir, 'roibrush.png')),
                                            self.tr("ROI Edictor"), self)
        self._actions['roibrush'].triggered.connect(self._roibrush_enable)
        self._actions['roibrush'].setCheckable(True)
        self._actions['roibrush'].setChecked(False)

        self._update_brush()

        self._actions['roidialog'] = QAction(QIcon(os.path.join(
            self._icon_dir,'roitool.png')),
                                             self.tr("ROI Toolset"), self)
        self._actions['roidialog'].setCheckable(True)
        self._actions['roidialog'].triggered.connect(self._roidialog_enable)

        # Undo
        self._actions['undo'] = QAction(QIcon(os.path.join(
            self._icon_dir, 'undo.png')),
                                        self.tr("Undo"), self)
        self._actions['undo'].triggered.connect(self._undo)

        # Redo
        self._actions['redo'] = QAction(QIcon(os.path.join(
            self._icon_dir, 'redo.png')),
                                        self.tr("Redo"), self)
        self._actions['redo'].triggered.connect(self._redo)

        # sphere and cube roi
        self._actions['regular_roi'] = QAction(QIcon(os.path.join(
            self._icon_dir, 'sphere_and_cube.png')),
                                               self.tr("Regular ROI"), self)
        self._actions['regular_roi'].triggered.connect(self._regular_roi)
        self._actions['regular_roi'].setEnabled(False)

        # Intersect
        self._actions['intersect'] = QAction(QIcon(os.path.join(
            self._icon_dir, 'intersect.png')),
                                             self.tr("Intersection"), self)
        self._actions['intersect'].triggered.connect(self._intersect)
        self._actions['intersect'].setEnabled(False)

        # ROI to Interface
        self._actions['r2i'] = QAction(QIcon(os.path.join(
        self._icon_dir, 'r2i.png')),
                                        self.tr("ROI2Interface"), self)
        
        self._actions['r2i'].triggered.connect(self._r2i)
        self._actions['r2i'].setEnabled(False)

        # Auto Labeling
        self._actions['auto_label'] = QAction(QIcon(os.path.join(
            self._icon_dir, 'auto_labeling.png')),
                                              self.tr("Auto Labeling"), self)
        self._actions['auto_label'].triggered.connect(self._auto_label)
        self._actions['auto_label'].setEnabled(False)

        # Open
        self._actions['open'] = QAction(QIcon(os.path.join(
            self._icon_dir, 'opening.png')),
                                        self.tr("Opening"), self)
        self._actions['open'].setShortcut("Ctrl+P")
        self._actions['open'].triggered.connect(self._open)
        self._actions['open'].setEnabled(False)


    def _add_toolbar(self):
        """
        Add toolbar.

        """
        # Initialize a spinbox for zoom-scale selection
        self._spinbox = QSpinBox()
        self._spinbox.setMaximum(500)
        self._spinbox.setMinimum(50)
        self._spinbox.setSuffix('%')
        self._spinbox.setSingleStep(10)
        self._spinbox.setValue(self.default_grid_scale_factor * 100)
        self._spinbox.valueChanged.connect(self._set_scale_factor)

        # Add a toolbar
        #self._toolbar = QToolBar()
        self._toolbar = self.addToolBar("Tools")
        self._toolbar.setIconSize(QSize(38,38))
        # Add file actions
        self._toolbar.addAction(self._actions['add_image'])
        self._toolbar.addAction(self._actions['remove_image'])
        self._toolbar.addAction(self._actions['new_image'])
        self._toolbar.addAction(self._actions['save_image'])
        # Add view actions
        self._toolbar.addSeparator()
        self._toolbar.addAction(self._actions['grid_view'])
        self._toolbar.addAction(self._actions['orth_view'])
        self._toolbar.addAction(self._actions['original_view'])
        self._toolbar.addAction(self._actions['cross_hover_view'])
        # Add draw tools
        # self._toolbar.addSeparator()
        # self._toolbar.addAction(self._actions['hand'])
        # self._toolbar.addAction(self._actions['cursor'])
        # self._toolbar.addAction(self._actions['brush'])
        # self._toolbar.addAction(self._actions['roibrush'])
        # Add undo redo
        self._toolbar.addSeparator()
        self._toolbar.addAction(self._actions['undo'])
        self._toolbar.addAction(self._actions['redo'])
        # Add automatic tools
        self._toolbar.addSeparator()
        self._toolbar.addAction(self._actions['regular_roi'])
        self._toolbar.addAction(self._actions['intersect'])
        self._toolbar.addAction(self._actions['open'])
        self._toolbar.addAction(self._actions['r2i'])
        self._toolbar.addAction(self._actions['roidialog'])

        self._toolbar.addSeparator()
        self._toolbar.addWidget(self._spinbox)

        #self.addToolBar(self._toolbar)

    def _set_scale_factor(self, value):
        """
        Set scale factor.

        """
        value = float(value) / 100
        self.model.set_scale_factor(value, self.image_view.display_type())

    def _add_template(self):
        """
        Open a dialog window and select a template file.

        """
        template_dir = os.path.join(self.label_path, 'standard',
                                    'MNI152_T1_2mm_brain.nii.gz')
        template_name = QFileDialog.getOpenFileName(
            self,
            'Open standard file',
            template_dir,
            'Nifti files (*.nii.gz *.nii)')
        if not template_name.isEmpty():
            template_path = str(template_name)
            self._add_img(template_path)

    def _add_image(self):
        """
        Add new item.

        """
        if self._temp_dir == None:
            temp_dir = QDir.currentPath()
        else:
            temp_dir = self._temp_dir
        file_name = QFileDialog.getOpenFileName(self,
                                                'Add new file',
                                                temp_dir,
                                                'Nifti files (*.nii.gz *.nii)')
        if not file_name.isEmpty():
            file_path = str(file_name)
            self._add_img(file_path)

    def _add_img(self, source, name=None, header=None, view_min=None,
                 view_max=None, alpha=255, colormap='gray'):
        """
        Add image.

        """
        # If model is NULL, then re-initialize it.
        if not self.model:
            self._init_label_config_center()
            self.model = VolumeListModel([], self._label_config_center)
            self.model.set_scale_factor(self.default_grid_scale_factor, 'grid')
            self.model.set_scale_factor(self.default_orth_scale_factor, 'orth')
            self.painter_status = PainterStatus(ViewSettings())

        # Save previous opened directory (except `standard` directory)
        file_path = str(source)
        temp_dir = os.path.dirname(file_path)
        if not os.path.samefile(temp_dir, os.path.join(self.label_path, 'standard')):
            self._temp_dir = temp_dir

        if self.model.addItem(file_path, None, name, header, view_min,
                              view_max, alpha, colormap):
            # Take different acions in different case.
            # If only one data in VolumeList, then initialize views.
            if self.model.rowCount() == 1:
                # initialize views
                self.list_view = LayerView(self._label_config_center)
                self.list_view.setModel(self.model)
                self.image_view = GridView(self.model, self.painter_status)
                # initialize display layout
                central_widget = QWidget()
                layout = QHBoxLayout()
                central_widget.setLayout(layout)
                central_widget.layout().addWidget(self.list_view)
                central_widget.layout().addWidget(self.image_view)
                self.setCentralWidget(central_widget)
                # add a toolbar
                self._add_toolbar()
                #self.setUnifiedTitleAndToolBarOnMac(True)
                # change button status
                self._actions['save_image'].setEnabled(True)
                self._actions['ld_lbl'].setEnabled(True)
                self._actions['ld_glbl'].setEnabled(True)
                self._actions['new_image'].setEnabled(True)
                self._actions['close'].setEnabled(True)
                self._actions['orth_view'].setEnabled(True)
                self._actions['cross_hover_view'].setEnabled(True)
                self._actions['original_view'].setEnabled(True)
                self._actions['open'].setEnabled(True)
                self._actions['regular_roi'].setEnabled(True)
                self._actions['r2i'].setEnabled(True)
                self._actions['binaryzation'].setEnabled(True)
                self._actions['binarydilation'].setEnabled(True)
                self._actions['binaryerosion'].setEnabled(True)
                self._actions['greydilation'].setEnabled(True)
                self._actions['greyerosion'].setEnabled(True)
                self._actions['roiorvoxelcurve'].setEnabled(True);
                self._actions['volumeintensity'].setEnabled(True);
                self._actions['undo'].setEnabled(False)
                self._actions['redo'].setEnabled(False)
                # connect signals with slots
                self.list_view.current_changed.connect(self._update_undo)
                self.list_view.current_changed.connect(self._update_redo)
                self.model.undo_stack_changed.connect(self._update_undo)
                self.model.redo_stack_changed.connect(self._update_redo)
                # set current volume index
                self.list_view.setCurrentIndex(self.model.index(0))
                # set crosshair as the center of the data
                self.model.set_cross_pos([self.model.getY()/2,
                                          self.model.getX()/2,
                                          self.model.getZ()/2])
                ## Enable cursor tracking
                # self.list_view._list_view.selectionModel().currentChanged.connect(
                #                self._switch_cursor_status)
            elif self.model.rowCount() > 1:
                self._actions['remove_image'].setEnabled(True)
                self._actions['intersect'].setEnabled(True)
                self._actions['auto_label'].setEnabled(True)
                # set current volume index
                self.list_view.setCurrentIndex(self.model.index(0))
            self.is_save_configure = True
        else:
            QMessageBox.information(self,'FreeROI', 'Cannot load ' + "filename" + '.')

    def __new_image(self):
        self._new_image()

    def _new_image(self, data=None, name=None, colormap=None):
        """
        Create a new volume for brain parcellation.

        """
        if colormap is None:
            colormap = self._label_config_center.get_first_label_config()
        self.model.new_image(data, name, None, colormap)
        self.list_view.setCurrentIndex(self.model.index(0))

        # change button status
        self._actions['remove_image'].setEnabled(True)
        self._actions['intersect'].setEnabled(True)
        self._actions['regular_roi'].setEnabled(True)
        self._actions['r2i'].setEnabled(True)
        self._actions['auto_label'].setEnabled(True)
        self._actions['open'].setEnabled(True)

    def new_image_action(self):
        self._actions['remove_image'].setEnabled(True)
        self._actions['intersect'].setEnabled(True)
        self._actions['r2i'].setEnabled(True)
        self._actions['auto_label'].setEnabled(True)
        self._actions['open'].setEnabled(True)

    def _remove_image(self):
        """
        Remove current image.

        """
        row = self.list_view.currentRow()
        self.model.delItem(row)
        if self.model.rowCount() == 1:
            self._actions['remove_image'].setEnabled(False)
            self._actions['intersect'].setEnabled(False)
            self._actions['regular_roi'].setEnabled(False)
            self._actions['r2i'].setEnabled(False)
            self._actions['auto_label'].setEnabled(False)
            self._actions['open'].setEnabled(False)

    def _save_image(self):
        """
        Save image as a nifti file.

        """
        index = self.model.currentIndex()
        if self._temp_dir == None:
            temp_dir = str(QDir.currentPath())
        else:
            temp_dir = self._temp_dir
        file_path = os.path.join(temp_dir,
                                 str(self.model.data(index, Qt.DisplayRole))+'.nii.gz')
        path = QFileDialog.getSaveFileName(
            self,
            'Save image as...',
            file_path,
            'Nifti files (*.nii.gz *.nii)')
        if not path.isEmpty():
            self._temp_dir = os.path.dirname(str(path))
            self.model._data[index.row()].save2nifti(str(path))

    def _close_display(self):
        """
        Close current display.

        """
        self.setCentralWidget(QWidget())
        self._set_scale_factor(self.default_grid_scale_factor)
        self.removeToolBar(self._toolbar)
        self.model = None
        self._actions['add_template'].setEnabled(True)
        self._actions['add_image'].setEnabled(True)
        self._actions['remove_image'].setEnabled(False)
        self._actions['new_image'].setEnabled(False)
        self._actions['save_image'].setEnabled(False)
        self._actions['ld_glbl'].setEnabled(False)
        self._actions['ld_lbl'].setEnabled(False)
        self._actions['close'].setEnabled(False)
        self._actions['intersect'].setEnabled(False)
        self._actions['regular_roi'].setEnabled(False)
        self._actions['r2i'].setEnabled(False)
        self._actions['auto_label'].setEnabled(False)
        self._actions['open'].setEnabled(False)
        self._actions['grid_view'].setEnabled(False)
        self._actions['orth_view'].setEnabled(False)
        self._actions['original_view'].setEnabled(False)
        self._actions['binaryzation'].setEnabled(False)
        self._actions['binarydilation'].setEnabled(False)
        self._actions['binaryerosion'].setEnabled(False)
        self._actions['greydilation'].setEnabled(False)
        self._actions['greyerosion'].setEnabled(False)
        self._actions['roiorvoxelcurve'].setEnabled(False)
        self._actions['volumeintensity'].setEnabled(False)

    def _about_pybp(self):
        """
        About software.

        """
        QMessageBox.about(self,
                          self.tr("About FreeROI"),
                          self.tr("<p>the <b>FreeROI</b> could make ROI manually"
                                  "and automatically.</p>"
                                  "<p>Version: " + __version__ + "</p>"))

    def _create_menus(self):
        """Create menus."""
        self.file_menu = self.menuBar().addMenu(self.tr("File"))
        self.file_menu.addAction(self._actions['add_image'])
        self.file_menu.addAction(self._actions['add_template'])
        self.file_menu.addAction(self._actions['save_image'])
        self.file_menu.addSeparator()
        self.file_menu.addAction(self._actions['ld_lbl'])
        self.file_menu.addAction(self._actions['ld_glbl'])
        self.file_menu.addSeparator()
        self.file_menu.addAction(self._actions['close'])
        self.file_menu.addAction(self._actions['quit'])

        self.volume_menu = self.menuBar().addMenu(self.tr("Volume"))
        self.volume_menu.addAction(self._actions['new_image'])
        self.volume_menu.addAction(self._actions['remove_image'])

        self.view_menu = self.menuBar().addMenu(self.tr("View"))
        self.view_menu.addAction(self._actions['grid_view'])
        self.view_menu.addAction(self._actions['orth_view'])
        self.view_menu.addAction(self._actions['original_view'])
        self.view_menu.addAction(self._actions['cross_hover_view'])

        self.tool_menu = self.menuBar().addMenu(self.tr("Tools"))
        self.tool_menu.addAction(self._actions['regular_roi'])
        self.tool_menu.addAction(self._actions['intersect'])
        self.tool_menu.addAction(self._actions['r2i'])
        self.tool_menu.addAction(self._actions['auto_label'])
        self.tool_menu.addAction(self._actions['open'])
        self.tool_menu.addAction(self._actions['binaryzation'])
        self.tool_menu.addAction(self._actions['binarydilation'])
        self.tool_menu.addAction(self._actions['binaryerosion'])
        self.tool_menu.addAction(self._actions['greydilation'])
        self.tool_menu.addAction(self._actions['greyerosion'])
        self.tool_menu.addAction(self._actions['roiorvoxelcurve'])
        self.tool_menu.addAction(self._actions['volumeintensity'])

        self.help_menu = self.menuBar().addMenu(self.tr("Help"))
        self.help_menu.addAction(self._actions['about_pybp'])
        self.help_menu.addAction(self._actions['about_qt'])

    def _cursor_enable(self):
        """
        Cursor enabled.

        """
        if self._actions['cursor'].isChecked():
            self._actions['brush'].setChecked(False)
            self._actions['roibrush'].setChecked(False)
            self._actions['cursor'].setChecked(True)
            if isinstance(self.image_view, OrthView):
                self._actions['hand'].setChecked(False)

            self.painter_status.set_draw_settings(ViewSettings())

            if hasattr(self, 'roidialog'):
                self._roidialog_disable()

            self.image_view.set_label_mouse_tracking(True)
        else:
            self._actions['cursor'].setChecked(True)

    def _brush_enable(self):
        """
        Brush enabled.

        """
        if self._actions['brush'].isChecked():
            self._actions['cursor'].setChecked(False)
            self._actions['roibrush'].setChecked(False)
            self._actions['brush'].setChecked(True)
            if isinstance(self.image_view, OrthView):
                self._actions['hand'].setChecked(False)

            if hasattr(self, 'roidialog'):
                self._roidialog_disable()

            self._label_config_center.set_is_roi_edit(False)
            self.painter_status.set_draw_settings(self._label_config_center)
            self.image_view.set_label_mouse_tracking(False)

        else:
            self._actions['brush'].setChecked(True)

    def _roibrush_enable(self):
        """
        ROI brush enabled.

        """
        if self._actions['roibrush'].isChecked():
            self._actions['cursor'].setChecked(False)
            self._actions['brush'].setChecked(False)
            self._actions['roibrush'].setChecked(True)

            if hasattr(self, 'roidialog'):
                self._roidialog_disable()

            self._label_config_center.set_is_roi_edit(True)
            self.painter_status.set_draw_settings(self._label_config_center)
            self.image_view.set_label_mouse_tracking(False)
        else:
            self._actions['roibrush'].setChecked(True)

    def _roidialog_enable(self):
        if not hasattr(self, 'roidialog'):
            self._actions['cursor'].setChecked(False)
            self._actions['brush'].setChecked(False)
            self._actions['roibrush'].setChecked(False)

            self._actions['roidialog'].setChecked(True)
            self.roidialog = ROIDialog(self.model)
            self.list_view._list_view.selectionModel().currentChanged.connect(
                self.roidialog.clear_rois)
            self.painter_status.set_draw_settings(self.roidialog)
            self.roidialog.finished.connect(self._roidialog_disable)
            self.roidialog.show()
        self._actions['roidialog'].setChecked(True)

    def _roidialog_disable(self):
        if hasattr(self, 'roidialog'):
            del self.roidialog
        self._actions['roidialog'].setChecked(False)

    def _hand_enable(self):
        """
        Hand enabled.

        """
        if self._actions['hand'].isChecked():
            self._actions['cursor'].setChecked(False)
            self._actions['brush'].setChecked(False)
            #self._actions['eraser'].setChecked(False)
            self._actions['hand'].setChecked(True)

            self.painter_status.set_draw_settings(MoveSettings())

            if hasattr(self, 'label_dialog'):
                self.label_dialog.done(0)
                del self.label_dialog
            if hasattr(self, 'eraser_dialog'):
                self.eraser_dialog.done(0)
                del self.eraser_dialog
            if hasattr(self, 'roilabel_dialog'):
                self.roilabel_dialog.done(0)
                del self.roilabel_dialog
            if hasattr(self, 'roieraser_dialog'):
                self.roieraser_dialog.done(0)
                del self.roieraser_dialog

            self.image_view.set_label_mouse_tracking(True)
        else:
            self._actions['hand'].setChecked(True)

    def _switch_cursor_status(self):
        self._actions['cursor'].setChecked(True)
        self._cursor_enable()


    def _regular_roi(self):
        regular_roi_dialog = RegularROIDialog(self.model, self)
        regular_roi_dialog.exec_()


    #def _repaint_slices(self):
    #    self.model.update_current_rgba()

    #def _roieraser_enable(self):
    #    """
    #    ROI Eraser enabled.

    #    """
    #    if self._actions['roieraser'].isChecked():
    #        self._actions['cursor'].setChecked(False)
    #        self._actions['brush'].setChecked(False)
    #        self._actions['eraser'].setChecked(False)
    #        self._actions['roibrush'].setChecked(False)
    #        self._actions['roieraser'].setChecked(True)

    #        if hasattr(self, 'label_dialog'):
    #            self.label_dialog.done(0)
    #            del self.label_dialog
    #        if hasattr(self, 'eraser_dialog'):
    #            self.eraser_dialog.done(0)
    #            del self.eraser_dialog
    #        if hasattr(self, 'roilabel_dialog'):
    #            self.roilabel_dialog.done(0)
    #            del self.roilabel_dialog

    #        self.roieraser_dialog = ROIEraserDialog(self)
    #        self.roieraser_dialog.show()
    #        self.painter_status.set_draw_settings(self.roieraser_dialog)
    #        self.image_view.set_label_mouse_tracking(False)
    #    else:
    #        self._actions['roieraser'].setChecked(True)


    def _update_undo(self):
        if self.model.current_undo_available():
            self._actions['undo'].setEnabled(True)
        else:
            self._actions['undo'].setEnabled(False)

    def _update_redo(self):
        if self.model.current_redo_available():
            self._actions['redo'].setEnabled(True)
        else:
            self._actions['redo'].setEnabled(False)

    #def _current_layer_xyzvl_changed(self):
    #    """
    #      modified by dxb, need rethink the code. 
    #    """
    #    self.image_view.xyz_updated.connect(self._update_xyzvl)
    #    self.image_view.xyz_updated.emit([self.model.get_current_pos()[1],
    #                                      self.model.get_current_pos()[0],
    #                                      self.model.get_current_pos()[2]])

    #def _update_xyzvl(self, xyz):
    #    xyzvl = dict(zip(['y', 'x', 'z'], map(str, xyz)))
    #    value = self.model.get_current_value(xyz)
    #    xyzvl['value'] = str(value)
    #    xyzvl['label'] = self.model.get_current_value_label(int(value))
    #    self.list_view.update_xyzvl(xyzvl)

    def _init_label_config_center(self):
        lbl_path = os.path.join(self.label_config_dir,
                                '*.' + self.label_config_suffix)
        label_configs = glob.glob(lbl_path)
        self.label_configs = map(LabelConfig, label_configs)
        self._label_config_center = LabelConfigCenter(self.label_configs)
        # Label Config Changed
        self._label_config_center.label_config_changed_signal().connect(self._update_brush)

    def _init_label_config(self):
        label_path = os.path.join(self.label_path, self.config_file)
        if os.path.isfile(label_path):
            cwd_config = label_path
        else:
            cwd = os.getcwd()
            cwd_config = os.path.join(cwd, self.config_file)
            if not os.path.isfile(cwd_config):
                os.mknod(cwd_config)
        self.label_config = LabelConfig(cwd_config)

    def _get_label_config(self, file_path):
        """
        Get label config file.

        """
        # Get label config file
        dir = os.path.dirname(file_path)
        file = os.path.basename(file_path)
        split_list = file.split('.')
        nii_index = split_list.index('nii')
        file = ''.join(split_list[:nii_index])
        config_file = os.path.join(file, 'lbl')
        if os.path.isfile(config_file):
            label_config = LabelConfig(config_file, False)
        else:
            label_config = self.label_config

        return label_config

    def _undo(self):
        self.model.undo_current_image()

    def _redo(self):
        self.model.redo_current_image()

    def _intersect(self):
        """
        Make a intersection between two layers.

        """
        new_dialog = IntersectDialog(self.model)
        new_dialog.exec_()

    def _r2i(self):
        new_dialog = Roi2gwmiDialog(self.model)
        new_dialog.exec_()

    def _auto_label(self):
        auto_dialog = AutoLabelDialog(self.model)
        auto_dialog.exec_()

    def _open(self):
        new_dialog = OpenDialog(self.model)
        new_dialog.exec_()

    def _ld_lbl(self):
        file_name = QFileDialog.getOpenFileName(self,
                                                'Load Label File',
                                                QDir.currentPath(),
                                                "Label files (*.lbl)")
        if file_name:
            label_config = LabelConfig(str(file_name), False)
            self.model.set_cur_label(label_config)

    def _ld_glbl(self):
        file_name = QFileDialog.getOpenFileName(self,
                                                'Load Label File',
                                                QDir.currentPath(),
                                                "Label files (*.lbl)")
        if file_name:
            label_config = LabelConfig(str(file_name), True)
            self.model.set_global_label(label_config)

    def _grid_view(self):
        """
        Grid view option.

        """
        self._actions['grid_view'].setEnabled(False)
        self._actions['orth_view'].setEnabled(True)
        self._actions['hand'].setEnabled(False)
        self._actions['cursor'].trigger()

        self.centralWidget().layout().removeWidget(self.image_view)
        self.image_view.set_display_type('grid')
        self.model.scale_changed.disconnect()
        self.model.repaint_slices.disconnect()
        self.model.cross_pos_changed.disconnect(self.image_view.update_cross_pos)
        self.image_view.deleteLater()
        self._spinbox.setValue(100 * self.model.get_scale_factor('grid'))
        self.image_view = GridView(self.model, self.painter_status,
                                   self._gridview_vertical_scrollbar_position)
        self.centralWidget().layout().addWidget(self.image_view)

    def _orth_view(self):
        """
        Orth view option.
        """


        self._actions['orth_view'].setEnabled(False)
        self._actions['grid_view'].setEnabled(True)
        self._actions['hand'].setEnabled(True)
        self._actions['cursor'].trigger()

        self._gridview_vertical_scrollbar_position = \
            self.image_view.get_vertical_srollbar_position()
        self.centralWidget().layout().removeWidget(self.image_view)
        self.image_view.set_display_type('orth')
        self.model.scale_changed.disconnect()
        self.model.repaint_slices.disconnect()
        self.model.cross_pos_changed.disconnect(self.image_view.update_cross_pos)
        self.image_view.deleteLater()
        self._spinbox.setValue(100 * self.model.get_scale_factor('orth'))
        self.image_view = OrthView(self.model, self.painter_status)
        self.centralWidget().layout().addWidget(self.image_view)

    def _display_cross_hover(self):
        if self.model._display_cross:
            self.model.set_cross_status(False)
            self._actions['cross_hover_view'].setText('Enable cross hover')
            self._actions['cross_hover_view'].setIcon(QIcon(os.path.join(self._icon_dir,'cross_hover_disable.png')))
        else:
            self.model.set_cross_status(True)
            self._actions['cross_hover_view'].setText('Disable cross hover')
            self._actions['cross_hover_view'].setIcon(QIcon(os.path.join(self._icon_dir,'cross_hover_enable.png')))

    def _reset_view(self):
        """
        Reset view parameters.

        """
        if self.image_view.display_type() == 'orth':
            if not self.model.get_scale_factor('orth') == \
                    self.default_orth_scale_factor:
                self._spinbox.setValue(100 * self.default_orth_scale_factor)
            self.image_view.reset_view()
        elif self.image_view.display_type() == 'grid':
            if not self.model.get_scale_factor('grid') == \
                    self.default_grid_scale_factor:
                self._spinbox.setValue(100 * self.default_grid_scale_factor)

    def _update_brush(self):
        if self._label_config_center.is_drawing_valid():
            self._actions['brush'].setEnabled(True)
            self._actions['roibrush'].setEnabled(True)
        else:
            self._actions['brush'].setEnabled(False)
            self._actions['roibrush'].setEnabled(False)

    def _binaryzation(self):
        binaryzation_dialog = BinaryzationDialog(self.model)
        binaryzation_dialog.exec_()

    def _binaryerosion(self):
        binaryerosion_dialog = BinaryerosionDialog(self.model)
        binaryerosion_dialog.exec_()

    def _binarydilation(self):
        binarydilation_dialog = BinarydilationDialog(self.model)
        binarydilation_dialog.exec_()

    def _greyerosion(self):
        greyerosiondialog = GreyerosionDialog(self.model)
        greyerosiondialog.exec_()

    def _roiorvoxelcurve(self):
        self.ROI_or_voxel_dialog = ROIOrVoxelCurveDialog(self.model)
        self.list_view.current_changed.connect(self.ROI_or_voxel_dialog.listview_current_index_changed)
        self.ROI_or_voxel_dialog.exec_()

    def _volumeintensity(self):
        self.volume_intensity = VolumeIntensityDialog(self.model)
        self.list_view.current_changed.connect(self.volume_intensity._plot)
        self.volume_intensity.setModal(False)
        self.volume_intensity.setWindowFlags(Qt.WindowStaysOnTopHint)
        self.volume_intensity.show()

    def _greydilation(self):
        greydilation_dialog = GreydilationDialog(self.model)
        greydilation_dialog.exec_()
Example #5
0
class BpMainWindow(QMainWindow):
    """Class BpMainWindow provides UI interface of FreeROI.

    Example:
    --------

    >>> from PyQt4.QtGui import QApplication
    >>> import main
    >>> app = QApplication([])
    >>> win = main.BpMainWindow()
    ......
    >>> win.show()
    >>> app.exec_()

    """

    def __init__(self, parent=None):
        """
        Initialize an instance of BpMainWindow.
        
        """
        # Inherited from QMainWindow
        if sys.platform == 'darwin':
            # Workaround for Qt issue on OS X that causes QMainWindow to
            # hide when adding QToolBar, see
            # https://bugreports.qt-project.org/browse/QTBUG-4300
            super(BpMainWindow, self).__init__(parent,
                                               Qt.MacWindowToolBarButtonHint)
        else:
            super(BpMainWindow, self).__init__(parent)

        # temporary variable
        self._temp_dir = None
        self.is_save_configure = False

        # pre-define a model variable
        self.model = None

    def config_extra_settings(self, data_dir):
        """
        Set data directory and update some configurations.

        """
        # load data directory configuration
        self.label_path = data_dir
        self.label_config_dir = os.path.join(self.label_path, 'labelconfig')
        self.label_config_suffix = 'lbl'

        # set icon configuration
        self._icon_dir = get_icon_dir()

        # set window title
        self.setWindowTitle('FreeROI')
        #self.resize(1280, 1000)
        self.center()
        # set window icon
        self.setWindowIcon(QIcon(os.path.join(self._icon_dir,
                                              'logo.png')))

        self._init_configuration()

        # create actions
        self._create_actions()

        # create menus
        self._create_menus()

    def center(self):
        """
        Display main window in the center of screen

        """
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    def _init_configuration(self):
        """
        Load configuration for GUI.

        """
        config_file = os.path.expanduser('~/.pybp.conf')
        if os.path.exists(config_file):
            config = ConfigParser.RawConfigParser()
            config.read(config_file)
            self.window_width = config.getint('width', 'int')
            self.window_height = config.getint('height', 'int')
            self.orth_scale_factor = config.getint('orth_scale', 'int')
            self.grid_scale_factor = config.getint('grid_scale', 'int')
            self.window_xpos = config.getint('xpos', 'int')
            self.window_ypos = config.getint('ypos', 'int')

            self.resize(self.window_width, self.window_height)
            self.move(self.window_xpos, self.window_ypos)
            self.default_orth_scale_factor = float(self.orth_scale_factor) / 100
            self.default_grid_scale_factor = float(self.grid_scale_factor) / 100
        else:
            self.setWindowState(Qt.WindowMaximized)
            self.default_orth_scale_factor = 1.0
            self.default_grid_scale_factor = 2.0

    def _save_configuration(self):
        """
        Save GUI configuration to a file.

        """
        config_file = os.path.expanduser('~/.pybp.conf')

        config = ConfigParser.RawConfigParser()
        config.add_section('width')
        config.add_section('height')
        config.add_section('orth_scale')
        config.add_section('grid_scale')
        config.add_section('xpos')
        config.add_section('ypos')
        config.set('width', 'int', self.width())
        config.set('height', 'int', self.height())
        config.set('xpos', 'int', self.x())
        config.set('ypos', 'int', self.y())
        if hasattr(self, 'model') and isinstance(self.model, VolumeListModel):
            config.set('orth_scale', 'int',
                       int(self.model.get_scale_factor('orth')*100))
            config.set('grid_scale', 'int',
                       int(self.model.get_scale_factor('grid')*100))
        else:
            config.set('orth_scale', 'int',
                       int(self.default_orth_scale_factor * 100))
            config.set('grid_scale', 'int',
                       int(self.default_grid_scale_factor * 100))

        with open(config_file, 'wb') as conf:
            config.write(conf)

    def closeEvent(self, e):
        if self.is_save_configure:
           self._save_configuration()
        e.accept()

    def _create_actions(self):
        """
        Create actions.

        """
        # create a dictionary to store actions info
        self._actions = {}

        # Open template action
        self._actions['add_template'] = QAction(QIcon(os.path.join(
                                                self._icon_dir, 'open.png')),
                                            self.tr("&Open standard volume"),
                                            self)
        self._actions['add_template'].setShortcut(self.tr("Ctrl+O"))
        self._actions['add_template'].triggered.connect(self._add_template)
        self._actions['add_template'].setEnabled(True)

        # Add a new image action
        self._actions['add_image'] = QAction(QIcon(os.path.join(
                                                self._icon_dir, 'add.png')),
                                            self.tr("&Add volume"),
                                            self)
        self._actions['add_image'].setShortcut(self.tr("Ctrl+A"))
        self._actions['add_image'].triggered.connect(self._add_image)
        self._actions['add_image'].setEnabled(True)

        # Remove an image
        self._actions['remove_image'] = QAction(QIcon(os.path.join(
                                                self._icon_dir, 'remove.png')),
                                            self.tr("&Remove volume"),
                                            self)
        self._actions['remove_image'].setShortcut(self.tr("Ctrl+R"))
        self._actions['remove_image'].triggered.connect(self._remove_image)
        self._actions['remove_image'].setEnabled(False)

        # New image
        self._actions['new_image'] = QAction(QIcon(os.path.join(
                                                self._icon_dir, 'create.png')),
                                             self.tr("&New volume"),
                                             self)
        self._actions['new_image'].setShortcut(self.tr("Ctrl+N"))
        self._actions['new_image'].triggered.connect(self.__new_image)
        self._actions['new_image'].setEnabled(False)

        # Save image
        self._actions['save_image'] = QAction(QIcon(os.path.join(
                                                self._icon_dir, 'save.png')),
                                              self.tr("&Save volume as..."),
                                              self)
        self._actions['save_image'].setShortcut(self.tr("Ctrl+S"))
        self._actions['save_image'].triggered.connect(self._save_image)
        self._actions['save_image'].setEnabled(False)

        ## Load Label Config
        #self._actions['ld_lbl'] = QAction('Load Label', self)
        #self._actions['ld_lbl'].triggered.connect(self._ld_lbl)
        #self._actions['ld_lbl'].setEnabled(False)

        ## Load Global Label Config
        #self._actions['ld_glbl'] = QAction('Load Global Label', self)
        #self._actions['ld_glbl'].triggered.connect(self._ld_glbl)
        #self._actions['ld_glbl'].setEnabled(False)

        # Close display
        self._actions['close'] = QAction(self.tr("Close"), self)
        self._actions['close'].setShortcut(self.tr("Ctrl+W"))
        self._actions['close'].triggered.connect(self._close_display)
        self._actions['close'].setEnabled(False)

        # Quit action
        self._actions['quit'] = QAction(QIcon(os.path.join(
                                            self._icon_dir, 'quit.png')),
                                        self.tr("&Quit"),
                                        self)
        self._actions['quit'].setShortcut(self.tr("Ctrl+Q"))
        self._actions['quit'].triggered.connect(self.close)

        # Grid view action
        self._actions['grid_view'] = QAction(QIcon(os.path.join(
                                            self._icon_dir, 'gridview.png')),
                                             self.tr("Lightbox"),
                                             self)
        self._actions['grid_view'].triggered.connect(self._grid_view)
        self._actions['grid_view'].setEnabled(False)

        # Orth view action
        self._actions['orth_view'] = QAction(QIcon(os.path.join(
                                            self._icon_dir, 'orthview.png')),
                                             self.tr("Orthographic"),
                                             self)
        self._actions['orth_view'].triggered.connect(self._orth_view)
        self._actions['orth_view'].setEnabled(False)

        # return original size
        self._actions['original_view'] = QAction(QIcon(os.path.join(
                                        self._icon_dir, 'original_size.png')),
                                                 self.tr("Reset view"),
                                                 self)
        self._actions['original_view'].triggered.connect(self._reset_view)
        self._actions['original_view'].setEnabled(False)

        # whether display the cross hover
        self._actions['cross_hover_view'] = QAction(QIcon(os.path.join(
                                    self._icon_dir, 'cross_hover_enable.png')),
                                                self.tr("Disable cross hover"),
                                                self)
        self._actions['cross_hover_view'].triggered.connect(
                                            self._display_cross_hover)
        self._actions['cross_hover_view'].setEnabled(False)

        # Binaryzation view action
        self._actions['binarization'] = QAction(QIcon(os.path.join(
                                        self._icon_dir, 'binarization.png')),
                                                self.tr("Binarization"),
                                                self)
        self._actions['binarization'].triggered.connect(self._binarization)
        self._actions['binarization'].setEnabled(False)

        # Intersection action
        self._actions['intersect'] = QAction(QIcon(os.path.join(
                                        self._icon_dir, 'intersect.png')),
                                             self.tr("Intersection"),
                                             self)
        self._actions['intersect'].triggered.connect(self._intersect)
        self._actions['intersect'].setEnabled(False)

        # Extract mean time course
        self._actions['meants'] = QAction(QIcon(os.path.join(self._icon_dir,
                                                            'voxel_curve.png')),
                                          self.tr("Extract Mean Time Course"),
                                          self)
        self._actions['meants'].triggered.connect(self._meants)
        self._actions['meants'].setEnabled(False)

        # Voxel Stats
        self._actions['voxelstats'] = QAction(self.tr("Voxel number stats"),
                                              self)
        self._actions['voxelstats'].triggered.connect(self._voxelstats)
        self._actions['voxelstats'].setEnabled(False)

        # Local Max action
        self._actions['localmax'] = QAction(QIcon(os.path.join(
                                        self._icon_dir, 'localmax.png')),
                                             self.tr("Local Max"),
                                             self)
        self._actions['localmax'].triggered.connect(self._local_max)
        self._actions['localmax'].setEnabled(False)

        # Inversion action
        self._actions['inverse'] = QAction(QIcon(os.path.join(
                                        self._icon_dir, 'inverse.icon')),
                                             self.tr("Inversion"),
                                             self)
        self._actions['inverse'].triggered.connect(self._inverse)
        self._actions['inverse'].setEnabled(False)

        # Smoothing action
        self._actions['smoothing'] = QAction(QIcon(os.path.join(
                                        self._icon_dir, 'smoothing.png')),
                                             self.tr("Smoothing"),
                                             self)
        self._actions['smoothing'].triggered.connect(self._smooth)
        self._actions['smoothing'].setEnabled(False)

        # Region Growing action
        self._actions['region_grow'] = QAction(QIcon(os.path.join(
                                        self._icon_dir, 'grow.png')),
                                             self.tr("Region Growing"),
                                             self)
        self._actions['region_grow'].triggered.connect(self._region_grow)
        self._actions['region_grow'].setEnabled(False)

        # Watershed action
        self._actions['watershed'] = QAction(QIcon(os.path.join(
                                        self._icon_dir, 'watershed.png')),
                                             self.tr("Watershed"),
                                             self)
        self._actions['watershed'].triggered.connect(self._watershed)
        self._actions['watershed'].setEnabled(False)

        # Cluster action
        self._actions['cluster'] = QAction(QIcon(os.path.join(
                                        self._icon_dir, 'cluster.png')),
                                             self.tr("Cluster"),
                                             self)
        self._actions['cluster'].triggered.connect(self._cluster)
        self._actions['cluster'].setEnabled(False)

        # Opening
        self._actions['opening'] = QAction(self.tr("Opening"), self)
        self._actions['opening'].triggered.connect(self._opening)
        self._actions['opening'].setEnabled(False)

        # Binary_erosion view action
        self._actions['binaryerosion'] = QAction(self.tr("Binary Erosion"),
                                                 self)
        self._actions['binaryerosion'].triggered.connect(self._binaryerosion)
        self._actions['binaryerosion'].setEnabled(False)

        # Binary_dilation view action
        self._actions['binarydilation'] = QAction(self.tr("Binary Dilation"),
                                                  self)
        self._actions['binarydilation'].triggered.connect(self._binarydilation)
        self._actions['binarydilation'].setEnabled(False)

        # grey_erosion view action
        self._actions['greyerosion'] = QAction(self.tr("Grey Erosion"),
                                               self)
        self._actions['greyerosion'].triggered.connect(self._greyerosion)
        self._actions['greyerosion'].setEnabled(False)

        # grey_dilation view action
        self._actions['greydilation'] = QAction(self.tr("Grey Dilation"),
                                                self)
        self._actions['greydilation'].triggered.connect(self._greydilation)
        self._actions['greydilation'].setEnabled(False)

        # About software
        self._actions['about_pybp'] = QAction(self.tr("About FreeROI"), self)
        self._actions['about_pybp'].triggered.connect(self._about_pybp)

        # About Qt
        self._actions['about_qt'] = QAction(QIcon(os.path.join(
                                                self._icon_dir, 'qt.png')),
                                            self.tr("About Qt"),
                                            self)
        self._actions['about_qt'].triggered.connect(qApp.aboutQt)

        # Hand
        self._actions['hand'] = QAction(QIcon(os.path.join(
                                            self._icon_dir, 'hand.png')),
                                        self.tr("Hand"),
                                        self)
        self._actions['hand'].triggered.connect(self._hand_enable)
        self._actions['hand'].setCheckable(True)
        self._actions['hand'].setChecked(False)
        self._actions['hand'].setEnabled(False)

        # Cursor
        self._actions['cursor'] = QAction(QIcon(os.path.join(
                                            self._icon_dir, 'cursor.png')),
                                          self.tr("Cursor"),
                                          self)
        self._actions['cursor'].triggered.connect(self._cursor_enable)
        self._actions['cursor'].setCheckable(True)
        self._actions['cursor'].setChecked(True)
        self._actions['cursor'].setEnabled(True)

        # Edit
        self._actions['edit'] = QAction(QIcon(os.path.join(
                                            self._icon_dir, 'edit.png')),
                                         self.tr("Edit"), self)
        self._actions['edit'].triggered.connect(self._roidialog_enable)
        self._actions['edit'].setCheckable(True)
        self._actions['edit'].setChecked(False)

        # Undo
        self._actions['undo'] = QAction(QIcon(os.path.join(
                                            self._icon_dir, 'undo.png')),
                                        self.tr("Undo"),
                                        self)
        self._actions['undo'].triggered.connect(self._undo)

        # Redo
        self._actions['redo'] = QAction(QIcon(os.path.join(
                                            self._icon_dir, 'redo.png')),
                                        self.tr("Redo"),
                                        self)
        self._actions['redo'].triggered.connect(self._redo)

        # sphere and cube roi
        self._actions['regular_roi'] = QAction(QIcon(os.path.join(
                                                self._icon_dir,
                                                'sphere_and_cube.png')),
                                               self.tr("Regular ROI"), self)
        self._actions['regular_roi'].triggered.connect(self._regular_roi)
        self._actions['regular_roi'].setEnabled(False)

        # ROI to Interface
        self._actions['r2i'] = QAction(QIcon(os.path.join(
                                        self._icon_dir, 'r2i.png')),
                                       self.tr("ROI2Interface"), self)
        self._actions['r2i'].triggered.connect(self._r2i)
        self._actions['r2i'].setEnabled(False)

        # Edge detection for ROI
        self._actions['edge_dete'] = QAction(QIcon(os.path.join(
                                                self._icon_dir,
                                                'edge_detection.png')),
                                             self.tr("Edge Detection"), self)
        self._actions['edge_dete'].triggered.connect(self._edge_detection)
        self._actions['edge_dete'].setEnabled(False)

        # ROI Merging
        self._actions['roi_merge'] = QAction(QIcon(os.path.join(
                                                self._icon_dir, 'merging.png')),
                                             self.tr("ROI Merging"), self)
        self._actions['roi_merge'].triggered.connect(self._roi_merge)
        self._actions['roi_merge'].setEnabled(False)

    def _add_toolbar(self):
        """
        Add toolbar.

        """
        # Initialize a spinbox for zoom-scale selection
        self._spinbox = QSpinBox()
        self._spinbox.setMaximum(500)
        self._spinbox.setMinimum(50)
        self._spinbox.setSuffix('%')
        self._spinbox.setSingleStep(10)
        self._spinbox.setValue(self.default_grid_scale_factor * 100)
        self._spinbox.valueChanged.connect(self._set_scale_factor)

        # Add a toolbar
        self._toolbar = self.addToolBar("Tools")
        #self._toolbar.setIconSize(QSize(38,38))
        # Add file actions
        self._toolbar.addAction(self._actions['add_image'])
        self._toolbar.addAction(self._actions['remove_image'])
        self._toolbar.addAction(self._actions['new_image'])
        self._toolbar.addAction(self._actions['save_image'])
        # Add view actions
        self._toolbar.addSeparator()
        self._toolbar.addAction(self._actions['grid_view'])
        self._toolbar.addAction(self._actions['orth_view'])
        self._toolbar.addAction(self._actions['original_view'])
        self._toolbar.addAction(self._actions['cross_hover_view'])
        # Add cursor status
        self._toolbar.addSeparator()
        self._toolbar.addAction(self._actions['hand'])
        self._toolbar.addAction(self._actions['cursor'])
        self._toolbar.addAction(self._actions['edit'])
        # Add undo redo
        self._toolbar.addSeparator()
        self._toolbar.addAction(self._actions['undo'])
        self._toolbar.addAction(self._actions['redo'])

        self._toolbar.addSeparator()
        self._toolbar.addWidget(self._spinbox)

    def _set_scale_factor(self, value):
        """
        Set scale factor.

        """
        value = float(value) / 100
        self.model.set_scale_factor(value, self.image_view.display_type())

    def _add_template(self):
        """
        Open a dialog window and select a template file.

        """
        template_dir = os.path.join(self.label_path, 'standard',
                                    'MNI152_T1_2mm_brain.nii.gz')
        template_name = QFileDialog.getOpenFileName(
            self,
            'Open standard file',
            template_dir,
            'Nifti files (*.nii.gz *.nii)')
        if not template_name.isEmpty():
            if sys.platform == 'win32':
                template_path = unicode(template_name).encode('gb2312')
            else:
                template_path = str(template_name)
            self._add_img(template_path)

    def _add_image(self):
        """
        Add new item.

        """
        if self._temp_dir == None:
            temp_dir = QDir.currentPath()
        else:
            temp_dir = self._temp_dir
        file_name = QFileDialog.getOpenFileName(self,
                                                'Add new file',
                                                temp_dir,
                                                "Nifti files (*.nii *.nii.gz)")
        if not file_name.isEmpty():
            if sys.platform == 'win32':
                file_path = unicode(file_name).encode('gb2312')
            else:
                file_path = str(file_name)
            self._add_img(file_path)

    def _add_img(self, source, name=None, header=None, view_min=None,
                 view_max=None, alpha=255, colormap='gray'):
        """
        Add image.

        """
        # If model is NULL, then re-initialize it.
        if not self.model:
            self._init_label_config_center()
            self.model = VolumeListModel([], self._label_config_center)
            self.model.set_scale_factor(self.default_grid_scale_factor, 'grid')
            self.model.set_scale_factor(self.default_orth_scale_factor, 'orth')
            self.painter_status = PainterStatus(ViewSettings())

        # Save previous opened directory (except `standard` directory)
        file_path = source
        if sys.platform == 'win32':
            temp_dir = os.path.dirname(unicode(file_path, 'gb2312'))
            if not os.stat(temp_dir) == os.stat(os.path.join(self.label_path,
                                                             'standard')):
                self._temp_dir = temp_dir
        else:
            temp_dir = os.path.dirname(file_path)
            if not os.path.samefile(temp_dir, os.path.join(self.label_path,
                                                           'standard')):
                self._temp_dir = temp_dir

        if self.model.addItem(file_path, None, name, header, view_min,
                              view_max, alpha, colormap):
            # Take different acions in different case.
            # If only one data in VolumeList, then initialize views.
            if self.model.rowCount() == 1:
                # initialize views
                self.list_view = LayerView(self._label_config_center)
                self.list_view.setModel(self.model)
                self._init_roi_dialog()
                self.image_view = GridView(self.model, self.painter_status)
                # initialize display layout
                central_widget = QWidget()
                layout = QHBoxLayout()
                central_widget.setLayout(layout)
                central_widget.layout().addWidget(self.list_view)
                central_widget.layout().addWidget(self.image_view)
                self.setCentralWidget(central_widget)
                # add a toolbar
                self._add_toolbar()
                #self.setUnifiedTitleAndToolBarOnMac(True)
                # change button status
                self._actions['save_image'].setEnabled(True)
                #self._actions['ld_lbl'].setEnabled(True)
                #self._actions['ld_glbl'].setEnabled(True)
                self._actions['new_image'].setEnabled(True)
                self._actions['close'].setEnabled(True)
                self._actions['orth_view'].setEnabled(True)
                self._actions['cross_hover_view'].setEnabled(True)
                self._actions['original_view'].setEnabled(True)
                self._actions['undo'].setEnabled(False)
                self._actions['redo'].setEnabled(False)
                self._functional_module_set_enabled(True)
                # connect signals with slots
                self.list_view.current_changed.connect(self._update_undo)
                self.list_view.current_changed.connect(self._update_redo)
                self.model.rowsInserted.connect(self._update_remove_image)
                self.model.undo_stack_changed.connect(self._update_undo)
                self.model.redo_stack_changed.connect(self._update_redo)
                # set current volume index
                self.list_view.setCurrentIndex(self.model.index(0))
                # set crosshair as the center of the data
                self.model.set_cross_pos([self.model.getY()/2,
                                          self.model.getX()/2,
                                          self.model.getZ()/2])
                ## Enable cursor tracking
                # self.list_view._list_view.selectionModel().currentChanged.connect(
                #                self._switch_cursor_status)
            elif self.model.rowCount() > 1:
                self._actions['remove_image'].setEnabled(True)
                # set current volume index
                self.list_view.setCurrentIndex(self.model.index(0))
            self.is_save_configure = True
        else:
            QMessageBox.information(self,'FreeROI', 'Cannot load ' + \
                                    file_path + ': due to mismatch data size.')

    def __new_image(self):
        self._new_image()

    def _update_remove_image(self):
        if self.model.rowCount() == 1:
            self._actions['remove_image'].setEnabled(False)
        else:
            self._actions['remove_image'].setEnabled(True)

    def _new_image(self, data=None, name=None, colormap=None):
        """
        Create a new volume for brain parcellation.

        """
        if colormap is None:
            colormap = self._label_config_center.get_first_label_config()
        self.model.new_image(data, name, None, colormap)
        self.list_view.setCurrentIndex(self.model.index(0))

        # change button status
        self._actions['remove_image'].setEnabled(True)

    def new_image_action(self):
        self._actions['remove_image'].setEnabled(True)

    def _remove_image(self):
        """
        Remove current image.

        """
        row = self.list_view.currentRow()
        self.model.delItem(row)
        if self.model.rowCount() == 1:
            self._actions['remove_image'].setEnabled(False)

    def _save_image(self):
        """
        Save image as a nifti file.

        """
        index = self.model.currentIndex()
        if self._temp_dir == None:
            temp_dir = str(QDir.currentPath())
        else:
            temp_dir = self._temp_dir
        file_path = os.path.join(temp_dir,
                                 str(self.model.data(index, Qt.DisplayRole))+'.nii.gz')
        path = QFileDialog.getSaveFileName(
            self,
            'Save image as...',
            file_path,
            'Nifti files (*.nii.gz *.nii)')
        if not path.isEmpty():
            if sys.platform == 'win32':
                path = unicode(path).encode('gb2312')
                self._temp_dir = os.path.dirname(unicode(path, 'gb2312'))
            else:
                path = str(path)
                self._temp_dir = os.path.dirname(path)
            self.model._data[index.row()].save2nifti(path)

    def _close_display(self):
        """
        Close current display.

        """
        self.setCentralWidget(QWidget())
        self._set_scale_factor(self.default_grid_scale_factor)
        self.removeToolBar(self._toolbar)
        self.model = None
        self._actions['add_template'].setEnabled(True)
        self._actions['add_image'].setEnabled(True)
        self._actions['remove_image'].setEnabled(False)
        self._actions['new_image'].setEnabled(False)
        self._actions['save_image'].setEnabled(False)
        #self._actions['ld_glbl'].setEnabled(False)
        #self._actions['ld_lbl'].setEnabled(False)
        self._actions['close'].setEnabled(False)
        self._actions['grid_view'].setEnabled(False)
        self._actions['orth_view'].setEnabled(False)
        self._actions['cross_hover_view'].setEnabled(False)
        self._actions['original_view'].setEnabled(False)
        self._functional_module_set_enabled(False)

    def _about_pybp(self):
        """
        About software.

        """
        QMessageBox.about(self, self.tr("About FreeROI"),
                          self.tr("<p><b>FreeROI</b> is a versatile image "
                                  "processing software developed for "
                                  "neuroimaging data.</p>"
                                  "<p>Its goal is to provide a user-friendly "
                                  "interface for neuroimaging researchers "
                                  "to visualize and analyze their data, "
                                  "especially in defining region of interest "
                                  "(ROI) for ROI analysis.</p>"
                                  "<p>Version: " + __version__ + "</p>"
                                  "<p>Written by: Lijie Huang, Zetian Yang, "
                                  "Guangfu Zhou, Zhaoguo Liu, Xiaobin Dang, "
                                  "Xiangzhen Kong, Xu Wang, and Zonglei Zhen."
                                  "</p>"
                                  "<p><b>FreeROI</b> is under Revised BSD "
                                  "License.</p>"
                                  "<p>Copyright(c) 2012-2014 "
                                  "Neuroinformatic Team in LiuLab "
                                  "from Beijing Normal University</p>"
                                  "<p></p>"
                                  "<p>Please join and report bugs to:</p>"
                                  "<p><b>[email protected]</b></p>"))

    def _create_menus(self):
        """Create menus."""
        self.file_menu = self.menuBar().addMenu(self.tr("File"))
        self.file_menu.addAction(self._actions['add_image'])
        self.file_menu.addAction(self._actions['add_template'])
        self.file_menu.addAction(self._actions['save_image'])
        #self.file_menu.addSeparator()
        #self.file_menu.addAction(self._actions['ld_lbl'])
        #self.file_menu.addAction(self._actions['ld_glbl'])
        self.file_menu.addSeparator()
        self.file_menu.addAction(self._actions['close'])
        self.file_menu.addAction(self._actions['quit'])

        self.volume_menu = self.menuBar().addMenu(self.tr("Volume"))
        self.volume_menu.addAction(self._actions['new_image'])
        self.volume_menu.addAction(self._actions['remove_image'])

        self.view_menu = self.menuBar().addMenu(self.tr("View"))
        self.view_menu.addAction(self._actions['grid_view'])
        self.view_menu.addAction(self._actions['orth_view'])
        self.view_menu.addAction(self._actions['original_view'])
        self.view_menu.addAction(self._actions['cross_hover_view'])

        self.tool_menu = self.menuBar().addMenu(self.tr("Tools"))
        basic_tools = self.tool_menu.addMenu(self.tr("Basic Tools"))
        basic_tools.addAction(self._actions['binarization'])
        basic_tools.addAction(self._actions['intersect'])
        basic_tools.addAction(self._actions['localmax'])
        basic_tools.addAction(self._actions['inverse'])
        basic_tools.addAction(self._actions['smoothing'])
        basic_tools.addAction(self._actions['meants'])
        basic_tools.addAction(self._actions['voxelstats'])
        segment_tools = self.tool_menu.addMenu(self.tr("Segmentation"))
        segment_tools.addAction(self._actions['region_grow'])
        segment_tools.addAction(self._actions['watershed'])
        segment_tools.addAction(self._actions['cluster'])
        roi_tools = self.tool_menu.addMenu(self.tr("ROI Tools"))
        roi_tools.addAction(self._actions['edge_dete'])
        roi_tools.addAction(self._actions['roi_merge'])
        roi_tools.addAction(self._actions['regular_roi'])
        roi_tools.addAction(self._actions['r2i'])
        morphological_tools = self.tool_menu.addMenu(
                                    self.tr("Morphological Processing"))
        morphological_tools.addAction(self._actions['opening'])
        morphological_tools.addAction(self._actions['binarydilation'])
        morphological_tools.addAction(self._actions['binaryerosion'])
        morphological_tools.addAction(self._actions['greydilation'])
        morphological_tools.addAction(self._actions['greyerosion'])

        self.help_menu = self.menuBar().addMenu(self.tr("Help"))
        self.help_menu.addAction(self._actions['about_pybp'])
        self.help_menu.addAction(self._actions['about_qt'])

    def _cursor_enable(self):
        """
        Cursor enabled.

        """
        if self._actions['cursor'].isChecked():
            self._actions['cursor'].setChecked(True)
            if isinstance(self.image_view, OrthView):
                self._actions['hand'].setChecked(False)
            if self.roidialog.isVisible():
                self._roidialog_disable()

            self.painter_status.set_draw_settings(ViewSettings())
            self.image_view.set_cursor(Qt.ArrowCursor)
            self.image_view.set_label_mouse_tracking(True)
        else:
            self._actions['cursor'].setChecked(True)

    def _voxel_edit_enable(self):
        """
        Brush enabled.

        """
        self._label_config_center.set_is_roi_edit(False)
        self.painter_status.set_draw_settings(self._label_config_center)
        self.image_view.set_cursor(Qt.CrossCursor)
        self.image_view.set_label_mouse_tracking(False)

    def _roi_edit_enable(self):
        """
        ROI brush enabled.

        """
        self._label_config_center.set_is_roi_edit(True)
        self.painter_status.set_draw_settings(self._label_config_center)
        self.image_view.set_cursor(Qt.CrossCursor)
        self.image_view.set_label_mouse_tracking(False)

    def _roidialog_enable(self):
        if self._actions['edit'].isChecked():
            self._actions['cursor'].setChecked(False)
            if isinstance(self.image_view, OrthView):
                self._actions['hand'].setChecked(False)
            self._actions['edit'].setChecked(True)
            self.roidialog._voxel_clicked()
            self.roidialog.show()
        else:
            self._actions['edit'].setChecked(True)

    def _roi_batch_enable(self):
        self.image_view.set_label_mouse_tracking(False)
        self._label_config_center.set_is_roi_edit(False)
        self.painter_status.set_draw_settings(self.roidialog)

    def _roidialog_disable(self):
        self.roidialog.hide()
        self._actions['edit'].setChecked(False)

    def _hand_enable(self):
        """
        Hand enabled.

        """
        if self._actions['hand'].isChecked():
            self._actions['cursor'].setChecked(False)
            self._actions['hand'].setChecked(True)
 
            if hasattr(self, 'roidialog'):
                self._roidialog_disable()

            self.painter_status.set_draw_settings(MoveSettings())
            self.image_view.set_cursor(Qt.OpenHandCursor)
            self.image_view.set_label_mouse_tracking(True)
        else:
            self._actions['hand'].setChecked(True)

    def _switch_cursor_status(self):
        self._actions['cursor'].setChecked(True)
        self._cursor_enable()

    def _update_undo(self):
        if self.model.current_undo_available():
            self._actions['undo'].setEnabled(True)
        else:
            self._actions['undo'].setEnabled(False)

    def _update_redo(self):
        if self.model.current_redo_available():
            self._actions['redo'].setEnabled(True)
        else:
            self._actions['redo'].setEnabled(False)

    def _init_roi_dialog(self):
        """
        Initialize ROI Dialog

        """
        self.roidialog = ROIDialog(self.model, self._label_config_center, self)
        self.roidialog.voxel_edit_enabled.connect(self._voxel_edit_enable)
        self.roidialog.roi_edit_enabled.connect(self._roi_edit_enable)
        self.roidialog.roi_batch_enabled.connect(self._roi_batch_enable)
        self.list_view._list_view.selectionModel().currentChanged.connect(
                self.roidialog.clear_rois)

    def _init_label_config_center(self):
        """
        Initialize LabelConfigCenter

        """
        lbl_path = os.path.join(self.label_config_dir,
                                '*.' + self.label_config_suffix)
        label_configs = glob.glob(lbl_path)
        self.label_configs = map(LabelConfig, label_configs)
        self._label_config_center = LabelConfigCenter(self.label_configs)

    def _get_label_config(self, file_path):
        """
        Get label config file.

        """
        # Get label config file
        dir = os.path.dirname(file_path)
        file = os.path.basename(file_path)
        split_list = file.split('.')
        nii_index = split_list.index('nii')
        file = ''.join(split_list[:nii_index])
        config_file = os.path.join(file, 'lbl')
        if os.path.isfile(config_file):
            label_config = LabelConfig(config_file, False)
        else:
            label_config = self.label_config

        return label_config

    def _undo(self):
        self.model.undo_current_image()

    def _redo(self):
        self.model.redo_current_image()

    def _regular_roi(self):
        regular_roi_dialog = RegularROIDialog(self.model)
        regular_roi_dialog.exec_()

    def _edge_detection(self):
        edge_detection(self.model)

    def _roi_merge(self):
        new_dialog = ROIMergeDialog(self.model)
        new_dialog.exec_()

    def _r2i(self):
        new_dialog = Roi2gwmiDialog(self.model)
        new_dialog.exec_()

    def _opening(self):
        new_dialog = OpenDialog(self.model)
        new_dialog.exec_()

    def _voxelstats(self):
        new_dialog = VoxelStatsDialog(self.model, self)
        new_dialog.show()

    def _ld_lbl(self):
        file_name = QFileDialog.getOpenFileName(self,
                                                'Load Label File',
                                                QDir.currentPath(),
                                                "Label files (*.lbl)")
        if file_name:
            label_config = LabelConfig(str(file_name), False)
            self.model.set_cur_label(label_config)

    def _ld_glbl(self):
        file_name = QFileDialog.getOpenFileName(self,
                                                'Load Label File',
                                                QDir.currentPath(),
                                                "Label files (*.lbl)")
        if file_name:
            label_config = LabelConfig(str(file_name), True)
            self.model.set_global_label(label_config)

    def _grid_view(self):
        """
        Grid view option.

        """
        self._actions['grid_view'].setEnabled(False)
        self._actions['orth_view'].setEnabled(True)
        self._actions['hand'].setEnabled(False)
        self._actions['cursor'].trigger()

        self.centralWidget().layout().removeWidget(self.image_view)
        self.image_view.set_display_type('grid')
        self.model.scale_changed.disconnect()
        self.model.repaint_slices.disconnect()
        self.model.cross_pos_changed.disconnect(self.image_view.update_cross_pos)
        self.image_view.deleteLater()
        self._spinbox.setValue(100 * self.model.get_scale_factor('grid'))
        self.image_view = GridView(self.model, self.painter_status,
                                   self._gridview_vertical_scrollbar_position)
        self.centralWidget().layout().addWidget(self.image_view)

    def _orth_view(self):
        """
        Orth view option.
        """


        self._actions['orth_view'].setEnabled(False)
        self._actions['grid_view'].setEnabled(True)
        self._actions['hand'].setEnabled(True)
        self._actions['cursor'].trigger()

        self._gridview_vertical_scrollbar_position = \
            self.image_view.get_vertical_srollbar_position()
        self.centralWidget().layout().removeWidget(self.image_view)
        self.image_view.set_display_type('orth')
        self.model.scale_changed.disconnect()
        self.model.repaint_slices.disconnect()
        self.model.cross_pos_changed.disconnect(self.image_view.update_cross_pos)
        self.image_view.deleteLater()
        self._spinbox.setValue(100 * self.model.get_scale_factor('orth'))
        self.image_view = OrthView(self.model, self.painter_status)
        self.centralWidget().layout().addWidget(self.image_view)

    def _display_cross_hover(self):
        if self.model._display_cross:
            self.model.set_cross_status(False)
            self._actions['cross_hover_view'].setText('Enable cross hover')
            self._actions['cross_hover_view'].setIcon(QIcon(os.path.join(self._icon_dir,'cross_hover_disable.png')))
        else:
            self.model.set_cross_status(True)
            self._actions['cross_hover_view'].setText('Disable cross hover')
            self._actions['cross_hover_view'].setIcon(QIcon(os.path.join(self._icon_dir,'cross_hover_enable.png')))

    def _reset_view(self):
        """
        Reset view parameters.

        """
        if self.image_view.display_type() == 'orth':
            if not self.model.get_scale_factor('orth') == \
                    self.default_orth_scale_factor:
                self._spinbox.setValue(100 * self.default_orth_scale_factor)
            self.image_view.reset_view()
        elif self.image_view.display_type() == 'grid':
            if not self.model.get_scale_factor('grid') == \
                    self.default_grid_scale_factor:
                self._spinbox.setValue(100 * self.default_grid_scale_factor)

    def _binarization(self):
        binarization_dialog = BinarizationDialog(self.model)
        binarization_dialog.exec_()

    def _binaryerosion(self):
        binaryerosion_dialog = BinaryerosionDialog(self.model)
        binaryerosion_dialog.exec_()

    def _binarydilation(self):
        binarydilation_dialog = BinarydilationDialog(self.model)
        binarydilation_dialog.exec_()

    def _greyerosion(self):
        greyerosiondialog = GreyerosionDialog(self.model)
        greyerosiondialog.exec_()

    def _greydilation(self):
        greydilation_dialog = GreydilationDialog(self.model)
        greydilation_dialog.exec_()

    def _intersect(self):
        intersect_dialog = IntersectDialog(self.model)
        intersect_dialog.exec_()

    def _meants(self):
        new_dialog = MeanTSDialog(self.model)
        new_dialog.exec_()

    def _local_max(self):
        new_dialog = LocalMaxDialog(self.model, self)
        new_dialog.exec_()

    def _inverse(self):
        inverse_image(self.model)

    def _smooth(self):
        new_dialog = SmoothingDialog(self.model)
        new_dialog.exec_()

    def _region_grow(self):
        new_dialog = GrowDialog(self.model, self)
        new_dialog.exec_()

    def _watershed(self):
        new_dialog = WatershedDialog(self.model, self)
        new_dialog.exec_()

    def _cluster(self):
        new_dialog = ClusterDialog(self.model, self)
        new_dialog.exec_()

    def _functional_module_set_enabled(self, status):
        self._actions['binarization'].setEnabled(status)
        self._actions['intersect'].setEnabled(status)
        self._actions['meants'].setEnabled(status)
        self._actions['voxelstats'].setEnabled(status)
        self._actions['localmax'].setEnabled(status)
        self._actions['inverse'].setEnabled(status)
        self._actions['smoothing'].setEnabled(status)
        self._actions['region_grow'].setEnabled(status)
        self._actions['watershed'].setEnabled(status)
        self._actions['cluster'].setEnabled(status)
        self._actions['opening'].setEnabled(status)
        self._actions['binarydilation'].setEnabled(status)
        self._actions['binaryerosion'].setEnabled(status)
        self._actions['greydilation'].setEnabled(status)
        self._actions['greyerosion'].setEnabled(status)
        self._actions['regular_roi'].setEnabled(status)
        self._actions['r2i'].setEnabled(status)
        self._actions['edge_dete'].setEnabled(status)
        self._actions['roi_merge'].setEnabled(status)