Modifying a variable in a function has no result - python

I have a Tkinter application, so I have the mainloop which is typical of Tkinter, and various functions which handle mouse clicks and all that garbage.
In one of the functions, I generate a string and I want to store that string SOMEWHERE in the program, so that I can use it later on if some other function is called or maybe if I want to print it from the main loop.
import this and that, from here and there etc etc
#blah blah global declarations
fruit = ''
def somefunction(event):
blahblahblah;
fruit = 'apples'
return fruit
mainwin = Tk()
#blah blah blah tkinter junk
#my code is here
#its super long so I don't want to upload it all
#all of this works, no errors or problems
#however
button = Button( blahblahblha)
button.bind("<button-1", somefunction)
print fruit
#yields nothing
mainwin.mainloop()
This is an abridged example. Everything else in the program works fine, I can track my variable throughout the program, but when it's time for it to be saved for later use, it gets erased.
For example, I can print the variable as I pass it along from one function to another as an argument, and it will be fine. It is always preserved, and prints. The instant I try to get it back into the loop or store it for later use, it gets lost or overwritten (I'm not quite sure which).
I am really unfamiliar with Python, so I bet it's something simple that I've missed. I am assuming this is supposed to work like every other language, where you save a string to a global variable, and it will stay there UNTIL you or something resets it or overwrites it.
My current workaround is to create a text file and save the string in it until I need it.
I am using Python 2.7.11 on Windows 7, but have had the same issue on Ubuntu.

When you do fruit = 'anything' inside the function, it assigns it as a local variable. When the function ends, that local variable disappears. If you want to reassign to a global variable, you need to indicate that you'd like to do so with the global keyword.
def somefunction(event):
global fruit # add this
blahblahblah
fruit = 'apples'
return fruit
Note that functions can access global variables without this line, but if you want an assignment to the same name to apply to the global one you have to include it.
Also, "<button-1" should be "<button-1>".
Also, instead of binding to a Button, you should just add a command to it:
button = Button(mainwin, text='blahblah', command=somefunction)
And Button widgets, when clicked, don't send an event object to the function they're bound to, so define somefunction as def somefunction():.
Also, the print fruit is executed exactly once. If you want to change fruit and then see the new value, you'll have to print it as some point after you've done the reassignment. Using return to send a value to a Button doesn't do anything, as the widget can't do anything with it. This is why Tkinter apps are commonly created as object-oriented (OO) programs, so you can easily save instance variables without having to use global.

Learn classes and your problems disappear. Almost any of these cover classes https://wiki.python.org/moin/BeginnersGuide/Programmers Also a Tkinter reference so you can fix your typos http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm
import sys
if sys.version_info[0] < 3:
import Tkinter as tk ## Python 2.x
else:
import tkinter as tk ## Python 3.x
class StoreVariable():
def __init__(self, root):
self.fruit = ''
button = tk.Button(root, bg="lightblue")
button.grid()
button.bind("<Button-1>", self.somefunction)
tk.Button(root, text="Exit", bg="orange", command=root.quit).grid(row=1)
def print_fruit(self):
print self.fruit
def somefunction(self, event):
self.fruit = 'apples'
print "fruit changed"
mainwin = tk.Tk()
SV=StoreVariable(mainwin)
mainwin.mainloop()
## assume that once Tkinter exits there is something stored
## note that you have exited Tkinter but the class instance still exists
print SV.fruit
## call the class's built in function
SV.print_fruit()

Basing on your abridged function, here are some things that might caused your problems:
You might not saved fruit to a variable inside the main loop/program. Values saved inside a function will be erased once that function finishes. Unless you saved it inside a class variable using self.variable_name (applicable if you are using classes). If you don't like classes, just save it within a variable inside the main loop/function like:
fruit = somefunction()
other stuff
print fruit #the time where you access fruit again
where this statement is inside the main loop/program where you would accesss it again with print.
You might be changing the value of fruit with other statements/functions. Not definite since you haven't posted your whole code.

Related

There is a way to wait on a user's answer in tkinter?

I'm developing an application what have its functions set in different files.
The main file have a tkinter interface and the buttons, entrys and labels are in other file, like this:
Mainfile.py
from tkinter import *
class Program:
def __init__(self, root):
root.geometry('200x200')
self.main_frame = Frame(root)
self.main_frame.pack()
import Buttons
self.branch = Buttons.New_Button(self.main_frame)
#Here i wuold like to verify the hipotetic variable after the main_frame were destroyed
if self.branch.hipotetic_variable:
root.mainloop()
app = Program(Tk())
Buttons.py
from tkinter import *
import functools
class New_Button:
def __init__(self, using_frame):
self.button_1 = Button(using_frame, text = 'Button 1', command=functools.partial(self.Func, using_frame))
self.button_1.pack()
def Func(self, to_destroy):
to_destroy.destroy()
#Here is the hipotetic variable what i would like to verify with if statment
self.hipotetic_variable = True
The problem is that I want to keep managing the program in the main file calling the other functions and implementing it, but I cannot verify if it's time to update the screen because mainloop makes impossible to verify it using a while loop and an hipotetic variable that's created after user pressed button.
I wold like to know if there is an way to update an variable contained in the Buttons.py file on Mainfile.py to keep implementing all other canvas in this file.
Your if self.branch.hipotetic_variable: check in the Program.__init__() method is only going to be executed when the Program class instance gets created initially, which is before the button that could change the value of the variable could have been pressed. You also don't want to make the hipotetic_variable an attribute of the Button because that will be destroyed along with the Frame it is in when that's destroyed in the button callback function.
Tkinter applications are user-event driven, meaning that they're "run" by responding to events (that's what mainloop is all about). This type of programming paradigm is different from the procedural or imperative one you're probably used to.
Therefore to do what you want requires setting things up so an event that the program can respond to will be generated, which in this case to when the frame is destroyed. One way to do that is by taking advantage of tkinter Variable classes to hold this hipotetic variable you're interested in. It looks like a boolean, so I used a tkinter BooleanVar to hold its value. One interesting thing about Variables is that you can have changes to their values "traced" by defining functions to be called whenever that happens. That's what I have done in the code below, and the callback function in this case — check_hipotetic_variable() — updates a Label to display the new value of the variable when it's called.
Below is your code with the modifications necessary to use a tkinter BooleanVar and trace changes to its value.
Mainfile.py
from tkinter import *
import Buttons
class Program:
def __init__(self, root):
root.geometry('200x200')
self.main_frame = Frame(root)
self.main_frame.pack()
self.notice_lbl = Label(root, text='')
self.notice_lbl.pack(side=BOTTOM)
self.hipotetic_variable = BooleanVar(value=False)
# Set up a trace "write" callback for whenever its contents are changed.
self.hipotetic_variable.trace('w', self.check_hipotetic_variable)
self.branch = Buttons.New_Button(self.main_frame, self.hipotetic_variable)
root.mainloop()
def check_hipotetic_variable(self, *args):
"""Display value of the hipotetic variable."""
value = self.hipotetic_variable.get()
self.notice_lbl.config(text=f'hipotetic variable is: {value}')
app = Program(Tk())
Buttons.py
from tkinter import *
import functools
class New_Button:
def __init__(self, using_frame, variable):
self.button_1 = Button(using_frame, text = 'Button 1',
command=functools.partial(self.Func, using_frame))
self.button_1.pack()
self.variable = variable # Save for use in callback.
def Func(self, to_destroy):
to_destroy.destroy()
self.variable.set(True) # # Change value of the variable.
P.S. I noticed you're not following the PEP 8 - Style Guide for Python Code, which makes reading your code harder to read and follow that if you're were following them — for that reason I strongly suggest you read the guide and start following the suggestions, especially the Naming Conventions which apply to functions and variable names, as well as the names of script files.

