Tkinter App - My Toplevel window is not appearing. App is stuck in mainloop

51 Views Asked by At

I'm trying to build a work schedule tracker, starting with the header. A picture attachement shows how the design will look once completed

Schedule Header Cosmetics

I have three classes so far,

  • InputWindow: A Toplevel window for a user to select a month and year from two comboboxes
  • App: the main window, based on the user selections in InputWindow, App should load a header consisting of the elements in container TitleFrame
  • TitleFrame: a frame consisting of the app title, the selected month and selected year on the left, and a grid consisting of weekdays for the month in the first row, day numbers of the month on the second row, and crew shifts as "D" or "N" on the third row.

Here is the code:

# PEP8 Compliant Guidance
# Standard Library Imports
import tkinter as tk
from tkinter import ttk
import datetime

# Third-Party Library Imports

# Local Application/Library Specific Imports
import functions

class InputWindow(tk.Toplevel):
    def __init__(self, parent):
        super().__init__(parent)
        # Customize the top-level window as needed
        self.title("Schedule Selection")
        self.geometry("400x500")
        
        # widgets, labels, and other elements for user selections
        currentdate = datetime.datetime.now()
        currentmonth = currentdate.strftime("%B")
        currentyear = currentdate.strftime("%Y")
        
        # Month Selection
        self.select_schedule_month = ttk.Combobox(self, values=functions.months_list())
        self.select_schedule_month.set(currentmonth)
        self.select_schedule_month.pack()
        
        # Year Selection
        self.select_schedule_year = ttk.Combobox(self, values=functions.years_list())
        self.select_schedule_year.set(currentyear)
        self.select_schedule_year.pack()
        
        # Button to confirm selections and open the main application window
        sel_button = tk.Button(self, text="Confirm", command=self.confirm_selections)
        sel_button.pack()

    def confirm_selections(self):
        # Perform validation and check if the proper selections are made
        # If selections are valid, hide the top-level window and open the main application window
        if self.selections_are_valid():
            self.selected_month = self.select_schedule_month.get()
            self.selected_year = self.select_schedule_year.get()
            self.withdraw()  # Hide the top-level window
            self.master.deiconify()  # Show the main application window

    def selections_are_valid(self):
        # Implement your logic to validate selections
        # Return True if selections are valid, False otherwise
        return True

# The Main App Window - 35 columns | 5 rows
class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Schedule Window")
        self.geometry("1300x300")
        self.open_input_window()

        # title frame
        self.title_frame = TitleFrame(self)
        self.title_frame.grid(column=0, row=0, columnspan=35, padx=10, pady=10)
        
    def open_input_window(self):
        self.input_window = InputWindow(self)
        # self.wait_window(self.input_window)  # Wait for the top-level window to close
        self.handle_input_window_selections()
        
    def handle_input_window_selections(self):
        if self.input_window is not None:
            selected_month = self.input_window.selected_month
            selected_year = self.input_window.selected_year
            
            # Use the selected month and year to perform any necessary operations
            print(selected_month, selected_year)    
        
        # title frame
        self.title_frame = TitleFrame(self)
        self.title_frame.grid(column=0, row=0, columnspan=35, padx=10, pady=10)
    
    
# Schedule Header: Includes - Title, Calendar, Shifts, Crew Name, and Crew ID
class TitleFrame(tk.Frame):
    def __init__(self, parent):
        super().__init__(parent)
        self.config(borderwidth=1, relief="raised", width=1300)
        self.grid(column=0, row=0, columnspan=35, padx=10, pady=10)
        
        self.title_label = tk.Label(self, text="Overtime Schedule", font=("Calibri", 22))
        self.title_label.config(padx=462)
        self.title_label.grid(column=0, row=0, columnspan=24, sticky="w")
        
        self.ID_detection_label = tk.Label(self, text=f"Current User: {functions.get_user_id()}", 
                                           font=("Calibri", 12))
        # self.ID_detection_label.config(bg="black", fg="white")
        self.ID_detection_label.grid(column=34, row=0, sticky="e")
        
        self.Calendar_Month_hdr = tk.Label(self, text="Schedule Calendar", font=("Calibri", 12))
        self.Calendar_Month_hdr.grid(column=0, row=1)
        # self.Calendar_Month_hdr.configure(bg="black", fg="white")
        
        """Need to add a calendar widget for this feature"""
        self.Calendar_Month_label = tk.Label(self, text="Selected Month", font=("Calibri", 12))
        self.Calendar_Month_label.grid(column=0, row=2)
        self.Calendar_Year_label = tk.Label(self, text="Selected year", font=("Calibri", 12))
        self.Calendar_Year_label.grid(column=0, row=3)
        
# Top Level Selector
class SelectorFrame(tk.Toplevel):
    def __init__(self, parent):
        super().__init__(parent)
        pass

app = App()

app.withdraw()  # Hide the main application window

app.mainloop()

initially the app ran fine until I attempted to create the variables from the combobox selections in InputWindow and use them in TitleFrame as labels. I'm very new at python and tkinter and wanted to experiment with calling the variables from another class before attempting to update class labels.

InputWindow Line 42 & 43 - initial assignment

            self.selected_month = self.select_schedule_month.get()
            self.selected_year = self.select_schedule_year.get()

Calling to App Line 71-75 to print the values selected in InputWindow

    def handle_input_window_selections(self):
        if self.input_window is not None:
            selected_month = self.input_window.selected_month
            selected_year = self.input_window.selected_year
            
            # Use the selected month and year to perform any necessary operations
            print(selected_month, selected_year)    

All local import functions work as expected when tested

The error I'm seeing when trying to call the selected_month/selected_year variables is an AttributeError. I'm not sure how to call selections from outside of class so I need some education on this, but even removing these variables from the equasion, I still can't get the Toplevel window to appear.

Exception has occurred: AttributeError
'InputWindow' object has no attribute 'selected_month'
  File "C:\Users\edit\OneDrive\Py Scripts\Tkinter GUI\Schedule App\Schedule_GUI.py", line 71, in handle_input_window_selections
    selected_month = self.input_window.selected_month
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\edit\OneDrive\Py Scripts\Tkinter GUI\Schedule App\Schedule_GUI.py", line 67, in open_input_window
    self.handle_input_window_selections()
  File "C:\Users\edit\OneDrive\Py Scripts\Tkinter GUI\Schedule App\Schedule_GUI.py", line 58, in __init__
    self.open_input_window()
  File "C:\Users\edit\OneDrive\Py Scripts\Tkinter GUI\Schedule App\Schedule_GUI.py", line 116, in <module>
    app = App()
          ^^^^^
AttributeError: 'InputWindow' object has no attribute 'selected_month'

Thank you for any help, I certainly have a lot to learn.

0

There are 0 best solutions below