背景
本人不是处女座,但有着处女座的‘坏毛病’。tkinter原生的控件美感让我十分苦恼,于是我决定二次封装一些调用方便并且相对美观的控件。
1. LabelEntry(带标签的输入框)
本人最近开发tkinter可视化开发工具时,需要写一个实时改变控件属性的页面,其中就需要若干个Label+Entry(或者Button)阵列。简单而直接的办法就是Grid方法进行排列,但是调试过程十分麻烦,比如调边距、对齐方式、Entry的快捷输入等等。
于是就用一个Frame封装了Label、Entry、Button,可以将一个单键字典{“key”:"value"}传入,并保存value的数据类型,如果value是int型或float型,鼠标放到Entry控件中滚动,还可以调节值的大小。
上代码
from tkinter import Label,Entry,Frame,Tk,StringVar,Button
from tkinter.font import Font
from tkinter.colorchooser import askcolor
class LabelEntry():
def __init__(self,master, key,value,scrollChooser=True,colorChooser=False):
'''
:param master:
:param key: 键
:param value: 键值
:param callback: (func,args)元组,传递函数和参数
:param scrollChoose: 是否通过滚动鼠标来增加或减少数值
:param colorChooser: 是否添加颜色选择按钮
'''
self.__key_type=type(key).__name__
self.__value_type=type(value).__name__
self.__key=key
self.__callback=None
self.__value=StringVar(value=str(value))
self._frame=Frame(master)
self.__value_range=[-1000000,1000000,1]
self._label=Label(self._frame,text=str(key))
self._entry=Entry(self._frame,textvariable=self.__value,state='disabled')
self._btn = Button(self._frame, text='...')
self.style_config(colorChooser=colorChooser)
self.place = self.place_configure = self._frame.place
if scrollChooser:
self._entry.bind('<MouseWheel>',self.__scroll_change_value)
if colorChooser:
self._btn.config(command=self.__choose_color)
self._entry.unbind('<MouseWheel>')
def style_config(self,bg_l='#888888',bg_e='#A0A0A0',fg_l='black',fg_e='black',fontsize=13,w_l=80,w_e=100,height=25,anchor='w',colorChooser=True):
# 控件属性
self._frame.config(bg=bg_l,relief='solid',bd=1)
self._label.config(bg=bg_l,fg=fg_l,font=Font(family='等线',size=fontsize,weight='bold'),anchor=anchor)
self._entry.config(bg=bg_e,fg=fg_e,font=Font(size=fontsize),relief='solid',bd=1)
# 位置属性
self._frame.place_configure(height=height+2,width=w_l+w_e+2)
self._label.place_configure(x=0,y=0,height=height,width=w_l)
self._entry.place_configure(x=w_l,y=0,height=height,width=w_e)
self._btn.config(bg=bg_l,fg='black',relief='groove',bd=0)
if colorChooser:
self._btn.place_configure(x=w_l+w_e-1,y=0,height=height,width=20)
self._frame.place_configure(height=height + 2, width=w_l + w_e + 22)
def bind_func(self,callback=None):
self.__callback=callback
# 颜色选择函数
def __choose_color(self):
color=askcolor()[1]
self.__value.set(color)
self._entry.config(state='normal',bg=color)
if callable(self.__callback):
self.__callback()
# 通过鼠标滚轮改变输入框的值
def __scroll_change_value(self,event):
now_value=self.key_value[str(self.__key)]
if event.delta>0 and now_value<self.__value_range[1]:
self.__value.set(now_value+self.__value_range[2])
if callable(self.__callback):
self.__callback()
elif event.delta<0 and now_value>self.__value_range[0]:
self.__value.set(now_value - self.__value_range[2])
if callable(self.__callback):
self.__callback()
@property
def key_value(self):
return {eval(self.__key_type)(self.__key):eval(self.__value_type)(self.__value.get())}
@key_value.setter
def key_value(self,value):
self.__value.set(value)
def valueRange(self,_min=0,_max=10,step=1):
self.__value_range=[_min,_max,step]
if __name__ == '__main__':
root=Tk()
root.geometry('400x100')
lb=LabelEntry(root,'width',200,colorChooser=False)
lb.valueRange(200,250)
lb.bind_func(lambda :lb.place_configure(cnf=lb.key_value))
lb.place(x=10,y=30)
root.mainloop()
可以复制下来,自己尝试下,挺好用的
2. ScrollFrame(可以滚动页面的Frame)
这是一个嵌套Frame的Frame,内置了捕获滚动事件函数,以及限制滚动区域函数。
注意点:目前仅支持place方法布局,pack和grid方法不可用。
上代码
from tkinter import Frame,Tk,Label,X
class ScrollFrame(Frame):
def __init__(self,master,step=10,innerheight=500):
self._outFrame=Frame(master,bg='yellow')
Frame.__init__(self,self._outFrame,bg='black')
self.bind_all('<MouseWheel>', self.__scroll_func)
self.__step=step
self._inner_place(height=innerheight)
def scroll_page(self, length):
if length>0:
if self.winfo_y()+length>0:
self._inner_place(x=0,y=0)
else:
self._inner_place(x=0,y=self.winfo_y()+length)
elif length<0:
if self.winfo_y()+self.winfo_height()+length>self._outFrame.winfo_height():
self._inner_place(x=0,y=self.winfo_y()+length)
def __scroll_func(self,event):
def digui(widget):
if hasattr(widget,'scroll_page'):
widget.scroll_page(event.delta / abs(event.delta) * self.__step)
return True
elif hasattr(widget,'master'):
if hasattr(widget.master,'scroll_page'):
widget.master.scroll_page(event.delta/abs(event.delta)*self.__step)
return True
else:
digui(widget.master)
else:
return False
digui(event.widget)
def config(self, **kw):
self.configure(cnf=kw)
def place(self,**kw):
self._outFrame.place_configure(cnf=kw)
self._inner_place(x=0,y=0,width=kw['width'])
def _inner_place(self,cnf:dict=None, **kw):
self.tk.call(
('place', 'configure', self._w)
+ self._options(cnf, kw))
if __name__ == '__main__':
root=Tk()
frm=ScrollFrame(root,innerheight=300)
frm.place(x=10,y=10,width=100,height=200)
[Label(frm,text=str(x),bg='gray').pack(pady=10,fill=X) for x in range(10)]
root.mainloop()
同样的,测试代码也写好了,复制粘贴直接运行即可