how to get tkinter input outside of function within class - python

How do I get access to variables (ifrequency and iperiod) outside of the class?
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
class Parameters:
def __init__(self,master):
tk.Label(master,text='frequency').grid(row=0)
tk.Label(master,text='period').grid(row=1)
self.options1 = ['D1', 'D2']
self.options2 = ['daily', 'monthly']
self.mycombo1 = ttk.Combobox(master, value=self.options1)
self.mycombo1.bind("<<ComboboxSelected>>")
self.mycombo1.grid(row=0, column=1)
self.mycombo2 = ttk.Combobox(master, value=self.options2)
self.mycombo2.bind("<<ComboboxSelected>>")
self.mycombo2.grid(row=1, column=1)
self.myButton = tk.Button(master, text="Go", command=self.clicker).grid(row=3,column=1)
def clicker(self):
ifrequency = self.mycombo1.get()
iperiod = self.mycombo2.get()
return ifrequency, iperiod
p = Parameters(root)
root.mainloop()
print(p.ifrequency)
The code gives the error:
"AttributeError: 'Parameters' object has no attribute 'ifrequency'" when running the final line.
For a bit of context, I have built some code to get output from an API. I want the user to be able to pick the inputs (e.g. frequency, period) using a form and then have the main program call the API and using these inputs. My API code works but I need to get the user inputs assigned to a variable. I can do it but that variable is stuck in the function/class and I can't figure out the correct way to get it out.
This is my first question here (usually someone else has asked before me) so apologies if I have made any mistakes. Many thanks in advance for your help!

ifrequency and iperiod are not assigned to self and are just in the function's local scope so they disappear after clicker returns, and since it is called by the tkinter button, it's return value dosn't do anything. so try changing clicker so it assigns them to self
def clicker(self):
self.ifrequency = self.mycombo1.get()
self.iperiod = self.mycombo2.get()

Related

Python : how can I get the value of an Entry in a function

I'm a beginner in python and I'm creating an interface where I can click on a button to open a new window and then fill a form.
My problem here is about to get the value of the entry after the customer pressed the button print.
First as you can see I created a window.
This is example of what i want to do.
def return_nom(*args):
return ent1.get()
def save():
print("your name is", return_nom())
def new_win():
top = Toplevel(fen)
top.title("new window")
strvar = StringVar()
strvar.trace("w", return_nom)
ent1 = Entry(top, textvariable=strvar)
bouton1 = Button(top, text='print', command=save)
bouton1.pack()
ent1.pack()
top.mainloop()
fen = Tk()
lab = Label(fen)
lab.pack()
bouton = Button(fen, text='new window', command=new_win)
bouton.pack()
fen.mainloop()
If someone can tell me why it doesn't work and explain me why this technique works when I use this it for an entry on the main interface.
Thanks everybody ! ;)
The main issue you have here is scope, since you're trying to access the Entry and StringVar from functions that don't have access to it.
The name ent1 defined inside new_win() will not be accessible from return_nom().
Furthermore, it seems to me that what you actually want to query is the StringVar and not the Entry widget itself.
How you can pass the StringVar to the called functions, there are several ways:
Make the strvar a global variable, that way the other functions will have access to it. This is probably the worst possible solution to this problem, since the point of having functions is to avoid namespace pollution of a global namespace.
Make the return_nom() and save() functions inner functions of new_win(), which allows them to access the local variables in new_win() as a closure. This is slightly better.
Use an object-oriented interface, where your StringVar is an instance member and your save() is a method. State such as the StringVar is available to the whole instance. This is probably the best here, it's how Tkinter is actually intended to be used, it most naturally fits an object-oriented approach.
An example of using inner functions would be:
def new_win():
top = Toplevel(fen)
top.title("new window")
strvar = StringVar()
def return_nom(*args):
return strvar.get()
def save():
print("your name is", return_nom())
ent1 = Entry(top, textvariable=strvar)
bouton1 = Button(top, text='print', command=save)
bouton1.pack()
ent1.pack()
top.mainloop()
An example of an object-oriented approach would be:
class MyDialog:
def __init__(self, fen):
self.fen = fen
self.strvar = StringVar()
def return_nom(self, *args):
return self.strvar.get()
def save():
print("your name is", self.return_nom())
def new_win(self):
top = Toplevel(self.fen)
top.title("new window")
ent1 = Entry(top, textvariable=self.strvar)
bouton1 = Button(top, text='print', command=self.save)
bouton1.pack()
ent1.pack()
top.mainloop()
And then at the top-level:
my_dialog = MyDialog(fen)
bouton = Button(fen, text='new window', command=my_dialog.new_win)
But the above is still not the best approach, which would actually be to create subclasses of Tkinter.Frame for your windows, connect them together through methods and have your top-level code only instantiate a main Application class and call app.mainloop() on it, letting it drive the whole application flow through events connected to methods and other frames.
See a simple Hello World program in the Python tkinter documentation on the Python standard library to get a somewhat better idea of how tkinter is intended to be used.
That documentation also has pointers to many other resources on tkinter and on Tk itself that you can follow to get more in-depth knowledge of that toolkit library.
Check what the traceback is telling you when you start typing:
Exception in Tkinter callback
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/tkinter/__init__.py", line 1883, in __call__
return self.func(*args)
File "tktest.py", line 3, in return_nom
return ent1.get()
NameError: name 'ent1' is not defined
When you define a variable in a function, it's not automatically defined in other functions. That's called "scope of a variable". Either you define ent1 in the new_win() function with the global keyword or you make it a function attribute:
new_win.ent1 = Entry(top, textvariable=strvar)
...
new_win.ent1.pack()
and call it like that:
def return_nom(*args):
return new_win.ent1.get()
Happy programming!