If I created widgets in one function, how can I access them in another function using Python Tkinter?

this is my first project using Tkinter so please excuse me if the question is very simple to solve.
Depending on what option the user chooses from a dropdown, I call a function that creates and places certain widgets (e.g. an entry) on a frame. Then, when another button is pushed I want to access the text inside this entry. However, this seems to be giving me errors (saying that the widget is undefined) because I want to access a widget which I create when a function is called.
An obvious solution I see is to create all possible widgets I want to use outside the function, and only place them when the function is called. This seems quite sloppy and creates many more issues. Is there another fix?
Thanks in advance!
This is the function where I create and place the widgets on the frame.
def loadBook():
print("book is loaded")
#Authors
labelAuth1 = tk.Label(frame, text="Author 1 Name:")
entryAuth1 = tk.Entry(frame)
labelAuth1.place(relwidth=0.23, relheight=0.08, rely=0.1)
entryAuth1.place(relheight=0.08, relwidth=0.18, relx=0.3, rely=0.1)
This is a snippet of a function which uses input from the entry widget I created above:
def isBook():
if len(entryAuthSur1.get())==0:
pass
else:
bookString = ""
bookString += entryAuthSur1.get()
When the second function executes, I get a runtime error that entryAuthSur1 is not defined.
All variables inside functions are local. That means that it is deleted after the function call ends. As your variable (entryAuth1) wasn't global so it only existed inside the function and was deleted when the loadBook function ended. This is the working code:
import tkinter as tk
# Making a window for the widgets
root = tk.Tk()
def loadBook():
global entryAuth1 # make the entry global so all functions can access it
print("book is loaded")
#Authors
labelAuth1 = tk.Label(root, text="Author 1 Name:")
entryAuth1 = tk.Entry(root)
# I will check if the user presses the enter key to run the second function
entryAuth1.bind("<Return>", lambda e: isBook())
labelAuth1.pack()
entryAuth1.pack()
def isBook():
global entryAuth1 # make the entry global so all functions can access it
# Here you had `entryAuthSur1` but I guess it is the same as `entryAuth1`
if len(entryAuth1.get())==0:
pass
else:
bookString = ""
bookString += entryAuth1.get()
print(bookString) # I am going to print the result to the screen
# Call the first function
loadBook()
root.mainloop()

