NameError: name 'b' is not defined

Fragen zu Tkinter.
Antworten
mz002
User
Beiträge: 2
Registriert: Dienstag 11. Juli 2017, 15:30

Hallo allerseits,

ich hab gerade erst angefangen, programmieren zu lernen mit Python. Arbeite hierzu eine YouTube Tutorial Serie durch und hänge bei Teil 14 fest:
https://www.youtube.com/watch?v=3LvO6fVZ59E&t=456s

Es geht um eine grafische Benutzeroberfläche mit einem Button, der ein "Event" auslösen soll (jedes mal Klicken = + 1). Hierzu folgender Code:

Code: Alles auswählen

import tkinter

i = 0

def handleButton(event):
    global i
    i = i + 1
    w['text'] = "Hello World" + str(i)

top = tkinter.Tk()

w = tkinter.Label(top,text = '')
w.pack()

w = tkinter.Button(top,text = 'Hier Klicken')
w.pack()

b.bind('<Button-1>', handleButton)

top.mainloop()
Der Code ist 1 zu 1 so wie in dem Video gezeigt... hab es mehrmals geprüft. In dem Video funktioniert alles wie geplant aber wenn ich das Programm ausführe kommt folgende Fehlermeldung:

Traceback (most recent call last):
File "C:\Users\Sanna\AppData\Local\Programs\Python\Python36\Coding\python14_test1.py", line 18, in <module>
b.bind('<Button-1>', handleButton)
NameError: name 'b' is not defined


Nach mehreren Internet-Recherchen und Tüfteleien hoffe ich darauf, vielleicht hier eine Lösung zu finden ;-)

Vielen Dank und viele Grüße
Michael
Zuletzt geändert von Anonymous am Mittwoch 12. Juli 2017, 07:50, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Du könntest im ersten Schritt auf die Idee kommen, w statt b zu benutzen, wenn kein b, sondern nur lauter w vorkommen. Und im zweiten Schritt könntest Du auch noch ein wenig längere Namen für die Variablen benutzen, damit Du so kurze Variablen wie b und w nicht mehr verwechseln kannst. Also, mit einem w statt b funktioniert das Script, doch das wäre auch nicht richtig. Wenn Du genau hinschaust, erhielt der Button ein b und kein w. Benutze längere Bezeichner für die Variablen, dann gibt es weniger Verwechslungen.

Code: Alles auswählen

import tkinter

i = 0

def handleButton(event):
    global i
    i = i + 1
    w['text'] = "Hello World" + str(i)

top = tkinter.Tk()

w = tkinter.Label(top,text = '')
w.pack()

b = tkinter.Button(top,text = 'Hier Klicken')
b.pack()

b.bind('<Button-1>', handleButton)

top.mainloop()
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

So, nun habe ich in den ersten drei Tutorials hineingeschaut und na ja, global sollte nur in Ausnahmefällen benutzt werden. Weiß nicht, ob er das noch erwähnt und anderes ist von der Notierung auch nicht so berauschend.
mz002
User
Beiträge: 2
Registriert: Dienstag 11. Juli 2017, 15:30

Hallo und vielen herzlichen Dank für die bisherigen Antworten!

Wie mir das entgehen konnte, ist mir ein Rätsel, ich hab fast eine Stunde lang geschaut ob der Code richtig ist.. Naja vielleicht fehlt es mir einfach noch an ausreichendem Hintergrundwissen und Verständnis für Python.

Die Tutorials sind für mich als Einsteiger ein guter Start um den Stein ins Rollen zu bekommen aber tatsächlich werden manche Sachen nur halbherzig erwähnt bzw. angesprochen.

Könnt ihr mir ein gutes Grundlagenbuch auf amazon empfehlen?
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@Melewo: wer Tk benutzt, ohne Objektorientierung, der wird zwangsläufig viel globalen Zustand haben. Da aber 99% der Tutorials im Internet genau so arbeiten, wird sich daran, trotz aller Bemühungen eines kleinen Forums daran nichts ändern. Solche Beispiele bleiben ja immer nur bei einem Minimum von ein paar Dutzend Zeilen, wo man mit der Übersichlichkeit noch keine Probleme bekommt. Wer fängt schon gerne mit der doppelten Anzahl an Boilerplate-Code an?

Code: Alles auswählen

import tkinter as tk

class MainWindow(tk.Tk):
    def __init__(self):
        super().__init__()
        self.counter = 0
        self.greetings = tk.Label(self, text='')
        self.greetings.pack()
        tk.Button(self, text='Hier klicken', command=self.greet).pack()

    def greet(self):
        self.counter += 1
        self.greetings['text'] = "Hello World #{}".format(self.counter)

def main():
    main_window = MainWindow()
    main_window.mainloop()

if __name__ == '__main__':
    main()
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

@Sirius3: Ja, ich habe auch ein Script mit zwei globalen Variablen, nur weise ich dann darauf hin. Ob in Büchern oder Tutorials, es wird zu wenig darauf hingewiesen, was sich nicht nur auf Python bezieht. In JS und PHP habe ich mir z.B. angewöhnt, in Seiten, in denen ich nicht weiß, was möglicherweise noch alles eingebunden wird, globale Variablen oder Bezeichern von Klassen mit meinen Initialen oder Initialen von der Domain zu beginnen. Ein Plugin ist mal verschwunden, weil bereits ein gleichnamiges existierte und meins ersetzte.

@mz002: Habe jetzt noch in Folge 15 geschaut. Keine Ahnung, ob er erklärt hat, aus welchem Grund in den Beispielen Event-Handler benutzt werden, obwohl command = funktions_name genügt hätte.

Code: Alles auswählen

# aus Video:
b = tkinter.Button(top,text = 'Hier Klicken')
b.pack()
 
