根据单选按钮回答

时间:2016-04-02 22:21:25

标签: python-3.x tkinter

与我提出的另一个问题类似,但觉得这可能是经过深思熟虑后才能解决的问题。

我想要的是让用户选择两个单选按钮中的一个,点击“下一页”按钮并将其带到一帧。在该帧类中,将有两个标签,但根据前一帧中选择的单选按钮,我希望出现两个标签中的一个

这是我的代码 -

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()方法是否是实现此目的的正确方法?

更新4/4/16

经过一番挖掘后,我发现了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的内容中,根据选择的单选按钮,与单选按钮关联的字符串存储在String Var中?可能完全错了......无论如何,我现在更新了第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()

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

1 个答案:

答案 0 :(得分:0)

这有很多问题,但我试着采用与你使用的方法类似的方法。它有效......但我通常采取一种截然不同的方法。

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开发人员会发现奇怪的事情,比如保留一个框架字典。这不是必然错误的方法,但可能有更好的方法。如果你有一个未知或无限数量的帧,这可能是一个很好的方法。但实际上你可能会有两个。让它们成为属性是一种很好的方法。

第二种方法的另一个优点是被称为关注点分离。 PossibleSelectionsChoices框架彼此之间没有任何关系。他们对在其他类中找到的属性一无所知,或者甚至不知道其他类存在的属性。这是一件好事,因为这意味着如果你需要在Choices完成时发生不同的事情,你只需要改变Example所做的事情。你不需要摆弄Choices。这意味着你将有更少的机会来引入错误,如果你需要改变一些东西,你会有更多的灵活性。

最后,通过使用widget.destroy() vs _forget(),我们实际上正在销毁我们不再需要的小部件。如果我们只是调用_forget(),那么我们会将这些小部件保留在内存中。当然,如果您的应用程序只通过10个屏幕,您可能不会注意到内存泄漏,但如果您的应用程序增长,您将最终耗尽内存并且您的应用程序将崩溃。

进一步改进

当然,如果我真的在编写一个不仅仅是玩具/作业的应用程序,我可能会使用某种类型的Model-View-Presenter架构变体。使用这种架构,您可以编写应用程序,使您的UI层(所有Tkinter类)只处理与用户的接口。您的实际核心逻辑可以在您的模型类中找到,并通过演示者进行连接。这可能是过度的,但它可能是一个有用的练习。