Seite 1 von 1

GUI Freezed trotz zweiten Thread

Verfasst: Donnerstag 26. November 2020, 22:42
von Space
Hallo,

ich sitze in letzter Zeit an einem Programm mit welchem ich von meinem PC aus meinen LED streifen steuern kann. Das Projekt dient für mich zur Übung :D .
Ich bin IT Student und möchte dementsprechend lernen wenn ich etwas falsch mache also falls euch Fehler an meinem Code auffallen könnt ihr mir gerne bescheid sagen (ok der Code ist wahrscheinlich voll davon :P )

Nun aber zu meinem Problem. Ich habe mit QT eine GUI gebaut und das Programm schickt die ausgewählte Farbe an einen Raspberry Pi der den LED streifen ansteuert. Ich habe mehrere Modi eingebaut. In einem Modus soll das Licht immer wieder heller und dunkler werden. (wave_mode) Das habe ich über eine while schleife organisiert, da ich schon wusste das die GUI durch die (mehr oder weniger unendliche while schleife ) freezed, habe ich die Funktion in einen extra Thread verlagert. Nun freezed die GUI trotzdem.

Da ich auf dem Gebiet Threading noch keine Erfahrung habe denke ich das dort mein Fehler liegt.
Ich freue mich über jede Hilfe
MFG space


(Achso, ich wusste nicht wo ich das Thema einordnen sollte, bitte verschiebt es einfach in den richtigen Bereich)
Hier noch mein Code:
Das ist die Main.py

Code: Alles auswählen

import os
import re
import time
import sys, time
from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtCore import (QCoreApplication, QPropertyAnimation, QDate, QDateTime, QMetaObject, QPoint, QRect, QSize, QTime, QUrl, Qt, QEvent)
from PySide2.QtGui import (QBrush, QColor, QConicalGradient, QCursor, QFont, QFontDatabase, QIcon, QKeySequence, QLinearGradient, QPalette, QPainter, QPixmap, QRadialGradient)
from PySide2.QtWidgets import *
from PySide2.QtCharts import QtCharts
from PySide2.QtWidgets import QMessageBox
from PySide2.QtWidgets import QFileDialog
import socket
import cv2
import PIL
import pyautogui
import threading
import time

from ui_main import Ui_MainWindow

from UI_Functions import *


class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)

        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        UIFunctions.uiDefinitions(self)
        self.show()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    sys.exit(app.exec_())
und das sind die GUI Funktionen

Code: Alles auswählen

from main import *

