Follower

Sonntag, 18. Februar 2018

Menü-Leisten

Oh Mann, das hat mich Nerven gekostet. Da wollte ich mal eben am oberen Fensterrand ein paar Menüpunkte einrichten, aber welche Vorlage ich auch nutzte - nix tat sich. Dachte ich. Bis ich plötzlich feststellte, dass alles funktioniert hatte. Der obere Fensterrand ist tatsächlich der OBERE.

Hier:

Na super. Hab ich einfach nicht gesehen, wollte schon aufgeben. Aber das ist ein Mac-Phänomen - die Pull-Down-Menüs erscheinen am oberen Rand des Bildschirms.

Aber wie geht das überhaupt?

Hier ein einfachstes Beispiel:

from tkinter import *

def ausgeben():
    print("Wurde geklickt")

# wenn man auf die Menüpunkte klickt, wird nur diese eine Funktion ausgeführt - es wird "Wurde geklickt" ausgegeben. Ist natürlich nicht sinnvoll, aber dient der Demonstration.
from tkinter import *

Fenster = Tk()

# Das erzeugt ein Fenster

Menuleiste = Menu(Fenster)

# Menu sorgt für eine Menüleiste beim Fenster

dateiMenu = Menu(Menuleiste)
Menuleiste.add_cascade(label="Datei", menu = dateiMenu)
dateiMenu.add_command(label = "Neues Projekt anlegen", command = ausgeben)
dateiMenu.add_command(label = "Projekt öffnen", command = ausgeben)
dateiMenu.add_command(label = "Speichern", command = ausgeben)
dateiMenu.add_separator()
dateiMenu.add_command(label = "Schließen", command = exit)

# zuerst definiere einen neuen Menüpunkt, wieder mit Menu, namens dateiMenu. Mit add_cascade wird das Drop-Down-Menü erstellt, das mit label seinen namen erhält. mit menu ???
Mit add_command werden die einezelnen Punkte dem Dropdown-Menü hinzugefügt, der command-Befehlt ruft die Funktion "ausgeben" auf - dann wird immer "Wurde geklickt" ausgegeben.
add_separator fügt eine Linie ein.


einstellungenMenu = Menu(Menuleiste)
Menuleiste.add_cascade(label= "Einstellungen", menu = einstellungenMenu)
einstellungenMenu.add_command(label="Aussehen", command = ausgeben)
einstellungenMenu.add_command(label="Grundeinstellungen", command = ausgeben)

Ein weiterer Menüpunkt wird der Menüleiste hinzugefügt: einstellungenMenu mit dem Titel "Einstellungen".

Fenster.config(menu=Menuleiste)

Config sorgt dafür, dass die Sache läuft.

Und wieso muss man kein Fenster.mainloop() ans Ende setzen???



Montag, 12. Februar 2018

Eingabefelder

Mit Entry erzeugt man ein Eingabefeld. Hatte ich schon. Dort konnte ich auch mit eval direkt Funktionen ausführen lassen. Praktisch.

Was aber, wenn mit der Eingabe irgendwas passieren soll? Kommt jetzt:

from tkinter import *

def button_action():
    entry_text = eingabe.get()
    if (entry_text == ""):
        Gruss.config(text="Gib zuerst einen Namen ein.")
    else:
        entry_text = "Welcome " + entry_text + "!"
        Gruss.config(text=entry_text)

# hier wird eine Funktion definiert, die zum Zuge kommt, wenn der Button gedrückt wird. Nämlich dass sie sich den eingegebenen Text aus dem später definierten Eingabe-Feld (eingabe) holen soll. Wenn da nix steht (==""), soll im Fenster "Gib zuerst einen Namen ein." erscheinen. Ansonsten aber soll "Welcome", der eingegebene Name und ein ! erscheinen.

fenster = Tk()
fenster.title("Ich warte auf eine Eingabe von dir.")

Text = Label(fenster, text="Gib deinen Namen ein: ")

Gruss = Label(fenster)

# Hier steht erst mal nix drin, bis der Button gedrückt wird.

eingabe = Entry(fenster, bd=5, width=40)

# Hier wird die Größe des Eingabefeldes bestimmt

welcom_button = Button(fenster, text="Klick me", command=button_action)
exit_button = Button(fenster, text="Beenden", command=fenster.quit)


# Hier werden zwei Button festgelegt. Beim ersten soll die oben definierte Funktion ablaufen, beim unteren das Programm beendet werden.

Text.grid(row = 0, column = 0)
eingabe.grid(row = 0, column = 1)
welcom_button.grid(row = 1, column = 0)
exit_button.grid(row = 1, column = 1)
Gruss.grid(row = 2, column = 0, columnspan = 2)


# Das kenne ich - mit der grid Anweisung werden die Elemente angeordnet.

mainloop()


Ich wäre natürlich nie darauf gekommen, es so zu machen. Aber inzwischen tröste ich mich damit, dass ich den Code zumindest in größeren Teilen nachvollziehen kann.

Sonntag, 11. Februar 2018

Layout mit tkinter

Ich muss einfach ein wenig ausprobieren. Ich lerne, dass es neben pack (da werden die einzelnen Elemente in die zuvor festgelegten Fenster gepackt) und grid (da werden die Elemente in Reihen und Spalten gepackt) auch noch place gibt. Dazu weiter unten mehr. Die drei dürfen aber nicht in einem Fenster gleichzeitig verwendet werden. Dann passiert nix, wie ich eben ausprobiert habe.

Hier mein Versuch mit grid:

from tkinter import *
import sys

def ende():
    sys.exit()

def Aktion():
    Anweisung.config(text="Ich wurde geändert!")

Fenster = Tk()
Anzeige = Label(Fenster, text="Guten Tag")
Text = Entry(Fenster)
Anweisung = Button(Fenster, text="Drück mich", command=Aktion)
Knopf2 = Button(Fenster, text="Ende", command=ende)
Anzeige.grid(row=0, columnspan=2)
Text.grid(row=1,columnspan=2)
Anweisung.grid(row=2, column=0)
Knopf2.grid(row=2, column=1)
Fenster.mainloop()

Ist kein sinnvolles Programm, aber sortiert die Elemente ordentlich, verändert den Text in dem Button "Anweisung", beendet das Programm auf dem Button "Knopf2" und hat sogar ein Textfeld, das sich über die Buttons zieht. Da kann man etwas eingeben, allerdings ohne dass etwas geschieht.

Und wenn man die Elemente noch etwas anders platzieren möchte, kann man den Abstand zwischen ihnen festlegen mit padx (auf der x-Achse, also waagerecht) oder pady (eben senkrecht).

from tkinter import *
import sys

def ende():
    sys.exit()

def Aktion():
    Anweisung.config(text="Ich wurde geändert!")

Fenster = Tk()
Anzeige = Label(Fenster, text="Guten Tag")
Text = Entry(Fenster)
Anweisung = Button(Fenster, text="Drück mich", command=Aktion)
Knopf2 = Button(Fenster, text="Ende", command=ende)
Anzeige.grid(row=0, columnspan=2)
Text.grid(row=1,columnspan=2, padx = 30, pady = 60)
Anweisung.grid(row=2, column=0)
Knopf2.grid(row=2, column=1)
Fenster.mainloop()



Noch mal zurück zu pack. Hier kann man auch die Platzierung ändern, da müssen nicht alle Elemente untereinander stehen wie hier:

from tkinter import *
import sys

def ende():
    sys.exit()

def Aktion():
    Anweisung.config(text="Ich wurde geändert!")

Fenster = Tk()
Anzeige = Label(Fenster, text="Guten Tag")
Text = Entry(Fenster)
Anweisung = Button(Fenster, text="Drück mich", command=Aktion)
Knopf2 = Button(Fenster, text="Ende", command=ende)
Anzeige.pack()
Text.pack()
Anweisung.pack()
Knopf2.pack()
Fenster.mainloop()






Mit side = LEFT in die Klammer hinter pack werden die Elemente alle linksbündig nebeneinander gesetzt. mit side = RIGHT rechtsbündig und mit side = BOTTOM nach unten. side = TOP ist die Voreinstellung (default), muss ich also nicht eigens eingeben.

Sieht dann so aus:

from tkinter import *
import sys

def ende():
    sys.exit()

def Aktion():
    Anweisung.config(text="Ich wurde geändert!")

Fenster = Tk()
Anzeige = Label(Fenster, text="Guten Tag")
Text = Entry(Fenster)
Anweisung = Button(Fenster, text="Drück mich", command=Aktion)
Knopf2 = Button(Fenster, text="Ende", command=ende)
Anzeige.pack()
Text.pack(side=BOTTOM)
Anweisung.pack(side=LEFT)
Knopf2.pack(side=LEFT)
Fenster.mainloop()


Die beiden Buttons sitzen an der linken Seite, das Textfeld unten (BOTTOM). Setze ich den zweiten Button auf side=RIGHT, rutscht er an die rechte Seite.




Und dann gibt es noch die Möglichkeit, die Elemente nach genauen Vorgaben anzuordnen.

from tkinter import *
import sys

