Global variables and appending for use in other top level functions - python

I'm trying to create an "Admin" section to a small program that executes some maths.
The admin button on the main TK window creates a top level window with an entry field that only turns on when the correct password is entered into a password field (or at least it will be when I figure out how to do this.)
The submit button is intended to update the global variable of a price that will then be remembered by the program from the entry field that would have the new price input by the user. The issue I'm having is how to make the global variable update and change and stay changed once this button is pressed.
This code is only designed to test the ability to do this but for the sake of context I will post it here anyways. Any help towards this goal would be fantastic.
The issue is that this code does not work, it wont allow me to alter the global variables, and produces the error the variable int has no attribute append?
Further - So append was the wrong move, fair enough, the problem i have is that global12mmprice = 200 is not updating the globalvariable and at other points in the program it is still referencing the original value. Is there a way to completely update the global variable so that the program will reflect the new value and the old one will no longer exist?
global12mmprice = 86.67
global15mmprice = 191.19
int12mmprice = int(global12mmprice)
int15mmprice = int(global15mmprice)
class mainwindow(tk.Frame):
def __init__(self, root):
tk.Frame.__init__(self, root)
b1 = tk.Button(self, text="Glass Table", command = self.glsqWindow)
b1.grid(column=1,row=2,pady=50,padx=10)
self.count = 0
b2 = tk.Button(self, text='Round Table', command = self.glrnWindow)
b2.grid(column=2,row=2,pady=50,padx=10)
self.count = 0
b3 = tk.Button(self, text='Console Table', command = self.glcnWindow)
b3.grid(column=3,row=2,pady=50,padx=10)
self.count = 0
b4 = tk.Button(self, text='Admin', command = self.admin)
b4.grid(column=4,row=2,pady=50,padx=10)
self.count = 0
def admin(self):
self.count += 1
window = tk.Toplevel(self)
window.geometry("600x350+300+300")
def submit():
int12mmprice.append(200)
b1 = tk.Button(window,text='Submit', command=submit)
b1.grid(column=3,row=2,pady=50,padx=10)
There is alot more code after this but this is the relevant part. Also any general advice you might have is of course welcome.
ANSWER:- Provided with alot of assistance from "fdhsdrg". This is the solution that i implemented to get the desired result for anyone who has this question in future.
As was explained to me i needed to create a file that the program could read and write to that would create the necessary information for the program to access and alter as and when needed.
import tkinter as tk
from tkinter import *
from tkinter import Tk, Frame, Menu
import tkinter.messagebox as box
import pickle, os
file=open('prices.dat','rb')
data=pickle.load(file)
file.close
global12mmprice = data[0]
global15mmprice = data[1]
class mainwindow(tk.Frame):
def __init__(self, root):
tk.Frame.__init__(self, root)
b1 = tk.Button(self, text="Glass Table", command = self.glsqWindow)
b1.grid(column=1,row=2,pady=50,padx=10)
self.count = 0
b2 = tk.Button(self, text='Round Table', command = self.glrnWindow)
b2.grid(column=2,row=2,pady=50,padx=10)
self.count = 0
b3 = tk.Button(self, text='Console Table', command = self.glcnWindow)
b3.grid(column=3,row=2,pady=50,padx=10)
self.count = 0
b4 = tk.Button(self, text='Admin', command = self.admin)
b4.grid(column=4,row=2,pady=50,padx=10)
self.count = 0
def admin(self):
self.count += 1
window = tk.Toplevel(self)
window.geometry("600x350+300+300")
def submit():
global data
data[0] = '86.67'
file=open('prices.dat','wb')
pickle.dump(data,file)
file.close
global root
box.showinfo('Administration','The program will now terminate and the prices will be updated.')
root.destroy()
b1 = tk.Button(window,text='Submit', command=submit)
b1.grid(column=3,row=2,pady=50,padx=10)
As you can see the data list in the .dat file gets updated, later i will replace this with a get.entry() field but for now this demonstrates the intended design. You might want to consider using resetboard instead of destroy if you want the program to automatically relaunch after closing.

Well, the error message you added pretty much explains everything.
int12mmprice is an Integer, which does not have the method append. Append is a method which can be used on objects of type List:
>>> a=9
>>> type(a)
<type 'int'>
>>> a.append(15)
Traceback (most recent call last):
File "<pyshell#1>", line 1, in <module>
a.append(15)
AttributeError: 'int' object has no attribute 'append'
>>> a=[9]
>>> type(a)
<type 'list'>
>>> a.append(15)
>>> a
[9, 15]
EDIT:
Right, now the problem of the scopes. To edit the global int12mmprice put global int12mmprice at the start of the submit function. This makes sure that submit does not look at int12mmprice in its own function scope but in the global scope.

Related

