tkinter绘制组件(40)——滚动选值框

tkinter绘制组件(40)——滚动选值框

  • 引言
  • 布局
    • 函数结构
    • 文本展示
    • 选择器布局
    • 完整函数代码
  • 效果
    • 测试代码
    • 最终效果
  • github项目
  • pip下载
  • 结语

引言

2023年基本没有怎么更新TinUI组件部分,而滚动选值框(picker),是在2023年底、2024年初磨洋工磨出来的。

因为一些原因,TinUI更新速度在这段时间被放得“极缓”,但是好歹还是冒了个泡。picker作为TinUI5预发布组件,将在TinUI4.7(5-pre1)首次可用。这也是2024年暑假之前,TinUI唯一的大更新。

前情提要结束。开始正题。

本控件目的可以参考WinUI的TimePikcer和DataPicker,不过更加通用,没有限制数据选择类型。滚动选值框(选择器)提供了一套标准化方式,可使用户选择强相关的系列取值。


布局

函数结构

def add_picker(self,pos:tuple,height=250,fg='#1b1b1b',bg='#fbfbfb',outline='#ececec',activefg='#1b1b1b',activebg='#f6f6f6',onfg='#eaecfb',onbg='#3748d9',font=('微软雅黑',10),text=(('year',60),('season',100),),data=(('2022','2023','2024'),('spring','summer','autumn','winter')),tran='#01FF11',command=None):#绘制滚动选值框
'''
pos-位置
height-选择框高度
fg-文本颜色
bg-背景色
outline-边框色
activefg-选择时文本颜色
activebg-选择时背景颜色
onfg-选定时文本颜色
onbg-选定时背景颜色
font-字体
text-文本内容,需要与`data`对应。`((选值文本,元素宽度),...)`
data-选值内容,需要与`text`对应
tran-透明处理规避颜色
command-响应接受函数。需要接受一个参数:所有选值列表,全被选定时触发
'''  

文本展示

这一部分比较简单,就是通过text参数中给定的文本和元素宽度,在当前画布上绘制文本元素。既然比较复杂的文字排版table控件早就加入到TinUI中了,这个小操作不在话下。如果不太熟悉或没看懂绘制逻辑,可以看看本专栏的表格绘制。

        out_line=self.create_polygon((*pos,*pos),fill=outline,outline=outline,width=9)uid='picker'+str(out_line)self.addtag_withtag(uid,out_line)back=self.create_polygon((*pos,*pos),fill=bg,outline=bg,width=7,tags=uid)end_x=pos[0]+9y=pos[1]+9texts=[]#文本元素#测试文本高度txtest=self.create_text(pos,text=text[0][0],fill=fg,font=font)bbox=self.bbox(txtest)self.delete(txtest)uidheight=bbox[3]-bbox[1]for i in text:t,w=i#文本,宽度tx=self.create_text((end_x,y),anchor='w',text=t,fill=fg,font=font,tags=(uid,uid+'content'))texts.append(tx)end_x+=wif text.index(i)+1==len(text):#最后一个省略分隔符_outline=outlineoutline=''self.create_line((end_x-3,pos[1],end_x-3,pos[1]+uidheight),fill=outline,tags=(uid,uid+'content'))outline=_outlinedel _outline

不过需要注意的是,因为picker的选择器窗口是以像menu一样地使用子窗口,因此我们需要先行确定窗口的宽度。

顺便绑定一下响应事件。

        def _mouseenter(event):self.itemconfig(back,fill=activebg,outline=activebg)for i in texts:self.itemconfig(i,fill=activefg)def _mouseleave(event):self.itemconfig(back,fill=bg,outline=bg)for i in texts:self.itemconfig(i,fill=fg)def show(event):#这部分待会会用来现实选择器窗口......del _outlinewidth=end_x-pos[0]+9#窗口宽度cds=self.bbox(uid+'content')#变换背景元素尺寸coords=(cds[0],cds[1],cds[2],cds[1],cds[2],cds[3],cds[0],cds[3])self.coords(out_line,coords)self.coords(back,coords)#绑定事件self.tag_bind(uid,'<Enter>',_mouseenter)self.tag_bind(uid,'<Leave>',_mouseleave)self.tag_bind(uid,'<Button-1>',show)

选择器布局

这才是重点。

