Hello everybody,

Question:

I’m trying to write a program that should move an X in a defined frame. For this purpose, a while loop should start with a KeyPress and stop with a KeyReleases.

I only found the multithreading or multiprocessing methods on the Internet to do that simultaneously. The multithreading itself works for a certain amount of time (always differently) and then I get various error messages or the program freezes completely.

Often it is interrupted by signal 11 sigsegv.

The code is:

# GLOBAL
import faulthandler; faulthandler.enable()
import sys
import time
import requests

from pynput import keyboard
from pynput.keyboard import Key
from qtpy import QtWidgets
from PyQt5 import QtCore
from ui.mainwindow import Ui_MainWindow

# Globale Variablen:
PAN = 250
TILT = 150
SPEED = 1
TIME = 0.01
KEYS = []
THREADS = 0
DIRECTION = []

# HTTP SECTION


payload = {"name": "MK"}
r = requests.get("https://httpbin.org/get", params=payload)
print(r.status_code)

# BUILD GUI
app = QtWidgets.QApplication(sys.argv)


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = Ui_MainWindow()  # Laden des Style Frames
        self.ui.setupUi(self)  # Funktion aus Ui_MainWindow ausführen (Fenster zeichnen)
        self.ui.tilt.setText(str(TILT))
        self.ui.pan.setText(str(PAN))
        self.ui.speed.setText(str(SPEED))

        ## Button Funktionsaufrufe:
        self.thread = {}
        self.ui.btnUp.clicked.connect(self.moveUP)
        self.ui.btnDown.clicked.connect(self.moveDOWN)
        self.ui.btnLeft.clicked.connect(self.moveLEFT)
        self.ui.btnRight.clicked.connect(self.moveRIGHT)
        self.ui.btnCenter.clicked.connect(self.stop_worker)

        self.ui.speed.textChanged.connect(self.setSpeed)

    # Funktion zum bewegen über die Buttons 

    def startUp(self):
        self.thread[1] = ThreadClass(parent=None, index=1)
        self.thread[1].start()
        self.thread[1].any_signal.connect(self.my_function)
        time.sleep(0.5)
        self.thread[1].stop()
        print('init thread 1 on startup')
        self.thread[2] = ThreadClass(parent=None, index=2)
        self.thread[2].start()
        time.sleep(0.5)
        self.thread[2].stop()
        print('init thread 2 on startup')
        self.thread[3] = ThreadClass(parent=None, index=3)
        self.thread[3].start()
        time.sleep(0.5)
        self.thread[3].stop()
        print('init thread 3 on startup')
        self.thread[4] = ThreadClass(parent=None, index=4)
        self.thread[4].start()
        time.sleep(0.5)
        self.thread[4].stop()
        print('init thread 4 on startup')
        print('... n n n')

        print('Status = Ready:')
        print('Keys = ', KEYS)

    def moveUP(self):
        if bool(self.thread[2].isRunning()):
            self.stopDOWN()
        self.thread[1] = ThreadClass(parent=None, index=1)
        self.thread[1].start()
        self.thread[1].any_signal.connect(self.my_function)
        #self.ui.btnUp.setEnabled(False)

    def moveDOWN(self):
        # global DIRECTION
        # DIRECTION.append('Down')
        self.thread[2] = ThreadClass(parent=None, index=2)
        self.thread[2].start()
        self.thread[2].any_signal.connect(self.my_function)
        #self.ui.btnDown.setEnabled(False)

    def moveLEFT(self):
        # global DIRECTION
        # DIRECTION.append('Left')
        if bool(self.thread[4].isRunning()):
            self.stopRIGHT()
        self.thread[3] = ThreadClass(parent=None, index=3)
        self.thread[3].start()
        self.thread[3].any_signal.connect(self.my_function)
        #self.ui.btnLeft.setEnabled(False)

    def moveRIGHT(self):
        # global DIRECTION
        # DIRECTION.append('Right')
        self.thread[4] = ThreadClass(parent=None, index=4)
        self.thread[4].start()
        self.thread[4].any_signal.connect(self.my_function)
        #self.ui.btnRight.setEnabled(False)

    def stopUP(self):
        global KEYS
        if bool(self.thread[1].isRunning()):
            self.thread[1].stop()
            #self.ui.btnUp.setEnabled(True)
            KEYS.remove(Key.up)
        # DIRECTION.remove('Up')

    def stopDOWN(self):
        global KEYS
        if bool(self.thread[2].isRunning()):
            self.thread[2].stop()
            #self.ui.btnDown.setEnabled(True)
            KEYS.remove(Key.down)

        # DIRECTION.remove('Down')

    def stopLEFT(self):
        global KEYS
        if bool(self.thread[3].isRunning()):
            self.thread[3].stop()
            #self.ui.btnLeft.setEnabled(True)
            KEYS.remove(Key.left)
        # DIRECTION.remove('Left')

    def stopRIGHT(self):
        global KEYS
        if bool(self.thread[4].isRunning()):
            self.thread[4].stop()
            #self.ui.btnRight.setEnabled(True)
            KEYS.remove(Key.right)
        # DIRECTION.remove('Right')

    def stop_worker(self):
        global DIRECTION

        if 'Up' in DIRECTION:
            self.thread[1].stop()
            #self.ui.btnUp.setEnabled(True)
            DIRECTION.remove('Up')

        if 'Down' in DIRECTION:
            self.thread[2].stop()
            #self.ui.btnDown.setEnabled(True)
            DIRECTION.remove('Down')

        if 'Left' in DIRECTION:
            self.thread[3].stop()
            #self.ui.btnLeft.setEnabled(True)

        if 'Right' in DIRECTION:
            self.thread[4].stop()
            #self.ui.btnRight.setEnabled(True)
            DIRECTION.remove('Right')

    def my_function(self, counter):
        global KEYS
        cnt = counter
        index = self.sender().index
        if index == 1:
            print(cnt)

    def setSpeed(self):
        global SPEED
        if self.ui.speed.text():
            SPEED = int(self.ui.speed.text())

    # Funktion zum bewegen über die Tasten 
    def on_press(key):
        global KEYS
        if key not in KEYS:
            if key == Key.up:
                MainWindow.moveUP(window)
                KEYS.append(key)
                print('added: ' + str(key))
            if key == Key.down:
                if Key.up in KEYS:
                    pass
                else:
                    MainWindow.moveDOWN(window)
                    KEYS.append(key)
                    print('added: ' + str(key))

            if key == Key.left:
                MainWindow.moveLEFT(window)
                KEYS.append(key)
                print('added: ' + str(key))
            if key == Key.right:
                if Key.left in KEYS:
                    pass
                else:
                    MainWindow.moveRIGHT(window)
                    KEYS.append(key)
                    print('added: ' + str(key))

    def on_release(key):
        global KEYS
        if key in KEYS:
            if key == Key.up and Key.up in KEYS:
                MainWindow.stopUP(window)
            if key == Key.down and Key.down in KEYS:
                MainWindow.stopDOWN(window)
            if key == Key.left and Key.left in KEYS:
                MainWindow.stopLEFT(window)
            if key == Key.right and Key.right in KEYS:
                MainWindow.stopRIGHT(window)
        print(KEYS)
        if key == Key.esc:
            MainWindow.stop_worker(window)

    # Collect events until released
    listener = keyboard.Listener(
        on_press=on_press,
        on_release=on_release)
    listener.start()


