Python - Qt - AniGif

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Ulrich_G
User
Beiträge: 17
Registriert: Montag 25. Juni 2018, 07:20

Guten Morgen,
muss mich leider noch mal bei euch melden ... komme leider überhaupt nicht weiter mit meinem animierten Gif
Mein aktueller Code sieht wie folgt aus:

Code: Alles auswählen

from PyQt5 import QtCore, QtGui, QtWidgets, uic
import sys
import serial

class Ui(QtWidgets.QMainWindow):
    def __init__(self):
        super(Ui, self).__init__()
        uic.loadUi('firstgui.ui', self)
        self.btn_write.clicked.connect(self.read_Spannung)
        self.btn_read.clicked.connect(self.read_Kennung)
        self.btn_start.clicked.connect(self.statusPicture)
        self.btn_abbruch.clicked.connect(self.disable_FMInfo)
        self.status_txt = QtGui.QLabel()
        movie = QtGui.QMovie("etc/loading.gif")
        self.status_txt.setMovie(movie)
        movie.start()
        self.status_txt.setLayout(QtGui.QHBoxLayout())
        self.status_txt.layout().addWidget(QLabel('Loading...'))
        self.show()
    
    def statusPicture(self):
        pixmap = QPixmap('status_pass.png')
        self.pic_Status.setPixmap(pixmap)
        self.pic_Status.show()

    def read_Spannung(self):
        with serial.Serial('COM1',9600, 8, 'N',1) as ser:
            ser.write(b':MEASure:PRIMary:VOLTage:DC?\n')
            read_value = float(ser.readline())
            self.textBrowser.append(str(read_value)) 
            ser.close() 

    def read_Kennung(self):
        with serial.Serial('COM1',9600, 8, 'N',1) as ser:
            ser.write(b'*IDN?\n') 
            read_value = ser.readline()
            self.textBrowser.append(str(read_value)) 
            ser.close()

    def disable_FMInfo(self):
        with serial.Serial('COM1',115200, 8, 'N',1) as ser:
            ser.write(serial.to_bytes([0x04,0x00,0x00,0x26,0x01,0x00,0x00,0x01,0x00,0x84,0xA2,0x05])) 
            RS232_Antwort = (str(ser.read(13)))
            self.textBrowser.append(RS232_Antwort)
            ser.close()    

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = Ui()
    sys.exit(app.exec_())
Daraufhin bekomme ich immer die Meldung: E0602:Undefined variable 'QLabel'
was mache ich falsch?

Gruß
Ulrich
__deets__
User
Beiträge: 14541
Registriert: Mittwoch 14. Oktober 2015, 14:29

Da fehlt ein QtGui vor dem QLabel. Schau dir noch mal die Sektionen im Python Tutorial zu Importen an, um das besser zu verstehen.
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Ulrich_G: Die ganzen ``ser.close()`` sind übrigens überflüssig. Genau das erledigt die ``with``-Anweisung.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Ulrich_G
User
Beiträge: 17
Registriert: Montag 25. Juni 2018, 07:20

