示例#1
0
class GuiProgram(Ui_sweepergui):

    cols = 0
    sweep_id = 'mereni'
    full_data = []
    negative_current = False
    log_sweep = True
    stop = True
    decade_combo_values = ['5', '10', '25', '50']
    save_directory = False
    save_filename = False

    def __init__(self, dialog):
        Ui_sweepergui.__init__(self)
        self.setupUi(dialog)

        # Connect "add" button with a custom function (addInputTextToListbox)
        self.exportButton.clicked.connect(self.export_data)
        self.startBtn.clicked.connect(self.startFunc)
        self.logRadioButton.clicked.connect(self.switch_linear)
        self.linRadioButton.clicked.connect(self.switch_linear)
        self.dynamicDelayRadio.clicked.connect(self.switch_dynamic_delay)
        self.constantDelayRadio.clicked.connect(self.switch_dynamic_delay)

        # Populate comboBox
        self.decadeComboBox.clear()
        self.decadeComboBox.addItems(self.decade_combo_values)
        self.decadeComboBox.setCurrentIndex(1)

        self.exportButton.setEnabled(False)

        # Try to load last parameters from Pickle
        self.load_parameters()

        self.save_directory = os.path.dirname(os.path.abspath(__file__))

        # Připojení k instrumentu
        #self.inst = Instrument('GPIB0::17::INSTR', visa_location='C:\WINDOWS\SysWOW64\\visa32.dll')
        self.inst = Instrument('GPIB0::17::INSTR', virtual=True)

    def make_pretty(self):
        self.littleProgBar.setValue(84)
        self.littleProgBar.setMaximum(100)
        self.bigProgBar.setMaximum(5)
        self.bigProgBar.setValue(3)

        self.startBtn.setText('Stop')

        self.ax.clear()  # Clear contents of current axis

        # Plot cosmetics
        self.ax.set_xlabel('Current $I$ (nA)')
        self.ax.set_ylabel('Voltage $U$ (V)')
        self.ax.set_title(
            'Sweep ID: 2018-04-21_11:22:37  $n$-ZnO/$p$-GaN  sample #692')

        data = []
        dd = np.loadtxt('demo_data/01.txt')
        for i in range(int(len(dd.T) / 2)):
            data.append((dd[:, 2 * i] * 1e6, dd[:, 2 * i + 1]))

        color_1 = '#0066FF'
        color_3 = '#6600CC'
        color_2 = '#FF0000'

        self.ax.plot(data[2][0], data[2][1], color=color_2)
        self.ax.plot(data[3][0], data[3][1], '--', color=color_2)

        self.ax.plot(data[4][0], data[4][1], color=color_3)
        self.ax.plot(data[5][0], data[5][1], '--', color=color_3)

        self.ax.plot(data[0][0], data[0][1], color=color_1)
        self.ax.plot(data[1][0], data[1][1], '--', color=color_1)

        #plt.tight_layout()

    # --- PLOTTING -----------------------------------------------------------
    # ------------------------------------------------------------------------
    # ------------------------------------------------------------------------
    # ------------------------------------------------------------------------

    def plot_data(self, data):
        ''' Plots the provided data onto the figure in the GUI. '''

        original_xlim = self.ax.get_xlim()
        original_ylim = self.ax.get_ylim()

        self.ax.clear()  # Clear contents of current axis

        # Plot cosmetics
        self.ax.set_xlabel('I [A]')
        self.ax.set_ylabel('U [V]')
        self.ax.set_title('Sweep ID: ' + self.sweep_id)

        for i in range(len(data)):
            x, y = data[i]
            if i == len(data) - 1:
                color = '#0066FF'
            elif i == len(data) - 2:
                color = '#6600CC'
            else:
                color = '#FF0000'
            if self.chkLoop.checkState():
                half = int(len(x) / 2)
                end = len(x)
                self.ax.plot(x[0:half], y[0:half], color=color)
                self.ax.plot(x[half:end], y[half:end], '--', color=color)
            else:
                self.ax.plot(x, y, color=color)
            if self.log_sweep:
                self.ax.set_xscale('log')
            else:
                self.ax.set_xscale('linear')

            if i != 0 and self.chkLockRange.checkState():
                self.ax.set_xlim(original_xlim)
                self.ax.set_ylim(original_ylim)

        self.canvas.get_default_filename = self.get_default_filename
        self.canvas.draw()  # Propagate changes to GUI

    def clear_plot(self):
        self.ax.clear()  # Clear contents of current axis
        self.canvas.draw()  # Propagate changes to GUI

    def put_figure_into_gui(self, fig, ax):
        ''' Creates a figure and places it inside the GUI container. '''

        # Figure
        self.fig = fig
        self.ax = ax

        # Canvas
        self.canvas = FigureCanvas(self.fig)
        self.mplvl.addWidget(self.canvas)
        self.canvas.draw()

        # Toolbar
        self.toolbar = NavigationToolbar(self.canvas,
                                         self.mplwindow,
                                         coordinates=True)
        self.mplvl.addWidget(self.toolbar)

    def remove_figure_from_gui(self, ):
        ''' Deletes the figure from the GUI. '''

        # Canvas
        self.mplvl.removeWidget(self.canvas)
        self.canvas.close()
        self.canvas.deleteLater()  # this prevents memory leaks

        # Toolbar
        self.mplvl.removeWidget(self.toolbar)
        self.toolbar.close()
        self.toolbar.deleteLater()  # this prevent memory leaks

    def get_default_filename(self):
        """
        Return a string, which includes extension, suitable for use as
        a default filename.
        """
        default_basename = self.save_filename or "sweep_{}".format(
            self.sweep_id) or 'image'
        default_filetype = self.canvas.get_default_filetype()
        default_filename = default_basename + '.' + default_filetype

        save_dir = os.path.expanduser(
            matplotlib.rcParams.get('savefig.directory', ''))

        # ensure non-existing filename in save dir
        i = 1
        while os.path.isfile(os.path.join(save_dir, default_filename)):
            # attach numerical count to basename
            default_filename = '{0}_({1}).{2}'.format(default_basename, i,
                                                      default_filetype)
            i += 1

        return default_filename

    # --- GUI collecting and starting MEASUREMENTS ---------------------------
    # ------------------------------------------------------------------------
    # ------------------------------------------------------------------------
    # ------------------------------------------------------------------------

    def validateInput(self, sw_min, sw_max, decade, delay, log_sweep, step):
        # Unit testing :P
        try:
            a = float(sw_min)
            a = float(sw_max)
            if not log_sweep:
                a = float(step)
        except:
            return False, "Některé parametry se nedají převést na čísla."

        # Code
        if log_sweep:
            if (float(sw_min) > 0 and float(sw_max) > 0
                    and float(sw_min) != float(sw_max)):
                return True, "OK"
            else:
                return False, "Kraje intervalu jsou stejné nebo je alespoň jeden záp**ný."
        else:
            # lin sweep
            if (float(sw_min) != float(sw_max)
                    and abs(float(sw_max) - float(sw_min)) > float(step)):
                # step fits into (start, stop) interval and start != stop
                test_array = np.arange(float(sw_max), float(sw_min),
                                       float(step))
                if self.chkLoop.checkState() and len(test_array) * 2 > 1000:
                    return False, "Počet bodů ve sweepu je {} > 1000. \nBuffer by přetekl, zvolte jemnější krok, menší rozsah nebo vypněte obousměrnost měření.".format(
                        2 * len(test_array))
                elif len(test_array) > 1000:
                    return False, "Počet bodů ve sweepu je {} > 1000. \nBuffer by přetekl, zvolte jemnější krok nebo menší rozsah.".format(
                        len(test_array))
                else:
                    return True, "OK"
            else:
                return False, "Kraje intervalu se rovnají, nebo je krok větší než rozsah."

    def stopFunc(self):
        print('ABORT! Attempted to STOP sweep!')
        self.inst.operate(False)
        self.stop = True

    def startFunc(self):
        ''' Posbírá z GUI parametry a odpovídajícím způsobem přeloží.'''
        # vypnout ruční brzdu
        self.stop = False

        # aktuální čas pro ID
        self.sweep_id = '{0:%Y-%m-%d_%H-%M-%S}'.format(datetime.datetime.now())
        self.save_filename = False

        sense_local = self.senseLocalRadioButton.isChecked()
        sense_remote = self.senseRemoteRadioButton.isChecked()
        self.sense_local = sense_local

        sw_min = str(self.startEdit.text())
        sw_max = str(self.endEdit.text())
        step = str(self.stepEdit.text())

        # Pokud je zapojený zesilovač
        if not self.sense_local:
            sw_min = str(float(sw_min) * 1e3)
            sw_max = str(float(sw_max) * 1e3)
            step = str(float(step) * 1e3)

        # TODO: this should be made more logical...
        self.custom_capacity_used = self.dynamicDelayRadio.isChecked()
        if self.custom_capacity_used:
            self.custom_capacity = int(self.capacitySpinBox.value())

        delay = str(self.delaySpinBox.value())
        decade = str(self.decadeComboBox.currentIndex())
        n = self.pocetMereniBox.value()

        self.bigProgBar.setValue(0)
        self.bigProgBar.setMaximum(n)
        self.littleProgBar.setValue(0)

        log_sweep = self.logRadioButton.isChecked()
        lin_sweep = self.linRadioButton.isChecked()
        self.log_sweep = log_sweep

        col_source = self.sourceCheckBox.checkState()
        col_delay = self.delayCheckBox.checkState()
        col_measure = self.measureCheckBox.checkState()
        col_time = self.timeCheckBox.checkState()

        self.cols = 0
        if col_source:
            self.cols += 1
        if col_delay:
            self.cols += 2
        if col_measure:
            self.cols += 4
        if col_time:
            self.cols += 8

        input_valid, err = self.validateInput(sw_min, sw_max, decade, delay,
                                              log_sweep, step)
        if self.chkAutorange.checkState():
            sweep_range = '0'
        else:
            sweep_range = str(
                misc.get_range_number(float(sw_min), float(sw_max)))
            print("!! Using sweep range: {}".format(sweep_range))
            if sweep_range == '0':
                print(
                    'POZOR: Manualni nastaveni range selhalo, prilis vysoke proudy?'
                )

        if input_valid:
            self.save_parameters()
            self.measure(sw_min, sw_max, decade, delay, log_sweep, step, n,
                         sweep_range)
        else:
            self.show_notification(err)

    # --- ACTUAL MEASUREMENTS ------------------------------------------------
    # ------------------------------------------------------------------------
    # ------------------------------------------------------------------------
    # ------------------------------------------------------------------------

    def measure(self, sw_min, sw_max, decade, delay, log_sweep, step, n,
                sweep_range):
        ''' Spustí měření s již validovanými parametry. '''
        # Disable UI
        self.enable_ui(False)

        # Smaže aktuální graf
        self.clear_plot()

        # Declare Export not done on this sweep
        self.exportButton.setText('Export')

        # Local or Remote sense?
        if self.sense_local:
            print("Zapinam Sense - LOCAL.")
            self.inst.write("O0X")
        else:
            print("Zapinam Sense - REMOTE.")
            self.inst.write("O1X")

        # Úvodní stabilizace
        stabilize_time = self.stableSpinBox.value()
        print("Uvodni stabilizace v DC modu - {} s.".format(stabilize_time))
        self.stabilize(sw_min, stabilize_time)

        # Nastavení sweepu
        self.inst.set_source_and_function('I', 'Sweep')
        # Nastavení formátu dat
        self.inst.write("G" + str(self.cols) + ",2,2X")

        if self.log_sweep:
            self.inst.write("Q2," + sw_min + "," + sw_max + "," + decade +
                            "," + sweep_range + "," + delay + "X")
            if self.chkLoop.checkState():
                self.inst.write("Q8," + sw_max + "," + sw_min + "," + decade +
                                "," + sweep_range + "," + delay + "X")
        else:
            self.inst.write("Q1," + sw_min + "," + sw_max + "," + step + "," +
                            sweep_range + "," + delay + "X")
            if self.chkLoop.checkState():
                self.inst.write("Q7," + sw_max + "," + sw_min + "," + step +
                                "," + sweep_range + "," + delay + "X")

        # Manual modification of delays for each value
        if self.custom_capacity_used:
            if self.chkLoop.checkState():
                delay_arr = delays.delays_round(float(sw_min), float(sw_max),
                                                int(decade),
                                                self.custom_capacity)
            else:
                delay_arr = delays.delays_up(float(sw_min), float(sw_max),
                                             int(decade), self.custom_capacity)
            self.inst.set_custom_delays(delay_arr)

        data = []
        self.full_data = []
        self.np_data = []
        n_hotovych_mereni = 0
        for mereni in range(n):
            if self.stop:
                break
            output = self.run_sweep()
            if output:
                if not self.sense_local:
                    output = misc.shift_data(output, cols=self.cols, shift=-3)
                sweep_results = misc.nice_format(output, cols=self.cols)
                unpacked_results = misc.unpack(sweep_results, cols=self.cols)
                data.append(unpacked_results)
                self.full_data.append(sweep_results)
                self.np_data = misc.pack_data(self.np_data, unpacked_results)

                print('Ukladam docasna data...')
                self.dump_data()

                n_hotovych_mereni += 1
                self.bigProgBar.setValue(n_hotovych_mereni)
                self.plot_data(data)

                if n_hotovych_mereni != n:
                    sleep_time = self.sleepSpinBox.value()
                    print("Pauza mezi sweepy - {} s.".format(sleep_time))
                    self.artSleep(sleep_time)

            else:
                print("Output byl prazdny. Prerusene mereni?")

        self.inst.operate(False)
        self.enable_ui(True)

    def run_sweep(self):
        ''' Provede jeden sweep, který už musí být definovaný ve stroji.
            Na konci 3 vteřiny spí, aby se mělo napětí čas ustálit před dalším měřením.

            Vrací string se všemi hodnotami sweepu.
        '''
        print('\nSpoustim sweep...')
        self.inst.write("U8X")
        out = self.inst.read()
        print('U8X -> ' + out)
        out = out.replace('\r', '').replace('\n', '')
        print("Out: {}".format(out))
        sweep_defined_size = int(out[-4:])
        print('Pocet bodu ve sweepu: ' + str(sweep_defined_size))

        self.littleProgBar.setValue(0)
        self.inst.trigger()  # Immediate trigger
        sweep_done = False
        while not sweep_done:
            if self.stop:
                break
            self.artSleep(0.2)
            self.inst.write("U11X")
            status = self.inst.read()
            if (status == 'SMS' + str(sweep_defined_size).zfill(4) + '\r\n'):
                sweep_done = True
            else:
                status_edit = status.replace('\r', '').replace('\n', '')
                try:
                    progress = int(status_edit[-4:])
                    self.littleProgBar.setValue(
                        int(progress / sweep_defined_size * 100))
                except:
                    print('Invalid progress!')
        print('Jeden sweep hotov.')
        self.littleProgBar.setValue(100)

        if self.stop:
            return ""
        else:
            return self.inst.read()

    def stabilize(self, bias, stabilize_time):
        # Počáteční stabilizace
        self.inst.set_source_and_function('I', 'DC')
        self.inst.write("B" + bias + ",0,20X")
        self.inst.operate(True)
        self.inst.trigger()
        self.artSleep(stabilize_time)

    # --- EXPORT and DATA operations -----------------------------------------
    # ------------------------------------------------------------------------
    # ------------------------------------------------------------------------
    # ------------------------------------------------------------------------

    def dump_data(self):
        save_file_name = 'C:\Repa\k237\data_temp.txt'
        try:
            np.savetxt(save_file_name,
                       self.np_data,
                       fmt='%.4e',
                       header=self.get_measurement_parameters() +
                       "\nPOZOR, prubezne ukladana data mohou byt nekompletni",
                       delimiter='\t')
        except:
            print("Nouzovy dump se nepovedl.")

    def export_data(self):
        """
        Exportuje data pomocí ukládacího dialogu Qt. V případě chybného zadání
        souboru nic neudělá a postěžuje si do konzole.
        """

        proposed_name = self.save_filename or 'sweep_' + self.sweep_id

        # Qt Dialog na výběr souboru k uložení
        save_file_name, _ = QtWidgets.QFileDialog.getSaveFileName(
            None,
            'Exportovat výsledky měření',
            #'C:\Repa\k237\data\sweep_' + proposed_name + '.txt',
            os.path.join(self.save_directory, proposed_name + '.txt'),
            'Text Files (*.txt);;All Files (*)')

        # Vlastní uložení souboru
        try:
            np.savetxt(save_file_name,
                       self.np_data,
                       fmt='%.4e',
                       header=self.get_measurement_parameters(),
                       delimiter='\t')

            # Update GUI
            self.exportButton.setText('Export ✔')
            self.save_directory = os.path.dirname(save_file_name)
            self.save_filename = os.path.splitext(
                os.path.basename(save_file_name))[0]
        except:
            print("Export neuspesny. Data pravdepodobne nejsou ulozena!")

            # Update GUI
            self.exportButton.setText('Export ✗')

    def get_measurement_parameters(self):
        out = ''
        out += "Sweep ID: {}\n".format(self.sweep_id)
        if self.log_sweep:
            out += "Log sweep:\n"
            out += "Rozsah (min, max) [A]: {}, {}\n".format(
                self.startEdit.text(), self.endEdit.text())
            out += "Bodu na dekadu [-]: {}\n".format(
                self.decade_combo_values[self.decadeComboBox.currentIndex()])
        else:
            out += "Linear sweep\n"
            out += "Rozsah (min, max, points) [A]: {}, {}, {}\n".format(
                self.startEdit.text(), self.endEdit.text(),
                self.stepEdit.text())

        out += "Delay [ms]: {}\n".format(self.delaySpinBox.value())
        out += "Pocet charakteristik [-]: {}\n".format(
            self.pocetMereniBox.value())

        if self.sense_local:
            out += "Sense: local\n"
        else:
            out += "Sense: remote\n"

        out += "Sloupce (Source, Measure, Delay, Time): {} {} {} {}\n".format(
            int(self.sourceCheckBox.checkState() / 2),
            int(self.measureCheckBox.checkState() / 2),
            int(self.delayCheckBox.checkState() / 2),
            int(self.timeCheckBox.checkState() / 2))

        out += "Uvodni DC stabilizace [s]: {}\n".format(
            self.stableSpinBox.value())
        out += "Stabilizace [s]: {}\n".format(self.sleepSpinBox.value())
        out += "\nI[A] (x)\tU[V .e-3] (y1, y2, ...) "

        return out

    # --- GUI parameters and ENABLE/DISABLE ----------------------------------
    # ------------------------------------------------------------------------
    # ------------------------------------------------------------------------
    # ------------------------------------------------------------------------

    def save_parameters(self):
        parameters_dict = {
            'sw_min': str(self.startEdit.text()),
            'sw_max': str(self.endEdit.text()),
            'step': str(self.stepEdit.text()),
            'delay': int(self.delaySpinBox.value()),
            'decade': int(self.decadeComboBox.currentIndex()),
            'n': int(self.pocetMereniBox.value()),
            'log_sweep': self.logRadioButton.isChecked(),
            'lin_sweep': self.linRadioButton.isChecked(),
            'sense_local': self.senseLocalRadioButton.isChecked(),
            'sense_remote': self.senseRemoteRadioButton.isChecked(),
            'col_source': self.sourceCheckBox.checkState(),
            'col_delay': self.delayCheckBox.checkState(),
            'col_measure': self.measureCheckBox.checkState(),
            'col_time': self.timeCheckBox.checkState(),
            'stabilize_time': self.stableSpinBox.value(),
            'sleep_time': self.sleepSpinBox.value(),
            'chk_loop': self.chkLoop.checkState(),
            'chk_lock_range': self.chkLockRange.checkState(),
            'chk_autorange': self.chkAutorange.checkState(),
        }

        # For pickle, the file needs to be opened in binary mode, hence the "wb"
        with open("last_parameters.pickle", "wb") as params_file:
            pickle.dump(parameters_dict, params_file)

    def load_parameters(self):
        try:
            # For pickle, the file needs to be opened in binary mode, hence the "wb"
            with open("last_parameters.pickle", "rb") as params_file:
                parameters_dict = pickle.load(params_file)

            self.startEdit.setText(parameters_dict['sw_min'])
            self.endEdit.setText(parameters_dict['sw_max'])
            self.stepEdit.setText(parameters_dict['step'])
            self.delaySpinBox.setValue(parameters_dict['delay'])
            self.decadeComboBox.setCurrentIndex(parameters_dict['decade'])
            self.pocetMereniBox.setValue(parameters_dict['n'])

            self.logRadioButton.setChecked(parameters_dict['log_sweep'])
            self.linRadioButton.setChecked(parameters_dict['lin_sweep'])
            self.senseLocalRadioButton.setChecked(
                parameters_dict['sense_local'])
            self.senseRemoteRadioButton.setChecked(
                parameters_dict['sense_remote'])

            self.sourceCheckBox.setChecked(parameters_dict['col_source'])
            self.delayCheckBox.setChecked(parameters_dict['col_delay'])
            self.measureCheckBox.setChecked(parameters_dict['col_measure'])
            self.timeCheckBox.setChecked(parameters_dict['col_time'])

            self.stableSpinBox.setValue(parameters_dict['stabilize_time'])
            self.sleepSpinBox.setValue(parameters_dict['sleep_time'])

            self.chkLoop.setChecked(parameters_dict['chk_loop'])
            self.chkLockRange.setChecked(parameters_dict['chk_lock_range'])
            self.chkAutorange.setChecked(parameters_dict['chk_autorange'])

            self.switch_linear()
            self.switch_dynamic_delay()

            print("Nacteni poslednich parametru uspesne! :)")

        except:
            print("Nacteni poslednich parametru selhalo. :(")

    def switch_linear(self):
        log_sweep = self.logRadioButton.isChecked()
        lin_sweep = self.linRadioButton.isChecked()
        if log_sweep:
            self.stepEdit.setEnabled(False)
            self.decadeComboBox.setEnabled(True)
        else:
            self.decadeComboBox.setEnabled(False)
            self.stepEdit.setEnabled(True)

    def switch_dynamic_delay(self):
        const_delay = self.constantDelayRadio.isChecked()
        dynamic_delay = self.dynamicDelayRadio.isChecked()
        if const_delay:
            self.capacitySpinBox.setEnabled(False)
            self.delaySpinBox.setEnabled(True)
        else:
            self.delaySpinBox.setEnabled(False)
            self.capacitySpinBox.setEnabled(True)

    def enable_ui(self, status):
        ui_elements = [
            self.startEdit,
            self.endEdit,
            self.stepEdit,
            self.delaySpinBox,
            self.decadeComboBox,
            self.pocetMereniBox,
            self.logRadioButton,
            self.linRadioButton,
            self.senseLocalRadioButton,
            self.senseRemoteRadioButton,
            self.sourceCheckBox,
            self.delayCheckBox,
            self.measureCheckBox,
            self.timeCheckBox,
            self.stableSpinBox,
            self.sleepSpinBox,
            self.chkLoop,
            self.chkAutorange,
            self.exportButton,
        ]

        for element in ui_elements:
            element.setEnabled(status)

        if status:
            self.switch_linear()
            self.switch_dynamic_delay()

        if status:
            self.startBtn.clicked.disconnect()
            self.startBtn.clicked.connect(self.startFunc)
            self.startBtn.setText("Start")
            self.exportButton.setFocus()
        else:
            self.startBtn.clicked.disconnect()
            self.startBtn.clicked.connect(self.stopFunc)
            self.startBtn.setText("Abort")
            self.startBtn.setFocus()
            self.exportButton.setText('Export')

    def artSleep(self, sleepTime):
        """
        Čeká čas sleepTime v sekundách, zatím ale každých 50 milisekund řeší
        akce, o které se někdo pokoušel v GUI.
        """
        stop_time = QtCore.QTime()
        stop_time.restart()
        while stop_time.elapsed() < sleepTime * 1000:
            QtWidgets.QApplication.processEvents(QtCore.QEventLoop.AllEvents,
                                                 50)

    def show_notification(self, err):
        msg = QtWidgets.QMessageBox()
        msg.setIcon(QtWidgets.QMessageBox.Warning)
        msg.setText("Chybné parametry sweepu.")
        msg.setInformativeText(err)
        msg.setWindowTitle("Sweeper - varování")
        msg.exec_()
