Taschenrechner programmieren

Fragen zu Tkinter.
Antworten
Luisa00
User
Beiträge: 3
Registriert: Donnerstag 24. Februar 2022, 12:15

Hallo,
ich muss für ein Informatik Projekt einen Taschenrechner programmieren. Allerdings komm ich schon seit Tagen nicht weiter, da ich zwar ein Widget habe aber mein Taschenrechner nichts rechnen will. Mir wird auch keine Fehlermeldung angezeigt und ich weiß einfach nicht was ich tun soll. Kann mir jemand von euch weiterhelfen. Danke im vorraus. Alles hinter dem # könnt Ihr ignorieren, das sind nur Hilfestellungen für mich, da ich das Projekt nochmal vorstellen muss.
LG :D


import tkinter as tk #Tkinter ist ein vorinstalliertes Modul welches nur noch Importiert werden muss, tkinter
#wird jetzt tk genannt in unserem Code, durch das `as`


rechner = tk.Tk() #rechner ist unsere Variable, Ihr weisen wir die Funktionsweise des Objektes "tk.Tk()" zu
# hiermit bestimmt man die Größe der Benutzteroberfläche (gui), dies ist zurzeit nur ein
#weißes Fenster
rechner.geometry('400x450') # Mit dem Objekt der Klasse "geometry" können wir die Größe unseres Fenster festlegen
rechner.title('Taschenrechner') # mit dem Objekt der Klasse "title" können wir dem erstellten Fenster einen Namen geben


button_values = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '+', #Die Werte der Buttons werden in der Variable
'-', '0', '*', '/', '=', 'AC'] #"button_values" gespeichert

gui_items = list() #Der Variable "gui_items" weisen wir die Funktion einer (noch leeren) Liste aus

calculation = str()

def create_button(value): #Damit aus den einzelnen "button_values" tatsächlich Buttons werden
button = tk.Button(text=value) #erstellen wir eine Funktion, die aus jedem einzelnen Element der "button_values" einen Button erstellt
gui_items.append(button) # und diesen Button dann in die Liste "Gui_items" ablegt

for val in button_values: # jetzt wird diese Funktion für alle Elemente in der Liste aufgeführt → Button ist erstellt
create_button(val)

output_label = tk.Label(text='Hallo Klasse.') #Durch das Objekt der Klasse "Label" können wir ein Widget mithilfe von tk.Tk() erstellen
#Das Widget ist in diesem Fall unsere Anzeige, in der das Ergebnis der Rechnung stehen wird
#Anfangs, steht dort Hallo Klasse.
output_label.grid(row=0, columnspan=10) #Durch das Objekt der Klasse "grid" können wir einstellen in welcher Zeile (Zeilenbreite) und Spalte (Spaltenbreite) unser Text sich befindet

column_count = 0 #Die Buttons beginnen in Spalte 0
row_count = 1 #Die Buttons beginnen in Zeile 1
max_columns = 4 #und es gibt maximal 4 Spalten, bevor eine neue Zeile anfängt

for item in gui_items: #Das Programm durchläuft jetzt die "gui_items" Liste und ordnet deren Elemente in Gittern an

item.grid(row=row_count, column=column_count) #Die items (elemente aus der Liste) werden in Gittern angeordnet, deren Zeilen und Spalten den Werten der Variablen "row count" "column_count" entsprechen

column_count += 1 #Jeder weitere Button kommt in eine neue Zeile

if column_count == max_columns: #Wenn man beim maximalen, also beim 4. Button angekommen ist
column_count = 0 #Dann geht es nicht mehr in der Spalte
row_count += 1 #sondern in einer neuen Zeile weiter

if __name__ == '__main__':
rechner.mainloop() #Die Funktion des Objekts "tk.Tk()" wird durch die Methide "mainloop" gestartet
#Durch das Aufrufen der Methode "mainloop" stoppt die Abarbeitung des Python codes und es beginnt ein eventloop
#Das GUI Fenster wird geöffnet und es wird geschaut, ob ein Event auftritt (Interaktion des GUI Fensters mit dem Benutzer), wenn das passiert, dann verändert sich das Fenster dementsprechend

