Signale und emit

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Omm
User
Beiträge: 90
Registriert: Samstag 7. April 2018, 14:05

Hallo zusammen
ich habe eine Verständnisfrage zu Signale.
Wenn ich das Signal wie nachstehend mit einem Timer aufrufe, wird es ausgeführt.

Code: Alles auswählen

    @pyqtSlot()
    def update_label(self):
        self.sig_update_label_log.emit(True, 'Backup erstellt')

    def backup_timer(self):
        self.timer_backup = QTimer()
        self.timer_backup.timeout.connect(self.update_label)
        self.timer_backup.start(10000)
wenn ich jedoch das Signal wie nachstehend starten möchte, passiert nichts.
Beispielhaft so:

Code: Alles auswählen

  def backup_timer(self):
	self.sig_update_label_log.emit(True, 'Backup erstellt')
  
Kann mir bitte jemand sagen, wie ich das Problem lösen kann. Aus dem Netz heraus werde ich leider nicht schlau.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das hat einen Grund der außerhalb dieses Ausschnitts liegt. Am wahrscheinlichsten hast du nich keine Verbindung des Signals zu einem Slot.
Omm
User
Beiträge: 90
Registriert: Samstag 7. April 2018, 14:05

Der Einfachheit habe ich mal die Maindatei reinkopiert.
Das QLabel (self.letzter_eintrag) wird aus mehren Formularen überschrieben(aktualisiert).
Jedoch läuft letztendes alles über die Log Datei.
Macht es da Sinn, in der Log Datei eine Klasse Signale zu machen?

Code: Alles auswählen

#!/usr/bin/env python
import locale
import sys
from PyQt5.QtCore import Qt, QTimer, pyqtSlot
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QMainWindow, QApplication
from worklogapp.frm_projekt_eintragen import WindowProjektEintragen
from worklogapp.frm_barButtons import Bar_Buttons
from worklogapp.log import Log
from worklogapp.frm_container import  AnimButton, AnimLabel
import qtmodern.styles
import qtmodern.windows


class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        locale.setlocale(locale.LC_ALL, 'de')
        super(MainWindow, self).__init__(*args, **kwargs)
        self.iniSize = False
        self.secondsize = False
        self.top = 1
        self.left = 1
        self.width = 180
        self.height = 80
        self.width_min = self.size().width()
        self.height_min = self.size().height()
        self.setWindowTitle('WL-A')
        self.setGeometry(self.left, self.top, self.width, self.height)
        self.setMouseTracking(True)

        self.bar_buttons = Bar_Buttons()
        self.layout_frm = QVBoxLayout()
        self.V_bar_button = QVBoxLayout()
        self.layout = QHBoxLayout()
        self.widget = QWidget()
        self.timer_hover = QTimer()

        self.frm_projekt_eintragen = WindowProjektEintragen()
        self.frm_projekt_eintragen.sig_update_label.connect(self.update_textfeld_letzter_eintrag)
        self.log = Log()
        self.log.backup_timer()
        self.log.update_label()
        self.log.sig_update_label_log.connect(self.update_textfeld_from_log)

        self.initUI()

    def initUI(self):
        # Alles Randlos setzen
        self.layout_frm.setContentsMargins(2, 2, 2, 2)
        self.layout_frm.setSpacing(0)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.setSpacing(0)
        self.V_bar_button.setContentsMargins(0, 0, 0, 0)
        self.V_bar_button.setSpacing(0)

        # Label laden letzter Eintrag
        self.letzter_eintrag = AnimLabel(self.log.letzer_eintrag_auslesen().to_string())
        self.letzter_eintrag.setObjectName('letzter_eintrag')
        self.letzter_eintrag.setUpdatesEnabled(True)
        self.letzter_eintrag.setAlignment(Qt.AlignCenter)

        # Raster add alle Objekte
