Beispiel #1
0
class LoadImage(QtWidgets.QMainWindow, Ui_MainWindow):
    """Load board images from the menu and the pins icon list."""
    def __init__(self):
        """Set up the user interference from the QT Designer.

        Initiate the menu bar and clicks on menu options.
        """
        super().__init__()
        """Set up the user interference from QT Designer."""
        self.setupUi(self)

        self.df_cls = None
        self.board_type = None
        self.board_label = None
        self.first_load = True
        self.first_resize = True
        self.resize_timer = None
        self.list_items = {}

        self.centralwidget.setDisabled(True)

        # Creating two menu options: Boards and Export
        self.file_menu = self.menuBar().addMenu('&File')
        self.boards_menu = self.menuBar().addMenu('&Boards')
        # self.export_menu = self.menuBar().addMenu('&Export')

        self.refresh_menus()

        self.export_action = self.file_menu.addAction('Save Board Label')
        self.run_macro_action = self.file_menu.addAction('Run the Macro File')
        self.exit_action = self.file_menu.addAction('Exit')
        self.exit_action.triggered.connect(QtWidgets.qApp.quit)

        # self.export_action = self.export_menu.addAction('Save Board Label')

    def refresh_menus(self):
        self.boards_menu.clear()

        unprocessed_menu = self.boards_menu.addMenu('Unprocessed')
        processed_menu = self.boards_menu.addMenu('Processed')

        config = ConfigParser()

        config.read('socketear.ini')

        file_path = os.path.normpath(config.get('path', 'dirpath'))

        # Going through the Board folder to create the appropriate actions
        for dirpath, dirnames, filenames in os.walk(
                os.path.join(file_path, 'Processed')):

            if 'pins' in dirnames and all(
                    fn in filenames for fn in
                ['classification.csv', 'stitched.png', 'info.txt']):
                # Add actions for the right path
                action_cb = processed_menu.addAction(
                    os.path.split(os.path.basename(dirpath))[-1])
                action_cb.triggered.connect(
                    functools.partial(self.load_image, dirpath))

        for dirpath, dirnames, filenames in os.walk(
                os.path.join(file_path, 'Unprocessed')):
            if all(fn.endswith(".tif") for fn in filenames) and not dirnames:

                board_name = os.path.split(os.path.split(dirpath)[0])[1]
                save_path = os.path.join(file_path, 'Processed')
                action_nb = unprocessed_menu.addAction(board_name)
                action_nb.triggered.connect(
                    functools.partial(self.load_model, dirpath, board_name,
                                      save_path))

    def remove_board(self):
        """Remove the layer of matplotlib container and widgetlist.

        Also, disconnects all the buttons.
        """
        self.mpl_vl.removeWidget(self.canvas)
        self.canvas.close()
        # self.mpl_vl.removeWidget(self.toolbar)
        # self.toolbar.close()
        self.mpl_figs.clear()
        self.mpl_figs.currentItemChanged.disconnect()
        self.nwo_button.disconnect()
        self.nco_button.disconnect()
        self.normal_button.disconnect()
        self.hnp_button.disconnect()
        self.sb_button.disconnect()
        self.type1_button.disconnect()
        self.type2_button.disconnect()
        self.type3_button.disconnect()
        self.type4_button.disconnect()
        self.reject_button.disconnect()
        # self.no_crack_button.disconnect()
        self.type_a_button.disconnect()
        self.type_b_button.disconnect()
        self.type_c_button.disconnect()
        self.type_d_button.disconnect()
        self.type_e_button.disconnect()
        self.refresh_sjr_button.disconnect()
        self.refresh_sjq_button.disconnect()
        self.sjr_image_button.disconnect()
        self.export_action.disconnect()
        self.export_action.setDisabled(True)

    def add_mpl(self):
        """Add a layer of matplotlib container."""

        self.figure = Figure(figsize=(5, 4), dpi=100)
        self.canvas = FigureCanvas(self.figure)
        self.mpl_vl.addWidget(self.canvas)
        self.canvas.draw()
        # self.toolbar = NavigationToolbar(self.canvas,
        #                                  self.mpl_window,
        #                                  coordinates=True)
        # self.addToolBar(self.toolbar)

    def button_clicked(self, value):
        """Change the radio button.

        Updates the CSV file after a radio button was clicked.
        """
        pin_index = self.mpl_figs.currentItem().data(32)

        if self.board_type == 'SJR':
            if isinstance(value, six.string_types):
                self.df_cls.set_value(pin_index, 'DYE Correction', value)

            elif isinstance(value, int):
                self.df_cls.set_value(pin_index, 'SJR Correction', value)

        elif self.board_type == 'SJQ':
            self.df_cls.set_value(pin_index, 'SJQ Correction', value)

        image_path = self.df_cls.loc[pin_index]['Image Path']

        csv_path = os.path.split(
            os.path.split(os.path.normpath(image_path))[0])[0]

        # Saving the CSV file
        header = [
            'Row', 'Col', 'StitchedX', 'StitchedY', 'Pin', 'SJQ', 'SJR', 'DYE',
            'SJQ Correction', 'SJR Correction', 'DYE Correction',
            'Type Change', 'Sorted SJQ', 'Image Path', 'Label Coords',
            'DYE Image Path'
        ]
        self.df_cls.to_csv(os.path.join(csv_path, 'classification.csv'),
                           columns=header)

    def nwo_button_clicked(self, enabled):
        """Activate the nwo button for SJQ."""
        if enabled:
            self.button_clicked(0)

    def nco_button_clicked(self, enabled):
        """Activate the nco button for SJQ."""
        if enabled:
            self.button_clicked(1)

    def normal_button_clicked(self, enabled):
        """Activate the normal button for SJQ."""
        if enabled:
            self.button_clicked(2)

    def hnp_button_clicked(self, enabled):
        """Activate the hnp button for SJQ."""
        if enabled:
            self.button_clicked(3)

    def sb_button_clicked(self, enabled):
        """Activate the sb button for SJQ."""
        if enabled:
            self.button_clicked(4)

    def type1_button_clicked(self, enabled):
        """Activate the type1 button for SJR."""
        if enabled:
            self.button_clicked(0)

    def type2_button_clicked(self, enabled):
        """Activate the type2 button for SJR."""
        if enabled:
            self.button_clicked(1)

    def type3_button_clicked(self, enabled):
        """Activate the normal button for SJR."""
        if enabled:
            self.button_clicked(2)

    def type4_button_clicked(self, enabled):
        """Activate the type4 button for SJR."""
        if enabled:
            self.button_clicked(3)

    def reject_button_clicked(self, enabled):
        """Activate the type4 button for SJR."""
        if enabled:
            self.button_clicked(4)

    # def no_crack_button_clicked(self, enabled):
    #     """Activate the no crack button for SJR."""
    #     if enabled:
    #         self.button_clicked('O')

    def type_a_button_clicked(self, enabled):
        """Activate the typeA button for SJR."""
        if enabled:
            self.button_clicked('A')

    def type_b_button_clicked(self, enabled):
        """Activate the typeB button for SJR."""
        if enabled:
            self.button_clicked('B')

    def type_c_button_clicked(self, enabled):
        """Activate the typeC button for SJR."""
        if enabled:
            self.button_clicked('C')

    def type_d_button_clicked(self, enabled):
        """Activate the typeD button for SJR."""
        if enabled:
            self.button_clicked('D')

    def type_e_button_clicked(self, enabled):
        """Activate the typeE button for SJR."""
        if enabled:
            self.button_clicked('E')

    def refresh(self, predictions, dye_predictions=None):

        # start_refresh = datetime.now()

        self.board_label = self.drawing_tool(shape=1000,
                                             pred=predictions,
                                             dyepred=dye_predictions)

        # print('remove scatters!')

        self.axes1_scatter.remove()
        self.axes2_scatter.remove()

        self.axes2_image = self.axes2.imshow(self.board_label)

        self.canvas.draw()

        self.background_board = self.canvas.copy_from_bbox(self.axes2.bbox)

        pin_index = self.mpl_figs.currentItem().data(32)

        self.load_pin_cls(pin_index)

        cx_image = self.df_cls.ix[pin_index, 'StitchedX']
        cy_image = self.df_cls.ix[pin_index, 'StitchedY']

        self.axes1_scatter = self.axes1.scatter(cy_image,
                                                cx_image,
                                                edgecolor='#ff01d0',
                                                marker='s',
                                                s=80,
                                                linewidth='2',
                                                facecolors='none')

        cx_label = self.df_cls.ix[pin_index, 'Label Coords'][0] * 1000
        cy_label = self.df_cls.ix[pin_index, 'Label Coords'][1] * 1000

        self.axes2_scatter = self.axes2.scatter(cy_label,
                                                cx_label,
                                                edgecolor='#ff01d0',
                                                marker='s',
                                                s=80,
                                                linewidth='2',
                                                facecolors='none')

        self.canvas.blit(self.axes1.bbox)
        self.canvas.blit(self.axes2.bbox)

        # print('timer canvas.draw: ', datetime.now() - start_refresh)
        # start_refresh = datetime.now()

        self.sort_classification()

        # print('timer sort_classification: ', datetime.now() - start_refresh)
        # start_refresh = datetime.now()

        self.pin_dividers()

        # print('timer pin_dividers: ', datetime.now() - start_refresh)

        self.mpl_figs.clear()

        # start_refresh = datetime.now()

        self.load_pins()

        # print('timer load_pins: ', datetime.now() - start_refresh)

        self.mpl_figs.setCurrentItem(self.mpl_figs.item(0))
        board_pin_info = self.board_info + "\nPin name: {0}".format(
            str(self.df_cls.ix[pin_index, 'Pin']))
        self.board_display_label.setText(board_pin_info)

    def refresh_sjq_clicked(self):
        """Redraw the label board for SJQ model."""

        # start_refresh = datetime.now()
        print('Starting to refresh...')

        predictions = self.df_cls.set_index('Pin')['SJQ Correction'].to_dict()
        dye_predictions = None

        self.refresh(predictions, dye_predictions)

        # print('start_refresh: ', datetime.now() - start_refresh)

    def refresh_sjr_clicked(self):
        """Redraw the label board for SJQ model."""

        # start_refresh = datetime.now()
        print('Starting to refresh...')

        predictions = self.df_cls.set_index('Pin')['SJR Correction'].to_dict()
        dye_predictions = self.df_cls.set_index(
            'Pin')['DYE Correction'].to_dict()

        self.refresh(predictions, dye_predictions)

        # print('start_refresh: ', datetime.now() - start_refresh)

    def sjr_image_switch(self):
        """Switch the pin image and dyed pin image for SJR models."""

        pin_index = self.mpl_figs.currentItem().data(32)

        if self.pin_dye_image == self.df_cls.loc[pin_index, 'Image Path']:
            self.pin_dye_image = self.df_cls.loc[pin_index, 'DYE Image Path']
            self.pin_image.setPixmap(
                QtGui.QPixmap(QtGui.QImage(self.pin_dye_image)))

        elif self.pin_dye_image == self.df_cls.loc[pin_index,
                                                   'DYE Image Path']:
            self.pin_dye_image = self.df_cls.loc[pin_index, 'Image Path']
            self.pin_image.setPixmap(
                QtGui.QPixmap(QtGui.QImage(self.pin_dye_image)))

    def on_click(self, event):

        if not event.inaxes:
            print('Clicked outside axes bounds but inside plot window')
            return

        if event.inaxes == self.axes1:

            self.df_cls['Image Coords'] = self.df_cls[[
                'StitchedX', 'StitchedY'
            ]].apply(tuple, axis=1)
            z = self.df_cls[['StitchedX', 'StitchedY']].values
            distance = np.linalg.norm(
                (z - np.array([event.ydata, event.xdata])), axis=1)
            row_index = np.argmin(distance)

            current_item = None
            for index in range(self.mpl_figs.count()):
                item = self.mpl_figs.item(index)
                if item.data(32) == row_index:
                    current_item = item

            if current_item:
                self.mpl_figs.setCurrentItem(current_item)
                pin_index = current_item.data(32)
                self.load_pin_cls(pin_index)
            else:
                print('No item found for row {}'.format(row_index))

        if event.inaxes == self.axes2:
            z = np.zeros(shape=(len(self.guide.pins), 2))

            for j, pin in enumerate(self.guide.pins):
                z[j] = 1000 * np.array(self.guide.position(pin))

            distance = np.linalg.norm(
                (z - np.array([event.ydata, event.xdata])), axis=1)

            k = np.argmin(distance)

            pin_name = self.guide.pins[k]
            row = self.df_cls.Pin[self.df_cls.Pin ==
                                  pin_name].index.tolist()[0]

            current_item = None
            for index in range(self.mpl_figs.count()):
                item = self.mpl_figs.item(index)
                if item.data(32) == row:
                    current_item = item

            if current_item:
                self.mpl_figs.setCurrentItem(current_item)
                pin_index = current_item.data(32)
                self.load_pin_cls(pin_index)
            else:
                print('No item found for row {}'.format(row))

    def current_item_changed(self):
        """Update the zoom-in pin image and the board image position.

        Based on the items in the scrollable list(WidgetListItem).
        """
        if self.mpl_figs.currentItem():

            pin_index = self.mpl_figs.currentItem().data(32)

            self.load_pin_cls(pin_index)
            cx_label = self.df_cls.ix[pin_index, 'Label Coords'][0] * 1000
            cy_label = self.df_cls.ix[pin_index, 'Label Coords'][1] * 1000

            try:
                self.axes2_scatter.remove()
            except:
                pass

            self.axes2_scatter = self.axes2.scatter(cy_label,
                                                    cx_label,
                                                    edgecolor='#ff01d0',
                                                    marker='s',
                                                    s=80,
                                                    linewidth='2',
                                                    facecolors='none')

            cx_image = self.df_cls.ix[pin_index, 'StitchedX']
            cy_image = self.df_cls.ix[pin_index, 'StitchedY']

            try:
                self.axes1_scatter.remove()
            except:
                pass

            self.axes1_scatter = self.axes1.scatter(cy_image,
                                                    cx_image,
                                                    edgecolor='#ff01d0',
                                                    marker='s',
                                                    s=80,
                                                    linewidth='2',
                                                    facecolors='none')

            start_canvas = datetime.now()

            self.canvas.restore_region(self.background_image)
            self.canvas.restore_region(self.background_board)
            self.axes1.draw_artist(self.axes1_scatter)
            self.axes2.draw_artist(self.axes2_scatter)
            self.canvas.blit(self.axes1.bbox)
            self.canvas.blit(self.axes2.bbox)

            self.canvas.flush_events()
            # print('canvas: ', datetime.now() - start_canvas)
            board_pin_info = self.board_info + "\nPin name: {0}".format(
                str(self.df_cls.ix[pin_index, 'Pin']))
            self.board_display_label.setText(board_pin_info)

    def load_pin_cls(self, pin_index):
        """Load the zoom-in pin image.

        And activate the appropriate radio button.
        """

        self.pin_dye_image = self.df_cls.ix[pin_index, 'Image Path']
        self.pin_image.setScaledContents(True)
        self.pin_image.setPixmap(
            QtGui.QPixmap(QtGui.QImage(self.pin_dye_image)))

        if self.board_type == 'SJR':

            classification = self.df_cls.ix[pin_index, 'SJR Correction']
            dye_cls = self.df_cls.ix[pin_index, 'DYE Correction']

            if classification == 0:
                self.type1_button.setChecked(True)
            elif classification == 1:
                self.type2_button.setChecked(True)
            elif classification == 2:
                self.type3_button.setChecked(True)
            elif classification == 3:
                self.type4_button.setChecked(True)
            elif classification == 4:
                self.reject_button.setChecked(True)

            # if dye_cls == 'O':
            #     self.no_crack_button.setChecked(True)
            if dye_cls == 'A':
                self.type_a_button.setChecked(True)
            elif dye_cls == 'B':
                self.type_b_button.setChecked(True)
            elif dye_cls == 'C':
                self.type_c_button.setChecked(True)
            elif dye_cls == 'D':
                self.type_d_button.setChecked(True)
            elif dye_cls == 'E':
                self.type_e_button.setChecked(True)

        elif self.board_type == 'SJQ':

            classification = self.df_cls.ix[pin_index, 'SJQ Correction']

            if classification == 0:
                self.nwo_button.setChecked(True)
            elif classification == 1:
                self.nco_button.setChecked(True)
            elif classification == 2:
                self.normal_button.setChecked(True)
            elif classification == 3:
                self.hnp_button.setChecked(True)
            elif classification == 4:
                self.sb_button.setChecked(True)

    def sort_sjq(self, row):

        if row['SJQ Correction'] == 0:
            value = 'a'
        elif row['SJQ Correction'] == 1:
            value = 'b'
        elif row['SJQ Correction'] == 2:
            value = 'e'
        elif row['SJQ Correction'] == 3:
            value = 'c'
        elif row['SJQ Correction'] == 4:
            value = 'd'
        else:
            raise ValueError('Unknown classification')

        return value

    def set_buttons(self, column_names):

        if all(cn in column_names for cn in ['SJR', 'SJQ', 'DYE']):

            if not(self.df_cls['SJR'].isnull().all()) and \
                    not(self.df_cls['DYE'].isnull().all()) and \
                    self.df_cls['SJQ'].isnull().all():

                self.board_type = 'SJR'
                self.nwo_button.setDisabled(True)
                self.nco_button.setDisabled(True)
                self.normal_button.setDisabled(True)
                self.hnp_button.setDisabled(True)
                self.sb_button.setDisabled(True)
                self.refresh_sjq_button.setDisabled(True)
                self.type1_button.setDisabled(False)
                self.type2_button.setDisabled(False)
                self.type3_button.setDisabled(False)
                self.type4_button.setDisabled(False)
                self.reject_button.setDisabled(False)
                # self.no_crack_button.setDisabled(False)
                self.type_a_button.setDisabled(False)
                self.type_b_button.setDisabled(False)
                self.type_c_button.setDisabled(False)
                self.type_d_button.setDisabled(False)
                self.type_e_button.setDisabled(False)
                self.refresh_sjr_button.setDisabled(False)
                self.sjr_image_button.setDisabled(False)

            elif not(self.df_cls['SJR'].isnull().all()) and \
                    not(self.df_cls['SJQ'].isnull().all()) and \
                    self.df_cls['DYE'].isnull().all():

                self.board_type = 'SJQ'
                self.nwo_button.setDisabled(False)
                self.nco_button.setDisabled(False)
                self.normal_button.setDisabled(False)
                self.hnp_button.setDisabled(False)
                self.sb_button.setDisabled(False)
                self.refresh_sjq_button.setDisabled(False)
                self.type1_button.setDisabled(True)
                self.type2_button.setDisabled(True)
                self.type3_button.setDisabled(True)
                self.type4_button.setDisabled(True)
                self.reject_button.setDisabled(True)
                # self.no_crack_button.setDisabled(True)
                self.type_a_button.setDisabled(True)
                self.type_b_button.setDisabled(True)
                self.type_c_button.setDisabled(True)
                self.type_d_button.setDisabled(True)
                self.type_e_button.setDisabled(True)
                self.refresh_sjr_button.setDisabled(True)
                self.sjr_image_button.setDisabled(True)

            else:
                raise ValueError('Incomplete CSV file')

        else:
            raise ValueError('Unknown board')

    def sort_classification(self):

        if self.board_type == 'SJQ':
            self.df_cls['Sorted SJQ'] = self.df_cls['SJQ Correction']

            self.df_cls['Sorted SJQ'] = self.df_cls.apply(self.sort_sjq,
                                                          axis=1)
            self.df_cls = self.df_cls.sort_values(
                by=['Sorted SJQ', 'SJR Correction'], ascending=[True, True])
            self.df_cls = self.df_cls.reset_index(drop=True)

        if self.board_type == 'SJR':

            self.df_cls = self.df_cls.sort_values(
                by=['SJR Correction', 'DYE Correction'],
                ascending=[True, False])
            self.df_cls = self.df_cls.reset_index(drop=True)

    def pin_dividers(self):

        # Create columns to indicate the divider location for the QListWidgetItem
        if self.board_type == 'SJQ':
            self.df_cls['Type Change'] = self.df_cls['SJQ Correction'].shift(
                -1) != self.df_cls['SJQ Correction']
            if self.df_cls.ix[0, 'SJQ Correction'] != self.df_cls.ix[
                    1, 'SJQ Correction']:
                self.df_cls.ix[0, 'Type Change'] = True
            else:
                self.df_cls.ix[0, 'Type Change'] = False

        elif self.board_type == 'SJR':
            self.df_cls['Type Change'] = self.df_cls['SJR Correction'].shift(
                -1) != self.df_cls['SJR Correction']
            if self.df_cls.ix[0, 'SJR Correction'] != self.df_cls.ix[
                    1, 'SJR Correction']:
                self.df_cls.ix[0, 'Type Change'] = True
            else:
                self.df_cls.ix[0, 'Type Change'] = False

    def load_pins(self, first_load=False):

        if first_load:
            self.list_items = {}

        count_image_cat = 0

        # Create a list of the 'Image Path'  column
        image_path_values = self.df_cls['Image Path'].values.tolist()
        for image_path in self.df_cls['Image Path'].tolist():

            if first_load:
                icon = QtGui.QIcon(image_path)
            else:
                icon = self.list_items[image_path]

            # Adding the pin images to the scrollable list (WidgetListItem)
            item = QtWidgets.QListWidgetItem()
            item.setIcon(icon)
            self.list_items[image_path] = icon

            index = image_path_values.index(image_path)
            item.setData(32, index)

            self.mpl_figs.addItem(item)

            count_image_cat += 1

            if self.df_cls.iloc[index]['Type Change'] == True:

                mod_of_5 = count_image_cat % 5
                if mod_of_5 != 0:
                    amount_of_white = (5 - mod_of_5) + 5
                else:
                    amount_of_white = 5

                for x in range(0, amount_of_white):

                    item = QtWidgets.QListWidgetItem()  # delimiter
                    item.setData(32, -1)
                    item.setFlags(
                        QtCore.Qt.NoItemFlags)  # item should not be selectable
                    self.mpl_figs.addItem(item)

                count_image_cat = 0

    def get_pins(self, root):
        """Load list of pin icons."""

        # Reading the CSV file from the appropriate path
        self.df_cls = pd.read_csv(os.path.join(root, 'classification.csv'))

        if not ('SJQ Correction' in self.df_cls.columns):
            self.df_cls['SJQ Correction'] = self.df_cls['SJQ'].copy()
        if not ('SJR Correction' in self.df_cls.columns):
            self.df_cls['SJR Correction'] = self.df_cls['SJR'].copy()
        if not ('DYE Correction' in self.df_cls.columns):
            self.df_cls['DYE Correction'] = self.df_cls['DYE'].copy()

        # Adding the Pin column and adding the pin names to it by
        # combining the Col and Row Columns
        self.df_cls['Pin'] = self.df_cls[['Row', 'Col']].apply(
            lambda x: '{}{}{}'.format(x[0], '_', x[1]), axis=1)

        # Adding a label for the index column
        self.df_cls.columns.names = ['Index']

        column_names = list(self.df_cls)

        self.set_buttons(column_names)

        self.sort_classification()

        df_pin_list = self.df_cls['Pin'].tolist()

        # Adding the path of the each pin image to the Dataframe

        # Create a column in the dataframe to store the image paths
        self.df_cls['Image Path'] = np.nan

        # Create a column in the dataframe to store the DYE image paths
        self.df_cls['DYE Image Path'] = np.nan

        # Matching the pins with the correct image and adding them to
        # the 'Image Path' column

        # for every file (image_path) in the pins folder:
        for f in os.listdir(os.path.join(root, 'pins')):

            if f.endswith("_1.tif"):
                image_path = os.path.join(os.getcwd(),
                                          os.path.join(root, 'pins'), f)
                pin_name = os.path.basename(image_path).replace("_1.tif", "")

                # finding the index of the row where the pin is located in
                # the dataframe
                if pin_name in df_pin_list:

                    index = self.df_cls[self.df_cls['Pin'] ==
                                        pin_name].index[0]

                    # adding the pin's image path to the same row in the dataframe
                    # where the pin is located
                    self.df_cls.loc[index, 'Image Path'] = image_path

        if self.board_type == 'SJR':
            if 'dye' in os.listdir(os.path.join(root, '')):
                for f in os.listdir(os.path.join(root, 'dye')):

                    dye_path = os.path.join(os.getcwd(),
                                            os.path.join(root, 'dye'), f)
                    pin_name = os.path.basename(dye_path).replace("_1.tif", "")

                    if pin_name in df_pin_list:

                        index = self.df_cls[self.df_cls['Pin'] ==
                                            pin_name].index[0]

                        self.df_cls.loc[index, 'DYE Image Path'] = dye_path

            else:
                print('Unable to locate the DYE folder. Thus no dye images')
                self.sjr_image_button.setDisabled(True)

        self.pin_dividers()

        self.load_pins(first_load=True)

    def load_board(self, root):
        """Load the appropriate board based on the menu clicked."""
        self.centralwidget.setDisabled(True)

        print('Path: ', root)
        board_name = os.path.basename(root)

        print('Board name: ', board_name)
        print('Board type: ', self.board_type)

        new_font = QtGui.QFont("Arial", 10, QtGui.QFont.Bold)
        self.board_display_label.setFont(new_font)
        self.board_info = "Board name: {0}\nBoard type: {1}".format(
            board_name, self.board_type)
        self.board_display_label.setText(self.board_info)

        with open(os.path.join(root, 'info.txt')) as json_file:
            data = json.load(json_file)
            print('guide path: ', data['guidePath'])

        csvpath = data['guidePath']
        b2p_ratio = int(data['b2p_ratio'])
        from guides.generalguide import GeneralGuide

        self.guide = GeneralGuide(csvpath, b2p_ratio)

        pin_coords = []

        for pin in self.df_cls['Pin'].tolist():
            if pin in self.guide.pins:
                pin_coords.append(tuple(self.guide.position(pin)))

        self.df_cls['Label Coords'] = pd.Series(pin_coords,
                                                index=self.df_cls.index)

        self.drawing_tool = DrawPredictions(guide=self.guide,
                                            mode=self.board_type)

        cls_corrected_col = "{} Correction".format(self.board_type)
        # print('cls_corrected_col', cls_corrected_col)

        predictions = self.df_cls.set_index('Pin')[cls_corrected_col].to_dict()

        if self.board_type == 'SJR':
            dye_predictions = self.df_cls.set_index(
                'Pin')['DYE Correction'].to_dict()
            self.board_label = self.drawing_tool(shape=1000,
                                                 pred=predictions,
                                                 dyepred=dye_predictions)
        elif self.board_type == 'SJQ':
            self.board_label = self.drawing_tool(shape=1000, pred=predictions)
        else:
            raise ValueError('Unknown board')

        self.board_img = mpimg.imread(os.path.join(root, 'stitched.png'))

        gs = gridspec.GridSpec(1, 2)
        gs.update(left=0.005, right=0.99, wspace=0.05)

        self.axes1 = self.figure.add_subplot(gs[0])
        self.axes1.get_xaxis().set_visible(False)
        self.axes1.get_yaxis().set_visible(False)

        self.axes2 = self.figure.add_subplot(gs[1])
        self.axes2.get_xaxis().set_visible(False)
        self.axes2.get_yaxis().set_visible(False)

        # print('init scatter')

        self.axes1_scatter = None
        self.axes2_scatter = None

        self.canvas.mpl_connect('resize_event', self.connect_resize)
        self.export_action.triggered.connect(
            functools.partial(self.save_board_label, root))
        self.export_action.setDisabled(False)

        self.run_macro_action.triggered.connect(
            functools.partial(self.run_excel_macro, root))

        self.first_load = False

        self.centralwidget.setDisabled(False)

    def connect_resize(self, event):
        print('resize event', event)

        self.centralwidget.setDisabled(True)

        self.perform_resize()

        # try:
        #     print('resize event try', event)
        #     self.resize_timer.cancel()
        # except:
        #     pass
        #
        # self.resize_timer = Timer(1, self.perform_resize)
        # self.resize_timer.start()

    def perform_resize(self):
        # print('perform_resize')

        self.axes1.imshow(self.board_img)
        self.axes2.imshow(self.board_label)

        try:
            self.axes1_scatter.remove()
        except:
            print('Unable to remove axes1_scatter')
            pass

        try:
            self.axes2_scatter.remove()
        except:
            print('Unable to remove axes2_scatter')
            pass

        self.canvas.draw()

        self.background_image = self.canvas.copy_from_bbox(self.axes1.bbox)
        self.background_board = self.canvas.copy_from_bbox(self.axes2.bbox)

        self.centralwidget.setDisabled(False)

    def connect_click(self):
        """Just testing this."""

        # connect the click or arrow keys press on the the scrollable
        # image list to the currentItemChanged
        self.mpl_figs.currentItemChanged.connect(self.current_item_changed)

        self.nwo_button.toggled.connect(self.nwo_button_clicked)

        self.nco_button.toggled.connect(self.nco_button_clicked)

        self.normal_button.toggled.connect(self.normal_button_clicked)

        self.hnp_button.toggled.connect(self.hnp_button_clicked)

        self.sb_button.toggled.connect(self.sb_button_clicked)

        self.type1_button.toggled.connect(self.type1_button_clicked)

        self.type2_button.toggled.connect(self.type2_button_clicked)

        self.type3_button.toggled.connect(self.type3_button_clicked)

        self.type4_button.toggled.connect(self.type4_button_clicked)

        self.reject_button.toggled.connect(self.reject_button_clicked)

        # self.no_crack_button.toggled.connect(self.no_crack_button_clicked)

        self.type_a_button.toggled.connect(self.type_a_button_clicked)

        self.type_b_button.toggled.connect(self.type_b_button_clicked)

        self.type_c_button.toggled.connect(self.type_c_button_clicked)

        self.type_d_button.toggled.connect(self.type_d_button_clicked)

        self.type_e_button.toggled.connect(self.type_e_button_clicked)

        self.refresh_sjq_button.clicked.connect(self.refresh_sjq_clicked)

        self.refresh_sjr_button.clicked.connect(self.refresh_sjr_clicked)

        self.sjr_image_button.clicked.connect(self.sjr_image_switch)

        self.canvas.callbacks.connect('button_press_event', self.on_click)

    def save_board_label(self, path):
        import cv2
        cv2.imwrite(os.path.join(path, 'board.png'),
                    255 * self.board_label[:, :, ::-1])

    def run_excel_macro(self, path):

        try:
            xlApp = win32com.client.Dispatch('Excel.Application')
            xlsPath = os.path.abspath(os.path.join(path, 'MacroTest.xlsm'))
            print(xlsPath)
            macroxl = xlApp.Workbooks.Open(xlsPath)
            xlApp.Run('MacroTest.xlsm!TEST_1')
            macroxl.Save()
            xlApp.Quit()
            print("Macro ran successfully!")

        except Exception as e:
            print("Error found while running the excel macro!")
            raise

    def load_image(self, root):
        """Construct the board image and load it on matplotlib container."""
        # Rest the matplotlib container and the buttons

        print('Starting to load the images...')

        if not self.first_load:
            self.remove_board()

        # Add a matplotlib container
        self.add_mpl()

        # load pin images in the zoom-in container and
        # the scrollable image list
        self.get_pins(root)

        self.load_pin_cls(0)

        self.load_board(root)

        self.connect_click()

    def closeEvent(self, event):
        try:
            self.resize_timer.cancel()
        except:
            pass

    def load_model(self, root, board_name, save_path):

        default_settings = {
            'sampleID': board_name,
            'rawImgPath': root,
            'cropSavePath': os.path.join(save_path, "{}", 'pins'),
            'savePath': os.path.join(save_path, "{}"),
            'additionalCrop': 'None',
            'b2p_ratio': '150',
            'device': 'gpu0'
        }

        dialog = Ui_SettingsWindow(default_settings)
        dialog.setAttribute(QtCore.Qt.WA_DeleteOnClose)

        exit_code = dialog.exec_()
        # print(exit_code)

        if exit_code:
            # print(dialog.values)
            settings_dict = dialog.values

            userName = settings_dict['userName']
            sampleID = settings_dict['sampleID']
            analysisType = settings_dict['analysisType']
            rawImgPath = settings_dict['rawImgPath']
            cropSavePath = settings_dict['cropSavePath']
            segDataPath = settings_dict['segDataPath']
            savePath = settings_dict['savePath']
            guidePath = settings_dict['guidePath']
            additionalCrop = settings_dict['additionalCrop']
            b2p_ratio = int(settings_dict['b2p_ratio'])
            device = settings_dict['device']

            class TaskThread(QtCore.QThread):
                def run(self):
                    print("task is running")
                    try:
                        from guides.generalguide import GeneralGuide
                        guide = GeneralGuide(guidePath, b2p_ratio)

                        from model import Model
                        valid_additionalCrop = None if additionalCrop == 'None' else (
                            int(additionalCrop), int(additionalCrop))

                        f_model = Model(sampleID, analysisType, guide,
                                        valid_additionalCrop, rawImgPath,
                                        segDataPath, cropSavePath, savePath,
                                        device)

                        from time import time
                        st = time()
                        print('Starting the analysis...')
                        f_model(saveResults=True)
                        sp = time() - st
                        print('Analysis took {} seconds'.format(sp))
                    except Exception as e:
                        print(e)

            task_thread = TaskThread()

            from progress_bar import ConstantProgressBar
            progress_dialog = ConstantProgressBar(task_thread)
            exit_code = progress_dialog.exec_()
            print(exit_code)

            json.dump(settings_dict,
                      open(os.path.join(savePath, 'info.txt'), 'w'),
                      indent=4)

            self.refresh_menus()

            self.load_image(savePath)