def ende():
    sys.exit()

def Aktion():
    Anweisung.config(text="Ich wurde geändert!")

Fenster = Tk()
Anzeige = Label(Fenster, text="Guten Tag")
Text = Entry(Fenster)
Anweisung = Button(Fenster, text="Drück mich", command=Aktion)
Knopf2 = Button(Fenster, text="Ende", command=ende)
# jetzt wird die Fenstergröße festgelegt:

Fenster.geometry("400x200")
Anzeige.place(x = 0, y = 0, width=100, height=30)
Text.place(x = 120, y = 0, width=210, height=50)
Anweisung.place(x = 20, y = 70, width=150, height=40)
Knopf2.place(x = 190, y = 70, width=60, height=40)

# und dann die Lage (Horizontal und vertikal) und die Größe der Elemente. Das ist ziemliche Fummelarbeit.

Fenster.mainloop()



Ich verstehe schon, dass man diese Variante nur nutzen soll, wenn es gar nicht anders geht. Ist wirklich aufwändig.

Samstag, 10. Februar 2018

Buttons belegen

Wieder ein Beispiel, von dem ich etwas lernen sollte, das aber auch noch Fragen aufwirft.

from tkinter import *
import sys

# Ich importiere also alle tkinter Befehel und die sys Bibliothek

def ende():
    sys.exit()

#ich definiere ende, und dazu nutze ich exit aus der sys-Bibliothek, womit offenbar ein Programm beendet wird.

Fenster = Tk()
Anzeige = Label(Fenster, text="Guten Tag")
Knopf = Button(Fenster, text="Ende", command=ende)
Anzeige.pack()
Knopf.pack()
Fenster.mainloop()

Was Neues: mit command wird dem Button die Funktion übergeben, also ihm mitgeteilt, was er beim Anklicken tun soll. Komisch - in den bisherigen Beispielen taten die Buttons doch auch was, ohne die command option. Hier übrigens wird das Programm beendet, wenn man den Button anklickt. Soll ja auch so sein laut Funktion.
Das mit dem command möchte ich näher verstehen. Und finde ein neues Beispiel:

from tkinter import *

def Aktion():
    Anweisung.config(text="Ich wurde geändert!")

# oops, eine neue Funktion: config - Bestandteil von tkinter? offenbar

Fenster = Tk()
Fenster.title("Ich mache nun was.")

change = Button(Fenster, text="Ändern", command=Aktion)
Exit = Button(Fenster, text="Beenden", command=Fenster.quit)

# noch was Neues: quit - damit wird wohl auch ein Programm beendet. Zumindest in tkinter

Anweisung = Label(Fenster, text="Ich bin eine Anweisung:\n\
Klicke auf 'Ändern'.")



# das mit dem \n\ hatte ich schon erklärt - erzwingt einen Zeilenumbruch.

Info = Label(Fenster, text="Ich bin eine Info:\n\
Der Beenden-Button schließt das Programm.")

Anweisung.pack()
change.pack()
Info.pack()
Exit.pack()

Fenster.mainloop()

Tja, offenbar kann man mit command dem Button mitteilen, welche Funktion der ziehen soll

Grid - Einen Taschenrechner programmieren

Noch ein Beispiel für grafische Benutzeroberflächen (GUI) mit tkinter. Diesmal ein Taschenrechner. Wieder aus dem Video-Tutorial, und ich hab erst mal nix anderes gemacht, als es Zeile für Zeile abzutippen. Hatte ständig Fehler drin, unfassbar. Aber tatsächlich lernt man ja aus seinen Fehlern ;-)

Ich kopier mal das ganze Programm hier rein. Und dazwischen das, was ich als Erklärung verstanden haben, mit

from tkinter import *
from math import *

# erinnert Ihr euch? Wenn man alle Elemente aus einer Bibliothek nutzen möchte, kann man diese Form mit dem * wählen, dann muss man den Namen der Bibliothekt nicht jedes Mal vor ein Element schreiben

def calculate(event):
    gleichung = t.get()
    t.delete(0, END)
    try:
            t.insert(0, eval(gleichung))
    except:
            t.insert(0, "invalid syntax")

# Hier wird die Funktion calculate definiert. Zuerst wird mit t.get() das, was in das Textfeld t eingegeben wird, geholt, dann wird das Textfeld geleert (t.delete) und auf 0 gesetzt.
END habe ich noch nicht verstanden.
# Darunter etwas, das ich schon mal hatte: try und except - der Umgang mit Ausnahmen. Also wenn jemand eine Eingabe macht, die unsinnig ist.

top = Tk()

t = Entry(top)
t.grid(row=0,columnspan=3)


# Nun was Neues, nämlich grid. Damit können grafische Elemente in Reihen und Spalten angeordnet werden, dazu nutzt man row und column. Die erste Reihe erhält den Wert 0, weil man ja immer mit 0 anfängt zu zählen. Und columnspan sagt, dass das Textfeld sich über 3 Spalten zieht.

B1 = Button(top, text="1")
B1.grid(row=1,column=0)
B2 = Button(top, text="2")
B2.grid(row=1,column=1)
B3 = Button(top, text="3")
B3.grid(row=1,column=2)
B4 = Button(top, text="4")
B4.grid(row=2,column=0)
B5 = Button(top, text="5")
B5.grid(row=2,column=1)
B6 = Button(top, text="6")
B6.grid(row=2,column=2)
B7 = Button(top, text="7")
B7.grid(row=3,column=0)
B8 = Button(top, text="8")
B8.grid(row=3,column=1)
B9 = Button(top, text="9")
B9.grid(row=3,column=2)
B0 = Button(top, text="0")
B0.grid(row=4,column=1)
Bplus = Button(top, text="+")
Bplus.grid(row=0,column=3)
Bminus = Button(top, text="-")
Bminus.grid(row=1,column=3)
Bmal = Button(top, text="*")
Bmal.grid(row=2,column=3)
Bdurch = Button(top, text="/")
Bdurch.grid(row=3,column=3)
Berg = Button(top, text="=")
Berg.grid(row=4,column=3)
Bdel = Button(top, text="del")
Bdel.grid(row=4,column=2)


# Das ist alles nicht so schwer: Jeder Button wird in das top-Fenster eingebaut und mit einem Text versehen, dann mit .grid einer Reihe und einer Spalte zugeordnet.

B1.bind("<Button-1>", lambda x: t.insert(END,"1"))
B2.bind("<Button-1>", lambda x: t.insert(END,"2"))
B3.bind("<Button-1>", lambda x: t.insert(END,"3"))
B4.bind("<Button-1>", lambda x: t.insert(END,"4"))
B5.bind("<Button-1>", lambda x: t.insert(END,"5"))
B6.bind("<Button-1>", lambda x: t.insert(END,"6"))                       
B7.bind("<Button-1>", lambda x: t.insert(END,"7"))
B8.bind("<Button-1>", lambda x: t.insert(END,"8"))
B9.bind("<Button-1>", lambda x: t.insert(END,"9"))
B0.bind("<Button-1>", lambda x: t.insert(END,"0"))
Bplus.bind("<Button-1>", lambda x: t.insert(END,"+"))
Bminus.bind("<Button-1>", lambda x: t.insert(END,"-"))
Bmal.bind("<Button-1>", lambda x: t.insert(END,"*"))
Bdurch.bind("<Button-1>", lambda x: t.insert(END,"/"))

# Hier werden nun die ganzen Buttons mit Funktionen belegt - d.h. was passiert, wenn man sie mit der linken Maustaste ("<Button-1>") anklickt. Dafür gibt es die lambda Funktion - hab ich schon mal erklärt, auch wenn ich immer noch nicht sicher bin, ob ich das richtig verstanden habe. Und wieder das mit END, was mir auch noch nicht klar ist.

Berg.bind("<Button-1>", calculate)
Bdel.bind("<Button-1>", lambda x: t.delete(0, END))

# Mit dem Button Berg wird die Funktion aufgerufen, die oben definiert wurde, und mit Bdel wird die Eingabe gelöscht.

top.mainloop()

Das Ergebnis? Ein einfacher Taschenrechner. Cool. :-)


Textfeld im Fenster

Weiter geht es mit tkinter.

Diesmal ein kleines Programm, bei dem man etwas eingeben kann, hier eine Rechenaufgabe, die dann gelöst wird bei Knopfdruck.

Mal zuerst das ganze Programm:

import tkinter

def handleButton(event):
    Rahmen['text'] = eval(Textfeld.get())

Fenster = tkinter.Tk()

Fenster.title("Mein Programm")

Textfeld = tkinter.Entry(Fenster)

Rahmen = tkinter.Label(Fenster, text='Start')

Knopf = tkinter.Button(Fenster, text="Berechnen")

Textfeld.pack()

Rahmen.pack()

Knopf.pack()

Knopf.bind('<Button-1>', handleButton)

Fenster.mainloop()

Hier kommen jetzt ein paar neue Dinge hinzu. Die eval Funktion, die ja einen String erwartet, aber diesen dann ausführt je nach Funktion. Damit kann man nun rechnen. Entry erzeugt ein Textfeld. Und mit get() wird der Inhalt aus dem Textfeld gezogen. Mmmmh....




