我编写了一种算法,该算法允许ttk.Frame
将多个按钮包装在其中,以便当按钮水平占据过多空间时,受影响的按钮将自动重新定位到下一行。调整此ttk.Frame的大小时,还将显示这种按钮环绕行为。我将其称为FrameButton
类。
此小部件仍存在问题:
我该如何规避/克服上述问题?谢谢。
FrameButtons.py
#!/usr/bin/python3.6.5
# -*- coding: utf-8 -*-
#Load python3 modules
import tkinter as tk
import tkinter.ttk as ttk
import platform
class FrameButtons(ttk.Frame):
def __init__(self, master, **options):
background = master.winfo_toplevel().cget('background')
style = options.pop( 'style', ttk.Style() )
background = options.pop( 'background', background )
borderwidth = options.pop( 'borderwidth', 0 )
relief = options.pop( 'relief', 'flat' )
texts = options.pop( 'texts', ['0'] )
textwidth = options.pop( 'textwidth', 10 )
debug = options.pop( 'debug', False )
master.update_idletasks()
masterwidth = master.winfo_width()
masterwidth2 = masterwidth - borderwidth*2
print('masterwidth, w/border = ', masterwidth, masterwidth2)
width = masterwidth2
super().__init__( master, style='main.TFrame', width=width,
borderwidth=borderwidth, relief=relief )
self.grid( row=0, column=0, sticky='nsew' )
master.rowconfigure(0, weight=1)
master.columnconfigure(0, weight=1)
self.parent = master
self.style = style
self.texts = texts
self.bg = None
self.background = background
self.borderwidth = borderwidth
self.relief = relief
self.textwidth = textwidth
self.debug = debug
self.buttonframes = {}
self.buttons = {}
#Color code borders
if self.debug:
if platform.system() == 'Linux':
print('Linux')
bg = []
with open('/etc/X11/rgb.txt') as f:
lines = f.readlines()[30::10]
for line in lines:
color = line.replace('\t\t',' ').splitlines()[0]\
.split()[3]
#print('color = ', color)
invalid = ['ghost','floral','old','antique','papaya',
'blanched','peach','navajo','lemon','alice',
'cornflower','slate','light','royal', 'dark',
'mint','misty','dim','midnight','medium','dodger',
'deep','sky','steel','pale','rosy','indian',
'saddle','sandy','DebianRed', 'spring','forest',
'sea','lawn','cadet']
if color not in invalid:
bg.append( color )
self.bg = bg
else:
print('non-Linux')
self.bg = [ 'yellow', 'red','blue', 'grey','cyan','orange',
'black','gold','magenta','silver','maroon', 'salmon',
'honeydew','hotpink','indigo','ivory','khaki',
'lavender', 'lawn green', 'light blue','lime',
'midnight blue', 'olive']
else:
#no debug
self.bg = [ str(x).replace( str(x), background )
for x in range( len(texts) ) ]
print(self.bg)
self._setStyle()
self._createWidgets()
self._setBindings()
def _setStyle( self ):
self.style.configure( 'main.TFrame', background=self.background,
borderwidth=self.borderwidth,
relief=self.relief )
self.style.configure( 'buttons.TFrame', background=self.background )
self.style.configure( 'b.TButton', justify=tk.CENTER,
width=self.textwidth )
def _createButtonFrame( self, r ):
self.buttonframes[r] = tk.Frame( self, background=self.bg[r],
borderwidth=self.borderwidth,
relief=self.relief )
self.buttonframes[r].pack( anchor='w' )
def _createButton( self, r, b):
self.buttons[b] = ttk.Button( self, text=b, style='b.TButton' )
self.buttons[b].pack( in_=self.buttonframes[r], anchor='w', side='left')
self.buttons[b].update_idletasks()
def _updateButtonFrame( self, r):
return self.buttonframes[r].winfo_reqwidth()
def _createWidgets( self ):
wlimit = self.cget('width')
print('wlimit = ', wlimit)
self._createWidgets2( wlimit)
def _createWidgets2( self, wlimit ):
t_width=0; r=0; i=0
self._createButtonFrame( r )
r +=1
for b in self.texts:
if t_width <= wlimit:
self._createButton( r-1, b )
i += 1
t_width = self._updateButtonFrame( r-1 )
if self.debug: print( 'r={}, i={}, t_width={}'
.format( r-1, i-1, t_width ) )
# if buttons row width exceeded wlimit
if t_width > wlimit:
if self.debug: print('t_width > wlimit ({})'.format(wlimit) )
#remove button
self.buttons[b].pack_forget()
i -= 1
self._createButtonFrame( r )
r += 1
# create button
self._createButton(r-1, b)
i += 1
# update t_width
t_width = self._updateButtonFrame( r-1 )
if self.debug: print( 'r={}, i={}, t_width={}'
.format( r-1, i-1, t_width ) )
def _setBindings(self):
self.bind( '<Configure>', self._configButtonFrame )
def _configButtonFrame (self, event):
self.parent.update_idletasks()
wlimit = self.parent.winfo_width() - self.borderwidth*2
#print('wlimit = ', wlimit)
#remove old ButtonFrame widgets
self._cleanup()
self._createWidgets2( wlimit )
def _cleanup(self):
for k in self.buttons.keys():
self.buttons[k].destroy()
self.buttons.clear()
for k in self.buttonframes.keys():
self.buttonframes[k].destroy()
self.buttonframes.clear()
if __name__ == "__main__":
root = tk.Tk()
root.geometry( '102x500+10+0' )
borderwidth = 10
width = 100
minwidth = width+borderwidth*2; print('minwidth =', minwidth)
root.minsize( minwidth, 300)
texts = [ str(x) for x in range(20) ]
app = FrameButtons( root, background='pink', borderwidth=borderwidth,
relief=tk.RAISED, texts=texts, textwidth=2,
debug=True )
root.mainloop() # Start Dynamic part of program to handle Tk events
答案 0 :(得分:1)
最好的办法是停止在每个<Configure>
事件上创建新的窗口小部件。创建一次,然后仅在计算出需要移动它们时才移动它们。当我将主窗口的大小调整到足以创建一行的大小时,根据我执行调整大小的速度,您的代码将创建200至2000个按钮或更多的按钮。
您可能要考虑使用grid
而不是pack
,因为grid
不需要为每一行创建内部框架。
这是一个简单又肮脏的例子来说明这个概念。它没有经过太多测试,但似乎可以正常工作:
import tkinter as tk
import tkinter.ttk as ttk
class FrameButtons(ttk.Frame):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.buttons = []
self.bind("<Configure>", self._redraw)
def _redraw(self, event=None):
maxwidth = self.winfo_width()
row = column = rowwidth = 0
for button in self.buttons:
# will it fit? If not, move to the next row
if rowwidth + button.winfo_width() > maxwidth:
row += 1
column = 0
rowwidth = 0
rowwidth += button.winfo_width()
button.grid(row=row, column=column)
column += 1
def add_button(self, *args, **kwargs):
'''Add one button to the frame'''
button = ttk.Button(self, *args, **kwargs)
self.buttons.append(button)
self._redraw()
if __name__ == "__main__":
root = tk.Tk()
button_frame = FrameButtons(root)
button_frame.pack(side="top", fill="x", expand=False)
for i in range(20):
button_frame.add_button(text=str(i))
root.mainloop()