Python - tkinter - dynamic checkboxes
<p>One of my courses made a passing reference to tkinter, and that it was out of the scope of what the introductory course offered.  It piqued my curiosity though and I wanted to dig deeper into it.</p>

<p>I have the basics down - I can get checkboxes to appear where I want them to, but I'd like to make it more dynamic/pythonic.</p>

<p>Column 1 - all checkboxes should always display.  This works as expected.
Column 2 - only the first checkbox should display, and if it's clicked, then the additional 4 checkboxes should display.  When any of those 4 checkboxes are clicked, it should display a message in the text box at the bottom of the UI.</p>

<p>When I click the checkboxes for the focus courses in the 2nd column at the moment, they do nothing.  Also, the ABC 702 checkbox that is normally hidden seems to randomly appear when the checkbox for a core course is selected.  I can't figure out why.</p>

<pre><code>    from tkinter import *

    class Application(Frame):
        def __init__(self, master):
            Frame.__init__(self, master)
            self.grid()
            self.create_widgets()
            # self.grid()
            # self.create_widgets()

        def create_widgets(self):
            Label(self, text="How many of the core courses have you completed so far?"
                  ).grid(row=1, column=0, sticky=W)

            self.checkBoxA = BooleanVar()
            Checkbutton(self,
                        text="ABC 501",
                        variable=self.checkBoxA,
                        command=self.update_text
                        ).grid(row=2, column=0, sticky=W)

            self.checkBoxB = BooleanVar()
            Checkbutton(self,
                        text="ABC 502",
                        variable=self.checkBoxB,
                        command=self.update_text
                        ).grid(row=3, column=0, sticky=W)

            self.checkBoxC = BooleanVar()
            Checkbutton(self,
                        text="ABC 601",
                        variable=self.checkBoxC,
                        command=self.update_text
                        ).grid(row=4, column=0, sticky=W)
            self.checkBoxD = BooleanVar()
            Checkbutton(self,
                        text="ABC 602",
                        variable=self.checkBoxD,
                        command=self.update_text
                        ).grid(row=5, column=0, sticky=W)
            self.checkBoxE = BooleanVar()
            Checkbutton(self,
                        text="ABC 603",
                        variable=self.checkBoxE,
                        command=self.update_text
                        ).grid(row=6, column=0, sticky=W)
            self.checkBoxF = BooleanVar()
            Checkbutton(self,
                        text="ABC 701",
                        variable=self.checkBoxF,
                        command=self.update_text
                        ).grid(row=7, column=0, sticky=W)

            self.results_txt = Text(self, width=80, height=10, wrap=WORD)
            self.results_txt.grid(row=8, column=0, columnspan=3)

            Label(self, text="Which focus are you enrolled in?"
                  ).grid(row=1, column=1, sticky=W)

            self.checkBoxF1 = BooleanVar()
            Checkbutton(self,
                        text="Focus #1",
                        variable=self.checkBoxF1,
                        command=self.update_text
                        ).grid(row=2, column=1, sticky=W)

            # if self.checkBoxF1.get():
            #     self.checkBoxF1A = BooleanVar()
            #     Checkbutton(self,
            #                 text="ABC 503",
            #                 variable=self.checkBoxF1A,
            #                 command=self.update_text
            #                 ).grid(row=3, column=1, sticky=W)


        def update_text(self):
            courses = ""
            counter = 0
            focusCounter = 0

        ##The checkboxes for the focus courses do not display text at the bottom
            ##The ABC 702 checkbox randomly appears when clicking core courses
            ##Is this related to reusing self vs creating another attribute?
            if self.checkBoxF1.get():
                self.checkBoxF1A = BooleanVar()
                Checkbutton(self,
                            text="ABC 503",
                            variable=self.checkBoxF1A,
                            command=self.update_text
                            ).grid(row=3, column=1, sticky=W)

                self.checkBoxF1B = BooleanVar()
                Checkbutton(self,
                            text="ABC 504",
                            variable=self.checkBoxF1B,
                            command=self.update_text
                            ).grid(row=4, column=1, sticky=W)

                self.checkBoxF1C = BooleanVar()
                Checkbutton(self,
                            text="ABC 505",
                            variable=self.checkBoxF1C,
                            command=self.update_text
                            ).grid(row=5, column=1, sticky=W)

            self.checkBoxF1D = BooleanVar()
            Checkbutton(self,
                        text="ABC 702",
                        variable=self.checkBoxF1D,
                        command=self.update_text
                        ).grid(row=6, column=1, sticky=W)

            if self.checkBoxA.get():
                courses += "Introductory class \n"
                counter += 1

            if self.checkBoxB.get():
                courses += "Next level class description \n"
                counter += 1

            if self.checkBoxC.get():
                courses += "Core class #3 \n"
                counter += 1

            if self.checkBoxD.get():
                courses += "Another core class \n"
                counter += 1

            if self.checkBoxE.get():
                courses += "A higher level class \n"
                counter += 1

            if self.checkBoxF.get():
                courses += "An advanced class \n"
                counter += 1

            if self.checkBoxF1A.get():
                specialty = "Focused class #1"
                focusCounter += 1

            if self.checkBoxF1B.get():
                specialty = "Focused class #2"
                focusCounter += 1

            if self.checkBoxF1C.get():
                specialty = "Focused class #3"
                focusCounter += 1

            if self.checkBoxF1D.get():
                specialty = "Focused class #4"
                focusCounter += 1

            self.results_txt.delete(0.0, END)
            self.results_txt.insert(0.0, courses)
            if focusCounter &gt; 0:    #this means the user selected at least 1 focus course
                self.results_txt.insert(0.0, specialty)
            if counter == 6:
                congrats = ("\n Congratulations on completing all of your core courses! \n \n")
                self.results_txt.insert(0.0, congrats)
            # print(focusCounter)

    root = Tk()
    root.title("This is where the title goes")
    app = Application(root)
    root.mainloop()
</code></pre>

<p>If you first click checkbox <code>Focus #1</code> then it shows new checkboxes and they work as expected</p>

<p>But if you click other checkbox as first then you get error message </p>

<blockquote>
  <p>AttributeError: 'Application' object has no attribute 'checkBoxF1A'</p>
</blockquote>

<p>Problem is because in <code>update_text()</code> you use <code>checkBoxF1A</code>, <code>checkBoxF1B</code>, etc. even if they don't exist yet.</p>

<pre><code>if self.checkBoxF1A.get():
    specialty = "Focused class #1"
    focusCounter += 1
</code></pre>

<p>Create <code>self.checkBoxF1A = BooleanVar()</code> in <code>create_widgets()</code> - not in <code>if self.checkBoxF1.get()</code> and you will not have problem. </p>

<p>You can even create Checkbuttons without <code>grid()</code> in <code>create_widgets()</code></p>

<pre><code> self.checkbutton_F1 = Checkbuttons(...)
</code></pre>

<p>and in <code>if self.checkBoxF1A.get():</code> use <code>self.checkbutton_F1.grid(...)</code> to show widget and (in <code>else:</code>) <code>self.checkbutton_F1.grid_forget()</code> to hide it.</p>

python user-interface checkbox tkinter python-3.5