279 lines
12 KiB
Python
279 lines
12 KiB
Python
from PyQt6.QtGui import *
|
|
from PyQt6.QtWidgets import *
|
|
from PyQt6.QtCore import *
|
|
|
|
import multiprocessing
|
|
import multiprocessing.managers
|
|
|
|
import time
|
|
import traceback, sys, os
|
|
import numpy as np
|
|
|
|
# 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 drivers import Tabor_LS6081B
|
|
import import_txt
|
|
|
|
from design_files.Tabor_LS6081B_design import Ui_MainWindow
|
|
|
|
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)
|
|
|
|
#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_T_LS6081B')
|
|
self.sync_T_LS6081B = manager.sync_T_LS6081B()
|
|
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+"\\env\\Scripts\\python.exe", [self.current_dir+'\\global_variables.py'])
|
|
manager.connect()
|
|
manager.register('sync_T_LS6081B')
|
|
self.sync_T_LS6081B = manager.sync_T_LS6081B()
|
|
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, if they are not defined in global variables
|
|
self.sync_T_LS6081B.update({'freq':4.8e9, 'Power':-10,'RF':False, 'Pulse':False, 'Pulsewidth':1e-3, 'DC':0.5})
|
|
|
|
#import Gui from QT designer file
|
|
super(MainWindow, self).__init__(*args, **kwargs)
|
|
self.setupUi(self)
|
|
|
|
#setup plot
|
|
|
|
|
|
#set up pyQT threadpool
|
|
self.threadpool = QThreadPool()
|
|
|
|
#start 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.button_RF.clicked.connect(self.update_RF)
|
|
self.button_Pulse.clicked.connect(self.update_Pulse)
|
|
self.line_DC.editingFinished.connect(self.update_DC)
|
|
self.line_Freq.editingFinished.connect(self.update_f)
|
|
self.line_Power.editingFinished.connect(self.update_P)
|
|
self.line_width.editingFinished.connect(self.update_w)
|
|
|
|
#define constants
|
|
self.running = True #true while app is running
|
|
self.set_new = [4.8e9, -10, False, False, 1e-3, 0.5] #variable to save the 'new' set values to compare them to the global variables
|
|
self.set_old = [0, 0, False, False, 0, 0] #variable to save the 'old' set values to compare them to the global variables
|
|
self.change = False
|
|
self.lines_config_float = [self.line_Freq, self.line_Power,self.line_width, self.line_DC] #is used for config file
|
|
self.lines_config_strings = [self.line_devAdr] #is used for config file
|
|
|
|
#read default values from config and set values in them in gui
|
|
self.read_default()
|
|
#write gui values in global variables. RF and Pulse is just kept off
|
|
self.update_DC()
|
|
self.update_f()
|
|
self.update_P()
|
|
self.update_w()
|
|
|
|
def start_meas(self):
|
|
#Connect to device
|
|
address = self.line_devAdr.text()
|
|
self.gen = Tabor_LS6081B.TaborLS6081B(address)
|
|
#start thread for communication with device
|
|
worker = Worker(self.update_output)
|
|
self.threadpool.start(worker)
|
|
print('connected')
|
|
|
|
|
|
def update_output(self,progress_callback): #check if global variables changed. If so, send new values to device
|
|
while self.running == True:
|
|
for i,n in enumerate(['freq', 'Power','RF', 'Pulse', 'Pulsewidth', 'DC']): #get new set values from global variables to compare them to old ones.
|
|
self.set_new[i] = self.sync_T_LS6081B.get(n)
|
|
if self.set_new != self.set_old:
|
|
f = self.set_new[0]
|
|
P = self.set_new[1]
|
|
RF = self.set_new[2]
|
|
Pulse = self.set_new[3]
|
|
w = self.set_new[4]
|
|
DC = self.set_new[5]
|
|
|
|
#turn RF ON or OFF with settings in gui
|
|
if RF == 1:
|
|
if Pulse == 0: #Check if pulse is activated or not
|
|
self.gen.CW(P, f, 1)
|
|
else:
|
|
self.gen.Pulse(P, f, w, DC, 1)
|
|
else:
|
|
self.gen.CW(P, f, 0)
|
|
|
|
#set gui to values in global variables
|
|
self.update_gui(self.set_new)
|
|
|
|
|
|
self.set_old = self.set_new.copy() #.copy() is needer, otherwise it sets just a pointer to self.set_new
|
|
time.sleep(0.1)
|
|
|
|
del(self.gen) #disconnect device when self.running is set to False
|
|
|
|
|
|
def update_gui(self,setV): #set values given in setV in the corresponding lineEdits
|
|
self.line_Freq.setText(f"{setV[0]:.6e}")
|
|
self.line_Power.setText(str(setV[1]))
|
|
self.line_width.setText(f"{setV[4]:.3e}")
|
|
self.line_DC.setText(str(setV[5]))
|
|
|
|
if setV[2] == 1: #set RF button in the correct state
|
|
self.button_RF.setText('RF ON/off')
|
|
self.label_RF.setStyleSheet("background-color: green")
|
|
else:
|
|
self.button_RF.setText('RF on/OFF')
|
|
self.label_RF.setStyleSheet("background-color: red")
|
|
|
|
if setV[3] == 1: #set Pulse button in the correct state
|
|
self.button_Pulse.setText('Pulse ON/off')
|
|
self.label_Pulse.setStyleSheet("background-color: green")
|
|
else:
|
|
self.button_Pulse.setText('Pulse on/OFF')
|
|
self.label_Pulse.setStyleSheet("background-color: red")
|
|
|
|
|
|
|
|
def update_f(self): #changes global variable. The change will be detected by update_output and it will be passed to the device
|
|
self.sync_T_LS6081B.update({'freq': get_float(self.line_Freq)})
|
|
|
|
def update_P(self): #changes global variable. The change will be detected by update_output and it will be passed to the device
|
|
self.sync_T_LS6081B.update({'Power': get_float(self.line_Power)})
|
|
|
|
def update_w(self): #changes global variable. The change will be detected by update_output and it will be passed to the device
|
|
self.sync_T_LS6081B.update({'Pulsewidth': get_float(self.line_width)})
|
|
|
|
def update_DC(self): #changes global variable. The change will be detected by update_output and it will be passed to the device
|
|
self.sync_T_LS6081B.update({'DC': get_float(self.line_DC)})
|
|
|
|
def update_RF(self): #updates global variable depending of the old state of the RF button. (If it was off, the new value is set to 1(= RF on))
|
|
if self.button_RF.text() == 'RF on/OFF':
|
|
self.sync_T_LS6081B.update({'RF': 1})
|
|
else:
|
|
self.sync_T_LS6081B.update({'RF': 0})
|
|
|
|
def update_Pulse(self): #updates global variable depending of the old state of the Pulse button. (If it was off, the new value is set to 1(= Pulse on))
|
|
if self.button_Pulse.text() == 'Pulse on/OFF':
|
|
self.sync_T_LS6081B.update({'Pulse': 1})
|
|
else:
|
|
self.sync_T_LS6081B.update({'Pulse': 0})
|
|
|
|
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\\Tabor_LS6081B_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')
|
|
|
|
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\\Tabor_LS6081B_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:
|
|
return
|
|
formats = ['.6e','.2f','.3e','.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][4:]):
|
|
l.setText(v)
|
|
|
|
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() |