Search code examples
pythonmacosterminalconcurrencypython-multithreading

Key-listener that Runs Concurrently With a Terminal Timer using Threads


I am trying to make a timer that runs concurrently with a key-listener. The key-listener should perform a function when it takes a key and stop the timer. Below is the code I wrote. Though when I run it, the timer runs infinitely but the the key-listener does not listen for keys thus not print what it's supposed to.

Nota bene I am just getting into threading so the whole code I have written may be logically wrong in the paradigm of threading.

import concurrent.futures
import time
import os
import keyboard

run_timer = True
keep_scanning = True

def startTimer(seconds):
    while run_timer:
        for i in range(1, seconds):
            print(i)
            time.sleep(1)
            os.system("clear")
            
def scanForInput():
    while keep_scanning:
        print(keep_scanning)
        if keyboard.is_pressed('space'):
            keep_scanning = False
            run_timer = False
            return "HIT"
            
with concurrent.futures.ThreadPoolExecutor() as executor:
    f1 = executor.submit(startTimer, 4)
    f2 = executor.submit(scanForInput) 


Solution

  • It was hard to debug. Your scanForInput func terminating because keyboard library raise ImportError('You must be root to use this library on linux.') error when you have no root privileges, this is specified in the doc. The bad thing is ThreadPoolExecutor thread not log this error to stdout or stderr directly if we don't catch this error. I did some debug that this function is really working, and I have see that this function terminating after the print(keep_scanning) command, because i had no root privileges. So, to be able to use keyboard library in Linux, we need to have sudo(root) privileges.

    And second thing, you need to use global keyword to be able to update the keep_scanning and run_timer variables in the scanForInput function, but using global keyword is not good practice.

    you can update your code like this :

    import concurrent.futures
    import time
    import os,threading
    import keyboard
    
    run_timer = True
    keep_scanning = True
    
    def startTimer(seconds):
        while run_timer:
            for i in range(1, seconds):
                print(i)
                time.sleep(1)
                os.system("clear")
                
    def scanForInput():
        global keep_scanning,run_timer
        try:
            while keep_scanning:
                print(keep_scanning)
                if keyboard.is_pressed('space'):
                    keep_scanning = False
                    run_timer = False
                    return "HIT"
        except Exception as e:
            print(e)
            
    with concurrent.futures.ThreadPoolExecutor() as executor:
        f1 = executor.submit(startTimer, 4)
        f2 = executor.submit(scanForInput)
    

    and you need to run this command with sudo privileges like sudo python3 mycode.py