显示基于单选按钮答案的标签小部件



类似于我问的另一个问题,但经过深思熟虑,我觉得这可能是最好的方法。

我想要的是用户从两个单选按钮中选择一个,点击"下一页"按钮,然后只进入一帧。在该帧类中会有两个标签,但根据上一帧上选择的单选按钮,我希望两个标签中的一个出现

这是我的代码-

import Tkinter as tk
class MainApp(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        # the main container that holds all the frames
        container = tk.Frame(self)
        container.pack(side = "top", fill = "both", expand = True)
        container.grid_rowconfigure(0, weight = 1)
        container.grid_columnconfigure(0,weight = 1)

        self.frames = {}
         # adding frames to the dictionary
        for F in (Page1,Page2):
             frame = F(container,self)
             self.frames[F] = frame
             frame.grid(row = 0, column = 0, sticky = "w")
        self.show_frame(Page1)
    def show_frame(self,page_name):
        #SHOWS A FRAME WITH THE GIVEN NAME
        for frame in self.frames.values():
            frame.grid_remove()
        frame = self.frames[page_name]
        frame.grid()
        #STACKING THE FRAMES
        #frame = self.frames[cont]
        #frame.tkraise()
class Page1(tk.Frame):
    def __init__(self,parent,controller):
        tk.Frame.__init__(self,parent)
        lbl1 = tk.Label(self,text = "Yes",font =("Helvetica",12,"bold"))
        lbl1.grid(row=1,sticky="W")
        lbl2 = tk.Label(self,text = "No",font =("Helvetica",12,"bold"))
        lbl2.grid(row=1,column=1,sticky="W")
        btn1 = tk.Button(self, text="next page", font=('MS', 24, 'bold'))
        btn1.grid(row=3,column = 0,columnspan=1)
        self.var1 = tk.BooleanVar()
        rButton1 = tk.Radiobutton(self,variable = self.var1,value=True)
        rButton1.grid(row=2,sticky = "W")
        rButton2 = tk.Radiobutton(self,variable = self.var1,value=False)
        rButton2.grid(row=2,column=1,sticky = "W")
        btn1['command']= lambda: controller.show_frame(Page2)

class Page2(tk.Frame,Page1):
    def __init__(self,parent,controller):
        tk.Frame.__init__(self,parent)
        self.var1 = Page1.var1
        lbl = tk.Label(self,text="This is reccomendation 2",font=("Helvetica",12,"bold"))
        lbl.pack_forget()
        lbl1 = tk.Label(self,text="This is reccomendation 3",font=("Helvetica",12,"bold"))
        lbl1.pack_forget()
        # if self.var1.get() == 0:
        #     lbl.pack()
        # else:
        #     lbl1.pack()

所以有两件事,首先,我假设Page2必须继承Page1的self.var1,我尝试用这个

class Page2(tk.Frame,Page1):

但只接收此错误消息-

    self.var1 = Page1.var1
 AttributeError: class Page1 has no attribute 'var1'

我觉得奇怪,因为第1页确实有var1?!其次,我甚至不确定pack_forget()方法是否是实现这一目标的正确方法?

2016年4月4日更新

经过一番挖掘,我发现了StringVar变量。

所以在实施之后

    def get_page(self,className):
    for page in self.frames.values():
        if str(page.__class__.__name__) == className:
            return page
        return None

在控制器中,我现在可以访问第1页的self.var1

我已经更新了我的单选按钮-

    self.var1 = tk.StringVar()
    rButton1 = tk.Radiobutton(self,variable = self.var1,value="reco 1"
                              ,command = "this is reco 1")
    rButton1.grid(row=2,sticky = "W")
    rButton2 = tk.Radiobutton(self,variable = self.var1,value="reco 2"
                              ,command = "this is reco 2")
    rButton2.grid(row=2,column=1,sticky = "W")

根据我收集到的关于StringVar的信息,根据选择了哪个单选按钮,与单选按钮关联的字符串将存储在StringVar中?可能是完全错误的。。。无论如何,我现在已经更新了第2页-

class Page2(tk.Frame):
def __init__(self,parent,controller):
    tk.Frame.__init__(self,parent)
    self.controller = controller
    page_one = self.controller.get_page("Page1")
    # Access to Page1's self.var1
    reco = page_one.var1.get()
    lbl = tk.Label(self,text= reco,font=("Helvetica",12,"bold"))
    lbl.pack()

程序运行,但令人沮丧的是,它没有按我想要的方式运行,一旦按下下一页按钮,第二帧上就不会出现任何内容。我在这个思考过程中是朝着正确的方向前进,还是有其他方法可以做到这一点?无论哪种方式,文本都必须出现在下一帧上。

这有很多问题,但我试图保持与您使用的方法类似的方法。它有效。。。但我通常会采取一种截然不同的方式。

try:
    import Tkinter as tk
except ImportError:
    import tkinter as tk

class MainApp(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        # the main container that holds all the frames
        container = tk.Frame(self)
        container.pack(side = "top", fill = "both", expand = True)
        container.grid_rowconfigure(0, weight = 1)
        container.grid_columnconfigure(0,weight = 1)
        def show_1(button):
            self.frame_2.grid_remove()
            self.frame_1.show(button)
        def show_2(button):
            self.frame_1.grid_remove()
            self.frame_2.show(button)
        self.frame_1 = Page1(container, show_2)
        self.frame_2 = Page2(container, show_1)

class Page1(tk.Frame):
    def __init__(self,parent,callback):
        tk.Frame.__init__(self,parent)
        self.grid(row = 0, column = 0, sticky = "w")
        self.callback = callback
        lbl1 = tk.Label(self,text = "Yes",font =("Helvetica",12,"bold"))
        lbl1.grid(row=1,sticky="W")
        lbl2 = tk.Label(self,text = "No",font =("Helvetica",12,"bold"))
        lbl2.grid(row=1,column=1,sticky="W")
        btn1 = tk.Button(self, text="next page", font=('MS', 24, 'bold'))
        btn1.grid(row=3,column = 0,columnspan=1)
        self.var1 = tk.BooleanVar()
        rButton1 = tk.Radiobutton(self,variable = self.var1,value=True)
        rButton1.grid(row=2,sticky = "W")
        rButton2 = tk.Radiobutton(self,variable = self.var1,value=False)
        rButton2.grid(row=2,column=1,sticky = "W")
        btn1['command']= self.button_clicked
    def button_clicked(self):
        if self.var1.get():
            self.callback('button_one')
        else:
            self.callback('button_two')

class Page2(tk.Frame):
    def __init__(self,parent,controller):
        tk.Frame.__init__(self,parent)
    def show(self, selected_button):
        if selected_button == 'button_one':
            text = "This is reccomendation 2"
        elif selected_button == 'button_two':
            text = "This is reccomendation 3"
        else:
            text = selected_button
        print(selected_button)
        lbl = tk.Label(self,text=text,font=("Helvetica",12,"bold"))
        lbl.pack()
        self.grid()

app = MainApp()
app.mainloop()

更好的方法

如果我只是使用Tkinter,我可能会使用更像这样的东西:

import random
try:
    import tkinter as tk
except ImportError:
    import Tkinter as tk

class Choices(tk.Frame):
    def __init__(self, choice_a, choice_b, callback):
        super().__init__()
        self._choice = tk.StringVar()
        self.left_button = tk.Radiobutton(
            self,
            variable=self._choice,
            text=choice_a,
            value=choice_a,
        )
        self.left_button.grid(row=0, column=0, sticky=tk.W)
        self.right_button = tk.Radiobutton(
            self,
            variable=self._choice,
            text=choice_b,
            value=choice_b
        )
        self.right_button.grid(row=0, column=1, sticky=tk.E)
        self._choice.set(choice_a)
        self.next_button = tk.Button(self, text='Next', command=callback)
        self.next_button.grid(row=1, column=0, columnspan=2)
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)
        self.grid_columnconfigure(1, weight=1)
    @property
    def choice(self):
        return self._choice.get()
    @property
    def choice_a(self):
        return self.left_button['text']
    @choice_a.setter
    def choice_a(self, value):
        self.left_button.config(text=value, value=value)
    @property
    def choice_b(self):
        return self.right_button['text']
    @choice_b.setter
    def choice_b(self, value):
        self.right_button.config(text=value, value=value)

class PossibleSelections(tk.Frame):
    def __init__(self, root, selections, command):
        super().__init__(root)
        self.command = command
        self.show_selections(selections)
    def show_selections(self, selections):
        for widget in self.winfo_children():
            widget.destroy()
        for selection in selections:
            tk.Label(self, text=selection).pack()
        tk.Button(self, command=self.command).pack()

class Example(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title('Example Chooser')
        self.choice_frame = Choices(
            'Do you like this?',
            'Do you like that?',
            self.show_options,
        )
        self.choice_frame.pack()
        self.selections_frame = PossibleSelections(self, [], self.show_choices)
    def show_options(self):
        self.choice_frame.pack_forget()
        self.selections_frame.show_selections(
            random.sample('ABCDEF', 3)+[self.choice_frame.choice],
        )
        self.selections_frame.pack()
    def show_choices(self):
        self.selections_frame.pack_forget()
        options = random.sample([
            'My favorite color is blue',
            'My favorite color is yellow',
            'My favorite color is blu-no, yelloooooow!',
            'I seek the grail',
            'My name is Arthur, King of the Britons',
        ], 2)
        self.choice_frame.choice_a = options[0]
        self.choice_frame.choice_b = options[1]
        self.choice_frame.pack()

if __name__ == '__main__':
    Example().mainloop()

为什么这样更好

在您当前的方法中,您正在做一些其他Python开发人员会觉得奇怪的事情,比如保留一个框架字典。这不一定是错误的方法,但可能有更好的方法。如果你有未知或无限数量的帧,这可能是一个很好的方法。但事实上,你可能会有两个。让它们成为属性是一个很好的方法。

第二种方法的另一个优点是被称为关注点分离。CCD_ 1和CCD_。他们不知道在另一个类上找到了什么属性,甚至不知道另一个类别是否存在。这是一件好事,因为这意味着如果你需要在Choices完成时发生一些不同的事情,你只需要改变Example的功能。你不需要摆弄Choices。这意味着你引入bug的机会会减少,如果你需要改变一些东西,你会有更多的灵活性。

最后,通过使用widget.destroy()_forget(),我们实际上是在破坏我们不再需要的小部件。如果我们简单地调用_forget(),那么我们将把这些小部件保存在内存中。当然,如果你的应用程序只经过10个屏幕,你可能永远不会注意到内存泄漏,但如果你的程序增长,你最终会耗尽内存,应用程序也会崩溃。

进一步改进

当然,如果我真的在编写一个不仅仅是玩具/作业的应用程序,我可能会使用模型视图演示器架构的某种变体。使用这种体系结构,您将以这样一种方式编写应用程序,即您的UI层(所有的Tkinter类)将只处理与用户的接口。您的实际核心逻辑可以在模型类中找到,通过演示器进行连接。这可能太过分了,但这可能是一个有用的练习。

最新更新