2017-12-07 9 views
2

Ich möchte ein Rasterlayout erstellen, mit einem Raster, das die erste Zeile füllt, bis kein Platz mehr im Fenster ist, und Elemente dynamisch in die darunter liegende Zeile verschieben (z. B. Zeilenumbruch) . Wenn die Fensterbreite angepasst wird, passt sich das Raster an. Die Größenänderung der Boxen ist nicht erwünscht. Ich beabsichtige, die Größe jeder kleinen Box beizubehalten, aber zu ändern, wo das Layout jede Box platziert.Tkinter Grid Dynamisches Layout

Ich stelle mir vor, dass diese Funktionalität möglich ist, indem man die Breite des Rahmens misst, und wenn die (Anzahl der Kästchen) * (Breite jedes Kästchens) die Breite überschreitet, gehe zur nächsten Reihe. Ich habe mich nur gefragt, ob es einen besseren Weg gibt, den ich nicht verstehe.

Wenn das obige die einzige Option ist, was ist der beste Weg, um das zu aktualisieren? Muss ich ein Event auf Fenstergröße oder etwas einstellen? Es scheint, als müsste ich einen Layout-Manager nicht mehr nachbearbeiten, so fühlt sich das an. Ich möchte nur prüfen, ob eine ähnliche Funktionalität bereits eingebaut ist. Grid scheint ein leistungsfähiger Layout-Manager zu sein, aber ich konnte diese Option nicht finden.

Die folgenden Bilder beschreiben das Verhalten, das ich mit dem gleichen Satz von 6 Boxen auf einem einzigen Rahmen mit Raster-Layout verwenden möchte.

Fenster ist breit genug, um alle 6 Boxen zu halten, so dass sie alle in Zeile 1 passen. Sie passen sich dann als Fenstergröße an. enter image description here

enter image description here enter image description here

+1

Das ist keine Option in tkinter. Sie müssten genau das tun, was Sie erwähnt haben: Überwachen Sie die Fenstergröße und ändern Sie sie nach Bedarf neu. Es würde nicht sehr viel Code erfordern. – Novel

+0

Und wie sollte ich die Fenstergröße für eine Änderung überwachen? Ist es negativ, dies zu tun? Wenn es nicht empfohlen wird (da es nicht eingebaut ist), könnte ich eine andere Lösung finden, aber wenn es ziemlich Standard ist, so etwas zu tun, kann ich es sicher herausfinden – Tarm

+1

Verwenden Sie das Ereignis "". – Novel

Antwort

5

Wenn Sie planen, jedes Feld auf zwingen eine einheitliche Größe zu sein, die einfachste Lösung ist, den Text-Widget als Behälter zu verwenden, da es die eingebaute Fähigkeit zu wickeln hat.

Hier ist ein Arbeitsbeispiel. Klicken Sie auf die Schaltfläche "Hinzufügen", um weitere Felder hinzuzufügen. Verändern Sie die Größe des Fensters, um zu sehen, dass es automatisch umschlingt, wenn das Fenster vergrößert und verkleinert wird.

import Tkinter as tk 
import random 

class DynamicGrid(tk.Frame): 
    def __init__(self, parent, *args, **kwargs): 
     tk.Frame.__init__(self, parent, *args, **kwargs) 
     self.text = tk.Text(self, wrap="char", borderwidth=0, highlightthickness=0, 
          state="disabled") 
     self.text.pack(fill="both", expand=True) 
     self.boxes = [] 

    def add_box(self, color=None): 
     bg = color if color else random.choice(("red", "orange", "green", "blue", "violet")) 
     box = tk.Frame(self.text, bd=1, relief="sunken", background=bg, 
         width=100, height=100) 
     self.boxes.append(box) 
     self.text.configure(state="normal") 
     self.text.window_create("end", window=box) 
     self.text.configure(state="disabled") 

class Example(object): 
    def __init__(self): 
     self.root = tk.Tk() 
     self.dg = DynamicGrid(self.root, width=500, height=200) 
     add_button = tk.Button(self.root, text="Add", command=self.dg.add_box) 

     add_button.pack() 
     self.dg.pack(side="top", fill="both", expand=True) 

     # add a few boxes to start 
     for i in range(10): 
      self.dg.add_box() 

    def start(self): 
     self.root.mainloop() 

Example().start() 
+1

Schöne Lösung. Mir gefällt, wie ungleich große Widgets optimal einpacken. – Novel

+0

Ich mag diese Lösung wirklich! Sehr elegant und einfach. Es gibt mir definitiv, was ich will, und weil ich die gefürchtete Breitenmessung vermeide, werde ich das als eine Lösung bezeichnen. Auch, wie @Novel bereits erwähnt hat, handhabt es ungleiche Schachteln gleichermaßen (wenn auch nicht so ästhetisch). Das ist perfekt, Herr, danke. – Tarm

4

Hier ist ein funktionierendes Beispiel:

import Tkinter as tk 

class AutoGrid(tk.Frame): 
    def __init__(self, master=None, **kwargs): 
     tk.Frame.__init__(self, master, **kwargs) 
     self.columns = None 
     self.bind('<Configure>', self.regrid) 

    def regrid(self, event=None): 
     width = self.winfo_width() 
     slaves = self.grid_slaves() 
     max_width = max(slave.winfo_width() for slave in slaves) 
     cols = width // max_width 
     if cols == self.columns: # if the column number has not changed, abort 
      return 
     for i, slave in enumerate(slaves): 
      slave.grid_forget() 
      slave.grid(row=i//cols, column=i%cols) 
     self.columns = cols 

class TestFrame(tk.Frame): 
    def __init__(self, master=None, **kwargs): 
     tk.Frame.__init__(self, master, bd=5, relief=tk.RAISED, **kwargs) 

     tk.Label(self, text="name").pack(pady=10) 
     tk.Label(self, text=" info ........ info ").pack(pady=10) 
     tk.Label(self, text="data\n"*5).pack(pady=10) 

def main(): 
    root = tk.Tk() 
    frame = AutoGrid(root) 
    frame.pack(fill=tk.BOTH, expand=True) 

    TestFrame(frame).grid() # use normal grid parameters to set up initial layout 
    TestFrame(frame).grid(column=1) 
    TestFrame(frame).grid(column=2) 
    TestFrame(frame).grid() 
    TestFrame(frame).grid() 
    TestFrame(frame).grid() 
    root.mainloop() 

if __name__ == '__main__': 
    main() 

Hinweis dies die rowspan und Column Eigenschaften der Grid-Manager ruinieren.

+0

Ich habe den Code getestet und es funktioniert definitiv und ist eine gültige Lösung. Ich habe das Upvoted überarbeitet und werde es vielleicht in Zukunft verwenden, aber die Antwort von Bryan ist die bessere Lösung, da die Messung der Breite vermieden wird und die Layouter einfach ihre Arbeit machen lassen. Danke für die tolle Antwort! – Tarm