How to put tkinter Message widget on tkinter Canvas - python

I was trying to make a window with tkinter. On the home screen, I would have a label, a few buttons, and then a Message, all on the tkinter.Canvas. I created them in different functions, but even then, the label and buttons went on the canvas. The Message didn't, returning an error:
File "C:\Users\bobby\AppData\Local\Programs\Python\Python35-32\NFL Model\nflScreen.py", line 54, in homeScreen
self.homeText = tk.Message(master=self.canvas, text=self.getText(0), width=550)
File "C:\Users\bobby\AppData\Local\Programs\Python\Python35-32\lib\tkinter\__init__.py", line 2816, in __init__
Widget.__init__(self, master, 'message', cnf, kw)
File "C:\Users\bobby\AppData\Local\Programs\Python\Python35-32\lib\tkinter\__init__.py", line 2139, in __init__
(widgetName, self._w) + extra + self._options(cnf))
_tkinter.TclError: bad window path name ".59508144.59508176"
Here is my code to make the window:
import tkinter as tk
class Window(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.pack()
self.makeScroll(); self.showMenu(); self.homeScreen();
def makeScroll(self):
self.canvas = tk.Canvas(self, bg='#F0F0F0', width=1000, height=600)
self.canvas.grid(row=0, column=0) ###Makes the Canvas. This part works well
def showMenu(self):
self.home = tk.Button(self.canvas, text="Home", command=self.homeScreen, width=10)
self.home.grid(column=0, row=1) ###Make some buttons, this part works well.
self.rank = tk.Button(self.canvas, text="Rankings", command=self.rankingScreen, width=10)
self.rank.grid(column=1, row=1)
###I tried adding a Message here to the canvas, and it worked, but I need the message to be made in another function
def homeScreen(self):
###This part doesn't work. I know that self.canvas is still a variable.
self.homeText = tk.Message(master=self.canvas, text=self.getText(0), width=550)
self.homeText.grid(column=0, row=3, columnspan=7)
Basically, I create the tkinter.Canvas in makeScroll(). When I add widgets to it in showMenu(), that works. But when I try to add widgets in homeScreen(), it doesn't. Does anyone know why, and how to fix it?

I found out after a while that this was just a logic error. In my real code, I called a function that would delete the canvas object at the start of the homeScreen() function.

Related

Can't put tkinter widget inside Toplevel Frame

I am trying to open a toplevel widget from a button press, and generate a list within that frame from an example I found. However, when I try to place the widget within the generated frame, I get the following error:
_tkinter.TclError: can't put .!treeview inside .!errorexample.!toplevel.!mclistdemo.!frame.!frame
I have narrowed down the problem to
self.tree.grid(in_=f, row=0, column=0, sticky=NSEW)
within the _create_treeview method. When the in_ command is removed, the widget is generated correctly in the parent window. I suspect that the problem has something to do with my parent/self naming conventions, but I am still struggling to grasp that subject.
Most of the questions I've run across that are described similarly seem to be a matter of trying to place the widget while generating it, but that doesn't seem to be the case in this code.
from tkinter import *
from tkinter import ttk
from tkinter.font import Font
class ErrorExample(Frame):
def __init__(self, parent):
Frame.__init__(self,parent)
self.grid()
self.parent=parent
self.b4=Button(
self,
text="Load",
command=lambda: self.createWindow())
self.b4.grid()
def createWindow(self):
self.t = Toplevel(self)
MCListDemo(self)
class MCListDemo(ttk.Frame):
def __init__(self, parent, isapp=True):
ttk.Frame.__init__(self, parent.t)
self.grid()
self.isapp = isapp
self._create_widgets()
def _create_widgets(self):
if self.isapp:
self._create_demo_panel()
def _create_demo_panel(self):
demoPanel = Frame(self)
demoPanel.grid()
self._create_treeview(demoPanel)
self._load_data()
def _create_treeview(self, parent):
f = ttk.Frame(parent)
f.grid()
self.dataCols = ('country', 'capital', 'currency')
self.tree = ttk.Treeview(columns=self.dataCols,
show = 'headings')
self.tree.grid(in_=f, row=0, column=0, sticky=NSEW)
start=Tk()
ErrorExample(start)
if __name__=="__main__":
main()
You don't give the treeview a parent, so it has the root window as a parent. Widgets live in a hierarchy, and widget's can't be placed in a different part of the hierarchy.
The official documentation describes it like this:
The master for each slave must either be the slave's parent (the default) or a descendant of the slave's parent. This restriction is necessary to guarantee that the slave can be placed over any part of its master that is visible without danger of the slave being clipped by its parent.
If you want the treeview to be in f, the simplest way is to make f be the parent:
self.tree = ttk.Treeview(f, ...)
self.tree.grid(row=0, column=0, sticky=NSEW)

Tkinter Module Error erroe creating login page in calss pageone

I have try to write program for library Management system I am using tkinter module for it. I have writen the below code but when I am trying to create multiple Text box i am getting below error.
File "Hope_work.py", line 22, in __init__
frame = F(container, self)
File "Hope_work.py", line 62, in __init__
pwd_lable.pack()
UnboundLocalError: local variable 'pwd_lable' referenced before assignment
Below is the complete program I am getting error in PageOne class
import tkinter as tk
import os
LARGE_FONT= ("Verdana", 12)
class Myprogramapp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, PageOne):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
label = tk.Label(self, text="Library Managment System", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button = tk.Button(self, text="Admin Login",
command=lambda: controller.show_frame(PageOne))
button.pack()
button1 = tk.Button(self, text="Lib Login")
button1.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
name_label = tk.Label(self, text="User ID : ")
pwd_label = tk.Label(self.name_lable, text="Password:")
name_label.pack(pady=10,padx=10)
pwd_lable.pack(pady=10,padx=10)
name_lable = tk.Entry(self)
pwd_lable = tk.Entry(self, show="*")
name_lable.pack()
pwd_lable.pack()
button1 = tk.Button(self, text="Login")
button1.pack()
if __name__ == "__main__":
app = Myprogramapp()
app.mainloop()
**
It would appear that you are trying to bite off more than you can chew so to speak with this example of code. You are miss using parts of tkinter that should be understood before moving onto something more complicated like this.
Before you try to use multiple classes like this try to focus more on understanding how tkinter works and how to implement all its widgets properly in a single class or even outside of a class first.
You do not assign widgets to another widget like you are attempting to do here:
pwd_label = tk.Label(self.name_lable, text="Password:")
This is the problem refereed to in your trackback. You need to assign the Label widget to either a root window, a frame or a toplevel.
Your indention is not clean and if the way you have it pasted in your question is accurate then you code will not work as the def show_frame() method is not inside the Myprogramapp class.
You are importing os for no reason here and it is not good practice to import libraries you are not currently using.
You should make some important parts of your program into a class attribute, like your entry fields. If you are going to put in a password to that field and try and get() the string from the entry field inside a method you wont be able to as its not a class attribute. You can fix this by adding self. prefix to the widget name.
All and all after making those changes you will get a tkinter window with 2 buttons. The Admin Login button will display a login screen. That being said I think you should either step away from classes all together while learning tkinter or work in a single class for now until you have a solid understanding of how classes, methods, and attributes work and are used.

Tkinter grid manager can't manage 'top level window' menu

So I've got a Python 3 and Tkinter app. I'm currently trying to add a menu bar to the top of the window it pops up in. It's fine creating, but as soon as I try to grid() it, it all goes haywire and throws me this error:
[first line omitted]
File "C:\Users\Me\Documents\sync.py", line 13 in __init__
self.createWidgets()
File "C:\Users\Me\Documents\sync.py", line 21, in createWidgets
self.menubar.grid(column = 0, comlumnspan = 3)
File "C:\Program Files (x86)\Python34\lib\tkinter\__init__.py", line 2020, in grid_configure + self._options(cnf,kw))
_tkinter.TclError: can't manage ".41452544.49048880": it's a top-level window
So from that, it's fairly obvious that the flow is working: __init__ calls createWidgets creates self.menubar, adds submenus, calls self.menubar.grid.
What I can't figure out is why Tkinter thinks the menubar it's being called to grid is a top-level window. self.menubar.grid(), being listed in the trace, is clearly the cause of the problem rather than the root window, so that must be what it's thinking.
Here's the bit of code in question:
def createWidgets(self):
self.menubar = tk.Menu(self)
self.menubar.grid(column = 0, columnspan = 3)
SyncMenu = tk.Menu(self.menubar, tearoff = 0)
SyncMenu.add_command(label = "Connect", command = self.Sync.Connect)
SyncMenu.add_command(label = "Disconnect", command = self.Sync.Disconnect)
FileMenu = tk.Menu(self.menubar, tearoff = 0)
FileMenu.add_command(label = "Upload File", command = self.File.Upload)
FileMenu.add_command(label = "Browse Online Files", command = self.File.Browse)
self.menubar.add_cascade(label = "Sync", menu = SyncMenu)
self.menubar.add_cascade(label = "File", menu = FileMenu)
The same error comes up no matter where I put the call to grid().
The other part of this problem is that I'm in Python 3 and the updated Tkinter, so no tk.Tk() for me. Instead, to initialise, I have this:
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self,master)
self.grid()
self.createWidgets()
self.master.geometry("500x500")
You can't use grid on menus. Tkinter considers them a top-level window because they float above other windows.
The normal way to create a traditional menubar is to associate it with the menu attribute of the root window:
root = tk.Tk()
menubar = tk.Menu(root)
...
root.configure(menu=menubar)
I've figured it out. When I edited the question to add the second code block, I noticed the self.master.geometry line, to which I thought "That line affects the main window, couldn't I use that?"
The answer was yes, so the solution to finding the root window was just to use self.master, which is defined in my __init__ method by the supercall to tk.Frame.__init__:
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self,master) # This line defines self.master
self.grid()
self.createWidgets()
self.master.geometry("500x500") # This line uses it
So the solution to get my menubar into the correct window is now this:
self.master["menu"] = self.menubar
which is in the code after creating all the menu and menu items.

