2

I have a thread class and I want to start/stop my thread many times in my main function. I used this link with this method for solving my problem. Here is a simple thread which prints the keystrokes in the console:

global isWindows

isWindows = False


try:
    from win32api import STD_INPUT_HANDLE
    from win32console import GetStdHandle, KEY_EVENT, ENABLE_ECHO_INPUT, ENABLE_LINE_INPUT, ENABLE_PROCESSED_INPUT
    import win32gui
    import threading
    from time import sleep
    import sys
    isWindows = True
except ImportError as e:
    import sys
    import select
    import termios


class KeyPoller(threading.Thread):

    def __init__(self):
        super(KeyPoller, self).__init__()
        #threading.Thread.__init__(self)
        self.stop_event = threading.Event()
        global isWindows
        if isWindows:
            self.readHandle = GetStdHandle(STD_INPUT_HANDLE)
            self.readHandle.SetConsoleMode(ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT)

            self.curEventLength = 0
            self.curKeysLength = 0

            self.capturedChars = []
        else:
            # Save the terminal settings
            self.fd = sys.stdin.fileno()
            self.new_term = termios.tcgetattr(self.fd)
            self.old_term = termios.tcgetattr(self.fd)

            # New terminal setting unbuffered
            self.new_term[3] = (self.new_term[3] & ~termios.ICANON & ~termios.ECHO)
            termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.new_term)

    def poll(self):
        if isWindows:
            if not len(self.capturedChars) == 0:
                return self.capturedChars.pop(0)

            eventsPeek = self.readHandle.PeekConsoleInput(10000)

            if len(eventsPeek) == 0:
                return None

            if not len(eventsPeek) == self.curEventLength:
                for curEvent in eventsPeek[self.curEventLength:]:
                    if curEvent.EventType == KEY_EVENT:
                        if ord(curEvent.Char) == 0 or not curEvent.KeyDown:
                            pass
                        else:
                            curChar = str(curEvent.Char)
                            self.capturedChars.append(curChar)
                self.curEventLength = len(eventsPeek)

            if not len(self.capturedChars) == 0:
                return self.capturedChars.pop(0)
            else:
                return None
        else:
            dr,dw,de = select.select([sys.stdin], [], [], 0)
            if not dr == []:
                return sys.stdin.read(1)
            return None

    def stop(self):
        print("stopping the thread")
        self.stop_event.set()

    def stopped(self):
        return self.stop_event.is_set()

    def run(self):
        while not self.stopped():
            c=self.poll()
            if not c is None:
                print(c)

if __name__=='__main__':

    thr=KeyPoller()
    print("starting the thread #1")
    thr.start()
    sleep(5)
    print("stopping the thread #1")
    # sadly if you press any key in this time it would be saved and printed after  thr2.start
    thr.stop()
    thr.join()
    sleep(5)
    thr2=KeyPoller()
    print("starting the thread #2")
    thr2.start()
    sleep(5)
    print("stopping the thread #2")
    thr2.stop()
    print("Exiting the whole program")

My problem is when I call thr.stop() and try to press some keystrokes it exits the while loop and it seems that the thread has already stopped but when I call thr2.start() it prints the old keystrokes from the first instance of my thread and it seems that all keystrokes are still there no matter if I call stop function or not.

1 Answer 1

1

From my point of view I don't see the sense of initialize treading.event() as instance variable. Event is for threading/process synchronization and if you declare this in a instance, it not could be seen for others instances.

Having said that, given your code, I would use a boolean variable for stop_event. This works for me in Linux environment.

class KeyPoller(threading.Thread):

    def __init__(self):
        super(KeyPoller, self).__init__()
        #threading.Thread.__init__(self)
        self.stop_event = False
        global isWindows
        if isWindows:
            self.readHandle = GetStdHandle(STD_INPUT_HANDLE)
            self.readHandle.SetConsoleMode(ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT)

            self.curEventLength = 0
            self.curKeysLength = 0

            self.capturedChars = []
        else:
            # Save the terminal settings
            self.fd = sys.stdin.fileno()
            self.new_term = termios.tcgetattr(self.fd)
            self.old_term = termios.tcgetattr(self.fd)

            # New terminal setting unbuffered
            self.new_term[3] = (self.new_term[3] & ~termios.ICANON & ~termios.ECHO)
            termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.new_term)

    def poll(self):
        if isWindows:
            if not len(self.capturedChars) == 0:
                return self.capturedChars.pop(0)

            eventsPeek = self.readHandle.PeekConsoleInput(10000)

            if len(eventsPeek) == 0:
                return None

            if not len(eventsPeek) == self.curEventLength:
                for curEvent in eventsPeek[self.curEventLength:]:
                    if curEvent.EventType == KEY_EVENT:
                        if ord(curEvent.Char) == 0 or not curEvent.KeyDown:
                            pass
                        else:
                            curChar = str(curEvent.Char)
                            self.capturedChars.append(curChar)
                self.curEventLength = len(eventsPeek)

            if not len(self.capturedChars) == 0:
                return self.capturedChars.pop(0)
            else:
                return None
        else:
            dr,dw,de = select.select([sys.stdin], [], [], 0)
            if not dr == []:
                return sys.stdin.read(1)
            return None

    def stop(self):
        print("stopping the thread")
        self.stop_event = True

    def stopped(self):
        return self.stop_event

    def run(self):
        while not self.stopped():
            c=self.poll()
            if not c is None:
                print(c)
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the tip, I'm using windows but still, I'm facing the same problem.
with this function your answer didn't work but I tried to replace with a simple function it worked. Thanks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.