def start_plotting(self): """| Prepares for plotting by making a time axis based on the axis length and pause time, and starts an empty counts array. | Then opens 1 to 3 plot windows and a thread to plot them, depending on the selected channels. """ self.logger.info('Should start counting here') self.logger.debug('Settings: {}, {}, {}'.format(self.exp_type, self.pausetime, self.lengthaxis)) self.time_axis = np.linspace(0, self.lengthaxis.m_as('s') * self.pausetime.m_as('s'), int(self.lengthaxis.m_as('s'))) * ur('s') self.logger.debug("{}".format(self.time_axis)) if self.something_selected: if self.sync: self.draw0 = DrawCounts() self.draw0.counts_plot.setTitle("<span style=\"color:yellow;font-size:30px\">Counts on sync channel </span>") #self.draw0.counts_plot.plot(self.time_axis.m_as('s'), Counts, clear=True, pen=self.pen) if self.chan1: self.draw1 = DrawCounts() self.draw1.counts_plot.setTitle("<span style=\"color:orange;font-size:30px\">Counts on channel 1 </span>") #self.draw1.counts_plot.plot(self.time_axis.m_as('s'), Counts, clear=True, pen=self.pen) if self.chan2: self.draw2 = DrawCounts() self.draw2.counts_plot.setTitle("<span style=\"color:red;font-size:30px\">Counts on channel 2 </span>") #self.draw2.counts_plot.plot(self.time_axis.m_as('s'), Counts, clear=True, pen=self.pen) self.plotting_thread = WorkThread(self.update_plot) self.plotting_thread.start() else: self.logger.warning('You have to select something first, before making a graph.')
def make_thread(self): print('starting sweep') print(id(self.instr.controller._osa)) self.worker_thread = WorkThread(self.instr.take_spectrum) print("starting") self.worker_thread.start()
def __init__(self, thorlabs_instrument, also_close_output=False): super().__init__() self.logger = logging.getLogger(__name__) self.overall_layout = QHBoxLayout() self.setLayout(self.overall_layout) self.motor = thorlabs_instrument self.logger.debug('You are connected to a {}'.format(self.motor.kind_of_device)) self.title = 'Thorlabs {} GUI'.format(self.motor._name) self.saved_position = None self.current_position = None self.distance = 1.0*ur('mm') self.min_distance = -12.0 * ur('mm') self.max_distance = 12.0 * ur('mm') self.stop = self.stop_moving self.initUI() self.timer = QTimer() self.timer.timeout.connect(self.set_current_motor_position_label) self.timer.start(100) #time in ms self.moving_thread = WorkThread(self.motor.move_absolute, self.current_position, True)
def __init__(self, anc350_instrument, also_close_output=False): """Attocube """ super().__init__() self.logger = logging.getLogger(__name__) self.title = 'Attocube GUI' self.anc350_instrument = anc350_instrument name = 'attocube.ui' gui_folder = os.path.dirname(os.path.abspath(__file__)) gui_file = os.path.join(gui_folder, name) self.logger.debug('Loading the GUI file: {}'.format(gui_file)) self.gui = uic.loadUi(gui_file, self) self.max_amplitude_V = 60 self.max_frequency = 2000 self.max_dclevel_V = 60 * ur( 'V') #Pay attention: this max only goes for 4K, self.max_dcLevel_mV_300K = 60 * ur( 'V') #at room temperature use 60V as max self.max_distance = 5 * ur('mm') self.current_positions = {} self.current_axis = 'X,Y Piezo Stepper' self.current_move = 'step' self.direction = 'left' self.distance = 0 * ur('um') self.settings = { 'amplitudeX': 30, 'amplitudeY': 40, 'amplitudeZ': 30, 'frequencyX': 1000, 'frequencyY': 1000, 'frequencyZ': 1000 } self.temperature = 300 self.scanner_unitX = 'V' self.scanner_unitY = 'V' self.scanner_unitZ = 'V' self.dcX = 1 * ur(self.scanner_unitX) self.dcY = 1 * ur(self.scanner_unitY) self.dcZ = 0 * ur(self.scanner_unitZ) self.stop = self.stop_moving self.initUI() #This one is to continuously (= every 100ms) show the position of the axes self.timer = QTimer() self.timer.timeout.connect(self.show_position) self.timer.start(100) #time in ms self.moving_thread = WorkThread(self.anc350_instrument.move_to, self.current_axis, self.distance)
def moveby_both(self): self.moving_one_thread = WorkThread(self.wp_one.move_relative, self.wp_both_target_relative, True) self.moving_one_thread.start() self.moving_two_thread = WorkThread(self.wp_two.move_relative, self.wp_both_target_relative, True) self.moving_two_thread.start()
class TestWinspec(QWidget): """ Gui for beam blocker flags. Requires hyperion.instrument.misc.beam_flags_instr/BeamFlagsInstr instrument as input. :param beam_flags_instr: instrument object to control :type beam_flags_instr: an instance of the class """ def __init__(self, winspec_instr): """ Gui for beam blocker flags. :param beam_flags_instr: The beam flags instrument object to create the gui for :type beam_flags_instr: BeamFlagsInstr object """ super().__init__() self.logger = logging.getLogger(__name__) self.title = 'Test Winspec' self.left = 400 self.top = 400 self.width = 240 self.height = 80 self.ws = winspec_instr self.initUI() def initUI(self): """ Create all the gui elements and connect signals to methods. Adds 'state' and 'label' key and value to each flag in the settings dict. Reads the current state from the device and sets 'state' key and gui accordingly. """ self.logger.info('Creating gui elements') self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) layout = QGridLayout() button = QPushButton('test', self) button.pressed.connect(self.button_clicked) layout.addWidget(button, 0 , 0) self.setLayout(layout) self.show() def button_clicked(self): print('test button is clicked') ws.controller.spt_set('NEW_POSITION',300) self.worker_thread = WorkThread(ws.controller.spt.Move) self.worker_thread.start()
def use_keyboard(self): """Set text of keyboard_label to using keyboard. Collect events until released. """ self.keyboard_label.setText("using keyboard/npress q to exit") self.worker_thread = WorkThread(self.create_keyboard_listener) self.worker_thread.start() #set the text back to you can use the keyboard. self.keyboard_label.setText("use keyboard\nto control selected\n combobox thorlabs_motor:")
def go_to_input(self): """Starts a thread to make an absolute move with the distance that is read out in self.set_distance from the spinbox. Value error has become a little bit irrelevant, now that I changed to pint quantities for distance. """ try: self.moving_thread = WorkThread(self.motor.move_absolute, self.distance, True) self.moving_thread.start() #self.set_current_motor_position_label() except ValueError: self.logger.warning("The input is not a float, change this") return
def recover_position(self): """Sets position of motors to the saved position with a thread. When done, changes the save button to default. """ self.logger.debug("current position: {}".format(self.current_position)) if self.saved_position == None: self.logger.warning("the positions have not been set!") return else: self.moving_thread = WorkThread(self.motor.move_absolute, self.saved_position, True) self.moving_thread.start() self.save_pos_button.setStyleSheet("default")
def __init__(self, beam_flags_instr, also_close_output=False): """ Gui for beam blocker flags. :param beam_flags_instr: The beam flags instrument object to create the gui for :type beam_flags_instr: BeamFlagsInstr object """ super().__init__() self.logger = logging.getLogger(__name__) self.left = 400 self.top = 400 self.width = 240 self.height = 80 self.bfi = beam_flags_instr self.bf_settings = self.bfi.settings['gui_flags'] self.all_labels = {} if 'name' in self.bfi.settings: self.title = self.bfi.settings['name'] else: self.title = 'Beam Flags' self.red_char = self.bfi.settings['flag_states']['red'] self.green_char = self.bfi.settings['flag_states']['green'] self.red_color = self.bfi.settings['gui_red_color'] self.green_color = self.bfi.settings['gui_green_color'] self.initUI() # Start timer to repeatedly pull the current state of the toggle switches: self.logger.info('Starting timer thread') # self._busy = False if 'gui_state_update_ms' in self.bfi.settings: self.indicator_update_time = self.bfi.settings[ 'gui_state_update_ms'] else: self.indicator_update_time = 100 # ms #Pay attention: using the Qtimer makes the gui very slow #Instead, use a workthread self.timer_mode = False if self.timer_mode: self.timer = QTimer() self.timer.timeout.connect(self.update_label_states) self.timer.start(self.indicator_update_time) else: self._thread = WorkThread(self.update_wrapper) self._thread.start(priority=QThread.LowestPriority)
def control_motor_with_keyboard(self): """ In this method with the Listener object you can press a button on the keyboard and with that input a thorlabs_motor will move. """ #set text of keyboard_label to using keyboard self.keyboard_label.setText("using keyboard/npress esc to exit") # Collect events until released self.worker_thread = WorkThread(self.create_keyboard_listener) self.worker_thread.start() #set the text back to you can use the keyboard. self.keyboard_label.setText("use keyboard\nto control selected\n combobox thorlabs_motor:")
def __init__(self, hydra_instrument, draw=None, also_close_output=False): super().__init__() self.logger = logging.getLogger(__name__) self.title = 'Hydraharp400 correlator gui' self.left = 50 self.top = 50 self.width = 320 self.height = 200 self.histogram_number = 0 self.grid_layout = QGridLayout() self.setLayout(self.grid_layout) self.hydra_instrument = hydra_instrument if type(draw) is dict: self.draw = list(draw.values())[0] else: self.draw = draw #default values, could be put in a yml file as well self.array_length = 65536 self.resolution = "1ps" self.integration_time = 5 * ur('s') self.channel = '0' self.time_passed = 0 * ur( 's') #which also makes sure that the units are the same self.max_time = 24 * ur('hour') self.max_length = 65536 self.endtime = [] self.time_axis = [] self.units = 's' self.hydra_instrument.configurate() self.initUI() #This one is to continuously (= every 100ms) show the remaining time self.timer = QTimer() self.timer.timeout.connect(self.show_time_passed) # timer to update self.timer_plot = QTimer() self.timer_plot.timeout.connect(self.update_plot) self.histogram_thread = WorkThread( self.hydra_instrument.make_histogram, self.integration_time, self.channel) self.stop = self.stop_histogram
def use_keyboard_move(self): """Set text of keyboard_label to using keyboard. Collect events until released. """ if self.keyboard_use_move == False: self.worker_move_thread = WorkThread( self.create_keyboard_listener_move) self.worker_move_thread.start() self.keyboard_button_move.setStyleSheet("background-color: green") self.keyboard_use_move = True else: self.keyboard_button_move.setStyleSheet("") self.keyboard_use_move = False if self.worker_move_thread.isRunning(): self.listener_move.stop()
def take_histogram(self): """| In this method there will be made a histogram using the input of the user. | All user inputs were stored previously in the values as declared in the init | (histogram length, resolution, integration time and channel). | A thread is started to be able to also stop the histogram taking. | The data gets plot in the DrawHistogram plot(self.draw.histogram_plot.plot()). | The time axis is calculated in calculate_axis and used for the plot. """ self.logger.info("Taking the histrogram") #first, set the array length and resolution of the histogram self.logger.debug('chosen histogram length: ' + str(self.array_length)) self.logger.debug('chosen resolution: ' + str(self.resolution)) self.hydra_instrument.set_histogram(self.array_length, ur(self.resolution)) #Then, start the histogram + thread self.logger.debug('chosen integration time: ' + str(self.integration_time)) self.logger.debug('chosen channel: ' + str(self.channel)) self.timer.start(100) self.timer_plot.start(100) self.show_time_passed() self.histogram_thread = WorkThread( self.hydra_instrument.make_histogram, self.integration_time, self.channel) self.histogram_thread.start() #make it possible to press the save_histogram_button.(should be True) self.save_histogram_button.setEnabled(True) self.take_histogram_button.setEnabled(True) self.hydra_instrument.time_passed = 0 * ur('s') self.show_time_passed()
def initialize(self): self._is_initialized = False if self._force_threading is True or (self._force_threading is None and 'PyQt5' in sys.modules): # if _force_threading is None and PyQt5 is detected # if _force_threading is True self.logger.info( 'Winspec in threading mode ' + ['(automatic)', '(forced)'][self._force_threading is True]) from hyperion.view.general_worker import WorkThread from time import sleep init_thread = WorkThread(self.__initialize_in_thread) self._busy_with_thread = True init_thread.start() timeout_count = 50 # wait 5 seconds before spitting out warnings while self._busy_with_thread: sleep(.2) if timeout_count <= 0: self.logger.warning('Waiting for Winspec') init_thread.quit() self._threading_mode = True else: # if _force_threading is False # if _force_threading is None and PyQt5 is not detected self.logger.info( 'Winspec NOT in threading mode ' + ['(forced)', '(automatic)'][self._force_threading is None]) self._threading_mode = False self.__initialize_without_threading_support() self._generate_params() self._is_initialized = True # THIS IS MANDATORY!! # this is to prevent you to close the device connection if you # have not initialized it inside a with statement # self.xdim = self.exp_get('XDIM')[0] # self.ydim = self.exp_get('YDIM')[0] self.xdim = self.exp_get('XDIMDET')[0] self.ydim = self.exp_get('YDIMDET')[0]
class Hydraharp_aligning_GUI(BaseGui): """ :param :type """ def __init__(self, hydra_instrument): """Hydraharp aligning """ super().__init__() self.logger = logging.getLogger(__name__) self.title = 'Hydraharp Aligning GUI' self.left = 50 self.top = 50 self.width = 500 self.height = 250 self.hydra_instrument = hydra_instrument self.exp_type = 'Finite' self.pausetime = 50*ur('ms') self.lengthaxis = 10*ur('s') self.sync = True self.chan1 = False self.chan2 = False self.sync_counts = 0.0 self.counts1 = 0.0 self.counts2 = 0.0 self.running = False self.Sync_counts_array = [] self.Counts1_array = [] self.Counts2_array = [] self.time_axis = [] self.default_name = 'counts.txt' self.path = 'D:\\LabSoftware\\Data\\' self.something_selected = False self.pen = pg.mkPen(color=(0, 0, 0)) # makes the plotted lines black name = 'aligning.ui' gui_folder = os.path.dirname(os.path.abspath(__file__)) gui_file = os.path.join(gui_folder, name) self.logger.info('Loading the GUI file: {}'.format(gui_file)) self.gui = uic.loadUi(gui_file, self) self.initUI() self.timer = QTimer() self.timer.timeout.connect(self.ask_counts) self.timer.start(100) #time in ms def initUI(self): """Connect all buttons, comboBoxes and doubleSpinBoxes to methods """ self.logger.debug('Setting up the Measurement GUI') self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) self.show() self.gui.pushButton_start.clicked.connect(self.start_plotting) self.gui.pushButton_stop.clicked.connect(self.stop_plotting) self.pushButton_stop.setStyleSheet("background-color: red") self.gui.pushButton_save.clicked.connect(self.save_counts) self.gui.pushButton_save.setEnabled(False) self.gui.comboBox_finite.setCurrentText(self.exp_type) self.gui.comboBox_finite.currentTextChanged.connect(self.get_exp_type) self.gui.doubleSpinBox_pause.setValue(self.pausetime.m_as('ms')) self.gui.doubleSpinBox_pause.valueChanged.connect(self.set_pausetime) self.gui.doubleSpinBox_timeaxis.setValue(self.lengthaxis.m_as('s')) self.gui.doubleSpinBox_timeaxis.valueChanged.connect(self.set_lengthaxis) self.gui.checkBox_sync.setChecked(self.sync) self.gui.checkBox_sync.stateChanged.connect(self.set_channel) self.gui.checkBox_chan1.setChecked(self.chan1) self.gui.checkBox_chan1.stateChanged.connect(self.set_channel) self.gui.checkBox_chan2.setChecked(self.chan2) self.gui.checkBox_chan2.stateChanged.connect(self.set_channel) self.set_name_path() self.gui.lineEdit_name.textChanged.connect(self.get_name_path) self.gui.lineEdit_path.textChanged.connect(self.get_name_path) #Read user inputs # ----------------------------------------------------------------------------------------- def get_exp_type(self): self.logger.debug('Should read the experiment type here') self.exp_type = self.gui.comboBox_finite.currentText() self.logger.debug('Chosen type: {}'.format(self.exp_type)) def set_pausetime(self): self.logger.debug('Should set the pause time here') self.pausetime = self.gui.doubleSpinBox_pause.value()*ur('ms') self.logger.debug('Chosen time: {}'.format(self.pausetime)) def set_lengthaxis(self): self.logger.debug('Should set the length of the axis here') self.lengthaxis = self.gui.doubleSpinBox_timeaxis.value()*ur('s') self.logger.debug('Chosen length of axis: {}'.format(self.lengthaxis)) def set_channel(self): self.sync = self.checkBox_sync.isChecked() self.chan1 = self.gui.checkBox_chan1.isChecked() self.chan2 = self.gui.checkBox_chan2.isChecked() self.logger.debug('Sync channel: {}, channel 1: {}, channel 2: {}'.format(self.sync, self.chan1, self.chan2)) def set_name_path(self): self.gui.lineEdit_name.setText(self.default_name) self.gui.lineEdit_path.setText(self.path) def get_name_path(self): self.default_name = self.gui.lineEdit_name.text() self.path = self.gui.lineEdit_path.text() print(self.gui.lineEdit_name.text()) print(self.gui.lineEdit_path.text()) #Actual methods doing something #----------------------------------------------------------------------------------------- def ask_counts(self): """ | Connects to the device and read out the count rate of either the sync or on of the count channels. | Displays this on the labels on the gui, which are updated via the timer in the init. """ #self.logger.debug('Asking for counts') self.something_selected = False if self.sync: self.sync_counts = self.hydra_instrument.sync_rate() #self.logger.debug("{}".format(self.sync_counts)) self.gui.label_counts_sync.setText(str(self.sync_counts)) self.something_selected = True else: self.gui.label_counts_sync.setText('currently unavailable') if self.chan1: self.counts1 = self.hydra_instrument.count_rate(0) #self.logger.debug("{}".format(self.counts1)) self.something_selected = True self.gui.label_counts1.setText(str(self.counts1)) else: self.gui.label_counts1.setText('currently unavailable') if self.chan2: self.counts2 = self.hydra_instrument.count_rate(1) #self.logger.debug("{}".format(self.counts2)) self.gui.label_counts2.setText(str(self.counts2)) self.something_selected = True else: self.gui.label_counts2.setText('currently unavailable') if self.something_selected == False: self.logger.warning('Nothing is selected') def start_plotting(self): """| Prepares for plotting by making a time axis based on the axis length and pause time, and starts an empty counts array. | Then opens 1 to 3 plot windows and a thread to plot them, depending on the selected channels. """ self.logger.info('Should start counting here') self.logger.debug('Settings: {}, {}, {}'.format(self.exp_type, self.pausetime, self.lengthaxis)) self.time_axis = np.linspace(0, self.lengthaxis.m_as('s') * self.pausetime.m_as('s'), int(self.lengthaxis.m_as('s'))) * ur('s') self.logger.debug("{}".format(self.time_axis)) if self.something_selected: if self.sync: self.draw0 = DrawCounts() self.draw0.counts_plot.setTitle("<span style=\"color:yellow;font-size:30px\">Counts on sync channel </span>") #self.draw0.counts_plot.plot(self.time_axis.m_as('s'), Counts, clear=True, pen=self.pen) if self.chan1: self.draw1 = DrawCounts() self.draw1.counts_plot.setTitle("<span style=\"color:orange;font-size:30px\">Counts on channel 1 </span>") #self.draw1.counts_plot.plot(self.time_axis.m_as('s'), Counts, clear=True, pen=self.pen) if self.chan2: self.draw2 = DrawCounts() self.draw2.counts_plot.setTitle("<span style=\"color:red;font-size:30px\">Counts on channel 2 </span>") #self.draw2.counts_plot.plot(self.time_axis.m_as('s'), Counts, clear=True, pen=self.pen) self.plotting_thread = WorkThread(self.update_plot) self.plotting_thread.start() else: self.logger.warning('You have to select something first, before making a graph.') def update_plot(self): """| This method is called and threaded from start_plotting, depending on the selected channels it plots in 1 to 3 graphs. | It either works for a Finite amount of time or works infinitely, to make aligning possible. | It draws the plot with the time axis, which is the same for all channels, | and the Counts, that are a numpy array that is constantly filled with the current Rate. | Depending on the channel, the counts are plotted in three different self.draw windows. | After the plotting is finished, the prepare_save method is started, so the name to be given to a potential file is set. :param Counts: array of the size of the axis length :type Counts: numpy array """ self.running = True self.Sync_counts_array = np.zeros(int(self.lengthaxis.m_as('s'))) self.Counts1_array = np.zeros(int(self.lengthaxis.m_as('s'))) self.Counts2_array = np.zeros(int(self.lengthaxis.m_as('s'))) if self.exp_type == 'Finite': for ii in range(0, int(self.lengthaxis.m_as('s'))): if self.running == False: break else: if self.sync: curr_sync_Rate = self.sync_counts self.Sync_counts_array[ii] = curr_sync_Rate.m_as('cps') self.logger.debug('Counts: {}'.format(self.Sync_counts_array)) self.draw0.counts_plot.plot(self.time_axis, self.Sync_counts_array, clear = True,pen=self.pen) if self.chan1: currRate1 = self.counts1 self.Counts1_array[ii] = currRate1.m_as('cps') self.logger.debug('Counts: {}'.format(self.Counts1_array)) self.draw1.counts_plot.plot(self.time_axis, self.Counts1_array, clear = True, pen=self.pen) if self.chan2: currRate2 = self.counts2 self.Counts2_array[ii] = currRate2.m_as('cps') self.logger.debug('Counts: {}'.format(self.Counts2_array)) self.draw2.counts_plot.plot(self.time_axis, self.Counts2_array, clear = True, pen=self.pen) time.sleep(self.pausetime.m_as('s')) elif self.exp_type == 'Infinite': ii=0 while self.running: if self.sync: curr_sync_Rate = self.sync_counts if ii < int(self.lengthaxis.m_as('s')): self.Sync_counts_array[ii] = curr_sync_Rate.m_as('cps') else: self.Sync_counts_array = np.roll(self.Sync_counts_array, -1) self.Sync_counts_array[-1] = curr_sync_Rate.m_as('cps') self.logger.debug('{}'.format(self.Sync_counts_array)) self.draw0.counts_plot.plot(self.time_axis, self.Sync_counts_array, clear=True, pen=self.pen) if self.chan1: currRate1 = self.counts1 if ii < int(self.lengthaxis.m_as('s')): self.Counts1_array[ii] = currRate1.m_as('cps') else: self.Counts1_array = np.roll(self.Counts1_array, -1) self.Counts1_array[-1] = currRate1.m_as('cps') self.logger.debug('{}'.format(self.Counts1_array)) self.draw1.counts_plot.plot(self.time_axis, self.Counts1_array, clear=True, pen=self.pen) if self.chan2: currRate2 = self.counts2 if ii < int(self.lengthaxis.m_as('s')): self.Counts2_array[ii] = currRate2.m_as('cps') else: self.Counts2_array = np.roll(self.Counts2_array, -1) self.Counts2_array[-1] = currRate2.m_as('cps') self.logger.debug('{}'.format(self.Counts2_array)) self.draw2.counts_plot.plot(self.time_axis, self.Counts2_array, clear=True, pen=self.pen) ii+=1 time.sleep(self.pausetime.m_as('s')) self.running = False self.prepare_save() def stop_plotting(self): """| Stops the thread and the plotting, if there was actually something running. | Does not stop the showing of the counts, that happens no matter what. """ if self.running: self.logger.info('Should stop counting here') self.running = False self.plotting_thread.quit() else: self.logger.warning('There is nothing to stop.') def prepare_save(self): """| Enables the save button and prepares for saving by constructing a filename based on the data taken, | and filling that name in the input. | The data always have 4 columns, some of which might just contain zeros. """ self.gui.pushButton_save.setEnabled(True) rowlength = len(self.time_axis) self.data = np.zeros([rowlength, 4]) # first column will be time, the others counts on different channels self.data[:, 0] = self.time_axis.m_as('s') channels = '' if self.sync: self.data[:, 1] = self.Sync_counts_array channels += 'sync_' if self.chan1: self.data[:, 2] = self.Counts1_array channels += 'counts1_' if self.chan2: self.data[:, 3] = self.Counts2_array channels += 'counts2_' total_time = int(self.pausetime.m_as('ms') * self.lengthaxis.m_as('s')) now = datetime.now() datum = str(now.year) + '_' + str(now.month) + '_' + str(now.day) + '_' filename = '{}counts_{}time{}ms'.format(datum, channels, total_time) self.logger.debug('filename: {}'.format(filename)) self.default_name = filename self.set_name_path() self.logger.debug(self.path + filename + '.txt') def save_counts(self): self.logger.info('Saving the last plot') np.savetxt(self.path + self.default_name + '.txt',self.data) self.gui.pushButton_save.setEnabled(False)
def goto_two(self): self.moving_two_thread = WorkThread( self.wp_two.move_absolute, self.wp_two_target + self.offset_one, True) self.moving_two_thread.start()
class Thorlabs_motor_GUI(BaseGui): """ | The initialization of the thorlabs xyz gui. | Settings of the meta instrument are used. Here | Serial number and name are in the settings given underneath, so thorlabs_instrument knows them. | Initialize of the instrument is already done by the init of the thorlabs_instrument, that runs with the with downstairs. """ def __init__(self, thorlabs_meta_instrument): super().__init__() self.logger = logging.getLogger(__name__) self.left = 50 self.top = 50 self.width = 400 self.height = 200 self.grid_layout = QGridLayout() self.setLayout(self.grid_layout) self.wp_one = thorlabs_meta_instrument.wp_one self.logger.debug('You are connected to a {}'.format( self.wp_one.kind_of_device)) self.wp_two = thorlabs_meta_instrument.wp_two self.logger.debug('You are connected to a {}'.format( self.wp_one.kind_of_device)) self.title = 'Thorlabs {} GUI two waveplates' self.current_position_one = None self.current_position_two = None self.wp_one_target = 0 self.wp_two_target = 0 self.offset_one = 0 self.offset_two = 0 self.keyboard_use_move = False self.timer = QTimer() self.timer.timeout.connect(self.set_current_motor_position_label) self.timer.start(100) self.initUI() def initUI(self): self.logger.debug('Setting up the two waveplates Motor GUI') self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) self.make_buttons() self.make_labels() self.make_misc_gui_stuff() self.show() def make_buttons(self): self.make_goto_one_button() self.make_goto_two_button() self.make_moveby_both_button() self.make_reset_one_button() self.make_reset_two_button() self.make_keyboard_move_button() def make_labels(self): self.make_current_pos_one_label() self.make_current_pos_two_label() self.make_both_text_label() self.make_keyboard_text_label() def make_misc_gui_stuff(self): self.make_wp_one_spinbox() self.make_wp_two_spinbox() self.make_wp_both_spinbox() ############################Make buttons, boxes and labels######################################### def make_goto_one_button(self): self.goto_one_button = QPushButton("Go to", self) self.goto_one_button.setToolTip('Go to') self.goto_one_button.clicked.connect(self.goto_one) self.grid_layout.addWidget(self.goto_one_button, 0, 2) def make_goto_two_button(self): self.goto_two_button = QPushButton("Go to", self) self.goto_two_button.setToolTip('Go to') self.goto_two_button.clicked.connect(self.goto_two) self.grid_layout.addWidget(self.goto_two_button, 1, 2) def make_reset_one_button(self): self.reset_one_button = QPushButton("Reset", self) self.reset_one_button.setToolTip('Reset') self.reset_one_button.clicked.connect(self.reset_one) self.grid_layout.addWidget(self.reset_one_button, 0, 3) def make_reset_two_button(self): self.reset_two_button = QPushButton("Reset", self) self.reset_two_button.setToolTip('Reset') self.reset_two_button.clicked.connect(self.reset_two) self.grid_layout.addWidget(self.reset_two_button, 1, 3) def make_moveby_both_button(self): self.moveby_both_button = QPushButton("Move by", self) self.moveby_both_button.setToolTip('Reset') self.moveby_both_button.clicked.connect(self.moveby_both) self.grid_layout.addWidget(self.moveby_both_button, 2, 2) def make_keyboard_move_button(self): self.keyboard_button_move = QPushButton("keyboard\nmove", self) self.keyboard_button_move.setToolTip( "use the keyboard to move the thorlabs_motor,\nit works great") self.keyboard_button_move.clicked.connect(self.use_keyboard_move) self.grid_layout.addWidget(self.keyboard_button_move, 3, 3) def make_current_pos_one_label(self): self.current_pos_one_label = QLabel(self) try: self.current_pos_one_label.setText(self.wp_one.position()) except Exception: self.current_pos_one_label.setText("currently/unavailable") self.grid_layout.addWidget(self.current_pos_one_label, 0, 0) def make_current_pos_two_label(self): self.current_pos_two_label = QLabel(self) try: self.current_pos_two_label.setText(self.wp_two.position()) except Exception: self.current_pos_two_label.setText("currently/unavailable") self.grid_layout.addWidget(self.current_pos_two_label, 1, 0) def make_both_text_label(self): self.both_text_label = QLabel(self) self.both_text_label.setText("Both waveplates") self.grid_layout.addWidget(self.both_text_label, 2, 0) def make_keyboard_text_label(self): self.keyboard_text_label = QLabel(self) self.keyboard_text_label.setText( "y/u to move wp one - h/j to move wp two - n/m to move both\nt to quit)" ) self.grid_layout.addWidget(self.keyboard_text_label, 3, 0, 1, 2) def make_wp_one_spinbox(self): self.wp_one_spinbox = QDoubleSpinBox(self) self.grid_layout.addWidget(self.wp_one_spinbox, 0, 1) self.wp_one_spinbox.setValue(0) self.wp_one_spinbox.setMinimum( -999999999) #otherwise you cannot reach higher than 99 self.wp_one_spinbox.setMaximum(999999999) self.wp_one_spinbox.valueChanged.connect(self.set_wp_one_target) def make_wp_two_spinbox(self): self.wp_two_spinbox = QDoubleSpinBox(self) self.grid_layout.addWidget(self.wp_two_spinbox, 1, 1) self.wp_two_spinbox.setValue(0) self.wp_two_spinbox.setMinimum( -999999999) #otherwise you cannot reach higher than 99 self.wp_two_spinbox.setMaximum(999999999) self.wp_two_spinbox.valueChanged.connect(self.set_wp_two_target) def make_wp_both_spinbox(self): self.wp_both_spinbox = QDoubleSpinBox(self) self.grid_layout.addWidget(self.wp_both_spinbox, 2, 1) self.wp_both_spinbox.setValue(0) self.wp_both_spinbox.setMinimum( -999999999) #otherwise you cannot reach higher than 99 self.wp_both_spinbox.setMaximum(999999999) self.wp_both_spinbox.valueChanged.connect(self.set_wp_both_degrees) def set_current_motor_position_label(self): """ In the instrument level, the current position is remembered and updated through self.position, which is called in the moving_loop during the moves. This method read this out (continuously, through the timer in the init) and displays the value. """ self.current_position_one = self.wp_one.position() self.current_pos_one_label.setText( "waveplate one:" + str(round(self.current_position_one - self.offset_one, 2))) self.current_position_two = self.wp_two.position() self.current_pos_two_label.setText( "waveplate two:" + str(round(self.current_position_two - self.offset_two, 2))) def set_wp_both_degrees(self): value = self.wp_both_spinbox.value() unit = "degrees" toggle_distance = ur(str(value) + unit) self.logger.debug('toggle waveplate 1 distance value {}'.format(value)) self.wp_both_target_relative = toggle_distance def set_wp_one_target(self): value = self.wp_one_spinbox.value() unit = "degrees" toggle_distance = ur(str(value) + unit) self.logger.debug('toggle waveplate 1 distance value {}'.format(value)) self.wp_one_target = toggle_distance def reset_one(self): self.offset_one = self.wp_one.position() def reset_two(self): self.offset_two = self.wp_two.position() def set_wp_two_target(self): value = self.wp_two_spinbox.value() unit = "degrees" toggle_distance = ur(str(value) + unit) self.logger.debug('toggle waveplate 2 distance value {}'.format(value)) self.wp_two_target = toggle_distance def goto_one(self): self.moving_one_thread = WorkThread( self.wp_one.move_absolute, self.wp_one_target + self.offset_one, True) self.moving_one_thread.start() def goto_two(self): self.moving_two_thread = WorkThread( self.wp_two.move_absolute, self.wp_two_target + self.offset_one, True) self.moving_two_thread.start() def moveby_both(self): self.moving_one_thread = WorkThread(self.wp_one.move_relative, self.wp_both_target_relative, True) self.moving_one_thread.start() self.moving_two_thread = WorkThread(self.wp_two.move_relative, self.wp_both_target_relative, True) self.moving_two_thread.start() def use_keyboard_move(self): """Set text of keyboard_label to using keyboard. Collect events until released. """ if self.keyboard_use_move == False: self.worker_move_thread = WorkThread( self.create_keyboard_listener_move) self.worker_move_thread.start() self.keyboard_button_move.setStyleSheet("background-color: green") self.keyboard_use_move = True else: self.keyboard_button_move.setStyleSheet("") self.keyboard_use_move = False if self.worker_move_thread.isRunning(): self.listener_move.stop() def create_keyboard_listener_move(self): with Listener(on_press=self.on_press_move, on_release=self.on_release_move) as self.listener_move: self.listener_move.join() def on_press_move(self, key): """ In this method if the w is pressed the thorlabs_motor selected in the combobox will move forward or if s is pressed the thorlabs_motor will move backward. The w and s are written as: "'w'"/"'s'" because of syntacs. """ if str(key) == "'y'": self.moving_one_thread = WorkThread(self.wp_one.move_velocity, 2, True) self.moving_one_thread.start() elif str(key) == "'u'": self.moving_one_thread = WorkThread(self.wp_one.move_velocity, 1, True) self.moving_one_thread.start() elif str(key) == "'h'": self.moving_two_thread = WorkThread(self.wp_two.move_velocity, 2, True) self.moving_two_thread.start() elif str(key) == "'j'": self.moving_two_thread = WorkThread(self.wp_two.move_velocity, 1, True) self.moving_two_thread.start() elif str(key) == "'n'": self.moving_two_thread = WorkThread(self.wp_two.move_velocity, 2, True) self.moving_one_thread = WorkThread(self.wp_one.move_velocity, 2, True) self.moving_one_thread.start() self.moving_two_thread.start() elif str(key) == "'m'": self.moving_two_thread = WorkThread(self.wp_two.move_velocity, 1, True) self.moving_one_thread = WorkThread(self.wp_one.move_velocity, 1, True) self.moving_one_thread.start() self.moving_two_thread.start() def on_release_move(self, key): """ In this method if the w or s is released the thorlabs_motor will stop moving. If q is released the keyboard mode stops. """ if str(key) == "'y'" or str(key) == "'u'": #stop the thorlabs_motor from going self.moving_one_thread.quit() self.wp_one.stop_moving() elif str(key) == "'h'" or str(key) == "'j'": #stop the thorlabs_motor from going self.moving_two_thread.quit() self.wp_two.stop_moving() elif str(key) == "'n'" or str(key) == "'m'": # stop the thorlabs_motor from going self.moving_one_thread.quit() self.wp_one.stop_moving() self.moving_two_thread.quit() self.wp_two.stop_moving()
class Hydraharp_GUI(BaseGui): """ GUI class for the Hydraharp correlator instrument :param hydra_instrument: instrument to control with the GUI :type hydra_instrument: instance of the class for the instrument to control :param draw: a window where the plotting o the data acquired will be shown. :type draw: a plot widget class """ def __init__(self, hydra_instrument, draw=None, also_close_output=False): super().__init__() self.logger = logging.getLogger(__name__) self.title = 'Hydraharp400 correlator gui' self.left = 50 self.top = 50 self.width = 320 self.height = 200 self.histogram_number = 0 self.grid_layout = QGridLayout() self.setLayout(self.grid_layout) self.hydra_instrument = hydra_instrument if type(draw) is dict: self.draw = list(draw.values())[0] else: self.draw = draw #default values, could be put in a yml file as well self.array_length = 65536 self.resolution = "1ps" self.integration_time = 5 * ur('s') self.channel = '0' self.time_passed = 0 * ur( 's') #which also makes sure that the units are the same self.max_time = 24 * ur('hour') self.max_length = 65536 self.endtime = [] self.time_axis = [] self.units = 's' self.hydra_instrument.configurate() self.initUI() #This one is to continuously (= every 100ms) show the remaining time self.timer = QTimer() self.timer.timeout.connect(self.show_time_passed) # timer to update self.timer_plot = QTimer() self.timer_plot.timeout.connect(self.update_plot) self.histogram_thread = WorkThread( self.hydra_instrument.make_histogram, self.integration_time, self.channel) self.stop = self.stop_histogram def initUI(self): self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) self.show() self.make_groupBoxes() self.make_basics() self.make_user_input() self.make_saving_input() def make_groupBoxes(self): self.groupBox_basic = QGroupBox() self.grid_layout.addWidget(self.groupBox_basic, 0, 1) self.groupBox_basic_layout = QVBoxLayout() self.groupBox_basic.setLayout(self.groupBox_basic_layout) self.groupBox_basic.setStyleSheet( "QGroupBox {border: 1px solid orange; border-radius: 9px;}") self.groupBox_values = QGroupBox() self.grid_layout.addWidget(self.groupBox_values, 0, 0) self.groupBox_values_layout = QGridLayout() self.groupBox_values.setLayout(self.groupBox_values_layout) self.groupBox_values.setStyleSheet( "QGroupBox {border: 1px solid orange; border-radius: 9px;}") self.groupBox_saving = QGroupBox() self.grid_layout.addWidget(self.groupBox_saving, 1, 0, 1, 2) self.groupBox_saving_layout = QGridLayout() self.groupBox_saving.setLayout(self.groupBox_saving_layout) self.groupBox_saving.setStyleSheet( "QGroupBox {border: 1px solid orange; border-radius: 9px;}") def make_basics(self): self.take_histogram_button = QPushButton('take histogram', self) self.take_histogram_button.setToolTip('take the histogram') self.take_histogram_button.clicked.connect(self.take_histogram) self.stop_histogram_button = QPushButton('stop histogram', self) self.stop_histogram_button.setToolTip(('stop your histogram')) self.stop_histogram_button.clicked.connect(self.stop_histogram) self.stop_histogram_button.setStyleSheet("background-color: red") self.showing_remaining_time = QLabel(self) self.showing_remaining_time.setText(str(self.time_passed)) self.progressbar = QProgressBar(self) self.progressbar.setMaximum(int(self.integration_time.magnitude)) self.progressbar.setValue(int(self.time_passed.magnitude)) self.progressbar.setTextVisible(False) self.progressbar.valueChanged.connect(lambda: self.show_time_passed) self.groupBox_basic_layout.addWidget(self.take_histogram_button) self.groupBox_basic_layout.addWidget(self.stop_histogram_button) self.groupBox_basic_layout.addWidget(self.showing_remaining_time) self.groupBox_basic_layout.addWidget(self.progressbar) def make_user_input(self): self.integration_time_label = QLabel("Integration time: ") self.resolution_label = QLabel("Resolution: ") self.channel_label = QLabel("Channel: ") self.array_length_label = QLabel("Array length: ") self.array_length_label.setEnabled(False) self.time_axis_label = QLabel("Time on axis: ") self.integration_time_spinbox = QSpinBox(self) self.integration_time_spinbox.setValue(self.integration_time.m_as('s')) self.integration_time_spinbox.valueChanged.connect( self.set_integration_time) self.time_unit_combobox = QComboBox(self) self.time_unit_combobox.addItems(["s", "min", "hour"]) self.time_unit_combobox.setCurrentText('s') self.time_unit_combobox.currentTextChanged.connect( self.set_integration_time) self.array_length_spinbox = QSpinBox(self) self.array_length_spinbox.setMaximum(999999999) self.array_length_spinbox.setValue(self.array_length) self.array_length_spinbox.setEnabled(False) self.array_length_spinbox.valueChanged.connect(self.set_array_length) self.resolution_combobox = QComboBox(self) #self.resolution_spinbox.setMaximum(999999999) #self.resolution_spinbox.setValue(self.resolution.m_as('ps')) #self.resolution_spinbox.setSuffix('ps') self.resolution_combobox.addItems([ "1ps", "2ps", "4ps", "8ps", "16ps", "32ps", "64ps", "128ps", "256ps", "512ps", "1024ps" ]) self.resolution_combobox.setCurrentText(self.resolution) self.resolution_combobox.currentTextChanged.connect( self.set_resolution) self.endtime_label = QLabel(self) self.calculate_axis() self.channel_combobox = QComboBox(self) self.channel_combobox.addItems(["0", "1"]) self.channel_combobox.setCurrentText(self.channel) self.channel_combobox.currentTextChanged.connect(self.set_channel) self.groupBox_values_layout.addWidget(self.integration_time_label, 0, 0) self.groupBox_values_layout.addWidget(self.resolution_label, 1, 0) self.groupBox_values_layout.addWidget(self.channel_label, 2, 0) self.groupBox_values_layout.addWidget(self.array_length_label, 3, 0) self.groupBox_values_layout.addWidget(self.time_axis_label, 4, 0) self.groupBox_values_layout.addWidget(self.integration_time_spinbox, 0, 1) self.groupBox_values_layout.addWidget(self.time_unit_combobox, 0, 2) self.groupBox_values_layout.addWidget(self.resolution_combobox, 1, 1) self.groupBox_values_layout.addWidget(self.channel_combobox, 2, 1) self.groupBox_values_layout.addWidget(self.array_length_spinbox, 3, 1) self.groupBox_values_layout.addWidget(self.endtime_label, 4, 1) def make_saving_input(self): self.export_label = QLabel("Export file: ") self.save_histogram_button = QPushButton('save histrogram', self) self.save_histogram_button.setToolTip('save your histogram in a file') self.save_histogram_button.setEnabled(False) self.save_histogram_button.clicked.connect(self.save_histogram) self.export_textfield = QLineEdit(self) self.export_textfield.setText(root_dir) self.groupBox_saving_layout.addWidget(self.export_label, 0, 0) self.groupBox_saving_layout.addWidget(self.save_histogram_button, 0, 3) # 5,4 self.groupBox_saving_layout.addWidget(self.export_textfield, 0, 1, 1, 2) #------------------------------------------------------------------------------------ def show_time_passed(self): """This method asks the remaining time from the instrument level, and calculates the progress, so both can be displayed. """ self.time_passed = self.hydra_instrument.time_passed self.showing_remaining_time.setText(str(self.time_passed)) self.progressbar.setMaximum(int(self.integration_time.magnitude)) self.progressbar.setValue(int(self.time_passed.magnitude)) #print(self.hydra_instrument.remaining_time) # ------------------------------------------------------------------------------------ def set_channel(self): """ This method sets the channel that the user puts, and remembers the string in the init in self.channel. """ self.logger.debug('setting the channel') self.channel = self.channel_combobox.currentText() self.logger.debug('channel: ' + self.channel) def set_array_length(self): """This method sets the array length that the user puts, and remembers the int in the init in self.array_length. It compares it to a max and min value. """ self.logger.debug('setting the array length') self.logger.warning('are you sure you want to change this value?') if self.sender().value() > self.max_length: self.sender().setValue(self.max_length) elif self.sender().value() < 1: self.sender().setValue(1) self.calculate_axis() self.endtime_label.setText(str(self.endtime)) self.array_length = int(self.sender().value()) def set_integration_time(self): """This method combines the integration time that the user puts in the spinbox with the unit in the combobox, and remembers the pint quantity in the init in self.integration_time. It compares it to a max (24 hours) and min (1 s) value. """ self.logger.debug('setting the integration time') tijd = self.integration_time_spinbox.value() unit = self.time_unit_combobox.currentText() local_time = ur(str(tijd) + unit) self.logger.debug('local time value: ' + str(local_time)) if local_time > self.max_time: self.logger.debug('time really too much') local_time = self.max_time.to(unit) self.logger.debug(str(local_time)) self.integration_time_spinbox.setValue(local_time.m_as(unit)) elif local_time < 1 * ur('s'): self.logger.debug('you need to integrate more time') local_time = 1 * ur('s') self.integration_time_spinbox.setValue(local_time.m_as('s')) self.integration_time = local_time self.logger.debug('time remembered is: ' + str(self.integration_time)) def set_resolution(self): """| This method takes the chosen resolution by the user and remembers it for the rest of this class. | It would be cool if I could make it a spinbox, that only allows values of 2^n, but I couldnt make that work... """ self.logger.debug('setting the resolution') self.resolution = self.resolution_combobox.currentText() self.logger.debug('resolution: ' + self.resolution) self.calculate_axis() # # value = self.sender().value() # # Array = np.zeros(20) # for ii in range(20): # Array[ii] = 2**ii # # if value not in Array: # self.logger.debug('not in A') # Diff = abs(Array - value) # index = np.where(Diff == min(Diff)) # index = index[0][0] # self.sender().setValue(Array[index+1]) # self.logger.debug('new value: ' + str(Array[index+1])) # else: # index = np.where(Array == value) # index = index[0][0] # self.logger.debug('value: ' + str(Array[index])) # # #self.sender().setValue(Array[index+1]) # # self.resolution = self.sender().value()*ur('ps') # # # self.endtime_label.setText(str(round(self.endtime.to(self.units), 4))) # # self.logger.debug(str(self.sender().value())) def calculate_axis(self): """| This method calculates the axis that should be put on the graph. | This end time is both displayed in the gui and used for the graph time axis. """ self.endtime = round( ur(self.resolution).m_as('ns') * self.array_length * ur('s'), 4) if self.endtime.m_as( 's') < 120: # below two minutes, display in seconds self.units = 's' elif self.endtime.m_as( 's') < 120 * 60: # below two hours, display in minutes self.units = 'min' elif self.endtime.m_as( 's') < 120 * 60 * 24: # below two days, display in hours self.units = 'hour' else: self.units = 'days' self.endtime_label.setText(str(round(self.endtime.to(self.units), 4))) #self.logger.debug('endtime: {}, units: {}, array length: {}'.format(self.endtime, self.units, self.array_length)) self.time_axis = np.linspace(0, float(self.endtime.m_as(self.units)), self.array_length) #self.logger.debug('time axis: {}'.format(self.time_axis)) #------------------------------------------------------------------------------------ def take_histogram(self): """| In this method there will be made a histogram using the input of the user. | All user inputs were stored previously in the values as declared in the init | (histogram length, resolution, integration time and channel). | A thread is started to be able to also stop the histogram taking. | The data gets plot in the DrawHistogram plot(self.draw.histogram_plot.plot()). | The time axis is calculated in calculate_axis and used for the plot. """ self.logger.info("Taking the histrogram") #first, set the array length and resolution of the histogram self.logger.debug('chosen histogram length: ' + str(self.array_length)) self.logger.debug('chosen resolution: ' + str(self.resolution)) self.hydra_instrument.set_histogram(self.array_length, ur(self.resolution)) #Then, start the histogram + thread self.logger.debug('chosen integration time: ' + str(self.integration_time)) self.logger.debug('chosen channel: ' + str(self.channel)) self.timer.start(100) self.timer_plot.start(100) self.show_time_passed() self.histogram_thread = WorkThread( self.hydra_instrument.make_histogram, self.integration_time, self.channel) self.histogram_thread.start() #make it possible to press the save_histogram_button.(should be True) self.save_histogram_button.setEnabled(True) self.take_histogram_button.setEnabled(True) self.hydra_instrument.time_passed = 0 * ur('s') self.show_time_passed() def update_plot(self): pen = pg.mkPen(color=(0, 0, 0)) # makes the plotted lines black if self.hydra_instrument.hist_ended: self.take_histogram_button.setEnabled(True) self.timer_plot.stop() self.timer.stop() self.histogram = self.hydra_instrument.hist self.calculate_axis() self.draw.histogram_plot.plot(self.time_axis, self.histogram, clear=True, pen=pen) self.draw.histogram_plot.setLabel( 'bottom', "<span style=\"color:black;font-size:20px\"> Time ({}) </span>" .format(self.units)) else: self.take_histogram_button.setEnabled(False) self.histogram = self.hydra_instrument.hist self.calculate_axis() self.logger.debug( 'length time axis: {}, length histogram: {}'.format( len(self.time_axis), len(self.histogram))) if len(self.histogram) > 0: self.draw.histogram_plot.plot(self.time_axis, self.histogram, clear=True) self.draw.histogram_plot.setLabel( 'bottom', "<span style=\"color:black;font-size:20px\"> Time ({}) </span>" .format(self.units)) def save_histogram(self): """| In this method the made histogram gets saved. | This is done with pyqtgraph.exporters. The width and height can be set of the picture below. """ self.logger.info('saving the histogram') try: plt = pg.plot(self.histogram) exporter = pg.exporters.ImageExporter(plt.plotItem) # set export parameters if needed exporter.parameters( )['height'] = 100 # (note this also affects width parameter) exporter.parameters( )['width'] = 100 # (note this also affects height parameter) self.actually_save_histogram(exporter) #there must first be made another(or the same) histogram before this method can be accessed.(should be False) self.save_histogram_button.setEnabled(False) plt.close() except Exception: self.logger.warning( "There is no picture to export...change that by clicking the button above" ) def actually_save_histogram(self, exporter): """| In this method it is defined what the file_name is via checking if there is text in the export textfield. |If there is none, than a file_chooser will be used to have a location where the .png's will be saved. :param exporter: A exporter object with which you can save data :type exporter: pyqtgraph.exporter, doesn't say that much, I know """ if self.export_textfield.text() != "": # save to file via the textfield file_name = self.export_textfield.text() + "\\histogram_" + str( self.histogram_number) + ".png" self.histogram_number += 1 exporter.export(file_name) else: #a file chooser will be used file_name = self.get_file_path_via_filechooser() exporter.export(file_name) #self.make_progress_label.setText("The histogram has been saved at: \n" + str(file_name)) def get_file_path_via_filechooser(self): """| This is code plucked from the internet...so I have no clou what is happening and that is fine really. | If the code breaks, go to: https://pythonspot.com/pyqt5-file-dialog/ :return: the filepath, .png needs to be attached in order to save the picture as a...picture :rtype: string """ options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getSaveFileName( self, "QFileDialog.getSaveFileName()", "", "All Files (*);;Text Files (*.txt)", options=options) return fileName + ".png" def stop_histogram(self): """| Here the self.hydra_instrument.stop is set to True, which means the while loop in the instrument breaks. | Afterwards, the hydraharp itself is actually set to stop. | To avoid errors, it is important to quit the thread. """ self.logger.info('Histogram should stop here') self.hydra_instrument.stop = True self.hydra_instrument.stop_histogram() if self.histogram_thread.isRunning: self.logger.debug('histogram thread was running') self.histogram_thread.quit() self.hydra_instrument.time_passed = 0 * ur('s') self.show_time_passed() self.hydra_instrument.stop = False
def move(self, direction): """| Here the actual move takes place, after the user clicked on one of the four directional buttons. | The clicked button determines the direction that is chosen. | - For the continuous and step move, that is than converted to 0 or 1. | This is correct as it is written right now, I checked it. | - For the relative move, the direction is than converted in adding a minus sign or not. | - For every kind of move, the self.moving_thread is started, so the stop button can be used and the position label can be updated. | I gave this thread the same name every time, don't know whether that is bad practice. The thread is quit in the stop method. :param direction: direction of move, left, right, up, down :type direction: string """ self.direction = direction self.logger.debug('current direction: ' + direction) #remember axis name that instrument thinks in if 'Z' in self.current_axis: axis_string = 'ZPiezoStepper' else: if self.direction == 'left' or self.direction == 'right': axis_string = 'XPiezoStepper' else: axis_string = 'YPiezoStepper' if self.current_move == 'move absolute': #combine the spinbox and unit combobox user input to a pint quantity self.logger.info('moving to an absolute position') distance = self.gui.doubleSpinBox_distance.value() unit = self.gui.comboBox_unit.currentText() self.logger.debug('axis: ' + axis_string) local_distance = ur(str(distance) + unit) self.logger.debug('to position: ' + str(local_distance)) self.moving_thread = WorkThread(self.anc350_instrument.move_to, axis_string, local_distance) self.moving_thread.start() elif self.current_move == 'move relative': # combine the spinbox and unit combobox user input to a pint quantity # add minussign to communicate correct direction to instrument self.logger.info('moving relative') distance = self.gui.doubleSpinBox_distance.value() unit = self.gui.comboBox_unit.currentText() self.logger.debug('axis:' + axis_string) self.logger.debug('direction: ' + direction) if self.direction == 'right' or self.direction == 'up': local_distance = ur(str(distance) + unit) self.logger.debug(str(local_distance)) elif self.direction == 'left' or self.direction == 'down': local_distance = ur(str(-1 * distance) + unit) self.logger.debug(str(local_distance)) self.moving_thread = WorkThread( self.anc350_instrument.move_relative, axis_string, local_distance) self.moving_thread.start() elif self.current_move == 'continuous' or self.current_move == 'step': # convert direction buttons clicked to direction integers that instrument wants # than move for 1s continuously, since the stop button doesnt work yet if self.direction == 'left': if 'Z' in self.current_axis: direction_int = 0 # correct direction, corresponds to labels closer and away else: direction_int = 1 elif self.direction == 'right': if 'Z' in self.current_axis: direction_int = 1 # correct direction, corresponds to labels closer and away else: direction_int = 0 elif self.direction == 'up': direction_int = 0 elif self.direction == 'down': direction_int = 1 if self.current_move == 'continuous': self.logger.info('moving continuously') self.moving_thread = WorkThread( self.anc350_instrument.move_continuous, axis_string, direction_int) self.moving_thread.start() elif self.current_move == 'step': self.logger.info('making a step') self.anc350_instrument.given_step(axis_string, direction_int, 1)
class AutoMeasurementGui(BaseGui): """ Builds a Measurement GUI based on the Measurement actionlist in experiment.properties (which is read from the config file). The GUI will follow the nested structure in the actionlist. Note that the Actions in the actionlist need to refer to an appropriate GUI file and Widget and to an appropriate method in the experiment (which should contain nested() if nesting is to be possible). I also builds the Start/Pause, Break and Stop buttons that contol the flow of the measurement :param experiment: hyperion experiment object :param measurement: (str) name of a measurement (specified in the config of the experiment) :param parent: parent QWidget """ def __init__(self, experiment, measurement, parent=None, output_guis=None, graphs_in_standalone=None): self.logger = logging.getLogger(__name__) self.logger.debug('Creating BaseMeasurement object') super().__init__(parent) self.child_action_widgets = {} self.experiment = experiment self.measurement = measurement self.output_guis = output_guis self._parent = parent if not hasattr(self.experiment, 'properties'): self.logger.error('Experiment object needs to have properties dictionary. Make sure you load config.') if measurement not in self.experiment.properties['Measurements']: self.logger.error('Unknown measurement: {}'.format(measurement)) if 'automated_actionlist' not in self.experiment.properties['Measurements'][measurement]: self.logger.error("Measurement doesn't have automated_actionlist: {}".format(measurement)) if 'ActionTypes' in self.experiment.properties: self.types = self.experiment.properties['ActionTypes'] else: self.types = {} # self.measurement_thread = WorkThread(experiment.dummy_measurement_for_testing_gui_buttons) self.measurement_thread = WorkThread(lambda: experiment.perform_measurement(self.measurement)) self._valid = self.validate() # self.outer_layout = QGridLayout() self.outer_layout = QVBoxLayout() self.outer_layout.setSpacing(20) self.button_layout = self.create_buttons() # self.outer_layout.addLayout(self.button_layout, 0, 0) self.outer_layout.addLayout(self.button_layout) self.actions_layout = QVBoxLayout() label_incorrect = QLabel('incorrect config file') label_incorrect.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.actions_layout.addWidget(label_incorrect) self.create_actionlist_guis() # This line controls the size of the whole layout. # .SetDefaultConstraint causes it to adjust to the content size, but keeps it adjustable # .SetFixedSize adjust to the content and prevents manual resizing # self.outer_layout.setSizeConstraint(QLayout.SetDefaultConstraint) # self.outer_layout.setSizeConstraint(QLayout.SetFixedSize) self.setLayout(self.outer_layout) self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.show() # Set up QTimer to periodically update button states (based on self.timer_update_buttons = QTimer() self.timer_update_buttons.timeout.connect(self.update_buttons) self.timer_update_buttons.start(50) # in ms # If run in standalone mode, but with graphs, display those graphs self.graphs_in_standalone = graphs_in_standalone if graphs_in_standalone is False: self.logger.debug("Not using graphs_in_standalone mode") if graphs_in_standalone is None: if parent is None and output_guis is not None: self.logger.info("Automatically using graphs_in_standalone mode") self.graphs_in_standalone = True else: self.logger.info("Automatically not using graphs_in_standalone mode because parent is not None or output_guis is None") self.graphs_in_standalone = False elif graphs_in_standalone: self.logger.debug("Using graphs_in_standalone mode") if self.graphs_in_standalone: self.show_ouputs_if_standalone() # Set up QTimer to periodically update status message in parent gui if available. if self._parent is not None: self.timer_update_status = QTimer() self.timer_update_status.timeout.connect(lambda: self._parent.update_statusbar(self.measurement, timer=self.timer_update_status)) def closeEvent(self, *args, **kwargs): if self.graphs_in_standalone: self.logger.debug('Running in graphs_in_standalone mode: Closing the QApplication') QApplication.instance().quit() super().closeEvent(*args, **kwargs) def show_ouputs_if_standalone(self): for name, gui in self.output_guis.items(): gui.show() def create_buttons(self): """ Create Start/Pause, Break, Stop and Config Button. And link to appropriate methods. :return: layout containing buttons """ self.logger.debug('Creating start stop buttons') layout_buttons = QHBoxLayout() self.button_start_pause = QPushButton('Start', clicked = self.start_pause) self.button_break = QPushButton('Break', clicked = self.apply_break) self.button_stop = QPushButton('Stop', clicked = self.apply_stop) self.button_config = QPushButton('Config', clicked = self.config) layout_buttons.addWidget(self.button_start_pause) layout_buttons.addWidget(self.button_break) layout_buttons.addWidget(self.button_stop) layout_buttons.addWidget(self.button_config) return layout_buttons def create_actionlist_guis(self): """ Creates and updates the the (nested) measurement GUI. """ self.deleteItemsOfLayout(self.actions_layout) if self._valid: self.child_action_widgets = {} self.actions_layout = self.add_actions_recursively(self.experiment.properties['Measurements'][self.measurement]['automated_actionlist']) else: self.actions_layout = QVBoxLayout() self.actions_layout.addWidget(QLabel('incorrect config file')) # self.outer_layout.addLayout(self.actions_layout, 1, 0) self.outer_layout.addLayout(self.actions_layout) self.update() def add_actions_recursively(self, actionlist, nesting_level=0): """ Recursive function to build nested layout of action GUIs. :param actionlist: (list) :param nesting_level: (int) Used for recursion (use default when calling method) :return: layout containing the nested GUIs """ layout = QVBoxLayout() layout.setContentsMargins(0,0,0,0) layout.setSpacing(5+self.__shift(nesting_level)[0]) for act in actionlist: actiondict = ActionDict(act, self.types) box = QGroupBox(actiondict['Name']) box.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) box.setCheckable(True) # This adds the checkbox in the top-left corner box_layout = QVBoxLayout() box_layout.setContentsMargins(0,5,0,0) box_layout.setSpacing(0) # distance between action widget and its nested items if '_view' in actiondict: action_gui_class = get_class(actiondict['_view']) action_gui_widget = action_gui_class(actiondict, self.experiment, parent=box) self.child_action_widgets[actiondict['Name']] = action_gui_widget action_gui_widget.layout.setContentsMargins(7,0,20-self.__shift(nesting_level)[1],10) action_gui_widget.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) box_layout.addWidget(action_gui_widget) if '_disabled' in actiondict and actiondict['_disabled']: box.setChecked(False) box.toggled.connect(lambda state, a=actiondict: a.__setitem__('_disabled', not state)) if '~nested' in actiondict: nested_layout = self.add_actions_recursively(actiondict['~nested'], nesting_level+1) nested_layout.setContentsMargins(max(3,12-nesting_level), 0, self.__shift(nesting_level)[0],5+max(0,5-nesting_level)) box_layout.addLayout(nested_layout) if box_layout.count(): box.setLayout(box_layout) layout.addWidget(box) return layout def __shift(self, level, maxlev=5): # Helper function to calculating border widths for aligning nested layout in add_actions_recursively(). # Returns tuple inverted = max(0, maxlev - level) shift = 0 for s in range(inverted, maxlev): shift += s+2 return inverted, shift def start_plotting(self, *args, **kwargs): """ This method should be overwritten by a parent class. """ self.logger.warning('I didnt start plotting, ended up here in base_guis.AutoMeasurementGui') None def ensure_output_docks_are_open(self): """ Uses the show_dock method added by ExpGui to ensure that all aoutput guis are opened before starting the measurement""" if self._parent is not None: if self.output_guis is not None: for instance in self.output_guis.values(): if hasattr(instance, 'show_dock'): instance.show_dock(True) def start_pause(self): """ Called when Start/Pause/Continue button is pressed. Starts a measurement thread or communicates to experiment through the Measurement status flags that a measurement should be paused or continued. """ self.logger.debug('start/pause pressed') self.experiment.apply_pause = not self.experiment.apply_pause if self.experiment.running_status == self.experiment._not_running: self.ensure_output_docks_are_open() self.measurement_thread.start() if self._parent is not None: self.timer_update_status.start(100) self.start_plotting() self.update_buttons() def apply_break(self): """ Called when Break button is pressed. Communicates to experiment through the Measurement status flags that a measurement break ("soft stop") should be applied. """ self.logger.debug('break pressed') self.experiment.apply_break = True self.update_buttons() def apply_stop(self): """ Called when Stop button is pressed. Communicates to experiment through the Measurement status flags that a measurement should be stopped (immediately). """ self.logger.debug('stop pressed') self.experiment.apply_stop = True self.update_buttons() def config(self): """ Called when Config button is pressed. Opens the ModifyMeasurement Dialog to modify/correct the config text directly. """ # Prepare window to modify config: dialog_config = ModifyMeasurement(self.experiment, self.measurement, self) dialog_config.exec_() self.update_buttons() self.create_actionlist_guis() self.update() def update_buttons(self): """ Updates the status of the start/pause/continue, break, stop buttons. """ # note that: # experiment._not_running = 0 # experiment._running = 1 # experiment._pausing = 2 # experiment._breaking = 3 # experiment._stopping = 4 if not self._valid: self.button_start_pause.setEnabled(False) self.button_break.setEnabled(False) self.button_stop.setEnabled(False) self.button_config.setEnabled(True) return else: if self.experiment.running_status == self.experiment._not_running: self.button_start_pause.setText('Start') elif self.experiment.apply_pause: self.button_start_pause.setText('Continue') else: self.button_start_pause.setText('Pause') if self.experiment.apply_break: self.button_break.setText('Breaking') else: self.button_break.setText('Break') self.button_start_pause.setEnabled(self.experiment.running_status < self.experiment._breaking) self.button_break.setEnabled(self.experiment._not_running < self.experiment.running_status < self.experiment._breaking) self.button_stop.setEnabled(self.experiment._not_running < self.experiment.running_status < self.experiment._stopping) self.button_config.setEnabled(self.experiment.running_status == self.experiment._not_running) def validate(self): """ Uses self.experiment._validate_actionlist() to determine if the current actionlist in experiment.properties is valid. :return: (boolean) True if valid """ new_action_list, invalid_methods, invalid_names = self.experiment._validate_actionlist(self.experiment.properties['Measurements'][self.measurement]['automated_actionlist']) return (invalid_methods==0 and invalid_names==0) # if __name__ == '__main__': # app = QApplication(sys.argv) # pass # app.exec_() # # sys.exit(app.exec_())
def button_clicked(self): print('test button is clicked') ws.controller.spt_set('NEW_POSITION',300) self.worker_thread = WorkThread(ws.controller.spt.Move) self.worker_thread.start()
class ExampleGui(QWidget): """" This is simple pyqt5 gui with the ability to create threads and stop them, that is harder than it sounds. """ def __init__(self, example_ins): super().__init__() self.title = 'example gui' self.left = 40 self.top = 40 self.width = 320 self.height = 200 self.example_ins = example_ins self.initUI() def initUI(self): self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) self.setAutoFillBackground(True) self.p = self.palette() self.set_color(Qt.red) self.make_button_1() self.make_button_2() self.show() def make_button_1(self): self.button = QPushButton('start button', self) self.button.setToolTip('This is an example button') self.button.move(10, 10) self.button.clicked.connect(self.on_click) def make_button_2(self): self.button_2 = QPushButton('end button', self) self.button_2.setToolTip('end the function') self.button_2.move(90, 10) self.button_2.clicked.connect(self.stop_on_click_function) def set_color(self, color): """ Set the color of the widget :param color: a color you want the gui to be :type string """ self.p.setColor(self.backgroundRole(), color) self.setPalette(self.p) def on_click(self): #initialize a long(couple of seconds) test function. self.worker_thread = WorkThread(self.go_to_sleep) self.worker_thread.start() def stop_on_click_function(self): """ stop a thread if one is running """ if self.worker_thread.isRunning(): self.worker_thread.quit() self.worker_thread.wait() print('this function is going to stop the on_click function') else: return def go_to_sleep(self): """ function that starts the thread. """ print('button click') self.button.setEnabled(False) self.set_color(Qt.yellow) time.sleep(4) self.set_color(Qt.red) self.button.setEnabled(True)
def __init__(self, experiment, measurement, parent=None, output_guis=None, graphs_in_standalone=None): self.logger = logging.getLogger(__name__) self.logger.debug('Creating BaseMeasurement object') super().__init__(parent) self.child_action_widgets = {} self.experiment = experiment self.measurement = measurement self.output_guis = output_guis self._parent = parent if not hasattr(self.experiment, 'properties'): self.logger.error('Experiment object needs to have properties dictionary. Make sure you load config.') if measurement not in self.experiment.properties['Measurements']: self.logger.error('Unknown measurement: {}'.format(measurement)) if 'automated_actionlist' not in self.experiment.properties['Measurements'][measurement]: self.logger.error("Measurement doesn't have automated_actionlist: {}".format(measurement)) if 'ActionTypes' in self.experiment.properties: self.types = self.experiment.properties['ActionTypes'] else: self.types = {} # self.measurement_thread = WorkThread(experiment.dummy_measurement_for_testing_gui_buttons) self.measurement_thread = WorkThread(lambda: experiment.perform_measurement(self.measurement)) self._valid = self.validate() # self.outer_layout = QGridLayout() self.outer_layout = QVBoxLayout() self.outer_layout.setSpacing(20) self.button_layout = self.create_buttons() # self.outer_layout.addLayout(self.button_layout, 0, 0) self.outer_layout.addLayout(self.button_layout) self.actions_layout = QVBoxLayout() label_incorrect = QLabel('incorrect config file') label_incorrect.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.actions_layout.addWidget(label_incorrect) self.create_actionlist_guis() # This line controls the size of the whole layout. # .SetDefaultConstraint causes it to adjust to the content size, but keeps it adjustable # .SetFixedSize adjust to the content and prevents manual resizing # self.outer_layout.setSizeConstraint(QLayout.SetDefaultConstraint) # self.outer_layout.setSizeConstraint(QLayout.SetFixedSize) self.setLayout(self.outer_layout) self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.show() # Set up QTimer to periodically update button states (based on self.timer_update_buttons = QTimer() self.timer_update_buttons.timeout.connect(self.update_buttons) self.timer_update_buttons.start(50) # in ms # If run in standalone mode, but with graphs, display those graphs self.graphs_in_standalone = graphs_in_standalone if graphs_in_standalone is False: self.logger.debug("Not using graphs_in_standalone mode") if graphs_in_standalone is None: if parent is None and output_guis is not None: self.logger.info("Automatically using graphs_in_standalone mode") self.graphs_in_standalone = True else: self.logger.info("Automatically not using graphs_in_standalone mode because parent is not None or output_guis is None") self.graphs_in_standalone = False elif graphs_in_standalone: self.logger.debug("Using graphs_in_standalone mode") if self.graphs_in_standalone: self.show_ouputs_if_standalone() # Set up QTimer to periodically update status message in parent gui if available. if self._parent is not None: self.timer_update_status = QTimer() self.timer_update_status.timeout.connect(lambda: self._parent.update_statusbar(self.measurement, timer=self.timer_update_status))
class Attocube_GUI(BaseGui): """ | **Attocube piezo GUI for the instrument.** | Uses the attocube.ui file that was made with qt Designer. The maximum values for amplitude, frequency dcLevel on Scanner and distance are set here to be used in the rest of the class. | The start parameters for current_axis, current_move, direction and distance are set here, to be used and changed in the rest of the class. | A timer is started here to update the position real time. | A moving_thread is already made here, so the program doesnt break if somebody clicks stop before he has moved anywhere. :param anc350_instrument: class for the instrument to control. :type anc350_instrument: instance of the instrument class """ def __init__(self, anc350_instrument, also_close_output=False): """Attocube """ super().__init__() self.logger = logging.getLogger(__name__) self.title = 'Attocube GUI' self.anc350_instrument = anc350_instrument name = 'attocube.ui' gui_folder = os.path.dirname(os.path.abspath(__file__)) gui_file = os.path.join(gui_folder, name) self.logger.debug('Loading the GUI file: {}'.format(gui_file)) self.gui = uic.loadUi(gui_file, self) self.max_amplitude_V = 60 self.max_frequency = 2000 self.max_dclevel_V = 60 * ur( 'V') #Pay attention: this max only goes for 4K, self.max_dcLevel_mV_300K = 60 * ur( 'V') #at room temperature use 60V as max self.max_distance = 5 * ur('mm') self.current_positions = {} self.current_axis = 'X,Y Piezo Stepper' self.current_move = 'step' self.direction = 'left' self.distance = 0 * ur('um') self.settings = { 'amplitudeX': 30, 'amplitudeY': 40, 'amplitudeZ': 30, 'frequencyX': 1000, 'frequencyY': 1000, 'frequencyZ': 1000 } self.temperature = 300 self.scanner_unitX = 'V' self.scanner_unitY = 'V' self.scanner_unitZ = 'V' self.dcX = 1 * ur(self.scanner_unitX) self.dcY = 1 * ur(self.scanner_unitY) self.dcZ = 0 * ur(self.scanner_unitZ) self.stop = self.stop_moving self.initUI() #This one is to continuously (= every 100ms) show the position of the axes self.timer = QTimer() self.timer.timeout.connect(self.show_position) self.timer.start(100) #time in ms self.moving_thread = WorkThread(self.anc350_instrument.move_to, self.current_axis, self.distance) def initUI(self): """Connect all buttons, comboBoxes and doubleSpinBoxes to methods """ self.logger.debug('Setting up the Measurement GUI') self.setWindowTitle(self.title) self.show() self.make_combobox_scanner() self.make_combobox_movements() self.make_combobox_configurate() self.make_combobox_basic() def make_combobox_basic(self): """| *Layout of basic combobox* | Sets the blue border, the colour of the stop button and disables all other comboboxes. | Connects buttons and show_position, which works with a timer that is started in the init of this class. """ self.gui.groupBox_basic.setObjectName("Colored_basic") self.gui.groupBox_basic.setStyleSheet( "QGroupBox#Colored_basic {border: 1px solid blue; border-radius: 9px;}" ) self.gui.groupBox_configurate.setObjectName("Colored_configure") self.gui.groupBox_configurate.setStyleSheet( "QGroupBox#Colored_configure {border: 1px solid blue; border-radius: 9px;}" ) #combobox basic self.gui.comboBox_axis.setCurrentText(self.current_axis) self.gui.comboBox_axis.currentTextChanged.connect(self.get_axis) self.gui.pushButton_stop.clicked.connect(self.stop_moving) self.pushButton_stop.setStyleSheet("background-color: red") self.gui.doubleSpinBox_temperature.setValue(self.temperature) self.set_temperature() self.gui.doubleSpinBox_temperature.valueChanged.connect( self.set_temperature) def make_combobox_configurate(self): """| *Layout of configurate combobox* | Sets the blue border. Connects the spinboxes to methode set_value. | Values are read and remembered in the whole class. Connects the configurate button. """ self.gui.doubleSpinBox_amplitudeX.setValue(self.settings['amplitudeX']) self.gui.doubleSpinBox_frequencyX.setValue(self.settings['frequencyX']) self.gui.doubleSpinBox_amplitudeY.setValue(self.settings['amplitudeY']) self.gui.doubleSpinBox_frequencyY.setValue(self.settings['frequencyY']) self.gui.doubleSpinBox_amplitudeZ.setValue(self.settings['amplitudeZ']) self.gui.doubleSpinBox_frequencyZ.setValue(self.settings['frequencyZ']) self.gui.doubleSpinBox_frequencyX.valueChanged.connect( lambda: self.set_value('X', 'frequency')) self.gui.doubleSpinBox_amplitudeX.valueChanged.connect( lambda: self.set_value('X', 'amplitude')) self.gui.doubleSpinBox_amplitudeY.valueChanged.connect( lambda: self.set_value('Y', 'amplitude')) self.gui.doubleSpinBox_frequencyY.valueChanged.connect( lambda: self.set_value('Y', 'frequency')) self.gui.doubleSpinBox_amplitudeY.valueChanged.connect( lambda: self.set_value('Z', 'amplitude')) self.gui.doubleSpinBox_frequencyY.valueChanged.connect( lambda: self.set_value('Z', 'frequency')) self.gui.pushButton_configurateStepper.clicked.connect( self.configure_stepper) self.get_axis() def make_combobox_movements(self): """| *Layout of combobox of all movements* | Sets the blue border. Runs the self.get_move method to find out what kind of movement is selected (step, continuous, ...). | Connects the spinbox and unit combobox to method set_value. Values are read and remembered in the whole class. | Connects all buttons to the methode self.move, that figures out in which way and direction to move. """ self.gui.comboBox_kindOfMove.setCurrentText(self.current_move) self.gui.comboBox_kindOfMove.currentTextChanged.connect(self.get_move) self.gui.comboBox_unit.setCurrentText('um') self.gui.doubleSpinBox_distance.setValue(self.distance.m_as('um')) self.gui.doubleSpinBox_distance.valueChanged.connect(self.set_distance) self.gui.comboBox_unit.currentTextChanged.connect(self.set_distance) # self.gui.pushButton_left.setCheckable(True) # self.gui.pushButton_left.toggle() self.gui.pushButton_left.clicked.connect(lambda: self.move('left')) self.gui.pushButton_right.clicked.connect(lambda: self.move('right')) self.gui.pushButton_up.clicked.connect(lambda: self.move('up')) self.gui.pushButton_down.clicked.connect(lambda: self.move('down')) def make_combobox_scanner(self): """| *Layout of scanner combobox* | Connects the spinboxes to set_value, that in case of the scanner will immediately put the voltage to move the scanner. """ self.gui.comboBox_unitX.setCurrentText(self.scanner_unitX) self.gui.comboBox_unitX.currentTextChanged.connect( lambda: self.set_scanner_position('X')) self.gui.comboBox_unitY.setCurrentText(self.scanner_unitY) self.gui.comboBox_unitY.currentTextChanged.connect( lambda: self.set_scanner_position('Y')) self.gui.comboBox_unitZ.setCurrentText(self.scanner_unitZ) self.gui.comboBox_unitZ.currentTextChanged.connect( lambda: self.set_scanner_position('Z')) self.gui.doubleSpinBox_scannerX.setValue(int(self.dcX.m_as('V'))) self.gui.doubleSpinBox_scannerY.setValue(int(self.dcY.m_as('V'))) self.gui.doubleSpinBox_scannerZ.setValue(int(self.dcZ.m_as('V'))) self.move_scanner('dCX') self.move_scanner('dCY') self.move_scanner('dCZ') self.gui.doubleSpinBox_scannerX.valueChanged.connect( lambda: self.set_scanner_position('X')) self.gui.doubleSpinBox_scannerY.valueChanged.connect( lambda: self.set_scanner_position('Y')) self.gui.doubleSpinBox_scannerZ.valueChanged.connect( lambda: self.set_scanner_position('Z')) self.gui.pushButton_zero_scanners.clicked.connect(self.zero_scanners) def show_position(self): """In the instrument level, the current positions of both Stepper and Scanner are remembered in a dictionary and updated through get_position. This method read them out (continuously, through the timer in the init) and displays their values. """ self.anc350_instrument.update_all_positions() self.current_positions = self.anc350_instrument.current_positions self.gui.label_actualPositionX.setText( str(self.current_positions['XPiezoStepper'])) self.gui.label_actualPositionY.setText( str(self.current_positions['YPiezoStepper'])) self.gui.label_actualPositionZ.setText( str(self.current_positions['ZPiezoStepper'])) self.gui.scan_positionX.setText( str(self.current_positions['XPiezoScanner'])) self.gui.scan_positionY.setText( str(self.current_positions['YPiezoScanner'])) self.gui.scan_positionZ.setText( str(self.current_positions['ZPiezoScanner'])) def get_axis(self): """| *Layout stacked widgets plus blue borders* | Depending on the selected axis, the gui looks differently. | - The basic box is always enabled. | - If one of the Steppers is selected, only the configuration box is shown. | - After configuration, also the box with all the moves will be enabled. | - If one of the Scanners is selected, only the scanner box is shown. | - When the Z Piezo Stepper is selected, all of the X values change to Z. | - When the Z Piezo Scanner is selected, similar but now only for the two boxes in the scanner part. | - **Important** self.current_axis is saved here and used in the whole program. """ self.current_axis = self.gui.comboBox_axis.currentText() self.logger.debug('current axis:' + str(self.current_axis)) if 'Stepper' in self.current_axis: #self.gui.groupBox_configurate.setEnabled(True) self.gui.groupBox_configurate.setStyleSheet( "QGroupBox#Colored_configure {border: 1px solid blue; border-radius: 9px;}" ) self.gui.groupBox_actions.setStyleSheet("QGroupBox default") self.gui.stackedWidget_actions.setCurrentWidget( self.gui.page_configure_stepper) self.gui.stackedWidget_stepper.setCurrentWidget( self.gui.stackedWidgetMoving) self.gui.stackedWidgetMoving.setEnabled(False) if 'Z' in self.current_axis: #Disable the xy groupboxes, enable the z groupboxes, # choose the page_amplZ of the stackedWidget_configure self.gui.groupBox_XY.setEnabled(False) self.gui.groupBox_Z.setEnabled(True) self.gui.stackedWidget_configure.setCurrentWidget( self.gui.page_amplZ) self.gui.pushButton_up.setEnabled(False) self.gui.pushButton_down.setEnabled(False) self.gui.pushButton_left.setText('closer') self.gui.pushButton_right.setText('away') else: #Enable the xy groupboxes, disable the z groupboxes, # choose the page_amplXY of the stackedWidget_configure. self.gui.groupBox_XY.setEnabled(True) self.gui.groupBox_Z.setEnabled(False) self.gui.stackedWidget_configure.setCurrentWidget( self.gui.page_amplXY) self.gui.pushButton_up.setEnabled(True) self.gui.pushButton_down.setEnabled(True) self.gui.pushButton_left.setText('left') self.gui.pushButton_right.setText('right') elif 'Scanner' in self.current_axis: #Choose the page_move_scanner of the stackedWidget_actions and the stackedWidgetEmpty of the stackedWidget_stepper self.gui.stackedWidget_actions.setCurrentWidget( self.gui.page_move_scanner) self.gui.stackedWidget_stepper.setCurrentWidget( self.gui.stackedWidgetempty) #Give the configurate box a border and the action box none self.gui.groupBox_configurate.setStyleSheet( "QGroupBox#Colored_configure {border: 1px solid blue; border-radius: 9px;}" ) self.gui.groupBox_actions.setStyleSheet("QGroupBox default") #Choose either the page_scannerZ or page_scannerXY of the stackedWidget_voltScanner if 'Z' in self.current_axis: self.gui.stackedWidget_voltScanner.setCurrentWidget( self.gui.page_scannerZ) else: self.gui.stackedWidget_voltScanner.setCurrentWidget( self.gui.page_scannerXY) def get_move(self): """| *Layout of all moving options* | Similar to the get_axis, the box with all the moves has lots of options that get chosen from the stacked widgets. | - When continuous is selected, it gives you the speed in the selected axes. | - When step is selected, it gives you the stepsize of the selected axes. | - When move absolute or move relative are selected, the user can enter the desired position/distance. """ self.current_move = self.gui.comboBox_kindOfMove.currentText() self.logger.debug('current way of moving: ' + str(self.current_move)) if 'absolute' in self.current_move: #disable all buttons, except for one or two, to move if 'Z' in self.current_axis: self.gui.pushButton_left.setEnabled(False) self.gui.pushButton_up.setEnabled(False) self.gui.pushButton_down.setEnabled(False) self.gui.pushButton_right.setText('move Z') else: self.gui.pushButton_left.setEnabled(False) self.gui.pushButton_up.setText('move Y') self.gui.pushButton_down.setEnabled(False) self.gui.pushButton_right.setText('move X') else: #enable the buttons that were disabled in move absolute if 'Z' in self.current_axis: self.gui.pushButton_left.setEnabled(True) self.gui.pushButton_up.setEnabled(False) self.gui.pushButton_down.setEnabled(False) self.gui.pushButton_right.setText('away') else: self.gui.pushButton_left.setEnabled(True) self.gui.pushButton_up.setText('up') self.gui.pushButton_down.setEnabled(True) self.gui.pushButton_right.setText('right') if self.current_move == 'move relative': #disable the info box (with speed or step size), enable user input posibility self.gui.label_sortMove.setText('to relative distance') self.gui.stackedWidget_moveDependent.setCurrentWidget( self.gui.page_distance) elif self.current_move == 'move absolute': # disable the info box (with speed or step size), enable user input posibility self.gui.label_sortMove.setText('to absolute position') self.gui.stackedWidget_moveDependent.setCurrentWidget( self.gui.page_distance) elif self.current_move == 'continuous': #disable the user input possibility, show either the speed of current axes (depends on amplitude) if 'Z' in self.current_axis: self.gui.label_speed_stepZ.setText('speed Z') self.gui.label_speedsize_stepsizeZ.setText( str(self.anc350_instrument.Speed[1] * ur('nm/s').to('um/s'))) self.gui.stackedWidget_moveDependent.setCurrentWidget( self.gui.page_stepZ) else: self.gui.label_speed_stepX.setText('speed X') self.gui.label_speed_stepY.setText('speed Y') self.gui.label_speedsize_stepsizeX.setText( str(self.anc350_instrument.Speed[0] * ur('nm/s').to('um/s'))) self.gui.label_speedsize_stepsizeY.setText( str(self.anc350_instrument.Speed[2] * ur('nm/s').to('um/s'))) self.gui.stackedWidget_moveDependent.setCurrentWidget( self.gui.page_stepXY) elif self.current_move == 'step': # disable the user input possibility, show either the step size on current axes (depends on frequency) if 'Z' in self.current_axis: self.gui.label_speed_stepZ.setText('step size Z') self.gui.label_speedsize_stepsizeZ.setText( str(self.anc350_instrument.Stepwidth[1] * ur('nm'))) self.gui.stackedWidget_moveDependent.setCurrentWidget( self.gui.page_stepZ) else: self.gui.label_speed_stepX.setText('step size X') self.gui.label_speed_stepY.setText('step size Y') self.gui.label_speedsize_stepsizeX.setText( str(self.anc350_instrument.Stepwidth[0] * ur('nm'))) self.gui.label_speedsize_stepsizeY.setText( str(self.anc350_instrument.Stepwidth[2] * ur('nm'))) self.gui.stackedWidget_moveDependent.setCurrentWidget( self.gui.page_stepXY) def set_temperature(self): """Set the temperature and change the scanner piezo voltage limits accordingly. """ self.temperature = self.gui.doubleSpinBox_temperature.value() self.logger.debug('Changing the temperature to {}K'.format( self.temperature)) self.anc350_instrument.temperature = self.temperature self.anc350_instrument.set_temperature_limits() self.max_dclevel_V = self.anc350_instrument.max_dC_level self.logger.debug('Changed the scanner piezo limits to {}'.format( self.max_dclevel_V)) def set_value(self, axis, value_type): """| Reads the values that the user filled in: amplitude, frequency or dc level on scanner. | Sets either the user input or the default amplitudes/frequencies as in the dictionary. The value is saved in self.settings. | If X and Y Scanner are selected, values are set separately; with Z, there is only one spinbox to fill in. | Values from dictionary are used in configure_stepper, but only if the user clicks configure. | axis and value_type are locally changed into the name as known in the dictionaries, like amplitudeX or dcZ. :param axis: axis X, Y, Z :type axis: string :param value_type: amplitude, frequency or dc :type value_type: string """ self.logger.debug('changing a user input value') local_axis_name = value_type + axis if value_type == 'amplitude': self.logger.debug('changing the amplitude') max_value = self.max_amplitude_V elif value_type == 'frequency': self.logger.debug('changing the frequency') max_value = self.max_frequency if self.sender().value() > max_value: self.sender().setValue(max_value) elif self.sender().value() < 0: self.sender().setValue(0) # Store the new value in the dictionary in the init self.logger.debug(local_axis_name) self.settings[local_axis_name] = int(self.sender().value()) self.logger.debug(self.settings) self.logger.debug('axis changed: ' + str(local_axis_name)) self.logger.debug('value put: ' + str(self.settings[local_axis_name])) def set_scanner_position(self, axis): """To make it possible to use both V and mV for the scanner. The value of the spinbox and the unit in the combobox are combined. Then they are tested against the maximum and minimum values. If they are labeled too high or too low, the user input is changed to the maximum or minimum value. :param axis: axis X, Y, Z :type axis: string """ change = 'not' if axis == 'X': scanner_pos = self.gui.doubleSpinBox_scannerX.value() self.scanner_unitX = self.gui.comboBox_unitX.currentText() scanner_unit = self.scanner_unitX elif axis == 'Y': scanner_pos = self.gui.doubleSpinBox_scannerY.value() self.scanner_unitY = self.gui.comboBox_unitY.currentText() scanner_unit = self.scanner_unitY elif axis == 'Z': scanner_pos = self.gui.doubleSpinBox_scannerZ.value() self.scanner_unitZ = self.gui.comboBox_unitZ.currentText() scanner_unit = self.scanner_unitZ local_position = ur(str(scanner_pos) + scanner_unit) self.logger.debug('{} local scanner position value: {}'.format( axis, local_position)) if local_position > self.max_dclevel_V: self.logger.debug('value too high') local_max = self.max_dclevel_V.to(scanner_unit) self.logger.debug(str(local_max)) change = 'high' elif local_position > self.max_dcLevel_mV_300K: self.logger.warning( 'You are exceeding the 60V maximum for the piezo at room temperature' ) elif local_position < 0: self.logger.debug('value too low') change = 'low' if axis == 'X': if change == 'high': self.gui.doubleSpinBox_scannerX.setValue( local_max.m_as(scanner_unit)) elif change == 'low': self.gui.doubleSpinBox_scannerX.setValue(0) scanner_pos = self.gui.doubleSpinBox_scannerX.value() local_position = ur(str(scanner_pos) + scanner_unit) self.dcX = local_position self.logger.debug('dictionary position {} changed to: {}'.format( axis, self.dcX)) elif axis == 'Y': if change == 'high': self.gui.doubleSpinBox_scannerY.setValue( local_max.m_as(scanner_unit)) elif change == 'low': self.gui.doubleSpinBox_scannerY.setValue(0) scanner_pos = self.gui.doubleSpinBox_scannerY.value() local_position = ur(str(scanner_pos) + scanner_unit) self.dcY = local_position self.logger.debug('dictionary position {} changed to: {}'.format( axis, self.dcY)) elif axis == 'Z': if change == 'high': self.gui.doubleSpinBox_scannerZ.setValue( local_max.m_as(scanner_unit)) elif change == 'low': self.gui.doubleSpinBox_scannerZ.setValue(0) scanner_pos = self.gui.doubleSpinBox_scannerZ.value() local_position = ur(str(scanner_pos) + scanner_unit) self.dcZ = local_position self.logger.debug('dictionary position {} changed to: {}'.format( axis, self.dcZ)) self.move_scanner('dc' + axis) def set_distance(self): """Works similar to set_value method, but now only for the distance spinBox and unit. Combines value of spinbox with unit to make pint quantity and checks against maximum value defined up. Either applies the dictionary value of the distance, or changes that dictionary value and than applies it. """ distance = self.gui.doubleSpinBox_distance.value() unit = self.gui.comboBox_unit.currentText() local_distance = ur(str(distance) + unit) self.logger.debug('local distance value: ' + str(local_distance)) if local_distance > self.max_distance: self.logger.debug('value too high') local_max = self.max_distance.to(unit) self.logger.debug(str(local_max)) self.gui.doubleSpinBox_distance.setValue(local_max.m_as(unit)) distance = self.gui.doubleSpinBox_distance.value() elif local_distance < 0: self.logger.debug('value too low') self.gui.doubleSpinBox_distance.setValue(0) distance = self.gui.doubleSpinBox_distance.value() local_distance = ur(str(distance) + unit) #in case something changed self.distance = local_distance self.logger.debug('dictionary distance changed to: ' + str(self.distance)) def configure_stepper(self): """Configures the stepper, using the amplitude and frequency that had been set in set_frequency and set_amplitude. After configuration, the box with all the different moves is chosen and the get_move is run to set the layout fit for the current move. """ self.logger.info('configurating stepper') if 'Z' in self.current_axis: self.anc350_instrument.configure_stepper( 'ZPiezoStepper', self.settings['amplitudeZ'] * ur('V'), self.settings['frequencyZ'] * ur('Hz')) else: self.anc350_instrument.configure_stepper( 'XPiezoStepper', self.settings['amplitudeX'] * ur('V'), self.settings['frequencyX'] * ur('Hz')) self.anc350_instrument.configure_stepper( 'YPiezoStepper', self.settings['amplitudeY'] * ur('V'), self.settings['frequencyY'] * ur('Hz')) self.gui.groupBox_actions.setObjectName("Colored_actions") self.gui.groupBox_actions.setStyleSheet( "QGroupBox#Colored_actions {border: 1px solid blue; border-radius: 9px;}" ) self.gui.stackedWidgetMoving.setEnabled(True) self.get_move() def move_scanner(self, axis): """| Moves the scanner. | Is called by set_value, moves as soon as the user clicked Enter. :param axis: axis as they are called in the dictionary self.stepper_settings: dcX, dcY, dcZ :type axis: string """ self.logger.info('moving the scanner ' + axis) if 'X' in axis: self.logger.debug('move by {}'.format(self.dcX)) self.anc350_instrument.move_scanner('XPiezoScanner', self.dcX) elif 'Y' in axis: self.logger.debug('move by {}'.format(self.dcY)) self.anc350_instrument.move_scanner('YPiezoScanner', self.dcY) elif 'Z' in axis: self.logger.debug('move by {}'.format(self.dcZ)) self.anc350_instrument.move_scanner('ZPiezoScanner', self.dcZ) def zero_scanners(self): """Put 0V on all scanners. """ self.logger.info('Zero all Scanners.') self.anc350_instrument.zero_scanners() def move(self, direction): """| Here the actual move takes place, after the user clicked on one of the four directional buttons. | The clicked button determines the direction that is chosen. | - For the continuous and step move, that is than converted to 0 or 1. | This is correct as it is written right now, I checked it. | - For the relative move, the direction is than converted in adding a minus sign or not. | - For every kind of move, the self.moving_thread is started, so the stop button can be used and the position label can be updated. | I gave this thread the same name every time, don't know whether that is bad practice. The thread is quit in the stop method. :param direction: direction of move, left, right, up, down :type direction: string """ self.direction = direction self.logger.debug('current direction: ' + direction) #remember axis name that instrument thinks in if 'Z' in self.current_axis: axis_string = 'ZPiezoStepper' else: if self.direction == 'left' or self.direction == 'right': axis_string = 'XPiezoStepper' else: axis_string = 'YPiezoStepper' if self.current_move == 'move absolute': #combine the spinbox and unit combobox user input to a pint quantity self.logger.info('moving to an absolute position') distance = self.gui.doubleSpinBox_distance.value() unit = self.gui.comboBox_unit.currentText() self.logger.debug('axis: ' + axis_string) local_distance = ur(str(distance) + unit) self.logger.debug('to position: ' + str(local_distance)) self.moving_thread = WorkThread(self.anc350_instrument.move_to, axis_string, local_distance) self.moving_thread.start() elif self.current_move == 'move relative': # combine the spinbox and unit combobox user input to a pint quantity # add minussign to communicate correct direction to instrument self.logger.info('moving relative') distance = self.gui.doubleSpinBox_distance.value() unit = self.gui.comboBox_unit.currentText() self.logger.debug('axis:' + axis_string) self.logger.debug('direction: ' + direction) if self.direction == 'right' or self.direction == 'up': local_distance = ur(str(distance) + unit) self.logger.debug(str(local_distance)) elif self.direction == 'left' or self.direction == 'down': local_distance = ur(str(-1 * distance) + unit) self.logger.debug(str(local_distance)) self.moving_thread = WorkThread( self.anc350_instrument.move_relative, axis_string, local_distance) self.moving_thread.start() elif self.current_move == 'continuous' or self.current_move == 'step': # convert direction buttons clicked to direction integers that instrument wants # than move for 1s continuously, since the stop button doesnt work yet if self.direction == 'left': if 'Z' in self.current_axis: direction_int = 0 # correct direction, corresponds to labels closer and away else: direction_int = 1 elif self.direction == 'right': if 'Z' in self.current_axis: direction_int = 1 # correct direction, corresponds to labels closer and away else: direction_int = 0 elif self.direction == 'up': direction_int = 0 elif self.direction == 'down': direction_int = 1 if self.current_move == 'continuous': self.logger.info('moving continuously') self.moving_thread = WorkThread( self.anc350_instrument.move_continuous, axis_string, direction_int) self.moving_thread.start() elif self.current_move == 'step': self.logger.info('making a step') self.anc350_instrument.given_step(axis_string, direction_int, 1) def stop_moving(self): """| Stops movement of all steppers. | The check_if_moving loop in the instrument level checks whether the stop is True, and if so, breaks the loop. | Similar for a loop in the move_continuous in the instrument level, that checks for the stop. | The stop_moving in the instrument level actually stops the device. | Because of the moving_thread that is started in the method move in this class, the loops in the methods in instrument actually keep checking for this stop value. """ self.logger.info('stop moving') self.anc350_instrument.stop = True self.anc350_instrument.stop_moving('XPiezoStepper') self.anc350_instrument.stop_moving('YPiezoStepper') self.anc350_instrument.stop_moving('ZPiezoStepper') if self.moving_thread.isRunning: self.moving_thread.quit() self.anc350_instrument.stop = False
class App(QWidget): # CHANGE THIS F##%% name!!! def __init__(self, motor_hub): """ This init of this class, make sure that the .yml file is correctly configurated, else it will not work, trust me, I am an expert. """ super().__init__() self.title = 'thorlabs motors GUI' self.left = 50 self.top = 50 self.width = 400 self.height = 200 self.grid_layout = QGridLayout() self.setLayout(self.grid_layout) self.motor_hub = motor_hub self.motor_bag = {} self.slider_dict = {} self.position_1_all_motors_dict = {} self.position_2_all_motors_dict = {} self.position_3_all_motors_dict = {} self.velocity_motors_dict = {} self.initUI() def initUI(self): self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) self.motor_bag = self.motor_hub.initialize_available_motors(self.motor_bag) self.make_misc_gui_stuff() self.make_labels() self.make_buttons() self.show() def make_labels(self): #make labels: self.make_save_pos_1_label() self.make_save_pos_2_label() self.make_save_pos_3_label() self.make_recover_1_label() self.make_recover_2_label() self.make_recover_3_label() self.make_x_coordinate_label() self.make_y_coordinate_label() self.make_z_coordinate_label() self.make_x_coordinate_second_label() self.make_y_coordinate_second_label() self.make_z_coordinate_second_label() self.make_motor_x_current_position_label() self.make_motor_y_current_position_label() self.make_motor_z_current_position_label() self.make_use_keyboard_label() self.make_go_faster_label() def make_buttons(self): #make buttons self.make_save_pos_3_button() self.make_save_pos_1_button() self.make_save_pos_2_button() self.make_recover_1_button() self.make_recover_2_button() self.make_recover_3_button() self.make_go_home_button() self.make_go_to_button() self.make_use_keyboard_button() def make_misc_gui_stuff(self): #make all the miscellaneous gui stuff. """ In this method th slider and other miscellaneous gui stuff will be made. The slider get made by adding a tuple with the name of the slider and the name of the thorlabs_motor that the slider will use. """ slider_list = self.motor_hub.make_slider_list() opteller = 1 for slider in slider_list: self.make_slider(lambda: slider[0], slider[1], opteller) opteller += 2 self.make_go_faster_slider() self.make_dropdown_motor() self.make_go_to_input_textfield() def make_save_pos_1_label(self): label = QLabel(self) label.setText("save pos. 1:") self.grid_layout.addWidget(label, 1, 0) def make_save_pos_2_label(self): label = QLabel(self) label.setText("save pos. 2:") self.grid_layout.addWidget(label, 2, 0) def make_save_pos_3_label(self): label = QLabel(self) label.setText("save pos. 3:") self.grid_layout.addWidget(label, 3, 0) def make_recover_1_label(self): label = QLabel(self) label.setText("recover 1:") self.grid_layout.addWidget(label, 1, 2) def make_recover_2_label(self): label = QLabel(self) label.setText("recover 2:") self.grid_layout.addWidget(label, 2, 2) def make_recover_3_label(self): label = QLabel(self) label.setText("recover 3:") self.grid_layout.addWidget(label, 3, 2) def make_x_coordinate_second_label(self): label = QLabel(self) label.setText("x: ") self.grid_layout.addWidget(label, 1, 4) def make_y_coordinate_second_label(self): label = QLabel(self) label.setText("y: ") self.grid_layout.addWidget(label, 2, 4) def make_z_coordinate_second_label(self): label = QLabel(self) label.setText("z: ") self.grid_layout.addWidget(label, 3, 4) def make_x_coordinate_label(self): label = QLabel(self) label.setText("speed\nx: ") self.grid_layout.addWidget(label, 4, 0) def make_y_coordinate_label(self): label = QLabel(self) label.setText("speed\ny: ") self.grid_layout.addWidget(label, 4, 2) def make_z_coordinate_label(self): label = QLabel(self) label.setText("speed\nz: ") self.grid_layout.addWidget(label, 4, 4) def make_motor_x_current_position_label(self): """ update the x position label. The same is done for the y and z in make_motor_y/z_current_position_label """ self.current_motor_x_position_label = QLabel(self) try: self.current_motor_x_position_label.setText(self.motor_bag[self.motor_combobox.currentText()].controller.position()) except Exception: self.current_motor_x_position_label.setText("currently\nunavailable") self.grid_layout.addWidget(self.current_motor_x_position_label, 1, 5) def make_motor_y_current_position_label(self): self.current_motor_y_position_label = QLabel(self) try: self.current_motor_y_position_label.setText(self.motor_bag[self.motor_combobox.currentText()].controller.position()) except Exception: self.current_motor_y_position_label.setText("currently\nunavailable") self.grid_layout.addWidget(self.current_motor_y_position_label, 2, 5) def make_motor_z_current_position_label(self): self.current_motor_z_position_label = QLabel(self) try: self.current_motor_z_position_label.setText(self.motor_bag[self.motor_combobox.currentText()].controller.position()) except Exception: self.current_motor_z_position_label.setText("currently\nunavailable") self.grid_layout.addWidget(self.current_motor_z_position_label, 3, 5) def make_use_keyboard_label(self): self.keyboard_label = QLabel(self) self.keyboard_label.setText("use keyboard\nto control selected\n combobox thorlabs_motor:") self.grid_layout.addWidget(self.keyboard_label, 3, 6) def make_go_faster_label(self): self.go_faster_label = QLabel(self) self.go_faster_label.setText("change\nthorlabs_motor speed:") self.grid_layout.addWidget(self.go_faster_label, 4, 6) #make buttons: def make_save_pos_1_button(self): self.save_1_button = QPushButton('save pos 1', self) self.save_1_button.setToolTip('save the first position') #self, button, color, position_all_motors_dict self.save_1_button.clicked.connect(lambda: self.save_position_for_all_motors(self.save_1_button, "green", self.position_1_all_motors_dict)) self.grid_layout.addWidget(self.save_1_button, 1, 1) def make_save_pos_2_button(self): self.save_2_button = QPushButton('save pos 2', self) self.save_2_button.setToolTip('save the second position') self.save_2_button.clicked.connect(lambda: self.save_position_for_all_motors(self.save_2_button, "yellow", self.position_2_all_motors_dict)) self.grid_layout.addWidget(self.save_2_button, 2, 1) def make_save_pos_3_button(self): self.save_3_button = QPushButton('save pos 3', self) self.save_3_button.setToolTip('save the third position') self.save_3_button.clicked.connect(lambda: self.save_position_for_all_motors(self.save_3_button, "red", self.position_3_all_motors_dict)) self.grid_layout.addWidget(self.save_3_button, 3, 1) def make_recover_1_button(self): button = QPushButton('recover 1', self) button.setToolTip('recover the first position') button.clicked.connect(lambda: self.recover_position_all_motors(self.position_1_all_motors_dict)) self.grid_layout.addWidget(button, 1, 3) def make_recover_2_button(self): button = QPushButton('recover 2', self) button.setToolTip('recover the second position') button.clicked.connect(lambda: self.recover_position_all_motors(self.position_2_all_motors_dict)) self.grid_layout.addWidget(button, 2, 3) def make_recover_3_button(self): button = QPushButton('recover 3', self) button.setToolTip('recover the third position') button.clicked.connect(lambda: self.recover_position_all_motors(self.position_3_all_motors_dict)) self.grid_layout.addWidget(button, 3, 3) def make_go_home_button(self): self.go_home_button = QPushButton('go home', self) self.go_home_button.setToolTip('go to home position') self.go_home_button.clicked.connect(self.go_home_motor) self.grid_layout.addWidget(self.go_home_button, 1, 6) def make_go_to_button(self): self.move_button = QPushButton('move to in um', self) self.move_button.setToolTip('move to given input') self.move_button.clicked.connect(self.go_to_input) self.grid_layout.addWidget(self.move_button, 2, 6) def make_use_keyboard_button(self): self.use_keyboard_button = QPushButton('use keyboard', self) self.use_keyboard_button.setToolTip('use keyboard to control motors') self.use_keyboard_button.clicked.connect(self.control_motor_with_keyboard) self.grid_layout.addWidget(self.use_keyboard_button, 3, 7) #make misc gui stuff: def make_slider(self, slider, motor_name, opteller): """ In this method sliders get made automatically. This done by getting the name of the slider an setting it in a slider_dict. The rest of the parameters get set in this method. :param slider: a slider name :type string :param motor_name: the name of the thorlabs_motor which connects with the slider :type string :param opteller: indication on which grid the slider must be set :type int """ self.slider_ding = QSlider(Qt.Vertical, self) self.slider_ding.setFocusPolicy(Qt.StrongFocus) self.slider_ding.setTickPosition(QSlider.TicksBothSides) self.slider_ding.setMinimum(1) self.slider_ding.setMaximum(9) self.slider_ding.setValue(5) self.slider_ding.setTickInterval(1) self.slider_ding.setSingleStep(1) self.slider_ding.sliderReleased.connect(lambda: self.set_slider_to_middle(slider, motor_name)) self.slider_ding.sliderMoved.connect(lambda: self.make_slider_motor_move(slider, motor_name)) self.slider_dict[slider] = self.slider_ding self.grid_layout.addWidget(self.slider_dict[slider], 4, opteller) def set_slider_to_middle(self, slider, motor_name): """ In this method an slider is set to the middle. This is done through connecting a signal and slot. :param slider_object: an slider that must be set to it's middle :type QSlider :param motor_name: the name of the thorlabs_motor to stop :type string """ self.slider_dict[slider].setValue(5) self.motor_bag[motor_name].controller.stop_profiled() def make_slider_motor_move(self, slider, motor_name): """ In this method the thorlabs_motor moves if the slider is moved. :param slider_object: the slider that is being moved :type QSlider :param motor_name: the name of the thorlabs_motor to move :type string """ if self.slider_dict[slider].value() > 5: #moving forward self.motor_bag[motor_name].controller.move_velocity(2) elif self.slider_dict[slider].value() < 5: #moving reverse self.motor_bag[motor_name].controller.move_velocity(1) def make_go_faster_slider(self): """ """ self.go_faster_slider = QSlider(Qt.Horizontal, self) self.go_faster_slider.setFocusPolicy(Qt.StrongFocus) self.go_faster_slider.setTickPosition(QSlider.TicksBelow) self.go_faster_slider.setMinimum(0) self.go_faster_slider.setMaximum(10) self.go_faster_slider.setValue(0) self.go_faster_slider.setTickInterval(1) self.go_faster_slider.setSingleStep(1) self.go_faster_slider.sliderReleased.connect(self.change_motor_speed) self.grid_layout.addWidget(self.go_faster_slider, 4, 7) def change_motor_speed(self): #print("for thorlabs_motor: "+str(self.motor_combobox.currentText())+" the limits are: "+str(self.motor_bag[self.motor_combobox.currentText()].controller.get_velocity_parameter_limits())) output = self.motor_bag[self.motor_combobox.currentText()].controller.get_velocity_parameters() # minimum velocity = output[0] # acceleration = ouput[1] # maximum velocity = output[2] if not self.motor_combobox.currentText() in self.velocity_motors_dict: #setting the original highest value of the thorlabs_motor to prevent degrading in speed. self.velocity_motors_dict[self.motor_combobox.currentText()] = output[2] if (output[0] - output[2]) == 0: #the numbers are equal meaning that the speed cannot change print("you cannot change the speed of this thorlabs_motor") else: #the speed can change total_speed = self.velocity_motors_dict[self.motor_combobox.currentText()] - output[0] tenth_of_total_speed = total_speed / 10 print(tenth_of_total_speed) slider_value = self.go_faster_slider.value() print(slider_value) new_speed = slider_value * tenth_of_total_speed print("the max speed: "+str(self.velocity_motors_dict[self.motor_combobox.currentText()])+"the new speed "+str(new_speed)) print("-"*40) if new_speed < output[2] and new_speed > output[0]: self.motor_bag[self.motor_combobox.currentText()].controller.set_velocity_parameters(output[0], output[1], new_speed) def make_dropdown_motor(self): list_with_motors = [] self.motor_combobox = QComboBox(self) #these are all the available motors: for index in self.motor_bag.keys(): list_with_motors.append(str(index)) self.motor_combobox.addItems(list_with_motors) self.motor_combobox.currentIndexChanged.connect(self.set_current_motor_label) self.grid_layout.addWidget(self.motor_combobox, 1, 7) def make_go_to_input_textfield(self): self.input_textfield = QLineEdit(self) self.input_textfield.setText("0.01") self.grid_layout.addWidget(self.input_textfield, 2, 7) def set_current_motor_label(self): """ The position for the x, y, z motor will be set in this method. If another motor is found the x label will change. Why x...because, IDK. What else to do. """ if self.motor_combobox.currentText() == "zMotor": self.current_motor_x_position_label.setText(str(round(self.motor_bag[self.motor_combobox.currentText()].controller.position, 2))) elif self.motor_combobox.currentText() == "yMotor": self.current_motor_y_position_label.setText(str(round(self.motor_bag[self.motor_combobox.currentText()].controller.position, 2))) elif self.motor_combobox.currentText() == "testMotor": self.current_motor_z_position_label.setText(str(round(self.motor_bag[self.motor_combobox.currentText()].controller.position, 2))) else: #this is a thorlabs_motor that is not the zMotor, yMotor or the testMotor, so #let's set the x position to something else self.current_motor_x_position_label.setText(str(round(self.motor_bag[self.motor_combobox.currentText()].controller.position, 2))) def go_home_motor(self): selected_motor = str(self.motor_combobox.currentText()) self.motor_bag[selected_motor].controller.move_home(True) self.set_current_motor_label() def go_to_input(self): selected_motor = str(self.motor_combobox.currentText()) try: go_to_input = self.input_textfield.text() * ur('micrometer') self.motor_bag[selected_motor].move_absolute(float(go_to_input.magnitude)) self.set_current_motor_label() except ValueError: print("The input is not a float, change this") return def on_press(self, key): """ In this method if the w is pressed the thorlabs_motor selected in the combobox will move forward or if s is pressed the thorlabs_motor will move backward. The w and s are written as: "'w'"/"'s'" because of syntacs. """ if str(key) == "'w'": #forward self.set_current_motor_label() self.motor_bag[self.motor_combobox.currentText()].controller.move_velocity(2) self.set_current_motor_label() elif str(key) == "'s'": #backwards self.set_current_motor_label() self.motor_bag[self.motor_combobox.currentText()].controller.move_velocity(1) self.set_current_motor_label() def on_release(self, key): """ In this method if the w or s is released the thorlabs_motor will stop moving. If q is released the keyboard mode stops. """ if str(key) == "'w'" or str(key) == "'s'": #stop the thorlabs_motor from going self.motor_bag[self.motor_combobox.currentText()].controller.stop_profiled() self.set_current_motor_label() elif str(key) == "'q'": # Stop listener if self.worker_thread.isRunning(): self.set_current_motor_label() self.worker_thread.quit() self.worker_thread.wait() return False def control_motor_with_keyboard(self): """ In this method with the Listener object you can press a button on the keyboard and with that input a thorlabs_motor will move. """ #set text of keyboard_label to using keyboard self.keyboard_label.setText("using keyboard/npress esc to exit") # Collect events until released self.worker_thread = WorkThread(self.create_keyboard_listener) self.worker_thread.start() #set the text back to you can use the keyboard. self.keyboard_label.setText("use keyboard\nto control selected\n combobox thorlabs_motor:") def create_keyboard_listener(self): with Listener(on_press=self.on_press, on_release=self.on_release) as listener: listener.join() def save_position_for_all_motors(self, button, color, position_all_motors_dict): #make sure the user knows the button is pressed by setting it to a different color button.setStyleSheet("background-color: "+color) #get positions for motor in self.motor_bag.items(): #thorlabs_motor[0] == serial nummer #thorlabs_motor[1] == Thorlabs thorlabs_motor instance try: position = motor[1].controller.position except Exception: #the thorlabs_motor position has not been found, could be because it is a #piezo thorlabs_motor or because the software is not running as expected. print("for thorlabs_motor: "+ str(motor[0]) +" the position has not been set") position = None position_all_motors_dict[motor[0]] = position def recover_position_all_motors(self, position_all_motors_dict): #set position of motors #(this only works if the position of the motors is from the home position): #so, that should be changed. if bool(position_all_motors_dict) == False: print("the positions have not been set!") return for motor in self.motor_bag.items(): #thorlabs_motor[0] == serial nummer #thorlabs_motor[1] == Thorlabs thorlabs_motor instance retrieved_position = position_all_motors_dict[motor[0]] if retrieved_position != None and retrieved_position != motor[1].controller.position: motor[1].controller.set_position = float(retrieved_position)
def on_press_move(self, key): """ In this method if the w is pressed the thorlabs_motor selected in the combobox will move forward or if s is pressed the thorlabs_motor will move backward. The w and s are written as: "'w'"/"'s'" because of syntacs. """ if str(key) == "'y'": self.moving_one_thread = WorkThread(self.wp_one.move_velocity, 2, True) self.moving_one_thread.start() elif str(key) == "'u'": self.moving_one_thread = WorkThread(self.wp_one.move_velocity, 1, True) self.moving_one_thread.start() elif str(key) == "'h'": self.moving_two_thread = WorkThread(self.wp_two.move_velocity, 2, True) self.moving_two_thread.start() elif str(key) == "'j'": self.moving_two_thread = WorkThread(self.wp_two.move_velocity, 1, True) self.moving_two_thread.start() elif str(key) == "'n'": self.moving_two_thread = WorkThread(self.wp_two.move_velocity, 2, True) self.moving_one_thread = WorkThread(self.wp_one.move_velocity, 2, True) self.moving_one_thread.start() self.moving_two_thread.start() elif str(key) == "'m'": self.moving_two_thread = WorkThread(self.wp_two.move_velocity, 1, True) self.moving_one_thread = WorkThread(self.wp_one.move_velocity, 1, True) self.moving_one_thread.start() self.moving_two_thread.start()
class BeamFlagsGui(QWidget): """ Gui for beam blocker flags. Requires hyperion.instrument.misc.beam_flags_instr/BeamFlagsInstr instrument as input. :param beam_flags_instr: instrument object to control :type beam_flags_instr: an instance of the class """ def __init__(self, beam_flags_instr, also_close_output=False): """ Gui for beam blocker flags. :param beam_flags_instr: The beam flags instrument object to create the gui for :type beam_flags_instr: BeamFlagsInstr object """ super().__init__() self.logger = logging.getLogger(__name__) self.left = 400 self.top = 400 self.width = 240 self.height = 80 self.bfi = beam_flags_instr self.bf_settings = self.bfi.settings['gui_flags'] self.all_labels = {} if 'name' in self.bfi.settings: self.title = self.bfi.settings['name'] else: self.title = 'Beam Flags' self.red_char = self.bfi.settings['flag_states']['red'] self.green_char = self.bfi.settings['flag_states']['green'] self.red_color = self.bfi.settings['gui_red_color'] self.green_color = self.bfi.settings['gui_green_color'] self.initUI() # Start timer to repeatedly pull the current state of the toggle switches: self.logger.info('Starting timer thread') # self._busy = False if 'gui_state_update_ms' in self.bfi.settings: self.indicator_update_time = self.bfi.settings[ 'gui_state_update_ms'] else: self.indicator_update_time = 100 # ms #Pay attention: using the Qtimer makes the gui very slow #Instead, use a workthread self.timer_mode = False if self.timer_mode: self.timer = QTimer() self.timer.timeout.connect(self.update_label_states) self.timer.start(self.indicator_update_time) else: self._thread = WorkThread(self.update_wrapper) self._thread.start(priority=QThread.LowestPriority) def update_wrapper(self): while True: sleep(self.indicator_update_time / 1000.0) if not self._busy: self.update_label_states() else: self.logger.warning( 'Still busy: skipping checking for manual flag changes') def initUI(self): """ Create all the gui elements and connect signals to methods. Adds 'state' and 'label' key and value to each flag in the settings dict. Reads the current state from the device and sets 'state' key and gui accordingly. """ self.logger.info('Creating gui elements') self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) layout = QGridLayout() for index, (flag_id, gui_flag) in enumerate(self.bf_settings.items()): self.logger.info("Adding flag '{}' to gui".format(flag_id)) state = self.bfi.get_specific_flag_state(flag_id) self.bf_settings[flag_id]['state'] = state label = QLabel('') self.all_labels[flag_id] = label #self.bf_settings[flag_id]['label'] = label #this will save a python qt object in the yml file = wrong button = QPushButton(gui_flag['name'], self) button.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) label.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) button.pressed.connect(lambda x=flag_id: self.button_clicked(x)) if 'shortkey' in gui_flag: shortk = gui_flag['shortkey'] shortk_tip = ' (' + shortk + ')' button.setText(gui_flag['name'] + shortk_tip) shortcut = QShortcut( QKeySequence(shortk), self) # not sure if this will work in our modular design shortcut.activated.connect( lambda x=flag_id: self.button_clicked(x)) else: shortk_tip = '' button.setStatusTip('Toggle ' + gui_flag['name'] + shortk_tip) label.setAlignment(Qt.AlignCenter) self.set_label_state(flag_id) layout.addWidget(button, index, 0) layout.addWidget(label, index, 1) self.setLayout(layout) self.show() def update_label_states(self): """ Asks device for current states of all beam flags and updates state key and gui if it has changed.""" if self._busy: self.logger.warning() return self._busy = True if self.bfi.passive_update_from_manual_changes(): for flag_id in self.bf_settings.keys(): state = self.bfi.get_specific_flag_state(flag_id) if state != self.bf_settings[flag_id]['state']: self.logger.debug( "Manual state change detected of switch '{}': '{}'". format(flag_id, state)) self.bf_settings[flag_id]['state'] = state self.set_label_state(flag_id) self._busy = False def set_label_state(self, flag_id, keep_busy_flag=False): """ Sets gui label identified by flag_id to the corresponding value in the state key in settings.""" self._busy = True label = self.all_labels[flag_id] state = self.bf_settings[flag_id]['state'] self.logger.info("Set flag '{}' to '{}'".format(flag_id, state)) if state == self.red_char: label.setStyleSheet('background-color: ' + self.red_color) label.setText(self.bf_settings[flag_id]['red_name']) elif state == self.green_char: label.setStyleSheet('background-color: ' + self.green_color) label.setText(self.bf_settings[flag_id]['green_name']) else: self.logger.warning('received state is unknown') self._busy = keep_busy_flag def button_clicked(self, flag_id): """ The gui buttons connect to this function. It toggles the state flag of the corresponding beam flag in the settings dict. It creates and runs a thread that sets the state of the beam flag on the instrument. """ self._busy = True self.logger.debug("Button of flag '{}' clicked".format(flag_id)) state = self.bf_settings[flag_id]['state'] if state == self.red_char: state = self.green_char elif state == self.green_char: state = self.red_char else: self.logger.warning('unknown state in internal dictionary') self.bf_settings[flag_id]['state'] = state self.set_label_state(flag_id, keep_busy_flag=True) self.bfi.set_specific_flag_state(flag_id, state) # Actually toggle the flag self._busy = False
class OsaGui(QWidget): """ OSA Gui class. It builds the GUI for the OSA instrument. """ def __init__(self, instr, draw): """ Init of the class. The class needs an instance of the osa instrument. """ super().__init__() self.title = 'Osa gui' self.left = 50 #how many pixels are away from the left of the GUI self.top = 50 #how many pixels are away form the top of the GUI self.width = 700 #how many pixels in width the GUI is self.height = 350 #how many pixels in height the GUI is self.logger = logging.getLogger(__name__) self.logger.info('Class App created') self.layout = QGridLayout() self.setLayout(self.layout) self.instr = instr self.draw = draw self.initUI() def initUI(self): """ This method initialises all the QWidgets needed. """ self.set_gui_constructor() self.set_textboxs() #only works if the pyvisa bug is fixed #self.set_textboxs_to_osa_machine_values() self.set_labels() self.set_submit_button() self.set_recommended_sample_points_button() self.show() def set_gui_constructor(self): """ a constructor to set the properties of the GUI in""" self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) #self.statusBar().showMessage("Just some text") def set_recommended_sample_points_button(self): """calculate the recommended sample_points""" button_calculate_recommended_sample_points = QPushButton('get recommended sample_points', self) button_calculate_recommended_sample_points.setToolTip( "click this button to \n get the recommended sample_points") self.layout.addWidget(button_calculate_recommended_sample_points, 11, 0) button_calculate_recommended_sample_points.clicked.connect(self.on_click_recommended) def set_submit_button(self): """code to make the submit button """ self.button_submit = QPushButton('submit', self) self.button_submit.setToolTip('You are hovering over the button, \n what do you expect?') self.layout.addWidget(self.button_submit, 12, 0) self.button_submit.clicked.connect(self.on_click_submit) def set_labels(self): self.set_start_wav_label() self.set_end_wav_label() self.set_optical_resolution_label() self.set_sample_points_label() self.set_sensitivity_label() def set_sensitivity_label(self): # set the sensitivity label self.layout.addWidget(QLabel("the sensitivity"), 8, 0) def set_sample_points_label(self): # set the sample points label self.layout.addWidget(QLabel("the amount of sample points"), 6, 0) def set_optical_resolution_label(self): # set the optical resolution label self.layout.addWidget(QLabel("the optical resolution in stepvalue of nm"), 4, 0) def set_end_wav_label(self): # set the end_wav label self.layout.addWidget(QLabel("the end wavelength, from 600.00 nm to 1750.00 nm"), 2, 0) def set_start_wav_label(self): # set the start_wav label self.layout.addWidget(QLabel("the start wavelength, from 600.00 nm to 1750.00 nm"), 0, 0) def set_textboxs(self): self.set_start_wav_textbox() self.set_end_wav_text_box() self.set_optical_resolution_textbox() self.set_sample_points_textbox() self.set_sensitivity_dropdown() def set_sensitivity_dropdown(self): """"code to make the sensitivity_dropdown """ self.dropdown_sensitivity = QComboBox(self) self.dropdown_sensitivity.addItems( ["sensitivity normal range", "sensitivity normal range automatic", "sensitivity medium range", "sensitivity high 1 range", "sensitivity high 2 range", "sensitivity high 3 range"]) self.layout.addWidget(self.dropdown_sensitivity, 9, 0) def set_sample_points_textbox(self): # this is the sample_points_textbox self.textbox_sample_points = QLineEdit(self) self.layout.addWidget(self.textbox_sample_points, 7, 0) def set_optical_resolution_textbox(self): """"code to make the optical_resolution dropdown""" self.dropdown_optical_resolution = QComboBox(self) self.dropdown_optical_resolution.addItems(["0.01", "0.02", "0.05", "0.1", "0.2", "0.5", "1.0", "2.0", "5.0"]) self.layout.addWidget(self.dropdown_optical_resolution, 5, 0) def set_end_wav_text_box(self): # this is the end_wav_textbox self.textbox_end_wav = QLineEdit(self) self.layout.addWidget(self.textbox_end_wav, 3, 0) def set_start_wav_textbox(self): # this is the start_wav_textbox self.textbox_start_wav = QLineEdit(self) self.layout.addWidget(self.textbox_start_wav, 1, 0) def get_chosen_optical_resolution(self): return self.dropdown_optical_resolution.currentText() def get_chosen_sensitivity(self): return self.dropdown_sensitivity.currentText() def on_click_recommended(self): """"when the recommend sample points button is clicked the textfield will be set empty and the recommend sample points will be calculated. using the formula: 1 + (2*(end_wav - start_wav)/float(optical_resolution)) """ print("the recommended sample points will be calculated ") if self.instr.controller._is_initialized: self.instr.controller.perform_single_sweep() else: print("pyvisa does not work like intended") print(id(self.instr.controller._osa)) self.textbox_sample_points.setText("") #check if the textboxs are not empty if self.is_start_wav_not_empty() and self.is_end_wav_not_empty(): #all fields are filled self.get_recommended_sample_points() else: #some parameter are missing in one of the textboxs self.error_message_not_all_fields_are_filled() def is_sample_points_not_empty(self): if self.textbox_sample_points.text() != "": return True else: return False def is_end_wav_not_empty(self): if self.textbox_end_wav.text() != "": return True else: return False def is_start_wav_not_empty(self): if self.textbox_start_wav.text() != "": return True else: return False def error_message_not_all_fields_are_filled(self): message = "You did not type the\n" if not self.is_start_wav_not_empty(): message += " start wavelength value\n" if not self.is_end_wav_not_empty(): message += " end wavelength value\n" if not self.is_optical_resolution_not_empty(): message += " optical resolution value\n" if not self.is_sample_points_not_empty(): message += " sample points value\n" message += "you should set the specified value\n" QMessageBox.question(self, 'you did something wrong', message, QMessageBox.Ok, QMessageBox.Ok) def get_values_from_textboxs(self): # get all the values from the textfields start_wav = self.get_start_wav() end_wav = self.get_end_wav() optical_resolution = self.get_optical_resolution() sample_points = self.get_sample_points() return end_wav, optical_resolution, sample_points, start_wav def get_sample_points(self): sample_points = self.textbox_sample_points.text() return int(sample_points) def get_optical_resolution(self): optical_resolution = self.dropdown_optical_resolution.currentText() return float(optical_resolution) def get_end_wav(self): end_wav = self.textbox_end_wav.text() return Q_(end_wav) def get_start_wav(self): start_wav = self.textbox_start_wav.text() return Q_(start_wav) def get_recommended_sample_points(self): self.textbox_sample_points.setText( str(int(1 + (2 * (Q_(self.textbox_end_wav.text()) - Q_(self.textbox_start_wav.text())).m_as('nm') / float( self.dropdown_optical_resolution.currentText()))))) def on_click_submit(self): """" In this method there is a request to do a single sweep and plot the data. """ if self.instr.controller._is_initialized: self.instr.controller.perform_single_sweep() else: print("pyvisa does not work like intended") print(id(self.instr.controller._osa)) self.logger.info('submit button clicked') self.get_submit_button_status() if self.is_start_wav_not_empty() and self.is_end_wav_not_empty() and self.is_sample_points_not_empty(): #self.logger.info('fields not empty, grabbing fields') end_wav, optical_resolution, sample_points, start_wav = self.get_values_from_textboxs() #check if conditions for a single sweep are met if self.instr.is_start_wav_value_correct(start_wav) and self.instr.is_end_wav_value_correct(end_wav) and self.instr.is_end_wav_bigger_than_start_wav(start_wav,end_wav): #all tests are succesfull. now the rest of the code may be executed. # set the settings of the osa machine print('setting parameters in instrument') print('set_parameters_for_osa_machine( ... )') self.set_parameters_for_osa_machine(end_wav, optical_resolution, sample_points, start_wav) #try: self.make_thread() #except: # print("exception occured: "+str(sys.exc_info()[0])) self.plot_data() self.get_output_message(end_wav, optical_resolution, sample_points, start_wav) self.set_textboxs_to_osa_machine_values() else: self.error_message_not_all_fields_are_filled() def set_parameters_for_osa_machine(self, end_wav, optical_resolution, sample_points, start_wav): #print(self.instr.start_wav) #print(self.instr.end_wav) #print(self.instr.optical_resolution) #print(self.instr.sample_points) self.instr.start_wav = Q_(start_wav) self.instr.end_wav = Q_(end_wav) self.instr.optical_resolution = float(optical_resolution) self.instr.sample_points = int(sample_points) def make_thread(self): print('starting sweep') print(id(self.instr.controller._osa)) self.worker_thread = WorkThread(self.instr.take_spectrum) print("starting") self.worker_thread.start() def plot_data(self): """ Plot the data. On the x axis there is the wavelength and the y axis is the spectrum data. """ wav = self.instr.wav spec = self.instr.spec self.draw.random_plot.plot(wav, spec, clear=True) def set_textboxs_to_osa_machine_values(self): # set the textboxs to the value from the osa machine self.textbox_start_wav.setText("{} nm".format(self.instr.start_wav.m_as('nm'))) self.textbox_end_wav.setText("{} nm".format(self.instr.end_wav.m_as('nm'))) self.textbox_sample_points.setText("{}".format(int(self.instr.sample_points))) def get_output_message(self, end_wav, optical_resolution, sample_points, start_wav): # make a textbox to display given values. QMessageBox.question(self, 'What is this, a response?', "You typed: " + str(start_wav) + "\n" + str(end_wav) + "\n" + str(optical_resolution) + "\n" + str(sample_points), QMessageBox.Ok, QMessageBox.Ok) def get_submit_button_status(self): # when the button is clicked this method will be executed print('button says something') self.statusBar().showMessage("you have clicked the button, nothing happens(yet)")
def on_click(self): #initialize a long(couple of seconds) test function. self.worker_thread = WorkThread(self.go_to_sleep) self.worker_thread.start()