grid und pack mischen

Fragen zu Tkinter.
sedi
User
Beiträge: 104
Registriert: Sonntag 9. Dezember 2007, 19:22

Hallo zusammen,

ich habe nun eine etwas aufwändigere GUI zu bauen und hatte dabei Schwierigkeiten mit den Geometriemanagern.

Wann und wie lassen sich die beiden mischen? Ich habe im Netz natürlich darüber gelesen und überwiegend war die Rede sie nicht zu mischen - manche sagen aber, dass dies problemlos möglich ist. Meine ersten Gehversuche waren erst mal erfolgreich (siehe Beispiel unten). Das umfangreichere Beispiel versagte dann. Wann also lassen sich die Manager mischen? Wisst ihr dazu irgendeine Quelle, die das genau beschreibt?

Das nachfolgende, einfache Beispiel funktioniert

Code: Alles auswählen

import logging
Logger = logging.getLogger("root")


class Page(tk.Frame):
    def __init__(self, master, **kwargs):
        tk.Frame.__init__(self, master, **kwargs)
        
        self.headline = ""
        
        self.toppackframe = tk.Frame(self)
        self.toppackframe.pack(side=tk.TOP)
        
        self.contentgrid = tk.Frame(self)
        self.pack()
        
        self.bottompackframe = tk.Frame(self)
        self.bottompackframe.pack(side=tk.BOTTOM)
        Logger.debug("init done")
    
    def setup_header(self, headline=""):
        Logger.debug("setup_header startet [headline=%s]", headline)
        
        if headline:
            self.headline = headline
            tk.Label(self.toppackframe,
                     text=self.headline).pack(side=tk.TOP)
    
        Logger.debug("setup_header fertig")
        
    def setup_bottom(self):
        Logger.debug("setup_bottom startet")
        
        self.back = tk.Button(self.bottompackframe,
                              text="<< Back")
        self.cancel = tk.Button(self.bottompackframe,
                              text="Cancel")
        self.forward = tk.Button(self.bottompackframe,
                              text="Next >>")
        self.back.pack(side=tk.RIGHT)
        self.cancel.pack(side=tk.RIGHT)
        self.forward.pack(side=tk.RIGHT)
        
        Logger.debug("setup_header fertig")

    def setup(self, headline=""):
        Logger.debug("setup startet [headline=%s]", headline)
        
        self.setup_header(headline)
        self.setup_bottom()
        
        Logger.debug("setup_header fetig")
        

class MyPage(Page):
    def __init__(self, master, **kwargs):
        Page.__init__(self, master, **kwargs)
    
    def setup_content(self, sectionheader=""):
        Logger.debug("setup_content [sectionheader=%s]",
                     sectionheader)
        if sectionheader:
            Logger.debug("fuege Headerlabel ein")
            lbl = tk.Label(self.contentgrid, text=sectionheader)
            lbl.grid(column=0, row=0, columnspan=10)
        
        zeilen = range(10)
        spalten = range(10)
        for zeile in zeilen:
            for spalte in spalten:
                zelle = "Zelle (%s,%s)" % (spalte, zeile)
                l = tk.Label(self.contentgrid, text=zelle)
                l.grid(column=spalte, row=zeile+1)
                Logger.debug("%s eingefuegt", zelle)

        Logger.debug("setup_content fertig")
    
    def setup(self, headline="", sectionheader=""):
        Logger.debug("setup startet [headline=%s, sectionheader=%s]",
                     headline, sectionheader)
        self.setup_header(headline)
        self.setup_content(sectionheader)
        self.setup_bottom()
        Logger.debug("setup fertig")



def example():
    Logger.debug(" ######### START ##############")
    app=tk.Tk()

    page = MyPage(app)
    page.setup("Titel", "Sektion")
    page.pack()

    app.mainloop()


if __name__ == "__main__":
    logging.basicConfig(format="[%(lineno)4d:%(name)s:%(funcName)s]" + \
    " %(message)s", level=10)
    example()
CU sedi
----------------------------------------------------------
Python 3.5; Python 3.6
LinuxMint18
BlackJack

@sedi: Man sollte die gar nicht mischen. Wer empfiehlt denn so etwas? Das Ergebnis ist bestenfalls schwer vorhersehbar und im schlechtesten Fall hängt sich das Programm auf weil sich die beiden Manager nicht einig werden können wie das Ergebnis aussehen soll. Aus dem gleichen Grund sollte man bei `pack()` immer nur eine Richtung verwenden.