选择器应该遵循以下布局要求:

  1. 有几个选项,就要有几个选择器元素,且宽度与text中指定宽度基本一致

  2. 需要有确定和取消按钮

  3. 窗口默认位置应该与在TinUI中的文本元素对应

对于要求【1】,我参考了自己写的listbox代码。(真的是万事开头难,现在应该写不出当时的代码了……)

然后通过循环创建选择器元素。

        def _loaddata(box,items,mw):#这是listbox中的逻辑与绘制代码def __set_y_view(event):box.yview_scroll(int(-1*(event.delta/120)), "units")#mw: 元素宽度for i in items:end=box.bbox('all')end=5 if end==None else end[-1]text=box.create_text((5,end+7),text=i,fill=fg,font=font,anchor='nw',tags=('textcid'))bbox=box.bbox(text)#获取文本宽度back=box.create_rectangle((3,bbox[1]-4,3+mw,bbox[3]+4),width=0,fill=bg)box.tkraise(text)box.choices[text]=[i,text,back,False]#用文本id代表键,避免选项文本重复带来的逻辑错误#box.all_keys.append(text)box.tag_bind(text,'<Enter>',lambda event,text=text : pick_in_mouse(event,text))box.tag_bind(text,'<Leave>',lambda event,text=text : pick_out_mouse(event,text))box.tag_bind(text,'<Button-1>',lambda event,text=text : pick_sel_it(event,text))box.tag_bind(back,'<Enter>',lambda event,text=text : pick_in_mouse(event,text))box.tag_bind(back,'<Leave>',lambda event,text=text : pick_out_mouse(event,text))box.tag_bind(back,'<Button-1>',lambda event,text=text : pick_sel_it(event,text))bbox=box.bbox('all')box.config(scrollregion=bbox)box.bind('<MouseWheel>',__set_y_view)...for i in data:barw=text[__count][1]#本选择列表元素宽度pickbar=BasicTinUI(picker,bg=bg)pickbar.place(x=end_x,y=y,width=barw,height=height-50)maxwidth=0pickbar.newres=''#待选pickbar.res=''#选择结果#pickbar.all_keys=[]#[a-id,b-id,...]pickbar.choices={}#'a-id':[a,a_text,a_back,is_sel:bool]_loaddata(pickbar,i,barw)pickerbars.append(pickbar)__count+=1end_x+=barw+3del __count

要求【2】则简单多了。这里使用button2,但是需要调整背景元素。也相当于在TinUI的自身应用中给出控件元素返回值的操作范例。

        okpos=((5+(width-9)/2)/2,height-22)ok=bar.add_button2(okpos,text='✔️',font='{Segoe UI Emoji} 12',fg=fg,bg=bg,line='',activefg=activefg,activebg=activebg,activeline='',anchor='center',command=set_it)bar.coords(ok[1],(10,height-35,(width-9)/2-5,height-35,(width-9)/2-5,height-9,10,height-9))nopos=(((width-9)/2+width-4)/2,height-22)no=bar.add_button2(nopos,text='❌',font='{Segoe UI Emoji} 12',fg=fg,bg=bg,line='',activefg=activefg,activebg=activebg,activeline='',anchor='center',command=cancel)bar.coords(no[1],((width-9)/2+5,height-35,width-9,height-35,width-9,height-9,(width-9)/2+5,height-9))readyshow()

代码中的readyshow,就是要求【3】的内容了。

这部分主要用来计算和记录选择器窗口的位置信息,稍后会用在show函数中。

        def readyshow():#计算显示位置allpos=bar.bbox('all')#菜单尺寸winw=allpos[2]-allpos[0]+5winh=allpos[3]-allpos[1]+5#屏幕尺寸maxx=self.winfo_screenwidth()maxy=self.winfo_screenheight()wind.data=(maxx,maxy,winw,winh)

不同于menupicker的窗口需要直接贴近文本元素,因此需要额外计算文本元素边缘与点击的位置差,然后在与屏幕坐标相减。