class UIFunctions(MainWindow):
    def __init__(self):
        pass



    def uiDefinitions(self):
        self.LM_Mode = 'static'
        self.t1 = Wave_Mode_Thread()
        self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server_addr = ("192.168.178.38", 5050)
        self.client_socket.connect(self.server_addr)

        #CONFIGUREATE HORIZONTAL SLIDERS
        self.ui.Blue_Horizontalslider.setMinimum(1)
        self.ui.Blue_Horizontalslider.setMaximum(255)

        self.ui.Red_horizontalslider.setMinimum(1)
        self.ui.Red_horizontalslider.setMaximum(255)

        self.ui.Green_Horizontaslider.setMinimum(1)
        self.ui.Green_Horizontaslider.setMaximum(255)

        # DEFINE RADIOBUTTONS FUNKTION
        self.ui.Static_Mode_Radiobutton.clicked.connect(lambda: UIFunctions.launche_static_mode(self))
        self.ui.Wave_Mode_RadioButton.clicked.connect(lambda: UIFunctions.launche_wave_mode(self))
        self.ui.Audio_Mode_RadioButton.clicked.connect(lambda: UIFunctions.launche_wave_mode(self))
        self.ui.Desktop_Mode_RadioButton.clicked.connect(lambda: UIFunctions.launche_desktop_mode(self))

        #HORIZONTAL DEF
        self.ui.Green_Horizontaslider.valueChanged.connect(lambda: UIFunctions.color_slider_update(self))
        self.ui.Red_horizontalslider.valueChanged.connect(lambda: UIFunctions.color_slider_update(self))
        self.ui.Blue_Horizontalslider.valueChanged.connect(lambda: UIFunctions.color_slider_update(self))

        #BUTTON DEF
        self.ui.pushButton.clicked.connect(lambda: UIFunctions.brightness_up(self))
        self.ui.pushButton_2.clicked.connect(lambda: UIFunctions.brightness_down(self))



    #DEFINE SOCKET SENDING FUNKTION
    def send(self, r, g, b, mode):
        input_list = str(int(r)) + ", " + str(int(g)) + ", " + str(int(b))+ ', ' + str(mode)
        self.client_socket.send(bytes(input_list, 'utf-8'))

    #LAUNCHE MODE
    def launche_static_mode(self):
        if self.LM_Mode != 'static':
            self.LM_Mode = 'static'
            self.t1.status = False

        self.ui.MutliColor_RadioButton.setEnabled(True)
        self.ui.OneColor_RadioButton.setEnabled(True)
        r = self.ui.Red_horizontalslider.value()
        g = self.ui.Green_Horizontaslider.value()
        b = self.ui.Blue_Horizontalslider.value()
        print(r, g, b)
        UIFunctions.send(self, r, g, b, self.LM_Mode)


    def launche_wave_mode(self):
        if self.LM_Mode != 'wave':
            self.LM_Mode = 'wave'
            self.ui.MutliColor_RadioButton.setDisabled(True)
            self.ui.OneColor_RadioButton.setDisabled(True)
        g = self.ui.Green_Horizontaslider.value()
        b = self.ui.Blue_Horizontalslider.value()
        r = self.ui.Red_horizontalslider.value()

        first_list = [r, g, b]

        self.t1.status = True
        self.t1.run(first_list, self)

    def launche_desktop_mode(self):
        self.t1.status = False

        self.working_picture = r"E:\PycharmProjects\Screenshot_Farbmittelwert\1.png"
        self.LM_Mode = 'desktop'
        self.ui.MutliColor_RadioButton.setDisabled(True)
        self.ui.OneColor_RadioButton.setDisabled(True)

        while self.LM_Mode == 'desktop':
            screenshot = pyautogui.screenshot()
            screenshot.save(self.working_picture)
            pic = cv2.imread(self.working_picture)

            blue = 0
            green = 0
            red = 0

            for x in range(0, 1080, 10):
                for y in range(0, 2560, 10):
                    blue = blue + pic[x][y][0]
            blue = blue / (2560 * 10.8)

            for x in range(0, 1080, 10):
                for y in range(0, 2560, 10):
                    green = green + pic[x][y][1]
            green = green / (2560 * 10.8)

            for x in range(0, 1080, 10):
                for y in range(0, 2560, 10):
                    red = red + pic[x][y][2]
            red = red / (2560 * 10.8)

            if blue < green:
                if green < red:
                    green = green * 0.8
                    blue = blue * 0.8
                red = red * 0.8
                blue = blue * 0.8
            green = green * 0.8
            red = red * 0.8

            UIFunctions.send(self, red, green, blue, self.LM_Mode)

    def brightness_up(self):
        g = self.ui.Green_Horizontaslider.value()
        b = self.ui.Blue_Horizontalslider.value()
        r = self.ui.Red_horizontalslider.value()

        if g < 246 and r < 246 and b < 246:
            if g > 10:
                self.ui.Green_Horizontaslider.setValue(g + 10)
            if b > 10:
                self.ui.Blue_Horizontalslider.setValue(b + 10)
            if r > 10:
                self.ui.Red_horizontalslider.setValue(r + 10)

        if g < 10 and r < 10 and b < 10:
            self.ui.Green_Horizontaslider.setValue(g + 10)
            self.ui.Blue_Horizontalslider.setValue(b + 10)
            self.ui.Red_horizontalslider.setValue(r + 10)
            
    def brightness_down(self):
        g = self.ui.Green_Horizontaslider.value()
        b = self.ui.Blue_Horizontalslider.value()
        r = self.ui.Red_horizontalslider.value()

        if g > 10:
            self.ui.Green_Horizontaslider.setValue(g - 10)
        if b > 10:
            self.ui.Blue_Horizontalslider.setValue(b - 10)
        if r > 10:
            self.ui.Red_horizontalslider.setValue(r - 10)


    def color_slider_update(self):
        if self.LM_Mode == 'static':
            UIFunctions.launche_static_mode(self)
        elif self.LM_Mode == 'wave':
            UIFunctions.launche_wave_mode(self)

class Wave_Mode_Thread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.status = False

    def run(self, list, trans_self):
        intensity = 60
        r = list[0]
        g = list[1]
        b = list[2]
        for i in list:
            if i > (255 - intensity):
                dif = i -(255 - intensity)
                for sub in list:
                    sub = sub - dif

        while self.status == True:
            for x in range(0, intensity):

                UIFunctions.send(trans_self, r + x, g + x, b + x, 'wave')
                time.sleep(0.025)
            for x in range(0, intensity):
                UIFunctions.send(trans_self, r + (intensity - x), g + (intensity -x), b + (intensity-x), 'wave')
                time.sleep(0.025)

Re: GUI Freezed trotz zweiten Thread

