2

I am trying to create an app with 2 python files, the first reads the user input (either from python shell or directly from the keyboard via modules like pynput-keyboard) and stores it in a variable (after space is pressed). Then it sends the original string and a new transform string to the gui's labels.

The second creates the gui that has 2 labels and two buttons and takes the variable passed from the first file and changes the labels based on this variable (the buttons are used for data insert in a later step in a database).

I have created the gui and the python script that reads the input, but I am struggling on passing this variable to the second script and on changing the label dynamically.

Please see the code samples above.

read_user_input.py

import keyboard
import gui

def transform_string(string):
    if len(string)>0:
        return string[0:len(string)-1]
    else:
        return "0"

def logging_function():
    string = ""

    while(True):
        event = keyboard.read_event()
        if (str(event)[-5:-1] == "down"):
            key_pressed = ((str(event).rsplit(' ', 1))[0])[14:]

            if (key_pressed == "space"):
                """PASS THIS string AND transform_string TO OUR GUI's LABELS"""
                print("Pass to gui's labels")
            else:
                string = string + key_pressed




logging_function()

gui.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Hook</class>
 <widget class="QMainWindow" name="Hook">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>237</width>
    <height>120</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Hook</string>
  </property>
  <property name="windowIcon">
   <iconset>
    <normaloff>10-03-2020 thesis_01/keyboard.ico</normaloff>10-03-2020 thesis_01/keyboard.ico</iconset>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QLabel" name="label">
    <property name="geometry">
     <rect>
      <x>10</x>
      <y>0</y>
      <width>131</width>
      <height>31</height>
     </rect>
    </property>
    <property name="text">
     <string>Label1</string>
    </property>
   </widget>
   <widget class="QLabel" name="label_2">
    <property name="geometry">
     <rect>
      <x>10</x>
      <y>40</y>
      <width>131</width>
      <height>31</height>
     </rect>
    </property>
    <property name="mouseTracking">
     <bool>false</bool>
    </property>
    <property name="text">
     <string>Label2</string>
    </property>
   </widget>
   <widget class="QPushButton" name="pushButton">
    <property name="geometry">
     <rect>
      <x>160</x>
      <y>0</y>
      <width>61</width>
      <height>31</height>
     </rect>
    </property>
    <property name="text">
     <string>B1</string>
    </property>
   </widget>
   <widget class="QPushButton" name="pushButton_2">
    <property name="geometry">
     <rect>
      <x>160</x>
      <y>42</y>
      <width>61</width>
      <height>31</height>
     </rect>
    </property>
    <property name="text">
     <string>B2</string>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>237</width>
     <height>21</height>
    </rect>
   </property>
   <widget class="QMenu" name="menuHook">
    <property name="title">
     <string>Hook</string>
    </property>
   </widget>
   <widget class="QMenu" name="menuHelp">
    <property name="title">
     <string>Help</string>
    </property>
   </widget>
   <addaction name="menuHook"/>
   <addaction name="menuHelp"/>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

gui.py

from PyQt5 import QtWidgets, uic
import sys

class Ui(QtWidgets.QMainWindow):
    def __init__(self):
        super(Ui, self).__init__() # Call the inherited classes __init__ method
        uic.loadUi('untitled.ui', self) # Load the .ui file
        self.show() # Show the GUI

app = QtWidgets.QApplication(sys.argv) # Create an instance of QtWidgets.QApplication
window = Ui() # Create an instance of our class
app.exec_() # Start the application

The ui window is the above:

Ui

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
ppel123
  • 83
  • 3
  • 10
  • 1
    `str(x+2)` will not do anything because `x` is a string in your program. Do `x = int(input())` first. – Guimoute Mar 19 '20 at 20:33
  • 2
    It is rare that you want to use "input" to read data. Wouldn't it be better to use QLineEdit, QSpinBox, etc? That is, elements of the GUI to obtain user information, well, if you still want to get text from the console, then you should not use "input" but other more Qt friendly methods as I propose in the duplicate. – eyllanesc Mar 19 '20 at 20:36
  • You are right, but I want to explain that this gui will change the label based on user input,read directly from the keyboard. I have not provided the code that does the above functionality because it will make the question more complicated. The input is not being read within the PyQt window. – ppel123 Mar 19 '20 at 20:44
  • 1
    @ppel123 1) Do not simplify the problem for us because it generates an [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem), 2) In the duplicate question, implement the reading from the console and display it in a QLabel in a similar way to the one you want to be implemented with the "input()" function , you point out the pyinput but when not seeing anything relevant about it then I do not take it into consideration. Please use `@username` – eyllanesc Mar 19 '20 at 20:55
  • @eyllanesc I followed your advice and fixed it properly. Thanks. – ppel123 Mar 19 '20 at 21:06
  • 1
    @ppel123 From the code you provide I understand that the user writes text and the characters are stored until the "space" key is pressed, at that moment the information should be sent to the GUI but you have 2 QLabels, from what I understand in the first " enter "is sent to the first QLabel and the second" enter "is sent to the second QLabel Am I correct ?, What happens if after pressing the 2" enter "the user continues typing and presses a third" enter "? To which QLabel is this information sent or is it not sent anywhere? – eyllanesc Mar 19 '20 at 21:18
  • @eyllanesc The user types and when space is pressed the string and a new transform string are sent to the gui, in order to be shown in the 2 labels. Thanks for the help and the patience. – ppel123 Mar 19 '20 at 21:27

