Kiste
EN

← Alle Tutorials

Taschenrechner in fünf Etappen

Bau dir in einer halben Stunde einen richtigen Taschenrechner — mit Verlauf, Tastatur und exakter Rechnung. Schon nach Etappe 2 rechnet er.

Für Einsteiger — Kiste muss installiert sein · ca. 30 Minuten

Am Ende dieses Tutorials hast du kein Übungsprogramm, sondern ein Werkzeug, das du wirklich benutzt: ein Taschenrechner mit Tastenfeld, Verlauf der letzten Rechnungen, Tastatur-Bedienung und einer Anzeige, die deutsche Zahlen schreibt.

Es ist derselbe Rechner, der auf der Seite Mit Kiste gebaut steht — kein abgespeckter Übungsklon.

Wir bauen ihn in fünf Etappen. Jede endet mit einem Programm, das startet und etwas tut. Schon nach Etappe 2 rechnet dein Rechner — den Rest baust du an etwas Fertiges an, nicht ins Blaue hinein.

Du brauchst nur Kiste. Falls es noch nicht installiert ist: hier herunterladen, ein Klick, fertig.

Etappe 1 · Ein Fenster mit einer Anzeige

Fang mit dem an, was der Benutzer zuerst sieht. Leg eine Datei taschenrechner.ki an.

Die Anzeige ist keine normale Textzeile, sondern eine Leinwand — eine Fläche, auf die das Programm selbst malt. Der Grund: gleich soll dort eine große Zahl rechtsbündig stehen und darüber klein die laufende Rechnung. Das kann kein Standard-Etikett, aber selber malen ist einfacher als es klingt.

// Etappe 1 — ein Fenster mit einer Anzeige.
nutze gui

fest A_BREITE = 336
fest A_HOEHE  = 96
fest FELD = "#0f1318"
fest HELL = "#f2f4f7"

nimm anzeige = "0"
nimm feld = gui.leinwand(A_BREITE, A_HOEHE)

funktion zeichne_anzeige() {
    gui.leeren(feld)
    gui.rechteck(feld, 0, 0, A_BREITE, A_HOEHE, FELD)
    nimm breite = länge(anzeige) * 19
    gui.text_an(feld, A_BREITE - 16 - breite, 44, anzeige, HELL, 34)
}

gui.thema("dunkel")
zeichne_anzeige()
nimm fenster = gui.fenster_öffne("Kiste — Taschenrechner", 344, 104)
gui.zeige(fenster, feld)
gui.starte()

Bauen und starten — Programme mit Fenster laufen immer über kiste build:

kiste build taschenrechner.ki
taschenrechner.exe

Du siehst ein dunkles Fenster mit einer 0 am rechten Rand.

Der Rechtsbündig-Trick: gui.text_an setzt Text an einer linken Position. Damit die Zahl rechts klebt, schätzen wir ihre Breite (länge * 19 bei Schriftgröße 34) und ziehen sie vom rechten Rand ab. Nicht auf den Pixel genau, aber für Ziffern völlig ausreichend.

Warum fest statt nimm? fest ist ein Wert, der sich nie ändert. Schreibst du später versehentlich A_BREITE = 400, sagt Kiste dir das, statt still etwas kaputtzumachen.

Etappe 2 · Es rechnet

Jetzt kommen Tasten dazu — und damit der Moment, in dem aus dem Fenster ein Rechner wird.

Ein Rechner muss sich vier Dinge merken:

nimm anzeige = "0"          // was gerade groß dasteht
nimm gespeichert = 0.0      // die Zahl von vor dem Rechenzeichen
nimm operator = ""          // welches Zeichen gedrückt wurde
nimm neue_zahl = wahr       // fängt die nächste Ziffer eine neue Zahl an?

neue_zahl ist der unscheinbarste, aber wichtigste Merker. Tippst du 12, sollen die Ziffern aneinanderhängen. Tippst du 12 + 3, muss die 3 eine neue Zahl beginnen und darf nicht 123 ergeben. Genau das schaltet dieser Merker um.

Die Ziffern

