Ejemplo n.º 1
0
class Base_Plot(QtCore.QObject):
    def __init__(self, parent, widget, mpl_layout):
        super().__init__(parent)
        self.parent = parent

        self.widget = widget
        self.mpl_layout = mpl_layout
        self.fig = mplfigure.Figure()
        mpl.scale.register_scale(AbsoluteLogScale)
        mpl.scale.register_scale(BiSymmetricLogScale)
        
        # Set plot variables
        self.x_zoom_constraint = False
        self.y_zoom_constraint = False
        
        self.create_canvas()        
        self.NavigationToolbar(self.canvas, self.widget, coordinates=True)
        
        # AutoScale
        self.autoScale = [True, True]
        
        # Connect Signals
        self._draw_event_signal = self.canvas.mpl_connect('draw_event', self._draw_event)
        self.canvas.mpl_connect('button_press_event', lambda event: self.click(event))
        self.canvas.mpl_connect('key_press_event', lambda event: self.key_press(event))
        # self.canvas.mpl_connect('key_release_event', lambda event: self.key_release(event))
        
        self._draw_event()
    
    def create_canvas(self):
        self.canvas = FigureCanvas(self.fig)
        self.mpl_layout.addWidget(self.canvas)
        self.canvas.setFocusPolicy(QtCore.Qt.StrongFocus)
        self.canvas.draw()
        
        # Set scales
        scales = {'linear': True, 'log': 0, 'abslog': 0, 'bisymlog': 0}
        for ax in self.ax:
            ax.scale = {'x': scales, 'y': deepcopy(scales)}
            ax.ticklabel_format(scilimits=(-4, 4), useMathText=True)
        
        # Get background
        for ax in self.ax:
            ax.background = self.canvas.copy_from_bbox(ax.bbox)
    
    def _find_calling_axes(self, event):
        for axes in self.ax:    # identify calling axis
            if axes == event or (hasattr(event, 'inaxes') and event.inaxes == axes):
                return axes
    
    def set_xlim(self, axes, x):
        if not self.autoScale[0]: return    # obey autoscale right click option
    
        if axes.get_xscale() in ['linear']:
            # range = np.abs(np.max(x) - np.min(x))
            # min = np.min(x) - range*0.05
            # if min < 0:
                # min = 0
            # xlim = [min, np.max(x) + range*0.05]
            xlim = [np.min(x), np.max(x)]
        if 'log' in axes.get_xscale():
            abs_x = np.abs(x)
            abs_x = abs_x[np.nonzero(abs_x)]    # exclude 0's
            
            if axes.get_xscale() in ['log', 'abslog', 'bisymlog']:
                min_data = np.ceil(np.log10(np.min(abs_x)))
                max_data = np.floor(np.log10(np.max(abs_x)))
                
                xlim = [10**(min_data-1), 10**(max_data+1)]
        
        if np.isnan(xlim).any() or np.isinf(xlim).any():
            pass
        elif xlim != axes.get_xlim():   # if xlim changes
            axes.set_xlim(xlim)
    
    def set_ylim(self, axes, y):
        if not self.autoScale[1]: return    # obey autoscale right click option
        
        min_data = np.array(y)[np.isfinite(y)].min()
        max_data = np.array(y)[np.isfinite(y)].max()
        
        if min_data == max_data:
            min_data -= 10**-1
            max_data += 10**-1
        
        if axes.get_yscale() == 'linear':
            range = np.abs(max_data - min_data)
            ylim = [min_data - range*0.1, max_data + range*0.1]
            
        elif axes.get_yscale() in ['log', 'abslog']:
            abs_y = np.abs(y)
            abs_y = abs_y[np.nonzero(abs_y)]    # exclude 0's
            abs_y = abs_y[np.isfinite(abs_y)]    # exclude nan, inf
            
            if abs_y.size == 0:             # if no data, assign 
                ylim = [10**-7, 10**-1]
            else:            
                min_data = np.ceil(np.log10(np.min(abs_y)))
                max_data = np.floor(np.log10(np.max(abs_y)))
                
                ylim = [10**(min_data-1), 10**(max_data+1)]
                
        elif axes.get_yscale() == 'bisymlog':
            min_sign = np.sign(min_data)
            max_sign = np.sign(max_data)
            
            if min_sign > 0:
                min_data = np.ceil(np.log10(np.abs(min_data)))
            elif min_data == 0 or max_data == 0:
                pass
            else:
                min_data = np.floor(np.log10(np.abs(min_data)))
            
            if max_sign > 0:
                max_data = np.floor(np.log10(np.abs(max_data)))
            elif min_data == 0 or max_data == 0:
                pass
            else:
                max_data = np.ceil(np.log10(np.abs(max_data)))
            
            # TODO: ylim could be incorrect for neg/neg, checked for pos/pos, pos/neg
            ylim = [min_sign*10**(min_data-min_sign), max_sign*10**(max_data+max_sign)]
        
        if ylim != axes.get_ylim():   # if ylim changes, update
            axes.set_ylim(ylim)
    
    def update_xylim(self, axes, xlim=[], ylim=[]):
        data = self._get_data(axes)         

        # on creation, there is no data, don't update
        if np.shape(data['x'])[0] < 2 or np.shape(data['y'])[0] < 2:   
            return
        
        for (axis, lim) in zip(['x', 'y'], [xlim, ylim]):
            # Set Limits
            if len(lim) == 0:
                eval('self.set_' + axis + 'lim(axes, data["' + axis + '"])')
            else:
                eval('axes.set_' + axis + 'lim(lim)')
            
            # If bisymlog, also update scaling, C
            if eval('axes.get_' + axis + 'scale()') == 'bisymlog':
                self._set_scale(axis, 'bisymlog', axes)
            
            ''' # TODO: Do this some day, probably need to create 
                        annotation during canvas creation
            # Move exponent 
            exp_loc = {'x': (.89, .01), 'y': (.01, .96)}
            eval(f'axes.get_{axis}axis().get_offset_text().set_visible(False)')
            ax_max = eval(f'max(axes.get_{axis}ticks())')
            oom = np.floor(np.log10(ax_max)).astype(int)
            axes.annotate(fr'$\times10^{oom}$', xy=exp_loc[axis], 
                          xycoords='axes fraction')
            '''
        
        self._draw_event()  # force a draw
    
    def _get_data(self, axes):      # NOT Generic
        # get experimental data for axes
        data = {'x': [], 'y': []}
        if 'exp_data' in axes.item:
            data_plot = axes.item['exp_data'].get_offsets().T
            if np.shape(data_plot)[1] > 1:
                data['x'] = data_plot[0,:]
                data['y'] = data_plot[1,:]
            
            # append sim_x if it exists
            if 'sim_data' in axes.item and hasattr(axes.item['sim_data'], 'raw_data'):
                if axes.item['sim_data'].raw_data.size > 0:
                    data['x'] = np.append(data['x'], axes.item['sim_data'].raw_data[:,0])
        
        elif 'weight' in axes.item:
            data['x'] = axes.item['weight'].get_xdata()
            data['y'] = axes.item['weight'].get_ydata()
        
        elif any(key in axes.item for key in ['density', 'qq_data', 'sim_data']):
            name = np.intersect1d(['density', 'qq_data'], list(axes.item.keys()))[0]
            for n, coord in enumerate(['x', 'y']):
                xyrange = np.array([])
                for item in axes.item[name]:
                    if name == 'qq_data':
                        coordData = item.get_offsets()
                        if coordData.size == 0:
                            continue
                        else:
                            coordData = coordData[:,n]
                    elif name == 'density':
                        coordData = eval('item.get_' + coord + 'data()')
                    
                    coordData = np.array(coordData)[np.isfinite(coordData)]
                    if coordData.size == 0:
                        continue
                    
                    xyrange = np.append(xyrange, [coordData.min(), coordData.max()])

                xyrange = np.reshape(xyrange, (-1,2))
                data[coord] = [np.min(xyrange[:,0]), np.max(xyrange[:,1])]

        return data
    
    def _set_scale(self, coord, type, event, update_xylim=False):
        def RoundToSigFigs(x, p):
            x = np.asarray(x)
            x_positive = np.where(np.isfinite(x) & (x != 0), np.abs(x), 10**(p-1))
            mags = 10 ** (p - 1 - np.floor(np.log10(x_positive)))
            return np.round(x * mags) / mags
    
        # find correct axes
        axes = self._find_calling_axes(event)
        # for axes in self.ax:
            # if axes == event or (hasattr(event, 'inaxes') and event.inaxes == axes):
                # break
        
        # Set scale menu boolean
        if coord == 'x':
            shared_axes = axes.get_shared_x_axes().get_siblings(axes)               
        else:
            shared_axes = axes.get_shared_y_axes().get_siblings(axes)
        
        for shared in shared_axes:
            shared.scale[coord] = dict.fromkeys(shared.scale[coord], False) # sets all types: False
            shared.scale[coord][type] = True                                # set selected type: True

        # Apply selected scale
        if type == 'linear':
            str = 'axes.set_{:s}scale("{:s}")'.format(coord, 'linear')
        elif type == 'log':
            str = 'axes.set_{0:s}scale("{1:s}", nonpos{0:s}="mask")'.format(coord, 'log')
        elif type == 'abslog':
            str = 'axes.set_{:s}scale("{:s}")'.format(coord, 'abslog')
        elif type == 'bisymlog':
            # default string to evaluate 
            str = 'axes.set_{0:s}scale("{1:s}")'.format(coord, 'bisymlog')
            
            data = self._get_data(axes)[coord]
            if len(data) != 0:
                finite_data = np.array(data)[np.isfinite(data)] # ignore nan and inf
                min_data = finite_data.min()  
                max_data = finite_data.max()
                
                if min_data != max_data:
                    # if zero is within total range, find largest pos or neg range
                    if np.sign(max_data) != np.sign(min_data):  
                        pos_data = finite_data[finite_data>=0]
                        pos_range = pos_data.max() - pos_data.min()
                        neg_data = finite_data[finite_data<=0]
                        neg_range = neg_data.max() - neg_data.min()
                        C = np.max([pos_range, neg_range])
                    else:
                        C = np.abs(max_data-min_data)
                    C /= 1E3                  # scaling factor TODO: debating between 100, 500 and 1000
                    C = RoundToSigFigs(C, 1)  # round to 1 significant figure
                    str = 'axes.set_{0:s}scale("{1:s}", C={2:e})'.format(coord, 'bisymlog', C)
        
        eval(str)
        if type == 'linear' and coord == 'x':
            formatter = MathTextSciSIFormatter(useOffset=False, useMathText=True)
            axes.xaxis.set_major_formatter(formatter)
            
        elif type == 'linear' and coord == 'y':
            formatter = mpl.ticker.ScalarFormatter(useOffset=False, useMathText=True)
            formatter.set_powerlimits([-3, 4])
            axes.yaxis.set_major_formatter(formatter)
            
        if update_xylim:
            self.update_xylim(axes)
 
    def _animate_items(self, bool=True):
        for axis in self.ax:
            if axis.get_legend() is not None:
                axis.get_legend().set_animated(bool)
            
            for item in axis.item.values():
                if isinstance(item, list):
                    for subItem in item:
                        if isinstance(subItem, dict):
                            subItem['line'].set_animated(bool)
                        else:
                            subItem.set_animated(bool)
                else:
                    item.set_animated(bool)
    
    def _draw_items_artist(self):   
        for axis in self.ax:     # restore background first (needed for twinned plots)
            self.canvas.restore_region(axis.background)   
        
        for axis in self.ax:
            for item in axis.item.values():
                if isinstance(item, list):
                    for subItem in item:
                        if isinstance(subItem, dict):
                            axis.draw_artist(subItem['line'])
                        else:
                            axis.draw_artist(subItem) 
                else:
                    axis.draw_artist(item)
           
            if axis.get_legend() is not None:
                axis.draw_artist(axis.get_legend())
            
        self.canvas.update()
        # self.canvas.flush_events()    # unnecessary?
    
    def set_background(self):
        self.canvas.draw_idle() # for when shock changes
        for axis in self.ax:
            # axis.background = self.canvas.copy_from_bbox(axis.bbox)
            axis.background = self.canvas.copy_from_bbox(self.fig.bbox)
    
    def _draw_event(self, event=None):   # After redraw (new/resizing window), obtain new background
        self._animate_items(True)
        self.set_background()
        self._draw_items_artist()     
        # self.canvas.draw_idle()   # unnecessary?
    
    def clear_plot(self, ignore=[], draw=True):
        for axis in self.ax:
            if axis.get_legend() is not None:
                axis.get_legend().remove()
                
            for item in axis.item.values():
                if hasattr(item, 'set_offsets'):    # clears all data points
                    if 'scatter' not in ignore:
                        item.set_offsets(([np.nan, np.nan]))
                elif hasattr(item, 'set_xdata') and hasattr(item, 'set_ydata'):
                    if 'line' not in ignore:
                        item.set_xdata([np.nan, np.nan]) # clears all lines
                        item.set_ydata([np.nan, np.nan])
                elif hasattr(item, 'set_text'): # clears all text boxes
                    if 'text' not in ignore:
                        item.set_text('')
        if draw:
            self._draw_event()

    def click(self, event):
        if event.button == 3: # if right click
            if self.toolbar._active is None:
                self._popup_menu(event)
            # if self.toolbar._active is 'ZOOM':  # if zoom is on, turn off
                # self.toolbar.press_zoom(event)  # cancels current zooom
                # self.toolbar.zoom()             # turns zoom off
            elif event.dblclick:                  # if double right click, go to default view
                self.toolbar.home()

    def key_press(self, event):
        if event.key == 'escape':
            if self.toolbar._active is 'ZOOM':  # if zoom is on, turn off
                self.toolbar.zoom()             # turns zoom off
            elif self.toolbar._active is 'PAN':
                self.toolbar.pan()
        # elif event.key == 'shift':
        elif event.key == 'x':  # Does nothing, would like to make sticky constraint zoom/pan
            self.x_zoom_constraint = not self.x_zoom_constraint
        elif event.key == 'y':  # Does nothing, would like to make sticky constraint zoom/pan
            self.y_zoom_constraint = not self.y_zoom_constraint
        elif event.key in ['s', 'l', 'L', 'k']: pass
        else:
            key_press_handler(event, self.canvas, self.toolbar)
    
    # def key_release(self, event):
        # print(event.key, 'released')
    
    def NavigationToolbar(self, *args, **kwargs):
        ## Add toolbar ##
        self.toolbar = CustomNavigationToolbar(self.canvas, self.widget, coordinates=True)
        self.mpl_layout.addWidget(self.toolbar)

    def _popup_menu(self, event):
        axes = self._find_calling_axes(event)   # find axes calling right click
        if axes is None: return
        
        pos = self.parent.mapFromGlobal(QtGui.QCursor().pos())
        
        popup_menu = QMenu(self.parent)
        xScaleMenu = popup_menu.addMenu('x-scale')
        yScaleMenu = popup_menu.addMenu('y-scale')
        
        for coord in ['x', 'y']:
            menu = eval(coord + 'ScaleMenu')
            for type in axes.scale[coord].keys():
                action = QAction(type, menu, checkable=True)
                if axes.scale[coord][type]: # if it's checked
                    action.setEnabled(False)
                else:
                    action.setEnabled(True)
                menu.addAction(action)
                action.setChecked(axes.scale[coord][type])
                fcn = lambda event, coord=coord, type=type: self._set_scale(coord, type, axes, True)
                action.triggered.connect(fcn)
        
        # Create menu for AutoScale options X Y All
        popup_menu.addSeparator()
        autoscale_options = ['AutoScale X', 'AutoScale Y', 'AutoScale All']
        for n, text in enumerate(autoscale_options):
            action = QAction(text, menu, checkable=True)
            if n < len(self.autoScale):
                action.setChecked(self.autoScale[n])
            else:
                action.setChecked(all(self.autoScale))
            popup_menu.addAction(action)
            action.toggled.connect(lambda event, n=n: self._setAutoScale(n, event, axes))
                    
        popup_menu.exec_(self.parent.mapToGlobal(pos))    
    
    def _setAutoScale(self, choice, event, axes):
        if choice == len(self.autoScale):
            for n in range(len(self.autoScale)):
                self.autoScale[n] = event
        else:
            self.autoScale[choice] = event
        
        if event:   # if something toggled true, update limits
            self.update_xylim(axes)
