tkinter绘制组件(28)——评星级控件
- 引言
- 布局
- 函数结构
- 绘制星级
- 绑定事件
- 共同的背景
- 完整代码函数
- 效果
- 测试代码
- 最终效果
- 2020-8-9新改进
- 2023-1-16新改进
- 2023-1-25新样式
- 2024-2-14新样式
- 2026-7-18样式更新
- github项目
- pip下载
- 结语
引言
可能大多数tkinter“玩家”对于评星级控件不了解,实际上这种组件很常见(其实我也不知道常不常见),它会出现在谷歌、苹果、微软的应用商场,以及各类反馈界面中。实际上,评星级控件是一个重要的交互式控件,它允许使用者对于某件事做出价值评价。
在tkinter中没有此控件,但是在winUI3中存在,即RatingControl。TinUI的评星级控件是ratingbar。
布局
函数结构
defadd_ratingbar(self,pos:tuple,fg='#585858',bg='#f3f3f3',onfg='#3041d8',onbg='#3041d8',r=10,num:int=5,linew:int=10,command=None):#绘制评星级控件''' pos::位置 fg::边框颜色 bg::星级颜色 onfg::响应鼠标时边框颜色 onbg::响应鼠标时星级颜色 r::星级半径 num::星级个数 linew::一行星级个数 command::响应函数,必须接受一个参数,当次选择时所选择的星级数 '''绘制星级
这是这个控件的重点。
如何绘制五角星,需要满足以下条件:
完整的五角星
连接十个顶点☆,而不是连接五个顶点
可区分边框和本体
显而易见,我们需要绘制多边形,准确来讲是十边形。
以下是纯计算内容,不想看可以跳过。很简单的,看看无妨。
五个外围顶点,设为A, B, C, D, E 五边形中心,(pos[0]+r,pos[1]+r) => (x,y) A(x,y-r) B(x+r*sin(2*pi/5),y-(r*cos(2*pi/5)) C(x+r*sin(pi/5),y+r*cos(pi/5)) D(x-(r*sin(pi/5)),y+r*cos(pi/5)) E(x-(r*sin(2*pi/5)),y-(r*cos(2*pi/5))) 内围五个顶点的半径,rm=r*sin(pi/10)/sin(7*pi/10) 坐标求解思路同上,略。以上是绘制一个星级的方法,那么我们需要绘制多个星级,也就是需要循环。
nowon=0sin=math.sin cos=math.cos pi=math.pi rm=r*sin(pi/10)/sin(7*pi/10)bars=[]item_num=1#总数量line_num=1#行数量center_x=pos[0]+r center_y=pos[1]+r uid='ratingbar'+str(id(bars))foriinrange(0,num):points=(#左上顶点center_x-(r*sin(2*pi/5)),center_y-(r*cos(2*pi/5)),center_x+(rm*cos(7*pi/10)),center_y-(rm*sin(7*pi/10)),#上顶点center_x,center_y-r,center_x+(rm*cos(3*pi/10)),center_y-(rm*sin(3*pi/10)),#右上顶点center_x+(r*sin(2*pi/5)),center_y-(r*cos(2*pi/5)),center_x+(rm*cos(19*pi/10)),center_y-(rm*sin(19*pi/10)),#右下顶点center_x+(r*sin(pi/5)),center_y+(r*cos(pi/5)),center_x+(rm*cos(3*pi/2)),center_y-(rm*sin(3*pi/2)),#左下顶点center_x-(r*sin(pi/5)),center_y+(r*cos(pi/5)),center_x+(rm*cos(11*pi/10)),center_y-(rm*sin(11*pi/10)),)bar=self.create_polygon(points,fill=bg,outline=fg,joinstyle='miter',tags=uid)bars.append(bar)ifitem_num==num:breakifline_num==linew:center_x=pos[0]+r center_y+=3*r line_num=1continueitem_num+=1line_num+=1center_x+=3*r其中,nowon记录当前响应个数,这个稍后会讲到;bars提供了一个整合所有星级了列表,方便后续操作;line_num记录本行个数是否达到限制个数;item_num记录总数量。
此外,在每次循环后,需要根据下一次绘制条件重新更改坐标中心。
绑定事件
样式变化。都已经是第29篇TinUI构建文章了,样式变化就直接给代码了。
def__onnum(num):foriinbars[:num+1]:self.itemconfig(i,fill=onbg,outline=onfg)ifnum!=len(bars):foriinbars[num+1:]:self.itemconfig(i,fill=bg,outline=fg)defclick(barid):nonlocalnowon nowon=bars.index(barid)ifcommand!=None:command(nowon+1)defonin(barid):index=bars.index(barid)__onnum(index)defonleave(barid):pass会好奇为什么onleave“无作为”吗?
共同的背景
实际上,因为绘制的是多边形,因此鼠标的进入和离开发生在多边形顶点所包围的范围内,也就是说,这里的响应范围是☆。
所以,不能每次离开一个星级就要重绘一次,这样不符合逻辑也耗能。因此,创建一个统一的背景进行控件背景范围的管理。
defleaveback(event):ifnowon==0:foriinbars:self.itemconfig(i,fill=bg,outline=fg)return__onnum(nowon)#...start=self.bbox(uid)back=self.create_rectangle(start,fill='',outline=fg,width=1,tags=uid)self.tag_bind(back,'<Leave>',leaveback)完整代码函数
defadd_ratingbar(self,pos:tuple,fg='#585858',bg='#f3f3f3',onfg='#3041d8',onbg='#3041d8',r=10,num:int=5,linew:int=10,command=None):#绘制评星级控件def__onnum(num):foriinbars[:num+1]:self.itemconfig(i,fill=onbg,outline=onfg)ifnum!=len(bars):foriinbars[num+1:]:self.itemconfig(i,fill=bg,outline=fg)defclick(barid):nonlocalnowon nowon=bars.index(barid)ifcommand!=None:command(nowon+1)defonin(barid):index=bars.index(barid)__onnum(index)defonleave(barid):passdefleaveback(event):ifnowon==0:foriinbars:self.itemconfig(i,fill=bg,outline=fg)return__onnum(nowon)nowon=0sin=math.sin cos=math.cos pi=math.pi rm=r*sin(pi/10)/sin(7*pi/10)bars=[]item_num=1#总数量line_num=1#行数量center_x=pos[0]+r center_y=pos[1]+r uid='ratingbar'+str(id(bars))foriinrange(0,num):points=(#左上顶点center_x-(r*sin(2*pi/5)),center_y-(r*cos(2*pi/5)),center_x+(rm*cos(7*pi/10)),center_y-(rm*sin(7*pi/10)),#上顶点center_x,center_y-r,center_x+(rm*cos(3*pi/10)),center_y-(rm*sin(3*pi/10)),#右上顶点center_x+(r*sin(2*pi/5)),center_y-(r*cos(2*pi/5)),center_x+(rm*cos(19*pi/10)),center_y-(rm*sin(19*pi/10)),#右下顶点center_x+(r*sin(pi/5)),center_y+(r*cos(pi/5)),center_x+(rm*cos(3*pi/2)),center_y-(rm*sin(3*pi/2)),#左下顶点center_x-(r*sin(pi/5)),center_y+(r*cos(pi/5)),center_x+(rm*cos(11*pi/10)),center_y-(rm*sin(11*pi/10)),)bar=self.create_polygon(points,fill=bg,outline=fg,joinstyle='miter',tags=uid)bars.append(bar)self.tag_bind(bar,'<Enter>',lambdaevent,bar=bar:onin(bar))self.tag_bind(bar,'<Leave>',lambdaevent,bar=bar:onleave(bar))self.tag_bind(bar,'<Button-1>',lambdaevent,bar=bar:click(bar))ifitem_num==num:breakifline_num==linew:center_x=pos[0]+r center_y+=3*r line_num=1continueitem_num+=1line_num+=1center_x+=3*r start=self.bbox(uid)back=self.create_rectangle(start,fill='',outline=fg,width=1,tags=uid)self.tag_bind(back,'<Leave>',leaveback)returnbars,uid效果
测试代码
#...b.add_ratingbar((0,1150),num=28,command=print)#...最终效果
2020-8-9新改进
- 背景颜色为
bg,目前还不想添加相应参数 - 不点击到星星图标上仍然可以响应
2023-1-16新改进
当再次点击第一个星级时,取消选择,command返回值为0。
2023-1-25新样式
小修外观。
2024-2-14新样式
巅峰外观。
2026-7-18样式更新
- 可适配高DPI
- 边框改为圆角,添加“边框”多边形元素
github项目
TinUI的github项目地址
pip下载
pipinstalltinui结语
TinUI的改进需要更多智慧,欢迎前往GitHub-TinUI开源代码库献计献策。
🔆tkinter创新🔆