Probleme mit Variablen und tkinter

Fragen zu Tkinter.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

kaytec hat geschrieben:doch bei meinem wollte ich nicht, dass die Zeichen +,- etc. im Rechenvorgang zusehen sind, sondern es wie ein richtiger taschenrechener funktioniert.
Mein "richtiger Taschenrechner" Zeigt auch Rechenzeichen an - finde ich sehr praktisch, weil ich die Eingaben editieren kann.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo pystyler !!

Da hast Du recht - habe ja auch ihn gefunden bei meiner Suche nach einem Anstoss, doch ihn kopieren wäre ja nicht gerade eine Anforderung - code zu klauen ist ok, doch wollte ich es halt anders machen und dabei lernen. Es geht nicht um besser oder schlechter, sondern um den Spass an der Sache (intrinsiche Motivation).

Danke für Deine Links - gruss frank
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

So - habe es mal eingebaut! Es passiert nicht´s - bekomme aber auch keine Fehlermeldung. Das Entry - Feld wird auch nicht gelöscht ??

Code: Alles auswählen

 self.funktasten = Button(self, text=text,width=width, command=lambda text=text:self._make_operator_command)
                  self.funktasten.grid(row=row, column=column, padx=padx, pady=pady)

                
            
            self.ausgabe = Entry(self,width=29,justify="right",)
            self.ausgabe.grid(row=0, column=0,columnspan=6,pady=10)


              
      def _make_operator_command(self, text):
            
              def operator_command():
                  self.speicher += self.ausgabe.get() + text
                  self.ausgabe.delete(0, END)
              return operator_command

gruss und dank frank
BlackJack

Erst einmal brauchst Du natürlich das ``lambda`` nicht mehr. Die Funktion soll ja von der neuen Methode erzeugt werden. Und dazu musst Du die auch aufrufen ─ mit dem Text der eingefügt werden soll als Argument.

Code: Alles auswählen

taste = Button(..., command=self._make_operator_command(text))
Der Name `taste` braucht auch nicht an das `Rechner`-Objekt gebunden werden. Ausserdem ist Mehrzahl falsch, der Name ist ja immer nur an *einen* `Button` gebunden. In der Zifferntasten-Schleife ist es das gleiche.
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Danke BlackJack !!!

Habe voher mal versucht die links über closures zu verstehen - ok hat nicht geklappt !!! :-) Durch Deine Hilfe geht es jetzt - super und danke

gruss frank
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

So an den Rest habe ich auch mal versucht einzubauen, doch macht das so Sinn oder geht es noch einfacher ?

Code: Alles auswählen

def make_auswertung(self,text):

            def auswerten():

                  if text =="C":
                        
                        self.ausgabe.delete(0,END)
                        self.speicher = ""
                        self.mspeicher = ""

                  if text =="%":

                        self.speicher = self.ausgabe.get()
                        self.speicher = (self.speicher + "/" + "100" + "*")
                        self.ausgabe.delete(0,END)

                  if text =="M":

                        if self.ausgabe.get() =="":
                  
                              self.speicher = self.speicher + self.mspeicher
                  
                        if self.ausgabe.get() !="":
                  
                              self.mspeicher = self.ausgabe.get()
                              self.ausgabe.delete(0,END)
                              self.ausgabe.insert(50,"")


                  if text =="+/-":

                        self.vorzeichen = str(float (self.ausgabe.get()) * -1)
                        self.ausgabe.delete(0,END)
                        self.ausgabe.insert(50,self.vorzeichen)
                  
                  if text =="=":
            
                        try:
                  
                              self.speicher = str(eval (self.speicher + self.ausgabe.get()))
                              self.ausgabe.delete(0,END)
                              self.ausgabe.insert(50,self.speicher)
                              self.speicher = ""

                        except:
                              
                              self.ausgabe.delete(0,END)
                              self.speicher = ""
                              self.ausgabe.insert(50,"E")
               

            return auswerten
gruss und dank frank
BlackJack

Das "verstecken" der Alternativen in einer inneren Funktion und die ganzen ``if``-Abfragen finde ich persönlich unschön. Eine Funktion sollte möglichst nur eine abgegrenzte Funktionalität implementieren. `auswerten()` macht dagegen eine ganze Menge verschiedener Dinge.
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Danke BlackJack !!!

Jede Taste ausformulieren, damit ich an jede einzelne Taste eine Funktion binden kann ? So wie der Rechner am Anfang war ?

gruss frank
BlackJack