The tk window won't show even though there is a mainloop

Even though there is a mainloop being called my tk window will not appear. The code used to work but as soon as I coded in the second function in the nums class there is no tk window. I would like for someone to point out the mistake instead of simply handing out the answer.
Can someone please help me fix this problem?
I use Python IDLE 3.8
Image: [1]: https://i.stack.imgur.com/o65WI.png
Code:
from tkinter import *
from random import randint
import time
#number assignments
class nums:
def __init__(self):
self.value=randint(1,100)
def assignnewnums(oldnum1,oldnum2,lbltxt,lbl,answer):
getans = answer.get()
if(getans==str((oldnum1.value+oldnum2.value))):
del(oldnum1)
del(oldnum2)
oldnum1=nums()
oldnum2=nums()
lbltxt="Correct!"
lbl.config(text=lbltxt)
time.sleep(5)
lbltxt="What is {} + {}".format(oldnum2.value,oldnum1.value)
lbl.config(text=lbltxt)
else:
lbltxt="Wrong! Try Again!"
lbl.config(text=lbltxt)
time.sleep(3)
lbltxt="What is {} + {}".format(oldnum2.value,oldnum1.value)
lbl.config(text=lbltxt)
a = nums()
b = nums()
#GUI startup
root = Tk()
#Label
title = Label(root, text="AddPrac", fg="dark blue")
title.pack()
#Question
questxt = "What is {} + {}".format(a.value,b.value)
ques = Label(root,text=questxt,fg="red")
ques.pack()
#UserAnswer
ans = Entry(root)
ans.pack()
#SubmitButton
enter = Button(root,text="Submit Answer!",fg="yellow",command=nums.assignnewnums(a,b,questxt,ques,ans))
enter.pack()
#GUI continued startup
root.mainloop()
I tried your code and the window does appear if you wait a few seconds.
This is due to the following offending code snippet:
command=nums.assignnewnums(a,b,questxt,ques,ans)
This doesn't do what you think it does. You were thinking of:
command=lambda: nums.assignnewnums(a, b, questxt, ques, ans)
The way your code is written now, it does not bind a callback to the button, but rather, calls- and executes the function (since you are invoking it explicitly), and attempts to bind the return value as a callback, which makes no sense. As a side effect of calling the function, the main thread sleeps (since assignnewnums uses time.sleep) for a bit before you reach root.mainloop.
Anytime you are binding a callback to a button, you want to provide a callable object - either just a function object, or if arguments are critical, a lambda or functools.partial.

Cannot type in Python Entry Widget

