Strelok
Strelok

Reputation: 189

How pass values from Entry lines into a python function called by a button

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

Answers (1)

musicamante
musicamante

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

Related Questions