from PyQt6.QtGui import * from PyQt6.QtWidgets import * from PyQt6.QtCore import * import multiprocessing import multiprocessing.managers import time from datetime import datetime import traceback,sys,os import numpy as np import pyqtgraph as pg from scripts import import_txt import pyqtgraph.opengl as gl from scipy.optimize import curve_fit import collections # Get the current script's directory current_dir = os.path.dirname(os.path.abspath(__file__)) # Get the parent directory by going one level up parent_dir = os.path.dirname(current_dir) # Add the parent directory to sys.path sys.path.append(parent_dir) from design_files.BT_3D_Plot_design import Ui_MainWindow class Vector: def __init__(self, vec_0, vec_direction, length, width, color, view): #transform axis such that it fits the real setup vec_direction_new = np.array([1,1,1], dtype=float) vec_direction_new[0] = vec_direction[1] vec_direction_new[1] = vec_direction[0] #plus or minus on the right side? vec_direction_new[2] = vec_direction[2] self.vec_0 = vec_0 self.vec_direction = vec_direction_new self.length = length self.width = width self.color = color self.view = view #After initialization draw Vector self.draw() def Calc_Vec1_Vec2(self): #Calculate start- and end-vector of the vector to be drawn vec_direction_length = np.sqrt(self.vec_direction[0]**2+self.vec_direction[1]**2+self.vec_direction[2]**2) if vec_direction_length == 0: direction_normalized = np.array([0,0,0]) else: direction_normalized = self.vec_direction/vec_direction_length self.vec1 = self.vec_0 - direction_normalized*self.length/2 self.vec2 = self.vec_0 + direction_normalized*self.length/2 self.headvec1 = np.empty(shape=(0,3)) self.headvec2 = np.empty(shape=(0,3)) Headsteps = 5 headlength = self.length/5 headsegment = headlength/Headsteps for i in range(0,Headsteps): vec1x = self.vec2 + i*headsegment*direction_normalized vec2x = self.vec2 + (i+1)*headsegment*direction_normalized self.headvec1 = np.append(self.headvec1, [vec1x], axis=0) self.headvec2 = np.append(self.headvec2, [vec2x], axis=0) def draw(self): self.Calc_Vec1_Vec2() #Draw Line of Vector linedata = np.array([self.vec1,self.vec2]) self.Line = gl.GLLinePlotItem() self.Line.setData(pos=linedata, mode="lines", color=np.array([self.color,self.color]), width=self.width) self.Line.setGLOptions('translucent') self.view.addItem(self.Line) #Draw Head of Vector self.headline1 = gl.GLLinePlotItem() self.headline2 = gl.GLLinePlotItem() self.headline3 = gl.GLLinePlotItem() self.headline4 = gl.GLLinePlotItem() self.headline5 = gl.GLLinePlotItem() self.headlinearray = [self.headline1, self.headline2, self.headline3, self.headline4, self.headline5] i=0 for headline in self.headlinearray: headlinedata = np.array([self.headvec1[i,:],self.headvec2[i,:]]) headlinewidth = (1-i/5)*20 headline.setData(pos=headlinedata, mode="lines", color=np.array([self.color,self.color]), width=headlinewidth) headline.setGLOptions('translucent') self.view.addItem(headline) i=i+1 def set_changes(self): #Everytime a setting has changed this function should be called to update changes in the plot #ChangeLine linedata = np.array([self.vec1,self.vec2]) self.Line.setData(pos=linedata, mode="lines", color=np.array([self.color,self.color]), width=self.width) #ChangeHead i=0 for headline in self.headlinearray: # print(str(np.shape(self.headvec1)) +" " +str(np.shape(self.headvec2))) headlinedata = np.array([self.headvec1[i,:],self.headvec2[i,:]]) headlinewidth = (1-i/5)*20 headline.setData(pos=headlinedata, mode="lines", color=np.array([self.color,self.color]), width=headlinewidth) i=i+1 def change_direction(self, vec_direction): #transform axis such that it fits the real setup vec_direction_new = np.array([1,1,1], dtype=float) vec_direction_new[0] = vec_direction[1] vec_direction_new[1] = vec_direction[0] #plus or minus on the right side? vec_direction_new[2] = vec_direction[2] self.vec_direction = vec_direction_new self.Calc_Vec1_Vec2() self.set_changes() def change_length(self, length): self.length = length self.Calc_Vec1_Vec2() self.set_changes() def change_width(self, width): self.width = width self.Line.setData(width=self.width) self.set_changes() def change_color(self, color): self.color = color self.Line.setData(color=np.array([self.color,self.color])) self.set_changes() class Plane: def __init__(self, vec0, vec1, vec2, color, view): #vec0 base edge of plane, vec1 and vec2 edges relative to vec0 self.vec0 = vec0 self.vec1 = vec1 self.vec2 = vec2 self.color = color self.view = view self.draw() def draw(self): VecData = [self.vec0, self.vec0+self.vec1, self.vec0+self.vec2, self.vec0+self.vec1+self.vec2, self.vec0+self.vec1, self.vec0+self.vec2] PlaneMeshData = gl.MeshData() PlaneMeshData.setVertexes(verts=VecData) self.PlaneItem = gl.GLMeshItem(meshdata=PlaneMeshData, smooth=False, glOptions="translucent") self.PlaneItem.setColor(self.color) self.view.addItem(self.PlaneItem) def change_color(self, color): self.color = color self.PlaneItem.setColor(self.color) class Quader: def __init__(self, vec0, vec1, vec2, vec3, color, view): #vec0 base edge of quader, vec1 and vec2 and vec3 edges relative to vec0 self.vec0 = vec0 self.vec1 = vec1 self.vec2 = vec2 self.vec3 = vec3 self.color = color self.view = view self.draw() def draw(self): self.colorfactor1 = np.array([0.8,0.8,0.8,1]) self.colorfactor2 = np.array([0.6,0.6,0.6,1]) self.Plane1 = Plane(self.vec0, self.vec1, self.vec2, self.color, self.view) self.Plane2 = Plane(self.vec0, self.vec2, self.vec3, self.color*self.colorfactor1, self.view) self.Plane3 = Plane(self.vec0, self.vec1, self.vec3, self.color*self.colorfactor2, self.view) self.Plane4 = Plane(self.vec0+self.vec1+self.vec2+self.vec3, -self.vec1, -self.vec2, self.color, self.view) self.Plane5 = Plane(self.vec0+self.vec1+self.vec2+self.vec3, -self.vec3, -self.vec2, self.color*self.colorfactor1, self.view) self.Plane6 = Plane(self.vec0+self.vec1+self.vec2+self.vec3, -self.vec3, -self.vec1, self.color*self.colorfactor2, self.view) def change_color(self,color): self.color = color self.Plane1.change_color(self.color) self.Plane2.change_color(self.color*self.colorfactor1) self.Plane3.change_color(self.color*self.colorfactor2) self.Plane4.change_color(self.color) self.Plane5.change_color(self.color*self.colorfactor1) self.Plane6.change_color(self.color*self.colorfactor2) class Niobium: def __init__(self, vec0, vec1, vec2, vec3, color, view): #vec0 base edge of quader, vec1 and vec2 and vec3 edges relative to vec0 self.vec0 = vec0 self.vec1 = vec1 self.vec2 = vec2 self.vec3 = vec3 self.color = color self.view = view self.NoSegments = 8 self.draw() def draw(self): self.Quader1 = Quader(self.vec0+self.vec3/self.NoSegments*7, self.vec1, self.vec2, self.vec3/self.NoSegments, self.color, self.view) self.Quader2 = Quader(self.vec0+self.vec3/self.NoSegments*6, self.vec1, self.vec2, self.vec3/self.NoSegments, self.color, self.view) self.Quader3 = Quader(self.vec0+self.vec3/self.NoSegments*5, self.vec1, self.vec2, self.vec3/self.NoSegments, self.color, self.view) self.Quader4 = Quader(self.vec0+self.vec3/self.NoSegments*4, self.vec1, self.vec2, self.vec3/self.NoSegments, self.color, self.view) self.Quader5 = Quader(self.vec0+self.vec3/self.NoSegments*3, self.vec1, self.vec2, self.vec3/self.NoSegments, self.color, self.view) self.Quader6 = Quader(self.vec0+self.vec3/self.NoSegments*2, self.vec1, self.vec2, self.vec3/self.NoSegments, self.color, self.view) self.Quader7 = Quader(self.vec0+self.vec3/self.NoSegments*1, self.vec1, self.vec2, self.vec3/self.NoSegments, self.color, self.view) self.Quader8 = Quader(self.vec0+self.vec3/self.NoSegments*0, self.vec1, self.vec2, self.vec3/self.NoSegments, self.color, self.view) self.QuaderList = [self.Quader1, self.Quader2, self.Quader3, self.Quader4, self.Quader5, self.Quader6, self.Quader7, self.Quader8] def change_color(self, ColorArray): i=0 for Quader in self.QuaderList: Quader.change_color(ColorArray[i]) i=i+1 def linearfunction(x,a,b): #Linear function for fitting purposes return a*x+b class WorkerSignals(QObject): ''' Defines the signals available from a running worker thread. Supported signals are: finished: No data error: tuple (exctype, value, traceback.format_exc() ) result: object data returned from processing, anything progress: int indicating % progress ''' finished = pyqtSignal() error = pyqtSignal(tuple) result = pyqtSignal(object) progress = pyqtSignal(list) class Worker(QRunnable): ''' Worker thread Inherits from QRunnable to handler worker thread setup, signals and wrap-up. :param callback: The function callback to run on this worker thread. Supplied args and kwargs will be passed through to the runner. :type callback: function :param args: Arguments to pass to the callback function :param kwargs: Keywords to pass to the callback function ''' def __init__(self, fn, *args, **kwargs): super(Worker, self).__init__() # Store constructor arguments (re-used for processing) self.fn = fn self.args = args self.kwargs = kwargs self.signals = WorkerSignals() # Add the callback to our kwargs self.kwargs['progress_callback'] = self.signals.progress @pyqtSlot() def run(self): ''' Initialise the runner function with passed args, kwargs. ''' # Retrieve args/kwargs here; and fire processing using them try: result = self.fn(*self.args, **self.kwargs) except: traceback.print_exc() exctype, value = sys.exc_info()[:2] self.signals.error.emit((exctype, value, traceback.format_exc())) else: self.signals.result.emit(result) # Return the result of the processing finally: self.signals.finished.emit() # Done def get_float(Qline,default = 0): #gets value from QLineEdit and converts it to float. If text is empty or cannot be converted, it returns "default" which is 0, if not specified try: out = float(Qline.text()) except: out = default return(out) class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, *args, **kwargs): # Get the current script's directory self.current_dir = os.path.dirname(os.path.abspath(__file__)) # Get the parent directory by going one level up self.parent_dir = os.path.dirname(current_dir) #import Gui from QT designer file super(MainWindow, self).__init__(*args, **kwargs) self.setupUi(self) #setup T plot self.graphWidget.setBackground('w') self.graphWidget.setTitle("T gradient") self.graphWidget.setLabel('left', 'z (cm)') self.graphWidget.setLabel('bottom', 'T (K)') temp = [0,1] pen1 = pg.mkPen(color=(255, 0, 0), width=2) #Pen for temperature plotting pen2 = pg.mkPen(color=(40, 40, 40), width=1 ) #Pen for Tc (dashed line) plotting pen3 = pg.mkPen(color=(255, 100, 100), width=1 ) #Pen for linear gradient fit self.plot_1 = self.graphWidget.plot(temp,[1,0],pen = pen1, name = 'T', symbol ='x', symbolPen ='r', symbolBrush = 0.2) self.plot_2 = self.graphWidget.plot(temp,[1,0],pen = pen2, name = 'Tc') self.plot_3 = self.graphWidget.plot(temp,[1,0],pen = pen3, name = 'T Gradient') #setup 3D BT Plot self.openGLWidget.setBackgroundColor("white") #Niobium Sample #Dimensions in cm Nbx = 6 Nby = 10 Nbz = 0.3 vec0 = np.array([-Nbz/2,-Nbx/2,-Nby/2]) vec1 = np.array([0,0,Nby]) vec2 = np.array([0,Nbx,0]) vec3 = np.array([Nbz,0,0]) self.Niobium1 = Niobium(vec0,vec3,vec2,vec1, np.array([0.2,0.2,0.2,1]), self.openGLWidget) xdiff = 1 #Distance between niobium sample and B-vectors # yVectorArray = np.linspace(-3,3,3) yVectorArray = [1.3,0,-1.3,1.3,0,-1.3,1.3,0,-1.3,1.3,0,-1.3,1.3,0,-1.3] # zVectorArray = np.linspace(-5,5,5) zVectorArray = [3.2,3.2,3.2,1.6,1.6,1.6,0,0,0,-1.6,-1.6,-1.6,-3.2,-3.2,-3.2] self.vectorcolor = [0.2,0.8,0.2,1] self.vectorwidth = 4 self.VectorArray = [] # for y in yVectorArray: # for z in zVectorArray: # self.VectorArray.append(Vector(np.array([xdiff,y,z]), np.array([1,1,1]), 1, self.vectorwidth, self.vectorcolor,self.openGLWidget)) for i in range(0,15): y = yVectorArray[i] z = zVectorArray[i] self.VectorArray.append(Vector(np.array([xdiff,y,z]), np.array([1,1,1]), 1, self.vectorwidth, self.vectorcolor,self.openGLWidget)) #set up pyQT threadpool self.threadpool = QThreadPool() #define and standard threads. #define signals and slots self.actionSet_default.triggered.connect(self.set_default) self.actionReset_default.triggered.connect(self.read_default) self.button_connect.clicked.connect(self.start_meas) self.checkBox_disableplots.stateChanged.connect(self.set_displot) self.T_scale_min.editingFinished.connect(self.set_T_scale_min) self.T_scale_max.editingFinished.connect(self.set_T_scale_max) self.Tc.editingFinished.connect(self.set_Tc_value) self.Time_Slider.valueChanged.connect(self.set_Qslider_pos) self.Saved_seconds.editingFinished.connect(self.set_Saved_seconds_value) self.checkBox_livedata.stateChanged.connect(self.set_ShowLiveData) self.Play_Pause.clicked.connect(self.set_Replay_Play) self.Replay_speed.editingFinished.connect(self.set_Replay_speed_value) self.Show_sc_phase.stateChanged.connect(self.set_Show_sc_phase_value) self.Show_gradient_fit.stateChanged.connect(self.plot_gradient_hide) #define constants self.BT_Storage = collections.deque([]) #store magnetic field and temperature data self.BT_Replay_Storage = collections.deque([]) #store magnetic field and temperature data from BT_Storage deque for Replay functionality self.Qslider_pos = 1 #Position of the replay Qslider in [0,100] self.ShowLiveData = True #If True then live data is plotted in the 3D plot. If False then Replay-Mode is activated self.Replay_Play = False #Play / Pause state for replay function self.Replay_speed_value = 1 #Replay speed self.Show_sc_phase_value = True #If True: Show sc area in 3D plot self.t = [time.time()] #store timestamps self.Npoints = 200 #number of point to plot self.Tscale_min = 0 #Max T value for colors in 3d plot self.Tscale_max = 20 #Max T value for colors in 3d plot self.Tc_value = 9.2 #Value of Tc for plotting dashed line and coloring superconducting state self.Saved_seconds_value = 30 #Number of seconds in which the last measurements are saved for replay functionality self.CernoxPositions = np.linspace(1/16*10,15/16*10,8) #Cernox positions in cm self.running = True #true while app is running self.disable_plot = False #constant to disable plot to improve performance. Is changed by checkbox checkBox_disableplots self.timing_save = 5 #save intervall [s] self.set_old = [0,0,1] #variable to save the 'old' set values to compare them to the global variables. It differs from set_new in the first iteration. This ensures that new parameters are send to the device self.set_new = [0,0,0] #variable to save the new set values to compare them to the old ones self.lines_config_float = [self.T_scale_min, self.T_scale_max, self.B_scale_min, self.B_scale_max, self.Tc, self.Replay_speed, self.Saved_seconds]#is used for config file self.lines_config_strings = []#is used for config file self.checkboxes_config = [self.checkBox_disableplots,self.Show_sc_phase, self.Show_gradient_fit]#is used for config file #read default values from config and set them in gui # self.read_default() def start_meas(self): self.bla=0 #Variable for dummy measurements #establish connection to global variables} try: #try to connect to global variables manager = multiprocessing.managers.BaseManager(address=('localhost',5001), authkey=b'') manager.connect() # manager.register('sync_LS_218') manager.register('sync_converted') # self.sync_LS_218 = manager.sync_LS_218() self.sync_converted = manager.sync_converted() except: #open global variables, if no connection can be made (i.e. it is not running). Then connect to it # subprocess.call(['D:\\Python instrument drivers\\env\\Scripts\\python.exe', 'D:\\Python instrument drivers\\StandAlones\\global_variables.py']) self.global_vars = QProcess() self.global_vars.start(self.current_dir+"\\.venv\\Scripts\\python.exe", [self.current_dir+'\\global_variables_TF.py']) manager.connect() # manager.register('sync_LS_218') manager.register('sync_converted') # self.sync_LS_218 = manager.sync_LS_218() self.sync_converted = manager.sync_converted() print('!!!\nI opened global variables myself. If you close me, global variables will shut down too. Consider starting global variables in own instance for more security\n!!!') #fill in variables, in case they are not defined in global variables self.sync_converted.update({'T':[0,0,0,0,0,0,0,0]}) self.worker = Worker(self.update_all) self.worker.signals.progress.connect(self.update_gui) self.threadpool.start(self.worker) def update_all(self, progress_callback): #get values from device and write them to global variables. Checks if global variables changed from last iteration. Also pass it to upddate_gui with emit(T) while self.running == True: #Get magnetic field data from AMR-sensors: AMR_x = self.sync_converted.get('AMR_x') AMR_y = self.sync_converted.get('AMR_y') AMR_z = self.sync_converted.get('AMR_z') Array_B = [] #List with [[AMR1_x, AMR1_y, AMR1_z], [AMR2_x, AMR2_y, AMR2_z], [AMR3_x, AMR3_y, AMR3_z], ...] for i in range(0,15): B = np.array([AMR_x[i], AMR_y[i], AMR_z[i]]) Array_B.append(B) #Get temperature data from sync_converted dict Array_T = self.sync_converted.get('T') #DUMMY DATA FOR TEST # Array_B = [] # for i in range(0,15): # B = np.array([1+0.5*np.sin(self.bla),np.sin(self.bla),np.cos(self.bla)]) # Array_B.append(B) # Array_T = [] # for i in range(0,8): # T = 10+5*np.sin(i*0.5+self.bla*0.2) # Array_T.append(T) self.bla = self.bla + 0.1 time.sleep(0.03) progress_callback.emit([Array_B, Array_T]) #Emits list of all B and T measurements # self.set_old = self.set_new[:] #List needs to be sliced so that only values are taken and not just a pointer is created # del(self.BK) #disconnect device when self.running is set to False def update_gui(self, List): #Convert List into original format Array_B = List[0] Array_T = List[1] currenttime = time.time() #save newest data + timestamp in queues self.BT_Storage.append([currenttime, Array_B, Array_T]) #check for Saved_seconds limit in BT_Storage. In case of older measurements -> Delete them needfordelete = True i = 0 while needfordelete and i<=len(self.BT_Storage): #Just if needfordelete = True: Check and delete old entries timestamp = self.BT_Storage[i][0] if timestamp < currenttime - self.Saved_seconds_value: #if timestamp is older than current time minus saved_seconds_value self.BT_Storage.popleft() #Delete oldest entry else: needfordelete = False #If all entries are within the time interval: No need for continue with deleting-for-loop i=i+1 if self.ShowLiveData == True: #Show live measured data self.update_Plots([currenttime, Array_B, Array_T]) self.Replay_Play = False else: #Show replay data (according to position of Qslider) if self.Replay_Play == False: #"Slider Mode" without playback self.latest_time_in_Replay_Storage = self.BT_Replay_Storage[-1][0] #show the newest timestamp in BT_Replay_Storage self.oldest_time_in_Replay_Storage = self.BT_Replay_Storage[0][0] #show the newest timestamp in BT_Replay_Storage timediff = self.latest_time_in_Replay_Storage - self.oldest_time_in_Replay_Storage self.slider_time = self.latest_time_in_Replay_Storage -(1-self.Qslider_pos)*timediff index = self.get_deque_index_closest_to_timestamp(self.BT_Replay_Storage, self.slider_time) #index closest to slider_time Array_B = self.BT_Replay_Storage[index][1] Array_T = self.BT_Replay_Storage[index][2] self.update_Plots([self.slider_time, Array_B, Array_T]) self.last_pause_time = time.time() else: #"Playback Mode" with set playback speed play_time = self.slider_time + (currenttime - self.last_pause_time)*self.Replay_speed_value if play_time >= self.latest_time_in_Replay_Storage: #If play_time reaches the end of BT_Replay_Storage start from beginning self.last_pause_time = time.time() index = self.get_deque_index_closest_to_timestamp(self.BT_Replay_Storage, play_time) #index closest to play_time Array_B = self.BT_Replay_Storage[index][1] Array_T = self.BT_Replay_Storage[index][2] self.update_Plots([play_time, Array_B, Array_T]) def update_Plots(self,value): timestamp = value[0] Array_B = value[1] Array_T = value[2] B_label_Array = [self.B_3,self.B_2,self.B_1, self.B_6,self.B_5,self.B_4, self.B_9,self.B_8,self.B_7, self.B_12,self.B_11,self.B_10, self.B_15,self.B_14,self.B_13] for i,Vector in enumerate(self.VectorArray): B = Array_B[i] Vector.change_direction(B) # Vector.change_length(1+0.5*np.sin(self.bla+i/10)) #Change B in textlabel B_total = np.linalg.norm(B) B_label_Array[i].setText(str(np.round(B_total,2))) colorarray = [] T_label_Array = [self.T_1,self.T_2,self.T_3,self.T_4,self.T_5,self.T_6,self.T_7,self.T_8] for i,T_label in enumerate(T_label_Array): T = Array_T[i] if self.Show_sc_phase_value == True and T <= self.Tc_value: red = (T-self.Tscale_min)/(self.Tscale_max - self.Tscale_min) blue = 1-(T-self.Tscale_min)/(self.Tscale_max - self.Tscale_min) color = np.array([red*0.6,0.6,0.5,1]) else: red = (T-self.Tscale_min)/(self.Tscale_max - self.Tscale_min) blue = 1-(T-self.Tscale_min)/(self.Tscale_max - self.Tscale_min) color = np.array([red,0,blue,1]) colorarray.append(color) #Change T in textlabel T_label.setText(str(np.round(T,2))) self.Niobium1.change_color(colorarray) #Update T Plot self.plot_1.setData(Array_T,np.flip(self.CernoxPositions)) self.plot_2.setData([self.Tc_value, self.Tc_value],[0,10]) #Tc Line #Linear fit for temperature gradient GradientParams, GradientCov = curve_fit(linearfunction, self.CernoxPositions, Array_T) GradientStd = np.diag(GradientCov) self.Line_T_gradient.setText(str(np.round(GradientParams[0],2)) +" +- " +str(np.round(GradientStd[0],2))) #Plot Gradient Fit self.plot_3.setData(linearfunction(self.CernoxPositions,*GradientParams),np.flip(self.CernoxPositions)) #set plotted time in time-label self.line_Time_in_plot.setText(str(time.strftime('%H:%M:%S', time.localtime(timestamp)))) def get_deque_index_closest_to_timestamp(self, deque, timestamp): #with timestamps in the 0.st column of deque timediffarray = np.empty(shape=(1,0)) for value in deque: timediff = np.abs(value[0]-timestamp) #timedifference in seconds timediffarray = np.append(timediffarray, timediff) min_index = np.argmin(timediffarray) #search for the index with the lowest difference in time to timestamp return min_index def set_Qslider_pos(self, value): self.Qslider_pos = value/100 # print(self.Qslider_pos) def set_ShowLiveData(self): self.ShowLiveData = self.checkBox_livedata.isChecked() if self.ShowLiveData == False: self.BT_Replay_Storage = self.BT_Storage.copy() #Write current BT_Storage buffer into Replay_Storage which isnt changed every iteration print("saved") def set_Replay_Play(self): #Change state of Replay_Play from True (Play) to False (Pause) and reverse if self.Replay_Play == True: self.Replay_Play = False else: self.Replay_Play = True def set_Replay_speed_value(self): self.Replay_speed_value = get_float(self.Replay_speed) def set_Show_sc_phase_value(self): self.Show_sc_phase_value = self.Show_sc_phase.isChecked() def set_T_scale_min(self): self.Tscale_min = get_float(self.T_scale_min) # print(self.T_scale_min) def set_T_scale_max(self): self.Tscale_max = get_float(self.T_scale_max) def set_Tc_value(self): self.Tc_value = get_float(self.Tc) def set_Saved_seconds_value(self): self.Saved_seconds_value = get_float(self.Saved_seconds) def set_displot(self): #sets variable to disable plot so checkbox state does not need be read out every iteration self.disable_plot = self.checkBox_disableplots.isChecked() def plot_gradient_hide(self): #shows or hides gradient fit plot according to the checkbox if self.Show_gradient_fit.isChecked() == True: self.plot_3.show() else: self.plot_3.hide() def set_default(self): #saves current set values to txt file in subdirectory configs. All entries that are saved are defined in self.lines_config #Overwrites old values in config file. current_dir = os.path.dirname(os.path.abspath(__file__)) path = current_dir+'\\configs\\BT_3D_Plot_config.txt' #To make shure the config file is at the right place, independent from where the program is started the location of the file is retrieved file = open(path,'w') for l in self.lines_config_float: temp = f"{get_float(l)}" file.write(temp+'\t') for l in self.lines_config_strings: file.write(l.text()+'\t') for c in self.checkboxes_config: file.write(str(c.isChecked())+'\t') file.write('\n') file.close def read_default(self): #reads default values from config file in subdirectory config and sets the values in gui. Then self.change is set to true so values are send #to device. (If no config file exists, it does nothing.) current_dir = os.path.dirname(os.path.abspath(__file__)) path = current_dir+'\\configs\\BT_3D_Plot_config.txt' #To make shure the config file is read from the right place, independent from where the program is started the location of the file is retrieved try: #exit function if config file does not exist vals = import_txt.read_raw(path) except: print('no config file found on') print(path) return formats = ['.2f', '.2f', '.2f','.2f','.2f','.2f','.2f'] for l,v,f in zip(self.lines_config_float,vals[0],formats): v = float(v) #convert string in txt to float, so number can be formatted according to "formats" when it's set l.setText(format(v,f)) for l,v in zip(self.lines_config_strings,vals[0][len(self.lines_config_float):]): l.setText(v) for c,v in zip(self.checkboxes_config,vals[0][len(self.lines_config_float)+len(self.lines_config_strings):]): c.setChecked(v == 'True') self.change = True def closeEvent(self,event): #when window is closed self.running is set to False, so all threads stop self.running = False time.sleep(1) event.accept() app = QApplication(sys.argv) window = MainWindow() window.show() app.exec()