How do I access class variables? - python-3.x

In this program I want a user to enter credentials and then based on the inputs validate whether it is correct. I am using tkinter to provide a GUI. I want to be able to take the auth function outside of the class so I can shut the tkinter dialog once the account has been logged in, however, the problem here is that the auth function is within the class, I've tried various ways to retrieve the variable but I've had no luck.
from tkinter import *
import tkinter.messagebox as tm
class LoginFrame(Frame):
def __init__(self, master):
super().__init__(master)
self.label_Email = Label(self, text="Email")
self.label_password = Label(self, text="Password")
self.entry_Email = Entry(self)
self.entry_password = Entry(self, show="*")
self.label_Email.grid(row=0, sticky=E)
self.label_password.grid(row=1, sticky=E)
self.entry_Email.grid(row=0, column=1)
self.entry_password.grid(row=1, column=1)
self.checkbox = Checkbutton(self, text="Keep me logged in")
self.checkbox.grid(columnspan=2)
self.logbtn = Button(self, text="Login", command=self._login_btn_clicked)
self.logbtn.grid(columnspan=2)
self.pack()
def _login_btn_clicked(self):
# print("Clicked")
Email = self.entry_Email.get()
password = self.entry_password.get()
# print(Email, password)
self.answer = auth(Email, password)
root = Tk()
lf = LoginFrame(root)
if 'Bearer' in lf.answer:
root.quit()
root.mainloop()
My auth function will return a bearer token for the next stage if the login is successful, therefore I am checking whether or not the answer variable has returned it. If it has then I will shut the tkinter dialog

You're accessing the instance variable correctly, just in the wrong "order". Meaning, the answer must be checked only after the button is clicked. Basically, when your GUI loads, or directly after making the frame, the button isn't clicked, so the variable isn't defined, yet you're trying to access it immediately
One simple option is to not access the instance variable, and just use the passed in Tk object of the master
def _login_btn_clicked(self):
# print("Clicked")
Email = self.entry_Email.get()
password = self.entry_password.get()
# print(Email, password)
answer = auth(Email, password)
if 'Bearer' in answer:
self.master.quit()

Related

program flow hanging inside inner function

Really hoping to get an explanation /fix for this issue. Using Python3 and tkinter.
I have a program window that calls a chain of functions for log in to a database. One of them calls a toplevel() log in dialog that should return an array / list of values. The toplevel dialog creates a set of entries that are read by a nested function and values stashed in a global list and then destroys toplevel window. The list is returned by the outer function to an original calling function.
Works great except if this function call originates from a higher level TK dialog. As is, I have to close out the entire program to get a return from that function. How do I fix this? Function listed here
Thanks!
# GUI constructor for log in
def login():
# Create logwin window
logwin = Toplevel()
logwin.title("Log In")
# Called by login() GUI/bsubmit - returns credentials from log in GUI
def userinfo():
global carray
sname=serventry.get()
lname=lnentry.get()
pname=passentry.get()
carray = [sname, lname, pname]
logwin.destroy()
print("userinfo() = ", carray)
# Create GUI
servlabel = Label(logwin, text="Server Address: ")
servlabel.grid(row=0, column=0)
serventry = Entry(logwin)
serventry.grid(row=0, column=1)
serventry.focus()
lnlable = Label(logwin, text="User Name: ")
lnlable.grid(row=1, column=0, padx=5)
lnentry = Entry(logwin)
lnentry.grid(row=1, column=1, padx=4)
passlabel = Label(logwin, text="Enter Password: ")
passlabel.grid(row=2, column=0)
passentry = Entry(logwin, show="*")
passentry.grid(row=2, column=1)
bsubmit = Button(logwin, text="Submit", command=userinfo, default='active')
bsubmit.grid(row=4, column=1, columnspan=2)
logwin.mainloop()
print("login() end:", carray)
return(carray)
I found the answer to this one:
I was using logwin.destroy in the inner function userinfo() and for whatever reason this did not allow the function to continue while the main Tk() construct was running. I then tried placing logwin.quit() on the inner loop and this allowed the function to progress as intended but did not destroy the logwin construct. Placing logwin.destroy() after the logwin.mainloop() then takes care of this. Data is passed back to my program and life is good.
def login():
# Create logwin window
logwin = Toplevel(root)
logwin.title("Log In")
# Called by login() GUI/bsubmit - returns credentials from log in GUI
def userinfo():
global carray
sname=serventry.get()
lname=lnentry.get()
pname=passentry.get()
carray = [sname, lname, pname]
logwin.quit()
print("userinfo() = ", carray)
return carray
# Create GUI
servlabel = Label(logwin, text="Server Address: ")
servlabel.grid(row=0, column=0)
serventry = Entry(logwin)
serventry.grid(row=0, column=1)
serventry.focus()
lnlable = Label(logwin, text="User Name: ")
lnlable.grid(row=1, column=0, padx=5)
lnentry = Entry(logwin)
lnentry.grid(row=1, column=1, padx=4)
passlabel = Label(logwin, text="Enter Password: ")
passlabel.grid(row=2, column=0)
passentry = Entry(logwin, show="*")
passentry.grid(row=2, column=1)
bsubmit = Button(logwin, text="Submit", command=userinfo, default='active')
bsubmit.grid(row=4, column=1, columnspan=2)
logwin.bind('<Return>', userinfoalt) # binds alternate credentials function to the return key.
logwin.mainloop()
logwin.destroy()
print("login() end:", carray)
return(carray)