funktion tippe(z) {
    wenn neue_zahl {
        anzeige = z
        neue_zahl = falsch
    } sonst {
        wenn anzeige == "0" {
            anzeige = z
        } sonst {
            anzeige = anzeige + z
        }
    }
    zeichne_anzeige()
}

Das Rechnen

anwenden führt die vorgemerkte Rechnung aus. Beachte den Zahlentyp: wir wandeln die Anzeige mit als_dezimal um, nicht mit als_komma. Dazu gleich mehr.

funktion anwenden() {
    nimm jetzt = als_dezimal(anzeige)
    wenn operator == "+" {
        gespeichert = gespeichert + jetzt
    }
    wenn operator == "-" {
        gespeichert = gespeichert - jetzt
    }
    wenn operator == "×" {
        gespeichert = gespeichert * jetzt
    }
    wenn operator == "÷" {
        gespeichert = gespeichert / jetzt
    }
    anzeige = "{gespeichert}"
}

funktion rechne_weiter(op) {
    wenn operator == "" {
        gespeichert = als_dezimal(anzeige)
    } sonst {
        wenn nicht neue_zahl {
            anwenden()
        }
    }
    operator = op
    neue_zahl = wahr
    zeichne_anzeige()
}

funktion gleich() {
    wenn operator != "" {
        anwenden()
        operator = ""
        neue_zahl = wahr
        zeichne_anzeige()
    }
}

Der Kniff in rechne_weiter: Drückst du 2 + 3 + 4, soll das zweite + erst 2 + 3 ausrechnen und mit dem Ergebnis weitermachen. Genau das macht der sonst-Zweig — er kettet Rechnungen, ohne dass du jedes Mal = drücken musst.

Die Tasten anlegen und verdrahten

Jede Taste entsteht in zwei Schritten: erst der Knopf, dann was er tun soll.

nimm k7 = gui.knopf_neu("7")
gui.bei_klick(k7, funktion(k) { tippe("7") })

Das wiederholst du für die Ziffern 0 bis 9. Für die Rechenzeichen dasselbe Muster, nur mit rechne_weiter:

nimm k_mal = gui.knopf_neu("×")
gui.bei_klick(k_mal, funktion(k) { rechne_weiter("×") })
nimm k_gleich = gui.knopf_neu("=")
gui.bei_klick(k_gleich, funktion(k) { gleich() })

Die Tasten anordnen

Positionen ausrechnen musst du nicht. Du sagst gui.raster, wie viele Spalten du willst, und gibst die Tasten in Lesereihenfolge:

// Die letzte Reihe ist noch nicht voll — Komma, Löschen und Prozent
// kommen in Etappe 3 dazu.
nimm gitter = gui.raster(4, [
    k7, k8, k9, k_div,
    k4, k5, k6, k_mal,
    k1, k2, k3, k_minus,
    k0, k_gleich, k_plus
])

Zum Schluss die Anzeige über das Tastenfeld stapeln und das Fenster größer machen. Die letzten Zeilen aus Etappe 1 ersetzt du dabei — das Fenster zeigt jetzt nicht mehr die Leinwand allein, sondern beides übereinander:

nimm inhalt = gui.vertikal([feld, gitter])
nimm fenster = gui.fenster_öffne("Kiste — Taschenrechner", 344, 300)
gui.zeige(fenster, inhalt)
gui.starte()

Lässt du die alten Zeilen versehentlich stehen, öffnen sich beim Start zwei Fenster — dann weißt du, woran es liegt.

Neu bauen, starten, 7 × 8 = klicken: 56. Du hast einen funktionierenden Rechner.

Warum dezimal und nicht komma?

Tipp in fast jeder anderen Programmiersprache 0.1 + 0.2 ein. Das Ergebnis ist nicht 0,3, sondern 0,30000000000000004. Das ist kein Fehler dieser Sprachen, sondern die Natur von Gleitkommazahlen: Sie rechnen im Binärsystem und können ein Zehntel gar nicht exakt darstellen — so wie du ein Drittel nicht exakt als Dezimalzahl schreiben kannst.

Kiste hat für genau diesen Fall einen eigenen Zahlentyp. dezimal rechnet in Dezimalstellen und ist exakt. Deshalb steht in deinem Rechner nach 0,1 + 0,2 wirklich 0,3. Für Geld gilt dasselbe — und ein Rechner ist Geld sehr ähnlich.