此外,picker采用淡入动画。

        def show(event):#显示的起始位置#初始位置maxx,maxy,winw,winh=wind.databbox=self.bbox(uid)scx,scy=event.x_root,event.y_root#屏幕坐标dx,dy=round(self.canvasx(event.x,)-bbox[0]),round(self.canvasy(event.y)-bbox[3])#画布坐标差值sx,sy=scx-dx,scy-dyif sx+winw>maxx:x=sx-winwelse:x=sxif sy+winh>maxy:y=sy-winhelse:y=sypicker.geometry(f'{winw+15}x{winh+15}+{x-4}+{y}')picker.attributes('-alpha',0)picker.deiconify()picker.focus_set()for i in [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1]:picker.attributes('-alpha',i)picker.update()time.sleep(0.05)picker.bind('<FocusOut>',unshow)

好了,到此,picker的两部分内容已经完成绘制。完整的逻辑代码会在下方给出。

完整函数代码

    def add_picker(self,pos:tuple,height=250,fg='#1b1b1b',bg='#fbfbfb',outline='#ececec',activefg='#1b1b1b',activebg='#f6f6f6',onfg='#eaecfb',onbg='#3748d9',font=('微软雅黑',10),text=(('year',60),('season',100),),data=(('2022','2023','2024'),('spring','summer','autumn','winter')),tran='#01FF11',command=None):#绘制滚动选值框def _mouseenter(event):self.itemconfig(back,fill=activebg,outline=activebg)for i in texts:self.itemconfig(i,fill=activefg)def _mouseleave(event):self.itemconfig(back,fill=bg,outline=bg)for i in texts:self.itemconfig(i,fill=fg)def set_it(e):#确定选择results=[]#结果列表for ipicker in pickerbars:num=pickerbars.index(ipicker)if ipicker.newres=='':#没有选择unshow(e)returnipicker.res=ipicker.newrestx=texts[num]self.itemconfig(tx,text=ipicker.res)results.append(ipicker.res)unshow(e)if command!=None:command(results)def cancel(e):#取消选择for ipicker in pickerbars:if ipicker.res=='':passunshow(e)#以后或许回考虑元素选择复原,也不一定,或许不更改交互选项更方便def pick_in_mouse(e,t):box=e.widgetif box.choices[t][-1]==True:#已被选中returnbox.itemconfig(box.choices[t][2],fill=activebg)box.itemconfig(box.choices[t][1],fill=activefg)def pick_out_mouse(e,t):box=e.widgetif box.choices[t][-1]==True:#已被选中box.itemconfig(box.choices[t][2],fill=onbg)box.itemconfig(box.choices[t][1],fill=onfg)else:box.itemconfig(box.choices[t][2],fill=bg)box.itemconfig(box.choices[t][1],fill=fg)def pick_sel_it(e,t):box=e.widgetbox.itemconfig(box.choices[t][2],fill=onbg)box.itemconfig(box.choices[t][1],fill=onfg)box.choices[t][-1]=Truefor i in box.choices.keys():if i==t:continuebox.choices[i][-1]=Falsepick_out_mouse(e,i)box.newres=box.choices[t][0]def readyshow():#计算显示位置allpos=bar.bbox('all')#菜单尺寸winw=allpos[2]-allpos[0]+5winh=allpos[3]-allpos[1]+5#屏幕尺寸maxx=self.winfo_screenwidth()maxy=self.winfo_screenheight()wind.data=(maxx,maxy,winw,winh)def show(event):#显示的起始位置#初始位置maxx,maxy,winw,winh=wind.databbox=self.bbox(uid)scx,scy=event.x_root,event.y_root#屏幕坐标dx,dy=round(self.canvasx(event.x,)-bbox[0]),round(self.canvasy(event.y)-bbox[3])#画布坐标差值sx,sy=scx-dx,scy-dyif sx+winw>maxx:x=sx-winwelse:x=sxif sy+winh>maxy:y=sy-winhelse:y=sypicker.geometry(f'{winw+15}x{winh+15}+{x-4}+{y}')picker.attributes('-alpha',0)picker.deiconify()picker.focus_set()for i in [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1]:picker.attributes('-alpha',i)picker.update()time.sleep(0.05)picker.bind('<FocusOut>',unshow)def unshow(event):picker.withdraw()picker.unbind('<FocusOut>')def _loaddata(box,items,mw):def __set_y_view(event):box.yview_scroll(int(-1*(event.delta/120)), "units")#mw: 元素宽度for i in items:end=box.bbox('all')end=5 if end==None else end[-1]text=box.create_text((5,end+7),text=i,fill=fg,font=font,anchor='nw',tags=('textcid'))bbox=box.bbox(text)#获取文本宽度back=box.create_rectangle((3,bbox[1]-4,3+mw,bbox[3]+4),width=0,fill=bg)box.tkraise(text)box.choices[text]=[i,text,back,False]#用文本id代表键,避免选项文本重复带来的逻辑错误#box.all_keys.append(text)box.tag_bind(text,'<Enter>',lambda event,text=text : pick_in_mouse(event,text))box.tag_bind(text,'<Leave>',lambda event,text=text : pick_out_mouse(event,text))box.tag_bind(text,'<Button-1>',lambda event,text=text : pick_sel_it(event,text))box.tag_bind(back,'<Enter>',lambda event,text=text : pick_in_mouse(event,text))box.tag_bind(back,'<Leave>',lambda event,text=text : pick_out_mouse(event,text))box.tag_bind(back,'<Button-1>',lambda event,text=text : pick_sel_it(event,text))bbox=box.bbox('all')box.config(scrollregion=bbox)box.bind('<MouseWheel>',__set_y_view)out_line=self.create_polygon((*pos,*pos),fill=outline,outline=outline,width=9)uid='picker'+str(out_line)self.addtag_withtag(uid,out_line)back=self.create_polygon((*pos,*pos),fill=bg,outline=bg,width=7,tags=uid)end_x=pos[0]+9y=pos[1]+9texts=[]#文本元素#测试文本高度txtest=self.create_text(pos,text=text[0][0],fill=fg,font=font)bbox=self.bbox(txtest)self.delete(txtest)uidheight=bbox[3]-bbox[1]for i in text:t,w=i#文本,宽度tx=self.create_text((end_x,y),anchor='w',text=t,fill=fg,font=font,tags=(uid,uid+'content'))texts.append(tx)end_x+=wif text.index(i)+1==len(text):#最后一个省略分隔符_outline=outlineoutline=''self.create_line((end_x-3,pos[1],end_x-3,pos[1]+uidheight),fill=outline,tags=(uid,uid+'content'))outline=_outlinedel _outlinewidth=end_x-pos[0]+9#窗口宽度cds=self.bbox(uid+'content')coords=(cds[0],cds[1],cds[2],cds[1],cds[2],cds[3],cds[0],cds[3])self.coords(out_line,coords)self.coords(back,coords)self.tag_bind(uid,'<Enter>',_mouseenter)self.tag_bind(uid,'<Leave>',_mouseleave)self.tag_bind(uid,'<Button-1>',show)#创建窗口picker=Toplevel(self)picker.geometry(f'{width}x{height}')picker.overrideredirect(True)picker.attributes('-topmost',1)picker.withdraw()#隐藏窗口picker.attributes('-transparent',tran)wind=TinUINum()#记录数据bar=BasicTinUI(picker,bg=tran)bar.pack(fill='both',expand=True)bar.create_polygon((9,9,width-9,9,width-9,height-9,9,height-9),fill=bg,outline=bg,width=9)bar.lower(bar.create_polygon((8,8,width-8,8,width-8,height-8,8,height-8),fill=outline,outline=outline,width=9))__count=0end_x=8y=9pickerbars=[]#选择UI列表for i in data:barw=text[__count][1]#本选择列表元素宽度pickbar=BasicTinUI(picker,bg=bg)pickbar.place(x=end_x,y=y,width=barw,height=height-50)maxwidth=0pickbar.newres=''#待选pickbar.res=''#选择结果#pickbar.all_keys=[]#[a-id,b-id,...]pickbar.choices={}#'a-id':[a,a_text,a_back,is_sel:bool]_loaddata(pickbar,i,barw)pickerbars.append(pickbar)__count+=1end_x+=barw+3del __count#ok buttonokpos=((5+(width-9)/2)/2,height-22)ok=bar.add_button2(okpos,text='✔️',font='{Segoe UI Emoji} 12',fg=fg,bg=bg,line='',activefg=activefg,activebg=activebg,activeline='',anchor='center',command=set_it)bar.coords(ok[1],(10,height-35,(width-9)/2-5,height-35,(width-9)/2-5,height-9,10,height-9))#cancel buttonnopos=(((width-9)/2+width-4)/2,height-22)no=bar.add_button2(nopos,text='❌',font='{Segoe UI Emoji} 12',fg=fg,bg=bg,line='',activefg=activefg,activebg=activebg,activeline='',anchor='center',command=cancel)bar.coords(no[1],((width-9)/2+5,height-35,width-9,height-35,width-9,height-9,(width-9)/2+5,height-9))readyshow()#texts=[],pickerbars=[]return picker,bar,texts,pickerbars,uid

