<p>这是我的解决方案(相当长,但我会尽可能地解释大部分部分(解释如下),但在这种情况下,不会有新的窗口,而且这更多的是一个示例,说明如何做到这一点,只是为了给您一个想法。):</p>
<pre class="lang-py prettyprint-override"><code>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()
</code></pre>
<p>因此,第一件事是导入将要使用的所有必要模块</p>
<p>接下来,我将一个变量<code>stored_data</code>设置为一个字典,该字典将是主要的数据存储位置,所有关于球员的数据都将在这里存储</p>
<p>然后我创建了一个名为<code>MainWindow</code>的类,它继承了tkinter的主窗口<code>Tk</code>,这样就可以获得<code>Tk()</code>功能,例如<code>.mainloop()</code>方法</p>
<p>在<code>MainWindow</code>构造函数方法(启动时将调用什么)中,我首先将窗口设置为在x和y方向上都不可压缩,这样就不会导致窗口小部件不可见的问题</p>
<p>然后,我创建了一个框架来组织所有播放器内容的显示位置,还创建了另一个用于存储StringVar值的列表,以便轻松更新当前数据标签(启动程序时看到的标签)</p>
<p>然后,我为字典中的每个键创建了一个框架来再次组织显示的小部件。然后在框架中我打包了所有相关的东西,我输入了球员的名字,他们的统计数据和编辑按钮(因为我只关注这个功能)。还为每个玩家在列表中添加了StringVar。这些StringVar设置将显示统计信息的标签的值。StringVar是通过索引访问的</p>
<p>然后我调用了<code>self.refresh_data()</code>,它基本上将列表中每个StringVar的值设置为相应的字典值。基本上显示统计数据</p>
<p>然后还有定义的<code>select()</code>方法(通过按钮调用),该方法启动<code>EditData</code>,然后在从几何体管理器中删除<code>self.players_frame</code>后将其放在屏幕上,使其不再可见,只有编辑框可见</p>
<p><code>EditData</code>类。在初始化时,它接受给定给它的参数,并将它们设置为类属性,以便在整个类中更容易访问</p>
<p>然后测量总宽度以计算网格的列跨度,并通过测量由<code>stored_data</code>键创建的列表的长度来完成,因此基本上长度就是玩家的数量</p>
<p>然后这个名字就会出现在屏幕上</p>
<p>再次创建另一个用于存储StringVar的<code>self.value_list</code>,并且将索引设置为0(因为我不知道如何在for循环中实现它(足够简单)(使用<code>enumerate()</code>))。每次迭代都会增加索引</p>
<p>然后创建标签和按钮,并将其网格化到<code>item_frame</code>中,该类位于从Frame继承的类内,因此该类基本上是一个较大的帧,在该类内,程序创建较小的帧,用于数据更改和布局组织。然后在<code>item_frame</code>中,每个列都被设置为minsize=50,这样它们的大小就相同了,因为所有数据都不会占用那么多空间</p>
<p>然后再次设置StringVar并将其附加到列表中,以便以后访问它</p>
<p>整体</p>
<pre><code> name
- data +
</code></pre>
<p>布局被创建,按钮被设置为方法<code>change()</code>并给定参数(该<code>partial()</code>将这些函数设置为始终使用相同的给定变量执行,以便可以轻松地在循环中创建按钮)。当调用change时,它会获取该数据部分的当前值,然后根据它是“-”还是“+”,它会对当前值进行加减,并将其设置为新值,同时还会更改字典中的值,以便下次显示此帧时,它会显示新数据,并在“首页”上显示统计信息显示更新的数据</p>
<p>还有一个“Done”按钮,按下该按钮时调用<code>done()</code>方法,然后调用parent(即<code>MainWindow</code>)方法<code>refresh_data()</code>来显示新的数据,然后它会将调用之前删除的帧打包回去摧毁这个类,然后摧毁自己</p>
<p>然后我们就开始<code>MainWindow</code>:</p>
<pre><code>root = MainWindow()
root.mainloop()
</code></pre>
<p>就这样</p>
<p>显然,可以进行一些设计改进。<br/>
此外,如果希望保存数据,以便每次启动时都显示更改的数据,则可以在关闭窗口之前将数据写入文件</p>
<p>如果你有问题,就问他们</p>
<p>关于文件保存:</p>
<p>您可以添加以下(<code>MainWindow</code>类):</p>
<pre><code>self.protocol('WM_DELETE_WINDOW', lambda: self.save(stored_data))
</code></pre>
<p>调用<code>__init__</code>方法来执行关闭窗口时给定的函数</p>
<p>还应添加以下内容:</p>
<pre><code>with open('stored_data.json') as file:
stored_data = json.load(file)
</code></pre>
<p>在文件开始时,请确保您的目录中有该文件,您还必须:</p>
<pre><code>import json
</code></pre>
<p>然后定义关闭窗口时调用的<code>save()</code>方法(在<code>MainWindow</code>类中):</p>
<pre><code>def save(self, data):
with open('stored_data.json', 'w') as file_:
json.dump(data, file_, indent=2)
self.destroy()
</code></pre>
<p>因此,它会将更新的数据写入该文件,并记住在之后销毁该窗口,否则它将无法关闭</p>
<p>现在,您可以在运行程序时加载数据,如果进行了更改,这些数据将保存到文件中,以便下次运行程序时访问</p>
<p>如果你有问题,再次问他们</p>
<p>添加了Toplevel(确实是最小的更改,但添加了一些类似bind的内容,以确保如果用户将焦点放在编辑窗口之外,它将关闭,从而多个相同的窗口不会打开(因此还定义了另一种方法),所有更改如下所示:</p>
<pre class="lang-py prettyprint-override"><code>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()
</code></pre>
<p>改变<code>MainWindow</code>:</p>
<p>更改了<code>select()</code>方法以显示顶层</p>
<pre class="lang-py prettyprint-override"><code>def select(self, key):
player_edit_frame = EditData(self, stored_data, key)
player_edit_frame.focus_force()
</code></pre>
<p>变化<code>EditFrame</code>:</p>
<p>从顶层继承的</p>
<pre class="lang-py prettyprint-override"><code>class EditData(Toplevel):
def __init__(self, parent, data, key):
Toplevel.__init__(self, parent)
</code></pre>
<p>附加绑定</p>
<pre class="lang-py prettyprint-override"><code>self.bind('<FocusOut>', self.focus_out)
</code></pre>
<p>bind附带的一个方法(它检查焦点是否在整个窗口之外,否则即使焦点在条目小部件之外也会导致它关闭)</p>
<pre class="lang-py prettyprint-override"><code>def focus_out(self, event):
self.parent.refresh_data()
if event.widget == self:
self.destroy()
</code></pre>