这是我的解决方案(很长,但我会尽我所能解释大部分部分(解释如下)但是在这种情况下不会有一个新窗口,而且这更多是一个例子怎么做只是为了给你一个想法。):
from tkinter import Tk, Button, Frame, Label, StringVar
from tkinter.ttk import Separator
from functools import partial
stored_data = {'Lionel Notmessi': {'Games Played': 340, 'Goals': 102, 'Assists': 223}, 'Firstname Lastname': {'Games Played': 279, 'Goals': 84, 'Assists': 56}}
class MainWindow(Tk):
def __init__(self):
Tk.__init__(self)
self.resizable(False, False)
self.players_frame = Frame(self)
self.players_frame.pack(fill='both', expand=True)
self.data_value_list = []
for index, player in enumerate(list(stored_data)):
player_frame = Frame(self.players_frame)
player_frame.pack(side='left')
Label(player_frame, text=player, height=2, width=30, anchor='n', font='default 11 bold').pack(side='top', pady=10)
var = StringVar()
self.data_value_list.append(var)
Label(player_frame, textvariable=self.data_value_list[index], height=8, width=30).pack(side='top', pady=10)
Button(player_frame, text='Edit', command=partial(self.select, player)).pack(side='bottom', pady=10, fill='x', expand=True)
self.refresh_data()
def select(self, key):
self.players_frame.pack_forget()
player_edit_frame = EditData(self, stored_data, key)
player_edit_frame.pack(fill='both', expand=True)
def refresh_data(self):
for var, player in zip(self.data_value_list, list(stored_data)):
data = ''
for name, value in stored_data[player].items():
data += f'{name}: {value}\n\n'
var.set(data)
class EditData(Frame):
def __init__(self, parent, data, key):
Frame.__init__(self, parent)
self.parent = parent
self.data = data
self.key = key
self.total_width = len(list(data[key].keys()))
name = Label(self, text=key, anchor='n', height=3)
name.grid(row=0, column=0, columnspan=self.total_width, sticky='nsew', pady=10)
self.value_list = []
index = 0
for key, value in data[self.key].items():
item_frame = Frame(self)
item_frame.grid(row=1, column=index, sticky='nsew', padx=5)
for i in range(3):
item_frame.columnconfigure(i, minsize=50)
var = StringVar()
var.set(value)
self.value_list.append(var)
Label(item_frame, text=key).grid(row=0, column=0, columnspan=3, sticky='nsew')
minus = Button(item_frame, text='-', command=partial(self.change, self.value_list[index], '-', key))
minus.grid(row=1, column=0, sticky='nsew')
value_label = Label(item_frame, textvariable=self.value_list[index])
value_label.grid(row=1, column=1, sticky='nsew')
plus = Button(item_frame, text='+', command=partial(self.change, self.value_list[index], '+', key))
plus.grid(row=1, column=2, sticky='nsew')
index += 1
Separator(self, orient='horizontal').grid(row=2, column=0, columnspan=self.total_width, sticky='nsew', pady=5)
Button(self, text='Done', command=self.done).grid(row=3, column=0, columnspan=self.total_width, sticky='nsew')
def change(self, var, operator, key):
current = var.get()
if operator == '-':
total = int(current) - 1
var.set(str(total))
self.data[self.key][key] = total
if operator == '+':
total = int(current) + 1
var.set(str(total))
self.data[self.key][key] = total
def done(self):
self.parent.refresh_data()
self.parent.players_frame.pack(expand=True, fill='both')
self.destroy()
root = MainWindow()
root.mainloop()
所以首先要导入所有需要用到的模块。
接下来,我将变量stored_data 设置为一个字典,该字典将是存储所有玩家数据的主要数据存储位置。
然后我创建了一个名为 MainWindow 的类,它继承自 tkinter 的主窗口 Tk,这样就可以获得 Tk() 功能,例如 .mainloop() 方法。
在MainWindow 构造函数方法(初始化时会调用它)中,我首先将窗口设置为在 x 和 y 方向上都不可调整大小,以免导致小部件不可见的问题。
然后我创建了一个框架来组织所有播放器内容的显示位置,还创建了另一个列表来存储 StringVar 值,以便轻松更新当前数据标签(启动程序时看到的标签)。
然后,我为字典中的每个键创建了一个框架来再次组织显示的小部件。然后在框架中我打包了所有相关的东西,我输入了玩家姓名、他们的统计数据和编辑按钮(因为我只关注那个功能)。还为每个玩家附加了一个 StringVar 列表。那些 StringVar 设置将显示统计信息的标签的值。并且 StringVar 是通过索引访问的。
然后我调用了self.refresh_data(),它基本上将列表中每个 StringVar 的值设置为相应的字典值。基本上显示统计数据。
然后还定义了select() 方法(由按钮调用),该方法启动EditData,然后在从几何管理器中删除self.players_frame 后将其放在屏幕上,使其不再可见并且只有编辑框可见。
EditData 类。启动后,它接受给它的参数并将它们设置为类属性,以便在整个类中更轻松地访问。
然后测量总宽度以计算网格的列跨度,它是通过测量由stored_data 键创建的列表的长度来完成的,所以长度基本上就是玩家的数量。
然后名字就会出现在屏幕上。
再次创建另一个self.value_list 用于存储StringVar,并将索引设置为0(因为我不知道如何在for 循环中实现它(很简单)(使用enumerate()))。并且每次迭代都会增加索引。
然后创建标签和按钮并将其网格化到item_frame,它位于继承自 Frame 的类内部,因此基本上该类是一个更大的 Frame,并且在其中程序创建更小的框架用于数据更改和布局组织目的。然后在item_frame 内部,每一列都设置为 minsize=50 以便它们的大小相同,因为无论如何都没有数据占用那么多空间。
然后再次设置一个 StringVar 并将其附加到列表中以便稍后访问它。
整体
name
- data +
创建布局并将按钮设置为方法change() 并给定参数(partial() 将这些函数设置为始终使用相同的给定变量执行,以便可以轻松地在循环中创建按钮)。调用更改时,它会获取该数据部分的当前值,然后根据它是“-”还是“+”,它会从当前值中添加或减去,并将其设置为新值,并更改字典,以便下次显示此帧时,它会显示新数据,并且“首页”上的统计信息会显示更新后的数据。
还有一个“完成”按钮,按下该按钮时会调用done() 方法,然后调用父(即MainWindow)方法refresh_data() 来显示新数据,然后将已删除的帧打包回之前调用这个类然后销毁自己。
然后我们就发起MainWindow:
root = MainWindow()
root.mainloop()
就是这样。
显然可以进行一些设计改进。
此外,如果您想保存数据,以便每次启动时都会显示更改的数据,您可以在关闭窗口之前将数据写入文件。
如果您有任何问题,请直接问他们。
关于该文件的保存:
您可以添加这个(MainWindow 类):
self.protocol('WM_DELETE_WINDOW', lambda: self.save(stored_data))
到__init__方法执行关闭窗口时给出的函数
还要加上这个:
with open('stored_data.json') as file:
stored_data = json.load(file)
在您文件的开头,但请确保您的目录中也有该文件您必须:
import json
然后定义关闭窗口时调用的save()方法(在MainWindow类中):
def save(self, data):
with open('stored_data.json', 'w') as file_:
json.dump(data, file_, indent=2)
self.destroy()
以便它将更新的数据写入该文件并记住之后销毁窗口,否则它不会关闭。
现在您可以在运行程序时加载数据,如果您进行更改,它们将保存到文件中,以便下次运行程序时访问它们。
如果你有问题再问他们。
添加了 Toplevel(确实是最小的更改,但添加了一些内容,例如 bind 以确保如果用户将焦点移出编辑窗口,它会被关闭,从而不会打开多个相同的窗口(因此还定义了另一种方法),所有更改如下):
from tkinter import Tk, Button, Frame, Label, StringVar, Toplevel, Entry
from tkinter.ttk import Separator
from functools import partial
stored_data = {'Lionel Notmessi': {'Games Played': 340, 'Goals': 102, 'Assists': 223}, 'Firstname Lastname': {'Games Played': 279, 'Goals': 84, 'Assists': 56}}
class MainWindow(Tk):
def __init__(self):
Tk.__init__(self)
self.resizable(False, False)
self.players_frame = Frame(self)
self.players_frame.pack(fill='both', expand=True)
self.data_value_list = []
for index, player in enumerate(list(stored_data)):
player_frame = Frame(self.players_frame)
player_frame.pack(side='left')
Label(player_frame, text=player, height=2, width=30, anchor='n', font='default 11 bold').pack(side='top', pady=10)
var = StringVar()
self.data_value_list.append(var)
Label(player_frame, textvariable=self.data_value_list[index], height=8, width=30).pack(side='top', pady=10)
Button(player_frame, text='Edit', command=partial(self.select, player)).pack(side='bottom', pady=10, fill='x', expand=True)
self.refresh_data()
def select(self, key):
player_edit_frame = EditData(self, stored_data, key)
player_edit_frame.focus_force()
def refresh_data(self):
for var, player in zip(self.data_value_list, list(stored_data)):
data = ''
for name, value in stored_data[player].items():
data += f'{name}: {value}\n\n'
var.set(data)
class EditData(Toplevel):
def __init__(self, parent, data, key):
Toplevel.__init__(self, parent)
self.parent = parent
self.data = data
self.key = key
self.bind('<FocusOut>', self.focus_out)
self.total_width = len(list(data[key].keys()))
name = Label(self, text=key, anchor='n', height=3)
name.grid(row=0, column=0, columnspan=self.total_width, sticky='nsew', pady=10)
self.value_list = []
index = 0
for key, value in data[self.key].items():
item_frame = Frame(self)
item_frame.grid(row=1, column=index, sticky='nsew', padx=5)
for i in range(3):
item_frame.columnconfigure(i, minsize=50)
var = StringVar()
var.set(value)
self.value_list.append(var)
Label(item_frame, text=key).grid(row=0, column=0, columnspan=3, sticky='nsew')
minus = Button(item_frame, text='-', command=partial(self.change, self.value_list[index], '-', key))
minus.grid(row=1, column=0, sticky='nsew')
value_label = Label(item_frame, textvariable=self.value_list[index])
value_label.grid(row=1, column=1, sticky='nsew')
plus = Button(item_frame, text='+', command=partial(self.change, self.value_list[index], '+', key))
plus.grid(row=1, column=2, sticky='nsew')
index += 1
Separator(self, orient='horizontal').grid(row=2, column=0, columnspan=self.total_width, sticky='nsew', pady=5)
Button(self, text='Done', command=self.done).grid(row=3, column=0, columnspan=self.total_width, sticky='nsew')
def change(self, var, operator, key):
current = var.get()
if operator == '-':
total = int(current) - 1
var.set(str(total))
self.data[self.key][key] = total
if operator == '+':
total = int(current) + 1
var.set(str(total))
self.data[self.key][key] = total
def done(self):
self.parent.refresh_data()
self.destroy()
def focus_out(self, event):
self.parent.refresh_data()
if event.widget == self:
self.destroy()
root = MainWindow()
root.mainloop()
更改MainWindow:
更改了select() 方法以显示顶层
def select(self, key):
player_edit_frame = EditData(self, stored_data, key)
player_edit_frame.focus_force()
EditFrame 的变化:
从顶层继承
class EditData(Toplevel):
def __init__(self, parent, data, key):
Toplevel.__init__(self, parent)
添加绑定
self.bind('<FocusOut>', self.focus_out)
bind 附带的方法(它检查焦点是否在整个窗口之外,否则即使焦点在入口小部件之外也会导致它关闭)
def focus_out(self, event):
self.parent.refresh_data()
if event.widget == self:
self.destroy()