Error when import a save file in Tkinter with Python

I have this problem in Tkinter. I wish to import a file (*.txt) with Open Button and save some values with Save Button. I wish to show a Error message when no file is open.
I am wrong becuse i get always this message:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python27\lib\lib-tk\Tkinter.py", line 1470, in __call__
return self.func(*args)
File "C:/Users/samsung/PycharmProjects/test_FirstDensity/openANDread.py", line 30, in save
if not self.filename.name:
AttributeError: MainWindow instance has no attribute 'filename'
This is a very simplify example
from Tkinter import *
import tkMessageBox
import Tkinter, Tkconstants, tkFileDialog
class MainWindow(Frame):
def __init__(self):
Frame.__init__(self)
self.master.title("input")
self.master.minsize(250, 150)
self.grid(sticky=E+W+N+S)
top=self.winfo_toplevel()
top.rowconfigure(0, weight=1)
top.columnconfigure(0, weight=1)
for i in range(2):self.rowconfigure(i, weight=1)
self.columnconfigure(1, weight=1)
self.button0 = Button(self, text="open", command=self.askopenfilename, activeforeground="red")
self.button0.grid(row=0, column=0, columnspan=2, pady=2, padx=2, sticky=E+W+N+S)
self.button1 = Button(self, text="save", command=self.save, activeforeground="red")
self.button1.grid(row=1, column=0, columnspan=2, pady=2, padx=2, sticky=E+W+N+S)
def askopenfilename(self):
self.filename = tkFileDialog.askopenfilename(filetypes=[("Text Files",'.txt')])
return self.filename.name
def save(self):
if not self.filename.name:
tkMessageBox.showerror("Error", message="None file to save")
return
It seems like you are calling the save() method before calling the askopenfilename() method. That's why, you get the AttributeError. Make sure the flow of your code reflects this change.
You might also want to include proper error handling in your askopenfilename() method itself to include situations where no file is opened.
You could do something on these lines. First initialize the self.filename = None in your constructor for the MainWindow class. Then you could modify your method as such:
def askopenfilename(self):
self.filename = tkFileDialog.askopenfilename(filetypes=[("Text Files",'.txt')])
if not self.filename: return None
return self.filename.name
However this is just to give you an idea, I have not myself worked with Tkinter much. All in all, it would depend on what exactly you are trying to achieve.

