Threaded global keyboard events











up vote
0
down vote

favorite












I'm creating a PyQt Application. The main core/logic of the program runs in a separate loop I've created on a new thread. The core needs to be listening for defined hotkeys that will in turn trigger some functionality. I've removed all the logic and just made a dummy test scenario, as I'm really looking for feedback/help on structure/architecture. I'm very new to all of this.



I created a Keyboard class that also runs in its own thread, where you can register functions that will get called when a key is pressed.



import threading
from win32 import win32api
import win32con
import pythoncom
import pyHook

class Keyboard(threading.Thread):
def __init__(self):
super().__init__()
self.hook_manager = pyHook.HookManager()
self.hook_manager.KeyDown = self.on_keyboard_event

self._callbacks =

self.is_running = False

def stop(self):
self.is_running = False
self.hook_manager.UnhookKeyboard()

def run(self):
self.is_running = True
self.hook_manager.HookKeyboard()

while self.is_running:
pythoncom.PumpWaitingMessages()

def press(*args):
for arg in args:
win32api.keybd_event(arg, 0, 0, 0)

def release(*args):
for arg in args:
win32api.keybd_event(arg, 0, win32con.KEYEVENTF_KEYUP, 0)

def register_callback(self, f):
self._callbacks.append(f)

def on_keyboard_event(self, event):
print(event.KeyID)
for callback in self._callbacks:
callback(event)

return True


Now the core of the application. It has two main functions: it needs to switch between when running and the second one needs to run for a set amount of time then revert back to the first. The way I've handled this switching and looping seems really bad.



import time


class ThreadedTask(threading.Thread):
MODE_ONE = 0
MODE_TWO = 1

def __init__(self):
super().__init__()
self.is_running = False
self.processing_mode = 0

self.x = 0

self.keyboard = Keyboard()
self.keyboard.register_callback(self.key_pressed)
self.keyboard.start()

def stop(self):
self.is_running = False

def run(self):
self.is_running = True

while self.is_running:
if self.processing_mode == ThreadedTask.MODE_ONE:
self.function_one()
elif self.processing_mode == ThreadedTask.MODE_TWO:
self.function_two()

def function_one(self):
print("function_one")
y = self.x + 2

if y == 4:
self.processing_mode = 1
self.x = 0

time.sleep(0.3)

def function_two(self):
timeout = time.time() + 5
while self.processing_mode == ThreadedTask.MODE_TWO and time.time() < timeout:
print("function_two")
time.sleep(0.3)

self.processing_mode = 0

def key_pressed(self, event):
self.x = 2


And lastly, I feel like I'm going to encounter race conditions, what if the Keyboard calls the key_pressed function which modifies the value of x while function_one is trying to access x. I know very little about threading, this is the first time I've tried it. The above code runs fine in my tests, but I read that race conditions are unpredictable, so that it running fine doesn't necessarily mean there isn't a problem.



To run the program:



task = ThreadedTask()
task.start()