效果

测试代码

b.add_picker((1400,230),command=print)

最终效果

在这里插入图片描述

左下角是expander友情出演。


github项目

TinUI的github项目地址

pip下载

pip install tinui

结语

这样相当于一个比较粗糙的选择器吧。TinUI5将对大部分控件进行样式升级。

🔆tkinter创新🔆

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/243316.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《WebKit 技术内幕》学习之七(4): 渲染基础

4 WebKit软件渲染技术 4.1 软件渲染过程 在很多情况下&#xff0c;也就是没有那些需要硬件加速内容的时候&#xff08;包括但不限于CSS3 3D变形、CSS3 03D变换、WebGL和视频&#xff09;&#xff0c;WebKit可以使用软件渲染技术来完成页面的绘制工作&#xff08;除非读者强行…

Vue基础入门 - Vue的快速创建、Vue的开发者工具安装及Vue的常用指令(v-model,v-bind,computed计算属性,watch侦听器)

Vue 文章目录 Vue1 什么是Vue2 创建Vue实例2.1 快速创建2.2 插值表达式 {{}}2.3 响应式特性2.3.1 访问与修改 3 Vue开发者工具安装4 Vue中的常用指令4.1 内容渲染指令4.2 条件渲染指令4.3 事件绑定指令4.4 属性绑定指令4.5 案例-上下页图片翻页4.6 列表渲染指令4.7 案例-能删除…

