基于tk界面库的扩展类
对python中自带的tkinter库,可以用来创建简单的界面来完成同用户的交互,对于tk,窗体上创建各种控件没有象QT的设计器创建那么方便,于是就采用列表变量的方式来保存各种控件的属性,在tk窗体上创建控件时,将列表变量导入,对列表变量中的属性值进行解析来在窗体上创建各种控件,减少简单交互界面上編程花费的时间。
tk示例窗体运行后的界面如下:
主窗体main.py代码如下
# -*- coding: utf-8 -*-
"""
main.py测试无窗体的主模块代码
"""
import sys,os,time,math,copy,random #导入系统模页
from math import *
import numpy as npfrom tkEx import *
#导入测试模块
tkFrm=None
#####################################################################################
def on_command_click(): #对TK的按纽控件即可以绑定主进程的函数,也可以绑定tkEx中的函数(标签控件只能绑定tkEx中的事件函数)print('主进程:on_command_click,下面的代码作tkEx类测试')tkFrm.setWidgetsText('edit_pas1','123456789',fgCol='blue',bgCol='red',selfgCol='red',selbgCol='blue')txt=tkFrm.getWidgetsText('edit_pas1')print(f'得到的文本={txt}')#tkFrm.setWidgetsVisable('button_OK',False)tkFrm.setWidgetsVisable('button_OK',True)tkFrm.setWidgetsVisable('line2',False)tkFrm.setWidgetsEnable('button_OK',False)tkFrm.setWidgetsEnable('edit_name',False)tkFrm.setWidgetsEnable('edit_name',True)#从tkFrm窗体中返回主进程的回调函数值
def from_tkFrm_callback(self,objName,lstDatas):print(f'从tkFrm窗体对象得到回调信息:对象名称="{objName}",返回值={lstDatas}')def test1():"""测试tkEx窗口类"""global tkFrm# 0 1 2 3 4 5 6 7 8 9 10 11tkFrm=tkEx(title='tk窗体',width=600,height=800,callback=from_tkFrm_callback,closebtn=True,maxbtn=True,minbtn=False,modal=False,isTop=False,bgCol=None,bgImgFile='1.png',dragmove=False) #此时控件还没有初始化到窗体上""" 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25参数: lstWidgets 列表变量=[objType, objName text, x, y, width,height, tooltip, bg, fg, anchor, font, padx, pady, wraplength, command=None, cursor, justify, state, relief, bd, imgstretch,show,None,None,None]示例 'label','labelInfo', '标签内容', 10, 20, 50, 15 ,'提示信息, 'gray', 'black', 'nw', ('宋体',11), 2, 2 300 self.函数名 'hand2' 'left' 'normal' 'groove' 1 True ,'*',None,None,None 说明: 控件类型字符串 控件名称 标签或图象内容 屏幕位置 控件宽高 控件的上弹提示信息 背景色 前景色 单行对齐 字体 内定位边距 多行换行宽度 指定命令函数 鼠标悬停样式 多行对齐方式 状态 边框类型 边框宽度 图象是否拉伸 字符掩码类型: 字符串 字符串 字符串 整数(像素) 整数(像素) 字符串 字符串 字符串 字符串 元组 整数(像素) 整数(像素) 函数对象 字符串 字符串 字符串 字符串 整数(像素) 布尔 字符串"""# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25lstObj0=['label','label_info','标签内容', 22,22,100,30,'显示信息' ,'yellow','black','nw', ("宋体",9), 1, 1,800,on_command_click,'arrow', 'left','normal','flat', 5, None,None,None,None,None]lstObj1=['button','button_OK','确定', 22,362,100,80,'单击退出', 'gray','red', 'center', ('黑体',18), 2, 2,300,None, 'hand2', 'left','normal','raised',10, None,None,None,None,None]lstObj2=['button','button_test','按纽测试', 22, 460,200,80,'测试', 'green','black', 'center', ('黑体',18), 2, 2,300,on_command_click,'hand2', 'left','normal','raised',1, None,None,None,None,None]lstObj3=['image','img1','2.png', 22, 62,200,300,'图片', None,'black', 'center', None, 2, 2,300, None, 'hand2', 'left','normal','ridge',1, True,None,None,None,None]lstObj4=['line','line1','', 20, 20,500,600,'', None,'gray', 'xy', None, 100, 0,300,None, '', 'left' 'normal','raised',1, None,None,None,None,None]lstObj5=['line','line2','', 20, 60,500,5,'', None,'blue', 'x', None, 50, 50,300,None, '', 'left','normal','raised',1, None,None,None,None,None]lstObj6=['line','line3','', 224, 20,600,2,'', None,'red', 'y', None, 100, 0,300,None, '', 'left','normal','raised',1, None,None,None,None,None]lstObj7=['line','line4','', 20, 360,500,1,'', None,'green', 'x', None, 100, 0,300,None, '', 'left','normal','raised',1, None,None,None,None,None]lstObj8=['line','line5','', 20,450,500,1,'', None,'black', 'x', None, 100, 0,300,None, '', 'left','normal','raised',1, None,None,None,None,None]lstObj9=['edit_one','edit_name','单行编辑框', 225,25,250,30,'输入姓名', 'white','black', 'nw', ("宋体",9), 1, 1,800,None, 'arrow', 'left','normal','sunken',1, None,None,None,None,None]lstObj10=['edit_mu','edit_muline','多行编辑框', 225,62,250,150,'多行编辑框','yellow','blue', 'nw', ("宋体",9), 1, 1,800,None, 'arrow', 'left','normal','raised',1, None,None,None,None,None]lstObj11=['edit_pas','edit_pas1','密码输入框', 225,215,250,30,'输入密码', 'green','orange', 'nw', ("宋体",9), 1, 1,800,None, 'arrow', 'left','normal','groove',1, None,'*',None,None,None]lstObj12=['edit_readonly','edit_readonly1','只读文本框',225,250,250,30,'只读编辑框','white','red', 'nw', ("宋体",9), 1, 1,800,None, 'arrow', 'left','normal','ridge', 1, None,None,None,None,None]lstObjs=[]lstObjs.append(lstObj0)lstObjs.append(lstObj1)lstObjs.append(lstObj2)lstObjs.append(lstObj3)lstObjs.append(lstObj4)lstObjs.append(lstObj5)lstObjs.append(lstObj6)lstObjs.append(lstObj7)lstObjs.append(lstObj8)lstObjs.append(lstObj9)lstObjs.append(lstObj10)lstObjs.append(lstObj11)lstObjs.append(lstObj12)tkFrm.initWidgets(lstObjs) #将全部控件初始化到窗体上tkFrm.startTkWindows() #之后不能有代码了,否则关于tkFrm的不可用##################################################################################
if __name__ == '__main__':test1()
tk界面扩展类模块tkEx.py代码如下
"""
tkEx.py:定义基于tkinter界面库的扩展模块
"""
# -*- coding: utf-8 -*-
import sys,os,time,math,copy,random #导入系统模块
from math import *
import numpy as np
import tkinter as tk #使用py自带tkinter模块作为界面库
#from tkintertooltip import ToolTip
from tkinter import *
#from tkinter import ttk
from PIL import Image,ImageTkclass tkEx():"""类功能:tk库创建一简单的交互对话框(只显示内容,无深度交互功能)类对象初始化参数:title=窗本标题栏文本主要方法:"""TK_CANCLE=0 #取消按纽TK_OK=1 #确定按纽TK_EDIT_ONELINE=3 #单行编辑框TK_EDIT_PASSWORD=4 #密码输入框TK_EDIT_MULINE=5 #多行输入框TK_EDIT_READONLY=6 #只读编辑框def __init__(self,title='tk窗体',width=300,height=300,callback=None,closebtn=True,maxbtn=False,minbtn=False,modal=True,isTop=False,bgCol=None,bgImgFile=None,dragmove=False):self.callback=callback #创建窗口的进程中的回调函数,以方便即时得到此窗体中的一些操作和数据信息if(modal): #创建的是模态对话框self.root=tk.Toplevel(None) #注意:如当前没有父窗口,会新建一空白父窗口??出现两个窗口self.root.grab_set() #阻塞父窗口的操作else:self.root=tk.Tk()if(not maxbtn and not minbtn): #只有关闭按纽self.root.attributes("-toolwindow",1) self.root.resizable(False,False) elif(not closebtn): #没有任何按纽self.root.overrideredirect(True) elif(not maxbtn): #不能调整尺寸超过最大值#self.root.attributes("-toolwindow",0)self.root.maxsize(width,height)elif(not minbtn):#self.root.attributes("-toolwindow",2) self.root.minsize(width,height)self.appPath=sys.path[0] #当前代码运行根目录self.def_bgCol=self.root.cget("bg") #默认作体背景色if(bgCol is None or bgCol=='SYS'):self.bgCol=self.def_bgColelse:self.bgCol=bgColself.bgImgFile=self.getFilePath(bgImgFile)self.dragmove=dragmove #是否支持鼠标拖动self.widgetsID=1 # 0 1 2 3 4 5..... self.dicWidgets = {} #记录本窗体的全部控件及响应函数 '控件名称'=[ID,控件类型字符串,控件名称字串(同key),控件名称对应对象,提示信息框内容,左键单击时的函数,右键单击时的函数,鼠标移动时的函数,内容发生变化时的函数......]self.dicWidgets['root']=[0,'tkWin','root',self.root,'self.root窗体',self.on_def_Left_Click,self.onWin_Right_Click,self.onWin_Mouse_Move,None,None]self.root.bind("<Button-1>",self.on_def_Left_Click) #绑定窗口被左键单击的事件处理函数self.root.bind("<Button-2>",self.onWin_Mid_Click) #绑定窗口被左键单击的事件处理函数self.root.bind("<Button-3>",self.onWin_Right_Click) #绑定窗口被左键单击的事件处理函数#self.root.bind("<ButtonPress-1>",self.onWin_Left_Press) #绑定窗口被左键压下事件处理函数self.root.bind("<ButtonRelease-1>",self.onWin_Left_Release) #绑定窗口左键单击释放的事件处理函数self.root.bind("<B1-Motion>",self.onWin_move) #绑定鼠标可以拖动窗体self.root.bind("<Configure>",self.on_window_resize) #绑定窗体缩放时重绘图片self.root.bind("<Enter>",self.on_win_enter) #绑定进入窗体事件self.root.bind("<Leave>",self.on_win_leave) #绑定离开窗体事件 self.title = titleself.root.title(title)self.width=width #当前窗体的宽度self.height=height #当前窗体的高度#self.root.geometry(f"{width}x{height}") #指定窗体大小c_x=int(self.root.winfo_screenwidth()/2-self.width/2) #计算窗体居中显示坐标c_y=int(self.root.winfo_screenheight()/2-self.height/2)self.root.geometry(f"{self.width}x{self.height}+{c_x}+{c_y}") #窗体居中显示#用纯色作为窗体底色 if(bgCol is not None):self.root.configure(bg=bgCol)#用图片作为窗体背景if(bgImgFile is not None ):if(os.path.exists(self.bgImgFile)):self.orgImage=Image.open(self.bgImgFile)self.tk_image=Noneself.canvas=tk.Canvas(self.root,highlightthickness=0) #创建画布作为背影容器self.canvas.pack(fill='both',expand=True)self.draw_bgimg() #绘制背景图片if(isTop):self.root.attributes('-topmost',True)self.root.lift() #强制提升窗口层级置顶self.bmainloop=False#用图片作为窗体的背景图片def draw_bgimg(self):win_width=self.root.winfo_width()win_height=self.root.winfo_height()if(win_width<=1 or win_height<=1):returnprint(f'wxh={win_width}x{win_height}') #resized_img=self.orgImage.resize((win_width,win_height),Image.ANTIALIAS) #对pillow9.0+版本第二个参数可以改为 ,Image.Resampling.LANCZOS)resized_img=self.orgImage.resize((win_width,win_height),Image.Resampling.LANCZOS)self.tk_image=ImageTk.PhotoImage(resized_img)self.canvas.delete("bg")self.canvas.create_image(0,0,anchor="nw",image=self.tk_image,tags="bg")#对有关文件的处理函数:如文件名为相对路径,自动加上代码的运行目录改为绝对路径def getFilePath(self,fileName):if(fileName.find('\\')>=00 or fileName.find('/')>=0):return fileNameelse:return self.appPath + '\\' + fileName#按主要默认方式初始化一列表方便调用处修改完善后形成控件创建的一项列表def getDefOneWidgets(self,widgetsType='label'):ctlName=widgetsType.lower()def_bg=self.root.cget("bg")def_fg=self.root.cget("fg")def_command=self.on_def_Left_Click# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25lstOne=['label','labSample','内容xxx',0,0, 0, 0, '提示信息',def_bg,'black', 'nw', ('宋体',11), 2, 2, 300, def_command,'hand2','left','normal',2, 5, False, '' ,None,None,None]# [objType, objName text, x,y,width,height,tooltip, bg, fg, anchor, font, padx,pady, wraplength, command, cursor,justify,state,relief,bd, imgstretch,show,None,None,None]if(ctlName=='button'):lstOne[0]='button'lstOne[10]='center'elif(ctlName=='label'):lstOne[0]='label'elif(ctlName=='edit' or ctlName=='edit_one'):lstOne[0]='edit_one' elif(ctlName=='edit_mu'):lstOne[0]='edit_mu' elif(ctlName=='edit_pas'):lstOne[0]='edit_pas'lstOne[22]='*'elif(ctlName=='edit_readonly'):lstOne[0]='edit_readonly' elif(ctlName=='image'): #实际也是标签控件lstOne[0]='image'lstOne[2]='图象文件.png'lstOne[10]='center' #图象在正中心显示lstOne[21]=False #图象不拉伸elif(ctlName=='line'):lstOne[0]='line'lstOne[6]=1 #线宽1个像素lstOne[10]='x' #横向直线#为窗体初始化一些控件 def initWidgets(self,lstWidgets):"""函数功能: 按lstWidgets中的列表数据初始化一些控件在窗体上显示出来:"w"=左居中对齐 "n"=顶居中 "w"=左居中 "nw"= 左上角 "se"=右下角 "center"=正中间0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25参数: lstWidgets 列表变量=[objType, objName text, x, y, width,height, tooltip, bg, fg, anchor, font, padx, pady, wraplength, command=None, cursor, justify, state, relief, bd, imgstretch, show,None,None,None]示例 'label','labelInfo', '标签内容', 10, 20, 50, 15 ,'提示信息, 'gray', 'black', 'nw', ('宋体',11), 2, 2 300 self.函数名 'hand2' 'left' 'normal' 'groove' 1 True , '*',None,None,None 说明: 控件类型字符串 控件名称 标签或图象内容 屏幕位置 控件宽高 控件的上弹提示信息 背景色 前景色 单行对齐 字体 内定位边距 多行换行宽度 指定命令函数 鼠标悬停样式 多行对齐方式 状态 边框类型 边框宽度 图象是否拉伸 字符掩码类型: 字符串 字符串 字符串 整数(像素) 整数(像素) 字符串 字符串 字符串 字符串 元组 整数(像素) 整数(像素) 函数对象 字符串 字符串 字符串 字符串 整数(像素) 布尔 字符串对line 线宽 'x'/'y'/'xy' 矩形时线宽对edit 密码 """self.lstWidgets=lstWidgetsself.tk_frame=tk.Frame(self.root,bg=self.def_bgCol) #定义一个位于窗口上的框架子区域,创建的控件也以以此子窗体作为父类窗体self.tk_frame.pack(fill=tk.BOTH,expand=True)for obj in (lstWidgets):ctlType=obj[0].lower() #控件类型不区分大小写objName=obj[1] #控件名称要区分大小写if(obj[8] is None):obj[8]=self.def_bgCol #如没有指定背景色,同窗体颜色elif(obj[8]=='SYS'):obj[8]=self.def_bgCol if(obj[9] is None):obj[9]='black' elif(obj[9]=='SYS'):obj[9]='black' if(ctlType=='button'): #按纽控件 button=tk.Button(self.root,text=obj[2],bg=obj[8],fg=obj[9],anchor=obj[10],font=obj[11],padx=obj[12],pady=obj[13],wraplength=obj[14],command=obj[15],cursor=obj[16],justify=obj[17],state=obj[18],relief=obj[19],bd=obj[20]) button.place(x=obj[3],y=obj[4],width=obj[5],height=obj[6]) #这里面的widtht和height为像素了,若放在上行:widtht和height:值为第一个字符的宽度和高度,一个字符为值为1if(objName=='button_OK'): #对名字符合OK按纽的指定绑定响应函数button.bind("<Button-1>",self.on_button_OK) #在command参数中指定command=时,对应的响应函数参数可以不要enentelif(objName=='button_Cancle'):button.bind("<Button-1>",self.on_button_Cancle) else:button.bind("<Button-1>",self.on_def_Left_Click) #同时绑定本类中的单南事件函数,obj[13]可以为None,也可以为主进程中的一函数,可以同时响应两处的函数回调self.dicWidgets[obj[1]]=[self.widgetsID,ctlType,obj[1],button,obj[7],self.on_button_OK] ToolTip(button,obj[7],20,-30) #绑定tooltip功能self.widgetsID+=1elif(ctlType=='label'): #标签控件 label1=tk.Label(self.root,text=obj[2],bg=obj[8],fg=obj[9],anchor=obj[10],font=obj[11],padx=obj[12],pady=obj[13],wraplength=obj[14],command=None,cursor=obj[16],justify=obj[17],state=obj[18],relief=obj[19],bd=obj[20]) #对标签不能有command参数 label1.place(x=obj[3],y=obj[4],width=obj[5],height=obj[6]) label1.bind("<Button-1>",self.on_def_Left_Click) #绑定标签控件单击时的响应函数self.dicWidgets[obj[1]]=[self.widgetsID,ctlType,obj[1],label1,obj[7],self.on_def_Left_Click] label1.bind("<Enter>",self.on_win_enter) #绑定进入窗体事件 label1.bind("<Leave>",self.on_win_leave) #绑定离开窗体事件ToolTip(label1,obj[7],0,25) #绑定tooltip功能self.widgetsID+=1elif(ctlType=='edit_one' or ctlType=='edit' or ctlType=='edit_readonly'): #单行文本控件edit1=tk.Entry(self.root,text=obj[2],background=obj[8],foreground=obj[9],font=obj[11],command=None,cursor=obj[16],justify=obj[17],state=obj[18]) #对编辑框不能有参数: edit1.place(x=obj[3],y=obj[4],width=obj[5],height=obj[6])edit1.bind("<KeyRelease>",self.on_KeyRelease) #绑定编辑框按键松开时的事件edit1.bind("<Button-1>",self.on_def_Left_Click) #绑定标签控件单击时的响应函数self.dicWidgets[obj[1]]=[self.widgetsID,ctlType,obj[1],edit1,obj[7],self.on_KeyRelease] edit1.insert(0,obj[1])ToolTip(edit1,obj[7],0,25) #绑定tooltip功能self.widgetsID+=1if(ctlType=='edit_readonly' ): #单行只读文本控件edit1.config(state='readonly')elif(ctlType=='edit_mu'): #多行文本控件edit2=tk.Text(self.root,bg=obj[8],fg=obj[9],font=obj[11],padx=obj[12],pady=obj[13],command=None,cursor=obj[16],state=obj[18],relief=obj[19],bd=obj[20],wrap=tk.WORD) # #scrollbar=ttk.Scrollbar(self.root,orient=tk.VERTICAL,command=text_area_scroll.yview)edit2.place(x=obj[3],y=obj[4],width=obj[5],height=obj[6])edit2.bind("<KeyRelease>",self.on_KeyRelease) #绑定编辑框按键松开时的事件 edit2.bind("<Button-1>",self.on_def_Left_Click) #绑定标签控件单击时的响应函数self.dicWidgets[obj[1]]=[self.widgetsID,ctlType,obj[1],edit2,obj[7],self.on_KeyRelease] edit2.insert('1.0',obj[1])ToolTip(edit2,obj[7],0,25) #绑定tooltip功能self.widgetsID+=1elif(ctlType=='edit_pas'): #密码文本控件edit3=tk.Entry(self.root,text=obj[2],background=obj[8],foreground=obj[9],font=obj[11],command=None,cursor=obj[16],justify=obj[17],state=obj[18],show=obj[22]) #对标签不能有command参数 edit3.place(x=obj[3],y=obj[4],width=obj[5],height=obj[6])edit3.bind("<KeyRelease>",self.on_KeyRelease) #绑定编辑框按键松开时的事件edit3.bind("<Button-1>",self.on_def_Left_Click) #绑定标签控件单击时的响应函数self.dicWidgets[obj[1]]=[self.widgetsID,ctlType,obj[1],edit3,obj[7],self.on_KeyRelease] edit3.insert(0,obj[1])ToolTip(edit3,obj[7]),0,25 #绑定tooltip功能self.widgetsID+=1 elif(ctlType=='image'): #图像控件,实际也是标签控件if(os.path.exists(self.getFilePath(obj[2]))):pilImage=Image.open(self.getFilePath(obj[2]))if(obj[19]): #图象要拉伸显示pilImage=pilImage.resize((obj[5],obj[6]),Image.LANCZOS)tkImage=ImageTk.PhotoImage(pilImage)img=tk.Label(self.root,image=tkImage,bg=obj[8],fg=obj[9],anchor=obj[10],font=obj[11],padx=obj[12],pady=obj[13],wraplength=obj[14],command=None,cursor=obj[16],justify=obj[17],state=obj[18],relief=obj[19],bd=obj[20]) #对标签不能有command参数 img.place(x=obj[3],y=obj[4],width=obj[5],height=obj[6])img.image=tkImage #保持引用,防止被垃圾回收img.bind("<Button-1>",self.on_def_Left_Click) #绑定标签控件单击时的响应函数self.dicWidgets[obj[1]]=[self.widgetsID,ctlType,obj[1],img,obj[7],self.on_def_Left_Click] ToolTip(img,obj[7],0,25) #绑定tooltip功能self.widgetsID+=1elif(ctlType=='line'): #屏幕上画线条,实际是Frame控件self.draw_line(self.root,widgetName=obj[1],x=obj[3],y=obj[4],length=obj[5],linewidth=obj[6],orientation=obj[10],linecol=obj[9],rectWidth=obj[20]) #对xy双向画矩形时, 用obj[19]表示线宽,画单线时,用obj[6]表示线宽elif(ctlType=='canves'): #画布,可以在画布上画任意图形, 未扩展passelse:continue#用Frame控件在窗体上绘制指定位置的直线段def draw_line(self,parent,widgetName,x,y,length,linewidth,orientation='x',linecol='black',linetype='',rectWidth=1,**kwargs):if(orientation=='x' or orientation=='h' or orientation=='horizontal'):orientation='horizontal'line=tk.Frame(parent,height=linewidth,width=length,bg=linecol)line.place(x=x,y=y)self.dicWidgets[widgetName]=[self.widgetsID,'line',widgetName,line,'x'] self.widgetsID+=1elif(orientation=='y' or orientation=='v' or orientation=='vertical'):orientation='vertical'line=tk.Frame(parent,width=linewidth,height=length,bg=linecol)line.place(x=x,y=y)self.dicWidgets[widgetName]=[self.widgetsID,'line',widgetName,line,'y'] self.widgetsID+=1elif(orientation=='xy' or orientation=='hv' or orientation=='vh'):self.draw_line(parent,f'{widgetName}_xt',x,y,length,rectWidth,'x',linecol,'',**kwargs)self.draw_line(parent,f'{widgetName}_xb',x,y+linewidth,length,rectWidth,'x',linecol,'',**kwargs) self.draw_line(parent,f'{widgetName}_yl',x,y,linewidth,rectWidth,'y',linecol,'',**kwargs)self.draw_line(parent,f'{widgetName}_yr',x+length,y,linewidth,rectWidth,'y',linecol,'',**kwargs)else:print('绘制直线(Frame控件)有错误')#得到本窗体任何一个控件对象列表[ID,控件名称对应对象,左键单击时的函数,右键单击时的函数,鼠标移动时的函数,内容发生变化时的函数......] def getWinWidgets(self,objName):lstobj=self.dicWidgets.get(objName,None)if(lstobj is not None):return lstobj #得到数组中的对象else:return None#得到指定控件的文本:适用了标签,编辑框,按纽按件 def getWidgetsText(self,objName): lstobj=self.getWinWidgets(objName)text=''if(lstobj is not None):if(lstobj[1]=='button' or lstobj[1]=='label'):text=lstobj[3].cget('text')elif(lstobj[1]=='edit' or lstobj[1]=='edit_one' or lstobj[1]=='edit_pas' ):text=lstobj[3].get()elif(lstobj[1]=='edit_mu' or lstobj[1]=='edit_readonly' ):text=lstobj[3].get("1.0",tk.END)return text#设置指定控件的文本和颜色def setWidgetsText(self,objName,txt,fgCol=None,bgCol=None,selfgCol=None,selbgCol=None):lstobj=self.getWinWidgets(objName)if(lstobj is not None):if(lstobj[1]=='button'):lstobj[3].config(text=txt)if(fgCol is not None):lstobj[3].config(fg=fgCol)if(bgCol is not None):lstobj[3].config(bg=bgCol)if(selfgCol is not None):lstobj[3].config(activeforeground=selfgCol)if(selbgCol is not None):lstobj[3].config(activebackground=selbgCol)elif(lstobj[1]=='label'):lstobj[3].config(text=txt)if(fgCol is not None):lstobj[3].config(fg=fgCol)if(bgCol is not None):lstobj[3].config(bg=bgCol)elif(lstobj[1]=='edit'or lstobj[1]=='edit_one' or lstobj[1]=='edit_pas'):lstobj[3].delete(0,tk.END)lstobj[3].insert(0,txt)if(fgCol is not None):lstobj[3].config(foreground=fgCol)if(bgCol is not None):lstobj[3].config(background=bgCol)elif(lstobj[1]=='edit_mu' or lstobj[1]=='edit_readonly'):lstobj[3].delete(0.0,tk.END)lstobj[3].insert(0.0,txt)if(fgCol is not None):lstobj[3].config(fg=fgCol)if(bgCol is not None):lstobj[3].config(bg=bgCol)if(selfgCol is not None):lstobj[3].config(selectforeground=selfgCol)if(selbgCol is not None):lstobj[3].config(selectbackground=selbgCol) #设置指定控件的可见/不可见def setWidgetsVisable(self,objName,bVisable=True):lstobj=self.getWinWidgets(objName)if(lstobj is not None):if(bVisable):lstobj[3].place()else:lstobj[3].place_forget()#设置窗体是否可用(注:对控件即使是设置成状态不可用,但单击时同状态无关,需要先得到状态,再决定是否执行)def setWidgetsEnable(self,objName,bEnable=True):lstobj=self.getWinWidgets(objName)if(lstobj is not None):if(bEnable):lstobj[3].config(state='normal')else:lstobj[3].config(state='disabled')#得到指定控件的显示状态def getWidgetsEnable(self,objName,bEnable=True):lstobj=self.getWinWidgets(objName)if(lstobj is not None):if(bEnable):lstobj[3].config(state='normal')else:lstobj[3].config(state='disabled')#单击窗体上的【确定】按纽控件时def on_button_OK(self,event):self.root.destroy() #彻底关闭窗口objName='button_Ok'lstobj=self.getWinWidgets(objName)if(lstobj is not None):lstDatas=[lstobj,'回调给主进程:单击的【确定】按纽'] #[id,类型,控件对象]self.callback(None,objName,lstDatas)return tkEx.TK_OK #单击窗体上的【取消】按纽控件时def on_button_Cancle(self,enent):self.root.destroy() #self.showTkWindows(False)return tkEx.TK_CANCLE#窗体及所有控件的默认单击事件def on_def_Left_Click(self,event):print('本类中的窗体(或窗体中的控件)单击事件') curObj=event.widgetfor keys in self.dicWidgets:obj=self.dicWidgets.get(keys,None)if(obj is not None):if(curObj==obj[3]): #对窗体,本行比较不会成功print(f'\n你单击的是控件ID={obj[0]},控件类型={obj[1]},名称="{obj[2]}"\n')break#显示/隐藏窗体def showTkWindows(self,bShow=True):if(bShow):self.root.deiconify()else:self.root.withdraw()def on_KeyRelease(self,event):print('本类中的按键松开时的事件')#窗体左压下时的响应函数def onWin_Left_Press(self,event):print(f'窗体被左键压下x={event.x},y={event.y}')self.win_x=event.xself.win_y=event.y#窗体左键单击时的响应函数def onWin_Right_Click(self,event):print(f'窗体右键被单击x={event.x},y={event.y}')#窗体中键单击时的响应函数def onWin_Mid_Click(self,event):print(f'窗体中键被单击x={event.x},y={event.y}') #鼠标在窗上的滑动时的事件响应函数def onWin_Mouse_Move(self,event):print(f'窗体鼠标移动x={event.x},y={event.y}')self.win_x=event.xself.win_y=event.y#窗体左键释放时的响应函数def onWin_Left_Release(self,event):self.win_x=event.xself.win_y=event.y#窗体鼠标移动时的响应函数def onWin_move(self,event):if(self.dragmove):if(self.win_x is not None and self.win_y is not None and self.win_x is not None and self.win_y is not None):deltax=event.x-self.win_xdeltay=event.y-self.win_ynewx=int(self.root.winfo_x())+deltaxnewy=int(self.root.winfo_y())+deltayself.root.geometry(f'+{newx}+{newy}')#窗体缩放事件def on_window_resize(self,event):if(self.bgImgFile is not None):self.draw_bgimg()if(self.width!=self.root.winfo_width() or self.height!=self.root.winfo_height()): #窗体尺寸发生了变化self.width=self.root.winfo_width()self.height=self.root.winfo_height()print(f'{self.width}x{self.height}+{self.root.winfo_x()}+{self.root.winfo_y()},{self.root.winfo_width()}x{self.root.winfo_height()}')self.root.geometry(f"{self.width}x{self.height}") if(self.bgImgFile is not None):self.draw_bgimg()#进入窗体事件def on_win_enter(self,event):curObj=event.widgetfor keys in self.dicWidgets:obj=self.dicWidgets.get(keys,None)if(obj is not None):if(curObj==obj[3]): #对窗体,本行比较不会成功print(f'\n你进入的控件ID={obj[0]},控件类型={obj[1]},名称="{obj[2]}"\n')tooltipstr=obj[4]if(len(tooltipstr)>0): #有提示内容时,才显示提示信息#ToolTip(obj[3],tooltipstr)passbreak#进入窗体事件def on_win_leave(self,event):curObj=event.widgetfor keys in self.dicWidgets:obj=self.dicWidgets.get(keys,None)if(obj is not None):if(curObj==obj[3]): #对窗体,本行比较不会成功print(f'\n你离开的控件ID={obj[0]},控件类型={obj[1]},名称="{obj[2]}"\n')tooltipstr=obj[4]if(len(tooltipstr)>0): #有提示内容时,才销毁提示信息passbreak#调用此函数后,窗体将显示出来,此函数后不能再跟代码了,只能同窗体中的控件发生消息响应了def startTkWindows(self):if(not self.bmainloop):self.bmainloop=Trueself.root.mainloop()
###################################################
#工具提示类
class ToolTip:"""为控件增加tooltip工具提示功能"""def __init__(self,widget,text,offset_x=20,offset_y=-30,delaytime=200):self.widget=widgetself.text=textself.offset_x=offset_xself.offset_y=offset_yself.delytime=delaytimeself.tip_window=Noneself.id=Noneself.widget.bind('<Enter>',self.schedule)self.widget.bind('<Leave>',self.hide_tip)self.widget.bind('<Motion>',self.updatePos)self.getxy=False def schedule(self,event=None):self.hide_tip()self.id=self.widget.after(self.delytime,self.show_tip)def show_tip(self,event=None):if(self.tip_window or not self.text or self.text==''):return#获取鼠标绝对坐标x=self.widget.winfo_pointerx()+self.offset_xy=self.widget.winfo_pointery()+self.offset_yself.getxy=True#创建提示窗口self.tip_window=tk.Toplevel(self.tip_window)self.tip_window.update_idletasks()self.tip_window.wm_overrideredirect(True) #隐藏窗口装饰self.tip_window.wm_geometry(f"+{x}+{y}") #计算文字高度(用于动态调整位置)test_label=tk.Label(self.tip_window,text=self.text)test_label.update_idletasks()text_height=test_label.winfo_reqheight() test_label.destroy()#重新定位y=self.widget.winfo_pointery()-text_height-10 #上移文字高度10像素self.tip_window.wm_geometry(f"+{x}+{y}")label=tk.Label(self.tip_window,text=self.text,bg='yellow',relief='solid',borderwidth=1,padx=5,pady=2,justify='left')label.pack()def updatePos(self,event):if self.tip_window:x=self.widget.winfo_pointerx()+self.offset_xy=self.widget.winfo_pointery()+self.offset_yself.tip_window.wm_geometry(f"+{x}+{y}")def hide_tip(self,event=None):"""隐藏提示框"""if(self.tip_window):self.tip_window.destroy()self.tip_window=Noneself.widget.after_cancel(self.schedule)self.widget.update()
示例代码中要用到的两个图片1.png(作为窗体背景),2.png(标签控件显示图像)如下: