Search code examples
pythonpython-3.xmultithreadingpython-multithreadingethercat

Thread decorated function - is_alive attribute


TLDR: how to check if thread is alive when I used the thread function as a decorator on another function?

I'm creating a GUI using PyQt6 (other recommendations will be accepted in case its a bad choice). my issue is when I click the Connect button, I want to verify the connection thread is not running.

i marked with ** what is relevant due to the long code

So I created self.ecMaster attribute in the main init and im trying to check if ecConnection is up using is_alive method on which is decorated with @threaded.

i'm getting an error : AttributeError: 'function' object has no attribute 'is_alive'

How should i do it properly? Thank you

Main

from threading import thread
import pysoem
import ecAPI as ec
from PyQt6.QtWidgets import *
from PyQt6.QtCore import *
from PyQt6.QtGui import *

class mainWindow(QMainWindow):
    exitSignal = Signal()  # Define the custom exit signal

    def __init__(self,*args, **kwargs):        
        super().__init__(*args, **kwargs)
        self.exitSignal.connect(app.quit)
        self.central_widget = QWidget(self)
        self.test_widget = QWidget(self)
        self.modeSignal = ''
        self.operatorSignal = ''
        self.setCentralWidget(self.central_widget)
        self.connectionWindowLayout()
        **self.master = ec.ecMaster()**

def connectButtonClicked(self):
    self.connect_button.clickable = False
    # here i ran into trouble
    **if self.master.ecConnection.is_alive:**              # If thread is active
        self.master.ecConnection.terminate()
        logger.info('Connection thread was already active, terminated')
    logger.info('Starting connection thread')
    **self.master.ecConnection()**                      # Start connection thread
    time.sleep(0.1)  

if __name__ == '__main__':
    
    logger = logging.getLogger(__name__)
    logging.getLogger().setLevel(logging.INFO)
    
    app = QApplication(sys.argv)
    ModernStyle.apply(app)
    window = mainWindow()
    window.show()
    sys.exit(app.exec())

ecAPI.py

import pysoem, logging
from threading import Thread

def threaded(fn):
    # To use as decorator to make a function call threaded 
    def wrapper(*args, **kwargs):
        thread = Thread(target=fn, args=args, kwargs=kwargs)
        thread.start()
        return thread
    return wrapper

class ecMaster(pysoem.Master):
    def __init__(self):
        self.connectionSignal = 0
        super().__init__()
    
    def findAdapters(self):
        nicName = 'PCIe'            # Change based on PC --- 'PCIe
        network_adapters = pysoem.find_adapters()
        for adapter in network_adapters:
            if nicName in str(adapter[1]):
                networkAdapterID = adapter[0]
                self.open(networkAdapterID)
                logging.info('Found network adapter\n'+str(networkAdapterID))
        return len(network_adapters)>0 # True if found

    @threaded
    def ecConnection(self):
        if self.findAdapters():
            print('Searching for EC devices ... ')
            self.connectionSignal = self.config_init()         # Initilize connection
            if self.slaves:
                print(f'Found {len(self.slave_list)} devices')
                for i,slave in enumerate(self.slaves):
                    # ven = slave.sdo_read(0x1018, 1)
                    print(f'Found Device {slave.name},{slave.id},{slave.man}') #,{slave.sdo_read()}
                    logging.info(f'Found {self.connectionSignal} EC devices')
            else: 
                logging.info('No salves were found')
                print('Slaves not found')
        else:
            logging.warning('No network adapter found')
            print('No network adapter found')

Tried to remove wrapper and leave only thread. searched online. Expected the function I decorated to have Thread attribute but could't get it right.


Solution

  • found a solution. self.master.ecConnection() isn't a thread but a function. only when running it, it becomes a thread (due to @threaded decorator). therefore I added con_thread = self.master.ecConnection() and then con_thread was indeed a thread object and I could access con_thread.is_alive()