Listening for user inputs to to manipulate functions already running

Question:

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?

Asked By: Mikie

||

Answers:

import _thread
import time

should_say_bye = False

class Hello:
    def __init(self):
        pass

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

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

        
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")

You had used if should_say_bye = True and not set it as global

If you have a lot of flags like this, it might be better to use a class that you pass into multithreaded functions and set things.

import _thread
import time

class Flags:
    stop_saying_hello = False

def say_hello(Flags):
    i = 0
    while not Flags.stop_saying_hello:
        print(f"Hello {i}")
        i += 1
        time.sleep(2)
        
def get_input(Flags):
    Flags.stop_saying_hello = (input().lower() ==  'q')
    
t1 = _thread.start_new_thread(say_hello, (Flags,))
t2 = _thread.start_new_thread(get_input, (Flags,))

while not Flags.stop_saying_hello:
    time.sleep(2)

print("bye")
Answered By: arrmansa

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.

Answered By: Jason Chia