How to get the value of an Entry created in a def?

I'm working on a project and i would like to get the Value of an Entry created in a def (turned on by a button on Tkinter)
So I have my main tkinter menu, with a button which will call the def "panier".
The def "panier" is creating the Entry "value" and another button to call a second def "calcul".
The second def "calcul" will do things with the value of Entry...
But then, in the def "calcul", when i'm trying to do value.get() it tells "NameError: name 'value' is not defined"
Here is the code, btw the Entry must be created by the def...
from tkinter import *
def panier():
value=Entry(test)
value.pack()
t2=Button(test,text="Validate",command=calcul)
t2.pack()
def calcul(value):
a=value.get()
#here will be the different calculations I'll do
test=Tk()
t1=Button(test,text="Button",command=panier)
t1.pack()
test.mainloop()
Appreciate every feedback :)
You can make the variable global like this:
from tkinter import *
def panier():
global value
value = Entry(test)
value.pack()
t2 = Button(test, text="Validate", command=calcul)
t2.pack()
def calcul():
a = value.get()
print(a)
#here will be the different calculations I'll do
test = Tk()
t1 = Button(test, text="Button", command=panier)
t1.pack()
test.mainloop()
The global value line makes the variable global so you can use it anywhere in your program.
You can also pass in the variable as an argument like what #JacksonPro suggested
t2 = Button(test, text="Validate", command=lambda: calcul(value))
This is one way to do it. Globally create a collection (list or dictionary) to hold a reference to the Entry. When you create the Entry, add it to the collection. I made it with either a list or dictionary for holding the references, so toggle the commented variations in all three places to try it both ways.
import tkinter as tk
def panier():
for item in ('value', ):
ent = tk.Entry(test)
collection.append(ent)
# collection[item] = ent
ent.pack()
t2 = tk.Button(test,text="Validate",command=calcul)
t2.pack()
def calcul():
a = collection[0].get()
# a = collection['value'].get()
print(a)
collection = []
# collection = {}
test = tk.Tk()
t1 = tk.Button(test, text="Button", command=panier)
t1.pack()
test.mainloop()

Choosing from a List of methods in a tkinter Button

Good Day,
I'm new to this forum (and quite new to programming), so I hope my question is properly formulated.
I've been trying to create a GUI in python using tkinter, and I want to have two buttons calling methods of two different classes. One method is defining an integer, the second one is reporting content. I'd have a list of objects of the latter class, and I want to choose the right instance by the integer. Here's a MWE:
import tkinter as tk
class data:
def __init__(self, content):
self.content = content
def report(self):
print("This is reported as self.content:" + str(self.content)) #This doesnt report the correct value for some reason?
print("The Class does register the correct idx:" + str(Selector.idx))
print("Using the Dict the correct value can be returned:" + str(vocables[Selector.idx].content))
class increment:
def __init__(self):
self.idx = 0
def increase(self):
self.idx += 1
print(self.idx)
vocables[self.idx].report()
root = tk.Tk()
Selector = increment()
vocables = []
for id in range(10):
vocables.append(data(id))
# print(vocables[id].content)
CheckVocable = tk.Button(root, text="Report", command=vocables[Selector.idx].report)
CheckVocable.pack()
NextVocable = tk.Button(root, text="Increase Index", command=Selector.increase)
NextVocable.pack()
root.mainloop()
I do not understand why the print of line 8 always reports the value of the first item in the list (vocabules[0] in this instance) instead of my desired value, which is returned in all other print cases. Am I messing up the work with classes or is the button behavior confusing me?
Thanks in advance!

Retrieving value from Tkinter combobox

