craft-software/Legacy/TF_Control/BT_3D_Plot.py
2025-07-04 15:52:40 +02:00

697 lines
31 KiB
Python

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()