Python: Closing a sub-child-window prevents the opening of a new sub-child

For my job in a laboratory of my University of Applied Sciences I need to create a Python-programm which creates a child-windows with the possibility to create another one.
So far this works quite fine.
The tricky thing is where I close the childrens child and try to open a new "grandchild" of the main-window.
Closing and opening also works fine on the level of the first child. I can enter that child, go back to the main menu and so on as long I wish.
Here the code I am working on right now:
import tkinter
def Praktika():
global Praktika
Praktika = tkinter.Toplevel(main)
Praktika.geometry("320x200")
Prak1 = tkinter.Button(Praktika, text="Praktikum 1", command =Praktikum1)
Prak1.pack()
Haupt = tkinter.Button(Praktika, text="Hauptmenu", command = ClosePraktika)
Haupt.pack()
def ClosePraktika():
Praktika.destroy()
def Praktikum1():
global Praktikum1
Praktikum1 = tkinter.Toplevel(main)
Praktikum1.geometry("320x200")
Haupt = tkinter.Button(Praktikum1, text="Hauptmenu", command = ClosePraktikum1)
Haupt.pack()
def ClosePraktikum1():
Praktika.destroy()
Praktikum1.destroy()
def CloseAll():
main.quit()
main = tkinter.Tk()
main.geometry("320x200")
main.title("Fueh")
tkinter.Button(main, text="Praktika", command=Praktika).pack()
tkinter.Button(main, text="Exit", command=CloseAll).pack()
main.mainloop()
This is now the third attempt until now and ffter the research I have done I start to think that handling sub-children ain't that easy as I think.
So well,
already thank you very much for the help!
The problem is that you have a function named Praktikum1, and then you create a global variable named Praktikum1 which causes the function to be destroyed. So, the next time you call the function, you're actually "calling" the variable.
Don't use the same name for global variables and for functions.

Python and Tkinter: object oriented programming query

