Search code examples
pythontkinterscheduled-tasks

Why is this event not happening at the correct time, using schedule?


in my program I wanted to make it so that this a specific code gets triggered at a specific time during the day.

class MainGUI:
    def __init__(self):
        global allTerms
        global itemFinder
        with open("allTermsOut", "rb") as infile:
            allTerms = pickle.load(infile)
        with open("itemFinderOut", "rb") as infile:
            itemFinder = pickle.load(infile)

        self.t_entry = None
        self.d_entry = None
        self.curr_frame = None      

        #GUI Start
        
        schedule.run_pending()
        time.sleep(1)
        self.root = tk.Tk()
        self.term_entry = tk.StringVar()
        self.defn_entry = tk.StringVar()

        self.root.geometry("750x750")
        self.root.title("Rymn")
        
        # starting screen
        #prompt
        self.prompt = tk.Label(self.root, text = "What would you like to do?", font = ('Arial', 18))
        self.prompt.pack(padx = 10, pady = 10)

        self.frame = tk.Frame(self.root)
        # review button
        practiceButton = tk.Button(self.frame, text = f"Practice terms", font = ('Arial, 20'), command=self.startPractice)
        practiceButton.grid(row = 0, column=0)
        #add button
        addItems = tk.Button(self.frame, text = "Add Items", font = ('Arial', 20), command= self.addItem)
        addItems.grid(row= 0, column= 1, padx = 10)
        #delete button
        deleteItems = tk.Button(self.frame, text = "Delete Items", font = ('Arial', 20), command = self.deleteItems)
        deleteItems.grid(row=0,column=2)
        self.frame.pack()
        
        # Menu stuff
        self.frames2 = [None, None, None] # creating separate frames
        self.frames = [self.addItem, self.deleteItems, self.startPractice] # storing function for separate frames
        #menu bar
        menubar = tk.Menu(self.root)
        self.root.config(menu=menubar)
        #filemenu stuff
        filemenu = tk.Menu(menubar, tearoff = 0)
        #filemenu.add_command(label = "Home", command = lambda: self.showFrame('home'))
        filemenu.add_command(label = "Reviews Terms", command=lambda: self.showFrame('review'))
        filemenu.add_command(label = "Add Terms", command= lambda: self.showFrame('add'))
        filemenu.add_command(label = "Delete Terms", command= lambda: self.showFrame('delete'))
        menubar.add_cascade(label="Go To", menu = filemenu, underline = 0)
        
        self.root.mainloop()

I have a method called midnightPrinter which is just a test method:

def midnightPrinter(self):
    print("tis midnight")

Outside of my MainGUI method I have

try: 
    schedule.every().day.at("07:55").do(MainGUI.midnightPrinter)
    MainGUI()
finally:
    with open("allTermsOut", "wb") as outfile:
        print("allTerms ", allTerms)
        pickle.dump(allTerms, outfile)
    with open("itemFinderOut", "wb") as outfile:
        print("itemFinders ",itemFinder)
        pickle.dump(itemFinder, outfile)

To run my code and MainGUI method. The event is not triggered (nothing is printed in my console), at 7:55 AM. I believe the issue is where I'm placing my code for the schedule: schedule.every().day.at("07:55").do(MainGUI.midnightPrinter) but I'm just not sure where to put it?

Thanks


Solution

  • There are few issues in your code:

    • it is not recommended (actually should not) call .mainloop() inside class constructor function __init__()
    • according to document of schedule module, schedule.run_pending() should be called repeatedly. For a tkinter application, either use .after() or thread.
    • you need to pass the class function to schedule.do() using an instance of MainGUI class, not the class itself

    Below is an example based on provided code:

    import tkinter as tk
    import schedule
    
    class MainGUI:
        def __init__(self):
            self.root = tk.Tk()
            self.root.geometry("750x750")
            self.root.title("Rymn")
    
            # starting screen
            #prompt
            self.prompt = tk.Label(self.root, text = "What would you like to do?", font = ('Arial', 18))
            self.prompt.pack(padx = 10, pady = 10)
    
            self.frame = tk.Frame(self.root)
            # review button
            practiceButton = tk.Button(self.frame, text = f"Practice terms", font = ('Arial, 20'), command=self.startPractice)
            practiceButton.grid(row = 0, column=0)
            #add button
            addItems = tk.Button(self.frame, text = "Add Items", font = ('Arial', 20), command= self.addItem)
            addItems.grid(row= 0, column= 1, padx = 10)
            #delete button
            deleteItems = tk.Button(self.frame, text = "Delete Items", font = ('Arial', 20), command = self.deleteItems)
            deleteItems.grid(row=0,column=2)
            self.frame.pack()
    
            # Menu stuff
            self.frames2 = [None, None, None] # creating separate frames
            self.frames = [self.addItem, self.deleteItems, self.startPractice] # storing function for separate frames
            #menu bar
            menubar = tk.Menu(self.root)
            self.root.config(menu=menubar)
            #filemenu stuff
            filemenu = tk.Menu(menubar, tearoff = 0)
            #filemenu.add_command(label = "Home", command = lambda: self.showFrame('home'))
            filemenu.add_command(label = "Reviews Terms", command=lambda: self.showFrame('review'))
            filemenu.add_command(label = "Add Terms", command= lambda: self.showFrame('add'))
            filemenu.add_command(label = "Delete Terms", command= lambda: self.showFrame('delete'))
            menubar.add_cascade(label="Go To", menu = filemenu, underline = 0)
    
            #self.root.mainloop()  # should not call mainloop() here
    
        # dummy class functions
        def startPractice(self): print("practice")
        def addItem(self): print("add item")
        def deleteItems(self): print("delete items")
        def showFrame(self, frame): print("show", frame)
    
        def midnightPrinter(self):
            print("this midnight")
    
    # function to call schedule.run_pending() repeatedly using .after()
    def schedule_loop():
        schedule.run_pending()
        app.root.after(1000, schedule_loop)
    
    app = MainGUI() # create an instance of MainGUI
    schedule.every().day.at("07:55").do(app.midnightPrinter) # use instance instead
    schedule_loop()  # start the schedule loop
    app.root.mainloop() # start the tkinter mainloop