Fehler mit entry.get und set.labeltext

Fragen zu Tkinter.
Antworten
daz18
User
Beiträge: 6
Registriert: Montag 12. Dezember 2011, 17:40

Ich bin noch Python Anfänger und wollte eigentlich ein Stein_Schere_Papier Programm programmieren. Dafür möchte ich ein Eingabefeld erstellen, doch leider kann ich weder den text noch den Titel setzen und zudem sagt er mir, dass es einen Fehler mit der get methode gibt. Ich hoffe ihr könnt mir weiterhelfen.

Code: Alles auswählen

from Tkinter import *

class Eingabefeld():

        def __init__(self):

            self.Fenster = Tk()
            self.titel = "Test"
            self.Fenster.title(self.titel)
            self.frame = Frame(self.Fenster)
            self.frame.pack()
            self.text = Label(self.frame)
            self.text.grid(row = 0,column = 0)
            self.text2 = Label(self.frame)
            self.text2.grid(row = 1,column = 0)
            self.entry = Entry(self.frame)
            self.entry.grid(row=0, column=1)
            self.entry2 = Entry(self.frame)
            self.entry2.grid(row=1, column=1)
            self.okb = Button(self.frame,text='Ok',padx=100)
            self.okb.grid(row=2)
            self.Fenster.mainloop()
            self.nameget = self.entry.get()

        def titel(self,x):
                self.titel=x

        def text1(self,x):
                self.text.config(text = x)

        def text2(self,x):
                self.text2.config(text = x)

root = Eingabefeld()
root.titel('Hello World')
root.text1('Geben sie ihren Vornamen ein')
root.text2('Geben sie ihren Namen ein')
Fehlermeldung beim schließen des Programms:

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Users\user\Downloads\probe4.py", line 34, in <module>
    root = Eingabefeld()
  File "C:\Users\user\Downloads\probe4.py", line 23, in __init__
    self.nameget = self.entry.get()
  File "C:\Python27\lib\lib-tk\Tkinter.py", line 2391, in get
    return self.tk.call(self._w, 'get')
TclError: invalid command name ".45501640L.45501768L"
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi daz18

Kannst du das folgende Skript einmal ausprobieren:

Code: Alles auswählen

import Tkinter as tk

class Eingabefeld():

    def __init__(self, callback):

        self.callback = callback
        
        self.frame = tk.Frame(app)
        self.frame.pack()
        
        self.text1 = tk.Label(self.frame, anchor='w')
        self.text1.grid(row=0, column=0, padx=5, sticky='we')
        
        self.text2 = tk.Label(self.frame, anchor='w')
        self.text2.grid(row=1, column=0, padx=5, sticky='we')
        
        self.entry1 = tk.Entry(self.frame, bg='white')
        self.entry1.grid(row=0, column=1)
        
        self.entry2 = tk.Entry(self.frame, bg='white')
        self.entry2.grid(row=1, column=1)
        
        self.ok_button = tk.Button(self.frame,text='Ok', command=self.entry_ok)
        self.ok_button.grid(row=2, column=0, sticky='we')
    
    def entry_ok(self):
        self.callback(self.entry1, self.entry2)
          
    def set_text1(self, text):
        self.text1.config(text=text)

    def set_text2(self, text):
        self.text2.config(text=text)

def callback(entry_1, entry_2):
    
    print 'Eingabe-1:', entry_1.get()
    print 'Eingabe-2:', entry_2.get()
        
app = tk.Tk()
app.title('Hello World')

input_field = Eingabefeld(callback)

input_field.set_text1('Geben sie ihren Vornamen ein:')
input_field.set_text2('Geben sie ihren Namen ein:')

input_field.entry1.focus_set()

app.mainloop()
Gruß wuf :wink:
Take it easy Mates!
daz18
User
Beiträge: 6
Registriert: Montag 12. Dezember 2011, 17:40

Vielen Dank für die Antwort, der Code hilft mir weiter.
Ich verstehe nur nicht wieso die entry.get Methode nicht bei mir geklappt hat und wieso import Tkinter as tk besser ist.

Gruß David
BlackJack

@daz18: Mit dem Sternchenimport holst Du Dir ≈200 Namen in das Modul, mit wuf's Alternative nur einen.

Schau Dir mal die Zeile vor Deinem Fehler an. Die Hauptschleife kehrt erst zurück wenn sie zum Beispiel durch das schliessen des Fensters beendet wurde. Dann ist das Fenster aber weg, und damit auch das `entry`-Widget.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

@daz18:
Gehe ich richtig in der Annahme, dass du die Entry-Widgets mit dem aktivieren der OK-Schaltfläche auslesen möchtest oder nur beim schliessen des Hauptfensters?