Beispiel #2
0
class MPLCanvas(QtWidgets.QGroupBox):
    """Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.)."""
    @enum.unique
    class WindowTypes(enum.Enum):
        Rectangular = 0
        Hann = 1
        Flattop = 2
        Tukey_5Percent = 3

    windowFunctionMap = {
        WindowTypes.Rectangular:
        lambda M: windows.boxcar(M, sym=False),
        WindowTypes.Hann:
        lambda M: windows.hann(M, sym=False),
        WindowTypes.Flattop:
        lambda M: windows.flattop(M, sym=False),
        WindowTypes.Tukey_5Percent:
        lambda M: windows.tukey(M, sym=False, alpha=0.05),
    }

    dataIsPower = False
    dataSet = None
    prevDataSet = None
    _prevAxesLabels = None
    _axesLabels = None
    _prevDataLabel = None
    _dataLabel = None

    _lastPlotTime = 0
    _isLiveData = False

    def __init__(self, parent=None):
        style_mpl()

        super().__init__(parent)

        dpi = QtWidgets.qApp.primaryScreen().logicalDotsPerInch()
        self.fig = Figure(dpi=dpi)
        self.fig.patch.set_alpha(0)

        self.axes = self.fig.add_subplot(2, 1, 1)
        self.ft_axes = self.fig.add_subplot(2, 1, 2)

        self.canvas = FigureCanvasQTAgg(self.fig)
        self.mpl_toolbar = NavigationToolbar2QT(self.canvas, self)

        self.mpl_toolbar.addSeparator()

        self.autoscaleAction = self.mpl_toolbar.addAction("Auto-scale")
        self.autoscaleAction.setCheckable(True)
        self.autoscaleAction.setChecked(True)
        self.autoscaleAction.triggered.connect(self._autoscale)

        self.mpl_toolbar.addWidget(
            QtWidgets.QLabel("Fourier transform "
                             "window: "))

        self.windowComboBox = QtWidgets.QComboBox(self.mpl_toolbar)
        for e in MPLCanvas.WindowTypes:
            self.windowComboBox.addItem(e.name, e)
        self.mpl_toolbar.addWidget(self.windowComboBox)
        self.windowComboBox.currentIndexChanged.connect(self._replot)

        vbox = QtWidgets.QVBoxLayout(self)
        vbox.addWidget(self.mpl_toolbar)
        vbox.addWidget(self.canvas)
        vbox.setContentsMargins(0, 0, 0, 0)
        vbox.setStretch(0, 1)
        vbox.setStretch(1, 1)

        self.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
                           QtWidgets.QSizePolicy.Expanding)
        self.canvas.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
                                  QtWidgets.QSizePolicy.Expanding)
        self.updateGeometry()

        self.fig.tight_layout()

        self._lines = self.axes.plot([], [], [], [], animated=True)
        self._lines[0].set_alpha(0.25)
        self._ftlines = self.ft_axes.plot([], [], [], [], animated=True)
        self._ftlines[0].set_alpha(0.25)
        self.axes.legend(['Previous', 'Current'])
        self.ft_axes.legend(['Previous', 'Current'])
        self.axes.set_title('Data')
        self.ft_axes.set_title('Fourier transformed data')
        self._redraw()

        # Use a timer with a timeout of 0 to initiate redrawing of the canvas.
        # This ensures that the eventloop has run once more and prevents
        # artifacts.
        self._redrawTimer = QtCore.QTimer(self)
        self._redrawTimer.setSingleShot(True)
        self._redrawTimer.setInterval(100)
        self._redrawTimer.timeout.connect(self._redraw)

        # will be disconnected in drawDataSet() when live data is detected.
        self._redraw_id = self.canvas.mpl_connect('draw_event',
                                                  self._redraw_artists)

    def _redraw_artists(self, *args):
        if not self._isLiveData:
            self.axes.draw_artist(self._lines[0])
            self.ft_axes.draw_artist(self._ftlines[0])

        self.axes.draw_artist(self._lines[1])
        self.ft_axes.draw_artist(self._ftlines[1])

    def _redraw(self):
        self.fig.tight_layout()
        self.canvas.draw()
        self.backgrounds = [
            self.fig.canvas.copy_from_bbox(ax.bbox)
            for ax in (self.axes, self.ft_axes)
        ]
        self._redraw_artists()

    def showEvent(self, e):
        super().showEvent(e)
        self._redrawTimer.start()

    def resizeEvent(self, e):
        super().resizeEvent(e)
        self._redrawTimer.start()

    def get_ft_data(self, data):
        delta = np.mean(np.diff(data.axes[0]))
        winFn = self.windowFunctionMap[self.windowComboBox.currentData()]
        refUnit = 1 * data.data.units
        Y = np.fft.rfft(np.array(data.data / refUnit) * winFn(len(data.data)),
                        axis=0)
        freqs = np.fft.rfftfreq(len(data.axes[0]), delta)
        dBdata = 10 * np.log10(np.abs(Y))
        if not self.dataIsPower:
            dBdata *= 2
        data_slice = np.array(freqs) < 2.1
        return (freqs[data_slice], dBdata[data_slice])

    def _dataSetToLines(self, data, line, ftline):
        if data is None:
            line.set_data([], [])
            ftline.set_data([], [])
            return

        #data.data -= np.mean(data.data)
        line.set_data(data.axes[0], data.data)
        freqs, dBdata = self.get_ft_data(data)
        ftline.set_data(freqs, dBdata)

    def _autoscale(self, *, redraw=True):
        prev_xlim = self.axes.get_xlim()
        prev_ylim = self.axes.get_ylim()
        prev_ft_xlim = self.ft_axes.get_xlim()
        prev_ft_ylim = self.ft_axes.get_ylim()

        self.axes.relim()
        self.axes.autoscale()
        self.ft_axes.relim()
        self.ft_axes.autoscale()

        need_redraw = (prev_xlim != self.axes.get_xlim()
                       or prev_ylim != self.axes.get_ylim()
                       or prev_ft_xlim != self.ft_axes.get_xlim()
                       or prev_ft_ylim != self.ft_axes.get_ylim())

        if need_redraw and redraw:
            self._redraw()

        return need_redraw

    def _replot(self,
                redraw_axes=False,
                redraw_axes_labels=False,
                redraw_data_label=False):
        if not self._isLiveData:
            self._dataSetToLines(self.prevDataSet, self._lines[0],
                                 self._ftlines[0])
        self._dataSetToLines(self.dataSet, self._lines[1], self._ftlines[1])

        if self._axesLabels and redraw_axes_labels:
            self.axes.set_xlabel('{} [{:C~}]'.format(
                self._axesLabels[0], self.dataSet.axes[0].units))
            self.ft_axes.set_xlabel('1 / {} [1 / {:C~}]'.format(
                self._axesLabels[0], self.dataSet.axes[0].units))

        if self._dataLabel and redraw_data_label:
            self.axes.set_ylabel('{} [{:C~}]'.format(self._dataLabel,
                                                     self.dataSet.data.units))

            ftUnits = self.dataSet.data.units
            if not self.dataIsPower:
                ftUnits = ftUnits**2

            self.ft_axes.set_ylabel('Power [dB-({:C~})]'.format(ftUnits))

        axis_limits_changed = False
        if (self.autoscaleAction.isChecked()):
            axis_limits_changed = self._autoscale(redraw=False)

        # check whether a full redraw is necessary or if simply redrawing
        # the data lines is enough
        if (redraw_axes or redraw_axes_labels or redraw_data_label
                or axis_limits_changed):
            self._redraw()
        else:
            for bg in self.backgrounds:
                self.canvas.restore_region(bg)
            self._redraw_artists()
            self.canvas.blit(self.axes.bbox)
            self.canvas.blit(self.ft_axes.bbox)

    def drawDataSet(self, newDataSet, axes_labels, data_label):
        plotTime = time.perf_counter()

        looksLikeLiveData = plotTime - self._lastPlotTime < 1

        if looksLikeLiveData != self._isLiveData:
            if looksLikeLiveData:
                self.canvas.mpl_disconnect(self._redraw_id)
            else:
                self._redraw_id = self.canvas.mpl_connect(
                    'draw_event', self._redraw_artists)

        self._isLiveData = looksLikeLiveData

        # artificially limit the replot rate to 5 Hz
        if (plotTime - self._lastPlotTime < 0.2):
            return

        self._lastPlotTime = plotTime

        self.prevDataSet = self.dataSet
        self.dataSet = newDataSet

        redraw_axes = (self.prevDataSet is None
                       or len(self.prevDataSet.axes) != len(self.dataSet.axes))
        if not redraw_axes:
            for x, y in zip(self.prevDataSet.axes, self.dataSet.axes):
                if x.units != y.units:
                    redraw_axes = True
                    break

        redraw_axes_labels = (
            self._axesLabels != axes_labels
            or self.prevDataSet and self.dataSet
            and self.prevDataSet.axes[0].units != self.dataSet.axes[0].units)
        redraw_data_label = (
            self._dataLabel != data_label or self.prevDataSet and self.dataSet
            and self.prevDataSet.data.units != self.dataSet.data.units)

        self._axesLabels = axes_labels
        self._dataLabel = data_label

        self._replot(redraw_axes, redraw_axes_labels, redraw_data_label)