class ThreadClass(QtCore.QThread):
    any_signal = QtCore.pyqtSignal(int)

    def __init__(self, parent=None, index=0):
        super(ThreadClass, self).__init__(parent)
        self.index = index
        self.is_running = True

    def run(self):
        global TILT
        global PAN
        global TIME
        global SPEED
        print('Starting thread...', self.index)
        i = self.index
        while (True):
            if i == 1:
                if TILT <= 0:
                    pass
                else:
                    TILT -= SPEED
                    window.ui.tilt.setText(str(TILT))
                    window.ui.target.setGeometry(PAN, TILT, 10, 16)
                    # print("T", TILT)
                time.sleep(TIME)

            if i == 2:
                if TILT >= 301:
                    pass
                else:
                    TILT += SPEED
                    window.ui.tilt.setText(str(TILT))
                    window.ui.target.setGeometry(PAN, TILT, 10, 16)
                    # print('T', TILT)
                time.sleep(TIME)

            if i == 3:
                if PAN <= 0:
                    pass
                else:
                    PAN -= SPEED
                    window.ui.pan.setText(str(PAN))
                    window.ui.target.setGeometry(PAN, TILT, 10, 16)
                    # print("P", PAN)
                time.sleep(TIME)

            if i == 4:
                if PAN >= 500:
                    pass
                else:
                    PAN += SPEED
                    window.ui.pan.setText(str(PAN))
                    window.ui.target.setGeometry(PAN, TILT, 10, 16)
                    # print("P", PAN)
                time.sleep(TIME)

    def stop(self):
        self.is_running = False
        print('Stopping thread...', self.index)
        self.terminate()


window = MainWindow()

window.show()
window.startUp()

sys.exit(app.exec_())

I am currently programming in PyCharm on a MacBook with OS 12.6.

Asked By: Markus Kaiser

||

Answers:

I didn’t read all your code, but you are trying to access the GUI from a thread which is not the main thread. This is illegal and will result in crashes sooner or later.

Answered By: Jens