#        self.bt_stop_anim_qlabel= AnimButton('testbutton')
        self.V_bar_button.addWidget(self.bar_buttons)
        #self.layout_frm.addWidget(self.bt_stop_anim_qlabel)
        self.layout_frm.addWidget(self.letzter_eintrag)
        self.layout_frm.addWidget(self.frm_projekt_eintragen)

        # Einzelen Layout zur Main Layaut hinzufügen
        self.layout.addLayout(self.V_bar_button)
        self.layout.addLayout(self.layout_frm)

        # Layout auf Main legen und einpassen
        self.widget.setLayout(self.layout)
        self.setCentralWidget(self.widget)

        # Widget deaktivieren für Defaultgrösse
        self.frm_projekt_eintragen.setVisible(False)
        self.bar_buttons.setVisible(False)

    def enterEvent(self, event):
        # Maus auf dem Label(Main) "letzter_eintrag"
        self.timer_hover.stop()
        self.bar_buttons.setVisible(True)
        self.frm_projekt_eintragen.setVisible(True)
        self.update()
        if not self.iniSize:
            self.width_max = self.size().width()
            self.height_max = self.size().height()
            self.iniSize = True
            self.secondsize = True
        if self.secondsize:
            self.parent.setMinimumSize(self.width_max, self.height_max)
            self.parent.resize(self.width_max, self.height_max)

    def leaveEvent(self, event):
        # warten bis das Fenster verkleinert werden soll
        self.timer_hover.start(5000)
        self.timer_hover.timeout.connect(self.end_hoverd)

    def end_hoverd(self):
        # Fenster verkleinert
        self.timer_hover.stop()
        self.frm_projekt_eintragen.setVisible(False)
        self.bar_buttons.setVisible(False)
        self.update()
        self.parent.setMinimumSize(self.width, self.height + 7)
        self.parent.resize(self.width, self.height + 7)
        self.parent.update()

    def setparent(self,parent):
        self.parent = parent

    def update_textfeld_letzter_eintrag(self):
        self.letzter_eintrag.setText(self.log.letzer_eintrag_auslesen().to_string())
        self.setStyleSheet('')

    @pyqtSlot(bool, str)
    def update_textfeld_from_log(self, status, meldung):
        if status:
            self.letzter_eintrag.setText(meldung)
            self.setStyleSheet("""QLabel#letzter_eintrag{color: green;}""")
        else:
            self.letzter_eintrag.setText(meldung)
            self.setStyleSheet("""QLabel#letzter_eintrag{background-color: red;}""")
        self.timer_backup = QTimer()
        self.timer_backup.timeout.connect(self.update_textfeld_letzter_eintrag)
        self.timer_backup.start(3000)


def main():
    app = QApplication(sys.argv)
    qtmodern.styles.dark(app)
    window = qtmodern.windows.ModernWindow(MainWindow())
    window.w.setparent(window)
    window.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

"""

def main():
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()
"""

die Logdatei ist auf das Minimum reduziert.

Code: Alles auswählen


class Log(QObject):
    sig_update_label_log = pyqtSignal(bool, str)

    def __init__(self, *args, **kwargs):
        super(Log, self).__init__(*args, **kwargs)
        self.log_entry = Log_entry()
        ...

    def irgendEineFunktion(self): 
         ...
        self.update_label()


    def update_label(self):
        self.sig_update_label_log.emit(True, 'aber jetzt')
        
        

        
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wieso erfindest du denn immer noch logging neu? Und benutzt nicht dazu gedachte Modul?
Omm
User
Beiträge: 90
Registriert: Samstag 7. April 2018, 14:05

__deets__ kurz gesagt, ich komme mit dem logging Modul nicht zurecht. Ist für mein Level vom Code Verständnis zu hoch.
Ich habe zwar das Werkzeug(den Schraubendreher/logging), möchte aber damit einen Nagel einschlagen. In etwa so. :oops:
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Was ist denn daran schwer? Was klappt nicht?
Omm
User
Beiträge: 90
Registriert: Samstag 7. April 2018, 14:05

ok, jetzt sehe ich mein Problem. Ich hatte ein komplett falsches Bild, was das Modul macht.
Salopp gesagt, schaut das Modul ob sich was an der Zieldatei geändert hat und reagiert dementsprechend. Natürlich noch weit mehr als das, aber für mich ist dies die wichtigste Funktion.
Jetzt verstehe ich auch, wieso du das Modul erwähnt hast. (Ich dachte scho, ... :lol: )

Ich schau mir das Modul nochmals richtig an. Damit sollte einiges einfacher gehen. Jetzt habe ich bock darauf.

Danke für deine Beharrlichkeit. Daumen hoch
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich verstehe nicht so ganz, was du meinst. Aber wenn es funktioniert, ist ja alles gut.
Omm
User
Beiträge: 90
Registriert: Samstag 7. April 2018, 14:05

Zum allgemeinen Signale und emit habe ich noch Verständnis Probleme. Beispielhaft nur mit 'str'.
Nachstehend wie es funktioniert.
Also wenn ich in der class xyz was benötige passt es.

Code: Alles auswählen

import sys
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject
from PyQt5.QtWidgets import QApplication, QMainWindow


class xyz(QObject):
    def __init__(self, parent= None):
        super(xyz, self).__init__(parent)

    @pyqtSlot(str)
    def custom_slot(self, a):
        print(a)


