代码之家  ›  专栏  ›  技术社区  ›  Mathlight

Tkinter中的流体设计

  •  0
  • Mathlight  · 技术社区  · 5 年前

    我正试图用ttk(tkinter)创造一个有点“响应性”的设计。小部件的基本布局一点问题都没有,但让它随着程序的宽度而流畅是我无法实现的。在CSS中,我知道可以对所有容器说“float:left”,页面会根据屏幕大小进行调整。我还没有在Tkinter和frames中找到类似的东西。

    我的基本测试计划:

    #!/usr/bin/python3
    
    import tkinter
    from tkinter import ttk
    from ttkthemes import ThemedTk, THEMES
    
    class quick_ui(ThemedTk):
        def __init__(self):
            ThemedTk.__init__(self, themebg=True)
            self.geometry('{}x{}'.format(900, 150))
            self.buttons = {}
    
            self.frame1 = ttk.Frame(self)
            self.frame1.pack(side="left")
            self.frame2 = ttk.Frame(self)
            self.frame2.pack(side="left")
    
            #------------------------------------------------------- BUTTONS
            i = 0
            while (i < 5):
                i += 1
                self.buttons[i]= ttk.Button(self.frame1,
                                                text='List 1 All ' + str(i),
                                                command=self.dump)
                self.buttons[i].pack(side="left")
    
    
            while (i < 10):
                i += 1
                self.buttons[i]= ttk.Button(self.frame2,
                                                text='List 2 All ' + str(i),
                                                command=self.dump)
                self.buttons[i].pack(side="left")
    
        def dump(self):
            print("dump called")
    
    quick = quick_ui()
    quick.mainloop()
    

    这将创建一个包含10个按钮的窗口。 当我将窗口缩小到按钮不再适合屏幕时,我希望按钮显示在彼此下方

    因此,我所做的是添加一个调整大小的侦听器,并设置以下方法:

        def resize(self, event):
            w=self.winfo_width()
            h=self.winfo_height()
            # print("width: " + str(w) + ", height: " + str(h))
    
            if(w < 830):
                self.frame1.config(side="top")
                self.frame2.config(side="top")
    

    但是 Frame 没有财产 side ,这是该方法的一个参数 pack .所以这也没用。

    现在我迷路了。我花了很长时间在这方面,尝试网格和其他解决方案,但我觉得我错过了一个简单但非常重要的设置。

    0 回复  |  直到 5 年前
        1
  •  0
  •   Mathlight    4 年前

    我创建了一个(黑客的)解决方案,这是一个很好的解决方案,因为它是一个非常内部的程序。由于没有给出答案,我将在这里提供我的解决方案。它可能有很大的改进空间,但我希望这能给未来的人一些建议,让他们自己解决这个问题。

    #!/usr/bin/python3
    
    import re
    import sys
    import tkinter
    from tkinter import filedialog
    from tkinter import ttk
    from ttkthemes import ThemedTk, THEMES
    
    import subprocess
    import os
    from tkinter.constants import UNITS
    import json
    from functools import partial
    
    
    class quick_ui(ThemedTk):
    
        def __init__(self):
            ThemedTk.__init__(self, themebg=True)
            self.minsize(600, 250)
            self.elems = {}
            self.resize_after_id = None
    
    
            #------------------------------------------------------- Window menu bar contents
            self.menubar = tkinter.Menu(self)
            self.menubar.add_command(label="Open", command = self.dump)
            self.menubar.add_command(label="Refresh", command = self.dump)
            self.config(menu=self.menubar)
    
            # Theme menu
            self.themeMenu = tkinter.Menu(self.menubar, tearoff=0)
            self.menubar.add_cascade(label="Theme", menu=self.themeMenu)
            self.themeMenu.add_command(label="DEFAULT", command=partial(self.dump, "default"))
    
    
            #---------------------------------------------------------------------- top_frame
            self.top_frame = ttk.Frame(self)
            self.top_frame.pack( side = tkinter.TOP, expand='YES', fill='both', padx=10)
    
            self.top_top_frame = ttk.Frame(self.top_frame)
            self.top_top_frame.pack(side=tkinter.TOP, expand='YES', fill='both')
    
            self.top_bottom_frame = ttk.Frame(self.top_frame)
            self.top_bottom_frame.pack(side=tkinter.BOTTOM)
    
            self.top_bottom_top_frame = ttk.Frame(self.top_frame)
            self.top_bottom_top_frame.pack(side=tkinter.TOP)
    
    
            self.top_bottom_bottom_frame = ttk.Frame(self.top_frame)
            self.top_bottom_bottom_frame.pack(side=tkinter.BOTTOM)
    
            #------------------------------------------------------------------- bottom_frame
            self.bottom_frame = ttk.Frame(self, relief="sunken")
            self.bottom_frame.pack( side = tkinter.BOTTOM, 
                                    expand='YES', 
                                    fill='both', 
                                    padx=10, 
                                    pady=10 )
    
            #------------------------------------------------------- BUTTONS
            i = 0
            while (i < 15):
                self.elems[i]=ttk.Button(self.top_bottom_top_frame,
                                                    text='List All ' + str(i),
                                                    command=self.dump)
                i += 1
    
    
            self.label_test_strings1 = ttk.Label(self.top_top_frame, text='Test strings1')
            self.label_test_strings2 = ttk.Label(self.top_bottom_frame, text='Test strings2')
            self.label_test_strings4 = ttk.Label(self.top_bottom_bottom_frame, text='Test strings4')
    
            self.label_test_strings1.pack(side = tkinter.TOP)
            self.label_test_strings2.pack(side = tkinter.TOP)
            self.label_test_strings4.pack(side = tkinter.TOP)
    
    
            self.placeElems()
            # Setup a hook triggered when the configuration (size of window) changes
            self.bind('<Configure>', self.resize)
    
    
        def placeElems(self):
            for index in self.elems:
                self.elems[index].grid(row=0, column=index, padx=5, pady=5)
    
    
        # ------------------------------------------------------ Resize event handler
        def resize(self, event):
            # Set a low "time-out" for resizing, to limit the change of "fighting" for growing and shrinking
            if self.resize_after_id is not None:
                self.after_cancel(self.resize_after_id)
            self.resize_after_id = self.after(200, self.resize_callback)
    
    
        # ------------------------------------------------------ Callback for the resize event handler
        def resize_callback(self):
            # The max right position of the program
            windowMaxRight = self.winfo_rootx() + self.winfo_width()
    
            # Some basic declarations
            found = False
            willAdd = False
            maxColumn = 0
            currIndex = 0
            currColumn = 0
            currRow = 0
            counter = 0
            last_rootx = 0
            last_maxRight = 0
    
            # Program is still starting up, so ignore this one
            if(windowMaxRight < 10):
                return
    
            # Loop through all the middle bar elements
            for child in self.top_bottom_frame.children.values():
                # Calculate the max right position of this element
                elemMaxRight = child.winfo_rootx() + child.winfo_width() + 10
    
                # If we already found the first 'changable' child, we need to remove the following child's also
                if(found == True):
                    # Is the window growing?
                    if(willAdd == True):
                        # Check to see if we have room for one more object
                        calcMaxRight = last_maxRight + child.winfo_width() + 20
                        if(calcMaxRight < windowMaxRight):
                            maxColumn = counter + 1
                    # Remove this child from the view, to add it again later
                    child.grid_forget()
    
                # If this child doesn't fit on the screen anymore
                elif(elemMaxRight >= windowMaxRight):
                    # Remove this child from the view, to add it again later
                    child.grid_forget()
                    currIndex = counter
                    maxColumn = counter
                    currRow = 1
                    found = True
    
                else:
                    # If this child's x position is lower than the last child
                    # we can asume it's on the next row
                    if(child.winfo_rootx() < last_rootx):
                        # Check to see if we have room for one more object on the first row
                        calcMaxRight = last_maxRight + child.winfo_width() + 20
                        if(calcMaxRight < windowMaxRight):
                            child.grid_forget()
                            currIndex = counter
                            currColumn = counter
                            maxColumn = counter + 1
                            found = True
                            willAdd = True
    
                # Save some calculation data for the next run
                last_rootx = child.winfo_rootx()
                last_maxRight = elemMaxRight
                counter += 1
    
            # If we removed some elements from the UI
            if(found == True):
                counter = 0
                # Loop through all the middle bar elements (including removed ones)
                for child in self.top_bottom_frame.children.values():
                    # Ignore the elements still in place
                    if(counter < currIndex):
                        counter += 1
                        continue
    
                    # If we hit our maxColumn count, move to the next row
                    if(currColumn == maxColumn):
                        currColumn = 0
                        currRow += 1
    
                    # Place this element on the UI again
                    child.grid(row=currRow, column=currColumn, padx=5, pady=5)
                    currColumn += 1
                    counter += 1
    
    
        def dump(self):
            print("dump called")
    
    
    quick = quick_ui()
    quick.mainloop()