Eventually I want to use the values in the comboboxes as parameters in other functions, but I think if I can just get them to print for now, that will be enough to build off of. Here's what I have so far.
import tkinter as tk
from tkinter import ttk
import time
def ok():
betType = betTypeVar.get()
season = seasonVar.get()
print(betType, season)
def CreateSimPreviousSeasonWindow():
prevSeasonWindow = tk.Tk()
#============= Bet Type Input =============#
betTypeVar = tk.StringVar()
betTypeLabel = tk.Label(prevSeasonWindow, text="Bet type:").grid(row=0,column=0)
betTypeChosen = ttk.Combobox(prevSeasonWindow, values=['Moneyline','Total'])
betTypeChosen.grid(row=0, column=1)
seasonVar = tk.StringVar()
seasonLabel = tk.Label(prevSeasonWindow, text='Season:').grid(row=1, column=0)
seasonChosen = ttk.Combobox(prevSeasonWindow, values=['2018', '2017'])
seasonChosen.grid(row=1,column=1)
button = tk.Button(prevSeasonWindow, text='OK', command=ok)
button.grid(row=2,column=0)
prevSeasonWindow.mainloop()
This gives me
File "C:[directory...]", line 6, in ok
betType = betTypeVar.get()
NameError: name 'betTypeVar' is not defined
To me it looks pretty obvious that this error is because ok() doesn't have any parameters passed to it, so it has no idea what 'betTypeVar' is, but all the tutorials I've read do it this way, so I'm missing something. If I try actually passing ok() the arguments, it still doesn't work.
There are two things to fix in your code. First let's focus on CreateSimPreviousSeasonWindow:
betTypeVar = tk.StringVar()
seasonVar = tk.StringVar()
You defined two StringVar but you actually never used it or linked them to your combobox object. The correct way is to set them as a textvaraible:
betTypeChosen = ttk.Combobox(prevSeasonWindow, textvariable=betTypeVar, values=['Moneyline','Total'])
seasonChosen = ttk.Combobox(prevSeasonWindow, textvariable=seasonVar, values=['2018', '2017'])
Next, NameError: name 'betTypeVar' is not defined is due to your variables being local variables. You are trying to access the same variable across different functions. To pass them around, you need to declare global:
def ok():
global betTypeVar, seasonVar
betType = betTypeVar.get()
season = seasonVar.get()
print(betType, season)
def CreateSimPreviousSeasonWindow():
global betTypeVar, seasonVar
...
Also I want to point out that if you just want to retrieve the values of the combobox, you don't really need to create two StringVar. Just combobox.get() already works good enough.
import tkinter as tk
from tkinter import ttk
import time
def ok():
global betTypeChosen, seasonChosen
print (betTypeChosen.get(), seasonChosen.get())
def CreateSimPreviousSeasonWindow():
global betTypeChosen,seasonChosen
prevSeasonWindow = tk.Tk()
#============= Bet Type Input =============#
betTypeLabel = tk.Label(prevSeasonWindow, text="Bet type:").grid(row=0,column=0)
betTypeChosen = ttk.Combobox(prevSeasonWindow,values=['Moneyline','Total'])
betTypeChosen.grid(row=0, column=1)
seasonLabel = tk.Label(prevSeasonWindow, text='Season:').grid(row=1, column=0)
seasonChosen = ttk.Combobox(prevSeasonWindow, values=['2018', '2017'])
seasonChosen.grid(row=1,column=1)
button = tk.Button(prevSeasonWindow, text='OK', command=ok)
button.grid(row=2,column=0)
prevSeasonWindow.mainloop()
CreateSimPreviousSeasonWindow()

Tkinter - How to change the value of an argument for an event binding with lambda function?

