Widgets werden nicht angezeigt

Fragen zu Tkinter.
Antworten
Locke
User
Beiträge: 3
Registriert: Mittwoch 30. Januar 2008, 15:32

Huhu liebe Boardgemeinde,

ich bin momentan dabei eine Applikation in Python zu schreiben. Es ist zwar nichts besonderes und dient eigentlich nur dazu die Sprache ein wenig kennenzulernen.

Ich habe folgendes Problem:

Code: Alles auswählen

class Fenster(Frame):

    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.master.geometry("500x460")
        self.grid()
        self.Widgets()

    def Widgets(self):
        self.labelpktz=Label(self, text="Bitte Rohpunktzahl eingeben")
        self.labelpktz.place(x=50, y=55)

...

fenster = Fenster()
fenster.master.title("Application")
fenster.mainloop()
Ich habe jetzt mal nur das erste Label hier reingeschrieben die anderen sind vom Prinzip her auch so aufgebaut. Nur was komisch ist, das Label wird mir nicht angezeigt ... nehme ich allerdings das self bei

Code: Alles auswählen

self.labelpktz=Label(self, text="Bitte Rohpunktzahl eingeben")
(das in der Klammer) funktioniert das ganze nur dann habe ich das problem das ich via command-Befehl die Daten nicht auslesen kann welche z.B. in einer Entrybox stehen und somit ist das ganze dann fuer mich unbrauchbar.

Was allerdings merkwuerdig ist, ich habe ein anderes Programm geschrieben in welchem ich das gleiche Grundmuster wie hier verwendet hab und dort funktioniert alles ohne Probleme ...

Koennte mir von euch jemand sagen wo das Problem liegt?

Vielen Dank im voraus,

mfg Locke
BlackJack

Das Hauptfenster sollte explizit ein `Tkinter.Tk`-Objekt sein. Widgets sollten sich nicht selbst in ihrer `__init__()` "layouten". Ein `grid()` ohne Zellenangabe!?

Und man sollte weder die Fenstergrösse vorgeben, noch `place()` verwenden, wenn man GUIs schreiben will, die nicht nur auf dem eigenen Rechner richtig funktionieren.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Das Hauptfenster sollte explizit ein `Tkinter.Tk`-Objekt sein.
Warum?
Den von Locke verwendenten Ansatz findet man z.B. auch bei Shipman
(http://www.nmt.edu/tcc/help/pubs/tkinter.pdf).
Widgets sollten sich nicht selbst in ihrer `__init__()` "layouten".
Warum nicht? Ebenfalls so bei Shipman (s.o.).
Ein `grid()` ohne Zellenangabe!?
Warum nicht? column und row haben doch default-values. grid() ohne Optionen ist zulässig und völlig in Ordnung.
Und man sollte weder die Fenstergrösse vorgeben
Warum nicht?

------------

Das wirklich entscheidende Problem des Programms von Locke scheint mir darin zu liegen, dass er zwei Geometriemanager gemischt einsetzt und das geht in der Regel nicht gut. Also entweder alles mit place() (was man natürlich grundsätzlich eher nicht tun sollte) oder alles mit grid() - das sollte funktionieren.
BlackJack

`Tk` weil soweit ich weiss nicht auf jeder Plattform garantiert ist, dass dieses Objekt automatisch erzeugt wird.

"Layouten" in der `__init__()` von Widgets nimmt einem die Flexibilität das selber zu tun. Keines der bereits vorhandenen Widgets "layoutet" sich selbst.

`grid()` ohne Zellenangabe mag Default-Werte benutzen, aber welche sind das? Und warum `grid()` wenn es nur dieses eine Objekt gibt. Falls es noch andere gibt, vertragen sich die Default-Werte dann mit den Objekten die schon platziert wurden oder noch werden damit?

Wenn man die Fenstergrösse vorgibt, sollte man wissen wie gross der Inhalt ist. Das weiss man aber nicht, darum sollte man das dem Layout-Manager überlassen.

Und das Problem dürften nicht die Layout-Manager sein, weil das `grid()` den Frame auf dem `Tk`-Fenster setzt und die `place()`-Aufrufe die Labels auf dem Frame. Das sind also zwei verschiedene Container-Widgets.
Locke
User
Beiträge: 3
Registriert: Mittwoch 30. Januar 2008, 15:32

@pütone:

Danke fuer deine schnelle Antwort und danke das du auch gleich die Tkinter Referenz mit eingebracht hast. Habe mich naemlich an dieser orientiert.

Ich komm bei deiner Analyse jetzt nicht ganz hinterher, sollte ich also auf grid() + pack() umsteigen? Da stellt sich bei mir allerdings die frage wie ich dann die genaue Position eines Objektes definieren kann :)