class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__()

        self.setWindowTitle('Data Analysis for NSOR project')
        self.setWindowIcon(
            QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\window_icon.png'))
        '''
        q actions that are intend to be in menu or toolbar
        '''

        openFile = QAction(
            QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\open_file.png'),
            '&Open File...', self)
        openFile.setShortcut('Ctrl+O')
        openFile.setStatusTip('Open the data file')
        openFile.triggered.connect(self.open_file)

        exitProgram = QAction(
            QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\exit_program.png'),
            '&Exit', self)
        exitProgram.setShortcut("Ctrl+W")
        exitProgram.setStatusTip('Close the Program')
        exitProgram.triggered.connect(self.exit_program)

        renewData = QAction(
            QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\renew.png'), '&Renew',
            self)
        renewData.setShortcut("Ctrl+R")
        renewData.setStatusTip('Reload the original data')
        renewData.triggered.connect(self.renew_data)

        self.verticalZoom = QAction(
            QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\vertical_zoom.png'),
            '&Vertical Zoom', self)
        self.verticalZoom.setShortcut("Ctrl+Shift+V")
        self.verticalZoom.setStatusTip('Zoom in the vertical direction')
        self.verticalZoom.setCheckable(True)
        self.verticalZoom.toggled.connect(self.vzoom)

        self.horizontalZoom = QAction(
            QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\horizontal_zoom.png'),
            '&Horizontal Zoom', self)
        self.horizontalZoom.setShortcut("Ctrl+Shift+H")
        self.horizontalZoom.setStatusTip('Zoom in the horizaontal direction')
        self.horizontalZoom.setCheckable(True)
        self.horizontalZoom.toggled.connect(self.hzoom)

        self.moveCursor = QAction(
            QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\move_cursor.png'),
            '&Move Cursor', self)
        self.moveCursor.setShortcut("Ctrl+M")
        self.moveCursor.setStatusTip('Move cursors')
        self.moveCursor.setCheckable(True)
        self.moveCursor.toggled.connect(self.move_cursor)

        self.autoAxis = {}
        self.autoAxis['time_x'] = MyQAction(
            QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\auto_time_x.png'),
            '&Auto X axis (time)', 'time_x', self)
        self.autoAxis['time_y'] = MyQAction(
            QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\auto_time_y.png'),
            '&Auto Y axis (time)', 'time_y', self)
        self.autoAxis['freq_x'] = MyQAction(
            QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\auto_freq_x.png'),
            '&Auto X axis (freq)', 'freq_x', self)
        self.autoAxis['freq_y'] = MyQAction(
            QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\auto_freq_y.png'),
            '&Auto Y axis (freq)', 'freq_y', self)

        editParameters = QAction('&Edit Parameter', self)
        editParameters.setShortcut('Ctrl+E')
        editParameters.setStatusTip('open and edit the parameter file')
        editParameters.triggered.connect(self.edit_parameters)

        saveParameters = QAction('&Save Parameter', self)
        saveParameters.setShortcut('Ctrl+S')
        saveParameters.setStatusTip('save the parameters on screen to file')
        saveParameters.triggered.connect(self.save_parameters)

        self.data_type = QComboBox()
        self.data_type.setStatusTip(
            'bin for legacy data recorded from labview program, big endian coded binary data, npy for numpy type data'
        )
        self.data_type.addItems(['bin', '.npy'])
        '''
        setting menubar
        '''
        mainMenu = self.menuBar()  #create a menuBar
        fileMenu = mainMenu.addMenu('&File')  #add a submenu to the menu bar
        fileMenu.addAction(
            openFile)  # add what happens when this menu is interacted
        fileMenu.addSeparator()
        fileMenu.addAction(exitProgram)  # add an exit menu
        parameterMenu = mainMenu.addMenu('&Parameter')
        parameterMenu.addAction(editParameters)
        parameterMenu.addAction(saveParameters)
        '''
        setting toolbar
        '''
        self.toolbar = self.addToolBar(
            'nsor_toolbar')  #add a tool bar to the window
        if app.desktop().screenGeometry().height() == 2160:
            self.toolbar.setIconSize(QSize(100, 100))
        else:
            self.toolbar.setIconSize(QSize(60, 60))
        self.toolbar.addAction(
            openFile)  # add what happens when this tool is interacted

        self.toolbar.addWidget(self.data_type)
        self.toolbar.addAction(renewData)
        self.toolbar.addSeparator()

        self.toolbar.addAction(self.verticalZoom)
        self.toolbar.addAction(self.horizontalZoom)
        self.toolbar.addAction(self.moveCursor)
        self.toolbar.addSeparator()
        for key, item in self.autoAxis.items():
            self.autoAxis[key].setStatusTip(
                f'set {key} axis to size automatically')
            self.autoAxis[key].btnToggled.connect(self.auto_axis)
            self.toolbar.addAction(self.autoAxis[key])

        self.statusBar()  #create a status bar
        '''
        setting matplotlib
        '''

        if app.desktop().screenGeometry().height() == 2160:
            matplotlib.rcParams.update({'font.size': 28})
        else:
            matplotlib.rcParams.update({'font.size': 14})
        self.canvas = FigureCanvas(Figure(figsize=(40, 9)))
        self.fig = self.canvas.figure
        '''
        setting axis as dictionary,

        containing two axes of time and freq
        ax['time']
        ax['freq']
        also initiate the vertical lines
        vline['time_l']
        vline['time_r']
        vline['freq_l']
        vline['freq_r']
        '''
        self.ax = {}
        self.vline = {}
        self.ax['time'] = self.fig.add_subplot(121)
        self.ax['freq'] = self.fig.add_subplot(122)

        for axis in self.ax.values():
            if app.desktop().screenGeometry().height() == 2160:
                axis.tick_params(pad=20)
            elif app.desktop().screenGeometry().height() == 1080:
                axis.tick_params(pad=10)
            # axis.ticklabel_format(style='sci', axis='y', scilimits=(0,0))

        self.fourier_lb = QLabel("Ready", self)

        self.parameters = read_parameter(PARAMETER_FILE)
        '''
        setting edits and labels as dictionary,

        representing all time and freq edits
        "file_name" is excluded
        "time_x_limit"
        "time_y_limit"
        "freq_x_limit"
        "freq_y_imit"
        "time_cursor"
        "freq_cursor"
        '''
        self.edits = {}
        labels = {}
        for key, value in self.parameters.items():
            if type(value) == list:
                val = str(value[0]) + ' ' + str(value[1])
            if key == 'file_name':
                continue
            labels[key] = QLabel(key.replace('_', ' ').title(), self)
            self.edits[key] = MyLineEdit(key, val, self)
            self.edits[key].setStatusTip(f'{key}')
            self.edits[key].textModified.connect(self.limit_and_cursor)
            if key[0:4] == 'freq':
                self.edits[key].setFixedWidth(250)
            if 'cursor' in key:
                self.vline[key[0:4] + '_l'] = self.ax[key[0:4]].axvline(
                    float(value[0]), c='red')
                self.vline[key[0:4] + '_r'] = self.ax[key[0:4]].axvline(
                    float(value[1]), c='red')
                self.vline[key[0:4] + '_l'].set_animated(True)
                self.vline[key[0:4] + '_r'].set_animated(True)

        self.integral_label = QLabel('Peak Intensity: \n0', self)

        self.zeroPadPower = QComboBox(self)
        self.zeroPadPower.addItems(['x1', 'x2', 'x4', 'x8'])
        self.zeroPadPower.setStatusTip('This sets the zerofilling of the data')
        self.zeroPadPower.activated[str].connect(self.zero_padding)
        '''
        phase stuff
        '''
        self.toolbar.addSeparator()
        first_order_phase_check = QAction(
            QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\auto_phase_check.png'),
            '&First order on', self)
        first_order_phase_check.setStatusTip('Check to enbale 1st order phase')
        first_order_phase_check.setShortcut('Ctrl+F')
        first_order_phase_check.toggled.connect(self.first_order_phase_check)
        first_order_phase_check.setCheckable(True)
        self.toolbar.addAction(first_order_phase_check)

        auto_phase_btn = QAction(
            QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\auto_phase_btn.png'),
            '&Auto Phase', self)
        auto_phase_btn.setStatusTip('Auto phase the peak (0th order only)')
        auto_phase_btn.setShortcut('Ctrl+A')
        auto_phase_btn.triggered.connect(self.auto_phase)
        self.toolbar.addAction(auto_phase_btn)

        self.zeroth_slider = QSlider(self)
        self.zeroth_slider.setMinimum(0)
        self.zeroth_slider.setMaximum(360)
        self.zeroth_slider.setValue(0)
        self.zeroth_slider.setTickInterval(1)
        self.zeroth_slider.valueChanged.connect(self.zeroth_order_phase)
        self.zeroth_slider.sliderReleased.connect(self.slider_released)

        self.first_slider = QSlider(self)
        self.first_slider.setMinimum(0)
        self.first_slider.setMaximum(360)
        self.first_slider.setValue(0)
        self.first_slider.hide()
        self.first_slider.valueChanged.connect(self.first_order_phase)

        self.phase_info = QLabel('Current Phase: \n0th: 0\n1st: 0 \nInt: 0',
                                 self)
        '''
        setting layout
        '''
        self._main = QWidget()
        self.setCentralWidget(self._main)
        layout1 = QHBoxLayout(self._main)
        layout2 = QVBoxLayout()
        layout3 = QVBoxLayout()
        layout4 = QVBoxLayout()
        layout5 = QHBoxLayout()

        for key in labels.keys():
            if key[0:4] == 'time':
                layout2.addWidget(labels[key])
                layout2.addWidget(self.edits[key])
            elif key[0:4] == 'freq':
                layout4.addWidget(labels[key])
                layout4.addWidget(self.edits[key])
        layout4.addWidget(self.integral_label)
        layout4.addWidget(self.phase_info)

        layout4.addLayout(layout5)
        layout5.addWidget(self.zeroth_slider)
        layout5.addWidget(self.first_slider)

        layout2.addWidget(self.zeroPadPower)
        layout1.addLayout(layout2)
        layout2.addStretch(1)
        layout1.addLayout(layout3)
        layout3.addWidget(self.canvas)
        layout3.addWidget(self.fourier_lb)
        layout1.addLayout(layout4)
        # layout4.addStretch(1)

        self.threadpool = QThreadPool()  #Multithreading

    '''
    ################################################################################
    phase
    '''

    def slider_released(self):
        self.canvas.draw()
        key = 'freq'
        self.ax[key[0:4]].ticklabel_format(
            style='sci', axis='both',
            scilimits=(0, 0))  # format the tick label of the axes
        for k in self.ax.keys():
            self.ax[k].draw_artist(self.vline[k + '_l'])
            self.ax[k].draw_artist(self.vline[k + '_r'])

    def first_order_phase_check(self, toggle_state):
        if toggle_state:
            self.first_slider.show()
        else:
            self.first_slider.setValue(0)
            self.first_slider.hide()

    def auto_phase(self):
        try:
            reft = self.data['freq_y'].real[self.csL:self.csR]
            imft = self.data['freq_y'].imag[self.csL:self.csR]
            intensity_int = np.array([])
            for angle in range(360):
                phi = angle / 360 * 2 * pi
                intensity_int = np.append(
                    intensity_int,
                    np.sum(np.cos(phi) * reft + np.sin(phi) * imft))
            best_angle = intensity_int.argmax()
            best_phi = best_angle / 360 * 2 * pi
            self.zeroth_slider.setValue(best_angle)
            self.data['freq_real'] = self.data['freq_y'].real*np.cos(best_phi) + \
                                     self.data['freq_y'].imag*np.sin(best_phi)
            self.draw_phased_data()
        except AttributeError:
            dlg = QMessageBox.warning(self, 'WARNING',
                                      'No original data available!',
                                      QMessageBox.Ok)

    def zeroth_order_phase(self, value):
        phi = value / 360 * 2 * pi
        try:
            reft = self.data['freq_y'].real[self.csL:self.csR]
            imft = self.data['freq_y'].imag[self.csL:self.csR]
            self.data['freq_real'] = self.data['freq_y'].real*np.cos(phi) + \
                                     self.data['freq_y'].imag*np.sin(phi)
            intensity = np.sum(np.cos(phi) * reft + np.sin(phi) * imft)
            str = self.phase_info.text()
            str_lst = str.split('\n')
            intensity_str = "{:.5f}".format(intensity * 2)
            self.phase_info.setText(f'Current Phase: \n0th: {value}\n' +
                                    str_lst[2] + f'\nInt: {intensity_str}')
            self.draw_phased_data()
            self.canvas.blit(self.ax['freq'].bbox)
        except AttributeError:
            dlg = QMessageBox.warning(self, 'WARNING',
                                      'No original data available!',
                                      QMessageBox.Ok)

    def first_order_phase(self, value):
        intensity = 0
        str = self.phase_info.text()
        str_lst = str.split('\n')
        intensity_str = "{:.5f}".format(intensity * 2)
        self.phase_info.setText('Current Phase: \n' + str_lst[1] +
                                f'\n1st: {value}' + f'\nInt: {intensity_str}')

    def draw_phased_data(self):
        key = 'freq'
        self.ax[key].clear()
        self.ax[key].plot(self.data[key + '_x'], self.data[key + '_real'])

        cs_value = [
            float(x) for x in self.edits[key + '_cursor'].text().split(' ')
        ]
        self.vline[key + '_l'].set_xdata([cs_value[0], cs_value[0]])
        self.vline[key + '_r'].set_xdata([cs_value[1], cs_value[1]])

        lm_value = [
            float(x) for x in self.edits[key + '_x_limit'].text().split(' ')
        ]

        self.ax[key].set_xlim(lm_value[0], lm_value[1])

        self.canvas.draw()
        self.ax[key].ticklabel_format(
            style='sci', axis='both',
            scilimits=(0, 0))  # format the tick label of the axes
        for k in self.ax.keys():
            self.ax[k].draw_artist(self.vline[k + '_l'])
            self.ax[k].draw_artist(self.vline[k + '_r'])

    '''
    ################################################################################
    some less complicated slot
    '''

    def edit_parameters(self):
        os.startfile(PARAMETER_FILE)

    def save_parameters(self):
        for key in self.parameters.keys():
            if key == 'file_name':
                continue
            str = self.edits[key].text()
            self.parameters[key] = str.split(' ')

        save_parameter(PARAMETER_FILE, **self.parameters)

    def auto_axis(self, key):
        '''
        auto scale the axis
        '''
        if key != 'time_y':
            self.ax[key[0:4]].autoscale(axis=key[5])
        else:
            try:
                average = np.mean(np.abs(self.data['time_y']))
                self.ax['time'].set_ylim(-2 * average, 2 * average)
            except AttributeError:
                self.ax[key[0:4]].autoscale(axis=key[5])

        self.canvas.draw()
        self.ax[key[0:4]].ticklabel_format(
            style='sci', axis='both',
            scilimits=(0, 0))  # format the tick label of the axes
        for k in self.ax.keys():
            self.ax[k].draw_artist(self.vline[k + '_l'])
            self.ax[k].draw_artist(self.vline[k + '_r'])

    '''
    ################################################################################
    browse the figure
    calculate based on cursors
    '''

    def limit_and_cursor(self, key, text):
        '''
        respond to the change of text in the edits
        '''
        try:
            value = [float(x) for x in text.split(' ')]
            if 'limit' in key:
                if 'x' in key:
                    self.ax[key[0:4]].set_xlim(value[0], value[1])
                elif 'y' in key:
                    self.ax[key[0:4]].set_ylim(value[0], value[1])

            elif 'cursor' in key:
                self.vline[key[0:4] + '_l'].set_xdata([value[0], value[0]])
                self.vline[key[0:4] + '_r'].set_xdata([value[1], value[1]])
                try:
                    cs1 = np.argmin(
                        np.abs(self.data[key[0:4] + '_x'] - value[0])
                    )  # finding the index corresponding to the time stamp
                    cs2 = np.argmin(
                        np.abs(self.data[key[0:4] + '_x'] - value[1]))
                    if cs1 > cs2:
                        self.cursor_operation(key, cs2, cs1)
                    else:
                        self.cursor_operation(key, cs1, cs2)
                except AttributeError:
                    dlg = QMessageBox.warning(self, 'WARNING',
                                              'No original data available!',
                                              QMessageBox.Ok)

            self.canvas.draw()
            self.ax[key[0:4]].ticklabel_format(
                style='sci', axis='both',
                scilimits=(0, 0))  # format the tick label of the axes
            for k in self.ax.keys():
                self.ax[k].draw_artist(self.vline[k + '_l'])
                self.ax[k].draw_artist(self.vline[k + '_r'])

        except ValueError:
            dlg = QMessageBox.warning(self, 'WARNING', 'Input only number',
                                      QMessageBox.Ok)

    def cursor_operation(self, key, csL, csR):
        self.csL = csL
        self.csR = csR
        if 'time' in key:
            self.zero_padding(self.zeroPadPower.currentText(), [csL, csR])
        elif 'freq' in key:
            intensity = (np.sum(self.data['freq_y'].real)**2 +
                         np.sum(self.data['freq_y'].imag)**2)**(1 / 2)
            intensity_str = "{:.5f}".format(intensity)
            self.integral_label.setText(
                f'Peak Intensity: \n{intensity_str}')  #

    def cursor_lines_in_axis(self, ax):
        if ax == self.ax['time']:
            line1 = self.vline['time_l']
            line2 = self.vline['time_r']
        else:
            line1 = self.vline['freq_l']
            line2 = self.vline['freq_r']
        return line1, line2

    def move_cursor(self, state):
        def on_press(event):
            if self.in_ax:
                if self.current_line != None:
                    if event.button == 1:
                        ax = event.inaxes
                        self.last_ax = ax
                        self.c_lock = True
                        self.x0 = event.xdata
                        self.current_line.set_xdata([event.xdata, event.xdata])
                        line1, line2 = self.cursor_lines_in_axis(ax)

                        self.canvas.draw()
                        self.background = self.canvas.copy_from_bbox(ax.bbox)
                        ax.draw_artist(line1)
                        ax.draw_artist(line2)
                        self.canvas.blit(ax.bbox)

        def on_motion(event):
            ax = event.inaxes
            if ax != None:
                line1, line2 = self.cursor_lines_in_axis(ax)

                if self.c_lock:
                    self.current_line.set_xdata([event.xdata, event.xdata])
                    self.canvas.restore_region(self.background)
                    ax.draw_artist(line1)
                    ax.draw_artist(line2)
                    self.canvas.blit(ax.bbox)
                    if self.x0 > event.xdata:
                        self.c_side = 'left'
                    else:
                        self.c_side = 'right'

                else:
                    if abs(event.xdata -
                           line1.get_xdata()[0]) / self.xrange <= 0.02:
                        if self.cursor == 'arrow':
                            QApplication.setOverrideCursor(Qt.CrossCursor)
                            self.current_line = line1
                            self.cursor = 'cross'
                    elif abs(event.xdata -
                             line2.get_xdata()[0]) / self.xrange <= 0.02:
                        if self.cursor == 'arrow':
                            QApplication.setOverrideCursor(Qt.CrossCursor)
                            self.cursor = 'cross'
                            self.current_line = line2
                    else:
                        if self.cursor == 'cross':
                            QApplication.restoreOverrideCursor()
                            self.cursor = 'arrow'
                            self.current_line = None

        def on_release(event):
            if self.c_lock:
                self.background = None
                self.c_lock = False

                ax = event.inaxes
                if ax != self.last_ax:
                    ax = self.last_ax

                    limit = ax.get_xlim()
                    if self.c_side == 'left':
                        event.xdata = limit[0]
                    else:
                        event.xdata = limit[1]

                line1, line2 = self.cursor_lines_in_axis(ax)

                str1 = "{:.5E}".format(line1.get_xdata()[0])
                str2 = "{:.5E}".format(line2.get_xdata()[0])

                if line2.get_xdata()[0] < line1.get_xdata()[0]:
                    str1, str2 = str2, str1

                if ax == self.ax['freq']:
                    self.edits['freq_cursor'].setText(str1 + ' ' + str2)
                else:
                    self.edits['time_cursor'].setText(str1 + ' ' + str2)

        def move_in_ax(event):
            self.in_ax = True
            ax = event.inaxes
            xmin, xmax = ax.get_xlim()
            self.xrange = xmax - xmin

        def move_out_ax(event):
            self.out_ax = False

        if state:
            self.cursor = 'arrow'
            self.verticalZoom.setChecked(False)
            self.horizontalZoom.setChecked(False)
            self.c_lock = False
            self.c_onpick = False
            self.c_cid_press = self.canvas.mpl_connect('button_press_event',
                                                       on_press)
            self.c_cid_release = self.canvas.mpl_connect(
                'button_release_event', on_release)
            self.c_cid_motion = self.canvas.mpl_connect(
                'motion_notify_event', on_motion)
            self.c_in_ax = self.canvas.mpl_connect('axes_enter_event',
                                                   move_in_ax)
            self.c_out_ax = self.canvas.mpl_connect('axes_leave_event',
                                                    move_out_ax)

        else:
            self.canvas.mpl_disconnect(self.c_cid_press)
            self.canvas.mpl_disconnect(self.c_cid_release)
            self.canvas.mpl_disconnect(self.c_cid_motion)
            self.canvas.mpl_disconnect(self.c_in_ax)
            self.canvas.mpl_disconnect(self.c_out_ax)

    def vzoom(self, state):
        def on_press(event):
            if self.in_ax:
                ax = event.inaxes
                line1, line2 = self.cursor_lines_in_axis(ax)
                try:
                    if event.button == 1:
                        self.vlock = True
                        self.last_ax = ax
                        ymin, ymax = ax.get_ylim()
                        self.yrange = ymax - ymin
                        xmin, xmax = ax.get_xlim()
                        self.xrange = xmax - xmin
                        self.y0 = event.ydata
                        self.top_ln, = ax.plot([
                            event.xdata - self.xrange * 0.02,
                            event.xdata + self.xrange * 0.02
                        ], [event.ydata, event.ydata])
                        self.btm_ln, = ax.plot([
                            event.xdata - self.xrange * 0.02,
                            event.xdata + self.xrange * 0.02
                        ], [event.ydata, event.ydata])
                        self.vzoom_ln, = ax.plot([event.xdata, event.xdata],
                                                 [event.ydata, event.ydata])
                        self.top_ln.set_color('m')
                        self.btm_ln.set_color('m')
                        self.vzoom_ln.set_color('m')
                        # print(self.right_ln.get_xdata(), self.right_ln.get_ydata())
                        self.btm_ln.set_animated(True)
                        self.vzoom_ln.set_animated(True)
                        self.canvas.draw()
                        self.background = self.canvas.copy_from_bbox(ax.bbox)
                        ax.draw_artist(self.vzoom_ln)
                        ax.draw_artist(self.btm_ln)
                        line1, line2 = self.cursor_lines_in_axis(ax)
                        ax.draw_artist(line1)
                        ax.draw_artist(line2)
                        self.canvas.blit(ax.bbox)
                    else:

                        self.top_ln.remove()
                        self.vzoom_ln.remove()
                        self.btm_ln.remove()
                        self.canvas.draw()
                        self.background = None
                        self.vlock = False
                        ax.draw_artist(line1)
                        ax.draw_artist(line2)
                        self.canvas.blit(ax.bbox)
                except:
                    print('no')

        def on_release(event):
            if self.vlock:
                try:
                    self.top_ln.remove()
                    self.vzoom_ln.remove()
                    self.btm_ln.remove()
                    self.canvas.draw()
                    self.background = None
                    self.vlock = False
                    ax = event.inaxes
                    if ax != self.last_ax:
                        ax = self.last_ax
                        limit = ax.get_ylim()
                        if self.vside == 'btm':
                            event.ydata = limit[0]
                        else:
                            event.ydata = limit[1]

                    if self.y0 > event.ydata:
                        self.y0, event.ydata = event.ydata, self.y0
                    str1 = "{:.5E}".format(self.y0)
                    str2 = "{:.5E}".format(event.ydata)
                    if ax == self.ax['freq']:
                        self.edits['freq_y_limit'].setText(str1 + ' ' + str2)
                    else:
                        self.edits['time_y_limit'].setText(str1 + ' ' + str2)
                except:
                    print('no')

        def on_motion(event):
            if self.vlock:
                ax = event.inaxes
                if ax != None:
                    self.btm_ln.set_ydata([event.ydata, event.ydata])
                    self.vzoom_ln.set_ydata([self.y0, event.ydata])
                    self.canvas.restore_region(self.background)
                    ax.draw_artist(self.vzoom_ln)
                    ax.draw_artist(self.btm_ln)
                    line1, line2 = self.cursor_lines_in_axis(ax)
                    ax.draw_artist(line1)
                    ax.draw_artist(line2)
                    self.canvas.blit(ax.bbox)
                    if self.y0 > event.ydata:
                        self.vside = 'btm'
                    else:
                        self.vside = 'top'

        def move_in_ax(event):
            self.in_ax = True

        def move_out_ax(event):
            self.out_ax = False

        if state:
            self.horizontalZoom.setChecked(False)
            self.moveCursor.setChecked(False)
            self.vlock = False
            self.vcid_press = self.canvas.mpl_connect('button_press_event',
                                                      on_press)
            self.vcid_release = self.canvas.mpl_connect(
                'button_release_event', on_release)
            self.vcid_motion = self.canvas.mpl_connect('motion_notify_event',
                                                       on_motion)
            self.vin_ax = self.canvas.mpl_connect('axes_enter_event',
                                                  move_in_ax)
            self.vout_ax = self.canvas.mpl_connect('axes_leave_event',
                                                   move_out_ax)
        else:
            self.canvas.mpl_disconnect(self.vcid_press)
            self.canvas.mpl_disconnect(self.vcid_release)
            self.canvas.mpl_disconnect(self.vcid_motion)
            self.canvas.mpl_disconnect(self.vin_ax)
            self.canvas.mpl_disconnect(self.vout_ax)

    def hzoom(self, state):
        def on_press(event):
            if self.in_ax:
                ax = event.inaxes
                line1, line2 = self.cursor_lines_in_axis(ax)
                try:
                    if event.button == 1:
                        self.hlock = True
                        self.last_ax = ax
                        ymin, ymax = ax.get_ylim()
                        self.yrange = ymax - ymin
                        xmin, xmax = ax.get_xlim()
                        self.xrange = xmax - xmin
                        self.x0 = event.xdata
                        self.left_ln, = ax.plot([event.xdata, event.xdata], [
                            event.ydata - self.yrange * 0.02,
                            event.ydata + self.yrange * 0.02
                        ])
                        self.right_ln, = ax.plot([event.xdata, event.xdata], [
                            event.ydata - self.yrange * 0.02,
                            event.ydata + self.yrange * 0.02
                        ])
                        self.hzoom_ln, = ax.plot([event.xdata, event.xdata],
                                                 [event.ydata, event.ydata])
                        self.left_ln.set_color('m')
                        self.right_ln.set_color('m')
                        self.hzoom_ln.set_color('m')
                        # print(self.right_ln.get_xdata(), self.right_ln.get_ydata())
                        self.right_ln.set_animated(True)
                        self.hzoom_ln.set_animated(True)
                        self.canvas.draw()
                        self.background = self.canvas.copy_from_bbox(ax.bbox)
                        ax.draw_artist(self.hzoom_ln)
                        ax.draw_artist(self.right_ln)
                        ax.draw_artist(line1)
                        ax.draw_artist(line2)
                        self.canvas.blit(ax.bbox)

                    else:
                        self.left_ln.remove()
                        self.hzoom_ln.remove()
                        self.right_ln.remove()
                        self.canvas.draw()
                        self.background = None
                        self.hlock = False
                        ax.draw_artist(line1)
                        ax.draw_artist(line2)
                        self.canvas.blit(ax.bbox)

                except:
                    print('no')

        def on_motion(event):
            if self.hlock:
                ax = event.inaxes
                if ax != None:
                    self.right_ln.set_xdata([event.xdata, event.xdata])
                    self.hzoom_ln.set_xdata([self.x0, event.xdata])
                    self.canvas.restore_region(self.background)
                    ax.draw_artist(self.hzoom_ln)
                    ax.draw_artist(self.right_ln)
                    line1, line2 = self.cursor_lines_in_axis(ax)
                    ax.draw_artist(line1)
                    ax.draw_artist(line2)
                    self.canvas.blit(ax.bbox)
                    if self.x0 > event.xdata:
                        self.hside = 'left'
                    else:
                        self.hside = 'right'

        def on_release(event):
            if self.hlock:
                try:
                    self.left_ln.remove()
                    self.hzoom_ln.remove()
                    self.right_ln.remove()
                    self.canvas.draw()
                    self.background = None
                    self.hlock = False
                    ax = event.inaxes
                    if ax != self.last_ax:
                        ax = self.last_ax
                        limit = ax.get_xlim()
                        if self.hside == 'left':
                            event.xdata = limit[0]
                        else:
                            event.xdata = limit[1]

                    if self.x0 > event.xdata:
                        self.x0, event.xdata = event.xdata, self.x0

                    # ax.set_xlim(self.x0, event.xdata)
                    str1 = "{:.5E}".format(self.x0)
                    str2 = "{:.5E}".format(event.xdata)
                    if ax == self.ax['freq']:
                        self.edits['freq_x_limit'].setText(str1 + ' ' + str2)
                    else:
                        self.edits['time_x_limit'].setText(str1 + ' ' + str2)

                except:
                    print('no')

        def move_in_ax(event):
            self.in_ax = True

        def move_out_ax(event):
            self.out_ax = False

        if state:
            self.moveCursor.setChecked(False)
            self.verticalZoom.setChecked(False)
            self.hlock = False
            self.hcid_press = self.canvas.mpl_connect('button_press_event',
                                                      on_press)
            self.hcid_release = self.canvas.mpl_connect(
                'button_release_event', on_release)
            self.hcid_motion = self.canvas.mpl_connect('motion_notify_event',
                                                       on_motion)
            self.hin_ax = self.canvas.mpl_connect('axes_enter_event',
                                                  move_in_ax)
            self.hout_ax = self.canvas.mpl_connect('axes_leave_event',
                                                   move_out_ax)
        else:
            self.canvas.mpl_disconnect(self.hcid_press)
            self.canvas.mpl_disconnect(self.hcid_release)
            self.canvas.mpl_disconnect(self.hcid_motion)
            self.canvas.mpl_disconnect(self.hin_ax)
            self.canvas.mpl_disconnect(self.hout_ax)

    '''
    ################################################################################
    Multithreading fft calculation
    '''

    def fourier_multithreading(self, time_sig):
        self.fourier_lb.setText('Waiting...')
        fourier_worker = FourierWorker(time_sig, self.f_max)
        fourier_worker.signals.data.connect(self.set_fourier)
        fourier_worker.signals.finished.connect(self.fourier_finished)
        self.threadpool.start(fourier_worker)

    def set_fourier(self, data):
        self.data['freq_x'] = data[0]
        self.data['freq_y'] = data[1]
        self.draw('freq')
        self.edits['freq_x_limit'].returnPressed.emit()
        self.edits['freq_cursor'].returnPressed.emit()

    def fourier_finished(self):
        self.fourier_lb.setText('Ready')

    '''
    ################################################################################
    make zerofilling work
    '''

    def zero_padding(self, pad_power, value=[]):
        if value == []:
            value = [float(val) for val in self.parameters['time_cursor']]
            cs1 = np.argmin(np.abs(
                self.data['time_x'] -
                value[0]))  # finding the index corresponding to the time stamp
            cs2 = np.argmin(np.abs(self.data['time_x'] - value[1]))
        else:
            cs1 = value[0]
            cs2 = value[1]
        try:
            time_data = self.data['time_y'][cs1:cs2]
            pad_power = int(pad_power[1:])
            x = np.ceil(np.log2(len(self.data['time_y'])))
            n = 2**(pad_power - 1)
            l = int(2**x * n)
            time_sig = np.pad(time_data, (0, l - len(time_data)), 'constant')
            self.fourier_multithreading(time_sig)
        except AttributeError:
            dlg = QMessageBox.warning(self, 'WARNING',
                                      'No original data available!',
                                      QMessageBox.Ok)
            self.zeroPadPower.setCurrentIndex(0)

    '''
    ################################################################################
    other miscellaneous function
    '''

    def renew_data(self):
        try:
            self.data['time_x'] = self.data['raw_x']
            self.data['time_y'] = self.data['raw_y']
            self.draw('time')
            self.zeroPadPower.setCurrentIndex(0)
        except AttributeError:
            dlg = QMessageBox.warning(self, 'WARNING',
                                      'No original data available!',
                                      QMessageBox.Ok)

    def exit_program(self):
        choice = QMessageBox.question(
            self, 'Exiting', 'Are you sure about exit?',
            QMessageBox.Yes | QMessageBox.No)  #Set a QMessageBox when called
        if choice == QMessageBox.Yes:  # give actions when answered the question
            sys.exit()

    def open_file(self):
        '''
        open file and assign data to a dictionary self.Data
        self.data['raw_x']
        self.data['raw_y']
        above two are the original data
        self.data['time_x']
        self.data['time_y']
        self.data['freq_x']
        self.data['freq_y']

        '''
        dlg = QFileDialog()
        dlg.setDirectory(read_parameter(PARAMETER_FILE)['file_name'])
        if dlg.exec_():
            file_name = dlg.selectedFiles()[0]
            save_parameter(PARAMETER_FILE, **{"file_name": file_name})
            if str(self.data_type.currentText()) == 'bin':
                raw_data = np.fromfile(file_name, '>f8')
            elif str(self.data_type.currentText()) == '.npy':
                raw_data = np.load(file_name)
            self.data = {}
            self.data['raw_x'] = raw_data[::2]
            self.data['raw_y'] = raw_data[1::2]
            self.data['time_x'] = self.data['raw_x']
            self.data['time_y'] = self.data['raw_y']
            dt = self.data['time_x'][1] - self.data['time_x'][0]
            self.f_max = 1 / (2 * dt)
            self.fourier_multithreading(self.data['time_y'])
            self.edits['time_cursor'].returnPressed.emit()

            self.draw('time')

    '''
    ################################################################################
    '''

    def draw(self, key):
        self.ax[key].clear()
        if key == 'time':
            self.ax[key].plot(self.data[key + '_x'], self.data[key + '_y'])
        elif key == 'freq':
            self.ax[key].plot(self.data[key + '_x'],
                              np.abs(self.data[key + '_y']))
        value = [
            float(x) for x in self.edits[key + '_cursor'].text().split(' ')
        ]
        self.vline[key + '_l'].set_xdata([value[0], value[0]])
        self.vline[key + '_r'].set_xdata([value[1], value[1]])

        self.canvas.draw()
        self.ax[key].ticklabel_format(
            style='sci', axis='both',
            scilimits=(0, 0))  # format the tick label of the axes
        self.ax[key].draw_artist(self.vline[key + '_l'])
        self.ax[key].draw_artist(self.vline[key + '_r'])
        self.canvas.blit(self.ax[key].bbox)