tkinter catch value from lambda function

I'm trying to wrap my head around this problem.
Say I have a code like this:
def get_input(data_A, data_B):
all_data = [data_A.get(),dataB.get()]
return(all_data)
def the_gui():
root = Tk()
data_A = Entry(root)
data_B = Entry(root)
button = Button(root, text='Submit', command=lambda: get_input(data_A, data_B))
mainloop()
My goal is to get the value of data_A and data_B once I clicked the submit button.
I tried to use global variable and everything, but I kept failing to catch the value.
The only thing that works is when I put the whole get_input() function inside the_gui() function. However, I don't think that's a good practice to implement.
Any suggestions?
Here is a simple example of how you could write this to get the results you are looking for.
When using global is that all your root window and related fields are in a function. So you would have to define global in both function and this is not what you want to do.
Typically you will want to write the root window in the global namespace and not in a function or write it into a class so you can avoid global's all-together.
button = Button(...) may not be doing what you think it is. This does not return a value from the command once clicked. Tkinter buttons do not care about anything being returned. So you have to record that value elsewhere.
I am not sure how you code is working as you do not use geometry managers and mainloop() should be attached to the root window so I have added those in as well.
Example 1:
import tkinter as tk
def get_input():
global a_and_b
a_and_b = [data_a.get(), data_b.get()]
# If you want to keep a running record of all values submitted
# then you can do this instead:
# a_and_b.append([data_a.get(), data_b.get()])
def print_a_b():
print(a_and_b)
root = tk.Tk()
a_and_b = []
data_a = tk.Entry(root)
data_b = tk.Entry(root)
data_a.pack()
data_b.pack()
tk.Button(root, text='Submit', command=get_input).pack()
tk.Button(root, text='Print A/B List', command=print_a_b).pack()
root.mainloop()
Example 2 using OOP:
import tkinter as tk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.a_and_b = []
self.data_a = tk.Entry(self)
self.data_b = tk.Entry(self)
self.data_a.pack()
self.data_b.pack()
tk.Button(self, text='Submit', command=self.get_input).pack()
tk.Button(self, text='Print A/B List', command=self.print_a_b).pack()
def get_input(self):
self.a_and_b = [self.data_a.get(), self.data_b.get()]
def print_a_b(self):
print(self.a_and_b)
if __name__ == '__main__':
App().mainloop()

Viewing and Interact with function that requires User input in Tkinter

