2

I'm new to programming and Python, and am making a calculator app to practise. I'm using Tkinter with Python 2.7. The app has various buttons as you'd expect, and Entry and Label widgets for showing the numbers/result.

I think my program does start mainloop, but closing the window doesn't stop mainloop. Since it did work as normal before I added an after loop, I assume the after is the problem. I'm also using wait_variable.

I'd be very grateful if you could have a look at my code and give some advice! I've included the main stuff; the code to create the widgets and deal with user input/results output are in different files (Buttons, Displays, Inputs) but hopefully it's understandable without those.

import Tkinter as tk
# These contain the other bits of code
import Buttons as bt
import Displays as ds
import Inputs as ip


class MainWindow(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        self.parent = parent
        self.parent.title("Calculator")

        # Making frames to fill with widgets
        self.displays_frame = tk.Frame(parent)
        self.displays_frame.grid(padx=10, pady=10)
        self.buttons_frame = tk.Frame(parent)
        self.buttons_frame.grid()
        # Initialising the widgets and user input functions
        self.display = ds.Displays(self.displays_frame)
        self.button = bt.Button(self.buttons_frame)
        self.input = ip.Inputs()

    def take_inputs(self, parent):
        self.parent = parent

        # This waits for a button to be pressed
        self.wait_variable(self.button.button_pressed)
        # On a button press, its value is inserted into display Entry box/result is put in Label
        # Both self.button.button_pressed above and self.display.r below are StringVars
        self.input.process_input(self.button.button_pressed, self.display.entry_box, self.display.r)
        # Using after here so it keeps waiting for button presses to be recorded
        self.after(100, self.take_inputs, self.parent)

def main():
    root = tk.Tk()
    app = MainWindow(root)
    # Here the wait_variable and after functions are called
    app.take_inputs(root)
    # The below string is printed after the first button has been pressed
    print "main, now starting mainloop" 
    root.mainloop()
    # "finished" is never printed
    print "finished"

if __name__ == '__main__':
    main()

As I suppose I've made my own event handler instead of using mainloop, I did try adding self.parent.protocol("WM_DELETE_WINDOW", self.end(parent)) to the take_inputs method, so I could quit everything without needing to run mainloop. The self.end function there was a method I added to MainWindow class that printed "closing now" and then exited or destroyed the program.

However, whatever function I put in there for protocol was run immediately; "WM_DELETE_WINDOW" wasn't being looked up properly (replacing "WM_DELETE_WINDOW" with "foo" didn't give an error).

Thanks for your help!

MSC Wilson
  • 21
  • 1
  • 5
  • 1
    why are you making your own event handler? What's wrong with how tkinter handles events? – Bryan Oakley Jan 02 '15 at 16:22
  • I believe that closing the window exits both the mainloop and the process. In a pure gui app, there is no place to print to. I recommend reading some of the related questions and answers in the right sidebar. – Terry Jan Reedy Jan 02 '15 at 23:20
  • Hi Terry, thanks for your reply. This is not a pure GUI app so statements are printed to the terminal. Before I added the after and wait_variable it did successfully print "finished" when I closed the window, indicating the mainloop was exited. I've read all the likely looking questions I can find on here which is why I tried adding the wm_protocol method, but as described that didn't work. – MSC Wilson Jan 02 '15 at 23:29
  • Bryan, thanks for your reply. I wasn't intending to replace mainloop! Using after with wait_variable was the only way I could think of to record the input of button clicks and translate that into a string. As I said I'm a beginner so quite probably this is a bad way of doing it. I've seen plenty of examples here where people have used after() with mainloop() so I didn't know it would affect anything. – MSC Wilson Jan 02 '15 at 23:34

1 Answers1

0

I've rewritten the code without using wait_variable. I suppose the problem was that closing the window did not pass wait_variable so the code never got back to the mainloop.

Here's the new take_inputs method. button.button_go is a Boolean variable first defined as False, but set to True as part of the button binding to the mouse click (not shown).

def take_inputs(self, parent):
    self.parent = parent

    # button.button_go is immediately set to False on updating the display box
    if self.button.button_go == True:
        self.input.process_input(self.button, self.display.entry_box, self.display.r)
        self.button.button_go = False
    self.after(100, self.take_inputs, self.parent)

The after() method now works nicely with mainloop as expected. Shame about wait_variable. It sounded neat but apparently isn't very useful.

I don't know why the wm_protocol "WM_DELETE_WINDOW" definition didn't work!

MSC Wilson
  • 21
  • 1
  • 5