I've got an interesting problem with the tk Entry widget. If I run the following test code,
from Tkinter import *
root =Tk()
def pfunc(self):
print Input.get()
f=Frame(root)
f.pack()
Input=Entry(f)
#Input.bind("<Return>",pfunc)
Input.pack()
root.mainloop()
I can properly enter into the widget and print to console; however the following code, as part of a larger GUI, does not allow me to click in the Entry boxes at all.
self.Tlabel = Label(self.TempFrame, text="Temp")
self.Tlabel.pack( side = LEFT)
self.Tenter = Entry(self.TempFrame,width=10, bd =5)
self.Tenter.bind("<Return>",self.getFlux)
self.Tenter.pack (side=RIGHT)
self.Flabel = Label(self.FluxFrame, text="Flux")
self.Flabel.pack( side = LEFT)
self.Fenter = Entry(self.FluxFrame, width=10, bd =5)
self.Fenter.bind("<Return>",self.getTemp)
self.Fenter.pack(side = RIGHT)
def getFlux(self):
for i in range(len(self.fit_tuples)):
if self.fit_tuples[i][0]==self.currentBFMdate and self.fit_tuples[i][1]==self.cell.get():
fit_data=self.fit_tuples[i][2]
self.Fenter.set(fit_data[0]*np.exp(fit_data[1]*int(self.Tenter.get())))
else:
self.Fenter.set("Invalid")
def getTemp(self):
for i in range(len(self.fit_tuples)):
if self.fit_tuples[i][0]==self.currentBFMdate and self.fit_tuples[i][1]==self.cell.get():
fit_data=self.fit_tuples[i][2]
self.Tenter.set(np.log(float(self.Fenter.get())/fit_data[0])/fit_data[1])
else:
self.Tenter.set("Invalid")
Furthermore, if I run both codes on a separate windows PC I have the same problem. The only difference I can possibly think of is that I am using instance variables within a class; but it seems that other widgets are bound and working properly.
Basically, the bind method is passing "Return" as a parameter to getTemp. As another user suggested, just add another parameter to the function.
If you use bind method, callback is called with an event object. You should add event parameter to the method/function. (See Events and Bindings - An Introduction to Tkinter )
So, rpelcae following lines:
def getFlux(self, event):
...
def getTemp(self, event):
...
The first program work unintentionally. Its parameter name should be event, not self.

Global name error

I am still pretty new to Python, so I'm sorry if my question is trivial or even stupid. I am trying to build a little module that reacts on a callback of a Button (which isn't in the code). The callback is simulated in the main()-function. Now my problem is that I can't figure out how to get rid of the global name error. It says that drink0_ingred is not defined as global name. I need this as text for the label called self.l0. This is what I have so far:
import tkinter
from tkinter import ttk
def main():
root = tkinter.Tk()
callback = callbackkb0()
drink0 = Drink0(root)
root.mainloop()
def callbackkb0():
with open(file="drink0_ingred.txt") as ingred0:
drink0_ingred = ingred0.read()
print(drink0_ingred)
return drink0_ingred
class Drink0(ttk.Frame):
def __init__(self, root):
ttk.Frame.__init__(self, root)
self.grid()
self.widgets_create()
def widgets_create(self):
self.l0 = ttk.Label(self, text=drink0_ingred)
self.l0.grid()
main()
How do I define drink0_ingred as global name and make it accessable for other functions?
Thanks for helping me!
Two things you can do:
Globalise drink0_ingred:
with open(file="drink0_ingred.txt") as ingred0:
global drink0_ingred
drink0_ingred = ingred0.read()
Call the function instead of calling the variable:
self.l0 = ttk.Label(self, text=callbackkb0()) # Notice how I called the function
# because it returned drink0_ingred

Text Input in Tkinter

