[tkinter美化] 脱离系统样式的窗口(三系统通用)

前言

很多人都说tkinter不行,这没有,那没有,其中就包括窗口样式自定义。其实这些都可以用代码自己解决,为了方便其他使用tkinter的人,我将我之前写的代码贴出来

代码

Toplvel.py

#本代码基于MIT协议开源
import tkinter as tk
try:
    import mss
    from PIL import Image
    from PIL import ImageTk
    graber = mss.mss()
    enable_grab = 1
except:
    enable_grab = 0
#enable_grab = 0
############### StyledToplevel ###############
class Toplevel(tk.Toplevel):
    """
    a Toplevel with Style
    initalizal configure:
        eight risizers  -> Toplevel.rp_<direction>
        a titlebar      -> Toplevel.title
    you can change the function _configure_size
    to change Toplevel's action
    please use like a normal Toplevel.
    """
    def __init__(self,*a,**b):
        super(self.__class__,self).__init__(*a,**b)
        self.overrideredirect(1)
        self._icon = tk.Toplevel(self)
        self._grab = tk.Label(self._icon)
        self._grab.pack(expand=1,fill="both")
        self.title = _Titlebar(self,bg="white")
        self.title.pack()
        self.aera = tk.Frame(self,bg="#f0f0f0")
        self.rp_n = tk.Frame(self,cursor="sb_v_double_arrow")
        self.rp_s = tk.Frame(self,cursor="sb_v_double_arrow")
        self.rp_w = tk.Frame(self,cursor="sb_h_double_arrow")
        self.rp_e = tk.Frame(self,cursor="sb_h_double_arrow")
        self.rp_nw = tk.Frame(self,cursor="size_nw_se")
        self.rp_ne = tk.Frame(self,cursor="size_ne_sw")
        self.rp_sw = tk.Frame(self,cursor="size_ne_sw")
        self.rp_se = tk.Frame(self,cursor="size_nw_se")
        self._setup()
        self.geometry("200x200+0+0")
        self._configure_size()
    def _setup(self):
        self._resizable_w = 1
        self._resizable_h = 1
        self._zoom = 0
        self._zoomable = 1
        self._icon.bind("<FocusIn>",self._icon_focusin,1)
        self.rp_n.bind("<B1-Motion>",_move_n,1)
        self.rp_s.bind("<B1-Motion>",_move_s,1)
        self.rp_w.bind("<B1-Motion>",_move_w,1)
        self.rp_e.bind("<B1-Motion>",_move_e,1)
        self.rp_nw.bind("<B1-Motion>",_move_nw,1)
        self.rp_ne.bind("<B1-Motion>",_move_ne,1)
        self.rp_sw.bind("<B1-Motion>",_move_sw,1)
        self.rp_se.bind("<B1-Motion>",_move_se,1)
        self._icon_trace()
        self._update_size()
    def _configure_size(self):
        self.update()
        # geo = self.geometry()
        # w,h = geo.split("x")
        # h,x,y = h.split("+")
        # w,h,x,y = int(w),int(h),int(x),int(y)
        w = int(self.winfo_width())
        h = int(self.winfo_height())
        x = int(self.winfo_x())
        y = int(self.winfo_y())
        self.rp_n.place(x=2,y=0,width=w-4,height=2)
        self.rp_s.place(x=2,y=h-2,width=w-4,height=2)
        self.rp_w.place(x=0,y=2,width=2,height=h-4)
        self.rp_e.place(x=w-2,y=2,width=2,height=h-4)
        self.rp_nw.place(x=0,y=0,width=2,height=2)
        self.rp_ne.place(x=0,y=w-2,width=2,height=2)
        self.rp_sw.place(x=0,y=h-2,width=2,height=2)
        self.rp_se.place(x=w-2,y=h-2,width=2,height=2)
        self.title.place(x=2,y=2,width=w-4,height=20)
        self.aera.place(x=2,y=22,width=w-4,height=h-24)
    def _icon_trace(self):
        if self.winfo_ismapped():
            geo = self.geometry()
            w,h = geo.split("x")
            h,x,y = h.split("+")
            w,h,x,y = int(w),int(h),int(x),int(y)
            self._icon.geometry("%dx%d+%d+%d"%(w//2,h//2,x+w//4,y+h//4))
            #self._icon.update()
        self.after(10,self._icon_trace)
    def _icon_focusin(self,e):
        self._icon.update()
        self._icon.iconify()
        self.deiconify()
    def iconify(self):
        if self.winfo_ismapped():
            geo = self.geometry()
            w,h = geo.split("x")
            h,x,y = h.split("+")
            w,h,x,y = int(w),int(h),int(x),int(y)
            # self._icon.geometry("%dx%d+%d+%d"%(w//2,h//2,x+w//4,y+h//4))
            #self._icon.deiconify()
            if enable_grab:
                monitor = {"top":y,"left":x,"width":w,"height":h}
                mss_image = graber.grab(monitor)
                pil_image = Image.new("RGB",(w,h))
                pil_image.frombytes(mss_image.rgb)
                tk_image = ImageTk.PhotoImage(image=pil_image.resize((w//2,h//2)))
                #tk.Label(self.aera,image=tk_image).pack()
                self._grab["image"] = tk_image
                self._grab.image = tk_image
                self._icon.update()
            self.withdraw()
    def _update_size(self):
        self._configure_size()
        self.after(100,self._update_size)
    def resizable(self,x=None,y=None):
        if x is not None:
            self._resizable_w = x
        if y is not None:
            self._resizable_h = y
        return self._resizable_w,self._resizable_h
    def zoom(self):
        z = self._zoom
        if z:
            self.geometry(z)
            self._zoom = 0
        else:
            if self._zoomable:
                self._zoom = self.geometry()
                w = self.winfo_screenwidth()
                h = self.winfo_screenheight()
                self.geometry("%dx%d+0+0"%(w,h))
    def zoomable(self,boolean=1):
        self._zoomable = boolean

class _Titlebar(tk.Frame):
    "Move Window"
    def __init__(self,master=None,*a,**b):
        super().__init__(master=master,*a,**b)
        self.bind("<Button-1>",self._setpos,1)
        self.bind("<B1-Motion>",self._move,1)
        self.bind("<Double-Button-1>",lambda e:self.master.zoom(),1)
        self.mx=0;self.my=0
    def _setpos(self,event):
        x = self.master.winfo_x()
        y = self.master.winfo_y()
        self.mx = event.x_root-x
        self.my = event.y_root-y
    def _move(self,event):
        mx,my = self.mx,self.my
        self.master.geometry("+{}+{}".format(event.x_root-mx,event.y_root-my))
def _move_n(e):
    "Resizable window"
    widget = e.widget
    window = widget.master
    rex,rey = window.resizable()
    if not rey or window._zoom:
        return
    rx,ry = window.winfo_x(),window.winfo_y()
    rw,rh = window.winfo_width(),window.winfo_height()
    mx,my = e.x_root,e.y_root
    bx,by = rx+rw,ry+rh
    bw,bh = bx,by
    window.geometry("{}x{}+{}+{}".format(rw,by-my,rx,my))
def _move_s(e):
    widget = e.widget
    window = widget.master
    rex,rey = window.resizable()
    if not rey or window._zoom:
        return
    rx,ry = window.winfo_x(),window.winfo_y()
    rw,rh = window.winfo_width(),window.winfo_height()
    mx,my = e.x_root,e.y_root
    bx,by = rx+rw,ry+rh
    bw,bh = bx,by
    window.geometry("{}x{}+{}+{}".format(rw,my-ry,rx,ry))
def _move_w(e):
    widget = e.widget
    window = widget.master
    rex,rey = window.resizable()
    if not rex or window._zoom:
        return
    rx,ry = window.winfo_x(),window.winfo_y()
    rw,rh = window.winfo_width(),window.winfo_height()
    mx,my = e.x_root,e.y_root
    bx,by = rx+rw,ry+rh
    bw,bh = bx,by
    window.geometry("{}x{}+{}+{}".format(bw-mx,rh,mx,ry))
def _move_e(e):
    widget = e.widget
    window = widget.master
    rex,rey = window.resizable()
    if not rex or window._zoom:
        return
    rx,ry = window.winfo_x(),window.winfo_y()
    rw,rh = window.winfo_width(),window.winfo_height()
    mx,my = e.x_root,e.y_root
    bx,by = rx+rw,ry+rh
    bw,bh = bx,by
    window.geometry("{}x{}+{}+{}".format(mx-rx,rh,rx,ry))
def _move_nw(e):
    widget = e.widget
    window = widget.master
    rex,rey = window.resizable()
    if not (rex and rey) or window._zoom:
        return
    rx,ry = window.winfo_x(),window.winfo_y()
    rw,rh = window.winfo_width(),window.winfo_height()
    mx,my = e.x_root,e.y_root
    bx,by = rx+rw,ry+rh
    bw,bh = bx,by
    window.geometry("{}x{}+{}+{}".format(bw-mx,by-my,mx,my))
def _move_ne(e):
    widget = e.widget
    window = widget.master
    rex,rey = window.resizable()
    if not (rex and rey) or window._zoom:
        return
    rx,ry = window.winfo_x(),window.winfo_y()
    rw,rh = window.winfo_width(),window.winfo_height()
    mx,my = e.x_root,e.y_root
    bx,by = rx+rw,ry+rh
    bw,bh = bx,by
    window.geometry("{}x{}+{}+{}".format(mx-rx,by-my,rx,my))
def _move_sw(e):
    widget = e.widget
    window = widget.master
    rex,rey = window.resizable()
    if not (rex and rey) or window._zoom:
        return
    rx,ry = window.winfo_x(),window.winfo_y()
    rw,rh = window.winfo_width(),window.winfo_height()
    mx,my = e.x_root,e.y_root
    bx,by = rx+rw,ry+rh
    bw,bh = bx,by
    window.geometry("{}x{}+{}+{}".format(bw-mx,my-ry,mx,ry))
def _move_se(e):
    widget = e.widget
    window = widget.master
    rex,rey = window.resizable()
    if not (rex and rey) or window._zoom:
        return
    rx,ry = window.winfo_x(),window.winfo_y()
    rw,rh = window.winfo_width(),window.winfo_height()
    mx,my = e.x_root,e.y_root
    bx,by = rx+rw,ry+rh
    bw,bh = bx,by
    window.geometry("{}x{}+{}+{}".format(mx-rx,my-ry,rx,ry))
##############################
StyledToplevel = Toplevel

attributes = dict(tk.Tk.__dict__)  # keep the old attributes of StyleToplevel
attributes.update(dict(Toplevel.__dict__))
Tk = type("Tk",(tk.Tk,),dict(attributes)) # build Tk instead of Toplevel
Tk.__doc__ = Toplevel.__doc__.replace("Toplevel","Tk")

可以直接导入,当作Tk/Toplevel使用

#demo
import tkinter as tk
import Toplevel
root = Toplevel.Tk()
root.mainloop()

效果

还可以吧
看上去很简陋,但是只要能弄出第一步,美化什么的,也就能迎刃而解了!

使用事项

注意,这里Toplevel.title不是函数,而是_Titlebar类的实例,_Titlebar继承自tkinter.Frame,可用嵌套tkinter.Label的方式搞标题。
另外,Toplevel.方向 (小写)这个Frame可以自定义一些样式。
还有,截图上面有一些BUG,暂时不想修了。

总结

本文发于CSDN于 2022/7/17 10:56

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值