示例#2
0
class GuiProgram(Ui_sweepergui):

    cols = 0
    sweep_id = 0
    full_data = []
    negative_current = False
    log_sweep = True
    stop = True
    decade_combo_values = ['5', '10', '25', '50']

    def __init__(self, dialog):
        Ui_sweepergui.__init__(self)
        self.setupUi(dialog)

        # Connect "add" button with a custom function (addInputTextToListbox)
        self.exportButton.clicked.connect(self.export_data)
        self.startBtn.clicked.connect(self.startFunc)
        self.logRadioButton.clicked.connect(self.switch_linear)
        self.linRadioButton.clicked.connect(self.switch_linear)

        # Populate comboBox
        self.decadeComboBox.clear()
        self.decadeComboBox.addItems(self.decade_combo_values)
        self.decadeComboBox.setCurrentIndex(1)

        # Try to load last parameters from Pickle
        self.load_parameters()

        # Připojení k instrumentu
        #self.inst = Instrument('GPIB0::17::INSTR', visa_location='C:\WINDOWS\SysWOW64\\visa32.dll')
        self.inst = Instrument('GPIB0::17::INSTR', virtual=False)

    def artSleep(self, sleepTime):
        """
        Čeká čas sleepTime v sekundách, zatím ale každých 50 milisekund řeší
        akce, o které se někdo pokoušel v GUI.
        """
        stop_time = QtCore.QTime()
        stop_time.restart()
        while stop_time.elapsed() < sleepTime * 1000:
            QtWidgets.QApplication.processEvents(QtCore.QEventLoop.AllEvents,
                                                 50)

    def plot_figure(self):
        fig1 = Figure()
        ax1f1 = fig1.add_subplot(111)
        ax1f1.plot(np.random.rand(5))
        self.rmmpl()
        self.addmpl(fig1)

    def plot_data(self, data):
        fig1 = Figure()
        ax1f1 = fig1.add_subplot(111)
        ax1f1.set_xlabel('I [A]')
        ax1f1.set_ylabel('U [V]')
        ax1f1.set_title('Sweep ID: ' + self.sweep_id)
        for d in data:
            x, y = d
            ax1f1.plot(x, y, color='red')
            if self.log_sweep:
                ax1f1.set_xscale('log')
            else:
                ax1f1.set_xscale('linear')
        self.rmmpl()
        self.addmpl(fig1)

    def addmpl(self, fig):
        self.canvas = FigureCanvas(fig)
        self.mplvl.addWidget(self.canvas)
        self.canvas.draw()
        self.toolbar = NavigationToolbar(self.canvas,
                                         self.mplwindow,
                                         coordinates=True)
        self.mplvl.addWidget(self.toolbar)

    def rmmpl(self, ):
        self.mplvl.removeWidget(self.canvas)
        self.canvas.close()
        self.canvas.deleteLater()

        self.mplvl.removeWidget(self.toolbar)
        self.toolbar.close()
        self.toolbar.deleteLater()

    def save_parameters(self):
        parameters_dict = {
            'sw_min': str(self.startEdit.text()),
            'sw_max': str(self.endEdit.text()),
            'step': str(self.stepEdit.text()),
            'delay': int(self.delaySpinBox.value()),
            'decade': int(self.decadeComboBox.currentIndex()),
            'n': int(self.pocetMereniBox.value()),
            'log_sweep': self.logRadioButton.isChecked(),
            'lin_sweep': self.linRadioButton.isChecked(),
            'sense_local': self.senseLocalRadioButton.isChecked(),
            'sense_remote': self.senseRemoteRadioButton.isChecked(),
            'col_source': self.sourceCheckBox.checkState(),
            'col_delay': self.delayCheckBox.checkState(),
            'col_measure': self.measureCheckBox.checkState(),
            'col_time': self.timeCheckBox.checkState(),
            'stabilize_time': self.stableSpinBox.value(),
            'sleep_time': self.sleepSpinBox.value()
        }

        # For pickle, the file needs to be opened in binary mode, hence the "wb"
        with open("last_parameters.pickle", "wb") as params_file:
            pickle.dump(parameters_dict, params_file)

    def load_parameters(self):
        try:
            # For pickle, the file needs to be opened in binary mode, hence the "wb"
            with open("last_parameters.pickle", "rb") as params_file:
                parameters_dict = pickle.load(params_file)

            self.startEdit.setText(parameters_dict['sw_min'])
            self.endEdit.setText(parameters_dict['sw_max'])
            self.stepEdit.setText(parameters_dict['step'])
            self.delaySpinBox.setValue(parameters_dict['delay'])
            self.decadeComboBox.setCurrentIndex(parameters_dict['decade'])
            self.pocetMereniBox.setValue(parameters_dict['n'])

            self.logRadioButton.setChecked(parameters_dict['log_sweep'])
            self.linRadioButton.setChecked(parameters_dict['lin_sweep'])
            self.senseLocalRadioButton.setChecked(
                parameters_dict['sense_local'])
            self.senseRemoteRadioButton.setChecked(
                parameters_dict['sense_remote'])

            self.sourceCheckBox.setChecked(parameters_dict['col_source'])
            self.delayCheckBox.setChecked(parameters_dict['col_delay'])
            self.measureCheckBox.setChecked(parameters_dict['col_measure'])
            self.timeCheckBox.setChecked(parameters_dict['col_time'])

            self.stableSpinBox.setValue(parameters_dict['stabilize_time'])
            self.sleepSpinBox.setValue(parameters_dict['sleep_time'])

            self.switch_linear()

            print("Nacteni poslednich parametru uspesne! :)")

        except:
            print("Nacteni poslednich parametru selhalo. :(")

    def validateInput(self, sw_min, sw_max, decade, delay, log_sweep, step):
        # Unit testing :P
        try:
            a = float(sw_min)
            a = float(sw_max)
            if not log_sweep:
                a = float(step)
        except:
            return False

        # Code
        if log_sweep:
            if (float(sw_min) > 0 and float(sw_max) > 0
                    and float(sw_min) != float(sw_max)):
                return True
            else:
                return False
        else:
            # lin sweep
            if (float(sw_min) != float(sw_max)
                    and abs(float(sw_max) - float(sw_min)) > float(step)):
                return True
            else:
                return False

    def stopFunc(self):
        print('ABORT! Attempted to STOP sweep!')
        self.inst.operate(False)
        self.stop = True

    def startFunc(self):
        ''' Posbírá z GUI parametry a odpovídajícím způsobem přeloží.'''
        # vypnout ruční brzdu
        self.stop = False

        # aktuální čas pro ID
        self.sweep_id = '{0:%Y-%m-%d_%H-%M-%S}'.format(datetime.datetime.now())

        sw_min = str(self.startEdit.text())
        sw_max = str(self.endEdit.text())
        step = str(self.stepEdit.text())
        delay = str(self.delaySpinBox.value())
        decade = str(self.decadeComboBox.currentIndex())
        n = self.pocetMereniBox.value()

        self.bigProgBar.setValue(0)
        self.bigProgBar.setMaximum(n)
        self.littleProgBar.setValue(0)

        log_sweep = self.logRadioButton.isChecked()
        lin_sweep = self.linRadioButton.isChecked()
        self.log_sweep = log_sweep

        sense_local = self.senseLocalRadioButton.isChecked()
        sense_remote = self.senseRemoteRadioButton.isChecked()
        self.sense_local = sense_local

        col_source = self.sourceCheckBox.checkState()
        col_delay = self.delayCheckBox.checkState()
        col_measure = self.measureCheckBox.checkState()
        col_time = self.timeCheckBox.checkState()

        self.cols = 0
        if col_source:
            self.cols += 1
        if col_delay:
            self.cols += 2
        if col_measure:
            self.cols += 4
        if col_time:
            self.cols += 8

        if self.validateInput(sw_min, sw_max, decade, delay, log_sweep, step):
            self.save_parameters()
            self.measure(sw_min, sw_max, decade, delay, log_sweep, step, n)
        else:
            print('Input failed validation.')
            self.show_notification('failed_validation')

    def show_notification(self, code):
        if code == 'failed_validation':
            msg = QtWidgets.QMessageBox()
            msg.setIcon(QtWidgets.QMessageBox.Warning)
            msg.setText("Chybné parametry sweepu.")
            msg.setInformativeText("")
            msg.setWindowTitle("Sweeper - varování")
            msg.setDetailedText("TODO")
            msg.exec_()

    def measure(self, sw_min, sw_max, decade, delay, log_sweep, step, n):
        ''' Spustí měření s již validovanými parametry. '''
        # Disable UI
        self.enable_ui(False)

        # Declare Export not done on this sweep
        self.exportButton.setText('Export')

        # Local or Remote sense?
        if self.sense_local:
            print("Zapinam Sense - LOCAL.")
            self.inst.write("O0X")
        else:
            print("Zapinam Sense - REMOTE.")
            self.inst.write("O1X")

        # Úvodní stabilizace
        stabilize_time = self.stableSpinBox.value()
        print("Uvodni stabilizace v DC modu - {} s.".format(stabilize_time))
        self.stabilize(sw_min, stabilize_time)

        # Nastavení sweepu
        self.inst.set_source_and_function('I', 'Sweep')
        # Nastavení formátu dat
        self.inst.write("G" + str(self.cols) + ",2,2X")

        if self.log_sweep:
            self.inst.write("Q2," + sw_min + "," + sw_max + "," + decade +
                            ",0," + delay + "X")
        else:
            self.inst.write("Q1," + sw_min + "," + sw_max + "," + step +
                            ",0," + delay + "X")

        data = []
        self.full_data = []
        n_hotovych_mereni = 0
        for mereni in range(n):
            if self.stop:
                break
            output = self.run_sweep()
            if output:
                sweep_results = misc.nice_format(output, cols=self.cols)
                data.append(misc.unpack(sweep_results, cols=self.cols))
                self.full_data.append(sweep_results)

                print('Ukladam docasna data...')
                self.dump_data()

                n_hotovych_mereni += 1
                self.bigProgBar.setValue(n_hotovych_mereni)
                self.plot_data(data)

                if n_hotovych_mereni != n:
                    sleep_time = self.sleepSpinBox.value()
                    print("Pauza mezi sweepy - {} s.".format(sleep_time))
                    self.artSleep(sleep_time)

            else:
                print("Output was empty. Interrupted measurement?")

        self.inst.operate(False)
        self.enable_ui(True)
        #self.plot_data(data)

    def run_sweep(self):
        ''' Provede jeden sweep, který už musí být definovaný ve stroji.
            Na konci 3 vteřiny spí, aby se mělo napětí čas ustálit před dalším měřením.

            Vrací string se všemi hodnotami sweepu.
        '''
        print('\nSpoustim sweep...')
        self.inst.write("U8X")
        out = self.inst.read()
        print('U8X -> ' + out)
        out = out.replace('\r', '').replace('\n', '')
        print("Out: {}".format(out))
        sweep_defined_size = int(out[-4:])
        print('Pocet bodu ve sweepu: ' + str(sweep_defined_size))

        self.littleProgBar.setValue(0)
        self.inst.trigger()  # Immediate trigger
        sweep_done = False
        while not sweep_done:
            if self.stop:
                break
            self.artSleep(0.2)
            self.inst.write("U11X")
            status = self.inst.read()
            if (status == 'SMS' + str(sweep_defined_size).zfill(4) + '\r\n'):
                sweep_done = True
            else:
                status_edit = status.replace('\r', '').replace('\n', '')
                try:
                    progress = int(status_edit[-4:])
                    self.littleProgBar.setValue(
                        int(progress / sweep_defined_size * 100))
                except:
                    print('Invalid progress!')
        print('Jeden sweep hotov.')
        self.littleProgBar.setValue(100)

        if self.stop:
            return ""
        else:
            return self.inst.read()

    def stabilize(self, bias, stabilize_time):
        # Počáteční stabilizace
        self.inst.set_source_and_function('I', 'DC')
        self.inst.write("B" + bias + ",0,20X")
        self.inst.operate(True)
        self.inst.trigger()
        self.artSleep(stabilize_time)

    def switch_linear(self):
        log_sweep = self.logRadioButton.isChecked()
        lin_sweep = self.linRadioButton.isChecked()
        if log_sweep:
            self.stepEdit.setEnabled(False)
            self.decadeComboBox.setEnabled(True)
        else:
            self.decadeComboBox.setEnabled(False)
            self.stepEdit.setEnabled(True)

    def enable_ui(self, status):
        ui_elements = [
            self.startEdit,
            self.endEdit,
            self.stepEdit,
            self.delaySpinBox,
            self.decadeComboBox,
            self.pocetMereniBox,
            self.logRadioButton,
            self.linRadioButton,
            self.senseLocalRadioButton,
            self.senseRemoteRadioButton,
            self.sourceCheckBox,
            self.delayCheckBox,
            self.measureCheckBox,
            self.timeCheckBox,
            self.stableSpinBox,
            self.sleepSpinBox,
        ]

        for element in ui_elements:
            element.setEnabled(status)

        if status:
            self.switch_linear()

        if status:
            self.startBtn.clicked.disconnect()
            self.startBtn.clicked.connect(self.startFunc)
            self.startBtn.setText("Start")
        else:
            self.startBtn.clicked.disconnect()
            self.startBtn.clicked.connect(self.stopFunc)
            self.startBtn.setText("Abort")
            self.exportButton.setText('Export')

    def get_export_data(self):
        output = ""
        output += '# ======== Sweep ID: ' + self.sweep_id + ' ========' + '\n'
        output += '# ======== ' + str(len(
            self.full_data)) + ' sweeps' + ' ========' + '\n'

        output += self.get_measurement_parameters()

        for data in self.full_data:
            output += '# ======== SWEEP START ========\n'
            output += data.replace(',', '    ')
            output += '# ======== SWEEP END ========\n\n'

        return output

    def dump_data(self):
        save_file_name = 'C:\Repa\k237\sweeper\data_temp.txt'
        try:
            with open(save_file_name, "w", encoding="utf-8") as text_file:
                text = self.get_export_data()
                text_file.write(text)
        except:
            print("Nouzovy dump se nepovedl.")

    def export_data(self):
        """
        Exportuje data pomocí ukládacího dialogu Qt. V případě chybného zadání
        souboru nic neudělá a postěžuje si do konzole.
        """
        proposed_name = self.sweep_id

        # Qt Dialog na výběr souboru k uložení
        save_file_name, _ = QtWidgets.QFileDialog.getSaveFileName(
            None, 'Exportovat výsledky měření',
            'C:\Repa\k237\data\sweep_' + proposed_name + '.txt',
            'Text Files (*.txt);;All Files (*)')

        # Vlastní uložení souboru
        try:
            with open(save_file_name, "w", encoding="utf-8") as text_file:
                text = self.get_export_data()
                text_file.write(text)

            # Update GUI
            self.exportButton.setText('Export ✔')
        except:
            print("Export neuspesny. Data pravdepodobne nejsou ulozena!")

            # Update GUI
            self.exportButton.setText('Export ✗')

    def get_measurement_parameters(self):
        out = ''
        out += "# Sweep ID: {}\n".format(self.sweep_id)
        if self.log_sweep:
            out += "# Log sweep:\n"
            out += "# Rozsah (min, max) [A]: {}, {}\n".format(
                self.startEdit.text(), self.endEdit.text())
            out += "# Bodů na dekádu [-]: {}\n".format(
                self.decade_combo_values[self.decadeComboBox.currentIndex()])
        else:
            out += "# Linear sweep\n"
            out += "# Rozsah (min, max, points) [A]: {}, {}, {}\n".format(
                self.startEdit.text(), self.endEdit.text(),
                self.stepEdit.text())

        out += "# Delay [ms]: {}\n".format(self.delaySpinBox.value())
        out += "# Počet charakteristik [-]: {}\n".format(
            self.pocetMereniBox.value())

        if self.sense_local:
            out += "# Sense: local\n"
        else:
            out += "# Sense: remote\n"

        out += "# Sloupce (Source, Measure, Delay, Time): {} {} {} {}\n".format(
            int(self.sourceCheckBox.checkState() / 2),
            int(self.measureCheckBox.checkState() / 2),
            int(self.delayCheckBox.checkState() / 2),
            int(self.timeCheckBox.checkState() / 2))

        out += "# Uvodní DC stabilizace [s]: {}\n".format(
            self.stableSpinBox.value())
        out += "# Stabilizace [s]: {}\n".format(self.sleepSpinBox.value())

        return out
