Seite 1 von 4
Anfängerfragen
Verfasst: Sonntag 28. Februar 2010, 12:33
von DeKugelschieber
Hallo,
ich hab vor ca. 3 Tagen mit Python (3.1) angefangen, hab aber schon relativ viel Erfahrung mit Java und PHP. Ich probier gerade mein erstes Tkinter Programm zu schreiben und hab da ein paar Fragen, nach denen ich schon 2 Tage gegooglet habe (im Forum hab ich auch schon geguckt):
1. Kann ein Label automatische Zeilenumbrüche haben? Da mein Text immer über den Rand des Fensters läuft und wrap = 300 oder so auch nicht das liefert was ich will. wraplength oder wrap = 'word' geht auch nicht.
2. Aus einer Listbox will ich das man eine Frage auswählen kann, das abfangen mache ich damit:
Code: Alles auswählen
selected = self.list.curselection()
if selected != self.question:
self.question = selected
self.showQuestion()
self.wnd.after(250, self.selection)
Wie kann ich jetzt verhindern das "nichts" in self.question eingetragen wird wenn nichts ausgewählt ist, und wie kann ich dafür sorgen das selected ein int ist/wird (default in self.question ist 0, daran lese ich später die frage aus einer datei).
3. Generell will ich auch noch wissen wie man überprüfen kann ob eine variable noch nicht gesetzt ist (PHP: !isset(...)).
4. Kann man mit pack_forget() auch elemente löschen die nicht exestieren (also das nichts passiert wenn ich es aufrufe und keine Fehlermeldung kommt, was man sicher auch mit Frage 3 verhindern kann).
MfG
PS: Wenn es dazu schon was gibt dann ist die Suchfunktion doch ziemlich scheiße^^
EDIT:
ok 4 hab ich gerade gelöst mit try
Verfasst: Sonntag 28. Februar 2010, 12:50
von cofi
Ad 1: Ich glaube, dass kann das Label nicht (ich kenne allerdings Tkinter nicht, aber diverse andere TKs). Schau mal hier:
http://effbot.org/tkinterbook/text.htm
Ad 3: Wenn du auf einen Namen zugreifst, der nicht existiert, ist das ein `NameError`, aber ich kann mir nicht vorstellen, dass das ein Konstrukt ist, den man in sauberem Code haben will oder gar benoetigt ... Also erzaehl mal, wo du hinwillst.
Ad 4: Ich hoffe das ist kein `try...except:` - d.h. ein blankes `except`. Ansonsten ist es das gleiche wie mit 3. Eventuell kommen auch Listen zu Hilfe.
Verfasst: Sonntag 28. Februar 2010, 13:10
von DeKugelschieber
danke, die seite hab ich mir schon angeschaut. aber es soll ja keine textarea erzeugt werden sondern eine simple textausgabe bei der man das fenster von der größe her verändern kann wie man will und der text sich dementsprechend über mehrere zeilen erstrecken kann.
zu 3 nochmal:
ich hab das problem (gehabt) das ich einen bereich für die ausgabe der frage habe und in dem muss, wenn man eine andere frage auswählt hat, diese erscheinen und die alte soll gelöscht werden, weil sie ja sonst darunter gepackt wird. und ich hatte jetzt die idee die ensprechenden elemente (label, buttons, evt. Toplevel) zu löschen und dann neue einzufügen, was natürlich bei programmstart zu einem fehler führt, da die elemente noch nicht da sind.
Hier jetzt einfach mal der gesammte code:
Code: Alles auswählen
# MC Tutor 2010
# Version 1.0
# (c) 2010 Marvin Blum
from tkinter import *
from tkinter import messagebox
class Tutor:
question = 0
saved = 0
def __init__(self):
self.wnd = Tk()
self.wnd.title("MC Tutor 2010 | (c) 2010 Marvin Blum") # Fenstertitel
self.wnd.geometry("800x500") # Fenstergroesse
self.wnd.iconbitmap(default = 'graphics/icon.ico') # Fenstericon
self.wnd.protocol('WM_DELETE_WINDOW', self.exit)
self.drawGUI() # GUI erstellen
self.getQuestions() # Fragen auslesen
self.wnd.mainloop()
# GUI erstellen
def drawGUI(self):
# Grafiken laden
img_new = PhotoImage(file = 'graphics/new.gif') # Neue Befragung
img_answer = PhotoImage(file = 'graphics/answer.gif') # Antwort zeigen
img_save = PhotoImage(file = 'graphics/save.gif') # Speichern
img_exit = PhotoImage(file = 'graphics/exit.gif') # Beenden
# Menu
menu = Menu(self.wnd)
self.wnd.config(menu = menu)
# Dateimenu
file_menu = Menu(menu)
menu.add_cascade(label = 'Datei', menu = file_menu)
file_menu.add_command(label = 'Neue Befragung', command = self.restart)
file_menu.add_command(label = 'Speichern', command = self.save)
file_menu.add_command(label = 'Beenden', command = self.exit)
# Toolbar
toolbar = Frame(self.wnd)
toolbar.config(bg = "#dee3f3")
b = Button(toolbar, image = img_new, command = self.restart)
b.image = img_new
b.pack(side = LEFT, padx = 2, pady = 2)
b = Button(toolbar, image = img_answer, command = self.showAnswer)
b.image = img_answer
b.pack(side = LEFT, padx = 2, pady = 2)
b = Button(toolbar, image = img_save, command = self.save)
b.image = img_save
b.pack(side = LEFT, padx = 2, pady = 2)
b = Button(toolbar, image = img_exit, command = self.exit)
b.image = img_exit
b.pack(side = LEFT, padx = 2, pady = 2)
toolbar.pack(side = TOP, fill = X)
# Liste
self.list = Listbox(self.wnd, selectmode = SINGLE)
scroll = Scrollbar()
scroll.config(command = self.list.yview)
self.list.config(yscrollcommand = scroll.set, width = 30)
self.list.pack(side = LEFT, fill = Y)
scroll.pack(side = LEFT, fill = Y)
# Fragen auslesen
def getQuestions(self):
f = open('questions.txt')
tmp = f.read()
self.questions = tmp.split('\n')
# In Liste eintragen
for i in self.questions:
tmp = i.split('||')
self.list.insert(END, tmp[0])
self.selection() # Auswahl abfangen
# Frage anzeigen
def showQuestion(self):
# Zerlegen
tmp = self.questions[0].split('||')
# Loeschen
try:
self.title.pack_forget()
self.start.pack_forget()
self.answer1.pack_forget()
self.answer2.pack_forget()
self.answer3.pack_forget()
self.answer4.pack_forget()
self.image_wnd.destroy()
except:
pass
try:
self.end.pack_forget()
except:
pass
# Ausgabe
self.answer = tmp[6] # Richtige Antwort
self.title = Label(self.wnd, text = tmp[0])
self.title.config(anchor = W, justify = LEFT, bg = '#0000ff', fg = '#ffffff', font = 'Arial 12 normal')
self.title.pack(side = TOP, fill = BOTH, padx = 2, pady = 2)
# Frage
self.start = Label(self.wnd, text = tmp[1])
self.start.config(anchor = W, justify = LEFT, font = 'Arial 12 normal')
self.start.pack(side = TOP, fill = BOTH, padx = 2, pady = 10)
# Antwort A
self.answer1 = Button(self.wnd, text = 'A: '+tmp[2], command = self.answer)
self.answer1.config(anchor = W, justify = LEFT, bg = '#d0d0d0', relief = FLAT, font = 'Arial 12 normal')
self.answer1.pack(side = TOP, fill = BOTH, padx = 2, pady = 2)
# Antwort B
self.answer2 = Button(self.wnd, text = 'B: '+tmp[3], command = self.answer)
self.answer2.config(anchor = W, justify = LEFT, bg = '#d0d0d0', relief = FLAT, font = 'Arial 12 normal')
self.answer2.pack(side = TOP, fill = BOTH, padx = 2, pady = 2)
if tmp[4]:
#Antwort C
self.answer3 = Button(self.wnd, text = 'C: '+tmp[4], command = self.answer)
self.answer3.config(anchor = W, justify = LEFT, bg = '#d0d0d0', relief = FLAT, font = 'Arial 12 normal')
self.answer3.pack(side = TOP, fill = BOTH, padx = 2, pady = 2)
if tmp[5]:
# Antwort D
self.answer4 = Button(self.wnd, text = 'D: '+tmp[5], command = self.answer)
self.answer4.config(anchor = W, justify = LEFT, bg = '#d0d0d0', relief = FLAT, font = 'Arial 12 normal')
self.answer4.pack(side = TOP, fill = BOTH, padx = 2, pady = 2)
if tmp[7]:
# Anhang
self.end = Label(self.wnd, text = tmp[7])
self.end.config(anchor = W, justify = LEFT, font = 'Arial 12 normal')
self.end.pack(side = TOP, fill = BOTH, padx = 2, pady = 10)
if tmp[8]:
try:
image = PhotoImage(file = 'images/'+tmp[8])
self.image_wnd = Toplevel()
self.image_wnd.title('Bild: '+tmp[8])
self.image_wnd.transient(self.wnd)
img = Label(self.image_wnd, image = image)
img.image = image
img.pack()
except:
messagebox.showwarning('Grafik nicht gefunden', 'Die Grafik "'+tmp[8]+'" konnte nicht gefunden werden!')
# Neue Befragung
def restart(self):
self.question = 0
self.list.select_clear(0, END)
self.list.select_anchor(0)
self.list.select_set(0)
# Antwort zeigen
def showAnswer(self):
pass
# Antworten
def answer(self, ans):
pass
# Speichern
def save(self):
pass
# Frage auswaehlen
def selection(self):
# Auswahl
selected = self.list.curselection()
if selected != self.question:
self.question = selected
self.showQuestion()
self.wnd.after(250, self.selection)
# Beenden
def exit(self):
if self.saved == 0:
ask = messagebox.askyesno('Beenden', 'Willst du wirklich beenden ohne zu speichern?')
if ask == 1:
self.wnd.quit()
else:
self.wnd.quit()
Tutor()
da auch noch mal ein hinweis: in self.question soll die id der ausgewählten frage sein (am anfang 0), ändern will ich die über diese selection methode. nur kann ich damit nichts anfangen, weil es kein int ist (tmp = self.questions[0].split('||') soll tmp = self.questions[self.question].split('||') sein)
Verfasst: Sonntag 28. Februar 2010, 17:57
von DeKugelschieber
Ich hab da noch eine Frage: Wie kann man Argumente an eine Methode/Funktion bei einem command = ... übergeben? Ich versteh das nicht so ganz...
Verfasst: Sonntag 28. Februar 2010, 18:17
von cofi
Bitte lagere deinen Code auf eine Pastebin (z.b. paste.pocoo.org) aus, das Forum hat Probleme mit laengerem Code.
`command` kannst du nur ein Funktionsobjekt uebergeben.
Loesungen siehe
http://www.python-forum.de/topic-22047.html
Edit: Link gefixt.
Verfasst: Sonntag 28. Februar 2010, 19:23
von DeKugelschieber
Not found...