Mal nebenbei gefragt was ist an place() so schlimm? Ist jetzt nur ne Frage aus reinem Interesse denn mir wurde von nem Kumpel vor Zeiten mal gesagt das ich lieber place() anstelle von pack() benutzen solle weil es angeblich besser sei, deswegen habe ich darauf jetzt vertraut :)

@BlackJack:
Also ich bin eher ein Python-Anfaenger (welches deutlich aus meinem 1. Post hervorgeht) und kann mit deiner Aussage nicht wirklich was anfangen

Du sagtest:
Und man sollte weder die Fenstergrösse vorgeben, noch `place()` verwenden, wenn man GUIs schreiben will, die nicht nur auf dem eigenen Rechner richtig funktionieren.
Kann ich leider nicht ganz nachvollziehen, zumindest das mit der Fenstergroesse nicht, da ich davon ausgehe das jeder normale User mindestens eine Aufloesung von 1024x768 verwendet und Pixel werden von jedem System gleich verarbeitet, warum sollte es dann damit Probleme geben?

Danke fuer eure Hilfe :)

mfg Locke

edit:
zu deinem neuen post @ Blackjack:
Es herrscht Verwirrung in meinem Kopf ;)
btw. ich weiss wie gross der Inhalt ist :D
BlackJack

Die Frage ist nicht, ob das Fenster auf den Bildschirm passt, sondern ob der Inhalt in das Fenster passt. Schriftgrössen sind nicht überall gleich und Bildschirmauflösungen auch nicht. `place()` hat das gleiche Problem. Was bei Dir gut aussieht kann bei anderen zu Elementen führen die übereinander liegen, und damit teilweise unbenutzbar werden, oder zwischen den Elementen sind grössere Lücken.

Wenn man Frames und `pack()` und `grid()` benutzt, sorgt Tk dafür das alles ordentlich angezeigt wird und jedes Element soviel Platz bekommt wie es braucht, nicht mehr und nicht weniger. Und die Fenstergrösse wird auch entsprechend angepasst. Das gilt dann nicht nur für verschiedene Schriftgrössen und Bildschirmauflösungen, sondern auch wenn man mal die Texte auf Buttons oder Labels ändert. So etwas kann bei `place()` eine Menge Änderungen an Elementen in der "Umgebung" nach sich ziehen. Wenn man das Layout Tk überlässt, braucht man nur den Text ändern.

Edit: Zu Deinem Edit: Wenn der Inhalt nicht aus Pixelgrafiken besteht, weisst Du nicht wie gross er ist, weil sich Schriftgrössen und auch Abstände und Grösse von zum Beispiel Rahmen bei `Entry`\s auf anderen Plattformen ändern können.
Locke
User
Beiträge: 3
Registriert: Mittwoch 30. Januar 2008, 15:32

Hmmm das hab ich nun verstanden, danke fuer diese Erklaerung :)

Wie genau wendet man denn die grid()-Methode an? grid verraet mir ja schon das es sich um ein Gitter handelt, geh ich nu von einem Raster bei der Fotografie aus habe ich insgesamt 3x3 Kaestchen. Ist das bei Python auch so? Ich kann mich dunkel daran erinnern irgendwann mal was gehoert zu haben von 'N, W, E, S' ... (Himmelsrichtungen) mit welchen ich dann bestimmen soll wo sich die Elemente befinden sollen, aber wie genau geht das?

Danke im voraus :)

mfg Locke
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Ich empfehle das entsprechende Kapitel zu grid() aus Fredrik Lundhs "An Introduction to Tkinter".

http://effbot.org/tkinterbook/grid.htm
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

@BlackJack:
`grid()` ohne Zellenangabe mag Default-Werte benutzen, aber welche sind das?
column=0
row = 0 bzw. falls schon Zeilen genutzt sind, die erste ungenutzte Zeile
Und warum `grid()` wenn es nur dieses eine Objekt gibt. Falls es noch andere gibt, vertragen sich die Default-Werte dann mit den Objekten die schon platziert wurden oder noch werden damit?
Warum soll man nicht im Versuchsstadium, wenn es auf anderes ankommt, der Kürze halber einfach .grid() benutzen; ebenso wie .pack(). Auch hier überlässt man ja dem Geometriemanager sozusagen die Hoheit über die Platzierung, sofern man keine entsprechenden Optionen mitgibt.

