Frame auf Frame kleben

Fragen zu Tkinter.
Antworten
Dexter1997
User
Beiträge: 92
Registriert: Sonntag 2. Dezember 2012, 21:13

Hallo Freunde der Sonne,

folgender Code-Ausschnitt soll einfach nur einen Frame auf einen Frame kleben. Leider funktioniert das nicht, und es werden mir sogar zwei statt nur einem Frame angezeigt, auf denen nichtmal die aufgeklebten Elemente dargestellt werden.

Code: Alles auswählen

from tkinter import *

root = Tk()

class Question:
	def __init__(self, question, answers, correctLetter):
		self.question = question
		self.answers = answers
		self.correctLetter = correctLetter
		
	def check(self, letter):
		return self.correctLetter == letter
		
	def getView(self,):
		view = Frame()
		Label(view, text=self.question).pack()
		Button(view, text=self.answers[0], command=lambda *args: self.check("A")).pack()
		Button(view, text=self.answers[1], command=lambda *args: self.check("B")).pack()
		Button(view, text=self.answers[2], command=lambda *args: self.check("C")).pack()
		Button(view, text=self.answers[3], command=lambda *args: self.check("D")).pack()
		return view
		
question = Question("How are you?", ["Good", "Bad", "Option C", "Option D"], "A")
window = Tk()
Frame(window, question.getView())
window.mainloop()
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@Dexter1997: wurde Dir hier schon bestimmt 100mal gesagt, aber vielleicht fruchtet es mal: Eingerückt wird immer mit 4 Leerzeichen, nicht 3. Sternchenimporte sind böse. Methoden und Variablen werden klein_mit_unterstrich geschrieben. Was ist der sinn des weitern Frames? Du mußt die Frames ineinander packen, wie Du es ja schon mit den Buttons machst.
Dexter1997
User
Beiträge: 92
Registriert: Sonntag 2. Dezember 2012, 21:13

Sirius3 hat geschrieben: Was ist der sinn des weitern Frames? Du mußt die Frames ineinander packen, wie Du es ja schon mit den Buttons machst.
folgender Code-Ausschnitt soll einfach nur einen Frame auf einen Frame kleben. Leider funktioniert das nicht, und es werden mir sogar zwei statt nur einem Frame angezeigt
Wie wäre es, wenn du erstmal die Frage liest, bevor du mir falsche Sachen unterstellst?
statische Importe sind nicht "böse", sondenr hier zweckmäßig. Ich wollte außerdem keine Codebewertung sondern eine Beantwortung der Frage. Wie soll ich die Frames "ineinanderkleben" ? Genau das habe ich doch gemacht und es hat nicht funktioniert. Kannst du mal einen Einzeiler präsentieren, der genau das macht?
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@Dexter1997: wenn Du hier Hilfe willst, solltest Du die Helfenden nicht beleidigen. Wenn Du hier Hilfe willst, solltest Du es den Helfenden so einfach wie möglich machen, indem Du Dich an die Coding-Richtlinien hältst. Die sind ja nicht zum Spaß da, sondern weil sie helfen, sauberen, fehlerfreien, testbaren und wartbaren Code zu schreiben.

Es liegt an Dir.
Dexter1997
User
Beiträge: 92
Registriert: Sonntag 2. Dezember 2012, 21:13

Ich habe niemanden beleidigt, sowas hört sich anders an. Das mit den drei Leerzeichen ist mir unbegreiflich. Ich benutze Tabulatoren, scheint wohl so als würden die mit der Code-Formatierung dieses Forums als drei Leerzeichen dargestellt werden, dafür kann ich nichts, man vergebe mir diese Sünde.

Ich Schwachkopf habe nicht gesehen, dass ich oben bereits ein Frame erzeugt habe. Meine Fresse, bin ich manchmal dämlich, und dieser Thread entsprechend peinlich. Naja, wer den Schaden hat...

Hier der funktionierende Code, in dem alles ordnungsgemäß angezeigt wird:

Code: Alles auswählen

from tkinter import *