I am Creating an App with Tkinter and for the App I need to use a function in a package that requires user input. I am trying to use sys.stdout in a thread and outputting sys.stdout to a Listbox() this works for another function of mine doing like the same thing but there isn't any user input. In the first line of this thread I am changing a button then doing the operation, and the button doesn't change and the App goes into non-responsive. My question is will the sys.stdout send what the input is asking? and How do I have user input from entry box send to the function while it is still running in the thread? Thanks in advance
Truncated code bellow:
class Login(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
global Key
global button1
Key = tk.StringVar()
#Buttton to start thread
button1 = ttk.Button(self, text='Login',command=lambda:Setupthread2(lbx,button1,Key,KeyEntry))
button1.pack()
#Entry for user to input
KeyEntry = tk.Entry(self, show= '*', textvariable=Key)
KeyEntry.pack()
scb = tk.Scrollbar(self)
scb.pack(fill='y' ,side='right')
lbx = tk.Listbox(self, yscrollcommand=scb.set)
lbx.pack()
scb.config(command=lbx.yview)
def Setupthread2(object,button1,Key,KeyEntry):
global flag
send_process = threading.Thread(target=callback(object,button1,Key,KeyEntry))
send_process.start()
flag = False
def callback(object,button1,Key,KeyEntry):
#Changing the button so I can use the same button for both starting the tread and to send to sample func
button1.config(text = 'Send Code', command = lambda:SendUsrInput(Key,KeyEntry))
old_stdout = sys.stdout
sys.stdout = StdoutRedirectorLabel(object)
sample()
sys.stdout = old_stdout
#After Output finished set scrollbar to bottom
object.yview_moveto(1.0)
class StdoutRedirectorLabel(object):
def __init__(self,widget):
self.widget = widget
def write(self,text):
self.widget.insert('anchor',text)
# Me trying to send what they type in entry box to function
def SendUsrInput(Key,KeyEntry):
Key = Key.get()
print(Key)
KeyEntry.delete(0,'end')
#Truncated version of the function I need to show console and to send input to
def sample():
usrInput = input('Enter Input to get something Back')
print(usrInput + ' Was my input')
So in the module where it asks for user input with help I made a Tk window asking for input with a timeout using after()
def sample()
sms_code = loginTimeout()
def loginTimeout():
root = tk.Tk()
sms_code = ''
def get_entry() -> str:
"""Gets and returns the entry input, and closes the tkinter window."""
nonlocal sms_code
sms_code = entry_var.get()
root.destroy()
return sms_code
# Your input box
entry_var = tk.StringVar()
tk.Tk.wm_title(root,'Trading Bot')
tk.Label(root, text='Please Enter the Authorization Code').pack()
tk.Entry(root, textvariable=entry_var).pack()
# A button, or could be an event binding that triggers get_entry()
tk.Button(root, text='Confirm', command=get_entry).pack()
# This would be the 'timeout'
root.after(300000, get_entry)
root.mainloop()
return sms_code

Tkinter <Return> event binding is not working in python script

Thank you for your time to answer the question. I am a beginner at scripting so I am not too familiar with GUI principles.
I am writing a script involving tkinter to search and print information from wolframalpha and wikipedia based on user's text input and hitting the key.
class citrus(tkinter.Tk):
def __init__(self, master):
tkinter .Tk.__init__(self, master)
self.master = master
self.initialize()
def initialize(self):
self.grid()
self.entry = tkinter.Entry(self)
self.entry.bind = ("<Return>", self.OnEnter)
self.entry.grid(column=0, row=0)
self.grid_columnconfigure(0, weight=1)
self.resizable(True, True)
def OnEnter(self, event):
input = self.entryVariable.get()
input = input.lower
try:
appID = "ER92YJ-GAXAJEPXEK"
client = wolf.Client(appID)
res = client.query(input)
answer = next(res.results).text
print(answer)
except:
wikipedia.set_lang("en")
print(wikipedia.summary(input, sentences=3))
if __name__ == "__main__":
app = citrus(None)
app.title("citrus")
app.mainloop()
For some reason the GUI window that shows up would not do anything after a user input texts into the text box and hits the key whereas it's supposed to give information gathered from wolframalpha or wikipedia.
I would be grateful for explanations of the reason why the script failed.
You are redefining bind rather than calling it.
The problem is the = in this line of code:
self.entry.bind = ("<Return>", self.OnEnter)
The code should be this instead:
self.entry.bind("<Return>", self.OnEnter)

Passing OptionMenu into a callback (or retrieving a reference to the used widget)

I'm working on a (toplevel in a) GUI that consists of an array of 8 OptionMenus, each of them containing the same option list. Currently, Im building these widgets using a for-loop, and I save references in a dictionary. All OptionMenus link to the same (lambda) callback function.
To stay practical: the items in the option list represent a sequence of processing steps, and the user can alter the order of processes.
A change in one of the lists will result in one process being executed twice, and one process not at all. However, I want each item to occur only once. Hence, each user input should be accompanied by a second OptionMenu alteration.
For example: initial order 1-2-3 --> user changes the second process: 1-3-3, which autocorrects to: 1-3-2, where each process is again executed only once.
To my understanding, I can only get this to work if I have a reference to the OptionMenu that was just altered (from within the callback function). I was looking into passing the widget into the callback. The sample code is an attempt to implement the second suggested method, but the result is not what I would have expected.
The thing is that the OptionMenu widget seems to behave somewhat differently from other widgets. The OptionMenu does not allow for a re-defintion of the command function. No matter what input I pass along with the command function, the callback only seems to retrieve the OptionMenu selection, which is insufficient information for me to determine my process order.
Suggestions would be much apreciated!
import tkinter as tk
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.grid(row=0, column=0, sticky=tk.N+tk.S+tk.E+tk.W)
self.create_widgets()
def create_widgets(self):
self.active_procs = ['proc 1','proc 2','proc 3','proc 4',
'proc 5','proc 6','proc 7','proc 8']
itemnr, widgets = dict(), dict()
for index in range(8):
name_construct = 'nr' + str(index)
itemnr[name_construct] = tk.StringVar(root)
itemnr[name_construct].set(self.active_procs[index])
widgets[name_construct] = tk.OptionMenu(self, itemnr[name_construct], *self.active_procs,
command=lambda widget=name_construct:
self.order_change(widget))
widgets[name_construct].grid(row=index+2, column=2, columnspan=2,
sticky="nwse", padx=10, pady=10)
def order_change(self,widget):
print(widget)
root = tk.Tk()
root.title("OptionMenu test")
app = Application(master=root)
root.mainloop()
The OptionMenu will pass the new value to the callback, so you don't have to do anything to get the new value. That's why your widget value isn't the value of name_construct -- the value that is passed in is overwriting the default value that you're supplying in the lambda.
To remedy this you simply need to add another argument so that you can pass the value of name_construct to the callback to go along with the value which is automatically sent.
It would look something like this:
widgets[name_construct] = tk.OptionMenu(..., command=lambda value, widget=name_construct: self.order_change(value, widget))
...
def order_change(self, value, widget):
print(value, widget)
Note: the OptionMenu isn't actually a tkinter widget. It's just a convenience function that creates a standard Menubutton with an associated Menu. It then creates one item on the menu for each option, and ties it all together with a StringVar.
You can get the exact same behavior yourself fairly easily. Doing so would make it possible to change what each item in the menu does when selected.
For those interested, below you can find an example code of how I got the widget behaviour I wanted. I took Bryan's advice to replace the OptionMenu for a Menubutton/Menu combination. I also made use of this post to find duplicate entries in my process order list.
Any thoughts or suggestions on how to implement this in a cleaner or shorter way, or how to get the same functionality with a different interface (e.g. drag and drop), are ofcourse welcome!
import tkinter as tk
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.grid(row=0, column=0, sticky=tk.N+tk.S+tk.E+tk.W)
self.create_widgets()
def create_widgets(self):
# Assisting text
l1 = tk.Label(self, text = "Data in", font=(None, 15))
l1.grid(row=0, column=2)
l2 = tk.Label(self, text = u'\N{BLACK DOWN-POINTING TRIANGLE}', font=(None, 15))
l2.grid(row=1, column=2)
l3 = tk.Label(self, text = "Data out", font=(None, 15))
l3.grid(row=11, column=2)
l4 = tk.Label(self, text = u'\N{BLACK DOWN-POINTING TRIANGLE}', font=(None, 15))
l4.grid(row=10, column=2)
# Process list
self.active_procs = ['proc a','proc b','proc c','proc d',
'proc e','proc f','proc g','proc h']
self.the_value, self.widgets, self.topmenu = dict(), dict(), dict()
for index in range(8):
name_construct = 'nr' + str(index)
self.the_value[name_construct] = tk.StringVar(root)
self.the_value[name_construct].set(self.active_procs[index])
self.widgets[name_construct] = tk.Menubutton(self, textvariable=
self.the_value[name_construct],
indicatoron=True)
self.topmenu[name_construct] = tk.Menu(self.widgets[name_construct],
tearoff=False)
self.widgets[name_construct].configure(menu=self.topmenu[name_construct])
for proc in self.active_procs:
self.topmenu[name_construct].add_radiobutton(label=proc, variable=
self.the_value[name_construct],
command=lambda proc=proc,
widget=name_construct:
self.order_change(proc,widget))
self.widgets[name_construct].grid(row=index+2, column=2, columnspan=2,
sticky="nwse", padx=10, pady=10)
def order_change(self,proc,widget):
# Get the index of the last changed Menubutton
index_user_change = list(self.widgets.keys()).index(widget)
procs_order = [] # Current order from widgets
for index in range(8):
name_construct = 'nr' + str(index)
procs_order.append(self.widgets[name_construct].cget("text"))
# 1 change may lead to 1 double and 1 missing process
doubles = self.list_duplicates_of(procs_order,proc)
if len(doubles) == 2: # If double processes are present...
doubles.remove(index_user_change) # ...remove user input, change the other
missing_proc = str(set(self.active_procs)^set(procs_order)).strip('{"\'}')
index_change_along = int(doubles[0])
# Update references
self.active_procs[index_user_change] = proc
self.active_procs[index_change_along] = missing_proc
# Update widgets
name_c2 = 'nr'+str(index_change_along)
self.the_value[name_c2].set(self.active_procs[index_change_along])
self.widgets[name_c2].configure(text=missing_proc)
def list_duplicates_of(self,seq,item):
start_at = -1
locs = []
while True:
try:
loc = seq.index(item,start_at+1)
except ValueError:
break
else:
locs.append(loc)
start_at = loc
return locs
root = tk.Tk()
root.title("OptionMenu test")
app = Application(master=root)
root.mainloop()

Resources