Donnerstag, 8. Februar 2018

Buttons betätigen

Damit auf der grafischen Oberfläche etwas passiert, wenn ich eine Taste bediene oder die Maus, muss man eine Funktion schreiben und dann z.B. dem Button mitteilen, wie die Funktion heißt, die aufgerufen werden soll, wenn der Button gedrückt wird. Klingt logisch.

Und schon komme ich im Buch nicht mehr mit. Da erfahre ich, wie man ein Programm beendet, das verstehe ich aber nicht.

Also zurück zum Video-Tutorial, da wird mir erklärt, wie ich dem Button sage, was er zu tun hat, wenn ich ihn anklicke.

Hier kommt es.

Zuerst sage ich ihm, was passieren soll, wenn ich ihn mit der linken Maustaste anklicke. Der Befehl heißt <button-1>, und das noch in einfachen Anführungszeichen: '<button-1'>. Eingeleitet wird er mit bind, also:
Knopf.bind('<button-1>', handleButton)

handleButton ist die Funktion, die ich noch definieren muss.

Das mache ich am Anfang.

import tkinter
i = 0
def handleButton(event):
    global i
    i = i + 1
    Rahmen['text'] = i

Fenster = tkinter.Tk()

Fenster.title("Mein Programm")

Rahmen = tkinter.Label(Fenster, text='Hallo - drück mich und ich zähle')

Knopf = tkinter.Button(Fenster, text="Drücken")

Rahmen.pack()

Knopf.pack()

Knopf.bind('<Button-1>', handleButton)

Fenster.mainloop()

Ich habe also zu Beginn die Funktion handleButton definiert (die in Klammern event stehen haben muss?) Ein Event kann eben ein Tastendruck oder ein Mausklick sein. Die Funktion, die ich programmiere, sorgt dafür, dass beim Mausklick einfach hochgezählt wird (i = i + 1). Funktioniert, aber fragt mich nicht, wieso....


Dienstag, 6. Februar 2018

Zurück zu Grafiken

Mich hat etwas der Mut verlassen, das mit den Klassen überfordert mich. Also kehre ich zurück zum meinem Lieblingstutorial. Ist ja schon seltsam. Ich fange an einer Ecke an, halte bis zu einem bestimmten Punkt durch, dann verlässt mich der Mut, ich fange was Neues an, bis ich nichts mehr begreife und dann das nächste - um dann wieder zurückzukehren zu einem anderen "Lehrer". Aber was soll's - so lange ich nicht ganz aufgebe...

Also: Ich hatte mir angesehen, wie man einen Button programmiert mit Hilfe des Moduls tkinter. Und der Button konnte sogar zählen, d.h. wenn man draufklickt, zählt er vorwärts.

Also lerne ich erst mal was über die eval-Funktion.

Das ist eine höchst seltsame Geschichte. Sie erwartet einen String, also so:

eval("3+5")  - führt aber dann die funktion in dem String aus.

print(eval("3+5")) führt dann zu 8.

Wohingegen bei

eval("print('Scherz')")

Scherz ausgegeben wird.

mmmh....

Das Video-Tutorial geht mir zu schnell. Ich recherchiere etwas und finde ein Tkinter-Tutorial. Vielleicht hilft mir das zum Verständnis.
Zum Beispiel, was ein Label ist. Aber die Erklärungen sind wie Fremdsprache. Es ist ein Widget, das man nur betrachten, aber nicht irgendwie benutzen (anklicken usw.) kann. Und was ist ein Widget? "eine Komponente eines grafischen Fenstersystems". Da kann ich ein wenig mit anfangen.

Also: Wenn man so etwas einfügt, dann macht ein Fenster auf, und ein Label dient dazu, Texte oder Bilder anzuzeigen. Aber beim Anwenden stelle ich fest, dass die Befehle nicht funktionieren - ich vermute, es basiert auf Python 2. Kann mir also doch nicht helfen. Ich bastle mir was zusammen und komme dazu:

import tkinter

Fenster = tkinter.Tk()

w = tkinter.Label(Fenster, text="Hello Tkinter!")

w.pack()

Fenster.mainloop()
Klappt, es geht ein Fenster auf - yeah. Mit dem Text: Hello Tkinter.

Fenster = tkinter.Tk() ist das Basisfenster, also sorgt dafür, dass überhaupt eins geöffnet wird. Danach können verschiedene Elemente erzeugt werden. In Klammern immer mit dem Parameter Fenster, dem "Eltern"-Fenster.
Z.B. kann ein Button eingebaut werden.

import tkinter

Fenster = tkinter.Tk()

Fenster.title("Mein Programm")

Rahmen = tkinter.Label(Fenster, text="Hello Tkinter! Alles ok?")

Knopf = tkinter.Button(Fenster, text="Drücken")

Rahmen.pack()

Knopf.pack()

Fenster.mainloop()

Mit pack wird das Element dann in das Fenster eingefügt, mit mainloop wird das Fenster aufgerufen. Alles klar? So sieht mein kleines Fenster aus. Cool.


Samstag, 3. Februar 2018

Klassen

Das wird ein weiteres Desaster in Sachen Verständnis, aber was soll's, ich muss ja dadurch. Mal eine erste Erkenntnis: Jetzt geht es um die eigentliche Objektorientierte Programmierung (OOP), das, was Python ja eigentlich ist. Was das eigentlich bedeutet, wird hier ganz anschaulich dargestellt. Versuche ich erst gar nicht, hier wiederzugeben.

Für mich erst mal zum Merken: Klassen sind die Baupläne, sie enthalten die Merkmale der Objekte und die Verfahren bzw. Methoden, wie mit den Merkmalen umgegangen wird.
Die konkreten Objekte werden in auch als Instanzen bezeichnet. Deren Eigenschaften werden auch als Attribute einer Instanz bezeichnet.

Ich erfahre, dass Listen eigentlich eine Klasse darstellen und lerne eine neue Anweisung kennen: pop

x = [3,6,9]

x[1] = 99
x.append(42)
last = x.pop()
print(x)
print(last)

Dient erst mal für mich nur der Wiederholung. Die eckigen Klammern enthalten den Index, d.h. hier also das zweite Element der Liste, das druch 99 ersetzt wird. x.append bedeutet, dass der Liste x ein Element hinzugefügt wird, die 42.
Und x.pop() gibt das letzte Element einer Liste wieder, bzw. das mit dem höchsten Index - auch die 42 und entfernt es von der Liste. Wozu das gut sein soll...
Ausgegeben wird dann:
3 99 9
42
Witzig - stelle grade fest, dass das Element 42 zwar mit pop entfernt wird, aber offenbar nur bei der Ausgabe.

Dienstag, 30. Januar 2018

Rekursive Funktionen

... Funktionen, die sich selbst aufrufen. Ich dachte, ich hatte es sofort verstanden, aber Pustekuchen. Also: Funktionen können sich auch selbst aufrufen, das nennt man rekursive Funktionen. So kam ich auf das Rückwärtszählen, das ja mit der Schleife gut funktioniert. Warum also mit einer sich selbst aufrufenden Funktion? Mmmmhh... mal schauen.

def countdown(n):
  if n > 0:
   print(n)
   countdown(n-1)

countdown(3)

Was hier passiert, ist komplizierter, als ich zuerst dachte. Ich versuch mal eine Erklärung.
Mit countdown(3) rufe ich die Funktion auf. Die stellt fest, dass 3>0, also übergibt sie die 3. Dann ruft sie sich selbst auf, diesmal mit der 3. Wieder größer als 0, also übergibt sie die 3. Das Gleiche passiert dann mit der 2 und der 1. Dann ruft sie sich wieder auf, diesmal mit der 0. Die ist nicht größer 0, also gibt sie nix weiter.
Jetzt kommt das, wo bei mir die Vorstellungskraft wieder versagt. Warum endet jetzt die Funktion, denn da steht ja immer noch countdown(n-1). Die Erklärung: Die Funktion hatte beim letzten Selbstaufruf noch die 1 als Variable n. Hinter countdown(n-1) kommt nichts mehr, also geht sie zurück zum Selbstaufruf, wo n=2 war, und dann zum Aufruf, der von außen kam, nämlich n=3. Und damit ist Schluss. Alles klar? Nicht wirklich.

Vielleicht wird es klarer, wenn man eine rekursive Funktion erstellt, die hochzählt.

def rekursivzaehlen(m):
    if m>0:
        rekursivzaehlen(m-1)
        print(m)

rekursivzaehlen(3)