Verfasst: Donnerstag 26. November 2020, 23:27
von Sirius3
Du hast Kreisimporte, main importiert UI_Functions und UI_Functions importiert main. Das darf nicht sein. UIFunctions sollte in __init__ das __init__ der Basisklasse aufrufen.
Du benutzt ja auch diese Klasse völlig falsch, eigentlich sollte es gar keine zwei Klassen geben.
Man ruft keine Methoden über ihre Klasse auf, und übergibt self als erstes Argument, sondern benutzt self.Methode. Damit sind auch alle lambdas überflüssig.
Strings stückelt man nicht mit + zusammen, sondern benutzt Formatstrings.
socket.send ist falsch. Das garantiert nicht, dass alles gesendet wird. Auf der anderen Seite wird es auch nicht besser sein, da Du kein Protokoll benutzt und damit die ganze TCP-Kommunikation kaputt ist.
In launche_desktop_mode hast Du eine Endlosschleife, weil soweit ich sehe, self.LM_Mode nirgends verändert wird.
QT und Threading verträgt sich nicht, benutze QThreads. Auch dort darf man nicht einfach GUI-Elemente verändern. In `run` ist die erste for-Schleife wirkungslos und kann weg. Was soll da eigentlich gemacht werden?

Und nein, man wandelt keine ui-Dateien nach Python um, sondern nutzt loadui direkt.

Re: GUI Freezed trotz zweiten Thread

Verfasst: Donnerstag 26. November 2020, 23:36
von __blackjack__
@Space: Falsch ist schon mal der zirkuläre Import. Wenn Module sich gegenseitig importieren sind sie zu eng gekoppelt und sollten entweder gar nicht getrennt werden oder so, dass sie nicht gegenseitig abhängig sind.

Sternchen-Importe sind Böse™. Das macht den Code unnötig schwer nachzuvollziehen und es besteht die Gefahr von Namenskollisionen.

`UIFunctions` macht als Klasse keinen Sinn. Und die Funktionssammlung ist auch kein Hauptfenster. Die Vererbung ist also auch falsch. Und das `self` da eigentlich gar nicht das Objekt selbst ist, sondern das da immer ein anderes Objekt von einem anderen Typ übergeben wird, weil Du die ”Methoden” als Funktionen auf dem Klassenobjekt aufrufst, ist extrem verwirrend und natürlich auch falsch.

Im ganzen Programm wird kein Thread gestartet. Wenn Du die `run()`-Methode aufrufst, was man nicht macht, dann rufst Du einfach eine ganz normale Methode auf. `run()` darf keine Argumente erwarten/bekommen und einen Thread startet man mit der `start()`-Methode. Die auch keine Argumente erwartet.

Beim Senden über TCP muss man `sendall()` verwenden, denn `send()` garantiert nicht, dass alle Daten gesendet werden.

`list` ist der Name einer eingebauten Funktion/eines eingebauten Typs. Den Namen sollte man nicht an andere Werte binden.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase). Grunddaten haben nichts in Namen zu suchen.

Re: GUI Freezed trotz zweiten Thread

Verfasst: Freitag 27. November 2020, 08:58
von Space
Hey, vielen Dank erstmal für das Feedback.
Ich werde die nächste Zeit erst einmal den Code umbauen. Ist ja genug zu tun.

Mal schauen ob es dann funktioniert. Wenn ich auf weitere Probleme stoße melde ich mich.
noch mal vielen Dank

MFG Space

Re: GUI Freezed trotz zweiten Thread

Verfasst: Donnerstag 3. Dezember 2020, 19:02
von Space
So ich habe mir die Tipps angeschaut und bin auf ein paar Probleme gestoßen.
Ich habe die Klasse UI_Definitions komplett aufgelöst und in die Mainwindow Klasse integriert. Das funktioniert soweit.

1. @__blackjack__ meinte das die Funktionen nicht in eine Klasse mit Vererbung aus QMainwindow gehören. Bezieht sich das auf die (nun aufgelöste) Klasse UI_Definitions oder auf MainWindow?
2. Kennt ihr gute Quellen bei denen man das threading mittel QThreads nachlesen kann. Im Netzt finde ich nur semi-gut erklärende Beispiele.
3. @Sirius03 meinte in mein die TCP Kommunikation ist Kaputt da ich keine Protokoll nutze. Wie genau ist das gemeint. Gibt es auch hier gute Literatur um es zu lernen/ nach zu lesen?
4. Sollte die Definitionen der GUI also setzen der slider-werte, connecten der Buttons etc. in eine extra funktion oder können die auch mit in die __init__ funktion?

Re: GUI Freezed trotz zweiten Thread

Verfasst: Donnerstag 3. Dezember 2020, 20:09
von __deets__
Die Qt-Dokumentation selbst hat gute Beispiele, wie man mit QThreads umgehen soll. Ich wuerde das aber hier ueberhaupt nicht machen, sondern QSocketNotifier benutzen. Auch dazu ist die Qt-Doku da.