Etappe 3 · Die fehlenden Tasten, und nichts stürzt mehr ab

Jetzt kommen C, ±, %, das Komma und die Rücktaste dazu. Alle nach demselben Muster wie bisher. Interessant sind zwei Stellen.

Vorher eine Zeile ganz oben: Ab hier arbeiten wir mit Text — nachsehen, ob schon ein Komma drinsteht, das letzte Zeichen abschneiden. Diese Funktionen stecken im Modul text, und Module holst du dir, wenn du sie brauchst. Ergänze also unter nutze gui:

nutze gui
nutze text

Vergisst du das, sagt Kiste dir beim Start, dass es text nicht kennt.

Erstens: Teilen durch null. Kiste bricht bei einer Teilung durch null mit einem Fehler ab — richtig so, denn stillschweigend „unendlich" zu liefern würde nur Programmierfehler verstecken. Ein Taschenrechner darf deswegen aber nicht abstürzen, wenn jemand 5 ÷ 0 tippt. Also fragen wir vorher und merken uns einen Fehlerzustand:

nimm fehler = falsch

// … in anwenden():
wenn operator == "÷" {
    wenn jetzt == 0 {
        fehler = wahr
    } sonst {
        gespeichert = gespeichert / jetzt
    }
}

Die Anzeige zeigt dann statt der Zahl einen Text:

nimm gross = anzeige
wenn fehler {
    gross = "Nicht definiert"
}

Und jede Taste, die eine neue Eingabe beginnt, räumt den Fehler weg:

funktion tippe(z) {
    wenn fehler {
        alles_löschen()
    }
    // … wie bisher
}

Zweitens: das Komma darf nur einmal vorkommen. 3,,14 wäre keine Zahl. Eine Zeile verhindert es:

funktion komma() {
    wenn neue_zahl {
        anzeige = "0."
        neue_zahl = falsch
    } sonst {
        wenn nicht text.enthält(anzeige, ".") {
            anzeige = anzeige + "."
        }
    }
    zeichne_anzeige()
}

Beachte: intern schreiben wir den Punkt — das ist die Schreibweise, die als_dezimal versteht. Zum Komma machen wir es erst beim Anzeigen, in der nächsten Etappe.

Die Rücktaste schneidet das letzte Zeichen ab, das Vorzeichen hängt ein Minus davor oder nimmt es weg, und Prozent teilt durch 100. Das Tastenfeld hat jetzt fünf volle Reihen:

nimm gitter = gui.raster(4, [
    k_c,     k_vz, k_proz,  k_div,
    k7,      k8,   k9,      k_mal,
    k4,      k5,   k6,      k_minus,
    k1,      k2,   k3,      k_plus,
    k_rueck, k0,   k_komma, k_gleich
])

Etappe 4 · Eigenes Aussehen und deutsche Zahlen

Der Rechner funktioniert — jetzt soll er auch nach etwas aussehen.

Farben. Jeder Knopf kann seine eigene Hintergrundfarbe bekommen. Weil sich das zwanzigmal wiederholt, wandert das Anlegen in eine kleine Funktion, die den fertigen Knopf zurückgibt:

fest ZIFFER   = "#2b323c"
fest FUNKTION = "#232932"
fest OPERATOR = "#4a3a18"
fest GLEICH   = "#a06c0f"

funktion taste(beschriftung, farbe) {
    nimm k = gui.knopf_neu(beschriftung)
    gui.setze_knopf_farbe(k, farbe)
    gib k
}

nimm k7 = taste("7", ZIFFER)
gui.bei_klick(k7, funktion(k) { tippe("7") })

Aus zwei Zeilen pro Taste werden so wieder zwei — nur sieht sie jetzt aus, wie du willst. Die Rechenzeichen bekommen einen warmen Ton, das = den kräftigsten: So sieht man auf einen Blick, was Ziffer ist und was rechnet.

Deutsche Zahlen. 8680 liest sich schlecht, 8 680 gut. Die Regel: nach jeder dritten Stelle von rechts ein Leerzeichen. Also gehen wir die Zahl Zeichen für Zeichen durch und zählen, wie viele noch folgen:

