Zahlen mittels Entry Feld einlesen

Fragen zu Tkinter.
Antworten
AnyOne
User
Beiträge: 18
Registriert: Donnerstag 25. Oktober 2012, 15:57
Wohnort: Aschaffenburg

Hallo Community,

ich bin neu in der Python Programmiersprache, habe aber vorher bereits C und C++ gelernt. Momentan entwickle ich eine GUI, mit welcher der User Konstanten für eine Berechnung festlegen können soll. Ich habe mir bereits mehrere Seiten im Internet zum Thema Tkinter und GUI Programmierung durchgelesen, aber irgendwie verstehe ich den Zusammenhang zwischen dem Entry-Feld und dem Auslesen des Entry-Feldes nicht.

Wenn ich jetzt folgenden Code erstelle:

Code: Alles auswählen

schOffsetX = tk.StringVar()

getConst_schOffsetXLabel = tk.Label(self.getConst, text = "schOffsetX")
getConst_schOffsetXEntry = tk.Entry(self.getConst, textvariable = self.schOffsetX)
Wie komme ich da an die vom Nutzer eingegebene Zahl?

Ich programmiere hier eine Klasse und dieser Ausschnitt befindet sich in der Funktion "getConstants(self):". Ausgelesen soll das Feld erst, wenn der Nutzer auf "Next" klickt, also in der Funktion getConstNext()

Code: Alles auswählen

getConst_NextButton = tk.Button(self.getConst, text = "Weiter", command = self.getConstNext)
Die Ausgelesene Zahl muss dann global für alle Funktionen der Klasse zur Verfügung stehen, da diese an mehreren Stellen benötigt wird.

Strings konnte ich immer problemlos mit self.<Variable>.get() auslesen, aber das funktioniert hier nicht. Da moniert mein eclipse immer sofort, dass das Attribut 'get' nicht für ein 'int' Objekt verfügbar sei. Sorry, wenn diese Frage für viele vielleicht zu doof klingen mag, aber ich komme da leider wirklich nicht weiter :( Vielen Dank für eure Hilfe!

Viele Grüße
Dennis
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi AnyOne

Willkommen im Forum. Da musst du aber die Bücher (Tutorials) nochmals gründlich durchlesen. Hier eine Snippet zum ausprobieren:

Code: Alles auswählen

try:
    #~~ For Python 2.x
    import Tkinter as tk
except ImportError:
    #~~ For Python 3.x
    import tkinter as tk

def get_constant():
    label_var.set(entry_var.get())
    print(entry_var.get())
    
app_win = tk.Tk()

#schOffsetX = tk.StringVar()

#getConst_schOffsetXLabel = tk.Label(self.getConst, text = "schOffsetX")
#getConst_schOffsetXEntry = tk.Entry(self.getConst, textvariable = self.schOffsetX)

label_var = tk.StringVar()
entry_var = tk.StringVar()

constant_display = tk.Label(app_win, textvariable=label_var, relief='raised',
    width=15)
constant_display.pack(pady=5)

constant_entry = tk.Entry(app_win, textvariable=entry_var, bg='white')
constant_entry.pack(pady=5)

tk.Button(app_win, text="Konstante ins Label transferieren!",
    command=get_constant).pack(pady=5)

app_win.mainloop()
Gruß wuf :wink:
Take it easy Mates!
AnyOne
User
Beiträge: 18
Registriert: Donnerstag 25. Oktober 2012, 15:57
Wohnort: Aschaffenburg

Hallo wuf,

vielen Dank für deine schnelle und hilfreiche Antwort! Ich habe dein Skript ausprobiert und einmal durchdacht (natürlich hat es funktioniert, woran ich auch zu keiner Zeit gezweifelt habe ;) )

Nun habe ich aber noch eine Frage:
Du deklarierst ja jetzt für beide Variablen das tk.StringVar(). Ich möchte jetzt, um in deinem Beispiel zu bleiben, in späteren Funktionen meiner Klasse mit der label_var Berechnungen durchführen. Kann ich das dann ohne weiteres machen? Also z.B. x = label_var * 2 oder solche Sachen eben.

Viele Grüße
AnyOne
BlackJack

@AnyOne: Nein, das geht nicht. Das hättest Du auch sehr schnell heraus finden können. Ein Objekt vom Typ `Tkinter.StringVar` verhält sich weder wie eine Zeichenkette noch wie eine Zahl. Sondern eben wie eines vom Typ `StringVar`.