Gruß wuf :wink:
Take it easy Mates!
daz18
User
Beiträge: 6
Registriert: Montag 12. Dezember 2011, 17:40

Also eigentlich möchte ich die eingegeben Daten in einer Variable speichern und danach durch drücken des OK-Buttons das Fenster schließen.
problembär

Hi,

1. Man sollte nicht ".grid()" und ".pack()" vermischen. Entscheide Dich bitte für ein Konzept. Die meisten ziehen wohl ".pack()" vor. Das wäre auch meine Empfehlung. Im Beispiel unten habe ich das noch so gelassen, wie Du es geschrieben hattest.

2. Normalerweise bleibt das Hauptfenster geöffnet, solange das Programm läuft. Wenn Du das Hauptfenster schließt, mußt Du Dich entscheiden, wo Du die Werte weiterverarbeiten willst. Ich habe dazu unten mal eine Klasse "MainClass" dazugefügt. Bei großen Programmen ist es hilfreich, GUI und Datenverarbeitung sogar ganz zu trennen. Dazu verwendet man meist nicht nur zwei, sondern drei Klassen, siehe hier unter MVC.
Beachte: Das GUI und die GUI-Klasse sind zwei verschiedene Dinge: Wenn das Hauptfenster mit ".destroy()" geschlossen wird, kehrt der Programmablauf zu der __init__-Funktion in "MainClass" zurück. Die GUI-Klasse "Eingabefeld" ist dort aber über die Instanz ".eingfeld" immer noch bekannt.
Hier also ein Codevorschlag:

Code: Alles auswählen

#!/usr/bin/python
# coding: iso-8859-1

import Tkinter as tk

class MainClass:

    def __init__(self):
        self.eingfeld = Eingabefeld(titel = 'Hello World',
                                    text1 = 'Geben sie ihren Vornamen ein',
                                    text2 = 'Geben sie ihren Namen ein')
        for i in self.eingfeld.values.keys():
            print "Feldname: " + i
            print "Wert: " + self.eingfeld.values[i]
            print

class Eingabefeld:

    def __init__(self, titel, text1, text2):

        self.values = {}
        self.Fenster = tk.Tk()
        self.titel = titel
        self.Fenster.title(self.titel)
        self.frame = tk.Frame(self.Fenster)
        self.frame.pack()
        self.text = tk.Label(self.frame, text = text1)
        self.text.grid(row = 0,column = 0)
        self.text2 = tk.Label(self.frame, text = text2)
        self.text2.grid(row = 1,column = 0)
        self.entry = tk.Entry(self.frame, fg = 'black', bg = 'white')
        self.entry.grid(row=0, column=1)
        self.entry.focus()
        self.entry2 = tk.Entry(self.frame, fg = 'black', bg = 'white')
        self.entry2.grid(row=1, column=1)
        self.okb = tk.Button(self.frame, text='Ok', command = self.getValue)
        self.okb.grid(row=2)
        self.Fenster.mainloop()

    def getValue(self):
        self.values = {"entry1" : self.entry.get(),
                       "entry2" : self.entry2.get()}
        self.Fenster.destroy()

if __name__ == "__main__":
    MainClass()
daz18
User
Beiträge: 6
Registriert: Montag 12. Dezember 2011, 17:40

Vielen Dank für den Code, den übernehm ich aufjedenfall.
problembär

daz18 hat geschrieben:Vielen Dank für den Code, den übernehm ich aufjedenfall.
Das ist nett, aber eigentlich ist das nur ein kleines Beispiel. Das ist noch nicht so gut, daß man es so verwenden sollte. Es sollte Dir eigentlich nur ein bißchen dabei helfen, das Ganze zu verstehen, damit Du es dann selbst nochmal neu und besser schreibst. ;)
daz18
User
Beiträge: 6
Registriert: Montag 12. Dezember 2011, 17:40

Ok ich habe nun ein weiteres Problem bei meinem Stein Schere Papier Programm.
Es geht darum das ich den Befehl des Buttons nachträglich hinzufügen will.
Das Problem ist das er den Befehl einmal ausführt, wenn man das Programm startet und dann nicht mehr.

Hier der Code:

Code: Alles auswählen

import Tkinter as tk

import random


class Spiel_ssp:
    # Konstruktor
    def __init__(self):
        # Attribute: nick
        self.nick = 'Spieler1'
        self.nick2 = 'Spieler2'
        self.punkte = 0
        self.cpunkte = 0
        self.cnick = 'Computer'

#Fuer Spieler1
        
    def set_nick(self,wert):
        self.nick = wert

    def get_nick(self):
        return self.nick

