Reputation: 189
I am trying to make a user interface for a SRS DG645. I'm very new to PyQt5 (and let's face the truth - I am also very fresh with Python), and I was trying to do a small test menu to try and get some functionality - and understand how this works.
I was sucessfully able to initiate and close connection to the intrument via the 'Initialize' and 'Close' buttons.
What I wanted to do next was to be able to set delays. For that, I created two entrys - entry1 and entry2 - which would provide the 'channel' (an integer) and the 'timing' (a float).
For the script to work, I need to associate the 'Write Values' button with the a function which was described previously in the file - set_delay() - which requires a tuple of (channel,timing) in order to be sucessfully executed.
I am completely oblivious on how to implement the code for this button, as I don't understand how I can get the arguments passed by the entry boxes and insert them into the required dg.set_value() function.
I am aware of this answer: How to pass arguments to functions by the click of button in PyQt? by I believe it does not fully answer my present question
Any help would be greatly appreciated!
PS: A big thank you to Trappisch and Casagrande for making their code public in Git!
Please find below the MWE for my implementation
# -*- coding: utf-8 -*-
"""
Driver for devices from Stanford Research Systems
contains:
- DG645
File name: stanford_research_systems.py
Python version: 3.7
This script reuses code from
https://github.com/Galvant/InstrumentKit/blob/master/instruments/srs/srsdg645.py
by Reto Trappisch and Steven Casagrande
"""
from typing import Tuple, Union
import socket
import time
from termcolor import colored, cprint
import sys
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtGui as qtg
from PyQt5 import QtCore as qtc
class DG645:
""" Driver for delay generators """
DEFAULTS = {
'outputBNC': {
"T0":0,
"AB":1,
"CD":2,
"EF":3,
"GH":4
},
'channel': {
"T0":0,
"T1":1,
"A":2,
"B":3,
"C":4,
"D":5,
"E":6,
"F":7 ,
"G":8,
"H":9
},
'write_termination': '\n',
'read_termination': '\r\n',
}
def __init__(self, tcp: str, port: int, timeout: float = 0.010):
"""
Arguments:
tcp - IP address of device
port - port of device
timeout - time in seconds before recv() gives a timeout error
"""
self.tcp = tcp
self.port = port
self._device = None
self.timeout = timeout
def initialize(self):
"""Connect to the device."""
self._device = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._device.settimeout(self.timeout)
self._device.connect((self.tcp, self.port))
time.sleep(0.2)
print_blue_on_white = lambda x: cprint(x, 'blue', 'on_white')
print_blue_on_white(f'=======================\n Connection Sucessful! \n=======================\n{self.idn}')
# time.sleep(0.3)
# print_blue_on_white(f'MAC Address is \n{self.emac}')
def close(self):
"""Closing connection with the device"""
print_red_on_white = lambda x: cprint(x, 'red', 'on_white')
print_red_on_white(f'{self.idn}\n===================\n Connection closed \n===================')
self._device.close()
def write(self, cmd: str) -> None:
"""Send command to device"""
# Add write termination character and encode
termination_char = self.DEFAULTS['write_termination']
cmd += termination_char
cmd = cmd.encode()
# Send command
self._device.send(cmd)
def query(self, cmd: str) -> str:
"""Send a request to the device and return its respons."""
self.write(cmd)
respons = self._device.recv(256)
respons = respons.decode()
# Strip off read termination character
return respons.rstrip()
@property
def idn(self) -> str:
""" Get identification of device. """
idn = self.query('*IDN?')
return idn
def set_delay(self, channel: Union[int, str], delay: float, reference: Union[int, str] = "T0"):
"""Set the delay of a certain channel with respect to a reference.
Arguments:
channel -- str/int corresponding to a channel (see self.DEFAULTS)
delay -- float, with time in seconds
reference -- defaults to 'T0'
"""
if isinstance(channel, str):
channel = self.DEFAULTS['channel'][channel]
if isinstance(reference, str):
reference = self.DEFAULTS['channel'][reference]
cmd = f'DLAY {channel}, {reference}, {delay}'
self.write(cmd)
#wait for 100 ms, this is the time it will take to write the command
time.sleep(0.1)
def get_delay(self, channel: Union[int, str]) -> Tuple[int, float]:
"""Request the delay of a certain channel
Arguments:
channel -- str/int corresponding to a channel (see self.DEFAULTS)
Returns -- (int, float) | reference channel, delay in seconds.
"""
if isinstance(channel, str):
channel = self.DEFAULTS['channel'][channel]
cmd = f'DLAY? {channel}'
respons = self.query(cmd).split(',')
reference = int(respons[0])
delay = float(respons[1])
return reference, delay
def get_output_level(self, channel: Union[int, str]) -> float:
"""Request output amplitude of a channel
Arguments:
channel -- str/int corresponding to a channel (see self.DEFAULTS)
Returns --float, the amplitude in Volts
"""
if isinstance(channel, str):
channel = self.DEFAULTS['outputBNC'][channel]
cmd = f'LAMP? {channel}'
respons = self.query(cmd)
return float(respons)
def emac(self) -> str:
"Get MAC address of device"
emac = self.query('*EMAC?')
return emac
class MainWindow(qtw.QWidget) :
def __init__(self) :
"""MainWindow constructor"""
super().__init__()
##########################
# Main UI code goes here #
##########################
layout = qtw.QVBoxLayout()
self.setLayout(layout)
self.quitbutton = qtw.QPushButton('Quit',clicked=self.close)
self.initializebutton = qtw.QPushButton('initialize', clicked=dg.initialize)
self.closebutton = qtw.QPushButton('Close', clicked=dg.close)
self.layout().addWidget(self.quitbutton)
self.layout().addWidget(self.initializebutton)
self.layout().addWidget(self.closebutton)
self.label1 = qtw.QLabel('Channel')
self.entry1 = qtw.QLineEdit()
self.label2 = qtw.QLabel('Timing')
self.entry2 = qtw.QLineEdit()
self.layout().addWidget(self.label1)
self.layout().addWidget(self.entry1)
self.layout().addWidget(self.label2)
self.layout().addWidget(self.entry2)
self.wtrigbutton = qtw.QPushButton('Write Values', clicked=dg.set_delay)
##########################
# End of UI code Section #
##########################
self.show()
if __name__ == '__main__':
dg = DG645('169.254.74.114', 5025)
app = qtw.QApplication(sys.argv)
mw = MainWindow()
sys.exit(app.exec())
#################################################
## Commented code for DDG setup via script run ##
#################################################
# dg = DG645('169.254.74.114', 5025)
# dg.initialize()
# for i in range (0,8):
# delay = dg.get_delay(i)
# print(f"Delay {i} is set to ", delay)
# dg.set_delay(0, 0.000000000)
# print("Delay 1 is now set to", delay1)
# delay2 = dg.get_delay(2)
# print("Delay 2 is set to", delay2)
# dg.emac()
# dg.close()
################################################
################################################
Upvotes: 0
Views: 160
Reputation: 48231
Qt signals don't need to be directly connected to the "final" function that has to be called.
For simple cases, you can still use lambdas as the answers to the linked post suggest:
class MainWindow(qtw.QWidget):
def __init__(self) :
super().__init__()
# ...
self.wtrigbutton = qtw.QPushButton('Write Values')
self.wtrigbutton.clicked.connect(lambda:
dg.set_delay(self.entry1.text(), self.entry2.text()))
But in most cases, it's better to have an explicit function, which is also safer and more usable for debugging.
If you have to call a function that requires arguments, create a function that gets those arguments and eventually calls the actual function for the processing, then use that function for the signal connection:
class MainWindow(qtw.QWidget):
def __init__(self) :
super().__init__()
# ...
self.wtrigbutton = qtw.QPushButton('Write Values', clicked=self.setDelay)
def setDelay(self):
channel = self.entry1.text()
timing = self.entry2.text()
if channel and timing:
dg.set_delay(channel, timing)
Note that you should never use blocking functions like time.sleep()
(or blocking while loops), since that could potentially freeze the UI preventing proper user interaction and UI updates; if you need to delay a function call, use a QTimer, while if you need delayed processing (like waiting for IO), use a QThread.
Upvotes: 1