Für ein gescheites Layout wird man .grid() ebenso wie .pack() kaum ohne Parameter verwenden können.
Wenn man die Fenstergrösse vorgibt, sollte man wissen wie gross der Inhalt ist. Das weiss man aber nicht, darum sollte man das dem Layout-Manager überlassen.
Das verstehe ich nicht. Ich kann doch über .winfo_width() und .winfo_height() die Abmessungen von Widgets und Containern ermitteln und ggf. dann die Größe des Frames anpassen. Oder habe ich jetzt etwas falsch verstanden?
BlackJack

Wenn einem das Spass macht kann man natürlich die Abmessungen von Elementen selbst ermitteln, die Frames entsprechend anpassen, dann deren Container usw. bis zum Fenster. Also das was der Layout-Manager automatisch macht, selbst nochmal implementieren. Dann stell ich aber mal die Frage zurück: Warum?
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Dann stell ich aber mal die Frage zurück: Warum?
Beispiel: Ich habe eine kleine Anwendung, die aus einem Frame und ein paar Labels besteht, die alle nahtlos untereinander stehen, so dass sich für den Anwender die Darstellung eines mehrzeiligen Texts in verschiedenen Farben und Fonts ergibt. Der Benutzer hat die Möglichkeit, über einen Mausklick eine Eingabezeile (als Top-Level-Element) zu öffnen, und dort einen Text einzugeben, der anschließend einen Teil des vorher angezeigten Textes verändert; da die Textlänge sich unterscheiden kann, ergeben sich daraus neue Abmessungen für einzelne Labels und auch den umgebenden (Haupt-)Frame.

Soweit alles gut. ABER: Ich möchte, dass dieser Frame IMMER zentriert auf dem Bildschirm erscheint - auch nach Textänderungen. Da ich keine Methode oder Option "center()" o.ä. gefunden habe, die einen Top-Level-Frame auf dem Bildschirm zentriert, muss ich das manuell machen. Ist ja im Prinzip auch kein Problem, da sich die Bildschirmauflösung ermitteln lässt und - bei Kenntnis des Abmessunen des Frames - die entsprechenden Positonierungskoordinaten ebenfalls berechnen lassen.

Wenn ich es weiter richtig sehe, dann steht zur Positionierung auf dem Bildschirm aber nur die geometry()-Methode zur Verfügung, die ZWINGEND nicht nur die Angabe z.B. der linken oberen Ecke des zu positionierenden Frames verlangt, sondern eben auch seine Abmessunge. Die muss ich dafür kennen. ABER: Die Ermittlung dieser Abmessungen mittels .winfo_width() bzw. .winfo_height() schlägt fehl - selbst nach einem vorherigen Aufruf von .idle_tasks(), um die Darstellung vor dem Abfragen der Abmessungen auch tatsächlich auf dem neuesten Stand zu haben.

Vielleicht hast du dafür eine Lösung - das wäre super!
lunar

pütone hat geschrieben:Beispiel: Ich habe eine kleine Anwendung, die aus einem Frame und ein paar Labels besteht, die alle nahtlos untereinander stehen, so dass sich für den Anwender die Darstellung eines mehrzeiligen Texts in verschiedenen Farben und Fonts ergibt.
Mehrere Labels untereinander, um letztendlich nur eine Art HTML-Viewer oder Highlighting-Widget zu implementieren? Entweder habe ich deine GUI fundamental missverstanden, oder Tk ist schlimmer als ich dachte...
ABER: Ich möchte, dass dieser Frame IMMER zentriert auf dem Bildschirm erscheint - auch nach Textänderungen.
Will das dein User auch? Der durchschnittliche User ist nämlich im Allgemeinen in der Lage, seinen Desktop selbstständig zu organisieren, und mag nicht erfreut darüber sein, dass eine Anwendung im eine Organisation aufzwingen möchte.