Edit: Ergänzend dazu aus einem Bugtracker-Beitrag: „In TCL/Tk 8.6b2 and later there is a change: more strict approach to geometry
managers. From now on "mixing" pack and grid is considered an error.”

Das heisst man wird das in zukünftigen Tk-Versionen direkt als Fehler um die Ohren gehauen bekommen wenn man die beiden mischt.
Ene Uran
User
Beiträge: 125
Registriert: Sonntag 17. September 2006, 20:14
Wohnort: Hollywood

Man kann grid() und place() mischen. Man kann auch zwei verschiedene tk.Frame() benuetzen, eins fuer grid() und eins fuer pack().
Atomkraftwerkaktienbesitzer
BlackJack

@Ene Uran: Man kann sich auch in den Kopf schiessen. In dem Sinne kann man auch `pack()` und `grid()` mischen. Man sollte das halt bloss niemals tun, und wie gesagt in aktuellen Tk-Versionen *kann* man das *nicht* mehr tun. Es führt zu einer Fehlermeldung.
sedi
User
Beiträge: 104
Registriert: Sonntag 9. Dezember 2007, 19:22

Hallo,

man kann die beiden schon "mischen", wobei mischen dabei mit Vorsicht zu genießen ist:

In ein und demselben Frame darf man keinesfalls mischen, dh. Objekte verwenden die auf

Code: Alles auswählen

pack()
und

Code: Alles auswählen

grid()
zurückgreifen.

Allerdings wurde ich durch eine Seite im Internet (leider weiß ich die Adresse nicht mehr) darauf aufmerksam, dass man Subframes durch verschiedenen Geometriemanager darstellen lassen kann.

Allerdings gab es dabei auch noch Schwierigkeiten - meine Lösung war: Wollte ich zwei Bereiche mit verschiedenene Geometriemanagern
berechnen lassen, dann habe ich SubSubframes verwendet - das läuft problemlos:

Code: Alles auswählen


+-------------------------------+
| Outer Frame (pack)            |
|   +-----------------------+   |
|   | Subframe              |   |
|   |   +---------------+   |   |
|   |   | SubSub (grid) |   |   |
|   |   |               |   |   |
|   |   +---------------+   |   |
|   +-----------------------+   |
|   +-----------------------+   |
|   | Subframe              |   |
|   |   +---------------+   |   |
|   |   | SubSub (pack) |   |   |
|   |   |               |   |   |
|   |   +---------------+   |   |
|   +-----------------------+   |
+-------------------------------+
CU sedi
----------------------------------------------------------
Python 3.5; Python 3.6
LinuxMint18
bb1898
User
Beiträge: 200
Registriert: Mittwoch 12. Juli 2006, 14:28

Ganz klar ist mir das noch nicht:
- die beiden Subframes werden mit pack() in den Outer Frame eingepasst, richtig?
- in den oberen Subframe wird ein SubSub mit grid() hineingepackt, in den unteren ein anderer SubSub mit pack(), noch richtig?
- und die Steuerelemente in den SubSub-Frames? Oben mit grid(), unten mit pack()?
Es wäre schon ein bisschen hässlich, wenn man so was mit tkinter überhaupt nicht könnte, "die andern können's doch auch!".
BlackJack

@bb1898: Man kann in einem Container-Widget nur `pack()` *oder* `grid()` verwenden. Beides kann AFAIK auch kein anderes GUI-Toolkit. Man muss sich immer für *einen* Layoutmanager entscheiden innerhalb eines Containerwidgets. Die Verwirrung bei Tk gegenüber anderen GUI-Toolkits stellt sich ja auch nur weil Layouts in Tk keine eigenständigen Objekte sind sondern die Widgets immer beide Sätze von Methoden haben, egal für welches man sich entscheidet. Bei Qt beispielsweise hat man das Problem erst gar nicht, weil man da ein Layout hinzufügt, und angemeckert wird wenn man noch einen weiteres hinzufügt. Und man fügt Widgets über Methoden auf dem Layout hinzu und nicht über Methoden auf den Widgets selbst.
bb1898
User
Beiträge: 200
Registriert: Mittwoch 12. Juli 2006, 14:28