Beispiel #3
0
class MainWidget(QWidget):
    def __init__(self):
        super(MainWidget, self).__init__()
        self.initUI()

    def initUI(self):
        self.resize(1100, 650)
        self.setWindowTitle("Stok Viewer")
        vbox = QVBoxLayout()
        self.setLayout(vbox)

        dailyBtn = QPushButton("Daily", self)
        weeklyBtn = QPushButton("Weeky", self)
        monthlyBtn = QPushButton("Monthly", self)
        dailyBtn.clicked.connect(lambda: self.plot("day"))
        weeklyBtn.clicked.connect(lambda: self.plot("week"))
        monthlyBtn.clicked.connect(lambda: self.plot("month"))
        timelineBtnBox = QHBoxLayout()
        timelineBtnBox.addStretch(1)
        timelineBtnBox.addWidget(dailyBtn)
        timelineBtnBox.addWidget(weeklyBtn)
        timelineBtnBox.addWidget(monthlyBtn)

        # widgets and layouts for plotting
        self.ybuffer = 5.5  # buffer area for bottom and top(%)
        self.zoomSpeed = 5  # How much area to zoom for one scroll
        self.figure = Figure()
        self.canvas = Canvas(self.figure)
        self.canvas.mpl_connect("button_press_event", self.onPressEvent)
        self.canvas.mpl_connect("button_release_event", self.onReleaseEvent)
        self.canvas.mpl_connect("scroll_event", self.onScrollEvent)
        self.chartBox = QHBoxLayout()
        self.chartBox.addWidget(self.canvas)
        vbox.addLayout(timelineBtnBox)
        vbox.addLayout(self.chartBox)
        self.plot("day")
        self.show()

    def plot(self, timeline=""):
        self.figure.clf()
        self.df = DataFetcher.getStockByTimeline("006400.KS", timeline)
        self.ax = self.figure.add_subplot()
        rsiList = getRsi(self.df)
        self.canvas.draw_idle()
        custplot(self.ax, self.df)

        ### 중복되는 기능, delete it later
        self.lineEndList = []
        self.lineStartList = []
        for obj in self.ax.get_children():
            if type(obj) is type(matplotlib.collections.LineCollection([])):
                self.lineStartList.append(
                    obj.get_paths()[0].get_extents().get_points()[0][1])
                self.lineEndList.append(
                    obj.get_paths()[0].get_extents().get_points()[1][1])
        ###
        self.autoscale()

    def onPressEvent(self, event):
        # check if the pressed button was "LEFT"
        if not event.button == 1:
            return

        self.lineEndList = []
        self.lineStartList = []
        for obj in self.ax.get_children():
            if type(obj) is type(matplotlib.collections.LineCollection([])):
                self.lineStartList.append(
                    obj.get_paths()[0].get_extents().get_points()[0][1])
                self.lineEndList.append(
                    obj.get_paths()[0].get_extents().get_points()[1][1])

        for obj in self.ax.get_children():
            if type(obj) is type(matplotlib.patches.Rectangle):
                break
                #print(obj)

        self.xStart = self.ax.get_xlim()[0]
        self.xCursor = event.x
        self.cidDrag = self.canvas.mpl_connect("motion_notify_event",
                                               self.onDragEvent)

    def onDragEvent(self, event):
        xlim = self.ax.get_xlim()
        # make drag limits to prevent overflow
        ###
        # IMPLMENT PENDING
        ###
        movement = (event.x - self.xCursor) / 10
        self.xCursor = event.x
        self.ax.set_xlim(xlim[0] - movement, xlim[1] - movement)
        self.autoscale()
        self.canvas.draw_idle()

    def onReleaseEvent(self, event):
        self.canvas.mpl_disconnect(self.cidDrag)

    # method for getting new range of axes and updating them to the df
    def updateAxes(self):
        # calculate how much x axis was moved
        xlim = self.ax.get_xlim()
        xEnd = -xlim[0]

        # get new df
        ogdate = self.df["Date"].iloc[0]
        newdf = DataFetcher.getStockByDate("006400.KS", ogdate, xEnd)
        newax = custplot(self.ax, newdf, timeline="day")

        # update the new df to the ax
        background = self.canvas.copy_from_bbox(self.ax.bbox)
        self.ax.draw_artist(newax)
        self.canvas.blit(self.ax.bbox)

    def onScrollEvent(self, event):
        xlim = self.ax.get_xlim()
        width = xlim[1] - xlim[0]
        if event.button == "up" or event.button == "down":
            if event.button == "up":
                xmin = xlim[0] + (width / 100 * self.zoomSpeed)
                xmax = xlim[1] - (width / 100 * self.zoomSpeed)
            else:
                xmin = xlim[0] - (width / 100 * self.zoomSpeed)
                xmax = xlim[1] + (width / 100 * self.zoomSpeed)
            self.ax.set_xlim(xmin, xmax)
            self.autoscale()
            self.canvas.draw_idle()
        else:
            print("error: unrecognized scroll movement")

    # method for autoscaling y axis heights according to y limits
    def autoscale(self):
        xlim = self.ax.get_xlim()
        ylim = self.ax.get_ylim()
        # check if updating lines are out of bound
        start = int(xlim[0]) if int(xlim[0]) > 0 else 0
        end = int(xlim[1]) if int(xlim[1]) < len(self.df["High"]) else (
            len(self.df["High"]) - 1)
        # get the highest and lowest point of y from start to end
        highest = max(self.lineEndList[start:end + 1])
        lowest = min(self.lineStartList[start:end + 1])

        height = highest - lowest
        self.ax.set_ylim(lowest - (height / 100 * self.ybuffer),
                         highest + (height / 100 * self.ybuffer))
