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.
Related
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()
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 having a big issue. The Canvas loads perfectly but the image does not display.
I started Python 1 week ago and I have no clue why does is not working. Can anyone please show me the way to solve the issue of the image not loading on the canvas?
from Tkinter import *
from PIL import ImageTk
from PIL import Image
class Fake_Virus:
def __init__(self, master):
self.master = master
master.title("Totally not a virus!")
b = Button(master, text="Help", command=self.prank)
b.pack(padx=10, pady=10, side=LEFT)
quit = Button(master, text="Close", command=self.close_window)
quit.pack(padx=10, pady=10, side=RIGHT)
photo = PhotoImage("eh.gif")
label = Label(image=photo)
label.image = photo # keep a reference!
label.pack()
f = Frame(master, height=150, width=150)
f.pack_propagate(0) # don't shrink
f.pack()
def prank(self):
print "work"
return
def close_window(self):
root.destroy()
return
root = Tk()
my_gui = Fake_Virus(root)
root.mainloop()
You should use the file option to initialize the photo image object.
This means you need to change photo = PhotoImage("eh.gif") to photo = PhotoImage(file="eh.gif")
Now your code will work. But a working code is not necessarily a good code. There are other issues with your code. Let me go through them quickly:
It is better to code import Tkinter as Tk than from Tkinter import *
Why that hyphen in your class name? Follow PEP8 so that, in the futur, people will find it easy to review and understand your code.
Good that you have written self.master = master (read complete code to know why) but then you have never used it. This means you made a good decision and you render it useless.
You set the title of the window within the initializer. It is better if you do that in a separate function so that whenever you want to add additional settings to your GUI (such as the size, font or whatever) you will only add code to that function instead of vomiting lot of trash inside the initializer which rather needs to be clean.
None of the widgets you created is 'selfed' (you may read Why explicit self has to stay)
It is better you create the widgets in a separate function otherwise your __init__() will be dirty.
Why do you use return in prank() and close_window()? By default, Python functions that do not return something return None anyway so it is useless to code that.
Why did you pack one button to left and the other one to right and then no pack siding for the label? Read about the pack() geometry manager.
Why you did not attach the label to a parent widget as you did for the 2 other buttons? All Tkinter widgets need to be clung into a parent widget. The grand parent of those widgets is an instance of Tkinter.Tk()
Why did you create that frame and then you never used it? You are not doing anything with it, so ..?
Given these remarks, I want to provide you an improved -but not perfect- version of your program. You can then follow this 'philosophy' to add or modifying existing widgets:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import Tkinter as Tk
from PIL import ImageTk
class FakeVirus:
def __init__(self, master):
self.master = master
self.configure_gui()
self.create_widgets()
def configure_gui(self):
self.master.title('Totally not a virus!')
def create_widgets(self):
self.create_buttons()
self.create_label_for_image()
def create_buttons(self):
self.help = Tk.Button(self.master, text='Help', command=self.prank)
self.help.pack(side=Tk.LEFT)
self.quit = Tk.Button(self.master, text='Close', command=self.close_window)
self.quit.pack(side=Tk.LEFT)
def create_label_for_image(self):
self.image_label = Tk.Label(self.master)
self.image_label.pack(side=Tk.LEFT)
self.load_image_to_label()
def load_image_to_label(self):
self.photo = ImageTk.PhotoImage(file='eh.gif')
self.image_label.image = self.photo
self.image_label.config(image=self.photo)
def prank(self):
print "work"
def close_window(self):
root.destroy()
if __name__ == '__main__':
root = Tk.Tk()
my_gui = FakeVirus(root)
root.mainloop()
The output of the above program is:
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.
I am using Tkinter to build two windows. One the main one, pressing a button leads to the creation of the second window.
This second window does not get focus immediately when it created. That I am able to fix by calling .focus_force(). However, when I call the askdirectory() function from tkFileDialog, the focus changes back to the first window.
How can I prevent that focus switch from happening, without simply calling focus_force() all over the place?
To replicate problem:
from Tkinter import *
from tkFileDialog import *
class app:
def __init__(self, master):
Button(master, command = make_new).grid()
def make_new(self):
root = Tk()
new = new_win(root)
root.mainloop() #here the focus is on the first window
class new_win:
def __init__(self, master):
f = askdirectory() #even after placing focus on second window,
#focus goes back to first window here
I am using Python 2.7.3. Thanks!
the little-documented wm_attributes method might help:
from Tkinter import *
import tkFileDialog
root = Tk()
top = Toplevel()
top.wm_attributes('-topmost', 1)
top.withdraw()
top.protocol('WM_DELETE_WINDOW', top.withdraw)
def do_dialog():
oldFoc = top.focus_get()
print tkFileDialog.askdirectory()
if oldFoc: oldFoc.focus_set()
b0 = Button(top, text='choose dir', command=do_dialog)
b0.pack(padx=100, pady=100)
def popup():
top.deiconify()
b0.focus_set()
b1 = Button(root, text='popup', command=popup)
b1.pack(padx=100, pady=100)
root.mainloop()