Aber diese Sache funktioniert sowieso nicht wirklich. Für die Platzierung ist nämlich letztendlich der Window Manager zuständig, und der kann Fenster so platzieren, wie er will. Außerdem gibt es durchaus Window-Manager, die keine gleichmäßig breiten Rahmen um Fenster zeichnen, so dass das Fenster zwar in der Mitte steht, aber den Inhalt des Fensters wegen der unterschiedlichen Rahmenbreiten verschoben. Auf diese Rahmenbreite hast du aber keine Einfluss und auch keine Zugriff, weswegen du aus einer Anwendung heraus die tatsächliche Darstellung der Arbeitsfläche gar nicht beeinflussen kannst.
Da ich keine Methode oder Option "center()" o.ä. gefunden habe,
Die wäre auch sinnlos, da sie kaum mehr als eine Empfehlung an den Window Manager ist.
Vielleicht hast du dafür eine Lösung - das wäre super!
Lass dem Window Manager, was des Window Manager's ist. Die Platzierung geht dich nichts an, dass macht der Window Manager. Wenn dein Fenster in die Mitte soll, dann nutze dafür die Möglichkeiten deines Window Managers.
BlackJack

@pütone: Das hier funktioniert zumindest unter Linux, mit kwin als Fentermanager: http://paste.pocoo.org/show/24736/

Wie man sieht braucht man bei `geometrie()` die Fenstergrösse gar nicht anzugeben.
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

BlackJack hat geschrieben:Wie man sieht braucht man bei `geometrie()` die Fenstergrösse gar nicht anzugeben.
Ja, das löst wirklich pütones Problem in seinem Thread 'Problem mit Größenanpassung eines Frames'.
MfG
HWK
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

@BlackJack:
Super, das war's. Bisher hatte ich nirgends einen Hinweis darauf gefunden, dass man im geometry-String Teile weglassen kann - auf die Idee, es einfach mal zu probieren, war ich auch nicht gekommen. Lässt man die Angabe für die Abmessungen weg, dann funktioniert das Zentrieren tadellos.

@lunar:
Du erzählst seltsame Sachen, um es mal milde auszudrücken.
Will das dein User auch?
Ja, der User will das. Ich bin der User, und ich will die zentrierte Darstellung.
Aber diese Sache funktioniert sowieso nicht wirklich.
Natürlich funktioniert die Sache! Der Tipp von BlackJack ist da eindeutig. Probier das Programm doch aus. Wieso stellst du Behauptungen auf, die schlicht nicht stimmen?

Du scheinst von Tk wirklich nicht viel zu verstehen. Tk liefert Methoden zur Ermittlung der Bildschirmauflösung und der Fensterabmessungen - daraus eine zentrierte Positionierung zu ermitteln ist doch ein Leichtes, sofern die gelieferten Daten über die Fensterabmessungen korrekt geliefert werden.
Die wäre auch sinnlos, da sie kaum mehr als eine Empfehlung an den Window Manager ist.
Was hat das mit dem Window Manager zu tun? Tk hat Methoden zur absoluten Bildschirmpositionierung - der Window Manager hat da nix zu melden. Und: Warum soll ich meinen Window Manager ändern, wenn es mir nur um die zentrierte Darstellung EINES Fensters geht? Ansonsten bin ich mit der Einstellung meines WM ja zufrieden. "Sinnlos" ist hier jedenfalls nichts.
Die Platzierung geht dich nichts an
Mach du mit deinem WM, was du für richtig hältst. Aber wieso willst du anderen vorschreiben, dass sie ein Anwendungsfenster nicht gerne zentriert auf dem Bildschirm hätten und es Tk möglich macht, dies zu realisieren.

Statt dieses langen, überflüssigen Beitrages wäre eine kurze, klare Lösung mehr Wert gewesen. Siehe BlackJack.
BlackJack

Tk hat keine Methoden um die Bildschirmauflösung fest zu stellen und das Fenster auf eine gegebene Position zu verschieben, sondern nur welche, die den WM danach fragen bzw. darum bitten.

Ein rationaler Grund für einen WM diese Bitte zu ignorieren, ist zum Beispiel, wenn er weiss, dass sich der Screen über zwei nebeneinander angeordnete Bildschirme erstreckt. Dort Fenster mittig zu platzieren ist nämlich äusserst ungünstig für den Benutzer, weil er dann je ein halbes Fenster auf jedem Monitor hat.
lunar