I am trying to learn python, Tkinter and oop. Below is the code that I wrote after following tutorial on effbot.org
from Tkinter import Tk, Frame, Label
class Nexus(object):
"""Top level object which represents entire app"""
def __init__(self, main_window):
self.nexus_frame = Frame(main_window)
self.nexus_frame.pack()
self.label = Label(main_window, text="Tkinter")
self.label.pack()
def main():
main_window = Tk()
nexus_app = Nexus(main_window)
main_window.wm_title("Hello World Window")
width = main_window.winfo_screenwidth()
height = main_window.winfo_screenheight()
main_window.wm_minsize(width=width-100, height=height-100)
main_window.mainloop()
if __name__ == "__main__":
main()
Here a top level window is created first and it is passed as argument to Nexus class where I am adding a frame and a label to the frame. Then I am setting the size of top level window relative to current screen size back in the main function.
My question is why was the top level window create in main function?
Could it not be created inside __init__ of Nexus class itself?
What difference would it make if main_window was create inside __init__ of Nexus class and mainloop() was started therein?
Once Tk.mainloop is entered, no further code will be executed. Instead, the Tk event loop will take over (hence the name).
What that means is that if you, eg, did something like this:
def main():
...
main_window.mainloop()
print 'Hello world!'
then that print statement would never be executed (or, at least, not while the GUI is running).
So, with that in mind, why is there a problem with creating the root window and executing main loop within the constructor (the __init__ statement)? Well, two basic reasons:
It would mean that the constructor never returns, which is unexpected. If a programmer sees this:
def main():
Nexus()
print 'Hello world!'
then he or she will expect that print statement to be executed. As a rule, you don't expect creating an instance of a class to be the kind of thing which will cause an infinite loop (as the event loop is).
Related to that is the second reason: it would not be possible to create more than one instance of Nexus, because as soon as you create one, Tk.mainloop will take over. Again, that's unexpected: a class is a description of a type of object, and you would normally expect to be able to instantiate more than one object like that.
At the moment, if you write:
def main():
...
Nexus(main_window)
Nexus(main_window)
then you'll get two copies of your Nexus window on the screen. That's expected, and sensible. The alternative would not be.
So what's the take-away message?
When you're dealing with GUI programs, entering the event loop is the last thing you want to do. Your setup might involve creating one object (as now), or it might involve creating many objects (eg, a complex GUI app might have two or three windows).
Because we want to be able to write similar code in both cases, the usual approach is to create the root window (the Tk object) once, and then pass it in as a reference to any classes that need to know about it.

How to detect when an OptionMenu or Checkbutton change?

My tkinter application has several controls, and I'd like to know when any changes occur to them so that I can update other parts of the application.
Is there anything that I can do short of writing an updater function, and looping at the end with:
root.after(0, updaterfunction)
This method has worked in the past but I'm afraid that it might be expensive if there are many things to check on.
Even if I did use this method, could I save resources by only updating items with changed variables? If so, please share how, as I'm not sure how to detect specific changes outside of the update function.
Many tkinter controls can be associated with a variable. For those you can put a trace on the variable so that some function gets called whenever the variable changes.
Example:
In the following example the callback will be called whenever the variable changes, regardless of how it is changed.
def callback(*args):
print(f"the variable has changed to '{var.get()}'")
root = tk.Tk()
var = tk.StringVar(value="one")
var.trace("w", callback)
For more information about the arguments that are passed to the callback see this answer
To have an event fired when a selection is made set the command option for OptionMenu
ex.
def OptionMenu_SelectionEvent(event): # I'm not sure on the arguments here, it works though
## do something
pass
var = StringVar()
var.set("one")
options = ["one", "two", "three"]
OptionMenu(frame, var, *(options), command = OptionMenu_SelectionEvent).pack()
If you are using a Tkinter Variable class like StringVar() for storing the variables in your Tkinter OptionMenu or Checkbutton, you can use its trace() method.
trace(), basically, monitors the variable when it is read from or written to.
The trace() method takes 2 arguments - mode and function callback.
trace(mode, callback)
The mode argument is one of “r” (call observer when variable is read by someone), “w” (call when variable is written by someone), or “u” (undefine; call when the variable is deleted).
The callback argument is the call you want to make to the function when the variable is changed.
This is how it is used -
def callback(*args):
print("variable changed!")
var = StringVar()
var.trace("w", callback)
var.set("hello")
Source : https://dafarry.github.io/tkinterbook/variable.htm
This will print the dropdown selection to the console. but my suggestion is to avoid console in GUI based applications. create a text indicator and print output to it
use the function in below code
from tkinter import *
tk = Tk()
def OptionMenu_SelectionEvent(event):
print(var.get())
pass
var = StringVar(); var.set("one")
options = ["one", "two", "three"]
OptionMenu(tk, var, *(options), command = OptionMenu_SelectionEvent).pack()
tk.mainloop()

Categories

Resources