b.bind('<Button-1>', handleButton)

# würde es so schreiben:
button_name = tkinter.Button(top, text = 'Hier Klicken', command = handleButton)
button_name.pack()
Ob Du privat Funktionsnamen in camelCase oder camel_case schreibst, bleibt Dir überlassen, wobei die Betonung auf privat liegt. Doch so wie in Teil 3, dass da auch Variablen in camelCase statt in camel_case geschrieben wurden, solltest Du Dir gar nicht erst angewöhnen. Also wenigstens einmal lesen: "Style Guide for Python Code"

Es ist so, wie Sirius3 schreibt, es geht nicht immer ohne globale Varibalen. Doch so allgemein, z.B. wären die folgenden Beispiele in PHP nicht möglich, ohne eine Mitteilung wie "Fatal error: Cannot redeclare ..." zu erhalten. In Python wird in IDLE kein Fehler geworfen, da wird einfach eine Klasse oder eine Funktion durch eine zweite gleichnamige überschrieben. Bei Variablen werden ohnehin keine Warnungen ausgegeben, weder in JS, PHP oder Python und somit hat man sich allein um den Bereich zu kümmern, in dem eine Variable sichtbar oder gültig ist und global sollte halt nur wohl überlegt eingesetzt werden. Je mehr Variablen global, je fehleranfälliger.

Code: Alles auswählen

# Test - Bezeichner von Klassen - erste wird durch zweite ohne Warnung überschrieben.

class Musterklasse:
    def tue_etwas():
        print("#Ausgabe: Test 1")
    
class Musterklasse:
    def tue_etwas():
        print("#Ausgabe: Test 2")

Musterklasse.tue_etwas()
#Ausgabe: Test 2

# Test - Bezeichner von Funktionen - erste wird durch zweite ohne Warnung überschrieben.

def tue_anderes():
    print("#Ausgabe: Test 3")
    
def tue_anderes():
    print("#Ausgabe: Test 4")

tue_anderes()
#Ausgabe: Test 4
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@Melewo: Deine Beispielklassen sind leider sehr schlecht gewählt, weil Du keine Klasse definiert hast, sondern nur das Keyword class zum Einführen eines Namensraums mißbraucht hast. Korrekt wäre also:

Code: Alles auswählen

class Musterklasse:
    def tue_etwas(self):
        print("#Ausgabe: Test 1")
   
class Musterklasse:
    def tue_etwas(self):
        print("#Ausgabe: Test 2")

muster_instanz = Musterklasse()
muster_instanz.tue_etwas()
Dafür, dass nicht versehentlich Klassen überschrieben gibt es in Python Namensräume. In JS muß man explizit Namensräume einführen, was aber alle bekannten Bibliotheken auch machen. Initialien sind einfach nur zwei Buchstaben, die keine Information enthalten, das ist also keine Lösung.

bind Button-1 und command machen zwei unterschiedliche Events. bind möchte man bei Knöpfen nicht, weil man gewohnt ist, dass Aktionen erst ausgeführt werden, wenn man den Knopf wieder los läßt; außerdem macht das die Tastaturbedienung kaputt.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

JS Bibliotheken ja, doch gerade habe ich nachgeschaut, wie oft mein Cookie-Bestätigungs-Script heruntergeladen wurde. Nicht so berauschend oft, 149 Downloads innerhalb von knappen zwei Jahren. Und angenommen davon verwenden es 40 bis 50 oder mehr wirklich, ich weiß nicht, was die da noch an JS auf ihren Seiten eingebunden haben, noch bevor meins am Seitenende kommt. Und ja, zwei Einstellungen sind global, weil ich nicht wollte, dass da einer anfängt innerhalb der Funktion Änderungen vorzunehmen, wobei meine dann vorausgehende überschreiben würden.
Zuletzt geändert von Melewo am Mittwoch 12. Juli 2017, 12:57, insgesamt 1-mal geändert.
BlackJack

@Melewo: Da stellt man einfach eine Funktion zur Verfügung über die man die Einstellungen machen kann. Die kann ja einen ”globalen” Effekt haben haben, aber eben nicht als globale Variable sondern innerhalb Deines Namensraums.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Ja, könnte man machen, doch da hatte ich es halt einfach so gehandhabt. Und irgendwie war es zumindest bei älteren Seiten noch normal, dass da selbst Google alles einfach nur mit Variablen übergab, wie bei Ads:

[codebox=javascript file=Unbenannt.js]google_ad_width = 160;
google_ad_height = 600;[/code]
Und so lange die einen einmaligen Präfix besitzen und anschließend geprüft werden, passiert ja auch nichts.

[codebox=javascript file=Unbenannt.js]/** --------------------------------------------------------------------------------
* Scriptname: HM-CookieHinweise
* Description: Ein Script für die Ein- und Ausblendung von Cookie-Hinweisen.
* ...
* ...
* ---------------------------------------------------------------------------------
*/

var hm_auswahlein = "Fahne"; // Auswahl für verkleinerte Darstellung "Fahne" oder "Button"
var hm_ausblenden = 60000; // Ausblenden in 60000 Millisekunde o. nach Einstellung[/code]

Passiert ist es mir aber mit "Plugin Name: HM-MovieBlende", bevor ich da ein HM vorsetzte, kann mich nur nicht mehr daran erinnern, ob ich nur Movie oder MovieBlende als Name benutzte. Jedenfalls meins war weg und durch ein anderes Plugin bei einem Update überschrieben.
BlackJack

@Melewo: Google kann sich so etwas erlauben weil bei einer Namenskollision jeder sagen wird, naja `google_ad` als Präfix ”gehört” eher Google als jemand anderem. Bei `hm` sieht das anders aus. Die Gefahr einer Kollision ist bei einem Zweibuchstabenkürzel auch höher.
Antworten