pütone hat geschrieben:
Will das dein User auch?
Ja, der User will das. Ich bin der User, und ich will die zentrierte Darstellung.
Ich wusste ja nicht, dass das eine works-for-me-only-Lösung sein sollte... ich ging von der Annahme aus, dass dein Programm veröffentlicht werden sollte. In diesem Fall sollte man sich GUI-Styleguides halten, und diese empfehlen eigentlich immer, auf feste Positionsvorgaben zu verzichten, um den User die Organisation seines Desktops nicht aufzuzwingen.
Aber diese Sache funktioniert sowieso nicht wirklich.
Natürlich funktioniert die Sache! Der Tipp von BlackJack ist da eindeutig. Probier das Programm doch aus. Wieso stellst du Behauptungen auf, die schlicht nicht stimmen?
Er funktioniert unter KWin. Ein einziger Window-Manager ist unter Linux kaum "eindeutig", immerhin gibt es deren dutzende. larswm z.B. verfolgt ein ganz anderes Konzept des Fenstermanagement. Dort gibt es überhaupt gar keine zentrierten Fenster. Und auch KWin kann ich ohne weiteres dazu bringen, deine vorgegebene Position zu ignorieren.
Du scheinst von Tk wirklich nicht viel zu verstehen.
Ich verstehe tatsächlich wenig von Tk. Nur ist Tk selbst in dieser Sache irrelevant. Die endgültige Positionierung wird unter X11 nämlich durch den Window-Manager festgelegt.
Tk liefert Methoden zur Ermittlung der Bildschirmauflösung und der Fensterabmessungen - daraus eine zentrierte Positionierung zu ermitteln ist doch ein Leichtes, sofern die gelieferten Daten über die Fensterabmessungen korrekt geliefert werden.
Weder sind diese Daten immer korrekt, noch ist die berechnete Position des Fensters unter X11 endgültig. An die Abmessungen von Titelleiste und Fensterrahmen kommt die Anwendung unter X11 nämlich gar nicht heran. Folglich ist die berechnete Position höchstwahrscheinlich falsch. Das ist aber letztendlich auch egal, denn sie ist sowieso nur eine Empfehlung an den Fenstermanager. Zwar folgen die meisten Fenstermanager dieser Empfehlung, nur sind sie eben nicht dazu gezwungen. Dazu aus der Dokumentation von Qt4:
On X11, a window does not have a frame until the window manager decorates it. This happens asynchronously at some point in time after calling QWidget::show() and the first paint event the window receives, or it does not happen at all. Bear in mind that X11 is policy-free (others call it flexible). Thus you cannot make any safe assumption about the decoration frame your window will get. Basic rule: There's always one user who uses a window manager that breaks your assumption, and who will complain to you.

Furthermore, a toolkit cannot simply place windows on the screen. All Qt can do is to send certain hints to the window manager. The window manager, a separate process, may either obey, ignore or misunderstand them. Due to the partially unclear Inter-Client Communication Conventions Manual (ICCCM), window placement is handled quite differently in existing window managers.

X11 provides no standard or easy way to get the frame geometry once the window is decorated. Qt solves this problem with nifty heuristics and clever code that works on a wide range of window managers that exist today. Don't be surprised if you find one where QWidget::frameGeometry() returns wrong results though.

Nor does X11 provide a way to maximize a window. QWidget::showMaximized() has to emulate the feature. Its result depends on the result of QWidget::frameGeometry() and the capability of the window manager to do proper window placement, neither of which can be guaranteed.
Was hat das mit dem Window Manager zu tun? Tk hat Methoden zur absoluten Bildschirmpositionierung - der Window Manager hat da nix zu melden.
Der Window Manager ist immer für die Positionierung zuständig! Er kann Fenster letztendlich positionieren, wie er möchte! Unter Windows folgt der WM dem Fenster, unter Linux ist er aber keineswegs dazu gezwungen. Die "absolute Bildschirmplatzierung" in Tk ist mehr oder weniger eine Illusion. Absolute Bildschirmplatzierung gibt es unter X11 nicht, zumindest nicht auf Applikationsebene.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

@BlackJack & lunar:

Danke für die Nachilfe zum Thema "Window Manager". Wieder was dazu gelernt.

Da habe ich sozusagen Glück oder zufällig den richtigen Window Manager, der die zentrierte Ausgabe meiner kleinen Anwendung möglich macht.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Nachtrag @lunar:
Und auch KWin kann ich ohne weiteres dazu bringen, deine vorgegebene Position zu ignorieren.
Das hat mich neugierig gemacht. Zumindest mit den Einstellungen, die über das Kontrollzentrum hinsichtlich der Platzierung von Fenstern möglich sind, ist es mir nicht gelungen dafür zu sorgen, dass meine zentrierte Anwendung woanders als in der Bildschirmmitte angezeigt wird.

Auch wenn es nichts mit Python zu tun hat: WIE würdest du denn KWin dazu bringen, dass meine Anwendung nicht mehr zentriert ausgegeben wird?
Antworten