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.

Masoud Rahimi
  • 5,785
  • 15
  • 39
  • 67

1 Answers1

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)
alvarez
  • 456
  • 3
  • 9