Seite 1 von 1

Probleme beim Schließen eines GTK Windows.

Verfasst: Mittwoch 2. Oktober 2019, 06:57
von steffenrohwer01
Hallo,

zuerst möchte ich mich bei euch kurz vorstellen, weil ich nur durch mein aktuelles Problem auf dieses Forum gestoßen bin. Ich bin Steffen, 17 Jahre alt und bun aktuell in meinem letzten Schuljahr. Ab dem nächsten Jahr werde ich dann ein Duales Studium der Wirtschaftsinformatik aufnehmen.

Aktuell habe ich nun folgendes Problem bei der Programmierung für das UserInterface für unseren Teamstand bei einem Schulwettbewerb.

Ich versuche bei einer Veränderung der NFC Karte, die mit einem PN532 reader festgestellt wird, ein mit glade erstelltes GTK Window zu schließen und ein anderes zu öffnen.

Dabei bekomme ich folgende Fehlermeldung:
Traceback (most recent call last): File "sensorUI.py", line 53, in sensorabfrage self.changewindow(part="hauptmenue") File "sensorUI.py", line 38, in changewindow window.destroy() UnboundLocalError: local variable 'window' referenced before assignment
So sieht das aktuelle Programm aus:

Code: Alles auswählen

 from datetime import datetime
import time
import binascii
import socket
import signal
import sys

import Adafruit_PN532 as PN532
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject

# Pins NFC Modul
CS   = 18
MOSI = 23
MISO = 24
SCLK = 25

# Karten fuer NFC definieren
karten = {
    "hauptmenue": "hauptmenue",
    "112": "finalrs",
    "111": "v1"
}

letzter_tag = ""
startup = 1

class MainWindow(Gtk.Window):
    def __init__(self, sensor):
        global letzter_tag
        self.pn532 = sensor
        letzter_tag = "hauptmenue"
        self.changewindow(part="hauptmenue")

    def changewindow(self, part):
        global startup
        if startup is not 1:
            window.destroy()
        else:
            startup = 0
        builder = Gtk.Builder()
        builder.add_from_file("/home/pi/Desktop/InterfacesRS/{0}.glade".format(part))
        window = builder.get_object("window1")
        window.show_all()
        print("window changed")

def sensorabfrage(self):
    global letzter_tag
    uid = self.pn532.read_passive_target()
    if uid is None:
        if letzter_tag is not "hauptmenue":
            letzter_tag = "hauptmenue"
            print("Kein Tag in Reichweite!")
            self.changewindow(part="hauptmenue")
        data = self.pn532.mifare_classic_read_block(4)
        if data is not None:
            action = str(int(data[2:8].decode("utf-8"), 16))
            objekt = karten.get(action)
            if objekt is not None:
                if objekt != letzter_tag:
                    letzter_tag = objekt
                    print(objekt)
                    self.changewindow(part=objekt)
    return True

def startsensortimer(self):
    GObject.timeout_add(400, self.sensorabfrage)

def main():
    pn532 = PN532.PN532(cs=CS, sclk=SCLK, mosi=MOSI, miso=MISO)
    pn532.begin()
    pn532.SAM_configuration()

    win = MainWindow(sensor=pn532)
    win.startsensortimer()
    Gtk.main()

if __name__ == "__main__":
    main()
Dass die globalen Variablen lieber als Instanzattribute geschrieben werden sollten ist mir auch klar und wird sofort heute Nachmittag verändert.
Habt ihr eine Idee wo das andere Problem liegt?

Vielen Dank schonmal für die Hilfe.

LG
Steffen

Re: Probleme beim Schließen eines GTK Windows.

Verfasst: Mittwoch 2. Oktober 2019, 09:23
von __deets__
Na du hast halt nen Zweig in dem du window destroy aufrufst, ohne das es den Namen window vorher gab. Du musst stattdessen self.window benutzen, und das auch vorher im __init__ schon an "None" binden. Und dann pruefen, ob es das nicht ist, und dadurch das Fenster schliessen.

Und eine kurze Anmerkung: es ist ungewoehnlich so vorzugehen, Fenster schliessen und aufpoppen ist aus User-Sicht super nervig. Normalerweise aendert man den Inhalt des bestehenden Fensters.