share|improve this question




























    up vote
    0
    down vote

    favorite












    I'm creating a PyQt Application. The main core/logic of the program runs in a separate loop I've created on a new thread. The core needs to be listening for defined hotkeys that will in turn trigger some functionality. I've removed all the logic and just made a dummy test scenario, as I'm really looking for feedback/help on structure/architecture. I'm very new to all of this.



    I created a Keyboard class that also runs in its own thread, where you can register functions that will get called when a key is pressed.



    import threading
    from win32 import win32api
    import win32con
    import pythoncom
    import pyHook

    class Keyboard(threading.Thread):
    def __init__(self):
    super().__init__()
    self.hook_manager = pyHook.HookManager()
    self.hook_manager.KeyDown = self.on_keyboard_event

    self._callbacks =

    self.is_running = False

    def stop(self):
    self.is_running = False
    self.hook_manager.UnhookKeyboard()

    def run(self):
    self.is_running = True
    self.hook_manager.HookKeyboard()

    while self.is_running:
    pythoncom.PumpWaitingMessages()

    def press(*args):
    for arg in args:
    win32api.keybd_event(arg, 0, 0, 0)

    def release(*args):
    for arg in args:
    win32api.keybd_event(arg, 0, win32con.KEYEVENTF_KEYUP, 0)

    def register_callback(self, f):
    self._callbacks.append(f)

    def on_keyboard_event(self, event):
    print(event.KeyID)
    for callback in self._callbacks:
    callback(event)

    return True


    Now the core of the application. It has two main functions: it needs to switch between when running and the second one needs to run for a set amount of time then revert back to the first. The way I've handled this switching and looping seems really bad.



    import time


    class ThreadedTask(threading.Thread):
    MODE_ONE = 0
    MODE_TWO = 1

    def __init__(self):
    super().__init__()
    self.is_running = False
    self.processing_mode = 0

    self.x = 0

    self.keyboard = Keyboard()
    self.keyboard.register_callback(self.key_pressed)
    self.keyboard.start()

    def stop(self):
    self.is_running = False

    def run(self):
    self.is_running = True

    while self.is_running:
    if self.processing_mode == ThreadedTask.MODE_ONE:
    self.function_one()
    elif self.processing_mode == ThreadedTask.MODE_TWO:
    self.function_two()

    def function_one(self):
    print("function_one")
    y = self.x + 2

    if y == 4:
    self.processing_mode = 1
    self.x = 0

    time.sleep(0.3)

    def function_two(self):
    timeout = time.time() + 5
    while self.processing_mode == ThreadedTask.MODE_TWO and time.time() < timeout:
    print("function_two")
    time.sleep(0.3)

    self.processing_mode = 0

    def key_pressed(self, event):
    self.x = 2


    And lastly, I feel like I'm going to encounter race conditions, what if the Keyboard calls the key_pressed function which modifies the value of x while function_one is trying to access x. I know very little about threading, this is the first time I've tried it. The above code runs fine in my tests, but I read that race conditions are unpredictable, so that it running fine doesn't necessarily mean there isn't a problem.



    To run the program:



    task = ThreadedTask()
    task.start()









    share|improve this question


























      up vote
      0
      down vote

      favorite









      up vote
      0
      down vote

      favorite











      I'm creating a PyQt Application. The main core/logic of the program runs in a separate loop I've created on a new thread. The core needs to be listening for defined hotkeys that will in turn trigger some functionality. I've removed all the logic and just made a dummy test scenario, as I'm really looking for feedback/help on structure/architecture. I'm very new to all of this.



      I created a Keyboard class that also runs in its own thread, where you can register functions that will get called when a key is pressed.



      import threading
      from win32 import win32api
      import win32con
      import pythoncom
      import pyHook

      class Keyboard(threading.Thread):
      def __init__(self):
      super().__init__()
      self.hook_manager = pyHook.HookManager()
      self.hook_manager.KeyDown = self.on_keyboard_event

      self._callbacks =

      self.is_running = False

      def stop(self):
      self.is_running = False
      self.hook_manager.UnhookKeyboard()

      def run(self):
      self.is_running = True
      self.hook_manager.HookKeyboard()

      while self.is_running:
      pythoncom.PumpWaitingMessages()

      def press(*args):
      for arg in args:
      win32api.keybd_event(arg, 0, 0, 0)

      def release(*args):
      for arg in args:
      win32api.keybd_event(arg, 0, win32con.KEYEVENTF_KEYUP, 0)

      def register_callback(self, f):
      self._callbacks.append(f)

      def on_keyboard_event(self, event):
      print(event.KeyID)
      for callback in self._callbacks:
      callback(event)

      return True


      Now the core of the application. It has two main functions: it needs to switch between when running and the second one needs to run for a set amount of time then revert back to the first. The way I've handled this switching and looping seems really bad.



      import time


      class ThreadedTask(threading.Thread):
      MODE_ONE = 0
      MODE_TWO = 1

      def __init__(self):
      super().__init__()
      self.is_running = False
      self.processing_mode = 0

      self.x = 0

      self.keyboard = Keyboard()
      self.keyboard.register_callback(self.key_pressed)
      self.keyboard.start()

      def stop(self):
      self.is_running = False

      def run(self):
      self.is_running = True

      while self.is_running:
      if self.processing_mode == ThreadedTask.MODE_ONE:
      self.function_one()
      elif self.processing_mode == ThreadedTask.MODE_TWO:
      self.function_two()

      def function_one(self):
      print("function_one")
      y = self.x + 2

      if y == 4:
      self.processing_mode = 1
      self.x = 0

      time.sleep(0.3)

      def function_two(self):
      timeout = time.time() + 5
      while self.processing_mode == ThreadedTask.MODE_TWO and time.time() < timeout:
      print("function_two")
      time.sleep(0.3)

      self.processing_mode = 0

      def key_pressed(self, event):
      self.x = 2


      And lastly, I feel like I'm going to encounter race conditions, what if the Keyboard calls the key_pressed function which modifies the value of x while function_one is trying to access x. I know very little about threading, this is the first time I've tried it. The above code runs fine in my tests, but I read that race conditions are unpredictable, so that it running fine doesn't necessarily mean there isn't a problem.



      To run the program:



      task = ThreadedTask()
      task.start()









      share|improve this question















      I'm creating a PyQt Application. The main core/logic of the program runs in a separate loop I've created on a new thread. The core needs to be listening for defined hotkeys that will in turn trigger some functionality. I've removed all the logic and just made a dummy test scenario, as I'm really looking for feedback/help on structure/architecture. I'm very new to all of this.



      I created a Keyboard class that also runs in its own thread, where you can register functions that will get called when a key is pressed.



      import threading
      from win32 import win32api
      import win32con
      import pythoncom
      import pyHook

      class Keyboard(threading.Thread):
      def __init__(self):
      super().__init__()
      self.hook_manager = pyHook.HookManager()
      self.hook_manager.KeyDown = self.on_keyboard_event

      self._callbacks =

      self.is_running = False

      def stop(self):
      self.is_running = False
      self.hook_manager.UnhookKeyboard()

      def run(self):
      self.is_running = True
      self.hook_manager.HookKeyboard()

      while self.is_running:
      pythoncom.PumpWaitingMessages()

      def press(*args):
      for arg in args:
      win32api.keybd_event(arg, 0, 0, 0)

      def release(*args):
      for arg in args:
      win32api.keybd_event(arg, 0, win32con.KEYEVENTF_KEYUP, 0)

      def register_callback(self, f):
      self._callbacks.append(f)

      def on_keyboard_event(self, event):
      print(event.KeyID)
      for callback in self._callbacks:
      callback(event)

      return True


      Now the core of the application. It has two main functions: it needs to switch between when running and the second one needs to run for a set amount of time then revert back to the first. The way I've handled this switching and looping seems really bad.



      import time


      class ThreadedTask(threading.Thread):
      MODE_ONE = 0
      MODE_TWO = 1

      def __init__(self):
      super().__init__()
      self.is_running = False
      self.processing_mode = 0

      self.x = 0

      self.keyboard = Keyboard()
      self.keyboard.register_callback(self.key_pressed)
      self.keyboard.start()

      def stop(self):
      self.is_running = False

      def run(self):
      self.is_running = True

      while self.is_running:
      if self.processing_mode == ThreadedTask.MODE_ONE:
      self.function_one()
      elif self.processing_mode == ThreadedTask.MODE_TWO:
      self.function_two()

      def function_one(self):
      print("function_one")
      y = self.x + 2

      if y == 4:
      self.processing_mode = 1
      self.x = 0

      time.sleep(0.3)

      def function_two(self):
      timeout = time.time() + 5
      while self.processing_mode == ThreadedTask.MODE_TWO and time.time() < timeout:
      print("function_two")
      time.sleep(0.3)

      self.processing_mode = 0

      def key_pressed(self, event):
      self.x = 2


      And lastly, I feel like I'm going to encounter race conditions, what if the Keyboard calls the key_pressed function which modifies the value of x while function_one is trying to access x. I know very little about threading, this is the first time I've tried it. The above code runs fine in my tests, but I read that race conditions are unpredictable, so that it running fine doesn't necessarily mean there isn't a problem.



      To run the program:



      task = ThreadedTask()
      task.start()






      python python-3.x multithreading thread-safety






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited 11 mins ago









      Jamal

      30.2k11115226




      30.2k11115226










      asked 4 hours ago









      kainev

      311




      311



























          active

          oldest

          votes











          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f208800%2fthreaded-global-keyboard-events%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown






























          active

          oldest

          votes













          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes
















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Code Review Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          Use MathJax to format equations. MathJax reference.


          To learn more, see our tips on writing great answers.





          Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


          Please pay close attention to the following guidance:


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f208800%2fthreaded-global-keyboard-events%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Costa Masnaga

          Fotorealismo

          Sidney Franklin