Tkinter Eingabe

Fragen zu Tkinter.
Antworten
asdfgh
User
Beiträge: 13
Registriert: Donnerstag 11. Dezember 2014, 14:59

Hallo! Mein bisheriger Code:
https://github.com/toxinman/zeug/blob/m ... Vignere.py

Diesen will ich jetzt mittels Tkinter eine Gui verleihen, stehe aber ziemlich auf dem Schlauch. Muss ich eine eigene Klasse erstellen? Oder kann ich einfach eine Funktion erstellen?

Ich habe den auf dieser Seite: http://effbot.org/tkinterbook/entry.htm geschriebenen Code versucht zu bearbeiten, leider hat es nicht funktioniert.
asdfgh
User
Beiträge: 13
Registriert: Donnerstag 11. Dezember 2014, 14:59

Hier meine veränderte Main-Funktion:

Code: Alles auswählen

def main():
    """Hauptfunktion"""
    root = tk.Tk()
    tk.Label(root, text="Vigenere").pack()
    eingabe = tk.Entry(root).pack()
    neu = eingabe.get()
    root.mainloop()
    #text = raw_input("Text: ").upper()
    #code = (raw_input("Code: ").upper())*50
    
    #tabelle = [
     #   list(text),
      #  list(code)[:len(text)],
       # ["0"]*len(text)
    #]
    
    tabelle_ausgeben(vigenere_anwenden(tabelle))
Wie kann ich jetzt Sachen aus dem Eingabefeld auffangen?

Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "/Users/ich/Desktop/t.py", line 56, in <module>
    main()
  File "/Users/ich/Desktop/t.py", line 42, in main
    neu = eingabe.get()
AttributeError: 'NoneType' object has no attribute 'get'
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@asdfgh: die pack-Methode eines Entry-Objekts liefert eben None als Rückgabewert. Es wäre auch sinnvoller, wenn Du das Entry-Objekt direkt an eine Variable binden würdest. Was erwartest Du, wenn Du direkt nach erstellen den Wert Deines Eingabefeldes abfragst? GUIs verarbeiten Ereignisse. mit einem linearen Programmablauf kommst Du da nicht weiter.
asdfgh
User
Beiträge: 13
Registriert: Donnerstag 11. Dezember 2014, 14:59

@ Sirius3 Also sollte ich das ganze in eine Klasse packen?

P.S.: Hast du gute Website-Empfehlungen zu Tkinter?
BlackJack

@asdfgh: Man kann das auch funktional lösen, aber objektorientiert wäre in Python ”natürlicher”. Der Punkt ist wie von Sirius3 schon geschrieben wurde, dass GUI-Programmierung nicht linear funktioniert, also nicht *Du* die Reihenfolge des Programmablaufs insgesamt bestimmst, sondern nur in kleinen Abschnitten die auf Ereignisse aus der GUI reagieren. Du musst also den Code der das Eingabefeld ausliest, den Text verschlüsselt, und das Ergebnis irgendwie anzeigt an ein Ereignis binden das von der GUI kommt und vom Benutzer ausgelöst wird. Zum Beispiel die Enter-Taste die im Texteingabefeld betätigt wird, eine zusätzliche Schaltfläche, oder zum Beispiel ein `Tkinter.StringVar`-Objekt an das `Entry`-Objekt beim erstellen als `textvariable` übergeben. Bei `StringVar` kann man dann eine Funktion binden die bei jeder Veränderung des Inhalts aufgerufen wird.
asdfgh
User
Beiträge: 13
Registriert: Donnerstag 11. Dezember 2014, 14:59

Ok, ich habe mal ein paar Versuche gemacht:

Code: Alles auswählen

# -*- coding: UTF8

try:
    import tkinter as tk
except ImportError:
    import Tkinter as tk

ALPHABET = map(chr, [x for x in range(65,91)])

class Vigenere(object):

	def __init__(self):
		self.root = tk.Tk()

		self.ausgabe = tk.Label(self.root)
		self.text = tk.Entry(self.root)
		self.codewort = tk.Entry(self.root)
		self.button = tk.Button(self.root, text="Codieren", command=self.codieren)

		self.text.pack()
		self.codewort.pack()
		self.button.pack()
		self.ausgabe.pack()

		self.root.mainloop()

	def codieren(self):
		text = self.text.get()
		codewort = self.codewort.get()

		tabelle = [
			list(text),
			list(codewort)[:len(text)],
			["0"]*len(text)
		]
		tabelle_ausgeben(vigenere_anwenden(tabelle))


def verschieben(buchstabe, verschiebezahl):
    """Buchstaben der Ausgangsnachricht im Alphabet 
    um Verschiebezahl verschieben"""
    neu = chr(ord(buchstabe) + verschiebezahl)
    if neu not in ALPHABET:
        return chr(ord(neu) - 26)
    else:
        return neu


def verschiebezahl_ermitteln(buchstabe):
    """Verschiebezahl eines Buchstaben aus dem 
    Code-Wort ermitteln"""
    return ord(buchstabe) - 65


def vigenere_anwenden(tabelle):
    """Vignere-Verschluesselung anwenden"""
    for idx, buchstabe in enumerate(tabelle[0]):
        tabelle[2][idx] = verschieben(
            buchstabe, verschiebezahl_ermitteln(tabelle[1][idx])
        )
    return tabelle


def tabelle_ausgeben(tabelle):
    """Tabelle bestehend aus Ausgangstext, CodeWort 
    und Verschluesselung formatiert ausgeben"""
    for reihe in tabelle:
        print "".join(reihe)
Sollte ich das Programm an sich und die GUI in zwei verschiedene Klassen packen?
asdfgh
User
Beiträge: 13
Registriert: Donnerstag 11. Dezember 2014, 14:59

Also ich habe mir gerade das MVC-Modell angeschaut, aber dass schien mir ein wenig überdimensioniert für diese Aufgabe. Gibt es noch andere Möglichkeiten, dass Programm und die GUI sinnvoll zu trennen?
asdfgh
User
Beiträge: 13
Registriert: Donnerstag 11. Dezember 2014, 14:59

Könnte mir jemand eine grobe Struktur vorgeben, wie man sowas macht?
asdfgh
User
Beiträge: 13
Registriert: Donnerstag 11. Dezember 2014, 14:59

Oder mir eine Website empfehlen?
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@asdfgh: das einzige was Du beachten solltest ist "Separation of Concerns", dass also Deine Berechnungsfunktionen nichts über Deine GUI und umgekehrt, die GUI nichts über den Inneren Aufbau Deiner Berechnung wissen dürfen.

PS: ein __init__-ialisierter sollte nur Initialisieren und nicht auch noch die ganze Arbeit machen (mainloop). Die vigenere_anwenden-Funktion ist seltsam, da sie die Parameter als Tabelle will, und man sogar noch die Ergebnisliste vorinitialisieren muß. Alles Aufgaben, die die eigentliche Funktion vor dem Anwender verstecken sollte. In diesem Zusammenhang, es ist unüblich in Python mit einem Index explizit auf Tabellen zuzugreifen, wenn man auch über die Elemente iterieren kann. Also:

Code: Alles auswählen

def vigenere_anwenden(text, code):
    """Vignere-Verschluesselung anwenden"""
    return [ verschieben(char_text, verschiebezahl_ermitteln(char_code))
        for char_text, char_code in zip(text, code)]
Und tabelle_ausgeben wäre ja eine Funktion, in der die GUI beteiligt ist.
asdfgh
User
Beiträge: 13
Registriert: Donnerstag 11. Dezember 2014, 14:59

Danke für deine Hilfe :D Werde versuchen, es umzusetzen.
asdfgh
User
Beiträge: 13
Registriert: Donnerstag 11. Dezember 2014, 14:59

Habe das ganze jetzt so gemacht:

Code: Alles auswählen

# -*- coding: UTF8

try:
    import tkinter as tk
except ImportError:
    import Tkinter as tk

ALPHABET = map(chr, [x for x in range(65, 91)])


class Vigenere(object):

    def __init__(self):
        self.root = tk.Tk()

        self.text = tk.Entry(self.root)
        self.code = tk.Entry(self.root)
        self.button = tk.Button(
            self.root, text="Codieren",
            command=self.vigenere_anwenden
        )
        self.ausgabe = tk.Label(self.root)

        self.text.pack()
        self.code.pack()
        self.button.pack()
        self.ausgabe.pack()

    def vigenere_anwenden(self):
        text = self.text.get()
        code = self.code.get()
        self.ausgabe.config(text="".join([
            self.verschieben(char_text, self.verschiebezahl_ermitteln(char_code))
            for char_text, char_code in zip(text, code)
        ]))

    def verschieben(self, buchstabe, verschiebezahl):
        neu = chr(ord(buchstabe) + verschiebezahl)
        if neu not in ALPHABET:
            return chr(ord(neu) - 26)
        else:
            return neu

    def verschiebezahl_ermitteln(self, buchstabe):
        return ord(buchstabe) - 65

    def run(self):
        self.root.mainloop()


if __name__ == '__main__':
    vigenere = Vigenere()
    vigenere.run()
Leider stürzt das Programm ab. Außerdem weiß ich nicht, wie ich sowas

Code: Alles auswählen

def vigenere_anwenden(self):
        text = self.text.get()
        code = self.code.get()
        self.ausgabe.config...
ändern soll, weil hier ja die GUI in den Berechnungen auftritt. Leider wird das in meinen Büchern auch so gemacht, deshalb kenne ich keine andere Möglichkeit.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

asdfgh hat geschrieben:

Code: Alles auswählen

def vigenere_anwenden(self):
        text = self.text.get()
        code = self.code.get()
        self.ausgabe.config...