So würde ich das machen, ja. Wobei ich wohl auch den Taschenrechner, d.h. die Logik davon, von der GUI trennen würde. Damit man den Taschenrechner selbst, ohne GUI (automatisch) testen kann und eventuell auch die GUI austauschbar ist.
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Danke BlackJack !!!

Mir wurde bei einem anderem Programm gesagt:
"Hi kaytec!
Auf den ersten Blick würde ich sagen, dein Programm ist nicht kompliziert, sondern eher etwas zu ausformuliert. Du könntest mit einigen Schleifen sicher recht viel code sparen, da tw. eigentlich immer das selbe machst und nur die Namen austauscht. Zudem kann ich dir für solche Sachen Tix.LabelEntry empfehlen, dann ist's noch kürzer. "

Jetzt habe ich dieses Programm mit schleifen verkürzt - oder habe ich das nicht richtig verstanden?

Das wichtigste ist doch das Auslesen des string mit eval(self.speicher). Der Rest ist doch eigentlich nur für die Eingaben über die Oberfläche und das richtige Einlesen in den string ? Meinst du damit die Trennung von Oberfläche und Berechnung ?

gruss frank
BlackJack

Das mit den Schleifen zum Beseitigen von sich wiederholendem Code hast Du richtig verstanden.

Mit der Trennung meine ich, dass man Taschenrecher-Logik und GUI komplett trennt. Das ist jetzt bei Dir beides sehr eng miteinander verknüpft. Du speicherst zum Beispiel für das Programm wichtige Daten in der GUI (`self.ausgabe`) und die meisten Aktionen machen etwas mit dem internen Zustand des Taschenrechners *und* mit der GUI.

Dadurch lässt sich der Taschenrechner bzw. die einzelnen Funktionen nicht ohne GUI testen. Das macht automatisierte Tests und die Fehlersuche schwierig.

Hier ist mal ein minimales Beispiel eines Rechners ohne GUI, der so ähnlich aufgebaut ist wie Deiner:

Code: Alles auswählen

class Calculator(object):
    def __init__(self):
        self.display = ''
        self.expression = ''
    
    def clear(self):
        self.display = ''
        self.expression = ''
    
    def enter_number(self, number):
        self.expression += number
        self.display = number
    
    def enter_operator(self, operator):
        assert operator in list('+-*/')
        self.expression += operator
    
    def evaluate(self):
        try:
            self.display = eval(self.expression)
        except (SyntaxError, ArithmeticError):
            self.display = 'Error'
        self.expression = self.display
Den kann man jetzt interaktiv, oder mit einem Testprogramm ausprobieren:

Code: Alles auswählen

In [68]: a = test.Calculator()

In [69]: a.display
Out[69]: ''

In [70]: a.enter_number('42')

In [71]: a.display
Out[71]: '42'

In [72]: a.expression
Out[72]: '42'

In [73]: a.enter_operator('+')

In [74]: a.display
Out[74]: '42'

In [75]: a.expression
Out[75]: '42+'

In [76]: a.enter_number('23')

In [77]: a.expression
Out[77]: '42+23'

In [78]: a.display
Out[78]: '23'

In [79]: a.evaluate()

In [80]: a.display
Out[80]: 65

In [81]: a.expression
Out[81]: 65
Und wenn man sicher ist, das alles funktioniert, kann man eine GUI draufsetzen.

Diese Trennung wird umso wichtiger und nützlicher je komplexer die beiden Hälften Programmlogik und GUI werden. Und für einen vernünftigen Rechner müssten beide noch ein wenig komplexer werden. Diese `eval()`-Geschichte ist nämlich alles andere als robust.
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo BlackJack !!!

Wäre das eine Trennung ?

Code: Alles auswählen

class Calculator(object):
    def __init__(self):
        self.display = ''
        self.expression = ''
   
    def clear(self):
        self.display = ''
        self.expression = ''
   
    def enter_number(self, number):
        self.expression += number
        self.display = number
   
    def enter_operator(self, operator):
        assert operator in list('+-*/')
        self.expression += operator
   
    def evaluate(self):
        try:
            self.display = eval(self.expression)
        except (SyntaxError, ArithmeticError):
            self.display = 'Error'
            self.display = self.expression

a = Calculator()

a.enter_number(raw_input('Eingabe 1.Zahl: '))
print 'a.display:',a.display
print 'a.expression:',a.expression

a.enter_operator(raw_input('Eingabe +-*/: '))
print 'a.expression:',a.expression

a.enter_number(raw_input('Eingabe 2.Zahl: '))
print 'a.display:',a.display
print 'a.expression:',a.expression

a.evaluate()
print 'a.display:',a.display

gruss und dank frank
Antworten