tkinter创建嵌套子窗口

引言

在tkinter窗口中实现窗口嵌套,在一定程度上减少了桌面窗口复杂程度,可以给电脑操作系统带来较为整洁的桌面环境。父窗口的内部功能字窗口,可以嵌套在父窗口中。

虽然tkinter本身没有这样组件或功能,但我们可以进行一定程度的拓展。

各种方法

实现tkinter嵌套窗口的方式有很多,一下是常见的三种:

  1. 绑定鼠标事件,使子窗口随父窗口移动
    • 说明:绑定鼠标的<MouseMotion>,同步移动窗口
    • 优点:方便编写,代码量小
    • 缺点:实质上还是各个窗口自成一例,影响视觉效果
  2. 使用aardio语言,在winform窗口中嵌套tkinter程序和各种应用程序
    • 说明:aardio介绍查看官网。通过aardio中的win库,嵌套Python主程序,使窗口显示在winform窗口中
    • 优点:真正的内嵌窗口,可以实现多窗口内部显示
    • 缺点:代码量大,程序需要使用额外10M+的运行空间
  3. 通过tkinter.frame,代替实现
    • 说明:使用frame控件,至少还原50%的窗口结构,这是一种折中方式
    • 优点:真正的内嵌功能组件,可以多个显示
    • 缺点:无法实现100%的窗口功能

根据这三种方法的优劣取舍,这篇文章主要讲解第三种方法,拓展frame

构思

窗口最主要的部分是标题栏,因此我们先要实现标题栏的功能。
在编写代码之前,先想想那些功能不需要:
1、缩小键(缩小了怎么显示?当然,你可以建立一个任务栏)
2、图标(嵌套窗口只是功能窗口,图标不重要)
现在,我们减少了两个功能和十几行代码可以开工了。

创建窗口主体

为了方便窗口中再加组件,我们使用frame作为底层。

from tkiner import Button as tkButton,Tk,Frame,Label#...

#...

class ChildFrame(Frame):
   '''
   这是一个建立在Frame之上的子窗口控件
   该子窗口可以嵌入tkinter父窗口,但建议是在窗口而不是在控件中
   子窗口可以移动也可以不移动,通过属性设定来改变子窗口的状态
   需要注意的是,在内嵌子窗口导入控件时,注意起始高度为32

   因为组件限制,ChildFrame只能够实现真正窗口的一小部分功能
   '''

   def __init__(self,root,title_color,title='title',color='#f0f0f0'):
      self.root=root
      super().__init__(root,bg=color)#底层
      self.title=title
      self.tc=title_color
      self.titlebar=Frame(self,bg=self.tc)#标题栏
      Label(self.titlebar,text=self.title,bg=self.tc,fg='white').place(x=5,y=5)
      self.conbar=Frame(self.titlebar,bg=self.tc)
      self.conbar.pack(side='right')#工具栏
      self.desb=tkButton(self.conbar,text='×',font=('宋体',15),bg=self.tc,fg='white',activeforeground='white',activebackground='red',relief='flat',command=self.destroy)
      self.desb.pack(side='right')#关闭按钮
      self.desb.bind('<Enter>',lambda event:self.desb.configure(background='red'))
      self.desb.bind('<Leave>',lambda event:self.desb.configure(background=self.tc))#当鼠标划过时有红色样式
      self.expandb=tkButton(self.conbar,text='□',font=('宋体',15),fg='white',bg=self.tc,activeforeground='white',activebackground=self.tc,relief='flat',command=self.expandwin)
      self.expandb.pack(side='right')#最大化
      self.titlebar.pack(fill='x')
      self.titlebar.bind('<Button-1>',self._startmove,add='+')#移动
      self.titlebar.bind('<B1-Motion>',self._movewin,add='+')

可以看出,工具栏已经具备了关闭和放大的按钮以及标题,同时标题栏还拥有拖动功能。
但是,在完成余下的工作的同时,先要让窗口显示出来。

def show(self,x,y,width,height):#显示窗口
   self.wd=width
   self.hi=height
   self.oralx=x
   self.oraly=y
   self.place(x=x,y=y,width=width,height=height)

那么接下来我们将完成余下的功能。

关闭窗口

def destroy(self):#销毁窗口
   self.place_forget()

最大化

既然可以最大化窗口当然也要恢复窗口大小。

最大化窗口

def expandwin(self):#最大化窗口
   #记录窗口的起始位置
   self.oralx=self.winfo_x()
   self.oraly=self.winfo_y()
   self.root.update()
   w=self.root.winfo_width()
   h=self.root.winfo_height()
   self.place(x=0,y=0,width=w,height=h)
   self.expandb['text']='◪'
   self.expandb['command']=self.backexpandwin
   self.lockwin()#最大化不能移动