calculation = str() #leere Zeichenkette die wir als string definieren also abfolge von verschieden Zahlen und Wörtern
#Calculation ist die Variable für String

def add_button_value_to_calculation(values): #Hier wird der Wert des gedrückten Buttons zu der leeren Zeichenkette hinzugefügt
global calculation # Die Variable calculation ist global das heist dass sie sowohl in der Hauptfunktion als auch in der
# Unterfunktion den selben wert hat sobald man keine lokale Variable mit dem selben Namen hat
#globaler Gültigkeitsberecih, überall gültig

calculation = calculation + values #
output_label['text'] = calculation #Label ist ein Widget, welches man sich anschauen kann aber nicht mit interagieren kann

def add_button_value_to_calculation(value) :
global calculation

if val == 'AC': # AC (auch 'ALL CLEAR' genannt) löscht alles aus der Zeile, wenn der Wert AC benutzt wird
calculation = str() #Unsere Variable calculation ist jetzt wieder aus der Klasse String
output_label['text'] = '...' #nichts wird wieder ausgegeben und man geht an den Anfang der Variablen zurück3
return

if val == '=':
calculate : calculation
calculation = str()
return

calculation = calculation + val
output_label['text'] = calculation

def calculate(calc): #Hier definieren wir die Zeichenkette als Berechnung

try:
result : eval(calc) #eval bedeutet dass der String in eine Berechnung umgewandelt werden
print(result)
output_label['text'] = result

except Exception as e:
print(e)
output_label['text'] = 'Error'

def create_button(value): #
button = tk.Button(text=value, command=lambda: add_button_value_to_calculation(value))
gui_items.append(button)
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Die vielen Kommentare machen den Code so gut wie unlesebar.
Das Programm ist 46 Zeilen lang, und danach kommt noch viel Zeugs, was aber gar nicht benutzt wird.

Man setzt keine geometry explizit, weil das nur dazu führt, dass die Fenster komisch aussehen, weil entweder viel zu viel oder viel zu wenig Platz da ist.
Die Anordnung der Buttons ist für einen Taschenrechner ungewöhnlich.
Besser wäre eine Liste aus Listen, mit den Button-Beschriftungen, denn das entsprich ja mehr der Grid-Anordnung.
Dann muß man auch nicht selbst columns und rows zählen.
Globale Variablen benutzt man nicht, alles was eine Funktion braucht, sollte sie auch über ihre Argumente bekommen.
Widgets sollten als erstes Argument ihr root-Widget bekommen.

Das Hauptprogramm steht in einer Funktion `main`.

Berücksichtig man das alles, kommt ein schön kompakter Code heraus:

Code: Alles auswählen

import tkinter as tk

BUTTON_VALUES = [
    ['1', '2', '3', '+'],
    ['4', '5', '6', '*'],
    ['7', '8', '9', '-'],
    ['0', '/', '=', 'AC']
]

def main():
    rechner = tk.Tk()
    rechner.title('Taschenrechner')
    
    tk.Label(rechner, text='Hallo Klasse.').grid(row=0, column=0, columnspan=10)

    for row_index, row in enumerate(BUTTON_VALUES, 1):
        for column_index, text in enumerate(row):
            button = tk.Button(rechner, text=text)
            button.grid(row=row_index, column=column_index)
    rechner.mainloop()

if __name__ == '__main__':
    main()
Da ist aber nirgends eine Funktionalität hinter den Buttons implementiert.

Wenn ich mir den ungenutzen Code anschaue, dann gibt es da nochmals eine `create_button`-Funktion, die tatsächlich Funktionalität an die Buttons bindet.
`add_button_value_to_calculation` braucht als Argument eine Variable, die die Eingabe speichern kann, das geht mit einem String (wie bei dir die globale Variable calculation) nicht, weil die unveränderlich sind. tk.StringVar bietet sich da sehr gut an.
Was soll das s am Argument `values`? Es wird doch nur ein Wert übergeben.
In `add_button_value_to_calculation` ist nochmal eine Funktion `add_button_value_to_calculation` definiert, die keinen Sinn macht.
Dann gibt es plötzlich eine Variable `val` die aber gar nicht definiert ist.
Es scheint so, als ob Du mit der Funktion angefangen hast, dann gedacht hast, als ob Du die Funktion nochmal ändern möchtest, dann aber eine Mischung aus alt und neu entstanden ist.
Statt if und return benutzt man eine if-elif-Kette.
Die Zeile `calculate : calculation` macht keinen Sinn, hier wolltest Du wohl calculate aufrufen: `calculation = calculate()`.
Den gleichen Fehler hast du bei `eval` nochmal gemacht.