#Fuer Spieler2
    
    def set_nick2(self,wert):
        self.nick2 = wert

    def get_nick2(self):
        return self.nick2
    
    def auswertung_mittel(self,x):   

        if x == 1:
            print(self.nick +' spielt Stein')
        if x == 2:
            print(self.nick +' spielt Schere')
        if x == 3:
            print(self.nick +' spielt Papier')
            
        y = random.randint(1,3)

        if y == 1:
            print(self.cnick +' spielt Stein')
        if y == 2:
            print(self.cnick +' spielt Schere')
        if y == 3:
            print(self.cnick +' spielt Papier')         

        if x == y:
            print ('Unentschieden')
            print(self.punkte, self.cpunkte)
            
        elif x == 2 and y == 3:
            print (self.nick + ' gewinnt')
            self.punkte = self.punkte + 1
            print(self.punkte, self.cpunkte)
            
        elif x == 3 and y == 1:
            print (self.nick + ' gewinnt')
            self.punkte = self.punkte + 1
            print(self.punkte, self.cpunkte)
            
        elif x == 1 and y == 2:
            print (self.nick + ' gewinnt')
            self.punkte = self.punkte + 1
            print (self.punkte, self.cpunkte)
            
            
        else :
            print (self.cnick + ' gewinnt')       
            self.cpunkte += 1
            print(self.punkte, self.cpunkte)

class Buttonfeld():

        def __init__(self,Fenster,text1,text2,text3):
                self.frame = tk.Frame(Fenster)
                self.frame.pack()
                                    
                self.b1 = tk.Button(self.frame,text = text1)
                self.b1.pack()
                                    
                self.b2 = tk.Button(self.frame,text = text2)
                self.b2.pack()
                                    
                self.b3 = tk.Button(self.frame,text = text3)
                self.b3.pack()
                
        def set_config(self,x):
            self.b1['command'] = lambda:x
            
        def set_config2(self,x):
            self.b2['command'] = lambda:x
            
        def set_config3(self,x):
            self.b3['command'] = lambda:x
                
                
class MainClass:

    def __init__(self):

        self.app = tk.Tk()
        self.app2 = Spiel_ssp()

        self.bfeld = Buttonfeld(self.app,
                                text1 = 'Stein',
                                text2 = 'Schere',
                                text3 = 'Papier')
        self.bfeld.set_config(self.app2.auswertung_mittel(1))
        self.bfeld.set_config2(self.app2.auswertung_mittel(2))
        self.bfeld.set_config3(self.app2.auswertung_mittel(3))

        self.app.mainloop()

MainClass()
nomnom
User
Beiträge: 487
Registriert: Mittwoch 19. Mai 2010, 16:25

Hier sind mal ein paar Dinge, die du beachten solltest:
  • Deine Klasse `MainClass` ist sinnlos: Du instanziierst die Klasse sowieso auf Modulebene. Besser ist es, wenn du das in eine Funktion packst und dann die Funktion in einem if-Block aufrufst:

    Code: Alles auswählen

    if __name__ == '__main__':
        main()
  • Da du Python 2 verwendest, sollten deine Klassen von `object` erben, und du kannst auch `print` ohne Klammern verwenden, da du bei deinem Programm durch `print()` keine Vorzüge hast.
  • Deine `get_…`- bzw `set_…`-Methoden sind auch sinnlos: Du kannst auch einfach auf app.nick zugreifen, und es verändern.
  • Du solltest deskriptive Namen für deine Objekte wählen. Schau dir auch mal PEP 8 an.
  • `x = x + 1` geht in Python auch kürzer: `x += 1`
  • Magische Zahlen wie 1/2/3 sollten auch vermieden werden, und durch Namen repräsentiert werden: STEIN/SCHERE/PAPIER
"lambda: x" ist sinnfrei, das liefert nur x zurück. Und x ist in deinem Programm immer `None`, und jetzt überleg, warum! ;)
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi daz18

Kannst du das folgende einmal ausprobieren:

Code: Alles auswählen

import Tkinter as tk

import random


class Spiel_ssp:
    # Konstruktor
    def __init__(self):
        # Attribute: nick
        self.nick = 'Spieler1'
        self.nick2 = 'Spieler2'
        self.punkte = 0
        self.cpunkte = 0
        self.cnick = 'Computer'

#Fuer Spieler1
       
    def set_nick(self,wert):
        self.nick = wert

    def get_nick(self):
        return self.nick