funktion gruppiert(t) {
    nimm aus = ""
    nimm n = länge(t)
    nimm i = 0
    solange i < n {
        aus = aus + text.zeichen_bei(t, i)
        nimm rest = n - i - 1
        wenn rest > 0 und rest % 3 == 0 {
            aus = aus + " "
        }
        i = i + 1
    }
    gib aus
}

Und der Punkt wird zum Komma. Dafür zerlegen wir die Zahl an ihrem Punkt: vorne gruppieren, hinten unverändert lassen.

funktion hübsch(t) {
    nimm stücke = text.zerlegt(t, ".")
    wenn länge(stücke) == 2 {
        gib gruppiert(stücke[0]) + "," + stücke[1]
    }
    gib gruppiert(t)
}

Die kleine Zeile. Über der großen Zahl steht ab jetzt, was gerade gerechnet wird — 1 240 × 7 =. Dafür merkt sich das Programm einen zweiten Text neben und malt ihn kleiner und grauer über die Zahl.

Etappe 5 · Verlauf und Tastatur

Zwei Dinge fehlen zum fertigen Programm.

Der Verlauf ist eine zweite Leinwand rechts neben dem Tastenfeld. Sie zeigt die letzten sechs Rechnungen. Statt einer wachsenden Liste nehmen wir sechs feste Plätze und schieben bei jeder neuen Rechnung alles um einen Platz nach hinten:

nimm v_rechnung = ["", "", "", "", "", ""]
nimm v_ergebnis = ["", "", "", "", "", ""]

funktion merke(r, e) {
    nimm a = v_rechnung
    nimm b = v_ergebnis
    nimm i = 5
    solange i > 0 {
        a[i] = a[i - 1]
        b[i] = b[i - 1]
        i = i - 1
    }
    a[0] = r
    b[0] = e
    zeichne_verlauf()
}

Nebeneinander gestellt wird beides mit gui.horizontal:

nimm links = gui.vertikal([feld, gitter])
nimm inhalt = gui.horizontal([links, verlauf])

Die Tastatur ist der letzte Schliff — und fast geschenkt, weil die ganze Bedienlogik schon in Funktionen steckt. Der Tasten-Handler ruft einfach dieselben Funktionen auf wie die Knöpfe:

gui.bei_taste(fenster, funktion(f) {
    nimm t = gui.letzte_taste(f)
    wenn text.enthält("0123456789", t) {
        tippe(t)
    }
    wenn t == "," oder t == "." {
        komma()
    }
    wenn t == "+" {
        rechne_weiter("+")
    }
    wenn t == "=" oder t == "eingabe" {
        gleich()
    }
    wenn t == "escape" {
        alles_löschen()
    }
    wenn t == "rücktaste" {
        rücktaste()
    }
})

Weil beide Wege dieselben Funktionen benutzen, können sie gar nicht auseinanderlaufen. Was du an einer Taste änderst, gilt sofort für Maus und Tastatur.

Fenstergröße: Setz sie genau auf den Inhalt — 524 × 304. Kiste verteilt überschüssigen Platz nicht, er bliebe sonst als toter Rand unten rechts stehen.

Fertig — und jetzt?

Das ist der ganze Rechner: eine einzige Datei, keine Zusatzdateien, kein Framework.

Den kompletten Quelltext kannst du hier herunterladen — vergleich ihn ruhig mit deinem, wenn irgendwo etwas klemmt.

Drei Ideen zum Weiterbauen, von leicht nach schwer:

  1. Eine Wurzeltaste. mathe.wurzel gibt es schon — du musst sie nur an einen Knopf hängen. Wo passt sie ins Raster?
  2. Ein Speicher (M+ / MR). Eine Variable mehr, zwei Tasten mehr — und dein Rechner kann sich eine Zahl merken.
  3. Der Verlauf zum Anklicken. Ein Klick auf eine alte Rechnung holt ihr Ergebnis zurück in die Anzeige. Dafür brauchst du gui.bei_maus_klick auf der Verlaufs-Leinwand und ein bisschen Rechnen, welche Zeile getroffen wurde.