I have a list named chosenTestHolder (imported from the my_config file) that consists of several objects each with the attribute 'sentence'.
When pressing the button 'Press' for the first time, the attribute 'sentence' of the first object in the chosenTestHolder should be displayed in the text widget. The next time the button 'Press' is pressed the attribute 'sentence' of the second object in chosenTestHolder should be displayed and so on.
I am using lambda event for binding the 'Press' button and tries to use a new sentences as its first arguments after each pressing of the 'Press' button. However, it keeps showing the first sentence.
When searching Stackoverflow I have seen in
Using lambda function to change value of an attribute that you can't use assignments in lambda expressions but by reading that I still have not figured out how to solve my problem.
Grateful for help! Code is below!
main.py
from tkinter import font
import tkinter as tk
import tkinter.ttk as ttk
import my_config
import Testlist as tl
class TestWidgetTest:
def __init__(self):
ram = tk.Frame(root)
ram.grid(in_=root,row=0, column=0)
self.myText = tk.Text(ram, height = 5)
self.myText.grid(row=0,column=1)
my_config.counter = 0
self.myButton = tk.Button(ram, text = 'Press')
self.myButton.grid(row =1, column =0, columnspan =2)
indata =[my_config.chosenTestHolder[my_config.counter] , self.myText]
self.myButton.bind('<ButtonRelease-1>',lambda event, arg=indata : self.TagConfigure(event, arg))
def TagConfigure(self, event, arg):
arg[1].delete('1.0',tk.END)
arg[1].insert('1.0',arg[0].sentence)
my_config.counter += 1
root = tk.Tk()
TestWidgetTest()
root.mainloop()
my_config.py
import Testlist as tl
testListHolder = [ ['Fabian was very tired'],
['Thomas light the fire'],
['Anna eat a red apple ']]
chosenTestHolder = []
count = 0
while count <(len(testListHolder)):
chosenTestHolder.append(tl.Testlist(testListHolder[count][0]))
count += 1
counter = 0
Testlist.py
class Testlist:
def __init__(self, sentence):
self.sentence = sentence
Your issue is the assignment of indata.
You do only assign in init.
To get your code working you need to re-configure your sentecte...
indata =[my_config.chosenTestHolder[my_config.counter] , self.myText]
self.myButton.bind('&ltButtonRelease-1&gt',lambda event, arg=indata : self.TagConfigure(event, arg))
I would advise to keep track of the current sentence as an instance variable.
class Test_widget(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, args, kwargs)
self.sentences=["a", "b", "c", "d"] # the data
self.show = tk.StringVar() # the current displayed data
self.show.set("NULL")
self.counter=0 # the indexer
tk.Label(self, textvariable=self.show).grid(row=0)
tk.Button(self, command=self.click).grid(row=1)
def click(self, event):
self.show.set("%s"%self.sentences[self.counter]) # use the indexer to access the data
self.counter = self.counter + 1 # modify the indexer
if self.counter = len(self.sentences): # make sure you dont run in index-err
self.counter = 0
As you see, there is no need at all for the lambdas.
Edit
As to your questions:
The change in your original code was not intended.
I do not see a use case where you can use a lambda for its use inside your code.
At least none where a lambda is necessary.
Please remember to use lambda only and exclusively if there are
no ( == NULL ) other options.
Using inheritance (thats what the mechanism is called), you can inherit functions, "default" behaviour from other classes. It is a common mechanism in programming and not exclusive to python.
It is used like any normal object except you have to call the constructor of the base class (what I do using tk.Frame.__init__(self, args, kwargs) inside the init method. For more information on inheritance please refer to the uncounted manuals and tutorials available for that topic (google is your friend now that you know what the mechanism is called).

Tkinter, saving functions to a list and then running them

I'm working on a GUI for a project in school. All the buttons that I have in my GUI are bound with functions that I have created. These functions call for already predefined functions. For some of the predefined functions, I need one or two arguments and I have solved that with entries. I type in the arguments in the right entries that are connected to the specific button and when I press the button, the function will run with the corresponding arguments.
The thing I want to do is to in some way when I press a button, the function should be saved to a list instead of being executed right away. And when I push the "run" button(a new button that I will create) everything in my list will be executed. I have been thinking about using a list box but I don't know exactly how they work or if its even possible to run a list box that contains a number of functions. Does someone have any ideas or solutions for me? Can I use the list box for this or is there something else that is better to use?
class App:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.entry1 = IntVar()
self.entry2 = IntVar()
def do_something():
value1 = self.entry1.get()
value2 = self.entry2.get()
self.listbox.insert(END, "predefined_function(value1, value2)")
def run_listbox_contents():
pass
self.button = Button(frame, text="Move", command=lambda: do_something())
self.button.pack(side=TOP)
self.entry1.set("value1")
self.entry = Entry(frame, textvariable=self.entry1)
self.entry.pack(side=TOP)
self.entry2.set("value2")
self.entry = Entry(frame, textvariable=self.entry2)
self.entry.pack(side=TOP)
self.listbox = Listbox(master)
self.listbox.pack(side=TOP)
root = Tk()
app = App(root)
root.title("Mindstorms GUI")
root.geometry("800x1200")
root.mainloop()
root.destroy()
Just use a standard list.
something like this
def hest(txt):
print "hest: " +txt
def horse(txt):
print "horse: " + txt
funcList = []
funcList.append(hest)
funcList.append(horse)
for x in funcList:
x("Wow")
This outputs
hest: Wow
horse: Wow
Was this what you wanted?
If I were you, I wouldn't want to save functions to a list. I would suggest another solution for you.
I suppose you have heard of the principle of MVC (Model-View-Controller). In your case, the list box is a part of view, and the process that saves functions and then calls them at once is a part of controller. Separate them.
You might want to save and display any string in the list box to let the users know that the corresponding functions have been enlisted and ready to run. For example, save a string "Function1 aug1 aug2 aug3" or "Funtion2 aug1 aug2" or whatever you like as a handle of the corresponding function.
And for the controller part, write a function (let's say conductor()). It reads the handle strings from the list, parses them and calls the corresponding functions. Where you want to run the enlisted functions, there you just call conductor().
Update:
Due to your comment I understand that you are pretty new to program. Let me show you how to write a simplest parser with your given variable names.
def run_listbox():
to_do_list = #get the list of strings
for handle_string in to_do_list:
#Let's say you got
#handle_string = "Predfined_function1 value1 value2"
#by here
handle = handle_string.split(" ")
#Split the string by space, so you got
#handle = ["Predfined_function1", "value1", "value2"]
#by here
if handle[0] == "Predfined_function1":
Predfined_function1(handle[1], handle[2]) #Call Predfined_function1(value1, value2)
elif handle[0] == "Predfined_function2":
Predfined_function2(handle[1], handle[2])
#elif ...
#...
#elif ...
#...
#elif ...
#...
This is not a perfect parser, but I hope it could let you know what does a parser look like.

Categories