class Question:
	def __init__(self, question, answers, correctLetter):
		self.question = question
		self.answers = answers
		self.correctLetter = correctLetter
		
	def check(self, letter):
		return self.correctLetter == letter
		
	def getView(self, window):
		view = Frame(window)
		Label(view, text=self.question).pack()
		Button(view, text=self.answers[0], command=lambda *args: self.check("A")).pack()
		Button(view, text=self.answers[1], command=lambda *args: self.check("B")).pack()
		Button(view, text=self.answers[2], command=lambda *args: self.check("C")).pack()
		Button(view, text=self.answers[3], command=lambda *args: self.check("D")).pack()
		return view
		
question = Question("How are you?", ["Good", "Bad", "Option C", "Option D"], "A")
window = Tk()
frame = question.getView(window).pack()
window.mainloop()
Benutzeravatar
noisefloor
User
Beiträge: 3843
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
statische Importe sind nicht "böse", sondenr hier zweckmäßig.
_Sternchen_importe sind nicht zweckmäßig, weil man sich den Namensraum unkontrolliert "zumüllt". Das kann schlimmstfalls zu schwer bis nicht nachvollziehbaren Fehlern führen. Bei `from tkinter import *` sind das 136 Namen (!), die du in den Namensraum holtst. Mal abgesehen davon, dass der Code schwerer lebar ist, weil nicht klar ist, wo ein Name her kommt.
Ich benutze Tabulatoren,
Eingerückt wird per Konvention mit 4 Leerzeichen, nicht mit Tabs. Technisch funktioniert zwar auch letzteres, aber sauberer Stil ist es nicht.

Gruß, noisefloor
Dexter1997
User
Beiträge: 92
Registriert: Sonntag 2. Dezember 2012, 21:13

Es gibt wieder ein neues Problem, an dem ich jetzt eine halbe Stunde sitze, und mir die Haare ausreißen könnte:

Mein Programm stellt eine Frage. Durch anklicken der Buttons wird geprüft, ob die Eingabe richtig war. Danach soll "Richtig" als Label auf dem Fenster für eine Sekunde erscheinen. DANACH soll das Label gelöscht werden. So habe ich die Anweisungsfolge auch hingeschrieben. Jedoch führt das Programm zuerst die unpack-Anweisung aus und wartet danach eine Sekunde, obwohl das genau umgekehrt hingeschrieben wurde. Wisst ihr Rat?

Code: Alles auswählen

from tkinter import Tk, Frame, Label, Button 
from time import sleep

# this class represents a question which has the actual question and for answering possibilities
class Question:
	def __init__(self, question, answers, correctLetter):
		self.question = question
		self.answers = answers
		self.correctLetter = correctLetter
		
	def check(self, letter, view):
		if(letter == correctLetter):
			label = Label(view, text="Right!")
			label.pack()
			sleep(1)
		else:
			label = Label(view, text="Wrong!")
			label.pack()
			sleep(1)
		view.pack_forget();
		
	def getView(self, window):
		view = Frame(window)
		Label(view, text=self.question).pack()
		Button(view, text=self.answers[0], command=lambda *args: self.check("A", view)).pack()
		Button(view, text=self.answers[1], command=lambda *args: self.check("B", view)).pack()
		Button(view, text=self.answers[2], command=lambda *args: self.check("C", view)).pack()
		Button(view, text=self.answers[3], command=lambda *args: self.check("D", view)).pack()
		return view

# start quiz	
def start():
	global window, button
	button.pack_forget()

	for question in questions:
		question.getView(window).pack()
	Label(window, text="You've finished all Questions!")
	
		
# generate and initialize questions
questions = []
file = open("questions.txt", "r")
line = file.readline()
while(line != ""):
	questionString = line
	answers = []
	for i in range (4):
		answers.append(file.readline())
		
	correctLetter = file.readline()
	questions.append(Question(questionString, answers, correctLetter))
	line = file.readline()
file.close()
	
# generate window
window = Tk()
button = Button(window, text="Start", command=start)
button.pack()
	
window.mainloop()
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Einen Euro fuer jedes mal, das ich das hier erklaeren muss, und ich wuerde das hier vom Strand auf der Privatinsel mit Pina-Colada in einer Hand schreiben....