Updating Python Tkinter GUI using .after method

I have a listbox that I want to update every minute or so. It pulls XML data, parses it and put it into the list box automatically. I have figured out to use the .after method but upon implementing it outside of the class I run into bug after bug trying to make it run. I believe my main issue is just not calling the application correctly but I could be wrong. Here is some of the relevent code.
This is all outside the main class
def refresher(frame):
subreddit=Application.entryVar(Application)
Application.getXML(subreddit)
frame.after(1000,refresher,frame)
main = tk.Tk()
main.wm_title("Readdit")
# main.geometry("350x400")
app = Application(master=main)
# Begins the applications GUI loop
# app.__init__()
refresher(main)
app.mainloop()
Here is the beginning of the program and this is where it ultimately falls back upon with all the bugs.
class Application(tk.Frame):
print("what about this?")
def __init__(self, master=None):
self.threadTitle = tk.StringVar()
self.threadAuth = tk.StringVar()
self.threadPub = tk.StringVar()
self.threadArtLink = tk.StringVar()
self.threadLink = tk.StringVar()
self.threadImg = tk.StringVar()
self.threadArtLink.set('Click something to display thread info')
self.photo = Image.open("temp.png")
self.photo = self.photo.resize((250,250), Image.ANTIALIAS)
self.threadImage = ImageTk.PhotoImage(self.photo)
self.errMes = tk.StringVar()
if not os.path.exists('Pics'):
os.makedirs('Pics')
# print('Something')
# Intializes tkinter gui framework
tk.Frame.__init__(self, master)
# Packs widgets needed
self.grid()
# Creates the widgets functions
self.createWidgets()
# Intializes the man rss.xml
self.initial()
def createWidgets(self):
# Create entrybox and align to grid
self.send_entry = tk.Entry(self)
self.send_entry.grid(row=0,column=0)
# Create button,allign to grid, get xml
self.change_sub = tk.Button(self,text='Change Subreddit',padx=5, pady=5, command=lambda :self.entryVar())
And here is the full error
Traceback (most recent call last):
File "S:/Projects/xmlParser.py", line 306, in <module>
refresher(main)
File "S:/Projects/xmlParser.py", line 296, in refresher
subreddit=Application.entryVar(Application)
File "S:/Projects/xmlParser.py", line 290, in entryVar
rawInput=self.createWidgets(self).send_entry.get()
File "S:/Projects/xmlParser.py", line 40, in createWidgets
self.send_entry = tk.Entry(self)
File "C:\Python33\lib\tkinter\__init__.py", line 2506, in __init__
Widget.__init__(self, master, 'entry', cnf, kw)
File "C:\Python33\lib\tkinter\__init__.py", line 2068, in __init__
BaseWidget._setup(self, master, cnf)
File "C:\Python33\lib\tkinter\__init__.py", line 2046, in _setup
self.tk = master.tk
AttributeError: type object 'Application' has no attribute 'tk'
I think you should use your app directly in the function call to refresher:
def refresher(frame):
frame.getXML()# I Don`t know what do this function, just an example
frame.after(1000,refresher,frame)
main = tk.Tk()
main.wm_title("Readdit")
# main.geometry("350x400")
app = Application(master=main)
# Begins the applications GUI loop
# app.__init__()
refresher(app) #use app here
app.mainloop()
It looks like the problem is here:
Application.entryVar(Application)
Application is a class rather than an object, so my guess is that you should be using an instance of Application both places in that code.

Categories

Resources