BlackJack hat geschrieben:Man muss sich immer für *einen* Layoutmanager entscheiden innerhalb eines Containerwidgets. Die Verwirrung bei Tk gegenüber anderen GUI-Toolkits stellt sich ja auch nur weil Layouts in Tk keine eigenständigen Objekte sind sondern die Widgets immer beide Sätze von Methoden haben, egal für welches man sich entscheidet. Bei Qt beispielsweise hat man das Problem erst gar nicht, weil man da ein Layout hinzufügt, und angemeckert wird wenn man noch einen weiteres hinzufügt. Und man fügt Widgets über Methoden auf dem Layout hinzu und nicht über Methoden auf den Widgets selbst.
Stimmt. Aber der Witz ist eben "innerhalb eines Containerwidgets".
Ene Uran
User
Beiträge: 125
Registriert: Sonntag 17. September 2006, 20:14
Wohnort: Hollywood

An jabba the hutt ---
Man kann sich auch in den Kopf schiessen. In dem Sinne kann man auch `pack()` und `grid()` mischen.
Der Vorschlg war: "Man kann place() und grid() mischen."
Atomkraftwerkaktienbesitzer
BlackJack

@Ene Uran: Das ist ja noch dämlicher als sich in den Kopf zu schiessen. Da können nur verstrahlte drauf kommen… :twisted:
Ene Uran
User
Beiträge: 125
Registriert: Sonntag 17. September 2006, 20:14
Wohnort: Hollywood

Code: Alles auswählen

import tkinter as tk

root = tk.Tk()

frame1 = tk.Frame(root, bg='brown', width=400, height=200)
frame1.grid()

# you can mix grid() and place()
frame2 = tk.Frame(root, bg='yellow', width=300, height=200)
frame2.place(x=100, y=0)

root.mainloop()
Atomkraftwerkaktienbesitzer
BlackJack

Sag ich doch, ist 'ne blöde Idee. Was ja nicht mal am Mischen liegt, sondern das `place()` an sich schon keine gute Idee ist.
Ene Uran
User
Beiträge: 125
Registriert: Sonntag 17. September 2006, 20:14
Wohnort: Hollywood

Ach du Lieber!
Atomkraftwerkaktienbesitzer
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

BlackJack hat geschrieben:Sag ich doch, ist 'ne blöde Idee. Was ja nicht mal am Mischen liegt, sondern das `place()` an sich schon keine gute Idee ist.
Was würdest Du denn als vergleichbar einfache Methode vorschlagen, um ein Objekt pixelgenau auf einer grid-Grundstruktur zu platzieren (z.B. um sie dann mit der Maus zu verschieben)?
Wenn die nicht durch place from Grid-Manager getrennt wird, kann man sie in aller Regel nicht so unkompliziert verschieben, ohne dass das Gitter verzerrt wird.
Diese Nachricht zersört sich in 5 Sekunden selbst ...
BlackJack

@Michael Schneider: Gar nichts. Es ein lassen.
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

Das ist aber keine Lösung für das Problem.
Diese Nachricht zersört sich in 5 Sekunden selbst ...
BlackJack

@Michael Schneider: Welches Problem?
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

Jenes, welches ich in meinem letzen Beitrag beschrieb.
Muss ich dafür jetzt ein praktisches Anwendungsbeispiel nennen, weil Du auf dem Standpunkt stehst, dass man Probleme nur betrachten soll, wenn sie sich einem konkret stellen? Das war eine rethorische Frage und soll jetzt nicht vom Thema ablenken.

Es ging um ein Widget, das man mit der Maus über einen Grid-Hintergrund ziehen soll.
Diese Nachricht zersört sich in 5 Sekunden selbst ...
BlackJack

@Michael Schneider: Probleme die nur theoretisch bestehen sind nun mal keine die man lösen müsste. Natürlich ist `place()` immer die einfachste Lösung für Probleme die so formuliert sind das `place()` die einfachste Lösung ist. So findet man auch ganz viele tolle Anwendungen für ``global`` und `eval()` und ähnliches wovon man die Finger lassen sollte. ;-)
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

Lass bitte meine guten Freunde global, eval und alle anderen Als aus dem Spiel. :wink:

Du hast ja nicht ganz unrecht, dass man ein Szenario um einen Problemfall herumdichten kann - obwohl mir gerade nicht einfällt, wofür man global wirklich "braucht", außer um mal eben schnell eine akute Aktion durchzuführen.
Aber das bedeutet noch nicht, dass man etwas ganz sein lässt, nur weil die beste Lösung für ein Problem ein Konzept verwendet, das man persönlich ablehnt. Denn offiziell habe ich auch noch nicht gelesen, dass man place und einen anderen Manager nicht zusammen verwenden sollte oder in Zukunft nicht mehr darf.
Diese Nachricht zersört sich in 5 Sekunden selbst ...
Antworten