Re: Probleme beim Schließen eines GTK Windows.

Verfasst: Mittwoch 2. Oktober 2019, 09:39
von steffenrohwer01
__deets__ hat geschrieben: Mittwoch 2. Oktober 2019, 09:23 Na du hast halt nen Zweig in dem du window destroy aufrufst, ohne das es den Namen window vorher gab. Du musst stattdessen self.window benutzen, und das auch vorher im __init__ schon an "None" binden. Und dann pruefen, ob es das nicht ist, und dadurch das Fenster schliessen.

Und eine kurze Anmerkung: es ist ungewoehnlich so vorzugehen, Fenster schliessen und aufpoppen ist aus User-Sicht super nervig. Normalerweise aendert man den Inhalt des bestehenden Fensters.
Entschuldigung... was meinst du mit im _init_ an "None" binden?

Vielen Dank erstmal!

LG
Steffen

Re: Probleme beim Schließen eines GTK Windows.

Verfasst: Mittwoch 2. Oktober 2019, 09:43
von __deets__
Na, self.window = None, und dann spaeter if self.window is not None: <tuwas>

Re: Probleme beim Schließen eines GTK Windows.

Verfasst: Mittwoch 2. Oktober 2019, 10:49
von Sirius3
Sonstige Anmerkungen zum Code:
vergiss gleich mal wieder, dass es sowas wie `global` überhaupt gibt.
`letzter_tag` wäre doch ein ideales Attribut der MainWindow-Klasse.
Unsinnige Werte an Variablen zu binden, trägt nicht zum Verständnis bei. Das initialisieren von letzter_tag mit "" wird sofort wieder in __init__ durch "hauptmenue" überschrieben, ersteres ist also überflüssig. Aber "hauptmenue" hat für mich so gar nichts mit einem letzten Tag zu tun, Du mischst hier also verschiedene Funktionen durch eine Variable. Das sollte man nicht tun.
Vergleiche mit `is` bzw. `is not` sind nur selten sinnvoll. Ausnahme ist der Vergleich mit None. Alle anderen Vergleiche müssen == bzw != sein, so wie es jetzt ist, ist es falsch.

sensorabfrage ist falsch eingerückt, wenn es zur Klasse gehören soll.
`startsensortimer` ebenso.

Re: Probleme beim Schließen eines GTK Windows.

Verfasst: Mittwoch 2. Oktober 2019, 19:13
von steffenrohwer
Moin Moin!

Das Problem ist gelöst. Ich habe außerdem die anderen Tipps gerne entgegengenommen und das Programm dahingehend verbessert. Vielen Dank auch dafür, ich freue mich wenn ich mich hier weiterentwickeln kann. :P

Leider gibt es noch ein weiteres Problem und das konnte ich mir nach Stunden leidernicht erklären... :? :?:
Dieses Programm gibt für die Variable Data immer nur "None" aus. Mein Test-Script, welches ich auch anhänge, gibt jedoch die richtigen Zahlen aus, die auf den NFC-tags gespeichert sind.


Aktueller Code:

Code: Alles auswählen

from datetime import datetime
import time
import binascii
import socket
import signal
import sys

import Adafruit_PN532 as PN532
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject


class MainWindow(Gtk.Window):

    startup = 1
    def __init__(self):
        self.CS = 18
        self.MOSI = 23
        self.MISO = 24
        self.SCLK = 25

        self.pn532 = PN532.PN532(cs=self.CS, sclk=self.SCLK, mosi=self.MOSI, miso=self.MISO)
        self.pn532.begin()
        self.pn532.SAM_configuration()

        self.window = None
        self.letzter_tag = "hauptmenue"
        self.changewindow(part="hauptmenue")

    def changewindow(self, part):
        if self.startup != 1:
            self.window.destroy()
        else:
            self.startup = 0
        builder = Gtk.Builder()
        builder.add_from_file("/home/pi/Desktop/InterfacesRS/{0}.glade".format(part))
        self.window = builder.get_object("window1")
        self.window.show_all()
        print("window changed")

    def sensorabfrage(self):
        #print("Sensorabfrage")
        # Karten fuer NFC definieren
        self.karten = {
            "hauptmenue": "hauptmenue",
            "112": "finalrs",
            "111": "v1"
        }

