Я написал алгоритм, который позволяет 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