【问题标题】:Can I change the title bar in Tkinter?我可以更改 Tkinter 中的标题栏吗?
【发布时间】:2014-07-13 05:06:48
【问题描述】:

我使用 Tkinter 作为我的程序的 GUI,但正如我所见,许多程序不像 Tkinter 那样具有标准外观。标准外观是指标准标题栏、边框等。

例如Tkinter的标题栏:

vs GitHub 的标题栏:

了解他们如何拥有自己的自定义退出、调整大小和最小化按钮?是否可以使用 Tkinter 实现这种外观?

提前致谢! :)

【问题讨论】:

    标签: python tkinter


    【解决方案1】:

    是的,这是可能的。您可以使用根窗口上的overrideredirect() 方法来杀死标题栏和默认几何设置。之后,您需要从头开始重建所有这些方法,以便按照您的意愿进行设置。这是一个功能最少的小型工作示例:

    root = Tk()
    
    def move_window(event):
        root.geometry('+{0}+{1}'.format(event.x_root, event.y_root))
    
    root.overrideredirect(True) # turns off title bar, geometry
    root.geometry('400x100+200+200') # set new geometry
    
    # make a frame for the title bar
    title_bar = Frame(root, bg='white', relief='raised', bd=2)
    
    # put a close button on the title bar
    close_button = Button(title_bar, text='X', command=root.destroy)
    
    # a canvas for the main area of the window
    window = Canvas(root, bg='black')
    
    # pack the widgets
    title_bar.pack(expand=1, fill=X)
    close_button.pack(side=RIGHT)
    window.pack(expand=1, fill=BOTH)
    
    # bind title bar motion to the move window function
    title_bar.bind('<B1-Motion>', move_window)
    
    root.mainloop()
    

    【讨论】:

    • 它可以工作,但我的程序没有显示在 Windows 的开始栏中,也没有显示在进程中?当我最小化它时,它实际上完全消失了(我不能 atl+tab 它)...
    • 嗯,看起来可行,但是有什么方法可以将“右键单击以退出两个应用程序”(self.bind('
    • 是的。谷歌协议 wm_delete_window 查找一些信息、示例等
    • Nvm 我已经使用 root.destroy() 和 app.destroy() 完成了!非常感谢!
    【解决方案2】:

    大多数人都知道在使用上面使用的“move_window”方法时会出错;我找到了一个修复程序,它可以获取鼠标的确切位置并随之移动,而不是从角落移动:

        def get_pos(event):
            xwin = app.winfo_x()
            ywin = app.winfo_y()
            startx = event.x_root
            starty = event.y_root
    
            ywin = ywin - starty
            xwin = xwin - startx
    
    
            def move_window(event):
                app.geometry("400x400" + '+{0}+{1}'.format(event.x_root + xwin, event.y_root + ywin))
            startx = event.x_root
            starty = event.y_root
    
    
            app.TopFrame.bind('<B1-Motion>', move_window)
        app.TopFrame.bind('<Button-1>', get_pos)
    

    【讨论】:

      【解决方案3】:

      这些是我使用 python 3.7.2 所做的修改

      from tkinter import *
      root = Tk()
      root.overrideredirect(True) # turns off title bar, geometry
      root.geometry('400x100+200+200') # set new geometry
      
      # make a frame for the title bar
      title_bar = Frame(root, bg='#2e2e2e', relief='raised', bd=2,highlightthickness=0)
      
      # put a close button on the title bar
      close_button = Button(title_bar, text='X', command= root.destroy,bg = "#2e2e2e",padx = 2,pady = 2,activebackground='red',bd = 0,font="bold",fg='white',highlightthickness=0)
      
      # a canvas for the main area of the window
      window = Canvas(root, bg='#2e2e2e',highlightthickness=0)
      
      # pack the widgets
      title_bar.pack(expand=1, fill=X)
      close_button.pack(side=RIGHT)
      window.pack(expand=1, fill=BOTH)
      xwin=None
      ywin=None
      # bind title bar motion to the move window function
      
      def move_window(event):
          root.geometry('+{0}+{1}'.format(event.x_root, event.y_root))
      def change_on_hovering(event):
          global close_button
          close_button['bg']='red'
      def return_to_normalstate(event):
          global close_button
          close_button['bg']='#2e2e2e'
          
          
      title_bar.bind('<B1-Motion>', move_window)
      close_button.bind('<Enter>',change_on_hovering)
      close_button.bind('<Leave>',return_to_normalstate)
      root.mainloop()
      

      说明:
      我们使用 bd(border thickness)=0 来移除按钮的边框
      然后我们将&lt;Enter&gt; 事件绑定到一个函数 更改前景色。
      为了返回其原始状态,我们将&lt;Leave&gt; 事件绑定到另一个函数


      初始状态
      鼠标悬停在其上后状态变化

      注意:光标不可见,因为我的屏幕捕获软件以某种方式将其删除

      【讨论】:

        【解决方案4】:

        在 python3.5.2 中,我必须进行一些修改才能使其正常工作:

        #custom title bar for tkinter
        
        from tkinter import Tk, Frame, Button, Canvas
        
        root = Tk()
        
        def move_window(event):
            root.geometry('+{0}+{1}'.format(event.x_root, event.y_root))
        
        root.overrideredirect(True) # turns off title bar, geometry
        root.geometry('400x100+200+200') # set new geometry
        
        # make a frame for the title bar
        title_bar = Frame(root, bg='white', relief='raised', bd=2)
        
        # put a close button on the title bar
        close_button = Button(title_bar, text='Close this Window', command=root.destroy)
        
        # a canvas for the main area of the window
        window = Canvas(root, bg='black')
        
        # pack the widgets
        title_bar.pack(expand=1, fill="x")
        close_button.pack(side="right")
        window.pack(expand=1, fill="both")
        
        # bind title bar motion to the move window function
        title_bar.bind('<B1-Motion>', move_window)
        
        root.mainloop()
        

        【讨论】:

          【解决方案5】:

          enter image description here这里你更新到 python 3.8 和标题栏背景的新变量 主要内容背景和标题名称添加 和新的背景+清除一些缩进错误

          from tkinter import *
          
          root = Tk()
          # turns off title bar, geometry
          root.overrideredirect(True)
          # set new geometry
          root.geometry('400x100+200+200')
          # set background color of title bar
          back_ground = "#2c2c2c"
          
          # set background of window
          content_color = "#ffffff"
          # make a frame for the title bar
          title_bar = Frame(root, bg=back_ground, relief='raised', bd=1, highlightcolor=back_ground,highlightthickness=0)
          
          # put a close button on the title bar
          close_button = Button(title_bar, text='x',  command=root.destroy,bg=back_ground, padx=5, pady=2, activebackground="red", bd=0,    font="bold", fg='white',        activeforeground="white", highlightthickness=0)
           # window title
          title_window = "Title Name"
          title_name = Label(title_bar, text=title_window, bg=back_ground, fg="white")
          # a canvas for the main area of the window
          window = Canvas(root, bg="white", highlightthickness=0)
          
          # pack the widgets
          title_bar.pack(expand=1, fill=X)
          title_name.pack(side=LEFT)
          close_button.pack(side=RIGHT)
          window.pack(expand=1, fill=BOTH)
          x_axis = None
          y_axis = None
          # bind title bar motion to the move window function
          
          
          def move_window(event):
              root.geometry('+{0}+{1}'.format(event.x_root, event.y_root))
          
          
          def change_on_hovering(event):
              global close_button
              close_button['bg'] = 'red'
          
          
          def return_to_normal_state(event):
             global close_button
             close_button['bg'] = back_ground
          
          
          title_bar.bind('<B1-Motion>', move_window)
          close_button.bind('<Enter>', change_on_hovering)
          close_button.bind('<Leave>', return_to_normal_state)
          root.mainloop()
          

          【讨论】:

            【解决方案6】:

            This is how it looks我认为这是最简单的。我在我的一个项目中使用了它,由于客户端的一些原因,客户无法为该项目支付任何款项,浪费了很多时间。

            from cProfile import label
            from tkinter import *
            import ttkbootstrap as ttk
            from ttkbootstrap import Style
            from tkinter import messagebox as m_box
            
            
            
            win = Tk()
            win.overrideredirect(True)
            win.attributes("-topmost", True)
            win.geometry("600x300+300+300")
            win.resizable(1, 1)
            style = Style("cyborg")
            
            # ============================================================= Title bar Header
            
            
            def ext():
                exit()
            
            def minim():
                win.overrideredirect(0)
                win.wm_state("iconic")
                win.overrideredirect(1)
            
            def about():
                pass
                m_box.showinfo("About", "Developer: Vivek phogat\nemail: Chaudharyvivekphogat@outlook.com")
            
            
            #------------------------------- Header section
            Header_frame = ttk.Label(win)
            
            info_label = ttk.Label(Header_frame,text="Customized title bar enjoy :-)",bootstyle="WARNING",font=("Comic Sans MS", 15))
            info_label.pack(padx=5, pady=5,side = LEFT)
            
            win_close_btn1 = ttk.Button(Header_frame, text=" X ", command=(win.destroy), bootstyle="danger")
            win_close_btn1.pack( side = RIGHT,anchor= NE)
            Min_but = ttk.Button(Header_frame, text=" _ ",  command=minim, bootstyle="light")
            Min_but.pack( side = RIGHT,anchor= NE)
            about_btn1 = ttk.Button(Header_frame, text=" a ", command=about, bootstyle="secondary")
            about_btn1.pack(side = RIGHT,anchor= NE)
            
            
            Header_frame.pack(fill=X)
            #-------------------------------
            
            
            #title bar get position.
            def get_pos(event):
                global xwin
                global ywin
                xwin = event.x
                ywin = event.y
            
            #title bar drag functon.
            def drag(event):
                win.geometry(f"+{event.x_root - xwin}+{event.y_root - ywin}")
            
            
            Header_frame.bind("<B1-Motion>", drag)
            Header_frame.bind("<Button-1>", get_pos)
            info_label.bind("<B1-Motion>", drag)
            info_label.bind("<Button-1>", get_pos)
            
            
            Footer_frame = ttk.Frame(win)
            
            label = ttk.Label(Footer_frame, text= " Hope you all are doing good enjoy the code.",bootstyle="WARNING",font=("Comic Sans MS", 20)).pack()
            Footer_frame.pack(expand= TRUE )
            win.mainloop()
            

            【讨论】:

              【解决方案7】:

              好的,如果有人需要,我会留下旧答案。 它是一个从头开始重新创建的自定义任务栏,组装 Windows 10 栏

                  from tkinter import *
                  window = Tk()
                  bg = "#f5f6f7"
                  title_window = "test app"
              
              
                  class app:
                      def __init__(self, main):
                          self.main = main
                          self.main.configure(bg=bg)
                          self.main.overrideredirect(True)
                          self.main.geometry('230x130')
                          self.main.resizable(width=False, height=False)
                          self.top_bar = Frame(main,bg=bg, cursor="sizing")
                          self.top_bar.pack(fill=X)
                          self.title_txt = Label(self.top_bar, text=title_window ,bg=bg)
                          self.title_txt.pack(side="left", padx=3)
                          close_btn = Button(self.top_bar,text="x", cursor="arrow", bg=bg,    fg="black", highlightthickness=0,activebackground="red", activeforeground="white",bd=0, command=self.main.quit)
                          close_btn.pack(side="right")
                          bottom_bar = Frame(main, bg=bg)
                          bottom_bar.pack()
                          label_scr = Label(bottom_bar, text="label 1", padx=100, pady=5, bg=bg)
                          label_scr.grid(row=0, column=0, columnspan=3)
                          button_scr = Button(bottom_bar, text="Button1", bg=bg, bd=0)
                          button_scr.grid(row=2, column=0, columnspan=3, pady=3)
                          button2_scr = Button(bottom_bar, text="Button2", bg=bg,bd=0)
                          button2_scr.grid(row=3, column=0, columnspan=3, pady=3)
              
              
              
                  def move_window(event):
                      window.geometry(f"+{event.x_root}+{event.y_root}")
              
              
                  execution = app(window)
                  execution.top_bar.bind('<B1-Motion>', move_window)
                  execution.title_txt.bind('<B1-Motion>', move_window)
                  window.mainloop()
              

              【讨论】:

                【解决方案8】:

                这是我的

                  class CustomTitle():
                        """
                        Ex:
                            root = Tk()
                            titleBar = CustomTitle(root,title_text = 'Hello,World!' , bg = "#000000" , fg = '#ffffff')
                            titleBar.resizeable = True
                            titleBar.packBar()
                            root.mainloop()
                    
                            Note:
                                    Try to Give Color value in Hex and the 3rd car should be number
                                        #7a4e7a
                                           ↑ (this one)                  
                        """
                        resizeable = True
                        font_style = ('Candara',13)
                        
                    
                        def __init__(self,win,title_text='Custom Title Bar',bg='#ffffff',fg="#000000"):  
                            # deactivating main title bar
                            self._win = win
                            win.title(title_text)
                    
                            # props
                            self.bg = bg
                            self._maximized = False
                            self._win_width = win.winfo_width()
                            self._win_height = win.winfo_height()
                            self._scr_width = win.winfo_screenwidth()
                            self._scr_height = win.winfo_screenheight()
                            self._addWidget(title_text,bg,fg)
                            
                        def packBar(self):
                            self._title_bar.pack(fill=X)
                            self._checkAbility()
                            self._win.overrideredirect(1)
                            self._finilize()
                    
                        def _checkAbility(self):
                            if not self.resizeable:
                                self._maximize_btn.config(state=DISABLED)
                            else:
                                self._resizey_widget.pack(side=BOTTOM,ipadx=.1,fill=X)
                                self._resizex_widget.pack(side=RIGHT,ipadx=.1,fill=Y)
                    
                        def _maximize_win(self):
                            if not self._maximized:
                                self._past_size = root.geometry()
                                self._win.geometry(f"{self._scr_width}x{self._scr_height}+{0}+{0}")
                                self._maximize_btn.config(text = '?')
                            else:
                                self._win.geometry(self._past_size)
                                self._maximize_btn.config(text = '?')
                            self._maximized = not self._maximized
                    
                    
                        def _minimize(self):
                            Minimize = win32gui.GetForegroundWindow()
                            win32gui.ShowWindow(Minimize, win32con.SW_MINIMIZE)
                    
                        
                        def _setIconToTaskBar(self,mainWindow):
                            GWL_EXSTYLE = -20
                            WS_EX_APPWINDOW = 0x00040000
                            WS_EX_TOOLWINDOW = 0x00000080
                            # Magic
                            hwnd = windll.user32.GetParent(mainWindow.winfo_id())
                            stylew = windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
                            stylew = stylew & ~WS_EX_TOOLWINDOW
                            stylew = stylew | WS_EX_APPWINDOW
                            res = windll.user32.SetWindowLongW(hwnd, GWL_EXSTYLE, stylew)
                        
                            mainWindow.wm_withdraw()
                            mainWindow.after(10, lambda: mainWindow.wm_deiconify())
                    
                        def _addWidget(self,title_text,bg,fg):
                            self._title_bar = Frame(self._win,bd=1,bg=bg)
                    
                            self._title_text = Label(self._title_bar,text=title_text,bg=bg,fg=fg,font=self.font_style)
                            self._title_text.pack(side=LEFT,padx=4,pady=3)
                            self._title_text.bind("<B1-Motion>",self._drag)
                    
                            self._close_btn = Button(self._title_bar,text = '×',bd=0,bg=bg,fg=fg,width=3,font=self.font_style,command=lambda: quit())
                            self._close_btn.pack(side=RIGHT,fill=Y)
                            self._maximize_btn = Button(self._title_bar,text="?",bd=0,bg=bg,fg=fg,width=3,font=self.font_style,command=self._maximize_win)
                            self._maximize_btn.pack(side=RIGHT,fill=Y)
                            self._minimize_btn = Button(self._title_bar,text="_",bd=0,bg=bg,fg=fg,width=3,font=self.font_style,command=self._minimize)
                            self._minimize_btn.pack(side=RIGHT,fill=Y)
                            self._title_bar.bind('<Button-1>', self._drag)
                    
                            self._resizex_widget = Frame(self._win,cursor='sb_h_double_arrow')
                            self._resizex_widget.bind("<B1-Motion>",self._resizex)
                    
                            self._resizey_widget = Frame(self._win,cursor='sb_v_double_arrow')
                            self._resizey_widget.bind("<B1-Motion>",self._resizey)
                    
                            self._hover_effect()
                    
                        def _hover_effect(self):
                            try:
                                num = int(self.bg[3]) - 1
                                newbg = self.bg.replace(self.bg[3],str(num))
                            except:
                                newbg = "#c7ebe8"
                    
                            def change_bg(which_one,bg = newbg):
                                which_one.config(bg=bg)
                            def restore_bg(which_one):
                                which_one.config(bg=self.bg)
                            self._maximize_btn.bind('<Enter>',lambda event: change_bg(self._maximize_btn))
                            self._maximize_btn.bind('<Leave>',lambda event: restore_bg(self._maximize_btn))
                            self._minimize_btn.bind('<Enter>',lambda event: change_bg(self._minimize_btn))
                            self._minimize_btn.bind('<Leave>',lambda event: restore_bg(self._minimize_btn))
                            self._close_btn.bind('<Enter>',lambda event: change_bg(self._close_btn,bg='#db2730'))
                            self._close_btn.bind('<Leave>',lambda event: restore_bg(self._close_btn))
                    
                    
                        def _finilize(self):
                            self._win.after(10, lambda: self._setIconToTaskBar(self._win))
                    
                        def _drag(self,event):
                            xwin = root.winfo_x()
                            ywin = root.winfo_y()
                            startx = event.x_root
                            starty = event.y_root
                    
                            ywin = ywin - starty
                            xwin = xwin - startx
                    
                            def _move_window(event): # runs when window is dragged
                    
                                root.geometry(f'+{event.x_root + xwin}+{event.y_root + ywin}')
                    
                    
                            def _release_window(event): # runs when window is released
                                root.config(cursor="arrow")
                                
                            self._title_bar.bind('<B1-Motion>', _move_window)
                            self._title_bar.bind('<ButtonRelease-1>', _release_window)
                            self._title_text.bind('<B1-Motion>', _move_window)
                            self._title_text.bind('<ButtonRelease-1>', _release_window)
                    
                    
                        def _resizex(self,event):
                    
                            xwin = root.winfo_x()
                    
                            difference = (event.x_root - xwin) - root.winfo_width()
                    
                            if root.winfo_width() > 150 : # 150 is the minimum width for the window
                                try:
                                    root.geometry(f"{ root.winfo_width() + difference }x{ root.winfo_height() }")
                                except:
                                    pass
                            else:
                                if difference > 0: # so the window can't be too small (150x150)
                                    try:
                                        root.geometry(f"{ root.winfo_width() + difference }x{ root.winfo_height() }")
                                    except:
                                        pass
                    
                    
                        def _resizey(self,event):
                    
                            ywin = root.winfo_y()
                    
                            difference = (event.y_root - ywin) - root.winfo_height()
                    
                            if root.winfo_height() > 150: # 150 is the minimum height for the window
                                try:
                                    root.geometry(f"{ root.winfo_width()  }x{ root.winfo_height() + difference}")
                                except:
                                    pass
                            else:
                                if difference > 0: # so the window can't be too small (150x150)
                                    try:
                                        root.geometry(f"{ root.winfo_width()  }x{ root.winfo_height() + difference}")
                                    except:
                                        pass
                

                【讨论】:

                • 欢迎来到 Stack Overflow!通过在代码之外简要解释您的方法,可以改进您的答案。感谢您的回答!
                【解决方案9】:

                我找到了一种使用ctypes 使标题栏变黑的方法:

                Tkinter 深色标题栏示例:

                import ctypes as ct
                
                
                def dark_title_bar(window):
                    """
                    MORE INFO:
                    https://docs.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
                    """
                    window.update()
                    DWMWA_USE_IMMERSIVE_DARK_MODE = 20
                    set_window_attribute = ct.windll.dwmapi.DwmSetWindowAttribute
                    get_parent = ct.windll.user32.GetParent
                    hwnd = get_parent(window.winfo_id())
                    rendering_policy = DWMWA_USE_IMMERSIVE_DARK_MODE
                    value = 2
                    value = ct.c_int(value)
                    set_window_attribute(hwnd, rendering_policy, ct.byref(value),
                                         ct.sizeof(value))
                

                我搜索了将近一年的解决方案!

                【讨论】:

                • 通常,将样式“强制”到窗口是不明智的。如果用户想要更改“主题”,此窗口将不会跟随主题更改。窗口装饰通常由窗口管理器负责。
                • 以及如何切换回浅色主题?
                • 为 DWMWA_USE_IMMERSIVE_DARK_MODE 尝试不同的数字。我认为 1 让它再次亮起来。我认为更改 Tk.resizable() 属性也会恢复它
                猜你喜欢
                • 2012-05-31
                • 1970-01-01
                • 2010-11-20
                • 1970-01-01
                • 2020-11-17
                • 1970-01-01
                • 2021-07-30
                • 1970-01-01
                • 2011-02-17
                相关资源
                最近更新 更多