恢复窗口

def backexpandwin(self):#恢复窗口大小
   self.expandb['text']='□'
   self.expandb['command']=self.expandwin
   self.place(x=self.oralx,y=self.oraly,width=self.wd,height=self.hi)
   self.activewin()

窗口移动

def _startmove(self,event):#记录开始移动的坐标
   self.startx=event.x
   self.starty=event.y

def _movewin(self,event):#移动窗口
   self.place(x=self.winfo_x()+(event.x-self.startx),y=self.winfo_y()+(event.y-self.starty))

额外功能

这里的额外功能主要是针对子窗口的特殊功能和尽可能模仿主窗口的功能,可以丰富组件的使用。这里给出示例,可以自己拓展。

def lockwin(self):#禁止窗口移动
   self.titlebar.unbind('<B1-Motion>')

def activewin(self):#允许窗口移动
   self.titlebar.bind('<B1-Motion>',self._movewin,add='+')

def noexpand(self):#是否支持放大
   self.expandb.pack_forget()
def haveexpand(self):#支持放大
   self.expandb.pack()

完整代码

class ChildFrame(Frame):
   '''
   这是一个建立在Frame之上的子窗口控件
   该子窗口可以嵌入tkinter父窗口,但建议是在窗口而不是在控件中
   子窗口可以移动也可以不移动,通过属性设定来改变子窗口的状态
   需要注意的是,在内嵌子窗口导入控件时,注意起始高度为32

   因为组件限制,ChildFrame只能够实现真正窗口的一小部分功能
   '''

   def __init__(self,root,title_color,title='title',color='#f0f0f0'):
      self.root=root
      super().__init__(root,bg=color)#底层
      self.title=title
      self.tc=title_color
      self.titlebar=Frame(self,bg=self.tc)#标题栏
      Label(self.titlebar,text=self.title,bg=self.tc,fg='white').place(x=5,y=5)
      self.conbar=Frame(self.titlebar,bg=self.tc)
      self.conbar.pack(side='right')#工具栏
      self.desb=tkButton(self.conbar,text='×',font=('宋体',15),bg=self.tc,fg='white',activeforeground='white',activebackground='red',relief='flat',command=self.destroy)
      self.desb.pack(side='right')#关闭按钮
      self.desb.bind('<Enter>',lambda event:self.desb.configure(background='red'))
      self.desb.bind('<Leave>',lambda event:self.desb.configure(background=self.tc))#当鼠标划过时有红色样式
      self.expandb=tkButton(self.conbar,text='□',font=('宋体',15),fg='white',bg=self.tc,activeforeground='white',activebackground=self.tc,relief='flat',command=self.expandwin)
      self.expandb.pack(side='right')#最大化
      self.titlebar.pack(fill='x')
      self.titlebar.bind('<Button-1>',self._startmove,add='+')#移动
      self.titlebar.bind('<B1-Motion>',self._movewin,add='+')

   def show(self,x,y,width,height):#显示窗口
      self.wd=width
      self.hi=height
      self.oralx=x
      self.oraly=y
      self.place(x=x,y=y,width=width,height=height)

   def destroy(self):#销毁窗口
      self.place_forget()

   def _startmove(self,event):#记录开始移动的坐标
      self.startx=event.x
      self.starty=event.y

   def _movewin(self,event):#移动窗口
      self.place(x=self.winfo_x()+(event.x-self.startx),y=self.winfo_y()+(event.y-self.starty))

   def lockwin(self):#禁止窗口移动
      self.titlebar.unbind('<B1-Motion>')

   def activewin(self):#允许窗口移动
      self.titlebar.bind('<B1-Motion>',self._movewin,add='+')

   def backexpandwin(self):#恢复窗口大小
      self.expandb['text']='□'
      self.expandb['command']=self.expandwin
      self.place(x=self.oralx,y=self.oraly,width=self.wd,height=self.hi)
      self.activewin()

   def expandwin(self):#最大化窗口
      #记录窗口的起始位置
      self.oralx=self.winfo_x()
      self.oraly=self.winfo_y()
      self.root.update()
      w=self.root.winfo_width()
      h=self.root.winfo_height()
      self.place(x=0,y=0,width=w,height=h)
      self.expandb['text']='◪'
      self.expandb['command']=self.backexpandwin
      self.lockwin()#最大化不能移动

   def noexpand(self):#是否支持放大
      self.expandb.pack_forget()
   def haveexpand(self):#支持放大
      self.expandb.pack()

效果

在这里插入图片描述
Tin知识库

结语

tkinter的组件具有很强的拓展性和兼容性,通过二次封装可以实现很多丰富GUI使用的效果。

☀tkinter创新☀

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值