Wirklich jedes zweite Thema hier in Tkinter dreht sich darum, das man in GUIs *NICHT* blockierde Dinge wie zB schlafen oder lange Dateiverarbeitung etc machen kann. Weil die dann nicht mehr auf Ereignisse reagieren koennen, und sich auch nicht neu malen.

Die Loesung deines Problem lautet "after" und ist wirklich sehr, sehr, sehr, sehr viel diskutiert worden hier. Bitte einfach mal die letzten paar Themen durchschauen.
Dexter1997
User
Beiträge: 92
Registriert: Sonntag 2. Dezember 2012, 21:13

der begriff after trifft es gut, wenn man sich die qualität der probleme mit tkinter anschaut

auf jeden fall danke für die antwort! fühl dich geküsst!
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Da jedes GUI-Framework in jeder Sprache diese Probleme auch hat, liegt das Qualitaetsproblem eher an einer anderen Stelle. Aber Ahnungslosigikeit haelt ja die wenigsten von einer Meinung ab.
Dexter1997
User
Beiträge: 92
Registriert: Sonntag 2. Dezember 2012, 21:13

Danke, ich habe das korrigiert. Ich habe nur noch ein letztes Problem. Ich möchte, dass das Programm wartet, bis die alte Frage beantwortet wird, bevor das Programm eine neue Frage stellt. Wie erreiche ich das? Ich habe für dieses Problem bis jetzt keinen vernünftigen Lösungsansatz gefunden.
Astorek
User
Beiträge: 72
Registriert: Samstag 24. Januar 2009, 15:06
Kontaktdaten:

Vielleicht hilft folgender Denkanstoß:

So ziemlich alle* GUI-Frameworks arbeiten ereignis- statt prozedurbasiert! Heißt: Du gibst das Layout vor und die Funktionen, die aufgerufen werden sollen, wenn mit einem Element interagiert - z.B. ein Button gedrückt - wird.

Die "Mainloop" ist im Prinzip eine einzige Schleife (daher auch der Name), das permanent dafür sorgt, dass die GUI auf die Eingaben reagiert. Es fragt permanent die einzelnen GUI-Elemente ab, um Änderungen (eben z.B. durch Mausklicks ausgeführt, aber auch so grundlegende Sachen wie Verschieben des Fensters etc.) zu erfassen und darauf zu reagieren.

Deshalb ist es in GUIs immer eine besonders schlechte Idee, wenn mittendrin Sachen wie "sleep" drin vorkommen, weil nicht nur gewartet wird, sondern währenddessen auch die GUI selber angehalten wird. Während des Sleeps läuft die Mainloop nicht, also kann währenddessen auch nicht z.B. auf Eingaben reagiert oder geänderte Elemente neu gezeichnet werden.

Um das zu umgehen, bieten die GUI-Frameworks Methoden an, um stattdessen zeitlich gesteuert eine Funktion aufzurufen, z.B. die eben erwähnte ".after"-Methode in tkinter.

Lange Rede kurzer Sinn: Deiner Fragestellung nach und ohne den Quellcode angesehen zu haben, würde ich einfach einen Button "Antworten senden" (o.Ä.) einbauen, in dieser dann die Elemente mit den Antworten holen (z.B. von einer Textbox) und darauf basierend die GUI entsprechend ändern (z.B. Labeltext ändern in "Antwort falsch", bzw. die vorigen Elemente zur Frage- und Antwortdarstellung löschen und stattdessen die neuen Elemente erstellen...)



*(Nur der Vollständigkeit halber: Es gibt auch sehr krude Ausnahmen von der Regel, z.B. bietet die Sprache "AutoIt" tatsächlich an, innerhalb einer Schleife weitere Abfragen durchzuführen. Die Regel ist das aber nicht und als GUI-Programmierer muss man sich darauf auch einstellen... Außerdem hat die Methode andere Nachteile, die eigentlich kein Programmierer haben will, aber das ist ein anderes Thema...)
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

@Dexter1997: mir ist noch nicht so ganz klar was du erreichen moechtest. Soll das ein Fragebogen Tool werden? Mit nacheinander gestellten Fragen, die dann entweder visuelles Feedback geben wenn etwas nicht richtig ist, oder dann zur naechsten Frage schreiten, wenn die Antwort passt?
Antworten