Tkinter按钮Y轴≥200时不显示的Bug修复请求
问题描述
开发基于Tkinter的Osu!风格程序时遇到Bug:当按钮Y轴坐标设置为200或更高时,按钮无法在屏幕上显示。推测可能和标题界面的放大标题有关,按钮无法显示在标题下方区域。
程序运行说明:
- 启动程序进入标题界面,点击“Start”切换到游戏界面;
- 游戏界面应随机生成带数字的彩色按钮,点击按钮会生成新的随机按钮;
- 按钮不显示的情况随机出现,多次运行才可能正常显示。
用户代码如下:
from tkinter import * import random global counter counter = -1 global color_counter color_counter = -1 win = Tk() Titlescreen = Frame(win) Gamescreen = Frame(win) Deathscreen = Frame(win) # Page 1 H1 = Label(Titlescreen, text="Osu!", font=('Helvatical bold',100)) H1.grid(row=0, column=1) btn1 = Button(Titlescreen, text='Start', command=lambda:Gamescreen.tkraise()) btn1.grid(row=1, column=1) #Page 2 class rectangle(): def __init__(self, location, number, row, column, color): self.location = location self.number = number self.row = row self.column = column self.color = color #write under here the parameters for rectangle numbers = ['1','2','3','4','5','6','7','8','9'] x= list(range(20, 380)) y= list(range(20, 380)) colors = ['red','blue','green','yellow','purple','orange','pink'] def click(): circle_button.grid_forget() circle() global circle def circle(): global counter global color_counter counter += 1 if counter == 9: counter = 0 color_counter += 1 user = rectangle(Gamescreen, numbers[counter], random.choice(x), random.choice(y), colors[color_counter]) print(random.choice(x)) print(random.choice(y)) global circle_button circle_button = Button(user.location,text=user.number, bg=user.color, command=click) circle_button.place(x=random.choice(x),y=random.choice(y), anchor = CENTER) circle() #Page 3 Lb1 = Label(Deathscreen, text='GAME OVER', font=('Helvatical bold',50)) Lb1.grid(row=0,column=1) btn3 = Button(Deathscreen, text='Retry', command=lambda:Gamescreen.tkraise()) btn3.grid(row=1, column=1) btn4 = Button(Deathscreen, text='Exit', command=lambda:Titlescreen.tkraise()) btn4.grid(row=2, column=1) Titlescreen.grid(row=0, column=0, sticky='nsew') Gamescreen.grid(row=0, column=0, sticky='nsew') Deathscreen.grid(row=0, column=0, sticky='nsew') Titlescreen.tkraise() win.geometry('400x400') win.title('Control panel') win.resizable(False, False) win.mainloop()
问题分析与修复方案
核心问题
- Frame未适配窗口尺寸:三个Frame虽然设置了
sticky='nsew',但主窗口未配置网格权重,导致Frame无法自动填充整个400x400窗口。Gamescreen的初始尺寸过小,Y坐标200以上的按钮会被裁剪。 - 重复生成随机坐标:
circle()函数中两次调用random.choice()生成坐标,导致打印的坐标和按钮实际位置不一致,增加调试难度。 - 索引逻辑错误:
color_counter初始值为-1,第一次调用colors[color_counter]虽不会报错(Python允许负索引),但不符合从第一个颜色开始的逻辑;且未处理颜色索引超出列表长度的情况。 - 布局方法不匹配:按钮用
place()布局,click()中却调用grid_forget(),无法正确移除旧按钮。
修复代码
from tkinter import * import random counter = -1 color_counter = 0 # 修正初始值,从第一个颜色开始 win = Tk() # 配置主窗口网格权重,让Frame填充整个窗口 win.grid_rowconfigure(0, weight=1) win.grid_columnconfigure(0, weight=1) Titlescreen = Frame(win) Gamescreen = Frame(win) Deathscreen = Frame(win) # 给每个Frame配置内部网格权重,确保内容自适应 for frame in [Titlescreen, Gamescreen, Deathscreen]: frame.grid_rowconfigure(0, weight=1) frame.grid_columnconfigure(0, weight=1) # Page 1 H1 = Label(Titlescreen, text="Osu!", font=('Helvetica bold', 100)) H1.grid(row=0, column=1) btn1 = Button(Titlescreen, text='Start', command=lambda: Gamescreen.tkraise()) btn1.grid(row=1, column=1) # Page 2 class rectangle(): def __init__(self, location, number, x, y, color): self.location = location self.number = number self.x = x self.y = y self.color = color numbers = ['1','2','3','4','5','6','7','8','9'] x_range = list(range(20, 380)) y_range = list(range(20, 380)) colors = ['red','blue','green','yellow','purple','orange','pink'] circle_button = None # 初始化全局按钮变量 def click(): global circle_button if circle_button: circle_button.destroy() # 销毁旧按钮,避免布局残留 circle() def circle(): global counter, color_counter, circle_button counter += 1 if counter == len(numbers): counter = 0 color_counter += 1 # 颜色索引超出范围时重置 if color_counter == len(colors): color_counter = 0 # 只生成一次随机坐标,保证打印值和实际位置一致 rand_x = random.choice(x_range) rand_y = random.choice(y_range) user = rectangle(Gamescreen, numbers[counter], rand_x, rand_y, colors[color_counter]) print(f"X: {rand_x}, Y: {rand_y}") circle_button = Button(user.location, text=user.number, bg=user.color, command=click) circle_button.place(x=rand_x, y=rand_y, anchor=CENTER) circle() # Page 3 Lb1 = Label(Deathscreen, text='GAME OVER', font=('Helvetica bold',50)) Lb1.grid(row=0, column=1) btn3 = Button(Deathscreen, text='Retry', command=lambda: Gamescreen.tkraise()) btn3.grid(row=1, column=1) btn4 = Button(Deathscreen, text='Exit', command=lambda: Titlescreen.tkraise()) btn4.grid(row=2, column=1) # 布局三个Frame Titlescreen.grid(row=0, column=0, sticky='nsew') Gamescreen.grid(row=0, column=0, sticky='nsew') Deathscreen.grid(row=0, column=0, sticky='nsew') Titlescreen.tkraise() win.geometry('400x400') win.title('Control panel') win.resizable(False, False) win.mainloop()
修复说明
- 通过配置主窗口和Frame的网格权重,让Gamescreen完全填充400x400窗口,Y坐标200以上的按钮可正常显示;
- 统一随机坐标生成逻辑,确保打印的坐标和按钮实际位置一致;
- 修正
color_counter的初始值和循环逻辑,避免索引异常; - 改用
destroy()移除旧按钮,匹配place()布局的操作逻辑。
内容的提问来源于stack exchange,提问作者Samurai
相关产品推荐
相关产品推荐