Generell: `eval` darf man nicht verwenden. Ein klassischer Taschenrechner funktionier auch nicht so.

Ich hab jetzt aber das `eval` dringelassen, weil sonst noch viel mehr Code nötig wäre, was den Rahmen dieses Beitrags sprengen würde:

Code: Alles auswählen

import tkinter as tk
from functools import partial

BUTTON_VALUES = [
    ['1', '2', '3', '+'],
    ['4', '5', '6', '*'],
    ['7', '8', '9', '-'],
    ['0', '/', '=', 'AC']
]

def add_button_value_to_calculation(output, value):
    if value == 'AC':
        output.set('')
    elif value == '=':
        output.set(eval(output.get()))
    else:
        output.set(output.get() + value) 


def main():
    rechner = tk.Tk()
    rechner.title('Taschenrechner')
    
    output = tk.StringVar(rechner)
    tk.Label(rechner, textvariable=output).grid(row=0, column=0, columnspan=10)

    for row_index, row in enumerate(BUTTON_VALUES, 1):
        for column_index, text in enumerate(row):
            button = tk.Button(rechner, text=text, command=partial(add_button_value_to_calculation, output, text))
            button.grid(row=row_index, column=column_index)
    rechner.mainloop()

if __name__ == '__main__':
    main()
Luisa00
User
Beiträge: 3
Registriert: Donnerstag 24. Februar 2022, 12:15

Vielen Dank für deine schnelle Antwort! Das ist grad das erste mal, dass ich mit Python arbeite, deswegen ist es auch ein typisches Anfängerprojekt. Darf ich mal fragen wie lange du jetzt gebraucht hast um das alles zu schreiben? Ich wollte noch Konstanten einfügen und Wurzel Exponenten und so..... Ich hab diesen Code nämlich aus einem Internet Tutorial und hab halt auch versucht ihn mit verschieden YouTube Tutorials und so zu erklären, allerdings habe ich halt so Probleme das alles zu verstehen. Davor haben wir nämlich nur mit Java programmiert und ich musste mir das alles irgendwie auf die schnelle selbst beibringen.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Luisa00: Das ist IMHO kein typisches Anfängerprojekt. GUIs sind schon nichts für Anfänger, weil man da eigentlich bis einschliesslich objektorientierter Programmierung (OOP), also selbst Klassen schreiben, schon alles als Voraussetzung für können muss. Sirius3 ist da jetzt noch mit Closures/`functools.partial()` ausgekommen, aber OOP und Closures sind von der Komplexität das zu verstehen in etwa gleich.

Wenn man *so* ausführlich kommentieren will/muss, was aber sehr ungewöhnlich ist, und wie Sirius3 schon angemerkt hat, das Programm an sich schwerer verständlich machen kann, weil das eigentliche Programm von dem Text ”erdrückt” wird, geht das stark in die Richtung „literate programming“. Es gibt Programmiersprachen, die das entweder direkt unterstützen, beispielsweise Haskell oder CoffeeScript, oder wo es üblich(er) ist, das mit externen Werkzeugen so zu machen. Python gehört da nicht wirklich dazu, aber es gibt mit PyLit-3 ein passendes Werkzeug um zwischen Python-Code mit Kommentaren und reST-Dokument mit Python-Code-Blöcken in beide Richtungen zu konvertieren.

`eval()` ist evil, und eigentlich geschummelt. Das tatsächlich wie bei einem handelsüblichen Taschenrechner, oder entsprechenden Programmen, die so einem nachempfunden sind, zu lösen, ist deutlich komplizierter. Etwas dazwischen wäre ein RPN-Taschenrechner: https://de.wikipedia.org/wiki/Umgekehrt ... e_Notation
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten