I just need help in why this code does not work. I am unfamiliar with tkinter and this is the code that I was given and was told it should work. I see it says root is not defined but not sure what it should be defined as because I am assuming root is from tkinter? The only thing is that line 2 is grayed out. If you could help it would be greatly appreciated!
import tkinter as tk
from tkinter import *
class MyFirstGUI:
def __init__(self):
root.gui = tk.TK()
root.gui.title("A simple GUI")
root.gui.label = tk.Label(text="This is our first GUI!")
root.label.pack()
root.greet_button = tk.Button(text="Greet", command=self.greet)
root.greet_button.pack()
root.close_button = tk.Button(text="Close", command=self.gui.destroy)
root.close_button.pack()
def greet(root):
print("Greetings!")
root.mainloop()
my_gui = MyFirstGUI()
The identifier root is often assigned from tk.Tk(). If that's the case, then root will be the Tk application and the root of the Tk display graph.
You do all of your setup in the MyFirstGUI class's constructor, but you only use a local variable to keep track of the results. You therefore lose access to all of it when you exit the constructor.
I expect that you want to create everything as a root attribute on self so that your setup is encapsulated and preserved by the class.
Here's how I would rework your code to get it to do what I think you intend:
import tkinter as tk
from tkinter import *
class MyFirstGUI:
def __init__(self):
self.root = Tk()
self.root.title("A simple GUI")
self.root.label = tk.Label(text="This is our first GUI!")
self.root.label.pack()
self.root.greet_button = tk.Button(text="Greet", command=self.greet)
self.root.greet_button.pack()
self.root.close_button = tk.Button(text="Close", command=self.root.destroy)
self.root.close_button.pack()
def greet(root):
print("Greetings!")
my_gui = MyFirstGUI()
my_gui.root.mainloop()
Related
I am trying to create a GUI in Python 3.9.1 with tkinter and am having issues getting a default value to show in an Entry box.
from tkinter import *
from tkinter import ttk
class GUI:
def __init__(self, root, *args):
self.root = root
self.initial_frame = ttk.Frame(self.root)
self.initial_frame.grid(row=0)
ttk.Label(self.initial_frame, text="User question here?").grid(row=1, column=1)
self.ext_len = StringVar(value=4)
ext_len_entry = ttk.Entry(self.initial_frame, textvariable=self.ext_len)
ext_len_entry.grid(row=1, column=2)
root = Tk()
GUI(root)
root.mainloop()
Note: official tkinter docs say to use from tkinter import * and from tkinter import ttk.
See this link and look in the Python section in the step-by-step walkthrough https://tkdocs.com/tutorial/firstexample.html
I've also tried using self.ext_len.set(4) after initializing the entry but before putting it on the grid. I've tried self.ext_len.insert(0,4) as well.
If I add a button with a callback that does nothing, the 4 shows up in the entry box. I found that I don't even have to render it on the grid. Just initializing it is enough. A button without a callback does not work.
Working code:
from tkinter import *
from tkinter import ttk
class GUI:
def __init__(self, root, *args):
self.root = root
self.initial_frame = ttk.Frame(self.root)
self.initial_frame.grid(row=0)
ttk.Label(self.initial_frame, text="User question here?").grid(row=1, column=1)
self.ext_len = StringVar(value=4)
ext_len_entry = ttk.Entry(self.initial_frame, textvariable=self.ext_len)
ext_len_entry.grid(row=1, column=2)
ttk.Button(self.initial_frame, text="Test button", command=self.test_func)
def test_func(self, *args):
pass
root = Tk()
GUI(root)
root.mainloop()
Why does initializing the button cause it to work?
I have a python script which has an object of a class which basically opens a Tkinter window. The problem is that when I create the object, the rest of the program stops running because when I create the Tkinter object, it basically starts an infinite while loop.
The thing is that I want to change, i.e. the text in a label, but from my other class.
My two files look roughly like this:
from tkinter import *
class Panel():
def __init__(self):
self.root = Tk()
width = 300
screen_width = int(self.root.winfo_screenwidth())
screen_height = int(self.root.winfo_screenheight())
self.root.geometry(str(width)+"x50+"+str(screen_width-width)+"+0")
self.root.overrideredirect(True)
#Create Label
self.label = Label(self.root, text="Text")
self.label.pack()
self.root.mainloop()
def closePanel(self):
self.root.quit()
def editText(self,new_text):
self.label.configure(text=new_text)
And my other class:
from Panel import *
outputPanel = Panel()
outputPanel.editText("New Text")
When you create a tkinter application, your whole application should run as a tkinter application. Therefore you need to create the main window at the highest level. Everything else must run within the mainloop of the tkinter program.
In your case, the Panel module instantiates a class which instantiates a tkinter window. You must move that instantiation to the the Panel module itself. Here is my version of you modified code, just to give a basic idea:
from tkinter import *
class Panel():
def __init__(self, root): # root is passed when instantiating Panel()
self.root=root # make root an instance variable
width = 300
screen_width = int(self.root.winfo_screenwidth())
screen_height = int(self.root.winfo_screenheight())
self.root.geometry(str(width)+"x50+"+str(screen_width-width)+"+0")
self.root.overrideredirect(True)
#Create Label
self.label = Label(self.root, text="Text")
self.label.pack()
def closePanel(self):
self.root.quit()
def editText(self,new_text):
self.label.configure(text=new_text)
root = Tk() # create a window at highest level
outputPanel = Panel(root) # Panel may be imported and accessed here
outputPanel.editText("New Text")
outputPanel.editText("New Text")
root.mainloop()
There is an excellent post about how to structure a tkinter application here: Best way to structure a tkinter application?
Put outputPanel.root.mainloop() after outputPanel.editText("New Text") and remove self.root.mainloop().
I'm using Tkinter for the GUI of a little tool I wrote with Python. Basically I just want a callback-method to be executed as soon as the contents of an entry widget have changed. This can be done with Tkinter's own variable classes (StringVar, BooleanVar, etc. - see documentation for details: http://effbot.org/tkinterbook/variable.htm).
So I couldn't get the mechanism to work and I found a snippet online, where it works perfectly fine. Now I'm trying to figure out why my version does not work.
As you can see in the two code examples the only difference is, that I'm using the event-listening functionality inside a class, whereas the snippet I found online only demonstrates it in a straight top-to-bottom manner.
Here's what I've already tried:
I instantiated the Tk instance directly in the constructor of my GUI class - same behaviour.
I inherited directly from the Tk class (instead of Frame) - same behaviour.
I placed the callback outside of the class - same behaviour.
The only idea I have is that the problem might be scope related, which I tried to verify.
Working code snippet:
from tkinter import *
import tkinter as tk
def text_changed(*args):
print("Text changed.")
top = tk.Tk()
string_listener = StringVar()
string_listener.set("Init Text")
string_listener.trace("w", text_changed)
entry_widget = tk.Entry(top, textvariable = string_listener)
entry_widget.pack()
top.mainloop()
Not working code snippet
from tkinter import *
import tkinter as tk
root = tk.Tk()
class GUI(tk.Frame):
def __init__(self, master=root):
super(GUI, self).__init__(master)
string_listener = StringVar()
string_listener.set("Init Text")
string_listener.trace("w", self.text_changed_callback)
entry_widget = tk.Entry(master, textvariable=string_listener)
entry_widget.pack()
def text_changed_callback(self, *args):
print("Text changed.")
gui = GUI()
gui.mainloop()
Like in the working example, my code ought to print Text changed., everytime a character is either deleted from or appended to the string in the extry-widget.
The problem is that string_listener is a local variable, and python is destroying the variable when __init__ finishes running. This doesn't happen in your original code since the variable is created in the global scope.
A simple solution is to save a reference as an attribute of the class:
import tkinter as tk
root = tk.Tk()
class GUI(tk.Frame):
def __init__(self, master=root):
super(GUI, self).__init__(master)
self.string_listener = tk.StringVar()
self.string_listener.set("Init Text")
self.string_listener.trace("w", self.text_changed_callback)
entry_widget = tk.Entry(master, textvariable=self.string_listener)
entry_widget.pack()
def text_changed_callback(self, *args):
print("Text changed.")
gui = GUI()
gui.mainloop()
note: I also changed StringVar to tk.StringVar so that I could remove the redundant wildcard import of tkinter.
I am starting to learn Python and the tkinter package and I am writing a program to load a text file on the GUI window. To open the file browser, I installed the button and its necessary function as shown in the below code. The program runs but when I click on the "browse" button, I am getting an attribute error saying : "'assign_1' object has no attribute 'var_filename'". It would be great if anyone could help me with this.
from tkinter import *
from tkinter import messagebox
from tkinter import simpledialog
from tkinter import filedialog
from math import *
from numpy import *
import string
root = Tk()
def close_window_callback(root):
if messagebox.askokcancel("Quit", "Do you really wish to quit?"):
root.destroy()
class assign_1:
def __init__(self,master):
self.master = master
frame = Frame(master)
frame.pack()
self.canvas = Canvas(master,width=1000,height=1000, bg="yellow")
self.button_browse = Button(frame, text="Browse",
command=self.browse_file)
self.button_browse.pack()
self.button_load = Button(frame, text="Load")
self.button_load.pack(side = LEFT)
self.canvas.pack(expand=YES, fill=BOTH)
def browse_file(self):
self.var_filename.set(filedialog.askopenfilename(filetypes=[("allfiles","*"),("pythonfiles","*.txt")]))
filename = self.var_filename.get()
print(filename)
root.protocol("WM_DELETE_WINDOW", lambda root_window=root: close_window_callback(root_window))
assign_1(root)
root.mainloop()
Although, as Rinzler pointed out, your indentation is wrong in the code you posted, that would lead to another error (AttributeError: assign_1 instance has no attribute 'browse_file'). So I'm guessing the indentation in the code you actually use is correct.
The problem is that you try to use self.var_filename.set(...) without having defined what self.var_filename is. If you want it to be a StringVar, which seems to be the case since you use set and get, you have to initialize it. To do this you should put self.var_filename = StringVar(master) in the class' __init__ function. A small example demonstrating this:
root = Tk()
class assign_1:
def __init__(self, master):
self.master = master
self.var_filename = StringVar(master)
self.button_browse = Button(master, text="Browse", command=self.browse_file)
self.button_browse.pack()
def browse_file(self):
self.var_filename.set(filedialog.askopenfilename(filetypes=[("allfiles","*"),("pythonfiles","*.txt")]))
filename = self.var_filename.get()
print(filename)
assign_1(root)
root.mainloop()
However, from the looks of it, in your case there is no need to use a tkinter StringVar, just use a normal string variable:
root = Tk()
class assign_1:
def __init__(self, master):
self.master = master
self.button_browse = Button(master, text="Browse", command=self.browse_file)
self.button_browse.pack()
def browse_file(self):
self.filename = filedialog.askopenfilename(filetypes=[("allfiles","*"),("pythonfiles","*.txt")])
print(self.filename)
assign_1(root)
root.mainloop()
The indentation is wrong. The function browse_file you wanted to define as method of the class assign_1 (use capitalise letters to declare name of classes) is a global function as you defined it.
You have also not defined self.var_filename anywhere, so it will then give you the error:
AttributeError: 'assign_1' object has no attribute 'var_filename'
Under the function close_window_callback, you have also wrong indentation.
import Tkinter
class Application(Frame):
def __init__(self, master):
Frame.__init__(self,master)
self.grid()
self.CreateWidgets()
def CreateWidgets(self):
self.LoginButton = Button(Self)
self.LoginButton["text"] = "Login"
self.LoginButton.grid()
self.QUIT_Button = Button(self)
self.QUIT_Button["text"] = "Quit"
self.QUIT_Button["command"] = self.quit
self.QUIT_Button["fg"] = "red"
root = Tk()
root.title("Login")
root.geometry("500x500")
app = Application(root)
root.mainloop()
This is the youtube tutorial that I have been following: https://www.youtube.com/watch?v=YCLTv6wh3jE&index=39&list=PLB0701884E5AE1B45
And this is the error that keeps occurring:
Traceback (most recent call last):
File "C:\Users\omer\Desktop\test.py", line 3, in <module>
class Application(Frame):
NameError: name 'Frame' is not defined
I am a complete noob at Python and am still learning so any help would be appreciated.
Frame Tk, and Button are all located in the Tkinter namespace. Thus, you have to qualify them to let Python know where they are1:
import Tkinter
class Application(Tkinter.Frame):
...
Tkinter.Frame.__init__(self, master)
...
self.LoginButton = Tkinter.Button(self)
...
self.QUIT_Button = Tkinter.Button(self)
...
root = Tkinter.Tk()
That, or you could just import the names directly:
from Tkinter import Frame, Tk, Button
1If you decide to use this first solution, it would probably be best to import Tkinter like this:
import Tkinter as tk
That way, the code becomes this:
import Tkinter as tk
class Application(Tkinter.Frame):
...
tk.Frame.__init__(self, master)
...
self.LoginButton = tk.Button(self)
...
self.QUIT_Button = tk.Button(self)
...
root = tk.Tk()
which is a lot more brief.
You need to import Frame, Button, Tk.
You can either explicitly import all of them from Tkinter:
from Tkinter import Frame, Button, Tk
or import everything from Tkinter (which is not a good thing to do):
from Tkinter import *
or leave your import as is (import Tkinter) and get Frame, Button and Tk from the Tkinter namespace, e.g. for Frame:
class Application(Tkinter.Frame):
Even better would be to import tkinter in a universal way that would work for both python2 and python3:
try:
# Python2
import Tkinter as tk
except ImportError:
# Python3
import tkinter as tk
class Application(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self,master)
self.grid()
self.CreateWidgets()
def CreateWidgets(self):
self.LoginButton = tk.Button(self)
self.LoginButton["text"] = "Login"
self.LoginButton.grid()
self.QUIT_Button = tk.Button(self)
self.QUIT_Button["text"] = "Quit"
self.QUIT_Button["command"] = self.quit
self.QUIT_Button["fg"] = "red"
root = tk.Tk()
root.title("Login")
root.geometry("500x500")
app = Application(root)
root.mainloop()
Also, you have a typo, replace (watch Self):
self.LoginButton = Button(Self)
with:
self.LoginButton = Button(self)
You have to import Frame in order to use it like you are. As it stands you have imported Tkinter, but that doesn't give you access to Frame, Button or Tk the way you've used them. but you either need to do:
from Tkinter import Frame
or
from Tkinter import * (* means 'all' in this case, though this isn't necessary when only using a few modules)
or you could leave your import statement as is(import Tkinter) and change your code like so:
class Application(Tkinter.Frame):
and
self.LoginButton = Tkinter.Button(Self)
However, I would recommend that if you do this, you do:
import Tkinter as tk
That way, you can do tk.Frame and tk.Button etc.
For any modules that you want to use from Tkinter you need to import them also in the same fashion.
You can do single line imports like so:
from Tkinter import Tk, Frame, Button etc.
Check out this info on importing in Python: http://effbot.org/zone/import-confusion.htm
Well it is a bit late but for someone having same error, be sure there is no tkinter.py file in the folder.
I had this same error too. The issue with mine is I had a file called tkinter.py and that was overriding the built-in file tkinter. So to fix it, I changed my filename to something else.