flink部署模式介绍

在一些应用场景中&#xff0c;对于集群资源分配和占用的方式&#xff0c;可能会有特定的需求。Flink 为各种场景提供了不同的部署模式&#xff0c;主要有以下三种&#xff0c;它们的区别主要在于&#xff1a; 集群的生命周期以及资源的分配方式&#xff1b;应用的 main 方法到…

谷歌浏览器通过network模拟HTTP中的GET/POST请求获取response

1、F12打开network选中需要模拟的方法Copy->Copy as fetch 2、通过AI帮你进行转换一下调用格式 原代码 fetch("https://mp.amap.com/api/forward/aggregate?mtop.alsc.kbt.intergration.toolkit.call.queryCallBlockInfo", {"headers": {"acce…

03--数据库连接池

1、数据库连接池 1.1 JDBC数据库连接池的必要性 在使用开发基于数据库的web程序时&#xff0c;传统的模式基本是按以下步骤&#xff1a; 在主程序&#xff08;如servlet、beans&#xff09;中建立数据库连接进行sql操作断开数据库连接 这种模式开发&#xff0c;存在的问题:…

PyTorch 中的距离函数深度解析:掌握向量间的距离和相似度计算

目录 Pytorch中Distance functions详解 pairwise_distance 用途 用法 参数 数学理论公式 示例代码 cosine_similarity 用途 用法 参数 数学理论 示例代码 输出结果 pdist 用途 用法 参数 数学理论 示例代码 总结 Pytorch中Distance functions详解 pair…

Git学习笔记(第1章):Git概述

目录 1.1 版本控制 1.1.1 何为版本控制 1.1.2 为什么需要版本控制 1.1.3 版本控制工具 1.2 发展历史 1.3 工作机制 1.4 代码托管中心&#xff08;远程库&#xff09; Git是一个免费的、开源的分布式版本控制系统&#xff0c;可以快速高效地处理从小型到大型的各种项目。…

【UEFI基础】EDK网络框架(UDP4)

UDP4 UDP4协议说明 UDP的全称是User Datagram Protocol&#xff0c;它不提供复杂的控制机制&#xff0c;仅利用IP提供面向无连接的通信服务。它将上层应用程序发来的数据在收到的那一刻&#xff0c;立即按照原样发送到网络。 UDP报文格式&#xff1a; 各个参数说明如下&…

AD导出BOM表 导出PDF

1.Simple BOM: 这种模式下&#xff0c;最好在pcb界面&#xff0c;这样的导出的文件名字是工程名字&#xff0c;要是在原理图界面导出&#xff0c;会以原理图的名字命名表格。 直接在菜单栏 报告->Simple BOM 即可导出物料清单&#xff0c;默认导出 comment pattern qu…

【Java】学习一门开发语言,从TA的Hello World开始

欢迎来到《小5讲堂》 大家好&#xff0c;我是全栈小5。 这是《Java》序列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对知识点的理解和掌握…

【Redis】非关系型数据库之Redis的主从复制、哨兵和集群高可用

目录 一、主从复制、哨兵、集群的区别 二、主从复制 2.1主从复制的作用 2.2主从复制的原理 2.3主从复制的实操 步骤一&#xff1a;环境准备 步骤二&#xff1a;安装Redis以及配置文件修改 Redis的主从配置文件都一样 步骤四&#xff1a;验证主从复制 三、哨兵 3.1哨兵…

NODE介绍和环境安装

浏览器是JS的前端运行环境 Node.js是JS的后端运行环境 Node.js中无法调用DOM和BOM等浏览器内置API 基于Express框架&#xff0c;快速构建web应用 基于Electron框架&#xff0c;构建跨平台桌面应用 基于restify框架快速构建API接口项目 读写数据库 下载Node.js环境 网址&…

【分布式技术】注册中心zookeeper

目录 一、ZooKeeper是什么 二、ZooKeeper的工作机制 三、ZooKeeper特点 四、ZooKeeper数据结构 五、ZooKeeper应用场景 ●统一命名服务 ●统一配置管理 ●统一集群管理 ●服务器动态上下线 ●软负载均衡 六、ZooKeeper的选举机制 七、实操部署ZooKeeper集群 步骤一…

线性代数:矩阵运算(加减、数乘、乘法、幂、除、转置)

目录 加减 数乘 矩阵与矩阵相乘 矩阵的幂 矩阵转置 方阵的行列式 方阵的行列式&#xff0c;证明&#xff1a;|AB| |A| |B| 加减 数乘 矩阵与矩阵相乘 矩阵的幂 矩阵转置 方阵的行列式 方阵的行列式&#xff0c;证明&#xff1a;|AB| |A| |B|

续签KES证书

MiniO KES&#xff08;密钥加密服务&#xff09;是 MinIO 开发的一项服务&#xff0c;旨在弥合在 Kubernetes 中运行的应用程序与集中式密钥管理服务 &#xff08;KMS&#xff09; 之间的差距。中央 KMS 服务器包含所有状态信息&#xff0c;而 KES 在需要执行与获取新密钥或更新…

Unity 工厂方法模式(实例详解)

文章目录 在Unity中&#xff0c;工厂方法模式是一种创建对象的常用设计模式&#xff0c;它提供了一个接口用于创建对象&#xff0c;而具体的产品类是由子类决定的。这样可以将对象的创建过程与使用过程解耦&#xff0c;使得代码更加灵活和可扩展。 工厂模式的主要优点如下&…

Unity中实现捏脸系统

前言 目前市面上常见的捏脸一般是基于BlendShapes和控制骨骼点坐标两种方案实现的。后者能够控制的精细程度更高&#xff0c;同时使用BlendShapes来控制表情。 控制骨骼点坐标 比如找到控制鼻子的骨骼节点修改localScale缩放&#xff0c;调节鼻子大小。 BlendShapes控制表…

gin中间件篇

1. 全局中间件 所有请求都经过此中间件 package mainimport ("fmt""time""github.com/gin-gonic/gin" )// 定义中间 func MiddleWare() gin.HandlerFunc {return func(c *gin.Context) {t : time.Now()fmt.Println("中间件开始执行了&quo…

OceanBase集群扩缩容

​ OceanBase 数据库采用 Shared-Nothing 架构&#xff0c;各个节点之间完全对等&#xff0c;每个节点都有自己的 SQL 引擎、存储引擎、事务引擎&#xff0c;天然支持多租户&#xff0c;租户间资源、数据隔离&#xff0c;集群运行的最小资源单元是Unit&#xff0c;每个租户在每…

常用设计模式(工厂方法,抽象工厂,责任链,装饰器模式)

前言 有关设计模式的其他常用模式请参考 单例模式的实现 常见的设计模式(模板与方法&#xff0c;观察者模式&#xff0c;策略模式) 工程方法 定义 定义一个用于创建对象的接口&#xff0c;让子类决定实例化哪一个类。Factory Method使得一个类的实例化延迟到子类。 ——《设…