1 Answers1

3

What you have to do is use the pynput listener and analyze the keycode, then according to that build the word and send it by means of a signal to the GUI that according to its own logic add it to the QLabels:

read_user_input.py

from pynput.keyboard import Key, Listener

from PyQt5 import QtCore


class KeyMonitor(QtCore.QObject):
    wordPressed = QtCore.pyqtSignal(str)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.listener = Listener(on_release=self.on_release)
        self.word = ""

    def on_release(self, key):
        if hasattr(key, "char"):
            self.word += key.char
        if key == Key.space:
            self.wordPressed.emit(self.word)
            self.word = ""

    def stop_monitoring(self):
        self.listener.stop()

    def start_monitoring(self):
        self.listener.start()

gui.py

from itertools import cycle
import sys

from PyQt5 import QtCore, QtWidgets, uic

from read_user_input import KeyMonitor


class Ui(QtWidgets.QMainWindow):
    def __init__(self):
        super(Ui, self).__init__()
        uic.loadUi("untitled.ui", self)
        self.show()

        self.labels = cycle([self.label, self.label_2])

    @QtCore.pyqtSlot(str)
    def on_word_pressed(self, word):
        le = next(self.labels)
        le.setText(word)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = Ui()
    monitor = KeyMonitor()
    monitor.wordPressed.connect(window.on_word_pressed)
    monitor.start_monitoring()
    sys.exit(app.exec_())

Update:

If you want to use the keyboard library then you must use the following:

read_user_input.py

import threading

import keyboard

from PyQt5 import QtCore


class KeyMonitor(QtCore.QObject):
    wordPressed = QtCore.pyqtSignal(str)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.word = ""

        self.listener = threading.Thread(target=self._task, daemon=True)

    def _task(self):
        while True:
            event = keyboard.read_event()
            if event.event_type == keyboard.KEY_DOWN:
                if event.name == "space":
                    self.wordPressed.emit(self.word)
                    self.word = ""
                elif len(event.name) == 1:
                    self.word += event.name

    def start_monitoring(self):
        self.listener.start()

Update:

The logic of modifying the data is a trivial task that consists of converging the "word" and generating the new strings:

import sys

from PyQt5 import QtCore, QtWidgets, uic

from read_user_input import KeyMonitor


class Ui(QtWidgets.QMainWindow):
    def __init__(self):
        super(Ui, self).__init__()
        uic.loadUi("untitled.ui", self)
        self.show()

    @QtCore.pyqtSlot(str)
    def on_word_pressed(self, word):
        try:
            x = int(word)
        except ValueError:
            print("You must enter a whole number")
        else:
            self.label.setText("{}".format(x + 2))
            self.label_2.setText("Changed {}".format(x - 2))


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = Ui()
    monitor = KeyMonitor()
    monitor.wordPressed.connect(window.on_word_pressed)
    monitor.start_monitoring()
    sys.exit(app.exec_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Oo I will try it tomorrow thank you very much. Is the above coded provided by you easy to adapt to the keyboard module ( and not pynput ) ?? – ppel123 Mar 19 '20 at 22:18
  • one more question. If I want to pass 2 different strings from the read_user_input, one for the one label and one for the other label, what should I do?? – ppel123 Mar 20 '20 at 16:49
  • @ppel123 mmm, explain yourself better, the module "read_user_input" has a KeyMonitor class that detects the pressed keys and according to it detects each word using the "space", in your logic how would it detect the strings and the end of the strings? Or do you want that when the second space is pressed, the information of the 2 words is sent to the 2 QLabels? – eyllanesc Mar 20 '20 at 16:53
  • okkk I tried to explain this in the code after I corrected it, but I didnt't make it clear sorry. As you can see I have a string that is generated by pressing the space button, and a transformed_string, that is a string based on the first string, but with some changes (but it is a different string from the first). I want to pass the initial string to the first label and the second string (that is transformed through a function) to the second label. Hope I am clear now. – ppel123 Mar 20 '20 at 17:00
  • @ppel123 I understand that what you indicate has to do with your initial code where the word was converted to an integer, then 2 was added and the result was placed in the first QLabel, and in the second QLabel the same as the first QLabel + "Changed", am I correct? – eyllanesc Mar 20 '20 at 17:38
  • thank you very much I did what I wanted. One last question. If I want now, after the 2 words-numbers were successfully en printed in the labels, to push the button (B1 or B2) and pass the labels value to the read_user_input.py file again, should I use slots again, or it will be to messy? – ppel123 Mar 21 '20 at 19:25
  • @ppel123 That has nothing to do with your original question so I won't answer, bye. – eyllanesc Mar 21 '20 at 19:27