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)
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 Thorlabs_motor_GUI(BaseGui): """ | The initialization of the single_thorlabs gui. | 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_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 initUI(self): self.logger.debug('Setting up the Single Thorlabs Motor GUI') self.setWindowTitle(self.title) groupBox = QGroupBox() self.overall_layout.addWidget(groupBox) groupBox.setStyleSheet("QGroupBox {border: 1px solid green; border-radius: 9px;}") self.grid_layout = QGridLayout() groupBox.setLayout(self.grid_layout) self.make_buttons() self.make_boxes() self.fill_up_widget() self.show() def make_buttons(self): """This method makes all the buttons in this GUI and connects them to the correct methods. """ 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.move_button = QPushButton('move to', self) self.move_button.setToolTip('move to given input') self.move_button.clicked.connect(self.go_to_input) self.keyboard_button = QPushButton("keyboard", self) self.keyboard_button.setToolTip("use the keyboard to move the thorlabs_motor,\nit works great") self.keyboard_button.clicked.connect(self.use_keyboard) self.save_pos_button = QPushButton("save pos", self) self.save_pos_button.setToolTip('save the current position of the thorlabs_motor') self.save_pos_button.clicked.connect(self.save_position) self.recover_pos_button = QPushButton("recover pos", self) self.recover_pos_button.setToolTip("recover the set position of the thorlabs_motor") self.recover_pos_button.clicked.connect(self.recover_position) self.stop_button = QPushButton("stop moving", self) self.stop_button.setToolTip("stop any moving") self.stop_button.clicked.connect(self.stop_moving) self.stop_button.setStyleSheet("background-color: red") def make_boxes(self): """This method makes the labels, spinbox and combobox to make them available for the rest of this class, and connects them to the correct methods. """ self.current_motor_position_label = QLabel(self) try: self.current_motor_position_label.setText(self.motor.position()) except Exception: self.current_motor_position_label.setText("currently/nunavailable") self.save_label = QLabel() self.save_label.setText('saved:') self.distance_spinbox = QDoubleSpinBox(self) if self.motor.kind_of_device == 'waveplate': self.distance_spinbox.setValue(self.distance.m_as('mm')) self.distance = self.distance.m_as('mm') * ur('degrees') self.min_distance = 0 * ur('degrees') self.max_distance = 360 * ur('degrees') else: self.distance_spinbox.setValue(self.distance.m_as('mm')) self.distance_spinbox.setMinimum(-999999999) # otherwise you cannot reach higher than 99 self.distance_spinbox.setMaximum(999999999) self.distance_spinbox.valueChanged.connect(self.set_distance) self.unit_combobox = QComboBox(self) if self.motor.kind_of_device == 'waveplate': self.unit_combobox.addItems(["degrees"]) self.unit_combobox.setCurrentText('degrees') self.unit_combobox.setEnabled(False) else: self.unit_combobox.addItems(["nm", "um", "mm"]) self.unit_combobox.setCurrentText('mm') self.unit_combobox.currentTextChanged.connect(self.set_distance) self.set_current_motor_position_label() def fill_up_widget(self): """This method puts all the widgets in the correct positions in the grid. """ self.grid_layout.addWidget(self.go_home_button, 0, 0) self.grid_layout.addWidget(self.move_button, 1, 0) self.grid_layout.addWidget(QLabel("use keyboard\n(w/s, q to quit)"), 2, 0) self.grid_layout.addWidget(self.current_motor_position_label, 0, 1) self.grid_layout.addWidget(self.distance_spinbox, 1, 1) self.grid_layout.addWidget(self.keyboard_button, 2, 1) self.grid_layout.addWidget(self.save_label, 0, 3) self.grid_layout.addWidget(self.unit_combobox, 1, 3) self.grid_layout.addWidget(self.save_pos_button, 0, 4) self.grid_layout.addWidget(self.recover_pos_button, 1, 4) self.grid_layout.addWidget(self.stop_button, 2, 4) 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 = self.motor.current_position self.current_motor_position_label.setText(str(round(self.current_position, 2))) #---------------------------------------------------------------------------------------------------------------------- def set_distance(self): """| Reads the value that the user filled in the spinbox and combines it with the unit to make a pint quantity. | The pint quantity is saved in self.distance. | Also compares the wanted distance with the maximum and minimum values, | which are set in the init or changed to degrees in make_distance_spinbox. | If the user input is too high or low, the spinbox is changed to the maximum or minimum value. """ value = self.distance_spinbox.value() unit = self.unit_combobox.currentText() local_distance = ur(str(value)+unit) self.logger.debug('local distance value {}'.format(self.distance)) self.logger.debug("{}".format(value > self.max_distance.m_as(unit))) if value > self.max_distance.m_as(unit): self.logger.debug('value too high') local_max = self.max_distance.to(unit) self.logger.debug(str(local_max)) self.distance_spinbox.setValue(local_max.m_as(unit)) elif value < self.min_distance.m_as(unit): self.logger.debug('value too low') local_min = self.min_distance.to(unit) self.distance_spinbox.setValue(local_min.m_as(unit)) self.distance = local_distance self.logger.debug('dictionary distance changed to: ' + str(self.distance)) def go_home_motor(self): """Starts a thread and communicates to the instrument to move home. The instrument loop will take care of updating the current position and checking whether self.stop is True or False. """ self.moving_thread = WorkThread(self.motor.move_home, True) self.moving_thread.start() #self.motor.move_home(True) #self.set_current_motor_position_label() 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 save_position(self): """Saves the current position for the user. Makes sure the user knows the button is pressed by setting it to a different color. Gives an error if 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. """ self.save_pos_button.setStyleSheet("background-color: green") try: self.saved_position = self.motor.position() self.logger.debug(str(round(self.saved_position,2))) self.save_label.setText("saved: " + str(round(self.saved_position,2))) except Exception: self.logger.warning("the position has not been set yet") self.saved_position = None 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 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 create_keyboard_listener(self): with Listener(on_press=self.on_press, on_release=self.on_release) as listener: listener.join() 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_position_label() self.motor.controller.move_velocity(2) self.set_current_motor_position_label() elif str(key) == "'s'": #backwards self.set_current_motor_position_label() self.motor.controller.move_velocity(1) self.set_current_motor_position_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.stop_moving() self.set_current_motor_position_label() elif str(key) == "'q'": # Stop listener if self.worker_thread.isRunning(): self.set_current_motor_position_label() self.worker_thread.quit() self.worker_thread.wait() return False def stop_moving(self): """| Stops movement of the current cube. | The moving_loop method in the instrument level checks whether the stop is True, and if so, breaks the loop. | The stop_moving method in the instrument 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.motor.stop = True self.motor.stop_moving() if self.moving_thread.isRunning: self.logger.debug('Moving thread was running.') self.moving_thread.quit() self.motor.stop = False