Goal
I am trying to write a basic file which I can import in all other programs that will have a simple function that will take entry from the user and then return it.
Code
For that I have the following code:
class takeInput(object):
def __init__(self,requestMessage,parent):
self.string = ''
self.frame = Frame(parent)
self.frame.pack()
self.acceptInput(requestMessage)
def acceptInput(self,requestMessage):
r = self.frame
k = Label(r,text=requestMessage)
k.pack(side='left')
self.e = Entry(r,text='Name')
self.e.pack(side='left')
self.e.focus_set()
b = Button(r,text='okay',command=self.gettext)
b.pack(side='right')
def gettext(self):
self.string = self.e.get()
self.frame.destroy()
print self.string
def getString(self):
return self.string
def getText(requestMessage,parent):
global a
a = takeInput(requestMessage,parent)
return a.getString()
And I also added some script level code so as to test this:
root = Tk()
getText('enter your name',root)
var = a.getString()
print var
root.mainloop()
And what is really baffling me is that:
var does not have the value that I entered it has the empty string ''
a.string variable has the value that I entered and I checked this from the shell.
Also When I tried to assign the string returned from a.getString() to var in the shell, then it worked.
note I am new to Tkinter programming and dont fully understand how the mainloop() works. So maybe this is were the problem is. But I am not sure.
Specs
OS:Linux Mint 14
Python IDLE 2.7
Please help me out with this issue.
As other answers tell, you print var before entering the mainloop, that is, before your window is actually running, and your program is waiting for user input.
You could rely on tkSimpleDialog family to get your input:
import Tkinter
import tkSimpleDialog
root = Tkinter.Tk()
var = tkSimpleDialog.askstring("Name prompt", "enter your name")
print var
If you want to pursue your way, you could perform your print from the "ok" button callback (gettext in your case). You could also generate a virtual event when "ok" is pressed and bind to this event in your main program (http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/virtual-events.html)
The flow of your code goes like this:
the main scope calls getText.
getText creates a takeInput object a.
the takeInput object initializes itself, creating Labels & buttons etc.
getText returns a.getString(), which returns self.string, which still has its default value, the empty string.
the main scope prints var, which is empty.
So far, all of this has taken place within the span of a few nanoseconds. The user hasn't even seen the window yet.
the main scope then calls root.mainloop(), which finally gives the user the opportunity to interact with the window. But it's too late. var has already been printed.
If you want getText to not return until the user has submitted his text, then mainloop has to occur inside getText, not after it.
from Tkinter import *
class takeInput(object):
def __init__(self,requestMessage):
self.root = Tk()
self.string = ''
self.frame = Frame(self.root)
self.frame.pack()
self.acceptInput(requestMessage)
def acceptInput(self,requestMessage):
r = self.frame
k = Label(r,text=requestMessage)
k.pack(side='left')
self.e = Entry(r,text='Name')
self.e.pack(side='left')
self.e.focus_set()
b = Button(r,text='okay',command=self.gettext)
b.pack(side='right')
def gettext(self):
self.string = self.e.get()
self.root.destroy()
def getString(self):
return self.string
def waitForInput(self):
self.root.mainloop()
def getText(requestMessage):
msgBox = takeInput(requestMessage)
#loop until the user makes a decision and the window is destroyed
msgBox.waitForInput()
return msgBox.getString()
var = getText('enter your name')
print "Var:", var
The problem is that your test routine already prints out the value of var before the dialog has been shown, let alone text being entered. (You can easily validate this by adding some print statements to your test code.) This is because the call to mainloop() is at the very end. Instead, you should call mainloop after creating the frame, but before reading and returning the input, e.g. it might go to your getText method:
def getText(requestMessage,parent):
a = takeInput(requestMessage,parent)
parent.mainloop()
return a.getString()
This still does not work really well, as you have to close the dialog (click the [x]-button) even after clicking on 'okay', and I am not sure how to fix this.
However, note that there already is a module for this, tkSimpleDialog, providing methods such as askstring(title, prompt) that show just such an input dialog. So you might either use those, or look at the source code (found in /usr/lib/python2.7/lib-tk or the like) to find out how it's done.
Here's a quick snippet:
import tkinter
from tkinter import simpledialog
root = tkinter.Tk()
# withdraw() will make the parent window disappear.
root.withdraw()
# shows a dialogue with a string input field
youtube_url = simpledialog.askstring('YouTube URL', 'Enter the youtube URL of the video', parent=root)
if str(youtube_url).startswith('http'):
pass
else:
pass

Categories

Resources