I am making a rock paper scissors program and I need to change whose turn it is when they click a button, but I do not want to use the global keyword because the program is inside of a function.
Here is an example of what I am trying to do without using the global keyword:
from tkinter import *
root = Tk()
var = 1
def buttonClick():
global var
var = 1
print(var)
button = Button(root, text="button", command=buttonClick).pack()
root.mainloop()
I have tried to write command=(var = 1)
but that did not work.
CodePudding user response:
If the whole script is inside a function (including the buttonClick()
function) then use the nonlocal
keyword:
def buttonClick():
nonlocal var
var = 1
print(var)
If the function is not nested, the only way is to create a global variable and the global
keyword in both functions.
CodePudding user response:
No, you indeed can't. It would be possible to change the contents of a global var
if it were a list, for example. And then you could write your command as a lambda expression with no full function body.
But this is not the best design at all.
Tkinter event model couples nicely with Python object model - in a way that instead of just dropping your UI components at the toplevel (everything global), coordinated by sparse functions, you can contain everything UI related in a class - even if it will ever have just one instance - that way your program can access the var as "self.var" and the command as "self.button_click" with little danger of things messing up were they should not.
It is just that most documentation and tutorial you find out will have OOP examples of inheriting tkinter objects themselves, and adding your elements on top of the existing classes. I am strongly opposed to that approach: tkinter classes are complex enough, with hundreds of methods and attributes -wereas even a sophisticated program will only need a few dozens of internal states for you to worry about.
Best thing is association: everything you will ever care to access should eb a member of your class. In the start of your program, you instantiate your class, that will create the UI elements and keep references to them:
import tkinter as tk # avoid wildcard imports: it is hard to track what is available on the global namespace
class App:
def __init__(self):
self.root = tk.Tk()
self.var = 1
# keep a refernce to the button (not actually needed, but you might)
self.button = tk.Button(self.root, text="button", command=self.buttonClick)
self.button.pack()
def buttonClick(self):
# the button command is bound to a class instance, so
# we get "self" as the object which has the "var" we want to change
self.var = 1
print(self.var)
def run(self):
self.root.mainloop()
if __name__ == "__main__": # <- guard condition which allows claases and functions defined here to be imported by larger programs
app = App()
app.run()
CodePudding user response:
Yes, you can. Here's a hacky way of doing it that illustrates that it can be done, although it's certainly not a recommended way of doing such things. Disclaimer: I got the idea from an answer to a related question.
from tkinter import *
root = Tk()
var = 1
button = Button(root, text="button",
command=lambda: (globals().update(var=var 1), print(var)))
button.pack()
root.mainloop()