Verfasst: Sonntag 28. Februar 2010, 19:46
von DeKugelschieber
Ja ok ich hab ein bisschen rumprobiert und das geht über lambda, aber nicht in dem jetzigen Programm?! Liegt es vielleicht daran das der "Fragenblock" nachträglich erstellt wird? Weil in meinem Testprogramm geht es wenn ich das mainloop() nach dem Button schreibe.
Achja und wie sieht mein Programm sonst so aus?
http://paste.pocoo.org/show/183951/
Verfasst: Sonntag 28. Februar 2010, 20:46
von BlackJack
@DeKugelschieber: Scheint etwas tief eingerückt -- mit Tabs!? Solltest Du durch 4 Leerzeichen pro Ebene ersetzen.
Sternchenimport sollte man vermeiden. Sonst könnte man sich das Konzept von Namensräumen und Modulen auch gleich sparen.
Sollen `question` und `saved` wirklich Klassenattribute sein? Ist `saved` eine Zahl? Sieht eher nach einem Wahrheitswert aus → dafür gibt es `True`/`False`.
Namenskonvention für alles ausser Klassen (und "Konstanten") ist bei Python `kleingeschrieben_und_mit_unterstrichen`.
Abkürzungen sollte man vermeiden. `wnd`? Warum nicht `window`?
Der Aufbau der GUI bei dem Elemente immer wieder erstellt und dann wieder gelöscht werden, ist IMHO unschön. Alle erstellen und dann entsprechend mit Daten befüllen scheint mir vernünftiger zu sein. Damit würde sich Dein Ausgangsproblem auch gar nicht stellen -- die Elemente sind halt einfach immer da. Dann könnte man sich vielleicht auch die fixe Grössenvorgabe für das Hauptfenster sparen. Nach Ablauf der `__init__()`-Methode sollten alle Attribute auf einem Objekt existieren. Später in Methodenaufrufen welche hinzuzufügen ist schlechter Stil. Nach `__init__()` sollte ein Objekt möglichst voll funktionsfähig sein.
Kommentare sollten Mehrwert gegenüber dem Quelltext liefern und nicht triviales/offensichtliches nochmal wiederholen.
Die Kommentare über den Methoden würden sich vielleicht als Docstrings besser machen.
`getQuestions()` hiesse besser `read_questions()`.
`i` sollte man nicht an etwas anderes als ganze Zahlen binden, insbesondere nicht wenn es als Schleifenvariable verwendet wird.
Wenn man oft das gleiche schreibt, oder zumindest fast, und dabei aufsteigende Zahlen im Quelltext hat, und Namen durchnummeriert, sollte man *dringend* über eine Schleife nachdenken.
Ausführbarer Code auf Modulebene sollte vor dem ausführen beim importieren geschützt werden:
Verfasst: Sonntag 28. Februar 2010, 21:52
von DeKugelschieber
Danke für deine ganzen Tipps
Das mit dem Stil ist natürlich teils auch Personenbezogen, aber wenn man das bei Python so macht... Ich kenns nen bisschen anders, aber egal. Das mit den offensichtlichen Kommentaren hab ich nur hier erstmal so gemacht, damit ich mich an die fehlenden {} und was weiß ich nicht noch alles gewöhne (insbesondere ohne ; zu schreiben ist komisch^^). Und ja, das waren in Notepad++ noch 4 Leerzeichen... Der scheint das da geändert zu haben
Ich liefer hier morgen die verbesserte Version
Verfasst: Sonntag 28. Februar 2010, 22:27
von DeKugelschieber
So ich habs doch gerade noch verbessert:
http://paste.pocoo.org/show/184045/
aber das lambda in den Buttons funktiniert nicht:
TypeError: 'str' object is not callable
und bei der Fragenmethode:
list indices must be integers, not str
beide kann ich nicht so ganz nachvollziehen, vorallem der letzt, weil es eigentlich ein int sein müsste, und hat python nicht automatische typeconvertierung so wie php?
und sag mir mal wie ich das mit der schleife für die bottons regeln soll, kann man dann einfach self.i oder so schreiben?
Verfasst: Montag 1. März 2010, 01:16
von cofi
Nein Python hat keine automatische Konvertierung, da Python streng typisiert ist.
Deine `lambda` schaun mir auch kaputt aus. Wenn du einfach feste Zahlen nutzen willst, macht das auch `lambda: func(1)`.
Aber schau dir nochmal die Dokumentation zu `lambda` an.
Verfasst: Montag 1. März 2010, 09:00
von DeKugelschieber
ok, danke werd ich machen, aber wie bekomme ich das jetzt hin das die zahl aus curselection auch ein int ist?
Verfasst: Montag 1. März 2010, 12:19
von DeKugelschieber
Ich hab da jetzt mal noch eine Frage die mir gerade so gekommen ist:
Kann man irgendwie verhindern das sich das python.exe Fenster mit öffnet? Oder ist das nach dem umwandeln zu exe Datei eh erledigt?
Ach ja und nochmal ein 5-Tage-Python-Fazit: Unglaublich einfach zu lernen (ja gut ich bin vorbelastet), super lesbar, zum "mal eben was schreiben" super geeignet. Nicht so toll ist das man nicht sieht was für typen man da hat und das es keine richtige sichtbarkeit bei OOP gibt (oder täusche ich mich da?).
Verfasst: Montag 1. März 2010, 12:42
von HWK
DeKugelschieber hat geschrieben:Kann man irgendwie verhindern das sich das python.exe Fenster mit öffnet?
Indem Du das Script mit pythonw.exe startest.
MfG
HWK
Verfasst: Montag 1. März 2010, 13:40
von cofi
DeKugelschieber hat geschrieben:aber wie bekomme ich das jetzt hin das die zahl aus curselection auch ein int ist?
`int(curselection)` ? Wenn das keine Zahl ist, schmeisst das entsprechende Exceptions (das ist btw kein Cast, sondern eine echte Umwandlung).
DeKugelschieber hat geschrieben:Nicht so toll ist das man nicht sieht was für typen man da hat und das es keine richtige sichtbarkeit bei OOP gibt (oder täusche ich mich da?).
Nein, das stimmt schon. Ich frage mich allerdings, wo dir das konkret im Weg stand? Python regelt das ueber die Konvention, dass alles was mit einem `_` anfaengt als implementierungsdetails zu betrachten ist. Wer es dennoch anfasst, sollte eben wissen was er tut. Python folgt da dem Prinzip des nicht bloeden Entwicklers
http://tutorial.pocoo.org/classes.html# ... -variablen
Echte Zugangsbeschraenkungen bekommt man mit Deskriptoren hin.
Verfasst: Montag 1. März 2010, 15:19
von DeKugelschieber
@HWK danke
naja wenn man vorher int davorschreiben würde, würde der ja bei der zuordnung mit curselection eine fehlermeldung ausgeben, da hät ich das dann eher gemerkt. egal, funktioniert die typconvertierung generell so (oder so ähnlich) wie in java (auch mit string())?
achja, habt ihr vielleicht eine bessere idee wie man das mit den fragen regeln kann? ich mach das im moment so:
1 Zeile in der txt = 1 Frage
zerlegen mit || und dann:
[0] = titel
[1] = frage
[2] - [5] = antworten
[6] = evt. anhang (text)
[7] = richtige antwort
[8] = evt. grafik
Verfasst: Montag 1. März 2010, 15:34
von EyDu
Hallo.
Ja, die Umwandlung macht man immer so. Man geht erstmal davon aus, dass das gewünschte funktioniert und wenn nicht, dann behandelt man die entsprechende Ausnahme (EAFP-Prinzip).
Zu deiner Struktur: Ich würde das Feld mit der richtigen Antwort weglassen und dafür definieren, dass die erste Antwortmöglichkeit immer die richtige ist. Wenn du die Fragen dem Benutzer anzeigst, dann mischt du die Antworten die zu einer Frage gehören (random.shuffle).
Sebastian
Verfasst: Montag 1. März 2010, 15:55
von cofi
DeKugelschieber hat geschrieben:funktioniert die typconvertierung generell so (oder so ähnlich) wie in java (auch mit string())?
Ich weiss ja nicht was du fuer ein Java benutzt, aber ich muss da immer `Integer.valueOf`, `String.valueOf` und dergleichen bemuehen