#Fuer Spieler2
   
    def set_nick2(self,wert):
        self.nick2 = wert

    def get_nick2(self):
        return self.nick2
   
    def auswertung_mittel(self,x):  

        if x == 1:
            print(self.nick +' spielt Stein')
        if x == 2:
            print(self.nick +' spielt Schere')
        if x == 3:
            print(self.nick +' spielt Papier')
           
        y = random.randint(1,3)

        if y == 1:
            print(self.cnick +' spielt Stein')
        if y == 2:
            print(self.cnick +' spielt Schere')
        if y == 3:
            print(self.cnick +' spielt Papier')        

        if x == y:
            print ('Unentschieden')
            print(self.punkte, self.cpunkte)
           
        elif x == 2 and y == 3:
            print (self.nick + ' gewinnt')
            self.punkte = self.punkte + 1
            print(self.punkte, self.cpunkte)
           
        elif x == 3 and y == 1:
            print (self.nick + ' gewinnt')
            self.punkte = self.punkte + 1
            print(self.punkte, self.cpunkte)
           
        elif x == 1 and y == 2:
            print (self.nick + ' gewinnt')
            self.punkte = self.punkte + 1
            print (self.punkte, self.cpunkte)
           
           
        else :
            print (self.cnick + ' gewinnt')      
            self.cpunkte += 1
            print(self.punkte, self.cpunkte)

class Buttonfeld():

        def __init__(self,Fenster,text1,text2,text3):
                self.frame = tk.Frame(Fenster)
                self.frame.pack()
                                   
                self.b1 = tk.Button(self.frame,text = text1)
                self.b1.pack()
                                   
                self.b2 = tk.Button(self.frame,text = text2)
                self.b2.pack()
                                   
                self.b3 = tk.Button(self.frame,text = text3)
                self.b3.pack()
               
        def set_config(self, call_back, x):
            self.b1['command'] = lambda: call_back(x)
           
        def set_config2(self, call_back, x):
            self.b2['command'] = lambda: call_back(x)
           
        def set_config3(self, call_back, x):
            self.b3['command'] = lambda: call_back(x)
               
               
class MainClass:

    def __init__(self):

        self.app = tk.Tk()
        self.app2 = Spiel_ssp()

        self.bfeld = Buttonfeld(self.app,
                                text1 = 'Stein',
                                text2 = 'Schere',
                                text3 = 'Papier')
        self.bfeld.set_config(self.app2.auswertung_mittel,1)
        self.bfeld.set_config2(self.app2.auswertung_mittel,2)
        self.bfeld.set_config3(self.app2.auswertung_mittel,3)

        self.app.mainloop()

MainClass()
Gruß wuf :wink:
Take it easy Mates!
daz18
User
Beiträge: 6
Registriert: Montag 12. Dezember 2011, 17:40

Vielen Dank Wuf, dass du mein Problem gelöst hast. :)

@nomnom
Danke für die Tipps, aber ich verstehe ehrlich gesagt nicht, wieso x immer none ist.
nomnom
User
Beiträge: 487
Registriert: Mittwoch 19. Mai 2010, 16:25

daz18 hat geschrieben:Danke für die Tipps, aber ich verstehe ehrlich gesagt nicht, wieso x immer none ist.
Deswegen solltest du ja drüber nachdenken. :( Es ist wirklich nicht schwer. Wenn du das nicht verstehst, dann solltest du nochmal das Tutorial durcharbeiten.
problembär

@daz18: Könnte sein, daß Dir noch nicht ganz klar ist, wie das Programm bei einer GUI-Anwendung abläuft. Das ist ja anders als bei einer Konsolenanwendung, bei der lediglich ein Befehl nach dem anderen abgearbeitet wird.
Also: Bei einer GUI-Anwendung (in diesem Fall mit Tkinter) definierst Du zuerst die Fensterelemente und rufst dann ".mainloop()" des "tk.Tk"-Objekts auf. Dann hängt die Anwendung sozusagen im Mainloop. Von dort werden Funktionen aufgerufen, die z.B. bei der Button-Definition über "command = ..." angegeben wurden.
Nach Ablauf der jeweiligen Funktion kehrt die Anwendung wieder in den Mainloop zurück. Das geht solange, bis die ".destroy()"-Methode des "tk.Tk"-Objekts aufgerufen wird, und also das Hauptfenster geschlossen wird.
Das mit dem Mainloop ist nötig, weil die Fensteroberfläche mehrere Benutzereingaben ("Events") von mehreren Widgets gleichzeitig verarbeiten können muß.
Hier wieder ein kleines Beispiel zum Verständnis:

Code: Alles auswählen

#!/usr/bin/python
# coding: iso-8859-1

import Tkinter as tk

class TkApp(object):

    def __init__(self):
        self.clicked = 0
        self.app = tk.Tk()
        self.app.title("Testfenster")
        self.btn = tk.Button(text = "Knopf", command = self.btnClick)
        self.btn.pack(padx = 60, pady = 30)
        self.exit_btn = tk.Button(text = "Exit", command = self.app.destroy)
        self.exit_btn.pack(padx = 60, pady = 20)
        self.app.mainloop()

    def btnClick(self):
        self.clicked += 1
        print "Knopf wurde " + str(self.clicked) + " mal gedrückt."

TkApp()
Ist es jetzt etwas klarer?
Antworten