class twoDimage(QWidget):
    def __init__(self, viewer):
        super(twoDimage, self).__init__()
        self.data = Spectrum()
        self.viewer = viewer
        self.aspect = "auto"

        #init Image
        self.fig = Figure(figsize=(5, 3), dpi=150)
        gs = GridSpec(3, 3, figure=self.fig)
        self.ax_XDC = self.fig.add_subplot(gs[0, 0:2])  # X Distribution Curve
        self.ax_Spec = self.fig.add_subplot(gs[1:, 0:2])  # Spectrum
        self.ax_YDC = self.fig.add_subplot(gs[1:, -1])  # Y Distribution Curve
        self.lx1 = self.ax_Spec.axhline(self.ax_Spec.get_ybound()[0],
                                        visible=False,
                                        animated=True,
                                        color='k',
                                        linestyle='--',
                                        linewidth=0.75)  # the horiz line 1
        self.ly1 = self.ax_Spec.axvline(self.ax_Spec.get_xbound()[0],
                                        visible=False,
                                        animated=True,
                                        color='k',
                                        linestyle='--',
                                        linewidth=0.75)  # the vert line 1
        self.lx2 = self.ax_Spec.axhline(self.ax_Spec.get_ybound()[0],
                                        visible=False,
                                        animated=True,
                                        color='k',
                                        linestyle='--',
                                        linewidth=0.75)  # the horiz line 2
        self.ly2 = self.ax_Spec.axvline(self.ax_Spec.get_xbound()[0],
                                        visible=False,
                                        animated=True,
                                        color='k',
                                        linestyle='--',
                                        linewidth=0.75)  # the vert line 2
        self.vl = self.ax_XDC.axvline(self.ax_XDC.get_xbound()[0],
                                      visible=False,
                                      animated=True,
                                      color='k',
                                      linestyle='--',
                                      linewidth=0.75)
        self.hl = self.ax_YDC.axhline(self.ax_YDC.get_ybound()[0],
                                      visible=False,
                                      animated=True,
                                      color='k',
                                      linestyle='--',
                                      linewidth=0.75)  # the horiz line
        self.ax_cmap = self.fig.add_subplot(gs[0, 2])  # color bar
        self.gradient = np.vstack((np.linspace(0, 1,
                                               256), np.linspace(0, 1, 256)))
        self.setTickLabelFont(self.ax_Spec, "Comic Sans MS")
        self.setTickLabelFont(self.ax_XDC, "Comic Sans MS")
        self.setTickLabelFont(self.ax_YDC, "Comic Sans MS")
        self.ax_Spec.set_axisbelow(False)
        self.ax_Spec.tick_params(direction='inout', labelsize=8.5)
        self.ax_XDC.tick_params(axis='x', labelsize=8.5, labelbottom=False)
        self.ax_XDC.tick_params(axis='y',
                                which='both',
                                left=False,
                                labelleft=False)
        self.ax_YDC.tick_params(axis='x',
                                which='both',
                                bottom=False,
                                labelbottom=False)
        self.ax_YDC.tick_params(axis='y', labelsize=8.5, labelleft=False)
        self.fig.subplots_adjust(top=0.96,
                                 bottom=0.08,
                                 left=0.1,
                                 right=0.96,
                                 wspace=0.15,
                                 hspace=0.15)
        x0, y0, width, height = self.ax_cmap.get_position().bounds
        nheight = height * 0.2
        ny0 = y0 + height * 0.5 * (1 - 0.2)
        self.ax_cmap.set_position([x0, ny0, width, nheight])
        self.ax_cmap.tick_params(left=False,
                                 labelleft=False,
                                 bottom=False,
                                 labelbottom=False)
        self.ax_cmap.set_title("Color Scale", {'fontsize': 10},
                               fontname="Comic Sans Ms")
        self.fig.patch.set_facecolor("None")
        self.canvas = FigureCanvas(self.fig)
        self.canvas.setStyleSheet("background-color:transparent;")
        self.canvas.setFocusPolicy(Qt.ClickFocus)
        self.XDCLine = Line2D(np.array([0]),
                              np.array([0]),
                              color="blue",
                              linewidth=1,
                              animated=True,
                              solid_joinstyle="round")
        self.YDCLine = Line2D(np.array([0]),
                              np.array([0]),
                              color="blue",
                              linewidth=1,
                              animated=True,
                              solid_joinstyle="round")
        self.ax_XDC.add_line(self.XDCLine)
        self.ax_YDC.add_line(self.YDCLine)
        self.selector = Selector(self.ax_Spec, self.canvas)
        self.cid_press = self.canvas.mpl_connect('button_press_event',
                                                 self.OnPress)
        self.cid_release = self.canvas.mpl_connect('button_release_event',
                                                   self.OnRelease)
        self.cid_keypress = self.canvas.mpl_connect('key_press_event',
                                                    self.OnKeyPress)
        #self.cid_scroll = self.canvas.mpl_connect('scroll_event', self.OnExpand)

        #contextMenu
        self.contextMenu = QMenu(self)
        self.CropAction = self.contextMenu.addAction("Crop")
        self.CropAction.setIcon(QIcon("./image/crop.ico"))
        self.CropAction.triggered.connect(self.crop)
        self.RestoreAction = self.contextMenu.addAction("Restore")
        self.RestoreAction.setIcon(QIcon("./image/restore.ico"))
        self.RestoreAction.triggered.connect(
            self.viewer.Win.DataProcessor.tab_single.RestoreData)
        self.SelectMenu = self.contextMenu.addMenu("Select")
        self.SelectMenu.setIcon(QIcon("./image/select.ico"))
        self.SelectX = self.SelectMenu.addAction("X Range")
        self.SelectX.triggered.connect(self.setSelectionRange)
        self.SelectY = self.SelectMenu.addAction("Y Range")
        self.SelectY.triggered.connect(self.setSelectionRange)
        self.SelectXY = self.SelectMenu.addAction("XY Area")
        self.SelectXY.triggered.connect(self.setSelectionRange)
        self.outputMenu = self.contextMenu.addMenu("Output")
        self.outputMenu.setIcon(QIcon("./image/output.ico"))
        self.XDC_action = self.outputMenu.addAction("XDC")
        self.YDC_action = self.outputMenu.addAction("YDC")

        #init Tool
        self.toolPanel = QGroupBox("Coordinate System")
        self.toolPanel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.toolPanel.setCheckable(True)
        self.toolPanel.setChecked(False)
        self.toolPanel.setStyle(QStyleFactory.create('Fusion'))
        self.toolPanel.toggled.connect(self.begincursor)
        self.X = QDoubleSpinBox()
        self.X.setFixedWidth(100)
        self.X.setDecimals(6)
        self.X.setRange(-1, 1)
        self.X.setSingleStep(0.01)
        self.X.setKeyboardTracking(False)
        self.X.setStyle(QStyleFactory.create('Fusion'))
        self.X.setValue(-1)
        self.X.valueChanged.connect(self.setValueFromSpinBox)
        self.XLabel = QLabel("X:")
        self.XLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.Y = QDoubleSpinBox()
        self.Y.setFixedWidth(100)
        self.Y.setDecimals(6)
        self.Y.setRange(-1, 1)
        self.Y.setSingleStep(0.01)
        self.Y.setKeyboardTracking(False)
        self.Y.setStyle(QStyleFactory.create('Fusion'))
        self.Y.setValue(-1)
        self.Y.valueChanged.connect(self.setValueFromSpinBox)
        self.YLabel = QLabel("Y:")
        self.YLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        font = QFont()
        font.setPointSize(12)
        font.setBold(True)
        self.IntLabel = QLabel("Count:")
        self.IntLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.Int = QLabel("0")
        self.Int.setFont(font)
        self.Int.setMinimumWidth(100)
        self.Int.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        self.Int.setStyleSheet("QLabel {background-color: white;}")
        self.Int.setFrameStyle(QFrame.Panel)
        self.XSlider = QSlider(Qt.Horizontal)
        self.XSlider.setRange(0, 10000)
        self.XSlider.setStyle(QStyleFactory.create('Fusion'))
        self.XSlider.sliderMoved.connect(self.setValueFromSlider)
        self.XSLabel = QLabel("X:")
        self.YSlider = QSlider(Qt.Horizontal)
        self.YSlider.setRange(0, 10000)
        self.YSlider.setStyle(QStyleFactory.create('Fusion'))
        self.YSlider.sliderMoved.connect(self.setValueFromSlider)
        self.YSLabel = QLabel("Y:")
        self.XSlice = QSpinBox()
        self.XSlice.setRange(1, 10000)
        self.XSlice.setSingleStep(2)
        self.XSlice.setKeyboardTracking(False)
        self.XSlice.setStyle(QStyleFactory.create('Fusion'))
        self.XSlice.valueChanged.connect(self.setSlice)
        self.XSliceLabel = QLabel("Slice:")
        self.YSlice = QSpinBox()
        self.YSlice.setRange(1, 10000)
        self.YSlice.setSingleStep(2)
        self.YSlice.setKeyboardTracking(False)
        self.YSlice.setStyle(QStyleFactory.create('Fusion'))
        self.YSlice.valueChanged.connect(self.setSlice)
        self.YSliceLabel = QLabel("Slice:")
        hbox_tool = QHBoxLayout()
        hbox_tool.addStretch(2)
        hbox_tool.addWidget(self.XLabel)
        hbox_tool.addWidget(self.X)
        hbox_tool.addStretch(1)
        hbox_tool.addWidget(self.YLabel)
        hbox_tool.addWidget(self.Y)
        hbox_tool.addStretch(1)
        hbox_tool.addWidget(self.IntLabel)
        hbox_tool.addWidget(self.Int)
        hbox_tool.addStretch(2)
        hbox2_tool = QHBoxLayout()
        hbox2_tool.addWidget(self.XSLabel)
        hbox2_tool.addWidget(self.XSlider)
        hbox2_tool.addWidget(self.XSliceLabel)
        hbox2_tool.addWidget(self.XSlice)
        hbox3_tool = QHBoxLayout()
        hbox3_tool.addWidget(self.YSLabel)
        hbox3_tool.addWidget(self.YSlider)
        hbox3_tool.addWidget(self.YSliceLabel)
        hbox3_tool.addWidget(self.YSlice)
        vbox_tool = QVBoxLayout()
        vbox_tool.addLayout(hbox_tool)
        vbox_tool.addLayout(hbox2_tool)
        vbox_tool.addLayout(hbox3_tool)
        self.toolPanel.setLayout(vbox_tool)

        #color scale
        self.colorPanel = QGroupBox("Color Scale")
        self.colorPanel.setFixedHeight(105)
        self.colorPanel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.cmapmenu = CustomComboBox()
        self.cmapmenu.setFixedWidth(120)
        self.cmapmenu.setStyle(QStyleFactory.create('Fusion'))
        self.cmapmenu.setCurrentText("twilight")
        self.cmapmenu.currentIndexChanged.connect(self.changecmapbyindex)
        self.Vmin = QDoubleSpinBox()
        self.Vmin.setFixedWidth(100)
        self.Vmin.setRange(-1e15, 1e15)
        self.Vmin.setDecimals(4)
        self.Vmin.setKeyboardTracking(False)
        self.Vmin.valueChanged.connect(self.setDynamicRange)
        self.Vmin.setStyle(QStyleFactory.create('Fusion'))
        self.VminLabel = QLabel("Min:")
        self.VminLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.Vmax = QDoubleSpinBox()
        self.Vmax.setFixedWidth(100)
        self.Vmax.setRange(-1e15, 1e15)
        self.Vmax.setDecimals(4)
        self.Vmax.setKeyboardTracking(False)
        self.Vmax.valueChanged.connect(self.setDynamicRange)
        self.Vmax.setStyle(QStyleFactory.create('Fusion'))
        self.VmaxLabel = QLabel("Max:")
        self.VmaxLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.resetVrange = QPushButton("Re")
        self.resetVrange.setFixedWidth(25)
        self.resetVrange.clicked.connect(self.resetDynamicRange)
        self.resetVrange.setStyle(QStyleFactory.create('Fusion'))
        self.revcmap = QCheckBox("R")
        self.revcmap.stateChanged.connect(self.reversecmap)
        self.GammaLabel = QLabel("Gamma:")
        self.GammaLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.Gamma = QDoubleSpinBox()
        self.Gamma.setFixedWidth(100)
        self.Gamma.setDecimals(3)
        self.Gamma.setRange(0.05, 20)
        self.Gamma.setSingleStep(0.01)
        self.Gamma.setValue(1)
        self.Gamma.setKeyboardTracking(False)
        self.Gamma.valueChanged.connect(self.setGammaFromSpinBox)
        self.Gamma.setStyle(QStyleFactory.create('Fusion'))
        self.GSlider = QSlider(Qt.Horizontal)
        self.GSlider.setRange(1000000 * np.log(0.05), 1000000 * np.log(20))
        self.GSlider.setValue(0)
        self.GSlider.sliderMoved.connect(self.setGammaFromSlider)
        self.GSlider.setStyle(QStyleFactory.create('Fusion'))
        hbox1_cmap = QHBoxLayout()
        hbox1_cmap.addWidget(self.cmapmenu)
        hbox1_cmap.addStretch(1)
        hbox1_cmap.addWidget(self.VminLabel)
        hbox1_cmap.addWidget(self.Vmin)
        hbox1_cmap.addStretch(1)
        hbox1_cmap.addWidget(self.VmaxLabel)
        hbox1_cmap.addWidget(self.Vmax)
        hbox1_cmap.addStretch(1)
        hbox1_cmap.addWidget(self.resetVrange)
        hbox2_cmap = QHBoxLayout()
        hbox2_cmap.addWidget(self.GammaLabel)
        hbox2_cmap.addWidget(self.Gamma)
        hbox2_cmap.addWidget(self.GSlider)
        hbox2_cmap.addWidget(self.revcmap)
        vbox_cmap = QVBoxLayout()
        vbox_cmap.addLayout(hbox1_cmap)
        vbox_cmap.addLayout(hbox2_cmap)
        self.colorPanel.setLayout(vbox_cmap)
        self.colorPanel.setEnabled(False)

        #Layout
        box = QVBoxLayout()
        box.addWidget(self.toolPanel)
        box.addWidget(self.canvas)
        box.addWidget(self.colorPanel)
        self.setLayout(box)

    def loaddata(self, data):
        self.canvas.draw()
        self.selector.set_visible(False)
        self.data = data
        self.GetWhiteBackground()
        self.X.setRange(data.xmin, data.xmax)
        self.X.setSingleStep(data.xstep)
        self.Y.setRange(data.ymin, data.ymax)
        self.Y.setSingleStep(data.ystep)
        newX = 0
        if data.xmin * data.xmax < 0:
            newX = 0
        else:
            newX = (data.xmin + data.xmax) / 2
        if round(newX, 6) == self.X.value():
            self.X.valueChanged.emit(newX)
        else:
            self.X.setValue(newX)
        newY = 0
        if data.ymin * data.ymax < 0:
            newY = 0
        else:
            newY = (data.ymin + data.ymax) / 2
        if round(newY, 6) == self.Y.value():
            self.Y.valueChanged.emit(newY)
        else:
            self.Y.setValue(newY)
        if data.data[~np.isnan(data.data)].max() > 0:
            self.Vmax.setValue(data.data[~np.isnan(data.data)].max())
            self.Vmin.setValue(data.data[~np.isnan(data.data)].min())
        else:
            self.Vmin.setValue(data.data[~np.isnan(data.data)].min())
            self.Vmax.setValue(data.data[~np.isnan(data.data)].max())
        self.XSlice.setRange(1, self.data.dimension[0])
        self.YSlice.setRange(1, self.data.dimension[1])
        self.plotimage(data, self.getcurrentcmap(), self.Vmin.value(),
                       self.Vmax.value(), False)
        self.updatecolorscale(self.getcurrentcmap(), False)
        self.updateAxesPosition(True)
        self.colorPanel.setEnabled(True)

    def setValueFromSlider(self, value):
        if self.sender() == self.XSlider:
            self.X.setValue(value *
                            (self.X.maximum() - self.X.minimum()) / 10000 +
                            self.X.minimum())
        elif self.sender() == self.YSlider:
            self.Y.setValue(value *
                            (self.Y.maximum() - self.Y.minimum()) / 10000 +
                            self.Y.minimum())

    def setValueFromSpinBox(self, value):
        if self.sender() == self.X:
            self.XSlider.setValue(
                (value - self.X.minimum()) /
                (self.X.maximum() - self.X.minimum()) * 10000)
            half_wid_num = (self.XSlice.value() - 1) / 2
            self.ly1.set_xdata(value - self.data.xstep * half_wid_num)
            self.ly2.set_xdata(value + self.data.xstep * half_wid_num)
            self.vl.set_xdata(value)
            self.YDC = self.get_YDC(value)
            self.resetYDC()
            self.plotYDC(True, True)
            self.plotXDC(True, True)
        elif self.sender() == self.Y:
            self.YSlider.setValue(
                (value - self.Y.minimum()) /
                (self.Y.maximum() - self.Y.minimum()) * 10000)
            half_wid_num = (self.YSlice.value() - 1) / 2
            self.lx1.set_ydata(value - self.data.ystep * half_wid_num)
            self.lx2.set_ydata(value + self.data.ystep * half_wid_num)
            self.hl.set_ydata(value)
            self.XDC = self.get_XDC(value)
            self.resetXDC()
            self.plotXDC(True, True)
            self.plotYDC(True, True)
        self.plotArtist()
        self.setIntensity()

    def setIntensity(self):
        intensity = self.get_intensity(self.X.value(), self.Y.value())
        if np.isnan(intensity):
            self.Int.setText("NaN")
        elif np.abs(intensity) >= 0.01:
            self.Int.setText("%.2f" % intensity)
        else:
            self.Int.setText("%.2e" % intensity)

    def setSlice(self, value):
        if self.sender() == self.XSlice:
            if value % 2 == 0:
                self.XSlice.setValue(value - 1)
            else:
                if value == 1:
                    self.ly2.set_visible(False)
                else:
                    self.ly2.set_visible(True)
                half_wid_num = (self.XSlice.value() - 1) / 2
                self.ly1.set_xdata(self.X.value() -
                                   self.data.xstep * half_wid_num)
                self.ly2.set_xdata(self.X.value() +
                                   self.data.xstep * half_wid_num)
                self.plotArtist()
                self.YDC = self.get_YDC(self.X.value())
                self.resetYDC()
                self.plotYDC(True, True)
        if self.sender() == self.YSlice:
            if value % 2 == 0:
                self.YSlice.setValue(value - 1)
            else:
                if value == 1:
                    self.lx2.set_visible(False)
                else:
                    self.lx2.set_visible(True)
                half_wid_num = (self.YSlice.value() - 1) / 2
                self.lx1.set_ydata(self.Y.value() -
                                   self.data.ystep * half_wid_num)
                self.lx2.set_ydata(self.Y.value() +
                                   self.data.ystep * half_wid_num)
                self.plotArtist()
                self.XDC = self.get_XDC(self.Y.value())
                self.resetXDC()
                self.plotXDC(True, True)
        self.setIntensity()

    def setDynamicRange(self, value):
        if self.sender() == self.Vmin:
            if value <= self.Vmax.value():
                self.plotimage(self.data, self.getcurrentcmap(), value,
                               self.Vmax.value(), True)
            else:
                self.plotimage(self.data, self.getcurrentcmap(),
                               self.Vmax.value(), self.Vmax.value(), True)
        elif self.sender() == self.Vmax:
            if value >= self.Vmin.value():
                self.plotimage(self.data, self.getcurrentcmap(),
                               self.Vmin.value(), value, True)
            else:
                self.plotimage(self.data, self.getcurrentcmap(),
                               self.Vmin.value(), self.Vmin.value(), True)

    def resetDynamicRange(self):
        self.Vmin.setValue(self.data.data[~np.isnan(self.data.data)].min())
        self.Vmax.setValue(self.data.data[~np.isnan(self.data.data)].max())

    def setTickLabelFont(self, ax, font):
        for tick in ax.get_xticklabels():
            tick.set_fontname(font)
        for tick in ax.get_yticklabels():
            tick.set_fontname(font)

    def scale2pnt(self, value, scale):
        return (np.abs(scale - value)).argmin()

    def get_intensity(self, xvalue, yvalue):
        xidx = self.scale2pnt(xvalue, self.data.xscale)
        yidx = self.scale2pnt(yvalue, self.data.yscale)
        half_wid_xnum = int((self.XSlice.value() - 1) / 2)
        half_wid_ynum = int((self.YSlice.value() - 1) / 2)
        if xidx < half_wid_xnum:
            xminidx = 0
        else:
            xminidx = xidx - half_wid_xnum
        xmaxidx = xidx + half_wid_xnum + 1
        if yidx < half_wid_ynum:
            yminidx = 0
        else:
            yminidx = yidx - half_wid_ynum
        ymaxidx = yidx + half_wid_ynum + 1
        return self.data.data[xminidx:xmaxidx, yminidx:ymaxidx].sum()

    def get_XDC(self, yvalue):
        yidx = self.scale2pnt(yvalue, self.data.yscale)
        half_wid_num = int((self.YSlice.value() - 1) / 2)
        if yidx < half_wid_num:
            yminidx = 0
        else:
            yminidx = yidx - half_wid_num
        ymaxidx = yidx + half_wid_num + 1
        array = self.data.data[:, yminidx:ymaxidx]
        return np.sum(array, axis=1)

    def get_YDC(self, xvalue):
        xidx = self.scale2pnt(xvalue, self.data.xscale)
        half_wid_num = int((self.XSlice.value() - 1) / 2)
        if xidx < half_wid_num:
            xminidx = 0
        else:
            xminidx = xidx - half_wid_num
        xmaxidx = xidx + half_wid_num + 1
        array = self.data.data[xminidx:xmaxidx, :]
        return np.sum(array, axis=0)

    def getcurrentcmap(self):
        cmap_str = self.cmapmenu.currentText()
        if self.revcmap.checkState() == Qt.Checked:
            cmap_str += "_r"
        raw_cmap = cm.get_cmap(cmap_str, 256)
        linearIndex = np.linspace(0, 1, 256)
        nonlinearIndex = np.power(linearIndex, self.Gamma.value())
        new_cmap = ListedColormap(raw_cmap(nonlinearIndex))
        return new_cmap

    def setGammaFromSpinBox(self, value):
        self.updatecolorscale(self.getcurrentcmap(), True)
        self.plotimage(self.data, self.getcurrentcmap(), self.Vmin.value(),
                       self.Vmax.value(), True)
        self.GSlider.setValue(1000000 * np.log(value))

    def setGammaFromSlider(self, value):
        self.Gamma.setValue(np.exp(value / 1000000))

    def reversecmap(self, state):
        self.updatecolorscale(self.getcurrentcmap(), True)
        self.plotimage(self.data, self.getcurrentcmap(), self.Vmin.value(),
                       self.Vmax.value(), True)

    def changecmapbyindex(self, index):
        self.updatecolorscale(self.getcurrentcmap(), True)
        self.plotimage(self.data, self.getcurrentcmap(), self.Vmin.value(),
                       self.Vmax.value(), True)

    def updatecolorscale(self, cmap, removeflag):
        if removeflag:
            for img in self.ax_cmap.get_images():
                img.remove()
        self.cmapimage = self.ax_cmap.imshow(self.gradient,
                                             cmap=cmap,
                                             origin="lower",
                                             aspect="auto")
        self.ax_cmap.redraw_in_frame()
        self.canvas.blit(self.ax_cmap.bbox)

    def begincursor(self, on):
        if on:
            self.lx1.set_visible(True)
            self.ly1.set_visible(True)
            if self.YSlice.value() > 1:
                self.lx2.set_visible(True)
            if self.XSlice.value() > 1:
                self.ly2.set_visible(True)
            self.vl.set_visible(True)
            self.hl.set_visible(True)
            self.plotArtist()
            self.plotXDC(True, True)
            self.plotYDC(True, True)
            self.cid_mouse = self.canvas.mpl_connect('motion_notify_event',
                                                     self.navigate)
            self.cid_key = self.canvas.mpl_connect('key_press_event',
                                                   self.navigate)
        else:
            self.lx1.set_visible(False)
            self.ly1.set_visible(False)
            self.lx2.set_visible(False)
            self.ly2.set_visible(False)
            self.vl.set_visible(False)
            self.hl.set_visible(False)
            self.plotXDC(True, False)
            self.plotYDC(True, False)
            self.plotArtist()
            self.canvas.mpl_disconnect(self.cid_mouse)
            self.canvas.mpl_disconnect(self.cid_key)

    def navigate(self, event):
        if event.inaxes == self.ax_Spec and event.key == "control":
            self.X.setValue(event.xdata)
            self.Y.setValue(event.ydata)

    def OnPress(self, event):
        if event.inaxes == self.ax_Spec and event.button == 1:
            if not self.selector.visible:  #draw selector from nothing
                self.selector.moving_state = "draw"
                self.selector.origin = (event.xdata, event.ydata)
                self.cid_drawRS = self.canvas.mpl_connect(
                    'motion_notify_event', self.OnDrawRS)
            else:
                dist = self.selector.nearestCorner(event.x, event.y)
                if dist < 10:
                    self.selector.moving_state = "moveHandle"
                    self.selector.origin = (event.xdata, event.ydata)
                    self.cid_moveHandle = self.canvas.mpl_connect(
                        'motion_notify_event', self.OnMoveHandle)
                else:
                    if self.selector.isinRegion(
                            event.xdata, event.ydata):  #move the selector
                        self.selector.moving_state = "move"
                        self.selector.origin = (event.xdata, event.ydata)
                        self.cid_moveRS = self.canvas.mpl_connect(
                            'motion_notify_event', self.OnMoveRS)
                    else:  #clear the selector
                        self.selector.set_visible(False)
                        self.plotArtist()

    def OnDrawRS(self, event):
        if event.inaxes == self.ax_Spec and event.button == 1:
            if event.xdata != self.selector.origin[
                    0] and event.ydata != self.selector.origin[1]:
                self.selector.set_visible(True)
                self.selector.resize(self.selector.origin[0],
                                     self.selector.origin[1], event.xdata,
                                     event.ydata)
                self.plotArtist()

    def OnMoveRS(self, event):
        if event.inaxes == self.ax_Spec and event.button == 1:
            xmin, ymin, xmax, ymax = self.selector.region
            self.selector.resize(xmin + event.xdata - self.selector.origin[0],
                                 ymin + event.ydata - self.selector.origin[1],
                                 xmax + event.xdata - self.selector.origin[0],
                                 ymax + event.ydata - self.selector.origin[1])
            self.plotArtist()
            self.selector.origin = (event.xdata, event.ydata)

    def OnMoveHandle(self, event):
        if event.inaxes == self.ax_Spec and event.button == 1:
            xmin, ymin, xmax, ymax = self.selector.region
            if self.selector.active_handle == 0:
                self.selector.resize(
                    xmin + event.xdata - self.selector.origin[0],
                    ymin + event.ydata - self.selector.origin[1], xmax, ymax)
            elif self.selector.active_handle == 1:
                self.selector.resize(
                    xmin, ymin + event.ydata - self.selector.origin[1], xmax,
                    ymax)
            elif self.selector.active_handle == 2:
                self.selector.resize(
                    xmin, ymin + event.ydata - self.selector.origin[1],
                    xmax + event.xdata - self.selector.origin[0], ymax)
            elif self.selector.active_handle == 3:
                self.selector.resize(
                    xmin, ymin, xmax + event.xdata - self.selector.origin[0],
                    ymax)
            elif self.selector.active_handle == 4:
                self.selector.resize(
                    xmin, ymin, xmax + event.xdata - self.selector.origin[0],
                    ymax + event.ydata - self.selector.origin[1])
            elif self.selector.active_handle == 5:
                self.selector.resize(
                    xmin, ymin, xmax,
                    ymax + event.ydata - self.selector.origin[1])
            elif self.selector.active_handle == 6:
                self.selector.resize(
                    xmin + event.xdata - self.selector.origin[0], ymin, xmax,
                    ymax + event.ydata - self.selector.origin[1])
            elif self.selector.active_handle == 7:
                self.selector.resize(
                    xmin + event.xdata - self.selector.origin[0], ymin, xmax,
                    ymax)
            self.plotArtist()
            self.selector.origin = (event.xdata, event.ydata)

    def OnRelease(self, event):
        if event.inaxes == self.ax_Spec:
            if event.button == 1:
                if self.selector.moving_state == "draw":
                    self.canvas.mpl_disconnect(self.cid_drawRS)
                elif self.selector.moving_state == "move":
                    self.canvas.mpl_disconnect(self.cid_moveRS)
                elif self.selector.moving_state == "moveHandle":
                    self.canvas.mpl_disconnect(self.cid_moveHandle)
            if event.button == 3:
                if self.selector.visible and self.selector.isinRegion(
                        event.xdata, event.ydata):
                    self.CropAction.setEnabled(True)
                    self.SelectMenu.setEnabled(True)
                else:
                    self.CropAction.setEnabled(False)
                    self.SelectMenu.setEnabled(False)
                self.showContextMenu()
        else:
            if event.button == 3:
                self.CropAction.setEnabled(False)
                self.SelectMenu.setEnabled(False)
                self.showContextMenu()

    def showContextMenu(self):
        self.contextMenu.move(QCursor.pos())
        self.contextMenu.show()

    def crop(self):
        x0, y0, x1, y1 = self.selector.region
        self.viewer.Win.DataProcessor.tab_single.setSelectXYRange(
            x0, x1, y0, y1, True)
        self.selector.set_visible(False)
        self.plotArtist()
        self.viewer.Win.DataProcessor.tab_single.crop2D.clicked.emit()

    def setSelectionRange(self):
        x0, y0, x1, y1 = self.selector.region
        if self.sender() == self.SelectX:
            self.viewer.Win.DataProcessor.tab_single.setSelectXRange(x0, x1)
        elif self.sender() == self.SelectY:
            self.viewer.Win.DataProcessor.tab_single.setSelectYRange(y0, y1)
        elif self.sender() == self.SelectXY:
            self.viewer.Win.DataProcessor.tab_single.setSelectXYRange(
                x0, x1, y0, y1, True)
        self.selector.set_visible(False)
        self.plotArtist()

    def outputData(self):
        if self.sender() == self.XDC_action:
            pass
        elif self.sender() == self.YDC_action:
            pass

    def OnKeyPress(self, event):
        if event.key == 'ctrl+a':
            if self.toolPanel.isChecked():
                self.toolPanel.setChecked(False)
            else:
                self.toolPanel.setChecked(True)

    def GetWhiteBackground(self):
        self.ax_XDC.redraw_in_frame()
        self.canvas.blit(self.ax_XDC.get_window_extent())
        self.ax_YDC.redraw_in_frame()
        self.canvas.blit(self.ax_YDC.get_window_extent())
        self.background_XDC = self.canvas.copy_from_bbox(
            self.ax_XDC.get_window_extent())
        self.background_YDC = self.canvas.copy_from_bbox(
            self.ax_YDC.get_window_extent())
        self.background_Spec = self.canvas.copy_from_bbox(
            self.ax_Spec.get_window_extent())

    def plotArtist(self):
        self.canvas.restore_region(self.background_Spec)
        for line in self.ax_Spec.get_lines():
            self.ax_Spec.draw_artist(line)
        for artist in self.selector.artist:
            self.ax_Spec.draw_artist(artist)
        self.canvas.blit(self.ax_Spec.get_window_extent())

    def plotimage(self, data, cmap, vmin, vmax, useblit):
        for img in self.ax_Spec.get_images():
            img.remove()
        self.twoDspec = self.ax_Spec.imshow(
            data.data.T,
            cmap=cmap,
            vmin=vmin,
            vmax=vmax,
            origin="lower",
            extent=(data.xmin - 0.5 * data.xstep, data.xmax + 0.5 * data.xstep,
                    data.ymin - 0.5 * data.ystep,
                    data.ymax + 0.5 * data.ystep),
            aspect=self.aspect,
            interpolation='none')
        self.setTickLabelFont(self.ax_Spec, "Comic Sans MS")
        visible_flag = self.selector.visible
        self.selector.set_visible(False)
        if useblit:
            self.refreshimage()
        else:
            pass
        self.background_Spec = self.canvas.copy_from_bbox(
            self.ax_Spec.get_window_extent()
        )  #this line makes sure that the background will be updated when user change color map
        self.selector.set_visible(visible_flag)
        self.plotArtist()

    def refreshimage(self):
        self.ax_Spec.redraw_in_frame()
        self.canvas.blit(self.ax_Spec.get_window_extent())

    def plotXDC(self, useblit, usecursor):
        if useblit:
            self.canvas.restore_region(self.background_XDC)
            self.ax_XDC.draw_artist(self.XDCLine)
            if usecursor:
                self.ax_XDC.draw_artist(self.vl)
            self.canvas.blit(self.ax_XDC.get_window_extent())
        else:
            self.canvas.draw()

    def plotYDC(self, useblit, usecursor):
        if useblit:
            self.canvas.restore_region(self.background_YDC)
            self.ax_YDC.draw_artist(self.YDCLine)
            if usecursor:
                self.ax_YDC.draw_artist(self.hl)
            self.canvas.blit(self.ax_YDC.get_window_extent())
        else:
            self.canvas.draw()

    def resetXDC(self):
        mask = np.isnan(self.XDC)
        self.XDCLine.set_data(self.data.xscale[~mask], self.XDC[~mask])
        if len(self.XDC[~mask]) > 1:
            minvalue = min(self.XDC[~mask])
            maxvalue = max(self.XDC[~mask])
            if minvalue == maxvalue:
                minvalue = minvalue - 0.5
                maxvalue = maxvalue + 0.5
        else:
            minvalue = 0
            maxvalue = 1
        self.ax_XDC.set_ybound(lower=minvalue - 0.01 * (maxvalue - minvalue),
                               upper=maxvalue + 0.01 * (maxvalue - minvalue))
        self.ax_XDC.set_xbound(lower=self.data.xmin - self.data.xstep * 0.5,
                               upper=self.data.xmax + self.data.xstep * 0.5)

    def resetYDC(self):
        mask = np.isnan(self.YDC)
        self.YDCLine.set_data(self.YDC[~mask], self.data.yscale[~mask])
        if len(self.YDC[~mask]) > 1:
            minvalue = min(self.YDC[~mask])
            maxvalue = max(self.YDC[~mask])
            if minvalue == maxvalue:
                minvalue = minvalue - 0.5
                maxvalue = maxvalue + 0.5
        else:
            minvalue = 0
            maxvalue = 1
        self.ax_YDC.set_xbound(lower=minvalue - 0.01 * (maxvalue - minvalue),
                               upper=maxvalue + 0.01 * (maxvalue - minvalue))
        self.ax_YDC.set_ybound(lower=self.data.ymin - self.data.ystep * 0.5,
                               upper=self.data.ymax + self.data.ystep * 0.5)

    def changeAspect(self, state):
        if state:
            self.aspect = "equal"
        else:
            self.aspect = "auto"
        self.ax_Spec.set_aspect(self.aspect)
        self.updateAxesPosition(True)

    def updateAxesPosition(self, useDraw):
        x_Spec, y_Spec, w_Spec, h_Spec = self.ax_Spec.get_position().bounds
        x_XDC, y_XDC, w_XDC, h_XDC = self.ax_XDC.get_position().bounds
        x_YDC, y_YDC, w_YDC, h_YDC = self.ax_YDC.get_position().bounds
        self.ax_XDC.set_position([x_Spec, y_XDC, w_Spec, h_XDC])
        self.ax_YDC.set_position([x_YDC, y_Spec, w_YDC, h_Spec])
        visible_flag = self.selector.visible
        self.selector.set_visible(False)
        if useDraw:
            self.canvas.draw()
        self.GetWhiteBackground()
        self.selector.set_visible(visible_flag)
        self.plotArtist()
        self.plotXDC(True, True)
        self.plotYDC(True, True)

    def resizeEvent_wrapper(self):
        self.updateAxesPosition(False)