Aber ja, ganz grob gesagt es funktioniert so aehnlich. (Aber eben nicht Casts wie `(String)`)
Verfasst: Montag 1. März 2010, 16:01
von DeKugelschieber
Ja dann vielen Dank erstmal, wenn ich hier weg komme kann ich das dann zuhause weitermachen und geb euch das ergebnis, das mit dem shuffle klingt gut, aber ich machs erstmal so und bau das dann später erst ein

muss ja erstmal funktionieren.
Bei Java schreib ich nur a = (int)b; Version ist 1.6.17 (aktuell ist 1.6.18 )
MfG
Verfasst: Montag 1. März 2010, 18:34
von DeKugelschieber
Ja ok, das mit dem int() klappt super, aber zwei Fragen hab ich noch:
1. Wie geht das mit dem lambda jetzt? So nicht:
edit:
1 hat sich erledigt! Ich wusste nicht das es Python nicht egal ist ob eine Variable und eine Methode den selben Namen haben.
2. Es muss doch möglich sein automatische Zeilenumbrüche in einem Label zu machen, und wenn nicht (was eigentlich total bescheuert ist), was für alternativen gibt es (kein Text()!)?
edit:
Was mir gerade noch so einfällt, kann man ein kleines icon in eine liste vor einen string setzen?