#        self.CARD_KEY = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]

        uid = self.pn532.read_passive_target()
        #print(uid)
        if uid is None:
            if self.letzter_tag != "hauptmenue":
                print("Kein Tag in Reichweite!")
                self.letzter_tag = "hauptmenue"
                self.changewindow(part="hauptmenue")
 #       if not self.pn532.mifare_classic_authenticate_block(uid, 4, PN532.MIFARE_CMD_AUTH_B,self.CARD_KEY):
 #           print("Kartenfehler")
        data = self.pn532.mifare_classic_read_block(4)
        print(data)
        if data is not None:
            action = str(int(data[2:8].decode("utf-8"), 16))
            objekt = self.karten.get(action)
            if objekt != self.letzter_tag:
                letzter_tag = objekt
                print(objekt)
                self.changewindow(part=objekt)
        return True

    def startsensortimer(self):
        GObject.timeout_add(600, self.sensorabfrage)

def main():
    win = MainWindow()
    win.startsensortimer()
    Gtk.main()

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

Code: Alles auswählen

import binascii
import socket
import time
import signal
import sys

import Adafruit_PN532 as PN532

# Pins fuer NFC Modul fewstlegen
CS   = 18
MOSI = 23
MISO = 24
SCLK = 25


#Karten  definieren
karten = {
    "none": "landingpage",
    "112": "finalrs",
    "111": "v1"
}

#Hilfsvariablen definieren
letzter_tag = ""
DELAY = 0.1 #Refresh-Rate
karte_gefunden = "true"

# Karten Typ (Mifare)
CARD_KEY = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]

def close(signal, frame):
        sys.exit(0)

signal.signal(signal.SIGINT, close)

# PN532 einrichten
pn532 = PN532.PN532(cs=CS, sclk=SCLK, mosi=MOSI, miso=MISO)
pn532.begin()
pn532.SAM_configuration()

while True:
    # Suche nach Karte
    uid = pn532.read_passive_target()
    if uid is None:
        if karte_gefunden == "true":
            print("Kein Tag in Reichweite!")
            karte_gefunden = "false"
            # Zeige den Homescreeen
        continue
    # Block 4 lesen
    if not pn532.mifare_classic_authenticate_block(uid, 4, PN532.MIFARE_CMD_AUTH_B,
                                                   CARD_KEY):
        print('Kartenfehler')
        continue
    data = pn532.mifare_classic_read_block(4)
    # Datenverarbeitung
    if data is not None:
        action = '{0}'.format(int(data[2:8].decode("utf-8"), 16))
    if action is not None:
        objekt = karten.get(action)
    if objekt != None:
        karte_gefunden = "true"
        if objekt != letzter_tag:
            letzter_tag = objekt
            print(objekt)
    time.sleep(DELAY)
Mit freundlichen Grüßen
Steffen

Re: Probleme beim Schließen eines GTK Windows.

Verfasst: Mittwoch 2. Oktober 2019, 19:55
von Sirius3
`startup` sollte kein Klasseattribut sein, sondern ein Instanzattribut. Dagegen sind `CS`, `MOSI`, `MISO` und `SCLK` Konstanten und sollten keine Instanzattribute sein, am besten Konstanten auf Modulebene, weil man die am einfachsten am Anfang der Datei findet.
Das mit dem Fenster zerstören und neubauen, sieht immer noch seltsam aus. Du hast drei verschiedene Fenster, die am besten auch explizit, drei verschiedene Fenster sein sollten und nicht ein gemischtes. Das macht die Logik des Programms undurchschaubar.


Im Testskript würde ich sagen, das rumspielen mit den Signalen ist ein Programmierfehler. `continue` sollte man sparsam einsetzen, nur wenn es wirklich nötig ist.
Das `mifare_classic_authenticate_block` ist im ersten Skript auskommentiert, wird aber wahrscheinlich gebraucht. Wenn `data` None ist, ist `action` nicht definiert und das Programm bricht mit einem NameError ab. Wäre das nicht, würde es bei objekt mit dem selben Fehler abbrechen.
`karte_gefunden` wird nie benutzt, das an einen String mit Inhalt "true" zu binden, ist falsch, weil es vom Sinn her eigentlich True sein müßte.