使用 Tkinter 移动自定义标题栏 window,而不从 Top-left 角移动
Move a custom Title bar window with Tkinter without doing so from the Top-left corner
各位,新年快乐,
所以我是 Python 的新手,甚至是 Tkinter 的新手。我正在尝试为我的 UI 创建一个自定义标题栏,到目前为止我使用一些文章和视频都还不错。问题是,当创建自定义标题栏并尝试移动 window 时,它会从 Top-Left 角移动,因此 'teleporting' window。
看看这个:Thread 我的问题有答案,但我无法让它工作。
所以这是代码:
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
xwin = root.winfo_x()
ywin = root.winfo_y()
def get_pos(event):
global xwin
global ywin
xwin = xwin - event.x_root
ywin = ywin - event.y_root
def move_window(event):
root.geometry(f'+{event.x_root - xwin}+{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)
title_bar.bind("<Button-1>", get_pos)
close_button.bind('<Enter>', change_on_hovering)
close_button.bind('<Leave>', return_to_normal_state)
root.mainloop()
在这个版本中,它与上面的线程中显示的有点不同,因为我一直以其他方式收到错误。问题是现在 window 传送到一个新位置,我无法让它正常工作。
提前致谢!
代码在我使用时对我有效
def get_pos(event):
global xwin
global ywin
xwin = event.x
ywin = event.y
而你在
中忘记了ywin
def move_window(event):
root.geometry(f'+{event.x_root - xwin}+{event.y_root - ywin}')
编辑:
具有重组代码的完整工作代码。
在 Python 3 上测试,所以我不得不使用 tkinter
而不是 Tkinter
PEP 8 -- Style Guide for Python Code
#from tkinter import * # PEP8: `import *` is not preferred
import tkinter as tk
# --- classes ---
# empty
# --- functions ---
def get_pos(event):
global xwin
global ywin
xwin = event.x
ywin = event.y
def move_window(event):
root.geometry(f'+{event.x_root - xwin}+{event.y_root - ywin}')
def change_on_hovering(event):
close_button['bg'] = 'red'
def return_to_normal_state(event):
close_button['bg'] = back_ground
# --- main ---
# set background color of title bar
back_ground = "#2c2c2c"
# set background of window
content_color = "#ffffff"
# ---
root = tk.Tk()
# turns off title bar, geometry
root.overrideredirect(True)
# set new geometry
root.geometry('400x100+200+200')
# make a frame for the title bar
title_bar = tk.Frame(root, bg=back_ground, relief='raised', bd=1,
highlightcolor=back_ground,
highlightthickness=0)
# put a close button on the title bar
close_button = tk.Button(title_bar, text='x', bg=back_ground, padx=5, pady=2,
bd=0, font="bold", fg='white',
activebackground="red",
activeforeground="white",
highlightthickness=0,
command=root.destroy)
# window title
title_window = "Title Name"
title_name = tk.Label(title_bar, text=title_window, bg=back_ground, fg="white")
# a canvas for the main area of the window
window = tk.Canvas(root, bg="white", highlightthickness=0)
# pack the widgets
title_bar.pack(expand=True, fill='x')
title_name.pack(side='left')
close_button.pack(side='right')
window.pack(expand=True, fill='both')
# bind title bar motion to the move window function
title_bar.bind("<B1-Motion>", move_window)
title_bar.bind("<Button-1>", get_pos)
close_button.bind('<Enter>', change_on_hovering)
close_button.bind('<Leave>', return_to_normal_state)
root.mainloop()
编辑:
与此同时,我创建了使用 类 的版本 - 因此我可以轻松添加更多按钮。
它还可以在移动时更改标题栏颜色和文本。
import tkinter as tk
# --- constants --- (UPPER_CASE_NAMES)
# title bar colors
TITLE_FOREGROUND = "white"
TITLE_BACKGROUND = "#2c2c2c"
TITLE_BACKGROUND_HOVER = "green"
BUTTON_FOREGROUND = "white"
BUTTON_BACKGROUND = TITLE_BACKGROUND
BUTTON_FOREGROUND_HOVER = BUTTON_FOREGROUND
BUTTON_BACKGROUND_HOVER = 'red'
# window colors
WINDOW_BACKGROUND = "white"
WINDOW_FOREGROUND = "black"
# --- classes --- (CamelCaseNames)
class MyButton(tk.Button):
def __init__(self, master, text='x', command=None, **kwargs):
super().__init__(master, bd=0, font="bold", padx=5, pady=2,
fg=BUTTON_FOREGROUND,
bg=BUTTON_BACKGROUND,
activebackground=BUTTON_BACKGROUND_HOVER,
activeforeground=BUTTON_FOREGROUND_HOVER,
highlightthickness=0,
text=text,
command=command)
self.bind('<Enter>', self.on_enter)
self.bind('<Leave>', self.on_leave)
def on_enter(self, event):
self['bg'] = BUTTON_BACKGROUND_HOVER
def on_leave(self, event):
self['bg'] = BUTTON_BACKGROUND
class MyTitleBar(tk.Frame):
def __init__(self, master, *args, **kwargs):
super().__init__(master, relief='raised', bd=1,
bg=TITLE_BACKGROUND,
highlightcolor=TITLE_BACKGROUND,
highlightthickness=0)
self.title_label = tk.Label(self,
bg=TITLE_BACKGROUND,
fg=TITLE_FOREGROUND)
self.set_title("Title Name")
self.close_button = MyButton(self, text='x', command=master.destroy)
self.minimize_button = MyButton(self, text='-', command=self.on_minimize)
self.other_button = MyButton(self, text='#', command=self.on_other)
self.pack(expand=True, fill='x')
self.title_label.pack(side='left')
self.close_button.pack(side='right')
self.minimize_button.pack(side='right')
self.other_button.pack(side='right')
self.bind("<ButtonPress-1>", self.on_press)
self.bind("<ButtonRelease-1>", self.on_release)
self.bind("<B1-Motion>", self.on_move)
def set_title(self, title):
self.title = title
self.title_label['text'] = title
def on_press(self, event):
self.xwin = event.x
self.ywin = event.y
self.set_title("Title Name - ... I'm moving! ...")
self['bg'] = 'green'
self.title_label['bg'] = TITLE_BACKGROUND_HOVER
def on_release(self, event):
self.set_title("Title Name")
self['bg'] = TITLE_BACKGROUND
self.title_label['bg'] = TITLE_BACKGROUND
def on_move(self, event):
x = event.x_root - self.xwin
y = event.y_root - self.ywin
self.master.geometry(f'+{x}+{y}')
def on_minimize(self):
print('TODO: minimize')
def on_other(self):
print('TODO: other')
# --- functions ---
# empty
# --- main ---
root = tk.Tk()
# turns off title bar, geometry
root.overrideredirect(True)
# set new geometry
root.geometry('400x100+200+200')
title_bar = MyTitleBar(root)
#title_bar.pack() # it is inside `TitleBar.__init__()`
# a canvas for the main area of the window
window = tk.Canvas(root, bg=WINDOW_BACKGROUND, highlightthickness=0)
# pack the widgets
window.pack(expand=True, fill='both')
root.mainloop()
各位,新年快乐,
所以我是 Python 的新手,甚至是 Tkinter 的新手。我正在尝试为我的 UI 创建一个自定义标题栏,到目前为止我使用一些文章和视频都还不错。问题是,当创建自定义标题栏并尝试移动 window 时,它会从 Top-Left 角移动,因此 'teleporting' window。
看看这个:Thread 我的问题有答案,但我无法让它工作。
所以这是代码:
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
xwin = root.winfo_x()
ywin = root.winfo_y()
def get_pos(event):
global xwin
global ywin
xwin = xwin - event.x_root
ywin = ywin - event.y_root
def move_window(event):
root.geometry(f'+{event.x_root - xwin}+{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)
title_bar.bind("<Button-1>", get_pos)
close_button.bind('<Enter>', change_on_hovering)
close_button.bind('<Leave>', return_to_normal_state)
root.mainloop()
在这个版本中,它与上面的线程中显示的有点不同,因为我一直以其他方式收到错误。问题是现在 window 传送到一个新位置,我无法让它正常工作。
提前致谢!
代码在我使用时对我有效
def get_pos(event):
global xwin
global ywin
xwin = event.x
ywin = event.y
而你在
中忘记了ywin
def move_window(event):
root.geometry(f'+{event.x_root - xwin}+{event.y_root - ywin}')
编辑:
具有重组代码的完整工作代码。
在 Python 3 上测试,所以我不得不使用 tkinter
而不是 Tkinter
PEP 8 -- Style Guide for Python Code
#from tkinter import * # PEP8: `import *` is not preferred
import tkinter as tk
# --- classes ---
# empty
# --- functions ---
def get_pos(event):
global xwin
global ywin
xwin = event.x
ywin = event.y
def move_window(event):
root.geometry(f'+{event.x_root - xwin}+{event.y_root - ywin}')
def change_on_hovering(event):
close_button['bg'] = 'red'
def return_to_normal_state(event):
close_button['bg'] = back_ground
# --- main ---
# set background color of title bar
back_ground = "#2c2c2c"
# set background of window
content_color = "#ffffff"
# ---
root = tk.Tk()
# turns off title bar, geometry
root.overrideredirect(True)
# set new geometry
root.geometry('400x100+200+200')
# make a frame for the title bar
title_bar = tk.Frame(root, bg=back_ground, relief='raised', bd=1,
highlightcolor=back_ground,
highlightthickness=0)
# put a close button on the title bar
close_button = tk.Button(title_bar, text='x', bg=back_ground, padx=5, pady=2,
bd=0, font="bold", fg='white',
activebackground="red",
activeforeground="white",
highlightthickness=0,
command=root.destroy)
# window title
title_window = "Title Name"
title_name = tk.Label(title_bar, text=title_window, bg=back_ground, fg="white")
# a canvas for the main area of the window
window = tk.Canvas(root, bg="white", highlightthickness=0)
# pack the widgets
title_bar.pack(expand=True, fill='x')
title_name.pack(side='left')
close_button.pack(side='right')
window.pack(expand=True, fill='both')
# bind title bar motion to the move window function
title_bar.bind("<B1-Motion>", move_window)
title_bar.bind("<Button-1>", get_pos)
close_button.bind('<Enter>', change_on_hovering)
close_button.bind('<Leave>', return_to_normal_state)
root.mainloop()
编辑:
与此同时,我创建了使用 类 的版本 - 因此我可以轻松添加更多按钮。
它还可以在移动时更改标题栏颜色和文本。
import tkinter as tk
# --- constants --- (UPPER_CASE_NAMES)
# title bar colors
TITLE_FOREGROUND = "white"
TITLE_BACKGROUND = "#2c2c2c"
TITLE_BACKGROUND_HOVER = "green"
BUTTON_FOREGROUND = "white"
BUTTON_BACKGROUND = TITLE_BACKGROUND
BUTTON_FOREGROUND_HOVER = BUTTON_FOREGROUND
BUTTON_BACKGROUND_HOVER = 'red'
# window colors
WINDOW_BACKGROUND = "white"
WINDOW_FOREGROUND = "black"
# --- classes --- (CamelCaseNames)
class MyButton(tk.Button):
def __init__(self, master, text='x', command=None, **kwargs):
super().__init__(master, bd=0, font="bold", padx=5, pady=2,
fg=BUTTON_FOREGROUND,
bg=BUTTON_BACKGROUND,
activebackground=BUTTON_BACKGROUND_HOVER,
activeforeground=BUTTON_FOREGROUND_HOVER,
highlightthickness=0,
text=text,
command=command)
self.bind('<Enter>', self.on_enter)
self.bind('<Leave>', self.on_leave)
def on_enter(self, event):
self['bg'] = BUTTON_BACKGROUND_HOVER
def on_leave(self, event):
self['bg'] = BUTTON_BACKGROUND
class MyTitleBar(tk.Frame):
def __init__(self, master, *args, **kwargs):
super().__init__(master, relief='raised', bd=1,
bg=TITLE_BACKGROUND,
highlightcolor=TITLE_BACKGROUND,
highlightthickness=0)
self.title_label = tk.Label(self,
bg=TITLE_BACKGROUND,
fg=TITLE_FOREGROUND)
self.set_title("Title Name")
self.close_button = MyButton(self, text='x', command=master.destroy)
self.minimize_button = MyButton(self, text='-', command=self.on_minimize)
self.other_button = MyButton(self, text='#', command=self.on_other)
self.pack(expand=True, fill='x')
self.title_label.pack(side='left')
self.close_button.pack(side='right')
self.minimize_button.pack(side='right')
self.other_button.pack(side='right')
self.bind("<ButtonPress-1>", self.on_press)
self.bind("<ButtonRelease-1>", self.on_release)
self.bind("<B1-Motion>", self.on_move)
def set_title(self, title):
self.title = title
self.title_label['text'] = title
def on_press(self, event):
self.xwin = event.x
self.ywin = event.y
self.set_title("Title Name - ... I'm moving! ...")
self['bg'] = 'green'
self.title_label['bg'] = TITLE_BACKGROUND_HOVER
def on_release(self, event):
self.set_title("Title Name")
self['bg'] = TITLE_BACKGROUND
self.title_label['bg'] = TITLE_BACKGROUND
def on_move(self, event):
x = event.x_root - self.xwin
y = event.y_root - self.ywin
self.master.geometry(f'+{x}+{y}')
def on_minimize(self):
print('TODO: minimize')
def on_other(self):
print('TODO: other')
# --- functions ---
# empty
# --- main ---
root = tk.Tk()
# turns off title bar, geometry
root.overrideredirect(True)
# set new geometry
root.geometry('400x100+200+200')
title_bar = MyTitleBar(root)
#title_bar.pack() # it is inside `TitleBar.__init__()`
# a canvas for the main area of the window
window = tk.Canvas(root, bg=WINDOW_BACKGROUND, highlightthickness=0)
# pack the widgets
window.pack(expand=True, fill='both')
root.mainloop()