Problem with Python Tkinter Button command - python

I wrote a login/sign up system in python using Tkinter. The code is something like:
class Sign_Up:
def __init__(self, root):
self.root = root
root.geometry('500x500')
self.name = StringVar()
...
label_0 = Label(root, ...)
entry_0 = entry(root, text = name)
...
self.b = Button(root, command = flag, ...)
self.mainloop()
def flag(self):
name1 = self.name.get()
...
Flag function checks whether the username is available or passwords match and shows relative messages from tkinter.mesagebox.
Everything works fine and desired when I call the function below:
def signup():
root = Tk()
s = Sign_Up(root)
signup()
However, when I write another class Menu which is a class for a window that has 2 buttons: Sign up and Sign in and pass this function to its button command, it does not work:
class Menu:
def __init__(self, root):
self.root = root
...
self b1 = (root, command = signup, ...)
root.mainloop()
def signup(self):
root = Tk()
s = Sign_Up(root)
Sign up function does not work with command and I assume that the problem is about get function in the flag function above because every time it shows a warning 'fill in the blanks' which is supposed to be displayed when the length of the entries is 0.
As I said, flag function and sign up class works properly independently, but it does not work when I pass it to tkinter button command. How can I fix this?

I solved this problem, Tk() only has to be used for the first window, for the next ones, Toplevel() should be used.

Related

Submit button not showing in second module in Tkinter

I have two modules file1.py and file2.py. In file1.py i have created a class and a function with label and entry widgets. In file2.py, i inherit the class of file1.py and create a submit button in a function. So, when i click the submit button, the value entered in entry widget in file1.py should be displayed. But what i observe is, submit button is not dislayed and when i close the window, the entered value is displayed. I'm unable to understand this behavior, can anyone correct my mistake.
file1.py
from Tkinter import *
top = Tk()
class TestClass(object):
def __init__(self, master = None):
self.frame = Frame(master)
self.frame.pack()
self.func()
def func(self):
self.label = Label(self.frame, text = "LabelName")
self.label.pack()
self.x = StringVar()
self.entry = Entry(self.frame, textvariable=self.x)
self.entry.pack()
app = TestClass(master = top)
top.minsize(400, 400)
top.mainloop()
file2.py
from file1 import *
class ImportClass(TestClass):
def __init__(self):
super(ImportClass,self).__init__(master=None)
self.imp_func()
def imp_func(self):
def get_func():
print app.x.get()
self.s = Button(self.frame, text="Submit", command=get_func())
self.s.pack()
Im = ImportClass()
I see your problem, to get this to work you have to fix a few things:
First, you need to use your imported class as app, which has the submit button, to be still able to run just file1 you can check in file1.py the __name__ whether it's '__main__' like:
if __name__ == '__main__':
app = TestClass(master = top)
top.minsize(400, 400)
top.mainloop()
Secondly, your function is not called because you call the function and give the result to Button, here you should just pass the function without calling it:
self.s = Button(self.frame, text="Submit", command=get_func())
in the function itself you should not use a global variable like app, because for example if you have multiple instances of the same class they would all depend on one instance and in the TestClass you have set self.x which is also accessible in ImportClass so you should replace the print statement with print self.x.get() instead of print app.x.get() to set the master from ImportClass to top. I also added *args and **kwargs to be passed on in the __init__ method so all in all you get:
file1.py
from Tkinter import *
class TestClass(object):
def __init__(self, master = None):
self.frame = Frame(master)
self.frame.pack()
self.func()
def func(self):
self.label = Label(self.frame, text = "LabelName")
self.label.pack()
self.x = StringVar()
self.entry = Entry(self.frame, textvariable=self.x)
self.entry.pack()
if __name__ == '__main__':
#just run if the file is called as main
top = Tk()
app = TestClass(master = top)
top.minsize(400, 400)
top.mainloop()
and file2.py
from file1 import *
from Tkinter import *
class ImportClass(TestClass):
def __init__(self, *args, **kwargs):
#passing all args and kwargs to super
super(ImportClass,self).__init__(*args, **kwargs)
self.imp_func()
def imp_func(self):
def get_func():
print self.x.get()
#using class property instead of global
self.s = Button(self.frame, text="Submit", command=get_func)
#pass function not it's return value which has been None
self.s.pack()
if __name__ == '__main__':
top = Tk()
app = ImportClass(master = top)
#using the ImportClass to display the window
top.minsize(400, 400)
top.mainloop()
so this should work. Hopefully, this helps you to prevent further problems like this.
The main reason you're having trouble is that no lines will run after mainloop until the Tk instance is closed or an event happened. When you import file1, mainloop is eventually run and then the GUI is waited to be closed in order to first define the ImportClass and then later to initialize an object for it.
Simply remove:
top.mainloop()
from file1 and add:
top.mainloop()
to file2 as the last line.
After which there's another issue, command option of a button expects a reference to a callable object, as opposed to an actual call. Replace:
self.s = Button(..., command=get_func())
with:
self.s = Button(..., command=get_func)
Also, note that I think your imports are in reverse order, obtain GUI objects by the module that has the Tk instance as opposed to vice-versa.