Zuerst ist m=3, also größer 0. Dann kommt der Selbstaufruf, anschließend ist m=2. Wieder Selbstaufruf, m=1. Wieder Selbstaufruf, m=0 und damit nicht größer 1. Also kein Selbstaufruf, sondern die Funktion beendet den letzten Selbstaufruf, bei dem m=1 war und gibt die 1 aus, dann die 2 und dann die 3. Funktioniert. Ein bisschen klarer? Ich fürchte, das habe ich mega-schnell wieder vergessen.
Was ich aber glaube zu verstehen, ist der Nutzen dieser rekursiven Funktionen. In meinem tollen Buch steht, wenn man z.B. die Ordner auf seinem Rechner durchsuchen lassen will, dann kann man der Funktion mitteilen, dass sie sich selbst neu aufrufen soll, wenn sie auf einen Unterordner stößt, dann erst diese Dateien auslesen und dann wieder zum übergeordneten Ordner wechseln. Macht Sinn...

Weitere Funktionen

Seufz. Ich springe hin und her, schaue mir alles Mögliche an, jetzt mal wieder python4kids. Das ist zwar Python 2, aber so verfasst, dass ich noch mitkomme.

Also noch mal zu den Funktionen, da hab ich das mit den Klammern und den Parametern (ob mit oder ohne) noch nicht geschnallt.

Also:
def newLine():
  print()

def
threeLines():
  newLine()
  newLine()
  newLine()

print "First Line."
threeLines()
print "Second Line."


Damit hab ich eine Funktion geschrieben, die nichts anderes macht, als den Auftrag zu übergeben, eine Leerzeile auszugeben.
Und dann gleich noch eine Funktion, die die erste benutzt, um drei Leerzeilen zu erzeugen.

Das ist erstmal gut. Also wenn ich leere Klammern setze, dann wird halt keine Parameter erwartet, sondern die Funktion in der Funktion übergeben. Kann man das so sagen? Hier also die print-Funktion.

Ich hab noch eine kleine Funktion fabriziert und bin ganz angetan, dass sie funktioniert.

# Rückwärtszählen
n = 10

def rueckwaerts(n):
      while n>=1:
        n = n-1
        print(n, end=" ")
   
rueckwaerts(10)

Zur Erinnerung: end=" " bedeutet nur, dass die am Ende des Ausdrucks statt eines Zeilenumbruchs ein Leerzeichen kommt, also stehen die Zahlen hintereinander statt untereinander.

Sonntag, 28. Januar 2018

Mühsam

Ein paar Tage Pause, und ich merke, dass ich mich nur mit Widerstand aufraffen kann. Ich schnappe mir wieder das Buch, da geht es jetzt um "Klassen". Den Einstieg verstehe ich noch, dann stolpere ich über den "constructor". Ich stelle fest, dass mir die Geduld fehlt, mich da durch zu beißen. Aber was nun? Mein YouTube-Tutorial beschäftigt sich mit grafischen Oberflächen, da habe ich aber nicht das Gefühl, dass ich dafür schon fit bin.
Also gehe ich mal durch meine Lesezeichen und lande bei einem Tutorial, dass ich schon häufiger konsultiert habe.

Aber auch da weiß ich nicht, wo ich wieder einsteigen soll. Zuletzt war ich ja bei Funktionen, also suche ich hier ein wenig herum. Ob ich so weiterkomme?

Es geht hier erst mal weiter mit range. Ich erinnere mich, da war schon mal was. In Zusammenhang mit der for-Schleife.

Nun lerne ich, dass man bei Strings in einer Liste den range-Befehl mit dem len-Befehl kombinieren kann.

a = ['Mary', 'hatte', 'ein', 'kleines', 'Lamm']

for i in range(len(a)):
    print(i, a[i])

Bewirkt, dass erst die Länge der Liste a ermittelt wird, und wenn da keine anderen Parameter auftauchen, wird halt die von 0 bis 4 gezählt.
Mit print(i, a[i]) wird immer erst die jeweilige Zahl und dann das jeweilige Element ausgegeben.


Bilde mir ein, das verstanden zu haben.


Freitag, 19. Januar 2018

Ein Spiel programmieren

Hier kommt ein Programm, mit dem man Schiffe versenken spielen kann, in dem Buch "Bermuda-Projekt" genannt.

import random

TREFFER = 9

def peileSchiff(x, y, sx, sy):
    if x==sx and y==sy:
        return TREFFER
    if x==sx:
        return 1
    if y==sy:
        return 1
    if x-sx ==y-sy:
        return 1
    if x-sx == (y-sy)*-1:
        return 1
    return 0

schiffX = random.randint(0, 8)
schiffY = random.randint(0, 6)

print(schiffX, schiffY)

spielende = False

while not (spielende):
    x = eval(input("X-Koordinate eingeben(0-8):"))
    y = eval(input("Y-Koordinate eingeben(0-6):"))
    if x>-1 and y>-1:
        fund = peileSchiff(x, y, schiffX, schiffY)
        if fund == TREFFER:
            print("Schiff gefunden")
            print("gewonnen")
            spielende = True
        else:
            print("Peilungen: ", fund)
    else:
        spielende = True

Klar, dass ich hier über die globale Variable am Anfang gestolpert bin, aber das kann ich ja inzwischen erklären. Sie wird nur ausgelesen in der Zeile return TREFFER. Warum TREFFER auf 9 gesetzt wird, verstehe ich nicht. Muss wohl nur irgendwie definiert werden.
Hier werden die Koordinaten für das Schiff über randint zwischen 0 und 8 bzw zwischen 0 und 6 zufällig eingesetzt und erst mal ausgegeben (um das Programm zu verstehen)

In der Funktion wird definiert, dass bei einem Volltreffer TREFFER übergeben wird (und dann in dem Programm die strings "Schiff gefunden" und "gewonnen". Wenn ein eingegebener x-Wert oder ein y-Wert stimmt, wird 1 übergeben. Ebenso, wenn der eingegebene Wert in der Diagonalen des Zufallswertes liegt.
Trifft all das nicht zu, wird 0 übergeben.
Woran ich wirklich nach wie vor knabbere, ist das mit der Bedingungn für die Schleife. Dazu wird spielende = False gesetzt und dann als Bedingung formuliert:
while not(spielende):
Muss ich das so verstehen: So lange diese Bedingung zutrifft (not False ist ja True), wird die Schleife ausgeführt. Irgendwann setzt man dann spielende = True, die Bedingung wird wieder geprüft und nun ist sie ja falsch (not True), also ist Schluss mit der Schleife.

Wer jetzt glaubt, ich hätte an dem Programm irgendetwas selbst erstellt, der sei getröstet. Alles abgetippt, ich gebe mich damit zufrieden, fertige Programme wenigstens zu verstehen. 


Globale Variable

Mannomann, verwirrt mich das. Also:
Wenn ich eine Funktion definiere, dann belege ich innerhalb der Funktion Variablen, und deren Definition gilt nur innerhalb der Funktion, von außen kann ich auf sie nicht zugreifen. (Ich hoffe, das stimmt jetzt so).

So wie hier:

def summiere(ende):
    summe = 0
    for i in range(1, ende+1, 1):
        summe += i
    return(summe)
   
print(summiere(10))

Da kann ich außerhalb der Definition nix mit summe anfangen.

Ich kann auch eine Variable belegen, bevor ich die Funktion definiere.

summe = 10

def summiere(x):
    summe = summe + x
    return summe

print(summiere(6))

Funktioniert aber nicht, und zwar die Zeile summe = summe + x. Hier denkt Python, da wird einer "lokalen Variable" namens summe etwas zugewiesen, die aber vorher nicht definiert wurde.
Will ich trotzdem, dass die Funktion die vorher definierte Summe verwendet, dann muss ich ihr sagen, dass sie auf eine globale Summe zugreifen soll:

summe = 10

def summiere(x):
    global summe
    summe = summe + x
    return summe

print(summiere(6))

Komischerweise kann eine Funktion aber trotzdem auf eine Variable zugreifen, solange sie nur ausgelesen wird:

summe = 10

def summiere(x):
    zahl = summe + x
    return zahl

print(summiere(3))

Tja, muss man so hinnehmen. Vor allem aber: Es gilt als nicht gut, solche globalen Variabeln zu verwenden - also lieber immer lokal definieren, d.h. innerhalb der Funktion. Warum es dann trotzdem geht und wann das vorteilhaft ist, verstehe ich natürlich nicht.

lambda

uff, noch mehr Verwirrung. Man kann sich den Funktionsnamen sparen, indem man die Funktion direkt in dem Programm definiert. Das geht mit dem Ausdruck lambda.

Ich versuch's mal. Ich definiere eine Funktion auf die klassische Weise und gebe an dieser Stelle zu, dass mir das schon wieder schwer gefallen ist. Aber immerhin: Ohne fremde Hilfe und Nachlesen geschafft.

def versuch(x):
    zahl = x + 3
    return zahl

print(versuch(25))

Der Parameter x wird beim Aufruf durch die 25 ersetzt und zur 3 addiert. Ergebnis ist 28


Nun kann ich das wohl auch so machen:

print((lambda x: x + 3)(25))

Mit lambda x: erspare ich mir die Definition eines Funktionsnamens, klappt genauso. Das nennt man eine anonyme Funktion. Ich habe nachgelesen - sie ist bei Programmierern umstritten, und natürlich habe ich noch keine Ahnung, wo das sinnvoll anzuwenden ist.

Noch mal Parameter belegen

Wenn man eine Funktion mit wenigen Parametern definiert hat, kann man sich merken, in welcher Reihenfolge sie belegt werden müssen und einfach Werte zuweisen.
Aber angeblich wird man später (ob ich das auch mal machen werde?) viele Parameter in einer Funktion haben. Dann kann man diese als Aufrufer der Funktionen die Parameter auch mit Namen ansprechen. Hä?

Ist aber nicht so kompliziert - wenn man es einmal verstanden hat :-)
Habe ich selbst programmiert - und ewig gebraucht.