ändern soll, weil hier ja die GUI in den Berechnungen auftritt. Leider wird das in meinen Büchern auch so gemacht, deshalb kenne ich keine andere Möglichkeit.
Du schreibst einfach zwei Funktionen dafür. Die erste wird beim Klick auf den Button aufgerufen, liest die Werte der Widgets aus und wandelt ggf. noch die Eingaben in Integer, Floats oder welcher Datentyp auch immer gebraucht wird. Dann ruft die Funktion die reine Berechnungsfunktion auf, nimmt dessen Ergebnis entgegen und zeigt das dann ggf. an.
Das Leben ist wie ein Tennisball.
asdfgh
User
Beiträge: 13
Registriert: Donnerstag 11. Dezember 2014, 14:59

Danke für den Tipp :). Jetzt sieht das ganze so aus:

Code: Alles auswählen

    def __init__(self):
        self.root = tk.Tk()

        self.text = tk.Entry(self.root)
        self.code = tk.Entry(self.root)
        self.button = tk.Button(
            self.root, text="Codieren",
            command=self.button_clicked
        )
        self.ausgabe = tk.Label(self.root, text="Text und Codewort eingeben")

        self.text.pack()
        self.code.pack()
        self.button.pack()
        self.ausgabe.pack()

    def button_clicked(self):
        text = self.text.get()
        code = self.code.get()
        ergebnis = "".join(self.vigenere_anwenden(text, code))
        self.ausgabe.config(text=ergebnis)

    def vigenere_anwenden(self, text, code):
        return [
            self.verschieben(
                char_text, self.verschiebezahl_ermitteln(char_code))
            for char_text, char_code in zip(text, code)
        ]
...

    def run(self):
        self.root.mainloop()
Leider hängt es sich immer noch bei Betätigen des Buttons auf. Wie finde ich die Ursache davon?
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@asdfgh: das ist ja jetzt das genaue Gegenteil von Trennen der Verantwortlichkeiten. Was haben vigenere_anwenden, verschieben und verschiebezahl_ermitteln mit der GUI-Klasse zu tun?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

asdfgh hat geschrieben:Leider hängt es sich immer noch bei Betätigen des Buttons auf. Wie finde ich die Ursache davon?
Fehlermeldung?
Das Leben ist wie ein Tennisball.
Kalli87
User
Beiträge: 281
Registriert: Montag 10. November 2014, 11:27

Wenn sich der PC aufhängt, was ich bei dieser Aussage vermute, hat er eine
Endlosschleife die den Speicher zu spamt. Da wird es zu keiner Fehlermeldung
kommen ;)
BlackJack

In dem Fall fehlt allerdings der Quelltext in dem das Problem ist, denn im bisher gezeigten sehe ich keine solche Schleife.
asdfgh
User
Beiträge: 13
Registriert: Donnerstag 11. Dezember 2014, 14:59

Hier der aktuelle Quelltext:

Code: Alles auswählen

# -*- coding: UTF8 -*-

try:
    import tkinter as tk
except ImportError:
    import Tkinter as tk

ALPHABET = map(chr, [x for x in range(65, 91)])


class Vigenere(object):

    def __init__(self):
        self.root = tk.Tk()

        self.text = tk.Entry(self.root)
        self.code = tk.Entry(self.root)
        self.button = tk.Button(
            self.root, text="Codieren", command=self.button_clicked)
        self.ausgabe = tk.Label(self.root, text="Text und Codewort eingeben")

        self.text.pack()
        self.code.pack()
        self.button.pack()
        self.ausgabe.pack()

    def button_clicked(self):
        text = self.text.get()
        code = self.code.get()
        ergebnis = "".join(self.vigenere_anwenden(text, code))
        self.ausgabe.config(text=ergebnis)

    def vigenere_anwenden(self, text, code):
        return [
            self.verschieben(
                char_text, self.verschiebezahl_ermitteln(char_code))
            for char_text, char_code in zip(text, code)
        ]

    def verschieben(self, buchstabe, verschiebezahl):
        neu = chr(ord(buchstabe) + verschiebezahl)
        if neu not in ALPHABET:
            return chr(ord(neu) - 26)
        else:
            return neu

    def verschiebezahl_ermitteln(self, buchstabe):
        return ord(buchstabe) - 65

    def run(self):
        self.root.mainloop()


if __name__ == '__main__':
    vigenere = Vigenere()
    vigenere.run()
Zum Trennen von GUI und Logik muss ich die Logik-Funktionen außerhalb der Klasse schreiben und in der Klasse aufrufen?
BlackJack

@asdfgh: Ich sehe da keine Endlosschleife. Also weder im Code, noch hängt da irgendwas wenn ich das Programm ausführe.

Die Verschlüsselung ist allerdings fehlerhaft, da verschwindet Text wenn man Text/Code eingibt der nicht irgendwelchen Bedingungen entspricht.
Antworten