示例#3
0
class Main(QMainWindow, Ui_MainWindow):

    def __init__(self, ):
        super(Main, self).__init__()
        self.setupUi(self)

        self.showMaximized()
        self.ave = np.array([])         # empty array for average bore
        self.auto_flag = False          # autoscale flag
        self.spinBoxval = 0             # defualt 4D data_cube dimension
        self.spinBox.hide()             # hide option unless 4D
        self.colourmap = 'viridis'      # default colourmap
        self.interpMethod = 'nearest'   # default interp method
        self.cmapmin = None             # default colourbar range, i.e let matplotlib decide
        self.cmapmax = None
        self.hres = 1                   # default res, i.e jut voxel numbers on axis
        self.vres = 1
        self.Normx = None               # normalisation method. default set to None
        self.Normy = None
        self.Normz = None

        self.XView.setChecked(True)
        self.fig = Figure()
        self.ax1 = self.fig.add_subplot(111)
        self.X = datacube()
        self.AveBoreView = 'X'

        # change view of cube
        self.XView.toggled.connect(lambda: self.btnstate(self.XView))
        self.YView.toggled.connect(lambda: self.btnstate(self.YView))
        self.ZView.toggled.connect(lambda: self.btnstate(self.ZView))
        self.Bore.toggled.connect(lambda: self.btnstate(self.Bore))
        self.AverageBore.toggled.connect(
            lambda: self.btnstate(self.AverageBore))

        # update data when slider moved
        self.Scroll_Horz.valueChanged[int].connect(self.sliderval)
        self.Scroll_Vert.valueChanged[int].connect(self.sliderval)

        self.file_open(args)
        self.Open.triggered.connect(self.file_open)
        self.Save_Avg_Bore.triggered.connect(self.saveBore)
        self.Reset.triggered.connect(self.reset_plot)
        self.AutoScale.triggered.connect(self.Auto_Scale_plot)
        # self.Bore_View.triggered.connect(self.ViewBore)
        self.action_Save_Gif.triggered.connect(self.saveGif)
        self.action_Colour_Map.triggered.connect(self.changeColourMap)
        self.action_Interpolation_Method.triggered.connect(self.changeInterpolationMethod)
        self.action_Colour_Bar_Clip.triggered.connect(self.changeclipColourBarRange)
        self.action_Save_Image.triggered.connect(self.saveImage)
        self.action_Normalisation_Method.triggered.connect(self.changeNormMethod)
        self.action_Bore_Location.triggered.connect(self.setBoreLocation)

        self.spinBox.valueChanged.connect(self.changeSpinbox)

    def setBoreLocation(self, ):

        xloc, ok = QtWidgets.QInputDialog.getInt(
            self, 'Input location', 'Enter X location:')

        yloc, ok = QtWidgets.QInputDialog.getInt(
            self, 'Input location', 'Enter Y location:')

        self.Scroll_Horz.setValue(xloc)
        self.Scroll_Vert.setValue(yloc)

        if self.Bore.isChecked():
            if self.BoreView == 'X':
                self.im.set_ydata(self.X.data[:, xloc, yloc][::])
            elif self.BoreView == 'Y':
                self.im.set_ydata(self.X.data[xloc, :, yloc][::])
            elif self.BoreView == 'Z':
                self.im.set_ydata(self.X.data[yloc, xloc, :][::])

        # try and redraw
        try:
            self.im.axes.figure.canvas.draw()
            if self.auto_flag:
                self.im.autoscale()
        except AttributeError:
            pass

    def changeNormMethod(self, ):
        # func to change Normalisation method of matshow
        method = self.getNormDialog()
        if(method == 'Log'):
            self.Normx = colors.LogNorm(vmin=0.1,
                                        vmax=self.X.data[self.ind, :, :].max())
            self.Normy = colors.LogNorm(vmin=0.1,
                                        vmax=self.X.data[:, self.ind, :].max())
            self.Normz = colors.LogNorm(vmin=0.1,
                                        vmax=self.X.data[:, :, self.ind].max())
        elif(method == 'Symmetric Log'):
            self.Normx = colors.SymLogNorm(linthresh=1.,
                                           vmin=self.X.data[self.ind, :, :].min(),
                                           vmax=self.X.data[self.ind, :, :].max())
            self.Normy = colors.SymLogNorm(linthresh=1.,
                                           vmin=self.X.data[:, self.ind, :].min(),
                                           vmax=self.X.data[:, self.ind, :].max())
            self.Normz = colors.SymLogNorm(linthresh=1.,
                                           vmin=self.X.data[:, :, self.ind].max(),
                                           vmax=self.X.data[:, :, self.ind].max())
        elif(method == 'Linear'):
            self.Normx = None
            self.Normy = None
            self.Normz = None
        self.reset_plot(False)
        self.init_plot()

    def saveImage(self, ):
        # saves data and image of current view
        name = self.showGifDialog()
        if self.XView.isChecked():
            np.savetxt(name + '.dat', self.X.data[self.Scroll_Vert.value(), :, :],
                       delimiter=' ')
        elif self.YView.isChecked():
            np.savetxt(name + '.dat', self.X.data[:, self.Scroll_Vert.value(), :],
                       delimiter=' ')
        elif self.ZView.isChecked():
            np.savetxt(name + '.dat', self.X.data[:, :, self.Scroll_Vert.value()],
                       delimiter=' ')

        self.hres, self.vres = self.showextentDialog()
        # scale x, y ticks to actual scale based upon user definition
        # thanks https://stackoverflow.com/a/17816809/6106938
        # change so that it uses extent=[xmin, xmax, ymin, ymax]
        # set default as None
        # then change here. extent added to matshow(*args, extent=[...])
        ticks = ticker.FuncFormatter(lambda x, pos: '{0:g}'.format(x * self.hres))
        self.ax1.xaxis.set_major_formatter(ticks)
        ticks = ticker.FuncFormatter(lambda y, pos: '{0:g}'.format(y * self.vres))
        self.ax1.yaxis.set_major_formatter(ticks)
        self.fig.savefig(name + '.png')

    def changeColourMap(self, ):
        # change cmap
        self.colourmap = self.showColourmapsDialog()
        self.reset_plot(False)
        self.init_plot()

    def changeclipColourBarRange(self, ):
        # change vmin, vmax for cbar

        self.cmapmin, self.cmapmax = self.showclipColourBarDialog()

        self.reset_plot(False)
        self.init_plot()

    def changeInterpolationMethod(self, ):
        # change interpolation method for image
        self.interpMethod = str(self.showInterpolationDialog())
        self.reset_plot(False)
        self.init_plot()

    def saveGif(self):
        rang = self.showGifframesDialog()  # get range of frames
        step = self.showGifstepDialog()    # get number of images
        name = self.showGifDialog()        # name of file
        tight = self.showGifExtent()       # tight or not
        # loop over range and make images
        tmpplace = self.Scroll_Vert.value()
        if rang * step > tmpplace:
            rang = tmpplace
        for i in range(rang):
            self.Scroll_Horz.setValue(self.ind)
            self.Scroll_Vert.setValue(tmpplace - (i * step))
            self.sliderval()
            if tight:
                extent = self.ax1.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
                self.fig.savefig(str(i).zfill(3) + 'pic.png', bbox_inches=extent)
            else:
                self.fig.savefig(str(i).zfill(3) + 'pic.png')
        # use ffmpeg to create gif
        if tight:
            os.system("mogrify -trim *pic.png")
        os.system("ffmpeg -framerate 10 -pattern_type glob -i '*pic.png' -c:v libx264 -r 24 -pix_fmt yuv420p -vf 'pad=ceil(iw/2)*2:ceil(ih/2)*2' " + name + ".mp4")
        os.system('rm *pic.png')
        print('done')

    def changeSpinbox(self):
        # for 4d data cubes
        self.spinBoxval = int(self.spinBox.value())
        fd = open(self.name, 'rb')
        self.readslice(fd, self.ndim, np.float64, self.cubeorder)
        self.reset_plot()
        self.init_plot()

    def Auto_Scale_plot(self):
        # autoscale cbar on plot and reset clipping if any
        self.cmapmin = None
        self.cmapmax = None
        if not self.auto_flag:
            self.auto_flag = True
        else:
            self.auto_flag = False

    def sliderval(self):
        # move slider and update data
        if self.XView.isChecked():
            # fd = open(self.name, 'rb')
            self.X.readslice(self.Scroll_Horz.value())
            self.im.set_data(self.X.data[self.Scroll_Vert.value(), :, :])
            # self.Scroll_Horz.setValue(0)  # pin unsed slider
        elif self.YView.isChecked():
            self.X.readslice(self.Scroll_Horz.value())
            self.im.set_data(self.X.data[:, self.Scroll_Vert.value(), :])
            # self.Scroll_Horz.setValue(0)  # pin unsed slider
        elif self.ZView.isChecked():
            self.X.readslice(self.Scroll_Horz.value())
            self.im.set_data(self.X.data[:, :, self.Scroll_Vert.value()])
            # self.Scroll_Horz.setValue(0)  # pin unsed slider
        elif self.Bore.isChecked():
            if self.BoreView == 'X':
                self.im.set_ydata(self.X.data[:, self.Scroll_Horz.value(), self.Scroll_Vert.value()][::])
                if self.auto_flag:
                    self.ax1.relim()
                    self.ax1.autoscale_view(True, True, True)
            elif self.BoreView == 'Y':
                self.im.set_ydata(self.X.data[self.Scroll_Horz.value(), :, self.Scroll_Vert.value()][::])
                if self.auto_flag:
                    self.ax1.relim()
                    self.ax1.autoscale_view(True, True, True)
            elif self.BoreView == 'Z':
                self.im.set_ydata(self.X.data[self.Scroll_Vert.value(), self.Scroll_Horz.value(), :][::])
                if self.auto_flag:
                    self.ax1.relim()
                    self.ax1.autoscale_view(True, True, True)
        elif self.AverageBore.isChecked():
            self.Scroll_Horz.setValue(self.ind)
            self.Scroll_Vert.setValue(self.ind)

        # try and redraw
        try:
            self.im.axes.figure.canvas.draw()
            if self.auto_flag:
                self.im.autoscale()
        except AttributeError:
            pass

    def addmpl(self):
        # add plot to anvas
        self.rmmpl()
        self.canvas = FigureCanvas(self.fig)
        self.mplvl.addWidget(self.canvas)
        self.canvas.draw()
        self.toolbar = NavigationToolbar(self.canvas, self.mplwindow)
        self.mplvl.addWidget(self.toolbar)

    def rmmpl(self):
        # delete plot from canvas
        try:
            self.canvas.close()
            self.canvas.deleteLater()
            self.toolbar.close()
            self.toolbar.deleteLater()
            gc.collect()
        except:
            pass

    def saveBore(self,):
        # save bore as a list of points
        name = QtWidgets.QFileDialog.getSaveFileName(self, 'Save File')
        f = open(name, 'w')
        if len(self.ave) > 1:
            for i in range(len(self.ave)):
                f.write(str(self.ave[i]) + '\n')
            f.close()
        else:
            if self.BoreView == "X":
                tmp = self.X[:, self.Scroll_Horz.value(), self.Scroll_Vert.value()]
            elif self.BoreView == "Y":
                tmp = self.X[self.Scroll_Horz.value(), :, self.Scroll_Vert.value()]
            elif self.BoreView == "Z":
                tmp = self.X[self.Scroll_Horz.value(), self.Scroll_Vert.value(), :]

            for i in range(len(tmp)):
                f.write(str(tmp[i]) + '\n')

    def file_open(self, args):

        self.reset_plot()

        while True:
            try:
                # get file name
                if args.file is None:
                    self.X.name = QtWidgets.QFileDialog.getOpenFileName(self, 'Open File')[0]
                else:
                    self.X.name = args.file

                # get precision of data cube
                self.X.dtype, self.X.cubeorder, item = self.getPrec(args)

                # get dimensions of data cube. can be guessed
                bool4d = False
                if self.X.cubeorder == 4:
                    bool4d = True
                self.X.ndim = self.getSize(args, item, bool4d)

                try:
                    fd = open(self.X.name, 'rb')
                except FileNotFoundError:
                    self.ErrorDialog("File not Found!")
                    self.X.name = QtWidgets.QFileDialog.getOpenFileName(self, 'Open File')[0]

                self.X.readslice(0)
                self.init_plot()
                break
            except ValueError:
                size = os.path.getsize(self.X.name)
                if "Real*8" in item:
                    size /= 8
                elif "Real*4" in item:
                    size /= 4

                mssg = "Value of Ndim or precision is incorrect for this data cube.\n On disk size is: {:010d}.\n".format(int(size))

                val2 = self.X.is_perfect_n(size, 2.)
                val3 = self.X.is_perfect_n(size, 3.)

                if (val2 and val3) != 0:
                    mssg += " Try x=y={:04d}, z=1\n or x=y=z={:04d}.".format(int(val2), int(val3))
                elif val2 != 0:
                    mssg += "Try x=y={:04d}, z=1.".format(int(val2))
                elif val3 != 0:
                    mssg += "Try x=y=z={:04d}.".format(int(val3))
                self.ErrorDialog(mssg)

                args.ndim = None
                args.fpprec = None
            except UnboundLocalError:
                pass
                break

    def getSize(self, args, item, bool4d):
        if args.ndim is None and item:
            size = os.path.getsize(self.X.name)
            if "Real*8" in item:
                size /= 8
            elif "Real*4" in item:
                size /= 4
            if self.X.is_perfect_n(size, 3.) != 0:
                size = self.X.is_perfect_n(size, 3.)
                ndim = (size, size, size)
            else:
                ndim = self.showNdimDialog(bool4d)
        else:
            ndim = (args.ndim, args.ndim, args.ndim)

        return ndim

    def getPrec(self, args):
        # get precision of data cube
        item = None
        if args.fpprec is None:
            item = str(self.showDtDialog())
            if "Real*8" in item:
                dt = np.float64
            elif "Real*4" in item:
                dt = np.float32

            if "4 dim" in item:
                dim = 4
            elif "3 dim" in item:
                dim = 3
        else:
            if args.fpprec == 1:
                dt = np.float32
                dim = 4
            elif args.fpprec == 2:
                dt = np.float64
                dim = 4
            elif args.fpprec == 3:
                dt = np.float32
                dim = 3
            elif args.fpprec == 4:
                dt = np.float64
                dim = 3
        return dt, dim, item

    def btnstate(self, b):

        if b.text() == "X View":
            if b.isChecked() is True:
                self.reset_plot(False)
                self.Scroll_Vert.setMaximum(self.rows - 1)
                self.im = self.ax1.matshow(self.X.data[self.ind, :, :],
                                           vmin=self.cmapmin, vmax=self.cmapmax,
                                           cmap=str(self.colourmap), interpolation=self.interpMethod,
                                           norm=self.Normx)
                self.fig.colorbar(self.im)
                self.fig.set_tight_layout(True)
                self.ax1.set_aspect('auto')
                self.addmpl()

        if b.text() == "Y View":
            if b.isChecked() is True:
                self.reset_plot(False)
                self.Scroll_Vert.setMaximum(self.cols - 1)
                self.im = self.ax1.matshow(self.X.data[:, self.ind, :],
                                           vmin=self.cmapmin, vmax=self.cmapmax,
                                           cmap=str(self.colourmap), interpolation=self.interpMethod,
                                           norm=self.Normy)
                self.fig.colorbar(self.im)
                self.fig.set_tight_layout(True)
                self.ax1.set_aspect('auto')
                self.addmpl()

        if b.text() == "Z View":
            if b.isChecked() is True:
                self.reset_plot(False)
                self.Scroll_Vert.setMaximum(self.slices - 1)
                self.im = self.ax1.matshow(self.X.data[:, :, self.ind],
                                           vmin=self.cmapmin, vmax=self.cmapmax,
                                           cmap=str(self.colourmap), interpolation=self.interpMethod,
                                           norm=self.Normz)
                try:
                    self.fig.colorbar(self.im)
                except ZeroDivisionError:
                    self.ErrorDialog("Divison by zero, try another range")
                    self.Normz = None
                    self.Normy = None
                    self.Normz = None
                    self.cmapmin = None
                    self.cmapmax = None
                    self.btnstate(b)
                self.fig.set_tight_layout(True)
                self.ax1.set_aspect('auto')
                self.addmpl()

        if b.text() == "Draw Bore":
            if b.isChecked() is True:
                self.ViewBore()
                self.reset_plot(False)
                if self.BoreView == 'X':
                    self.im, = self.ax1.plot(self.X.data[:, self.ind, self.ind])
                elif self.BoreView == 'Y':
                    self.im, = self.ax1.plot(self.X.data[self.ind, :, self.ind])
                elif self.BoreView == 'Z':
                    self.im, = self.ax1.plot(self.X.data[self.ind, self.ind, :])
                self.fig.set_tight_layout(True)
                self.addmpl()

        if b.text() == "Avg. Bore":
            if b.isChecked() is True:
                self.AveBoreChecked()

    def ViewBore(self):
        self.BoreView = self.showBoreViewDialog()
        if self.BoreView == 'X':
            self.view = (1, 2)
        elif self.BoreView == 'Y':
            self.view = (0, 2)
        elif self.BoreView == 'Z':
            self.view = (0, 1)

    def AveBoreChecked(self):

        self.ViewBore()

        self.reset_plot(False)
        if len(self.ave) == 0:
            self.ave = np.array([])
            self.ave = np.sum(self.X.data, self.view)
            self.ave /= (len(self.X.data[self.view[0]]) * len(self.X.data[self.view[1]]))

        self.im = self.ax1.plot(self.ave[::])
        self.fig.set_tight_layout(True)
        self.addmpl()

    def reset_plot(self, *args):
        self.ave = np.array([])

        self.fig.clf()
        self.ax1.clear()
        gc.collect()  # fixes most of memory leak

        self.fig = Figure()
        self.ax1 = self.fig.add_subplot(111)
        self.rmmpl()

    def init_plot(self, ):

        self.rmmpl()

        if self.X.cubeorder == 4:
            self.rows, self.cols, self.slices, self.depth = self.X.ndim
        else:
            self.rows, self.cols, self.slices = self.X.ndim
            self.depth = 0
        self.ind = 0  # int(rows / 2)
        if self.XView.isChecked():
            view = self.XView
            self.Scroll_Vert.setMaximum(self.rows)
            self.Scroll_Horz.setMaximum(self.depth)
        elif self.YView.isChecked():
            view = self.YView
            self.Scroll_Vert.setMaximum(self.cols)
            self.Scroll_Horz.setMaximum(self.depth)
        elif self.ZView.isChecked():
            view = self.ZView
            self.Scroll_Vert.setMaximum(self.slices)
            self.Scroll_Horz.setMaximum(self.cols)
        elif self.AverageBore.isChecked():
            view = self.AverageBore
            self.Scroll_Vert.setMaximum(self.rows)
        elif self.Bore_View.isChecked():
            view = self.ViewBore
            self.Scroll_Vert.setMaximum(self.cols)
            self.Scroll_Horz.setMaximum(self.rows)
        self.btnstate(view)
        self.Scroll_Horz.setValue(self.ind)
        self.Scroll_Vert.setValue(self.ind)

    def showGifframesDialog(self, ):
        text, ok = QtWidgets.QInputDialog.getInt(
            self, '# of frames', 'Enter # of frames:')
        if ok:
            return(text)

    def showGifstepDialog(self, ):
        text, ok = QtWidgets.QInputDialog.getInt(
            self, 'Step size', 'Enter value of step:')
        if ok:
            return(text)

    def showGifExtent(self, ):
        items = ("Colour Bar", "No Colour Bar")

        text, ok = QtWidgets.QInputDialog.getItem(
            self, "Colour Bar on GIF?", " ", items, 0, False)

        if ok and text:
            if items == "Colour Bar":
                text = False
            else:
                text = True
            return text

    def showNdimDialog(self, bool4d):
        text1, ok1 = QtWidgets.QInputDialog.getInt(
            self, 'Input Ndim', 'Enter X Ndim:')
        if ok1:
            text2, ok2 = QtWidgets.QInputDialog.getInt(
                self, 'Input Ndim', 'Enter Y Ndim:')
            if ok2:
                text3, ok3 = QtWidgets.QInputDialog.getInt(
                    self, 'Input Ndim', 'Enter Z Ndim:')
                if ok3 and bool4d:
                    text4, ok4 = QtWidgets.QInputDialog.getInt(
                        self, 'Input Ndim', 'Enter T Ndim:')
                    return (text1, text2, text3, text4)
                else:
                    return (text1, text2, text3)

    def showDtDialog(self, ):
        items = ("4 dim Real*4", "4 dim Real*8",
                 "3 dim Real*4", "3 dim Real*8")

        item, ok = QtWidgets.QInputDialog.getItem(self, "Select Fortran Precision",
                                                  "Precisions", items, 0, False)

        if ok and item:
            return item

    def showBoreViewDialog(self, ):
        items = ("X", "Y", "Z")
        item, ok = QtWidgets.QInputDialog.getItem(self, "Select Average Bore Direction",
                                                  "Views", items, 0, False)
        if ok and item:
            return item

    def showGifDialog(self, ):
        text, ok = QtWidgets.QInputDialog.getText(
            self, 'Filename Dialog', 'Enter filename:')
        if ok:
            return str(text)

    def showextentDialog(self, ):
        hres, ok = QtWidgets.QInputDialog.getDouble(
            self, 'Data Extent', 'Enter horizontal resolution:', 0, -100, 100, 9,)
        if ok:
            vres, ok = QtWidgets.QInputDialog.getDouble(
                self, 'Data Extent', 'Enter vertical resolution:', 0, -100, 100, 9,)
            if ok:
                return (hres, vres)

    def getNormDialog(self, ):
        items = ("Log", "Linear", "Symmetric Log")

        item, ok = QtWidgets.QInputDialog.getItem(self, "Select cbar normalisation method",
                                                  "Method:", items, 0, False)
        if ok and item:
            return item

    def showColourmapsDialog(self, ):
        items = ('viridis', 'inferno', 'plasma', 'magma', 'Blues', 'BuGn',
                 'BuPu', 'GnBu', 'Greens', 'Greys', 'Oranges', 'OrRd', 'PuBu',
                 'PuBuGn', 'PuRd', 'Purples', 'RdPu', 'Reds', 'YlGn', 'YlGnBu',
                 'YlOrBr', 'YlOrRd', 'afmhot', 'autumn', 'bone', 'cool',
                 'copper', 'gist_heat', 'gray', 'hot', 'pink', 'spring',
                 'summer', 'winter', 'BrBG', 'bwr', 'coolwarm', 'PiYG', 'PRGn',
                 'PuOr', 'RdBu', 'RdGy', 'RdYlBu', 'RdYlGn', 'Spectral',
                 'seismic', 'Accent', 'Dark2', 'Paired', 'Pastel1', 'Pastel2',
                 'Set1', 'Set2', 'Set3', 'Vega10', 'Vega20', 'Vega20b',
                 'Vega20c', 'gist_earth', 'terrain', 'ocean', 'gist_stern',
                 'brg', 'CMRmap', 'cubehelix', 'gnuplot', 'gnuplot2',
                 'gist_ncar', 'nipy_spectral', 'jet', 'rainbow', 'gist_rainbow',
                 'hsv', 'flag', 'prism')
        item, ok = QtWidgets.QInputDialog.getItem(self, "Select Colour Map",
                                                  "Cmaps", items, 0, False)
        if ok and item:
            return item

    def showInterpolationDialog(self, ):
        items = ('none', 'nearest', 'bilinear', 'bicubic', 'spline16',
                 'spline36', 'hanning', 'hamming', 'hermite', 'kaiser',
                 'quadric', 'catrom', 'gaussian', 'bessel', 'mitchell',
                 'sinc', 'lanczos')
        item, ok = QtWidgets.QInputDialog.getItem(self, "Select Interpolation Method",
                                                  "Methods", items, 0, False)
        if ok and item:
            return item

    def showclipColourBarDialog(self, ):
        text1, ok1 = QtWidgets.QInputDialog.getDouble(
            self, 'Input cbar min', 'Enter min:', 0., np.finfo("d").min, np.finfo("d").max, 10)
        if ok1:
            text2, ok2 = QtWidgets.QInputDialog.getDouble(
                self, 'Input cbar max', 'Enter max:', 0., np.finfo("d").min, np.finfo("d").max, 10)
            if ok2:
                return (float(text1), float(text2))

    def ErrorDialog(self, ErrMsg):
        QtWidgets.QMessageBox.warning(self, "Error", ErrMsg)