I'm trying to make my Tkinter Label widget update but, where I thought it was straightforward, now I can't sort it out.
My code is:
import Tkinter as tk
import json, htmllib, formatter, urllib2
from http_dict import http_status_dict
from urllib2 import *
from contextlib import closing
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.grid()
self.createWidgets()
def createWidgets(self):
StatusTextVar = tk.StringVar()
self.EntryText = tk.Entry(self)
self.GetButton = tk.Button(self, command=self.GetURL)
self.StatusLabel = tk.Label(self, textvariable=StatusTextVar)
self.EntryText.grid(row=0, column=0)
self.GetButton.grid(row=0, column=1, sticky=tk.E)
self.StatusLabel.grid(row=1, column=0, sticky=tk.W)
def GetURL(self):
try:
self.url_target = ("http://www." + self.EntryText.get())
self.req = urllib2.urlopen(self.url_target)
StatusTextVar = "Success"
except:
self.StatusTextVar = "Wrong input. Retry"
pass
app = Application()
app.mainloop()
I've tried several ways but either the Label won't update, or the interpreter raises errors.
Note: In the excerpt I deleted as much as code as possible to avoid confusion.
You need to use the StringVar set method to change the label text. Also:
StatusTextVar = "Success"
is not referencing self and will not change any state.
You should first change all StatusTextVar to self.StatusTextVar and then update the set calls:
self.StatusTextVar = "Success"
self.StatusTextVar = "Wrong input. Retry"
to
self.StatusTextVar.set("Success")
self.StatusTextVar.set("Wrong input. Retry")
Updating all StatusTextVar instances and using the set method, I get:
import Tkinter as tk
import json, htmllib, formatter, urllib2
from urllib2 import *
from contextlib import closing
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.grid()
self.createWidgets()
def createWidgets(self):
self.StatusTextVar = tk.StringVar()
self.EntryText = tk.Entry(self)
self.GetButton = tk.Button(self, command=self.GetURL)
self.StatusLabel = tk.Label(self, textvariable=self.StatusTextVar)
self.EntryText.grid(row=0, column=0)
self.GetButton.grid(row=0, column=1, sticky=tk.E)
self.StatusLabel.grid(row=1, column=0, sticky=tk.W)
def GetURL(self):
try:
self.url_target = ("http://www." + self.EntryText.get())
self.req = urllib2.urlopen(self.url_target)
self.StatusTextVar.set("Success")
except:
self.StatusTextVar.set("Wrong input. Retry")
pass
root = tk.Tk()
app = Application(master=root)
app.mainloop()
It works as one would expect.
Related
I am working with tkinter's class-based structure and I need to get a value of text input. How do I do that?
Here is the relevant code:
#GUI
class PseudostreamWidget(tk.Frame):
def __init__(self, master=None, **kw):
tk.Frame.__init__(self, master, **kw)
self.targetIP = tk.Entry(self.Ipconfig)
self.targetIP.configure(background='black', font='{Lato} 12 {}', foreground='white', highlightbackground='black')
self.targetIP.configure(highlightcolor='black', justify='left', relief='raised')
_text_ = '''Friend's Public IP Here'''
self.targetIP.delete('0', 'end')
self.targetIP.insert('0', _text_)
self.targetIP.grid(column='1', row='0')
#
#
#
target_ip = self.targetIP
#
#
How do I get it to print target_ip outside the class?
if __name__ == '__main__':
root = tk.Tk()
widget = PseudostreamWidget(root)
widget.pack(expand=True, fill='both')
root.mainloop()
print(target_ip)
Try this:
import tkinter as tk
class PseudostreamWidget(tk.Frame):
def __init__(self, master=None, callback=None, **kwargs):
super().__init__(master, **kwargs)
self.targetIP = tk.Entry(self, fg="white", justify="left",
relief="raised", highlightbackground="black",
highlightcolor="black", bg="black",
insertbackground="white", font=("Lato", 12))
# self.targetIP.delete(0, "end") # No need for this
self.targetIP.insert(0, "Friend's Public IP Here")
self.targetIP.grid(column=1, row=0)
self.targetIP.bind("<Return>", callback)
def submit_data(event=None):
print("You typed in:", widget.targetIP.get())
root = tk.Tk()
widget = PseudostreamWidget(root, callback=submit_data)
widget.pack()
root.mainloop()
To use it just write something inside the entry and press the Enter key on your keyboard.
It passes in a function (submit_data) and binds to it.
NameError: name 'onOpen' is not defined
There is something wrong with the command function. I am not sure what I did wrong here. I had the code tested before the onOpen function and it works fine.
import tkinter as tk
from tkinter import filedialog
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.pack()
self.createWidgets()
def onOpen():
""" Ask the user to choose a file and update the values of label change button"""
button_label.set(filedialog.askopenfilename(filetypes = ()))
def createWidgets(self):
#instruction label
self.labelInstruct = tk.Label(self, text="Instructions:", padx=10, pady=10)
self.labelInstruct = tk.Label(self, text="All you have to do is insert the file and save it.\n The conversion is instant", padx=10, pady=10)
self.labelInstruct.pack()
#insertfile button
self.ifbut = tk.Button(self, text="Insert File", command=onOpen)
self.ifbut.pack()
button_label = tk.StringVar(self)
text = tk.Label(self, textvariable = button_label)
text.pack()
#save button
self.saveLabel = tk.Label(self, text="Save File", padx=10, pady=10)
self.saveLabel.pack()
self.saveButton = tk.Button(self)
self.saveButton.pack()
#quit button
self.quitButton = tk.Button(self, text='Quit',
command=self.quit)
self.quitButton.pack()
app = Application()
app.master.title('Sample application')
app.mainloop()
Seeing that there are many problems with the way this code is written I am only going to point out a few of them and tackle the main question from the OP.
Lets start with the fact that you need to define the main window with something like root = tk.Tk() and you also need to make sure all your methods in your class have the argument self as the first argument.
Also any variable you are defining also should have self so that the class can interact with it. This (button_label) for example should be self.button_label.
There is no reason to use return the way you are trying to in the onOpen(self): method. Return does not work like that. Return is there so you can return a value to something that is calling the function/method to be used for something else and is not for setting the value of something.
Note that I also add the root window variable to the app = Application(root) line. This lets us pass the main window into the class.
all and all the following should work for the onOpen(self): function but the rest still needs some work.
import tkinter as tk
from tkinter import filedialog
class Application(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.pack()
self.createWidgets()
def onOpen(self):
""" Ask the user to choose a file and update the values of label change button"""
self.button_label.set(filedialog.askopenfilename(filetypes = ()))
def createWidgets(self):
#instruction label
self.labelInstruct = tk.Label(self.parent, text="Instructions:", padx=10, pady=10)
self.labelInstruct = tk.Label(self.parent, text="All you have to do is insert the file and save it.\n The conversion is instant", padx=10, pady=10)
self.labelInstruct.pack()
#insert file button
self.ifbut = tk.Button(self.parent, text="Insert File", command = self.onOpen)
self.ifbut.pack()
self.button_label = tk.StringVar()
self.text = tk.Label(self.parent, textvariable = self.button_label)
self.text.pack()
#save button
self.saveLabel = tk.Label(self.parent, text="Save File", padx=10, pady=10)
self.saveLabel.pack()
self.saveButton = tk.Button(self.parent, text = "Save")
self.saveButton.pack()
#quit button
self.quitButton = tk.Button(self.parent, text='Quit', command=self.quit)
self.quitButton.pack()
root = tk.Tk()
app = Application(root)
app.master.title('Sample application')
app.mainloop()
You need to return the function value as below:
def onOpen():
""" Ask the user to choose a file and update the values of label change button"""
return button_label.set(filedialog.askopenfilename(filetypes = ()))
I have the following code. This creates a dialog box just fine.
import tkinter as tk
import tkinter.ttk as ttk
class Window(ttk.Frame):
def __init__(self, master=None):
super().__init__(master, padding=2) # Creates self.master
helloLabel = ttk.Label(self, text="Hello Tkinter!")
quitButton = ttk.Button(self, text="Quit", command=self.quit)
helloLabel.pack()
quitButton.pack()
self.pack()
#self.create_widgets()
#self.create_layout()
# def create_widgets(self):
# pass
# def create_layout(self):
# pass
application = tk.Tk()
application.title("Window")
Window(application)
application.mainloop()
How come when I uncomment the following two lines in the above code the code breaks?
def create_widgets(self):
pass
I'm using python 3.4 32 bit on Windows 7 64-bit.
I want to create a custom widget in tkinter such that when instantiated, displays a label and an entry box. Example I created a class named entry and call as.. entry ('name', master ) and this would display a label with text as main along side an entry box.
I have succeeded in doing that but my problem is with the geometry managers. they all seem to mess up everything
Your widget should subclass Frame. Within the frame you can use any geometry manager you want without affecting any other code. It's important that the widget class does not call grid, pack or place on itself -- that's the job of the function that creates the widget. Every widget, or function that creates a widget, should only ever worry about laying out its children.
Here's an example that creates a couple of different custom widgets. Each uses a different geometry manager to illustrate that they don't interfere with each other:
try:
# python 3.x
import tkinter as tk
except ImportError:
# python 2.x
import Tkinter as tk
class CustomWidget(tk.Frame):
def __init__(self, parent, label, default=""):
tk.Frame.__init__(self, parent)
self.label = tk.Label(self, text=label, anchor="w")
self.entry = tk.Entry(self)
self.entry.insert(0, default)
self.label.pack(side="top", fill="x")
self.entry.pack(side="bottom", fill="x", padx=4)
def get(self):
return self.entry.get()
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.label = tk.Label(self)
self.e1 = CustomWidget(self, "First Name:", "Inigo")
self.e2 = CustomWidget(self, "Last Name:", "Montoya")
self.submitButton = tk.Button(self, text="Submit", command=self.submit)
self.e1.grid(row=0, column=0, sticky="ew")
self.e2.grid(row=1, column=0, sticky="ew")
self.label.grid(row=2, column=0, sticky="ew")
self.submitButton.grid(row=4, column=0)
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(2, weight=1)
def submit(self):
first = self.e1.get()
last = self.e2.get()
self.label.configure(text="Hello, %s %s" % (first, last))
if __name__ == "__main__":
root = tk.Tk()
Example(root).place(x=0, y=0, relwidth=1, relheight=1)
root.mainloop()
I agree with Mr. Oakley. You should subclass frame to do your job.
The simplest way to do what you want is to create a module with the following code:
# AnnotatedEntry.py
def AnnotatedEntry(master, name="An annoted entry box"):
'''
As a little extra, name is a keyword-argument, which defaults to "An annotated
entry box."
'''
import tkinter as tk
overlord = tk.Frame(master, height=5, width=40)
labeller = tk.Label(overlord, text=name, font="Times 14 bold")
labeller.grid(sticky='new')
inputter = tk.Entry(overlord, font="Times 14 bold")
inputter.grid(sticky='sew', pady=(10,0))
return overlord
This would be used as follows:
# Main program
import tkinter
import AnnotatedEntry
root = tkinter.Tk()
hold = AnnotatedEntry.AnnotatedEntry(root, name="Hello, world!")
hold.grid()
I hereby affirm, on my Scout Honor, that this code has been fully tested, and is guaranteed to work in Python 3.7.4. That being said, there is currently no method for returning the data contained in the Entry; you will have to work that out for yourself.
Based on #Bryan Oakley answer, I do have some modification. I know it's out of topic somehow. This is how to return a value from the widget and it only allows integer up to some number of digits that the user must entered.
#create a global value
global tbVal
tbVal = 0
class CustomWidget(tk.Frame):
def __init__(self, parent, nDigits):
tk.Frame.__init__(self, parent)
self.entry = tk.Entry(self)
self.entry.pack(side="bottom", fill="x", padx=4)
self.entry.configure(validate='all',validatecommand=windows.register(self.sbValidate),'%P','%W',nDigits))
def get(self):
return self.entry.get()
def sbValidate(self, userInput, widget, nDigits):
global tbVal
tbVal = userInput
if userInput == '':
return True
if '.' in userInput or ' ' in userInput:
return False
n = len(userInput)
if n > int(nDigits):
return False
try:
val = int(float(userInput))
except ValueError:
return False
return val
class Example(tk.Frame):
def __init__(self, parent, nDigitsLimit):
tk.Frame.__init__(self, parent)
self.e1 = CustomWidget(self, nDigitsLimit)
self.e1.grid(row=0, column=0, sticky="ew")
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(2, weight=1)
def btnStartClick():
print(tbVal)
nDigitsLimit = 8
tbTest = ttk.Entry(Example(windows, nDigitsLimit).place(x=20, y=20, relwidth=0.25, relheight=0.05))
btnStart = tk.Button(frame, text='Start', command=btnStartClick)
btnStart.place(relx=0.50, rely=0.50)
I'm trying to add a "Done" button to my program that will print the content of both Entry widgets to a new box. I can get the button to appear, but I can't get the information to show up in a new box. What am I doing wrong?
from Tkinter import *
class Application(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.grid()
self._name = StringVar()
self._name.set("Enter name here")
self._age = IntVar()
self._age.set("Enter age here")
top = self.winfo_toplevel() # find top-level window
top.title("Entry Example")
self._createWidgets()
self._button = Button(self,
text = "Done")
self._button.grid(row = 1, column = 0, columnspan = 2)
def _createWidgets(self):
textEntry = Entry(self, takefocus=1,
textvariable = self._name, width = 40)
textEntry.grid(row=0, sticky=E+W)
ageEntry = Entry(self, takefocus=1,
textvariable = self._age, width = 20)
ageEntry.grid(row=1, sticky=W)
def _widget(self):
tkMessageBox.showinfo
# end class Application
def main():
Application().mainloop()
You need to assign an action to your button using command: option.
To get what is written in Entry you need to use get() method.
showinfo you need two arguments, one is the title, the other one is what is going to be shown.
Also you need to import tkMessageBox.
Here is a working example.
import Tkinter as tk
import tkMessageBox
class Example(tk.Frame):
def __init__(self,root):
tk.Frame.__init__(self, root)
self.txt = tk.Entry(root)
self.age = tk.Entry(root)
self.btn = tk.Button(root, text="Done", command=self.message)
self.txt.pack()
self.age.pack()
self.btn.pack()
def message(self):
ent1 = self.txt.get()
ent2 = self.age.get()
tkMessageBox.showinfo("Title","Name: %s \nAge: %s" %(ent1,ent2))
if __name__=="__main__":
root = tk.Tk()
root.title("Example")
example = Example(root)
example.mainloop()