IMHO sind diese `Tkinter.*Var`-Typen auch nicht besonders oft benutzt. In den meisten Fällen, so wie hier, sieht mir das wie eine unnötige Indirektion aus. Ich würde die glaube ich nur einsetzen, wenn ich das „Observer”-Entwurfsmuster bräuchte, weil man an diese Typen „listener” binden kann, die bei verschiedenen Ereignissen, zum Beispiel Änderung des Wertes, aufgerufen werden.

Im Beispiel kommt man jedenfalls auch bequem ohne die beiden aus, in dem man `Entry.get()` und `Label.configure()` oder den Indexoperator auf dem `Label`-Exemplar verwendet.
AnyOne
User
Beiträge: 18
Registriert: Donnerstag 25. Oktober 2012, 15:57
Wohnort: Aschaffenburg

@BlackJack: Also das 'Entry.get()' habe ich, mit meiner Entry-Bezeichnung, auch schon probiert. Hier ist aber das Problem, dass ich ja in einer anderen Funktion der Klasse auf das Entry keinen direkten Zugriff mehr habe, oder verstehe ich da grad was falsch?
BlackJack

@AnyOne: Ich verstehe die Frage nicht so ganz, denn mit den `StringVar`-Exemplaren hättest Du ja das gleiche Problem. Da scheinst Du es aber anscheinend hin zu bekommen? Wenn man in einer Methode auf ein Objekt zugreifen möchte, muss man es halt irgendwie zugänglich machen. In der Regel als Argument oder als Attribut auf dem Objekt auf dem die Methode existiert. Das ist in C++ doch auch nicht anders.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi AnyOne

Hier ein Lösungsansatz:

Code: Alles auswählen

try:
    #~~ For Python 2.x
    import Tkinter as tk
except ImportError:
    #~~ For Python 3.x
    import tkinter as tk

def get_constant():
    
    try:
        # Einlesen der Konstante
        constant = float(entry_var.get())
        # Math operation
        result = constant * 2.0
        # Ausgabe des Resultats
        label_var.set(result)
        
    # Abfangen von Eingabefehlern
    except ValueError as e:
        print(e)
        
app_win = tk.Tk()

label_var = tk.StringVar()
entry_var = tk.StringVar()

constant_display = tk.Label(app_win, textvariable=label_var, relief='raised',
    width=15, justify='left')
constant_display.pack(pady=5)

constant_entry = tk.Entry(app_win, textvariable=entry_var, bg='white')
constant_entry.pack(pady=5)

tk.Button(app_win, text="Konstante ins Label transferieren!",
    command=get_constant).pack(pady=5)

app_win.mainloop()
Gruß wuf :wink:
Take it easy Mates!
AnyOne
User
Beiträge: 18
Registriert: Donnerstag 25. Oktober 2012, 15:57
Wohnort: Aschaffenburg

@BlackJack: StringVar() mag er nur, wenn ich auch wirklich einen String eingebe. Sobald da z.B. "1.8" eingegeben wird, meckert der Interpreter. Ich werde das Ganze jetzt einmal mit Klassenvariablen versuchen, die ich im Konstruktor definiere. So müsste ich diese Variablen ja den Funktionen der Klasse zugänglich machen können. Ich hoffe das Entry-Feld kann darauf dann zugreifen?

Code: Alles auswählen

class Blubb(object):
    def __init__(self):
        self.__entryVar = 0

    ...

    def GUI(self):
        ...
        testEntry = tk.Entry(self.GUI, textvariable = self.__entryVar)
        ...

    def printEntry(self):
        print self.__entryVar
So müsste doch dann das Zusammenspiel klappen, hoffe ich :)

@wuf: Wenn ich später zuhause bin, werde ich dein neues Beispiel durchdenken, vielen Dank schon einmal hierfür :)

Viele Grüße
AnyOne
BlackJack

@AnyOne: Das kann nicht sein — man kann selbstverständlich auch die Zeichenkette '1.8' in einem `StringVar`-Objekt speichern:

Code: Alles auswählen

In [8]: import Tkinter as tk

In [9]: root = tk.Tk()

In [10]: sv = tk.StringVar(root, '1.8')

In [11]: sv.get()
Out[11]: '1.8'
Du möchtest keine Klassenvariablen sondern Instanzvariablen verwenden.

Das Beispiel kann so natürlich nicht funktionieren, weil das `textvariable`-Argument keine Zahl sondern zum Beispiel ein `StringVar`-Exemplar erwartet.

Die Namen halten sich nicht an PEP 8 -- Style Guide for Python Code.

In `GUI()` übergibst Du `Entry` als erstes Argument die Methode, was nicht funktioniert.

Der doppelte Unterstrich bei `__entryVar` ist einer zu viel. Es ist sicher nicht davon auszugehen, dass es da bei abgeleiteten Klassen zu Namenskollisionen kommen wird.
Antworten