Allow user to change default text in tkinter entry widget.

I'm writing a python script that requires the user to enter the name of a folder. For most cases, the default will suffice, but I want an entry box to appear that allows the user to over-ride the default. Here's what I have:
from Tkinter import *
import time
def main():
#some stuff
def getFolderName():
master = Tk()
folderName = Entry(master)
folderName.pack()
folderName.insert(END, 'dat' + time.strftime('%m%d%Y'))
folderName.focus_set()
createDirectoryName = folderName.get()
def callback():
global createDirectoryName
createDirectoryName = folderName.get()
return
b = Button(master, text="OK and Close", width=10, command=callback)
b.pack()
mainloop()
return createDirectoryName
getFolderName()
#other stuff happens....
return
if __name__ == '__main__':
main()
I know next to nothing about tkInter and have 2 questions.
Is over-riding the default entry using global createDirectoryName within the callback function the best way to do this?
How can I make the button close the window when you press it.
I've tried
def callback():
global createDirectoryName
createDirectoryName = folderName.get()
master.destroy
but that simply destroys the window upon running the script.
I don't know how experienced are you in Tkinter, but I suggest you use classes.
try:
from tkinter import * #3.x
except:
from Tkinter import * #2.x
class anynamehere(Tk): #you can make the class inherit from Tk directly,
def __init__(self): #__init__ is a special methoed that gets called anytime the class does
Tk.__init__(self) #it has to be called __init__
#further code here e.g.
self.frame = Frame()
self.frame.pack()
self.makeUI()
self.number = 0 # this will work in the class anywhere so you don't need global all the time
def makeUI(self):
#code to make the UI
self.number = 1 # no need for global
#answer to question No.2
Button(frame, command = self.destroy).pack()
anyname = anynamehere() #remember it alredy has Tk
anyname.mainloop()
Also why do you want to override the deafult Entry behavior ?
The solution would be to make another button and bind a command to it like this
self.enteredtext = StringVar()
self.entry = Entry(frame, textvariable = self.enteredtext)
self.entry.pack()
self.button = Button(frame, text = "Submit", command = self.getfolder, #someother options, check tkitner documentation for full list)
self.button.pack()
def getfolder(self): #make the UI in one method, command in other I suggest
text = self.enteredtext.get()
#text now has whats been entered to the entry, do what you need to with it

Communicate classes via tkinter's bind method

I'm developing a package with GUI using tkinter. Now there is a problem when communicating classes via tkinter's bind method. A simple code which represents what I want to do is listed below:
import Tkinter as tk
lists = [1,2,3,4,5,6,7]
class selects():
def __init__(self,root):
self.root = root
self.selectwin()
def selectwin(self):
""" listbox and scrollbar for selection """
sb = tk.Scrollbar(self.root)
lb = tk.Listbox(self.root, relief ='sunken', cursor='hand2')
sb.config(command=lb.yview)
sb.pack(side=tk.RIGHT, fill=tk.Y)
lb.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
lb.config(yscrollcommand=sb.set, selectmode='single')
for value in lists: lb.insert(tk.END,value)
lb.bind('<Double-1>',lambda event: self.getvalue())
self.listbox = lb
def getvalue(self):
""" get the selected value """
value = self.listbox.curselection()
if value:
self.root.quit()
text = self.listbox.get(value)
self.selectvalue = int(text)
def returnvalue(self):
return self.selectvalue
class do():
def __init__(self):
root = tk.Tk()
sl = selects(root)
# do something... for example, get the value and print value+2, as coded below
value = sl.returnvalue()
print value+2
root.mainloop()
if __name__ == '__main__':
do()
class selects adopt Listbox widget to select a value in lists and return the selected value for use via attribute returnvalue. However, error is raised when running the above codes:
Traceback (most recent call last):
File "F:\Analysis\Python\fpgui\v2\test2.py", line 47, in <module>
do()
File "F:\Analysis\Python\fpgui\v2\test2.py", line 41, in __init__
value = sl.returnvalue()
File "F:\Analysis\Python\fpgui\v2\test2.py", line 32, in returnvalue
return self.selectvalue
AttributeError: selects instance has no attribute 'selectvalue'
I think this error can be solved by combining classes selects and do together as a single class. But in my package, class selects will be called by several classes, so it is better to make selects as a standalone class. Further, communications between classes like this will be frequently applied in my package. For example, do something after picking some information in matplotlib figure using pick_event, or update a list in one class after inputting texts in another class using Entry widget. So, any suggestion about this? Thanks in advance.
You're calling sl.returnvalue() right after having created sl. However, at this point sl.getvalue() has never been called, which means that sl.selectvalue does not yet exist.
If I understand what you want to do correctly, you should move the call to root.mainloop() to right after the creation of sl (sl = selects(root)). This way, Tk hits the mainloop, which runs until the window is destroyed, which is when the user double-clicks one of the values. Then, sl.getvalue() has been run and the program can continue with calling sl.returnvalue() without errors.
Since you are not actually calling the mainloop in that part of the code, I've altered your code to reflect that and still work as you want it to. A key method in this is wait_window, which halts execution in a local event loop until the window is destroyed. I've used this effbot page on Dialog Windows for reference:
import Tkinter as tk
lists = [1,2,3,4,5,6,7]
class selects():
def __init__(self,root):
self.root = root
self.selectwin()
def selectwin(self):
""" listbox and scrollbar for selection """
sb = tk.Scrollbar(self.root)
lb = tk.Listbox(self.root, relief ='sunken', cursor='hand2')
sb.config(command=lb.yview)
sb.pack(side=tk.RIGHT, fill=tk.Y)
lb.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
lb.config(yscrollcommand=sb.set, selectmode='single')
for value in lists: lb.insert(tk.END,value)
lb.bind('<Double-1>',lambda event: self.getvalue())
self.listbox = lb
def getvalue(self):
""" get the selected value """
value = self.listbox.curselection()
if value:
self.root.quit()
text = self.listbox.get(value)
self.selectvalue = int(text)
self.root.destroy() # destroy the Toplevel window without needing the Tk mainloop
def returnvalue(self):
return self.selectvalue
class do():
def __init__(self, master):
self.top = tk.Toplevel()
self.top.transient(master) # Make Toplevel a subwindow ow the root window
self.top.grab_set() # Make user only able to interacte with the Toplevel as long as its opened
self.sl = selects(self.top)
self.top.protocol("WM_DELETE_WINDOW", self.sl.getvalue) # use the if value: in getvalue to force selection
master.wait_window(self.top) # Wait until the Toplevel closes before continuing
# do something... for example, get the value and print value+2, as coded below
value = self.sl.returnvalue()
print value+2
if __name__ == '__main__':
root = tk.Tk()
d = do(root)
root.mainloop()

Hiding Tkinter root window while showing modal window

I have a TKinter-based application that attempts to manage settings for a game. Users may have multiple versions of this game installed, and in that case, I need to ask the user which installation they want to manage on startup. That part works well enough on its own; the selection dialog pops up after the main window is constructed, and runs modally.
However, due to differences between game versions, it would be useful if I could adapt the interface slightly for those cases. That, however, means I can't really build the main window until I know which installation I'm working on, so it's going to be blank until the user makes a choice.
I would like to hide the root window while this dialog is being shown, but calling withdraw on the root window simply causes the modal dialog to not be shown either - Python just ends up hanging with no CPU usage, and I can't figure out how to get around the problem without having to resort to a non-modal window (and a significantly different control flow, which I'd like to avoid).
Sample code exhibiting the problem and general code structure (Python 2.7):
from Tkinter import *
from ttk import *
class TkGui(object):
def __init__(self):
self.root = root = Tk()
self.root.withdraw()
selector = FolderSelection(self.root, ('foo', 'bar'))
self.root.deiconify()
print(selector.result)
class ChildWindow(object): #Base class
def __init__(self, parent, title):
top = self.top = Toplevel(parent)
self.parent = parent
top.title(title)
f = Frame(top)
self.create_controls(f)
f.pack(fill=BOTH, expand=Y)
def create_controls(self, container):
pass
def make_modal(self, on_cancel):
self.top.transient(self.parent)
self.top.wait_visibility() # Python will hang here...
self.top.grab_set()
self.top.focus_set()
self.top.protocol("WM_DELETE_WINDOW", on_cancel)
self.top.wait_window(self.top) # ...or here, if wait_visibility is removed
class FolderSelection(ChildWindow):
def __init__(self, parent, folders):
self.parent = parent
self.listvar = Variable(parent)
self.folderlist = None
super(FolderSelection, self).__init__(parent, 'Select folder')
self.result = ''
self.listvar.set(folders)
self.make_modal(self.cancel)
def create_controls(self, container):
f = Frame(container)
Label(
f, text='Please select the folder '
'you would like to use.').grid(column=0, row=0)
self.folderlist = Listbox(
f, listvariable=self.listvar, activestyle='dotbox')
self.folderlist.grid(column=0, row=1, sticky="nsew")
Button(
f, text='OK', command=self.ok
).grid(column=0, row=2, sticky="s")
self.folderlist.bind("<Double-1>", lambda e: self.ok())
f.pack(fill=BOTH, expand=Y)
def ok(self):
if len(self.folderlist.curselection()) != 0:
self.result = self.folderlist.get(self.folderlist.curselection()[0])
self.top.protocol('WM_DELETE_WINDOW', None)
self.top.destroy()
def cancel(self):
self.top.destroy()
TkGui()
It seems that in your case there is no difference, modal window or not. In fact, you just need to use the wait_window() method. According the docs:
In many situations, it is more practical to handle dialogs in a
synchronous fashion; create the dialog, display it, wait for the user
to close the dialog, and then resume execution of your application.
The wait_window method is exactly what we need; it enters a local
event loop, and doesn’t return until the given window is destroyed
(either via the destroy method, or explicitly via the window manager).
Consider follow example with nonmodal window:
from Tkinter import *
root = Tk()
def go():
wdw = Toplevel()
wdw.geometry('+400+400')
e = Entry(wdw)
e.pack()
e.focus_set()
#wdw.transient(root)
#wdw.grab_set()
root.wait_window(wdw)
print 'done!'
Button(root, text='Go', command=go).pack()
Button(root, text='Quit', command=root.destroy).pack()
root.mainloop()
When you click Go button, nonmodal dialog will appear, but code wil stop execution and the string done! will be displayed only after you close the dialog window.
If this is the behavior that you want, then here is your example in a modified form (I modified __init__ in TkGui and make_modal method, also added mainloop()):
from Tkinter import *
from ttk import *
class TkGui(object):
def __init__(self):
self.root = root = Tk()
self.root.withdraw()
selector = FolderSelection(self.root, ('foo', 'bar'))
self.root.deiconify()
print(selector.result)
class ChildWindow(object): #Base class
def __init__(self, parent, title):
top = self.top = Toplevel(parent)
self.parent = parent
top.title(title)
f = Frame(top)
self.create_controls(f)
f.pack(fill=BOTH, expand=Y)
def create_controls(self, container):
pass
def make_modal(self, on_cancel):
#self.top.transient(self.parent)
#self.top.wait_visibility() # Python will hang here...
#self.top.grab_set()
self.top.focus_set()
self.top.protocol("WM_DELETE_WINDOW", on_cancel)
self.top.wait_window(self.top) # ...or here, if wait_visibility is removed
class FolderSelection(ChildWindow):
def __init__(self, parent, folders):
self.parent = parent
self.listvar = Variable(parent)
self.folderlist = None
super(FolderSelection, self).__init__(parent, 'Select folder')
self.result = ''
self.listvar.set(folders)
self.make_modal(self.cancel)
def create_controls(self, container):
f = Frame(container)
Label(
f, text='Please select the folder '
'you would like to use.').grid(column=0, row=0)
self.folderlist = Listbox(
f, listvariable=self.listvar, activestyle='dotbox')
self.folderlist.grid(column=0, row=1, sticky="nsew")
Button(
f, text='OK', command=self.ok
).grid(column=0, row=2, sticky="s")
self.folderlist.bind("<Double-1>", lambda e: self.ok())
f.pack(fill=BOTH, expand=Y)
def ok(self):
if len(self.folderlist.curselection()) != 0:
self.result = self.folderlist.get(self.folderlist.curselection()[0])
self.top.protocol('WM_DELETE_WINDOW', None)
self.top.destroy()
def cancel(self):
self.top.destroy()
TkGui()
mainloop()
The code stops on line selector = FolderSelection(self.root, ('foo', 'bar')) and then continue after you close the dialog.