Guten Morgen,
ich komme leider keinen Schritt weiter und bin etwas verzweifelt :-(
Ein QtGui steht ab PyQt5 anscheinend nicht mehr vor dem QLabel. Mein Code würde an sich schon auch laufen nur wird mir keine Animation angezeigt. Füge ich ein self.pic_AniGif.show() hinzu wird mir das AniGif nur als Popup angezeigt... jedoch nicht im Hauptfenster.

Mein aktueller Code:

Code: Alles auswählen

import sys
import serial
from PyQt5 import uic
from PyQt5.QtWidgets import (QWidget, QToolTip, QPushButton, QApplication, QMessageBox, QMainWindow, QLabel, QHBoxLayout)
from PyQt5.QtCore import QCoreApplication
from PyQt5.QtGui import QFont, QMovie, QPixmap

class Ui(QMainWindow):
    def __init__(self):
        super().__init__()
        #super(Ui, self).__init__()
        self.title = 'PyQt5 image - pythonspot.com'
        uic.loadUi('firstgui.ui', self)
        self.btn_write.clicked.connect(self.read_Spannung)
        self.btn_read.clicked.connect(self.read_Kennung)
        self.btn_start.clicked.connect(self.statusPicture)
        self.btn_abbruch.clicked.connect(self.disable_FMInfo)
        self.btn_Bootloader.clicked.connect(self.load_Bootloader)
        self.btn_Firmware.clicked.connect(self.load_Firmware)
        self.initUI()

    def initUI(self):
        self.setWindowTitle(self.title)
        
        # Create widget
        self.pic_AniGif = QLabel()
        movie = QMovie('C:\warten.gif')
        self.pic_AniGif.setMovie(movie)
        movie.start()
        #self.pic_AniGif.setLayout(QHBoxLayout())
        #self.pic_AniGif.layout().addWidget(QLabel('Loading...'))
        self.show()
    
    def statusPicture(self):
        pixmap = QPixmap('status_pass.png')
        self.pic_Status.setPixmap(pixmap)
        self.pic_Status.show()
        self.pic_AniGif.show()

    def read_Spannung(self):
        with serial.Serial('COM1',9600, 8, 'N',1) as ser:
            ser.write(b':MEASure:PRIMary:VOLTage:DC?\n')
            read_value = float(ser.readline())
            self.textBrowser.append(str(read_value)) 
            #ser.close() 

    def read_Kennung(self):
        with serial.Serial('COM1',9600, 8, 'N',1) as ser:
            ser.write(b'*IDN?\n') 
            read_value = ser.readline()
            self.textBrowser.append(str(read_value)) 
            #ser.close()

    def disable_FMInfo(self):
        with serial.Serial('COM1',115200, 8, 'N',1) as ser:
            ser.write(serial.to_bytes([0x04,0x00,0x00,0x26,0x01,0x00,0x00,0x01,0x00,0x84,0xA2,0x05])) 
            RS232_Antwort = (str(ser.read(13)))
            self.textBrowser.append(RS232_Antwort)
            #ser.close()   

    def load_Bootloader(self):
        with serial.serial('COM1',115200,8,'N',1) as ser:
            ser.write()
                #strProgramName = "C:\SMC\ToolsPart1\DownloadPart1.exe"
                #strArgument = COMPort & " " & DB_Pfad & "ToolsPart1\bootloader.bin"
            #ser.close() 

    def load_Firmware(self):
        with serial.serial('COM1',115200,8,'N',1) as ser:
            ser.write()
                #strProgramName = "C:\SMC\ToolsPart1\DownloadPart1.exe"
                #strArgument = COMPort & " " & DB_Pfad & "ToolsPart1\" & Firmware & " protect"
            #ser.close() 

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Ui()
    sys.exit(app.exec_())
Wäre es möglich meinen Code so abzuändern damit ich auf einen grünen Zweig komme? bin momentan etwas ratlos und auf Hilfe angewiesen.

Gruß
Ulrich
__deets__
User
Beiträge: 14541
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich mag mich vertan haben, es muss vielleicht QWidgets sein. Aber nochmal: du musst die Importe verstehen. Die Qt Dokumentation separiert die verschiedenen Klasse klar in Teilprojekte, und PyQt bildet das nach. Wenn in der Doku steht, das etwas Teil von QtCore ist, dann musst du das von da holen. Und entsprechend auch für die anderen.
__deets__
User
Beiträge: 14541
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nachtrag: ich habe deinen Post missverstanden. Vergiss meine letzte Bemerkung.
__deets__
User
Beiträge: 14541
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du musst dem QLabel dein Fenster als parent übergeben. QWidgets formen eine Hierarchie, und das steht auch so in dem Beispiel das ich dir gezeigt habe. Da bekommt das QLabel ein this-Argument, was irgendeine Fenster oder Widget Instanz ist.
Ulrich_G
User
Beiträge: 17
Registriert: Montag 25. Juni 2018, 07:20

Hallo,
konnte das Problem jetzt lösen.
Hier der Code ... wenn es jemanden interessiert:

Code: Alles auswählen

import sys
import serial
import subprocess
from PyQt5 import uic
from PyQt5.QtWidgets import (QWidget, QMainWindow, QPushButton, QApplication, QLabel, QSizePolicy, QVBoxLayout)
from PyQt5.QtCore import QSize, QByteArray
from PyQt5.QtGui import QFont, QMovie, QPixmap#, QFontMetrics, QPainter
import ctypes  # An included library with Python install.

class SMCTool(QWidget):
    def __init__(self, filename, title, parent=None):
        QWidget.__init__(self, parent)
        uic.loadUi('frame.ui', self)

        self.btn_reboot.clicked.connect(self.Set_Reboot)
        self.btn_FMInfo.clicked.connect(self.Set_disable_FMInfo)
        self.btn_led_green.clicked.connect(self.Set_LEDgreen)
        self.btn_status.clicked.connect(self.Read_Status)
        self.btn_start.clicked.connect(self.statusPicture)
        self.btn_abbruch.clicked.connect(self.Set_disable_FMInfo)
        self.btn_bootloader.clicked.connect(self.load_Bootloader)
        self.btn_firmware.clicked.connect(self.load_Firmware)

        # Load the file into a QMovie
        self.movie = QMovie(filename, QByteArray(), self)
        self.status_txt = QLabel()

        # Create the layout
        self.main_layout.addWidget(self.status_txt)

        # Add the QMovie object to the label
        self.movie.setCacheMode(QMovie.CacheAll)
        self.movie.setSpeed(100)
        self.status_txt.setMovie(self.movie)
        self.movie.start()

    def statusPicture(self):
        pixmap = QPixmap('status_pass.png')
        self.pic_Status.setPixmap(pixmap)
        self.pic_Status.show()
    
    def Set_Reboot(self):
        with serial.Serial('COM1',115200, 8, 'N',1) as ser:
            ser.write(serial.to_bytes([0x04, 0xFF, 0xFF, 0x27, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x6E, 0xD3, 0x05]))

    def Set_LEDgreen(self):
        with serial.Serial('COM1',115200, 8, 'N',1) as ser:
            ser.write(serial.to_bytes([0x04, 0xFF, 0xFF, 0x23, 0x01, 0x00, 0x03, 0x00, 0x02, 0xB4, 0xAD, 0x05]))  

    def Read_Status(self):
        with serial.Serial('COM1',115200, 8, 'N',1) as ser:
            ser.write(serial.to_bytes([0x04,0x00,0x00,0x26,0x01,0x00,0x00,0x01,0x00,0x84,0xA2,0x05])) 
            RS232_Antwort = (str(ser.read(13)))
            self.textBrowser.append(RS232_Antwort)
            #ser.close()   

    def Set_disable_FMInfo(self):
        with serial.Serial('COM1',115200, 8, 'N',1) as ser:
            ser.write(serial.to_bytes([0x04,0x00,0x00,0x26,0x01,0x00,0x00,0x01,0x00,0x84,0xA2,0x05])) 
            RS232_Antwort = str(ser.read(13))
            self.textBrowser.append(RS232_Antwort) 

    def load_Bootloader(self):
        p = subprocess.Popen(['C:\\SMC\\ToolsPart1\\DownloadPart1.exe', '1', 'C:\\SMC\\ToolsPart1\\bootloader.bin'], stdout=subprocess.PIPE)
        p.wait()
        str1 = str(p.stdout.read(), 'utf-8')
        print(str1)
        str2 = "Done"
        if str1.find(str2) > 0:     
            Mbox('Upload', 'Prozess erfolgreich abgeschlossen', 1) 
        else:
            Mbox('Upload', 'Prozess fehlerhaft', 1) 

    def load_Firmware(self):
        p = subprocess.Popen(['C:\\SMC\\ToolsPart1\\DownloadPart1.exe', '1', 'C:\\SMC\\ToolsPart1\\SMC47_crc_encr_V5_0.bin'], stdout=subprocess.PIPE)
        p.wait()
        str1 = str(p.stdout.read(), 'utf-8')
        print(str1)
        str2 = "Done"
        if str1.find(str2) > 0:     
            Mbox('Upload', 'Prozess erfolgreich abgeschlossen', 1) 
        else:
            Mbox('Upload', 'Prozess fehlerhaft', 1) 

def Mbox(title, text, style):
    return ctypes.windll.user32.MessageBoxW(0, text, title, style)

if __name__ == "__main__":
    gif = "Movie.gif"
    app = QApplication(sys.argv)
    player = SMCTool(gif, "was")
    player.show()
    sys.exit(app.exec_())
Danke für die Unterstützung

Gruß
Ulrich
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

Du solltest Dich auf eine Schreibweise beschränken, also die Methoden entweder nach QT-Style oder in Pythonkonvention benennen und nicht mischen und noch eigene Varianten erfinden, also entweder `readStatus` oder `read_status`, und das einheitlich für alle Methoden.
Es ist selten sinnvoll, die Stringrepräsentation von Bytes zu verwenden. Was sinnvoll ist, kommt drauf an, was Du mit der RS232_Anwort weiter machst.
`Read_Status` und `Set_disable_FMInfo` sind identisch, ist das so gewollt?
Benutze statt subprocess.Popen subprocess.run oder subprocess.check_output.
Variablen durchzunummerieren und dann noch so generische Namen wie str1 und str2 zu verwenden, sind ein Zeichen dafür, dass man nicht weiß, um was es sich bei den Werten handelt. Die Zuweisung der Konstanten ist komplett überflüssig, statt find benutze den in-Operator. Beim Benutzen von subprocess.check_output sollte hoffentlich schon ein nicht erfolgreicher Lauf eine Exception werfen, so dass das fragile Parsen des Outputs überflüssig ist.
Warum benutzt Du user32.MessageBox statt einer QT-Messagebox?
Ulrich_G
User
Beiträge: 17
Registriert: Montag 25. Juni 2018, 07:20

Vielen Dank für deine Tipps . Komme schon viel besser damit klar.

Hab noch eine kleine Frage:
Mein animiertes Gif wird während eines Uploads (Fremdprogramm über Exe) unterbrochen bis der Upload durchgeführt wurde. Es bleibt also stehen.
Kann man das umgehen? Welchen Ansatz würdet ihr mir hierzu empfehlen?

Gruß
Ulrich
__deets__
User
Beiträge: 14541
Registriert: Mittwoch 14. Oktober 2015, 14:29

Na du wartest ja auch darauf, dass der Prozess beendet wird. Dann blockiert die GUI. Da musst du anders rangehen, zb über eine Timer periodisch prüfen, ob der Prozess schon fertig ist.
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ich sehe beim Aufrufen der Prozesse noch ein weiteres Problem: erst `wait()` und dann die gepipete Ausgabe lesen führt zu einem Deadlock wenn das Programm mehr ausgibt als in den Puffer zwischen den beiden Prozesse passt.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@__deets__: dann muß man aber auch periodisch den Ausgabepuffer leeren, also z.B. über ein nicht-blockierendes read.
Antworten