Search code examples
pythonpython-3.xmultithreadinginputglobal-variables

Listening for user inputs to to manipulate functions already running


I have 2 files, main.py and app.py

main.py

import _thread
import time

def launch(thread_name):
    h = Hello()
    h.say_hello()

def input_reader(thread_name):
    global should_say_bye
    should_say_bye = False
    x = input()
    if x.lower() == "q":
        # Should run the say_bye() function from the Hello class. But using the same instance of the hello class from the launch() function
        should_say_bye = True
        print("Programming is about to quit.")

try:
    _thread.start_new_thread(launch, ("App",))
    _thread.start_new_thread(input_reader, ("Input_Reader",))
except:
    print("Error")

while 1:
    pass

app.py

import time

class Hello:
    def __init(self):
        pass

    def say_hello(self):
        i = 0
        while True:
            print(f"Hello {i}")
            i += 1
            if should_say_bye = True:
                self.say_bye()
                should_say_bye = False
            time.sleep(2)

    def say_bye(self):
        print("Bye")

Basically I have 2 threads. One that runs the function launch() and one that listens to the console input (input_reader()). When it sees the the input q it should run the say_bye() function but ensuring its the same class instance as the h variable in the launch() function. I tried using a global variable should_say_bye so when q is detected it changes the global variable but that variable isn't defined in Hello

My goal is, run 1 thread with launch() and one thread with input_reader(). When input_reader() detects q it manipulates launch() but also the functions launch() is running side it like say_hello()

How can I do this?


Solution

  • I would not use globals due as they are highly unrecommended. Consider using a Pipe().

    import _thread
    import time
    from multiprocessing import Pipe
    
    class Hello():
        def __init(self):
            pass
    
        def say_hello(self,p1):
            i = 0
            while True:
                print(f"Hello {i}")
                i += 1
                check_pipe = p1.poll(0) #Checking pipe for stuff
                if check_pipe: #If there is something
                    stuff = p1.recv() #We get it out
                    if stuff == "q": #If it meets our conditio
                        self.say_bye()# we say bye
                    break # and exit our loop? 
                time.sleep(2)
    
        def say_bye(self):
            print("Bye")
    
    
    def launch(thread_name,pipe_output,kill_pipe): 
        h = Hello()
        h.say_hello(pipe_output)
        kill_pipe.send("kill") #You could pass the kill_pipe deeper
        kill_pipe.close() #Just formal closing.
        
    def input_reader(thread_name,pipe_input):
        while True:
            x = input()
            if x.lower() == "q":
               
                pipe_input.send("q") #we put the input to the pipe
                print("Programming is about to quit.")
                pipe_input.close() #we close the pipe
                break
    
    if __name__=="__main__": #Standard main block
    
        parent, child = Pipe() #initialize connection between input thread and launch 
        parent2, child2 = Pipe() #Initialize launch and kill command so no infinite loops.
        _thread.start_new_thread(launch, ("App",child,parent2)) #Simply pass the pipe connections
        _thread.start_new_thread(input_reader, ("Input_Reader",parent,))
        
        while True:
            check_kill = child2.poll(0) #Polling to see if the pipe has anything
            if check_kill: #Dont bother checking because pipe will only have kill command in it.
                print("ending program")
                break
            pass
    

    This would work even if they are separate files as you can simply import Hello and pass the pipe connections to them.