Creating a popup message box with an Entry field

I want to create a popup message box which prompts user to enter an input. I have this method inside a class. I am basing my code on this guide by java2s.
class MyDialog:
def __init__(self, parent):
top = self.top = Toplevel(parent)
Label(top, text="Value").pack()
self.e = Entry(top)
self.e.pack(padx=5)
b = Button(top, text="OK", command=self.ok)
b.pack(pady=5)
def ok(self):
print "value is", self.e.get()
self.top.destroy()
root = Tk()
d = MyDialog(root)
root.wait_window(d.top)
But in this, top = self.top = Toplevel(parent) doesn't work for me.
I have a mockup of what I am trying to accomplish.
My program structure looks something like this:
class MainUI:
def__int__(self):
...
self.initUI()
def initUI(self):
.......
Popup = Button(self, text="Enter Value", command=self.showPopup)
def showPopup(self):
#create the popup with an Entry here
How can I create a message box in Python which accepts user input?
I'm a little confused about your two different blocks of code. Just addressing the first block of code, nothing happens because you never enter the mainloop. To do that, you need to call root.mainloop(). The typical way of doing this is to add a button to root widget and bind a callback function to the Button (which includes d=MyDialog() and root.wait_window(d.top))
Here's some basic code which I hope does what you want ...
from Tkinter import *
import sys
class popupWindow(object):
def __init__(self,master):
top=self.top=Toplevel(master)
self.l=Label(top,text="Hello World")
self.l.pack()
self.e=Entry(top)
self.e.pack()
self.b=Button(top,text='Ok',command=self.cleanup)
self.b.pack()
def cleanup(self):
self.value=self.e.get()
self.top.destroy()
class mainWindow(object):
def __init__(self,master):
self.master=master
self.b=Button(master,text="click me!",command=self.popup)
self.b.pack()
self.b2=Button(master,text="print value",command=lambda: sys.stdout.write(self.entryValue()+'\n'))
self.b2.pack()
def popup(self):
self.w=popupWindow(self.master)
self.b["state"] = "disabled"
self.master.wait_window(self.w.top)
self.b["state"] = "normal"
def entryValue(self):
return self.w.value
if __name__ == "__main__":
root=Tk()
m=mainWindow(root)
root.mainloop()
I get the value from the popupWindow and use it in the main program (take a look at the lambda function associated with b2).
Main window:
"Click me" window:
Main window while "click me" is open:
import tkinter as tk
from tkinter import simpledialog
ROOT = tk.Tk()
ROOT.withdraw()
# the input dialog
USER_INP = simpledialog.askstring(title="Test",
prompt="What's your Name?:")
# check it out
print("Hello", USER_INP)
Enjoy ...
I did it in Tkinter without any classes. I created a function that starts a new window.
popup.Tk()
popup.mainloop()
In that window there is an Entry field from where I get the text with a variable which value is: entry.get()
Then you can use that variable for whatever you need and it will take the text from that Entry field.
I just tried this:
def get_me():
s = simpledialog.askstring("input string", "please input your added text")
Source: https://www.youtube.com/watch?v=43vzP1FyAF8

Categories

Resources