在 tkinter 中使用顶层从不同的 class 更改主 class 标签的文本

Change main class label's text from different class using toplevel in tkinter

最近刚接触 tkinter

我有一个程序,当用户单击 [...] 按钮时,它会显示一个顶层 window,其中包含一个日历和其中的 [确定] 按钮。

当用户单击 [OK] 按钮时,我希望它更改 [startDate] 变量和主 window 中的 [labelStartDate] 标签。 我的下一个数据处理需要 [startDate] 变量。 [labelStartDate] 标签是为了向用户显示日期已更改。

如何实现? 我尝试使用 command=lambda 或 stringvar,但老实说,我有点迷失了尝试将它应用到我的程序中。

from datetime import date
from textwrap import fill
import tkinter as tk
from tkinter import ttk
from tkinter import Toplevel
from tkinter import font
from tkcalendar import Calendar
from turtle import color, width
    
# Define the GUI
class App(tk.Tk):
    def __init__(self):
        super().__init__()

        # root window
        self.title('Main Window')
        self.geometry('620x570')
        global startDate   #variable that I want to use for later data processing
        startDate = date.today().strftime("%Y/%m/%d/")

        #DATE MENU FRAME
        DateMenuBar = ttk.LabelFrame(self.master, borderwidth = 1, text='Setting')
        subFrame2 = tk.Frame(DateMenuBar, borderwidth = 1, relief = tk.FLAT, pady=0, padx=0)

        #SUB FRAME 2
        labelStart = tk.Label(subFrame2, text='Start',font=('meiryoui', 15))
        labelStartDate = tk.Label(subFrame2, text=startDate,font=('meiryoui', 15))
        btnOpenCalendar1 = tk.Button(subFrame2, height=1, background='#eeeeee', text='...', font=('meiryoui', 8), command=self.changeStartDate)

        labelStart.pack(side = tk.LEFT, ipadx=10)
        labelStartDate.pack(side = tk.LEFT, padx=(30,10))
        btnOpenCalendar1.pack(side = tk.LEFT)

        subFrame2.pack(fill = tk.X,padx=0, pady=10)
        DateMenuBar.pack(fill = tk.X,padx=20, ipadx=20, ipady=20)

    def changeStartDate(self):
        window = Window(self)
        window.grab_set() 


class Window(tk.Toplevel):
    def __init__(self, parent):
        super().__init__(parent)    

        self.title("Pick Date")
        self.geometry("250x250")
        
        def selectStartDate():
            startDate = cal.get_date()
            #I got stuck here, trying to figure out how to change the labelStartDate's text
        
        cal = Calendar(self, selectmode = 'day')
        cal.pack(padx=20, pady=10)
        frame = tk.Frame(self, borderwidth = 1, relief = tk.FLAT, pady=10, padx=20)
        btnOK = tk.Button(frame, height=2,width=8, background='#eeeeee', text='OK', font=('meiryoui', 9),command=selectStartDate)
        btnCancel = tk.Button(frame, height=2,width=8, background='#eeeeee', text='Cancel', font=('meiryoui', 9))

        btnOK.pack(side = tk.RIGHT, padx=(10,0))
        btnCancel.pack(side = tk.RIGHT)
        frame.pack(fill = tk.X)


if __name__ == "__main__":
    app = App()
    app.mainloop()

编辑备注: 我将缺少的代码添加到我的程序中,以便其他人可以 运行 :)

您可以先使用 tkinter.StringVar() 并将标签文本变量设置为相同,以便能够 modify the label's text.

self.labelStartDateVar = tk.StringVar() # Initalizing the text variable
self.labelStartDateVar.set(startDateData.start_date) # Setting initial value of the textvariable.
# Added textvariable as labelStartDateVar
self.labelStartDate = tk.Label(subFrame2, textvariable = labelStartDateVar, font = ('meiryoui', 15))

此外,使用 this post(of Observer Pattern) 中的一些知识,可以在检测到 startDate 发生变化时调用函数。我们通过定义一个新的 class 并使用 startDateData 对象作为全局对象来实现,要获取 startDate 本身的值,我们只需要访问它的 start_date 属性 startDateData.start_date 设置相同 属性 需要这样设置 -:

startDateData.start_date = cal.get_date()

完整代码如下所示-:

class startDate(object):
    def __init__(self):
        # Setting the default value as in the OP.
        self._start_date = date.today().strftime("%Y年 %m月 %d日")
        self._observers = []
        return

    @property
    def start_date(self):
        return self._start_date

    @start_date.setter
    def start_date(self, value):
        self._start_date = value
        for callback in self._observers:
            print('announcing change')
            callback(self._start_date)
        return

    def bind_to(self, callback):
        print('bound')
        self._observers.append(callback)

startDateData = startDate() # global startDateData object.

# Define the GUI
class App(tk.Tk):
    def __init__(self):
        super().__init__()

        # root window
        self.title('Main Window')
        self.geometry('620x570')
        global startDateData   #variable that I want to use for later data processing
        
        ###
        self.labelStartDateVar = tk.StringVar()
        self.labelStartDateVar.set(startDateData.start_date)
        startDateData.bind_to(self.updateStartDate) # Binding the updateStartDate function to be called whenever value changes.
        ###
        
        #SUB FRAME 2
        self.labelStart = tk.Label(subFrame2, text='開始',font=('meiryoui', 15))
        
        # Added textvariable as labelStartDateVar
        self.labelStartDate = tk.Label(subFrame2, textvariable = self.labelStartDateVar, font = ('meiryoui', 15))
        self.btnOpenCalendar1 = tk.Button(subFrame2, height=1, background='#eeeeee', text='...', font=('meiryoui', 8), command=self.changeStartDate)

        self.labelStart.pack(side = tk.LEFT, ipadx=10)
        self.labelStartDate.pack(side = tk.LEFT, padx=(30,10))
        self.btnOpenCalendar1.pack(side = tk.LEFT)

        subFrame2.pack(fill = tk.X,padx=0, pady=10)

    def updateStartDate(self, startDate) :
        self.labelStartDateVar.set(startDate)
        return


class Window(tk.Toplevel):
    def __init__(self, parent):
        super().__init__(parent)    

        self.title("Pick Date")
        self.geometry("250x250")
        
        # Globally fetch the startDateData object.
        global startDateData
        
        def selectStartDate():
            # All binded callbacks will be called, when the value is changed here.
            startDateData.start_date = cal.get_date()
        
        cal = Calendar(self, selectmode = 'day')
        cal.pack(padx=20, pady=10)
        frame = tk.Frame(self, borderwidth = 1, relief = tk.FLAT, pady=10, padx=20)
        btnOK = tk.Button(frame, height=2,width=8, background='#eeeeee', text='OK', font=('meiryoui', 9),command=selectStartDate)
        btnCancel = tk.Button(frame, height=2,width=8, background='#eeeeee', text='Cancel', font=('meiryoui', 9))

        btnOK.pack(side = tk.RIGHT, padx=(10,0))
        btnCancel.pack(side = tk.RIGHT)
        frame.pack(fill = tk.X)

注意: 由于 OP 中提供的代码不足以测试此解决方案是否有效。此外,由于提供的初始代码似乎不完整,最后答案中给出的完整代码也可能看起来不完整,但仍然实现了 OP 中给出的代码中存在的所有功能。

编辑:startDateData = startDate() 的先前位置是错误的,因为它试图在定义之前构造一个 class 的对象,现在该行已移至 startDate.

的 class 定义下方

编辑 2: 修正了一些拼写错误,正如@Mario Ariyanto 在评论中提到的那样。