tkinter绘制组件(28)——评星级控件

tkinter绘制组件(28)——评星级控件

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::响应函数,必须接受一个参数,当次选择时所选择的星级数 '''

绘制星级

这是这个控件的重点。

如何绘制五角星,需要满足以下条件:

  1. 完整的五角星

  2. 连接十个顶点☆,而不是连接五个顶点

  3. 可区分边框和本体

显而易见,我们需要绘制多边形,准确来讲是十边形。

以下是纯计算内容,不想看可以跳过。很简单的,看看无妨。

五个外围顶点,设为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样式更新

  1. 可适配高DPI
  2. 边框改为圆角,添加“边框”多边形元素

github项目

TinUI的github项目地址

pip下载

pipinstalltinui

结语

TinUI的改进需要更多智慧,欢迎前往GitHub-TinUI开源代码库献计献策。

🔆tkinter创新🔆