class MainWindow(QMainWindow):
    message = pyqtSignal(str)

    def __init__(self, parent= None):
        super(MainWindow, self).__init__(parent)
        ini_xyz = xyz()
        self.message.connect(ini_xyz.custom_slot)
        self.message.emit('xxxxx')



app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
Nun möchte ich es aber gedreht haben.
D.h. in der MainWindow soll was daherkommen.
Wie mach ich das korrekterweise?

Code: Alles auswählen

import sys
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject
from PyQt5.QtWidgets import QApplication, QMainWindow


class xyz(QObject):
    message = pyqtSignal(str)
    def __init__(self, parent= None):
        super(xyz, self).__init__(parent)
        self.message.emit('xxxxx')


class MainWindow(QMainWindow):
    def __init__(self, parent= None):
        super(MainWindow, self).__init__(parent)
        ini_xyz = xyz()
        ini_xyz.message.connect(self.custom_slot)

    @pyqtSlot(str)
    def custom_slot(self, a):
        print(a,'yyyy')


app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Omm: Wenn Du etwas sendest bevor das Signal mit einem Slot verbunden ist, dann geht das natürlich ins Nirvana. Es macht keinen Sinn in der `__init__()` eines Objektes bereits etwas senden zu wollen, denn dann *kann* ja noch keine Verbindung zu einem Empfänger bestehen.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Omm
User
Beiträge: 90
Registriert: Samstag 7. April 2018, 14:05

Ich verstehe was du sagst, nur kann ich das nicht auf den code ummünzen.
Der obere läuft und der untere nicht. Die Verbindung wir doch in beiden Beispielen vor dem emit gemacht.
Die Verbindung wird einmal aus der xyz Klasse gemacht und im anderen Beispiel aus der MainWindow Klasse heraus.
Irgendwie stehe ich noch auf dem Schlauch.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Omm: Im unteren Code wird die Verbindung eben *nicht* vor dem `emit()` hergestellt. Wie kommst Du darauf? Pack mal `print()`-Ausgaben zwischen die entsprechenden Zeilen an denen Du erkennen kannst in welcher Reihenfolge das ausgeführt wird.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Omm
User
Beiträge: 90
Registriert: Samstag 7. April 2018, 14:05

ok jetzt hab ich es gesehen.
ini_xyz () wird vor 'ini_xyz.message.connect(self.custom_slot)' inizialisiert.
Ich denke jetzt habe ich es verstanden. Danke dafür

Wie löse ich das Problem? Nehme ich da eine Klasse für die Verbindung?
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Omm: Welches Problem willst Du denn da überhaupt lösen. Warum muss ein Objekt schon bei der Initialisierung Nachrichten versenden wollen?
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Omm
User
Beiträge: 90
Registriert: Samstag 7. April 2018, 14:05

__blackjack__ ehrlich gesagt noch keines und schon gar nicht beim initialisieren.
Mein Ziel ist es, die Mechanik dahinter zu verstehen.
Deshalb dieses mini Beispiel, was offensichtlich peinlich falsch ist. :oops:
Omm
User
Beiträge: 90
Registriert: Samstag 7. April 2018, 14:05

8h später 1 Erfahrung reicher.
Ein Punkt habe ich leider noch.
Das senden eines Signal geht nur über den Pushbutton oder auch über einen Timer.
Wenn ich aber 'jobmessage' zur Laufzeit ausführe, kommt nichts an. Ich denke das hängt mit einen Event zusammen.
Kann dies sein?

Code: Alles auswählen

import sys
from PyQt5.QtCore import pyqtSignal, QObject
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton


class xyz(QObject):
    message = pyqtSignal(str)

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

    def jobmessage(self):
        self.message.emit('meldung')


class MainWindow(QMainWindow):
    def __init__(self, parent= None):
        super(MainWindow, self).__init__(parent)
        ini_xyz = xyz(self)
        ini_xyz.message.connect(self.meldung)

        self.btn = QPushButton("sender", self)
        self.btn.clicked.connect(ini_xyz.jobmessage)

    def meldung (self, meldung):
        print(meldung)



app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das liegt an etwas anderem. Dein Objekt ini_xyz wird kurz nach Entstehung garbage collected. Und damit verschwinden auch seine Verbindungen.
Omm
User
Beiträge: 90
Registriert: Samstag 7. April 2018, 14:05

keine Ahnung wie ich das verhindern kann.
Na ja, dann eben ohne Signale.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das kann man ja durchaus erlesen, was da so passiert. Und es dann verhindern. https://stackify.com/python-garbage-collection/
Antworten