Computer Science 112 Fundamentals of Programming II Command Buttons and Responding to Events
Event-Driven Programming 1.Layout and pop up a window 2.Wait for events 3.When an event occurs, respond 4.Goto step 2
Types of Events Button click Menu item selection Check box selection List box selection Keyboard entry Mouse move Mouse press Mouse release Mouse drag
Hello world! Revisited
Command Buttons The method addButton adds a widget of type tkinter.Button to the window and returns it Text, row, and column are required args Button attributes –text (the button’s label) –image (for “hot spots”) –state (“normal” or “disabled”) –command (a method to run when the button is clicked)
Methods as Attributes? Python methods and functions are first-class data objects They can be passed as arguments to other methods and functions, and returned as the values of other methods and functions The default command method for a button is a method of no arguments that does nothing
class HelloWorld(EasyFrame): """Illustrates buttons and user events.""" def __init__(self): """Sets up the window and the widgets.""" EasyFrame.__init__(self) self.label = self.addLabel(text = "Hello world!", row = 0, column = 0, columnspan = 2, sticky = "NSEW")
class HelloWorld(EasyFrame): """Illustrates buttons and user events.""" def __init__(self): """Sets up the window and the widgets.""" EasyFrame.__init__(self) self.label = self.addLabel(text = "Hello world!", row = 0, column = 0, columnspan = 2, sticky = "NSEW") # Add command buttons self.clearBtn = self.addButton(text = "Clear", row = 1, column = 0) self.restoreBtn = self.addButton(text = "Restore", row = 1, column = 1, state = "disabled") We can now view the layout, but the buttons can’t respond
class HelloWorld(EasyFrame): """Illustrates buttons and user events.""" def __init__(self): """Sets up the window and the widgets.""" EasyFrame.__init__(self) self.label = self.addLabel(text = "Hello world!", row = 0, column = 0, columnspan = 2, sticky = "NSEW") # Add command buttons self.clearBtn = self.addButton(text = "Clear", row = 1, column = 0, command = self.clear) self.restoreBtn = self.addButton(text = "Restore", row = 1, column = 1, command = self.restore, state = "disabled") The method’s names are here used just like variable references
class HelloWorld(EasyFrame): """Illustrates buttons and user events.""" def __init__(self): """Sets up the window and the widgets.""" EasyFrame.__init__(self) self.label = self.addLabel(text = "Hello world!", row = 0, column = 0, columnspan = 2, sticky = "NSEW") # Add command buttons self.clearBtn = self.addButton(text = "Clear", row = 1, column = 0, command = self.clear) self.restoreBtn = self.addButton(text = "Restore", row = 1, column = 1, command = self.restore, state = "disabled") # Methods to handle user events def clear(self): """Resets the label to the empty string and inverts the button states.""" self.label["text"] = "" self.clearBtn["state"] = "disabled" self.restoreBtn["state"] = "normal"
def __init__(self): """Sets up the window and the widgets.""" EasyFrame.__init__(self) self.label = self.addLabel(text = "Hello world!", row = 0, column = 0, columnspan = 2, sticky = "NSEW") # Add command buttons self.clearBtn = self.addButton(text = "Clear", row = 1, column = 0, command = self.clear) self.restoreBtn = self.addButton(text = "Restore", row = 1, column = 1, command = self.restore, state = "disabled") # Methods to handle user events def clear(self): """Resets the label to the empty string and inverts the button states.""" self.label["text"] = "" self.clearBtn["state"] = "disabled" self.restoreBtn["state"] = "normal" def restore(self): """Resets the label to 'Hello world!' and inverts the state of the buttons.""" self.label["text"] = "Hello world!" self.clearBtn["state"] = "normal" self.restoreBtn["state"] = "disabled"
Command Buttons and Event Handling Usually, each button has its own dedicated event handling method Kind of like an automatic if statement The GUI also automatically runs an event-driven loop, behind the scenes
A Calculator Prototype Can enter a number, but no other functions yet
class CalculatorDemo(EasyFrame): """Illustrates command buttons and user events.""" def __init__(self): """Sets up the window, label, and buttons.""" EasyFrame.__init__(self, "Calculator") self.digits = self.addLabel("0", row = 0, column = 0, columnspan = 3, sticky = "NSEW") digit = 9 for row in range(1, 4): for column in range(0, 3): button = self.addButton(str(digit), row, column) digit -= 1 # Instantiates and pops up the window. if __name__ == "__main__": CalculatorDemo().mainloop() Always lay out and pop up the window to refine the look of the UI, before you write the event handlers
Many Event Handlers, or One? Usually, each button has its own dedicated event handling method But, in this case, the method just adds the button’s text to the label’s text If we can find a way to access that text, we can use one event handling method for all the buttons
Solution Define a method that expects the button’s text as an argument Run that method when the button’s command attribute is set The method defines a nested function of no arguments, which adds the button’s text to the label’s text The method returns the function, which becomes the event handler for that button
def __init__(self): """Sets up the window, label, and buttons.""" EasyFrame.__init__(self, "Calculator") self.digits = self.addLabel("0", row = 0, column = 0, columnspan = 3, sticky = "NSEW") digit = 9 for row in range(1, 4): for column in range(0, 3): button = self.addButton(str(digit), row, column) button["command"] = self.makeCommand(str(digit)) digit -= 1 self.makeCommand is a method that defines and returns a function of no arguments
def __init__(self): """Sets up the window, label, and buttons.""" EasyFrame.__init__(self, "Calculator") self.digits = self.addLabel("0", row = 0, column = 0, columnspan = 3, sticky = "NSEW") digit = 9 for row in range(1, 4): for column in range(0, 3): button = self.addButton(str(digit), row, column) button["command"] = self.makeCommand(str(digit)) digit -= 1 # Event handling method builder for digit buttons def makeCommand(self, buttonText): """Define and return the event handler for a button.""" def addDigit(): if self.digits["text"] == "0": self.digits["text"] = "" self.digits["text"] += buttonText return addDigit The function addDigit has access to the button’s text
For Friday Input and output with data fields Responding to error conditions