def datum(tag, monat, jahr):
    print(jahr, "-", monat, "-", tag)

datum(jahr=2018, monat=1, tag=25)

Also: Ich definiere eine Funktion namens Datum, die eine Datumseingabe in der Form Jahr - Monat - Tag ausgeben soll.

Die Parameter habe ich tag, monat, jahr genannt. Gedruckt werden sollten die Zahlen in der Reihenfolge Jahr - Monat - Tag - mit dem String - dazwischen (sonst würde ja subtrahiert.
Jetzt kommt es: Gebe ich drei Zahlen ein und vertue mich in der Reihenfolge, werde ich ein fehlerhaftes Ergebnis bekommen. Nutze ich aber bei der Eingabe die Namen der Parameter, dann versteht mein Programm das. Funktioniert.




Donnerstag, 18. Januar 2018

Parameter belegen

Habe ein wenig Pause gemacht und steige wieder ein. Gleich mit einem Frust.

Was bedeutet denn auf einmal += ??

Also hier zum Beispiel:

a = [27,13,17,59,40]

for element in a:
    if element < 16:
        element = Element + 100
    print(element)

Die Liste besteht aus 5 Elementen, ich sage in der Schleife, dass für jedes Element geprüft werden soll, ob es kleiner als 16 ist. Wenn das der Fall ist, wird 100 addiert. Wenn ich dann die Element ausgeben lassen, steht da 27 130 17 59 40

Geht aber offenbar so auch:

a = [27,13,17,59,40]

for element in a:
    if element < 16:
        element += 100
    print(element)

Bedeutet also: Mit dem += kann ich Werte erhöhen - oder?

Aber ich wollte ja mit Funktionen weitermachen und gleich mächtig fluchen - über mich, weil ich ständig Fehler mache!!!

def summiereBis10():
    summe = 0
    for i in range(1,11,1):
        summe = summe + i
    return summe

zahl = summiereBis10()
print(zahl)

Hier wird also eine Funktion mit Namen summiereBis10 definiert. Dann alle Zahlen zwischen 1 und 11 (also bis 10) aufaddiert. Wenn die Funktion aufgerufen wird (mit zahl = ...) dann führt sie die definierte Aktion aus. Verstanden!!!

Das mit der leeren Klammer hinter dem Namen der Funktion hab ich noch nicht kapiert. Da soll eigentlich ein Parameter rein, aber wenn ich keinen vorgebe, muss ich trotzdem die leere Klammer setzen.
Hat was mit dem Speicherort der Funktion zu tun, wie man hier sieht:

def summiereBis10():
    summe = 0
    for i in range(1,11,1):
        summe = summe + i
    return summe

cool = summiereBis10

zahl = cool()
print(zahl)

Erst definiere ich die Funktion summiereBis10() und gebe den Wert zurück.

Dann weise ich ihr eine Variable namens cool zu und kann diese dann wie die Funktion nutzen. Wozu das nun wieder gut sein soll...

Nun nochmal zu den Parametern - hier ein Anwendungsfall. Ich gebe der Funktion einen Parameter (hatte ich doch schon mal, oder?) - so zum Beispiel:

def summiere(grenze):
    summe = 0
    for i in range(1, grenze+1, 1):
        summe += i
    return summe

zahl = summiere(10)
print(zahl)

Dem Parameter grenze kann nun jeder beliebige Wert zugeordnet werden und damit kann die Funktion summiere flexibel eingesetzt werden.

Auch hierüber bin ich erst mal schwer ins Grübeln gekommen:

def summiere(grenze=10):
    summe = 0
    for i in range(1, grenze+1, 1):
        summe += i
    return summe

zahl = summiere()
print(zahl)

Bedeutet, dass der Parameter grenze mit 10 vorbelegt wird. Gibt man keine Zahl bei zahl = summiere() ein, dann nimmt das Programm die 10, mit der der Parameter vorbelegt wurde.

 Setze ich ein Zahl ein, wird mit dieser gerechnet.



Parameter vorbelegen, damit geht es jetzt weiter. Man kann auch mehrere vorbelegen, aber sobald man das mit einem macht, muss der Rest auch vorbelegt werden. Mmmmhhhh....
Hier das Beispiel:

def zaehle(ende, schritt=1):
    for i in range(1, ende+schritt, schritt):
        print(i)

zaehle(15, 2)

Also: Hier sind zwei Parameter in der Funktion: ende und schritt, letzterer wird mit 1 vorbelegt.
Die Funktion soll nun für alle Zahlen von 1 bis eine beliebig gewählte, der noch mal 1 hinzuaddiert wird (sonst ist zu früh Schluss, weil ja der Index bei 0 anfängt) hochzählen, und man kann noch mal festlegen, in welchen Schritten hochgezählt wird. In dem Beispiel wird von 1 bis 15 gezählt, aber in 2er Schritten:
1
3
5
7
9
11
13
15

Gibt man keine zweite Zahl an, wird immer in 1er Schritten gezählt.

Mittwoch, 17. Januar 2018

Noch mal True - False

Mensch, was tue ich mich schwer mit dem Booleschen Datentyp. Ich denke, ich brauche einfach mehr Anwendungsfälle, un zu verstehen, wie man damit umgeht. Zum Beispiel hier:

for x in [True, False]:
    for y in [True, False]:
        print(x, y, x and y)

x = False

Ich wäre nie darauf gekommen, warum hier
True True True
True False False
False True False
False False False

ausgegeben wird. Mein Bruder, den ich gerade besuche, hat es mir erklärt.

In der ersten Schleife wird für die Variable x das erste Element abgearbeitet, dann springt das Programm in die zweite Zeile, arbeitet dann für y hier das erste Element ab und gibt dann die Variable x (aus der ersten Zeile), die ja wahr ist, dann für die Variable y, die ja auch wahr ist, und weil beide wahr sind, noch mal True aus.

Da da die zweite for-Schleife nocht nicht beendet ist, (da wäre ich nie drauf gekommen) wird jetzt y auf False gesetzt und beim Ausgeben ist x immer noch True, y ist jetzt False und weil ja True and False zu False wird, haben wir jetzt True False False,
Die zweite Schleife ist nun fertig, es genn springt das Programm wieder in die erste Zeile, setzt x jetzt auf False und weiter geht es...
Oh Mann...


Samstag, 13. Januar 2018

Noch mal for-Schleife

Im Buch geht es weiter mit der for-Schleife, mit der man Listen abarbeiten kann.

Vorher habe ich das ja so gelernt: Zuerst definiert man eine Liste:

a = [1, 3, 5, 9, 13, 45]

Dann lässt man sie mit einer Funktion abarbeiten, z.B. mit der print-Funktion:

for i in a:
    print(i)

Dann werden die einzelnen Elemente der Liste ausgeben, untereinander, einer nach dem anderen.

Geht auch mit strings:

todoes = ["putzen", "spülen", "kochen", "waschen"]
for m in todoes:
    print(todoes)

Jetzt erfahre ich, dass die eckigen Klammern gar nicht nötig sind. Habe ich da was falsch verstanden?


Und ich lerne, dass es ja einfacher geht - indem man for und in verbindet:

for a in 1, 2, 3, 4, 5
    print(a)

Klappt auch mit den eckigen Klammern:

for b in [11, 17, 365, -6]:
    print(b)

mmmmmhhhh.... Ich habe bisher immer gelesen, dass die eckigen Klammern für Listen wichtig sind, werde das also beibehalten.

Ach ja, noch was gelernt: Statt jedes Element in der Liste aufzuführen, kann man bei Zahlen auch den Bereicht und den Abstand zwischen den Zahlen vorgeben, und zwar mit range:

for i in range(3, 12, 1):
    print(i)

Dann startet die Liste mit der 3 und endet bei 11, wobei der Abstand zwischen den Werten 1 beträgt.

for i in range(3, 12, 2):
    print(i)

Hier geht es in 2er-Schritten voran: 3 5 7 9 11

Weiter geht es

Ich habe ein wenig pausiert, aber mir in der Zwischenzeit ein Buch gekauft: Python - Der Sprachkurs für Einsteiger und Individualisten von Arnold Willemer. Ich glaube, ich lerne besser mit einem Buch, das steckt so drin. Das werde ich in der nächsten Zeit von vorn bis hinten durchackern - zumindest nehme ich mir das vor.

Vorher aber noch ein paar kleinere Details, die ich nicht vergessen möchte, z.B. die pass-Anweisung.

Die kann man in eine Zeile schreiben, die man später programmieren will, dann läuft das Programm weiter.

if 3 > x
   pass

Oder die try Anweisung. Die allerdings hat mich wieder viele Versuche gekostet :-(

Es geht darum, dass man verhindern möchte, dass derjenige, der zu einer Eingabe aufgefordert wird, durch den falschen Zeichentyp (er soll z.B. eine Zahl eingeben, aber er nimmt einen Buchstaben), keine Fehlermeldung erhält, sondern auf seinen Fehler hingewiesen wird.

Wie in diesem Fall:


Der ganze Körper der Schleife wird hier also "eingerahmt" in try: und except:
Funktioniert, wie man sieht.

Das habe ich bisher an Neuigkeiten aus dem Buch gezogen, bin inzwischen auf Seite 59. Es hat sich herausgestellt, dass meine Vorkenntnisse dank der verzweifelten Versuche mit Tutorials im Internet durchaus nützlich sind. Vieles verstehe ich auf Anhieb, leider aber auch nicht alles. Und bald kommen ganz neue Sachen, da wird es vermutlich wieder umgekehrt sein: Ich werde recherchieren müssen. Aber was soll's - noch macht es Spaß.

Dienstag, 9. Januar 2018

Noch mehr zu Funktionen

Nachdem ich also meine erste Funktion selbstständig definiert und sogar angewendet habe, bin ich schon wieder hängengeblieben - an einem ganz einfachen Beispiel:

def printTwice(bak):
  print(bak, bak)

printTwice("Wille"*3)

Ich habe einfach zuerst nicht verstanden, warum ich in dem Programm nicht noch mal einen print-Befehl geben muss, aber dann dämmerte es mir:
Die Funktion printTwice ist ja eine print-Funktion, habe ich ja selbst definiert. Also gibt sie zweimal den String Wille mal 3 aus:
WilleWilleWille WilleWilleWille.

Und ich kann natürlich auch eine Variable als Argument einsetzen (Argument ist das, was in der Funktion in der Klammer steht):


Und wie sieht es hiermit aus?

def printTwice(bak):
  print(bak, bak)

def printHund(arg1, arg2):
  zak = arg1 + arg2
  printTwice(zak)

top = "Das ist toll, "
witz = "mein Lieber!"
printHund(top, witz)

Zuerst habe ich also printTwice definiert - wenn ich die Funktion anwende, wird der Ausdruck in der Klammer zweimal ausgegeben.
Dann habe ich printHund definiert, der zwei Argumente enthält. Dann habe ich mit der Variablen zak festgelegt, dass die beiden Argumente addiert bzw. verkettet werden.
Und dann habe ich bei der Funktion printHund die Funktion, die ich vorher definiert habe, verwendet, nämlich printTwice. Uff....
Funktioniert aber, "Das ist toll, " und "Mein Lieber!" wird verkettet und zweimal ausgegeben.



Verwende ich ganze Zahlen, klappt das auch.


Übrigens: Mische ich eine ganze Zahl (integer) und eine Kommazahl (float), funktioniert es trotzdem. Offenbar wandelt Python den Datentyp int dann automatisch in ein float um.

Sonntag, 7. Januar 2018

Motivationsloch

Ich stecke in einem Motivationsloch. Codecadamy überfordert mich - nicht zu fassen. Ein Problem: Es ist eine ältere Python-Version, nicht Python 3... Ob es außerdem das Englische ist oder ich einfach auch so oft nicht verstehe, was man da von mir will - keine Ahnung. Auf jeden Fall bin ich erst mal ausgestiegen und frage mich jetzt, wie es weitergeht..

Vielleicht mit Python for Kids? Scheint mir fast angemessen...

Was ich hier schon mal lerne: Variablen fangen immer mit einem Buchstaben an, dürfen aber auch Zahlen enthalten. Und Großbuchstaben sind verpönt.

Es gibt Schlüsselwörter, die "gesetzt" sind, diese hier habe ich bis jetzt gefunden:

and  as assert  break  class continue  def  del
elif  else except  False  finally for  from
global   if   import  in  is
lambda   None  nonlocal   not
or  pass  raise  return   True  try
while  with   yield       

Der Operator * funktioniert (wie auch das +) nicht nur mit Zahlen, sondern auch mit Strings. Das + reiht die Strings aneinander (hatten wir schon), das * wiederholt den String in der angegebenen Anzahl: hit*3  wird zu hithithit

Und ich lerne eine neue Funktion kennen: id. Wenn ich also print(id(5)) eingebe, dann wird mir der Wert oder die Variable als ganze mehrstellige ganze Zahl ausgegeben, die eindeutig dem Wert zugeordnet ist.


Schon wieder weiß ich nicht, wozu das gut sein soll....

Aber noch mal zu Funktionen: Es gibt bei Python eingebaute Funktionen (die man also nicht selbst definieren muss, dazu zählen neben print und type eben auch int, str, float, round...
Andere müssen wir aus Modulen bzw. Bibliotheken importieren durch from ... import:  (aber das hatten wir auch schon).

Und das muss ich mir erst mal klarmachen: Funktionen sind alles, was etwas bewirkt. So wie print das Ausgeben bewirkt, das type den Datentyp ausgibt, round einen Zahl abkürzt und str aus einer Zahl einen String erzeugt.
"Bewirkt" ist sicher sehr laienhaft ausgedrückt, aber so kann ich mir das besser merken.

Und natürlich kann man Funktionen selbst definieren. Das war mir bisher ein einziges Rätsel, aber - tärätärä - grade kam ein kleiner Durchbruch - ich habe mein erstes kleines Programm ohne jede Anleitung erstellt. Nach vielem Herumprobieren natürlich.

Hier ist es: 

def prozent(zahl1, zahl2):
   ergebnis = zahl1*100/zahl2
   return ergebnis

print("Wieviel % ist a von b?")
a = int(input("Geben Sie einen Wert für a ein: "))
b = int(input("Geben Sie einen Wert von b ein: "))

Resultat = prozent(a, b)

print(str(Resultat) + " %")

Vor allem die letzte Zeile hat mich viel Zeit gekostet - bis ich herausfand, dass ich den Integer "Resultat" erst in einen string umwandeln muss, um ihn mit einem anderen string zusammen ausgeben zu könne.
Für mich ist das ein Meilenstein. Ich hab kapiert, wie man eine Funktion definiert und übergibt!!!!


Und weil ich damit ein absolutes Hochgefühl habe, hier noch der Versuch, die Kommastellen auf zwei zu begrenzen:

return round(ergebnis, 2)

Ach ja: Wenn ich eine Leerzeile erzeugen möchte, muss ich nur print() einfügen.


Freitag, 5. Januar 2018

codecademy

Ich unterbreche die Bearbeitung des Video-Tutorials, weil ich auf codecademy einen Python-Einsteiger-Kurs gefunden habe und diesen nutze, um noch mal von vorn anzufangen.

Ich werde an dieser Stelle nach und nach die Dinge eintragen, die ich zu dem bereits bekannten Wissen hinzu erwerbe.

Da ist ziemlich am Anfang schon diese Erkenntnis:
Ein Kommentar beginnt ja mit einer #
Ist der Kommentar aber länger als eine Zeile, dann muss ich die # an jeden Zeilenanfang setzen.
Oder aber ich setze den Kommentag in drei Anführungszeichen: """das ist ein Kommentar"""

Nächste neue Erkenntnis: 2 hoch 3 wird mit 2**3 ausgeführt.

Und noch eine artithmetische Funktion 6%4 nennt man modulo und dividiert 6 durch 4, gibt aber nur die Zahl aus, und zwar den Rest, der übrig bleibt, in diesem Fall die 2.
Wozu braucht man das? Man kann damit ermitteln, ob eine Zahl durch eine andere teilbar ist (wenn eine 0 ausgegeben wird wie bei 12%3. Oder man kann die letzte Ziffer einer Zahl ermitteln wie bei 1337%10 oder die beiden letzten wie bei 31845%100. Wann man das braucht? Werde ich herausfinden...

Und wieder was Neues gelernt. len() kenne ich schon, das gibt die Länge eines Strings oder einer Liste, sprich: Anzahl der Zeichen aus.
Bei einem String kann man nun auch aus Großbuchstaben kleine und aus kleinen Buchstaben große machen.

Dazu definiere ich erst einen String, z.B. wort = Bullenstall, dann kommt

print((wort.lower()) - das führt zu bullenstall
oder eben
print((wort.upper()) - das führt zu BULLENSTALL

Was ich noch immer nicht verstehe: Was machen die leeren Klammern hinter dem lower bzw. upper?
Wozu überhaupt die lower und upper Funktion? Das habe ich später verstanden. Man kann ja manchmal Eingaben fordern und will sichergehen, dass der User keine Fehlermeldung kriegt, weil er einen Begriff am Anfang groß schreibt - also verwandelt man jeden String komplett in Kleinbuchstaben und prüft dann auf True.

Und dann gibt es noch str()  - wenn ich z.B. eine Zahl als String ausgeben will, dann kann man das mit str()
z.B.

wort = "Spieler "
print(wort + str(27))

Wobei man ja auch statt str(27) einfach die Zahl in Anführungszeichen setzen könnte, oder?




Also: len(xyz) und str(123), aber wort.upper() und wort.lower()
Warum? Keine Ahnung.

Dann habe ich noch den Platzhalter % und %s gefunden.

Wort1 = "Apfel"
Wort2 = "baum"

print ("Lasst uns einen %s%s pflanzen." % (Wort1, Wort2))

Also: Wir definieren einen oder mehrere Strings, nehmen den Platzhalter %s (kleines s wohlgemerkt) und eben dann nach einem % in Klammern die Variablen ein, die an Stelle der Platzhalter eingefügt werden sollen, getrennt durch ein Komma.


Und noch eine neue Erfahrung: Arbeiten mit der Uhrzeit. Diese importiert man aus einer Bibliothek, das Modul heißt datetime

from datetime import datetime
now = datetime.now()
print (now)

datetime.now() wirft die aktuelle Zeit aus.

Damit kann man jetzt einiges anstellen. Zum Beispiel kann ich mir das Jahr, den Monat oder den Tag getrennt ausgeben lassen. Oder das Datum in einer anderen Form darstellen.


from datetime import datetime

jetzt = datetime.now()
print (jetzt)
print (jetzt.year)
print (jetzt.month)
print (jetzt.day)

Und das jetzt noch mit den Platzhaltern kombinieren:


Cool, oder? Den Backslash habe ich verwendet, weil die Zeile zu lang wurde, also habe ich den Umbrauch sozusagen ungültig gemacht. Darauf bin ich schon stolz ;-)

Mittwoch, 3. Januar 2018

Einen Button programmieren

Auch wenn ich kaum etwas verstanden habe beim Programmieren der ersten grafischen Oberfläche, ich mache mal tapfer weiter mit meinem Video-Tutorial. Zuerst einmal bleibt alles gleich, ich kopiere das Programm vom letzten Versuch.

import tkinter
top = tkinter.Tk()
w = tkinter.Label(top, text='Das ist kompliziert')
w.pack()
top.mainloop()

Nun definiere ich also einen Button und geben ihm den Namen b:

import tkinter
top = tkinter.Tk()
w = tkinter.Label(top, text='Das ist kompliziert')
w.pack()
b = tkinter.Button(top, text='Drück hier')
b.pack()
top.mainloop()

Die Funktion Button gibt es also bei tkinter, und offenbar muss man alle Elemente irgendwohin packen z.b. w.pack() oder b.pack(). Und die Funktion Label erzeugt offenbar ein Fenster, dem man mit der Funktion text einen Text zuordnen kann.
Auf jeden Fall klappt das mit dem Button, und auf dem steht tatsächlich "Drück hier".
Aber wenn man draufklickt, passiert nix. Klar, habe ja auch nichts programmiert, was dann geschehen soll. Weiter im Tutorial anschauen.

Ich lerne, dass ich einen Event definieren muss, also ein Ereignis, das geschieht, wenn man den Button drückt.

Das macht man mit der Funktion bind:
b.bind(<'Button-1'>, handleButton)

Schluck, was ist das denn jetzt. Ich erfahre: Button-1 ist die linke Maustaste, und ein Event muss offenbar in kleiner - größer-Zeichen gesetzt werden.

Dahinter kommt der Callback - hier wird definiert, was bei dem Event (Button mit linker Maustaste drücken) passieren soll. Da das noch nicht definiert ist, muss also handleButton erst noch definiert werden.

Wie das funktioniert, da bin ich gänzlich ausgestiegen - mental, meine ich. Und zweifle daran, ob ich das mit dem Python programmieren oder überhaupt mit dem Programmieren durchhalten werde. Wie soll man das alles behalten geschweige denn anwenden können?

Hier das komplette Programm:

import tkinter

i = 0
def handleButton(event):
    global i
    i = i+1
    w['text'] = "Zahl Nr" + str(i)
   
top = tkinter.Tk()
w = tkinter.Label(top, text=' ')
w.pack()

b = tkinter.Button(top, text='Drück hier')
b.pack()

b.bind('<Button-1>', handleButton)

top.mainloop()

Ich probiere mal aufzuschreiben, was ich verstanden habe:

Die Variable i wird zu allererst definiert, sie gilt für die anschließende Funktion als auch für das Programm, also für alles, was folgt, und ist daher eine globale Variable. Das muss man offenbar der Funktion mit global i mitteilen.

Ich kann offenbar der Funktion mitteilen, dass in der späteren Variablen w die Funktion text mit einer Aktion belegt wird, dazu muss ich aber text später leer lassen.

Was passiert, wenn man das Programm startet?
Es kommt ein leeres Fenster mit dem Button, auf dem steht "Drück hier" - das habe ich bei b definiert.
Wenn man ihn mit der linken Maustaste bedient (Button-1), dann wird die Funktion handleButton ausgeführt - und die ist ganz oben definiert. Also soll ein Text erscheinen mit dem Inhalt "Zahl Nr" und dazu wird i addiert, aber als string. Da i mit jedem Durchgang um 1 erhöht wird (i + 1), erscheint bei jedem Drücker eine um 1 erhöhte Zahl.

Na gut, das verstehe ich also schon. Nur den Code anwenden können??? Wie soll ich das lernen?




Kleine Rate-Aufgabe

Ich habe irgendwo ein kleines Problem gefunden - nämlich ein Spiel zu programmieren, bei dem man eine Zahl erraten soll. Das Programm gibt immer aus, ob die geratene Zahl zu groß oder zu klein ist - so lange, bis man die richtige Zahl erwischt hat.

Ganz ehrlich - nein, ich habe die Lösung nicht allein geschafft.

import random

Zahl = random.randint(1,100)
rate = 0
i = 0
while rate != Zahl:
    rate = int(input("Raten Sie eine Zahl zwischen 0 und 100: "))
    if rate < Zahl:
            print("Zu klein")
    if rate > Zahl:
            print("Zu gross")
    i = i + 1

print("Super, Du hast es in ", i, "Versuchen geschafft!")

Immerhin - ich habe mich an randint erinnert, und mir war auch klar, dass ich die while- und die if-Schleife brauche. Aber das mit dem Definieren der Variablen vorher (rate auf 0 setzen und eine weitere zum Zählen der Versuche), da komme ich einfach nicht drauf. Ich denke offenbar noch lange nicht wie ein Programmierer.

Dienstag, 2. Januar 2018

Grafische Benutzeroberflächen

Das habe ich ja schon häufiger erwähnt. Ich - ohne jegliche Programmiererfahrung - frage mich fortlaufend, wozu dieses Wissen dient, dass ich mir bis jetzt angeeignet habe. Oder besser: Das ich VIELLEICHT reproduzieren kann.

Nun hat mir mein Video-Tutor etwas von GUI und grafischen Benutzeroberflächen erzählt. GUI steht für Graphic User interface. Ich erinnere mich an die ersten Rechner, mit denen ich zu tun hatte. Da war der Bildschirm schwarz und darauf nur weißer Text. Als ich dann vor dem ersten Mac saß, sah ich wohl meine erste grafische Benutzeroberfläche. Wie man eine solche mit Python bastelt, wird mir also jetzt erklärt.

Ich bin gespannt und nach dem ersten Video frustriert. Ich kann das zwar abtippen und kriege auch das schöne Ergebnis, aber warum ich da was eingeben muss, verstehe ich erst mal gar nicht.

Also: Was ich brauche, ist Tk, eine Werkzeugkiste zur Erstellung eben solcher Benutzeroberflächen, und das soll eines der gängigen Werkzeuge sein. Bei Python heißt es tkinter und wird klein geschrieben. Es ist ein Modul und muss vorher integriert werden.

Also:

import tkinter

Aber danach hört mein Verständnis auf.

import tkinter

top = tkinter.Tk()

w = tkinter.Label(top, text='Das ist kompliziert')

w.pack()

top.mainloop()

Offenbar hole ich mir mehrere Funktionen aus tkinter: TK, Label, pack und mainloop. Aber was diese bewirken - keine Ahnung.

Wenn ich das Programm starte, sollte das Fenster mit dem Text "Das ist kompliziert" auftauchen. Tut es aber nicht, ich bin frustriert - bis mir einfällt, dass es sich hinter all den Fenstern, die ich grade geöffnet habe, versteckt.

So ist es auch. Puh, immerhin. Also kann ich jetzt kleine Fenster basteln, die man auch größer ziehen kann. Wow. Aber ich weiß nicht, wie ich das gemacht habe.

Lass ich mal hier so stehen und warte ab, was noch so alles kommt.

Noch mal Bibliotheken

Ich musste ja bereits einmal eine Bibliothek nutzen. Noch mal zum Verständnis: In Bibliothken sind jede Menge Funktionen gespeichert, die in Python selbst noch nicht vorhanden sind. Wenn man sie nutzen will, muss man sie importieren. Oder nur die einzelne Funktion importieren.

Wo findet man nun solche Bibliotheken? Auf der Webseite python.org, dort unter Domentation und dort unter Python 3...

Auf dieser Seite kann man suchen - wobei man nun mal Englisch können muss. Ich folge mal wieder meinem Video-Tutor und suche nach einer Funktion, mit der man die Wurzel einer Zahl ziehen kann.

Ich gebe also in die Suchfunktion squareroot ein und - weil mir mein Video-Tutor das empfohlen hat - schaue unter Mathematical Funktions nach. Die Bibliothek heißt math.
Dort finde ich die Funktion tatsächlich.



Die Funktion sieht so aus:
math.sqrt(x)

 Also schreibe ich mein Mini-Programm - und ich schwöre, ich schaffe es diesmal, ohne mir das Tutorial weiter angeschaut zu haben.

import math

zahl = 10

ergebnis=math.sqrt(zahl)

print(ergebnis)

Super, ich bin ein bisschen stolz.

Wobei das natürlich auch viel einfacher geht:

import math

print(math.sqrt(10))

Ach ja, man kann auch nur die Funktion importieren, das sieht dann so aus:

from math import sqrt

print(sqrt(10))


Noch mal Funktionen

Weil es so schön ist, noch ein Beispiel einer selbst erstellten Funktion. Nun lehrt mich das Video-Tutorial noch zwei Dinge, nämlich zum einen, wie man eine Endlos-Schleife programmiert. Ganz einfach:

while 1==1

Immer wenn das Programm in der Schleife oben ankommt, stimmt ja diese Bedingung - 1 ist eben immer gleich 1. Also geht es von vorne los.

Zum anderen: Wenn ich das, was per input eingegeben wird, zitieren will, also dass es in meiner Ausgabe eingefügt wird, dann geht das mit "+wort+". (Später finde ich heraus, dass das auch mit ",wort," klappt - also sowohl mit dem Plus-Zeichen als auch mit Kommata)

Und jetzt das ganze Programm, hier wird eine Funktion definiert, die Palindrome erkennen kann. Zur Erinnerung- das hatten wir schon.

def istpalindrom(wort):
    i = 0
    laenge = len(wort)
    palindrom = True

    while i < laenge/2:
        if wort[i] != wort[laenge - i-1]:
            palindrom = False
        i = i + 1

    return(palindrom)

while 1==1:
    eingabe = input("Geben Sie hier das vermutete Palindrom ein: ")
    if istpalindrom(eingabe):
        print("Das Wort "+eingabe+" ist ein Palindrom")
    else:
        print("Das Wort "+eingabe+" ist nie und nimmer ein Palindrom")       

Ich habe auch hier wieder viel Zeit damit verbracht, die Logik zu verstehen. Das ganze Palindrom-Erkennungsprogramm steckt nun in der Funktion istpalindrom - die muss man dann eben an der richtigen Stelle in der richtigen Form einfügen. Oh Mann...


Funktionen programmieren

Es gibt in Python bereits eingebaute Funktionen wie z.B. print - und ich vermute, dass es davon eine ganze Menge gibt, also muss man nicht alles selbst von vorne programmieren, sondern kann solche Funktionen halt nutzen.

Aber man kann sie natürlich auch selbst programmieren, und wie das geht, zeigt mir das Video-Tutorial, das ist mir zweimal komplett angeschaut habe.

Los geht's mit dem, was ich verstanden habe:

Ich definiere mir eine Funktion (ist das richtig ausgedrückt?), und zwar mit def:

def multipliziere (faktor1, faktor2)

Ich schreibe mir also eine eigene Funktion für das Multiplizieren, und die sieht so aus:

   produkt = faktor1 * faktor2
   return produkt

Und das Ergebnis wird dann zurückgegeben (return).

Hab damit nun ein kleines Programm geschrieben:

def multipliziere(faktor1, faktor2):
   produkt = faktor1 * faktor2
   return produkt

zahl1 = int(input("Geben Sie die erste Zahl ein: "))
zahl2 = int(input("Geben Sie die zweite Zahl ein: "))

Ergebnis = multipliziere(zahl1, zahl2)

print(Ergebnis)

Und es klappt:


Wobei die Funktion summiere jetzt nicht irgendwo gespeichert ist und ich sie in jedem neuen Programm benutzen kann - sie funktioniert nur solange ich in dieser Datei herumprogrammiere.

Montag, 1. Januar 2018

True und False

Die beiden Begriffe sind schon mal aufgetaucht, aber ich hatte noch keine Gelegenheit sie anzwenden. Sie sind wohl ein eigener Datentyp, und sie müssen immer groß geschrieben werden.

Das Beispiel aus dem Video-Tutorial hat es in sich, und ich habe lange rumprobiert, bis ich es nachvollziehen konnte.

wort = input("Geben Sie hier das vermutete Palindrom ein: ")

i = 0

laenge = len(wort)
palindrom = True

while i < laenge/2:
    if wort[i] != wort[laenge - i-1]:
        palindrom = False
    i = i + 1

print(palindrom)

Uff - das sieht nicht nur kompliziert aus, das ist es auch. Also von oben nach unten:

Zuerst fordert man mit der input-Funktion den User auf, ein Wort einzugeben. Die Aufgabe des Programms lautet zu erkennen, ob das eingegebene Wort ein Palindrom ist - ein Wort also, das von vorn und von hinten identisch ist. anna zum Bespiel. Oder otto. oder lagerregal.

Nach dem input-Auftrag wird ein index (hier i) definiert. Warum das? Mit meinem laienhaften Verständnis deshalb, weil wir später die einzelnen Zeichen in dem String, den der User eingibt, indizieren wollen. Soll heißen: Das Programm soll später wissen, welches Zeichen in der Zeichenkette er mit welchem vergleichen soll.

Dann brauchen wir die Länge des Wortes. Dazu haben wir ja mal len (von length) kennengelernt. Mit len wird die Anzahl der Zeichen in einem String oder einer Liste erfasst.

Und dann wird zum ersten Mal der Befehl True verwendet. Es ist so wie in der Mathematik: Man setzt eine Bedingung auf wahr und prüft dann die anschließenden Fälle auf falsch.

Anschließend beginnt die erste Schleife. Das erste Zeichen des Wortes soll sich Python vorknöpfen, danach das zweite usw. - und zwar so lange, bis die Hälfte des Wortes erreicht wird. Warum die Hälfte? Weil man dann ja das erste mit dem letzten, das zweite mit dem vorletzten usw. verglichen hat.
Der Index des ersten Zeichens (der ja 0 ist, wie ich gelernt habe und vermutlich noch oft vergessen werde) wird in der letzten Zeile der while-Schleife immer um 1 erhöht, bis er gleich der Hälfte der Zeichen ist. Nicht so schwer zu verstehen.

Aber in der if-Schleife wird es komplizierter. Ich lerne, das != ungleich bedeutet, und hier wird nun das erste Zeichen - wort[i] mit dem letzten Zeichen der Kette wort[laenge - i -1] verglichen, bzw. geschaut, ob es ungleich ist. Wenn dies der Fall ist, wird palindrom mit False ausgegeben - zu Recht.
Warum nun [laenge - i - 1] ? Weil man bei 0 anfängt zu zählen. Hat das Wort vier Buchstaben, dann hat der letzte Buchstabe den Index 3. Da i mit 0 definiert wurde, ist 4 - 0 - 1 eben drei, und der vorletzte Buchstabe 4 - 1 - 1 gleich 2.


Noch was mit Schleifen: Fakultät

Wow, das stehe ich da - habe völlig vergessen, was Fakultät in der Mathematik bedeutet. Fakultät 7 schreibt man so: 7!

Und bedeutet, dass alle Zahlen von 1 bis 7 miteinander multipliziert werden (wobei die 1 eigentlich nicht wirklich eine Rolle spielt)

Wie schafft man es nun, dass Python uns die Fakultät einer Zahl ausgibt, die der Nutzer eingibt? Ich ermögliche dem Nutzer also erstmal mit input, dass er eine Zahl eingeben kann:

Zahl = input("Bitte geben Sie eine Zahl ein: ")

Natürlich vergesse ich, dass ich den Datentyp festlegen muss (nämlich int, für integer, die ganze Zahl):

Zahl = int(input("Bitte geben Sie eine Zahl ein: "))

Dann muss ich das Ergebnis definieren, also die Fakultät. Die setze ich erst auf 1.

Fakultaet = 1

Dann kann die Rechnerei losgehen:

while Zahl > 1:
   Fakultaet = Fakultaet * Zahl
   Zahl = Zahl - 1

print(Fakultaet)

Funktioniert, und ich verstehe auch, warum. Aber wäre ich darauf gekommen? Im Leben nicht. Ich habe mir erst das Video angeschaut und dann versucht, es noch mal selbst herzuleiten. Nix zu machen. Die Denkweise oder sogar die Art des logischen Denkens ist mir fremd. Ob ich das lerne? Auf jeden Fall viel zu früh zum Aufgeben. Ich bräuchte mal kleinere Aufgaben, die man mit meinem jetzigen Wissen lösen kann. Die Übungen, die ich im Internet finde, sind schon alle deutlich zu schwer :-(