![]() |
Threading for keyboard input - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: General Coding Help (https://python-forum.io/forum-8.html) +--- Thread: Threading for keyboard input (/thread-13354.html) |
Threading for keyboard input - porkpiehat - Oct-11-2018 Hi all, I'm trying to build a simple command-line interface that has a normal task running, and responds to a key pressed by the user. But I'm not getting it right, and I might be over-complicating it.. The goal is a command-line tool to update the clock of an Arduino with RTC module. The Arduino sends it current time every second. What I want to achieve is that the command line looks something like below: user connects, the system time and Arduino time are shown (updated every second over the same line). If the user presses 'u' or 'q', the program updates or stops, respectively. The user input can be with or without return, I don't mind. I attempted now to do it with two threads and input() and that sort of works. I also tried getch() and sys.stdin.read(1), but input() is so far most successful. What happens now is that the input-thread is started right away and the input '>' is not in the right spot. For elegance I looked into registering key presses rather than input(), but most solutions I found online use unnecessarily large libraries for this. Any thoughts on how I should approach this are welcome :) COM Ports available: COM1, COM2, COM3 Select COM port to connect: <user input> Connected to COM-port. Press 'u' to update or 'q' to quit. System date/time: 2018-10-11 09:44:00 <-- these lines are constantly updated Arduino date/time: 2001-01-01 00:00:00 <-- > (optional line where it waits for user input) Updating... System date/time: 2018-10-11 09:44:02 Arduino date/time: 2018-10-11 09:44:02 import time import threading data_ready = threading.Event() kill_flag = threading.Event() def keyboard_poller(): global key_pressed loop = True while loop: time.sleep(0.1) if kill_flag.isSet(): loop = False ch = input(">") if ch: key_pressed = ch data_ready.set() def main(): curr_millis = time.time() * 1000 prev_millis = curr_millis poller = threading.Thread(target=keyboard_poller) poller.start() loop = True while loop: curr_millis = time.time() * 1000 if (curr_millis - prev_millis) >= 1000: print("Another second passed..." + str(curr_millis) + "\r") prev_millis = curr_millis # Do some extra stuff here if data_ready.isSet(): if key_pressed.lower() == "q": kill_flag.set() loop = False else: print("You pressed: " + key_pressed) data_ready.clear() if __name__ == "__main__": print("Started..") main() input("Press any key to exit...") exit() RE: Threading for keyboard input - woooee - Oct-11-2018 I only know multiprocessing because it does everything I want to do so there is no reason to learn threading. This is a simple program that terminates the Process when "q" is entered, and is a little messy because the function running as a separate process also prints to the screen to show that something is happening, but you can still input a "q". Hopefully this will help. import time from multiprocessing import Process class TestClass(): def test_f(self): ctr = 0 while True: ctr += 1 print(" ", ctr) time.sleep(1.0) if __name__ == '__main__': ## run function in the background CT=TestClass() p = Process(target=CT.test_f) p.start() ## will not exit if function finishes, only when ## "q" is entered, but this is just a simple example stop_char="" while stop_char.lower() != "q": stop_char=input("Enter 'q' to quit ") if stop_char.lower() == "u": ## do something else print("terminate process") if p.is_alive(): p.terminate() RE: Threading for keyboard input - volcano63 - Oct-11-2018 (Oct-11-2018, 10:50 AM)woooee Wrote: I only know multiprocessing because it does everything I want to do so there is no reason to learn threading. multiprocessing.Process APIs are the same as threading.Thread , allowing painless switch from the latter to the former
RE: Threading for keyboard input - porkpiehat - Oct-11-2018 @woooee: You basically switched around the input-poll and the other process. That might not be a bad idea, although the problem you encounter as well is, indeed, the two threads using the print-statement will cause conflicts. Either way is fine for me: the main polling the keyboard and having a separate thread for the other process, or vice versa. I guess with one process requiring the print, and one the input() in my current code, I would be better off polling for a key press rather than input(). How should I go about that in an easy way? RE: Threading for keyboard input - woooee - Oct-11-2018 Moving the quit to a GUI from the console https://www.tutorialspoint.com/python/python_gui_programming.htm import sys if 3 == sys.version_info[0]: ## 3.X is default if dual system import tkinter as tk ## Python 3.x else: import Tkinter as tk ## Python 2.x import time from multiprocessing import Process class TestClass(): def test_f(self): ctr = 0 while True: ctr += 1 print(" ", ctr) time.sleep(1.0) def tk_quit(self): root=tk.Tk() tk.Button(root, text="Quit", command=root.quit, width=10).grid() root.mainloop() if __name__ == '__main__': ## run function in the background CT=TestClass() p = Process(target=CT.test_f) p.start() CT.tk_quit() print("terminate process") if p.is_alive(): p.terminate() |