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.
Related
I am trying to create an application to show real-time data in a study on electrical systems. The menu will be used to open information on history of each of the graphs shown on the "front page" and will also include a quit option. When I create the menu and add a couple items to it, it does not show up when I run the application. Unfortunately in my searches, all the alternatives I have tried do not show up either.
from tkinter import *
from tkinter import ttk
class PicoGridInterfacing(Frame):
def __init__(self, master=None):
super().__init__(master)
self.master = master
self.grid()
menubar = Menu(root)
menu = Menu(menubar)
menubar.add_cascade(label="Menu", menu=menu)
menu.add_command(label="History", command=None)
menu.add_command(label="Quit", command=root.quit)
batteryData = Frame(root)
batteryData.grid(row=1, column=0)
Label(batteryData, text="Hello!").grid()
root = Tk()
root.title("Electric Power Systems Lab Pico Grid Interfacing")
app = PicoGridInterfacing(master=root)
root.mainloop()
Here's a screenshot of the output I see:
Screenshot of output
Any help would be greatly appreciated, thank you!
Try this:
from tkinter import *
from tkinter import ttk
class PicoGridInterfacing(Frame):
def __init__(self, master=None):
super().__init__(master)
# master = master # `super().__init__(master)` already does that so its useless
# Creating the menu
menubar = Menu(root)
menu = Menu(menubar, tearoff=False)
menubar.add_cascade(label="Menu", menu=menu)
menu.add_command(label="History", command=None)
menu.add_command(label="Quit", command=root.destroy)
# Tell the `Tk()` that there is a menu that it need to display:
master.config(menu=menubar)
batteryData = Frame(self)
batteryData.grid(row=1, column=0)
Label(batteryData, text="Hello!").grid()
root = Tk()
root.title("Electric Power Systems Lab Pico Grid Interfacing")
app = PicoGridInterfacing(master=root)
# When you inherit from `Frame` you always what the user to call `.grid`
app.grid()
root.mainloop()
You need to tell the tkinter.Tk() that there is a menu otherwise it will ignore it. Also when inheriting from tkinter.Frame it's the caller's job to call .grid (it's convention).
You need to attach the menu bar to the root window.
So do this: root.config(menu=menubar)
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.
def createWidgets(self):
self.INSTRUCTIONS = Button(self) #creating button linked to instructions_window
self.INSTRUCTIONS["text"] = "Instructions"
self.INSTRUCTIONS["fg"] = "green"
self.INSTRUCTIONS["command"] = self.instruction_window #command which opens instructions_window
self.INSTRUCTIONS.pack({"side": "left"})
Currently, if I press the button multiple times then the instructions window will open multiple times. How do I ensure that when the button is pressed, if the window is already open then it will flash to show that the same window can't be opened. Is there a command? Or do I need to use a validation of some sort?
Here's a great article dealing with more complicated examples of dialog boxes.
Essentially what you are looking for is almost like a modal dialog window except it seems with the additional ability to still interact with the parent window to a degree. At this point it may be worth considering making it totally modal, but I do not know your use case. If not, you can definitely adapt the scripts given on the tutorial website to fit your needs.
The way I do this is to create a function that will create the window if it doesn't exist, and then display the window. Personally I don't think there's a need to flash the window, but you could do that if you want. Tkinter doesn't know how to flash a window, but you can do something simple like changing the colors briefly.
Here's an example:
import Tkinter as tk
class Example(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.instruction_window = None
self.instructions = tk.Button(self, text="Instructions", foreground="green",
command=self.show_instructions)
self.instructions.pack(side="left")
def show_instructions(self):
'''show the instruction window; create it if it doesn't exist'''
if self.instruction_window is None or not self.instruction_window.winfo_exists():
self.instruction_window = InstructionWindow(self)
else:
self.instruction_window.flash()
class InstructionWindow(tk.Toplevel):
'''A simple instruction window'''
def __init__(self, parent):
tk.Toplevel.__init__(self, parent)
self.text = tk.Text(self, width=40, height=8)
self.text.pack(side="top", fill="both", expand=True)
self.text.insert("end", "these are the instructions")
def flash(self):
'''make the window visible, and make it flash temporarily'''
# make sure the window is visible, in case it got hidden
self.lift()
self.deiconify()
# blink the colors
self.after(100, lambda: self.text.configure(bg="black", fg="white"))
self.after(500, lambda: self.text.configure(bg="white", fg="black"))
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(side="top", fill="both", expand=True)
root.mainloop()
I have a very simple program with TkInter in Python.
How to I use the "fill" or "expand" options with the following code?
from Tkinter import *
class Application(Frame):
def sayhello(self):
print "Hello!"
def createWidgets(self):
self.var = Button(self, text="Hello", command = self.sayhello)
self.var.pack(side=LEFT)
self.QUIT = Button(self, text="QUIT", fg="red", command = self.quit)
self.QUIT.pack(side=LEFT)
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.createWidgets()
root = Tk()
app = Application(master=root)
app.mainloop()
root.destroy()
Note: I have to use the self.var.pack() format so it can do a "command" when it's pressed...unless anyone has a better way?
To make an object fill it's container in the y axis, you would use the parameter fill="y" (or fill=Y if you import Y from Tkinter).
Note that this only controls how the widget fills its container. In your code, this makes the button fill the inner frame, but because your inner frame doesn't fill the main window in the y axis, you might not get the visual effect you expect.
Also, specifically in the case of buttons on the Macintosh, the button won't grow to fill the space. On OSX, buttons are native widgets which can't grow in height.
I am trying to add a custom title to a window but I am having troubles with it. I know my code isn't right but when I run it, it creates 2 windows instead, one with just the title tk and another bigger window with "Simple Prog". How do I make it so that the tk window has the title "Simple Prog" instead of having a new additional window. I dont think I'm suppose to have the Tk() part because when i have that in my complete code, there's an error
from tkinter import Tk, Button, Frame, Entry, END
class ABC(Frame):
def __init__(self,parent=None):
Frame.__init__(self,parent)
self.parent = parent
self.pack()
ABC.make_widgets(self)
def make_widgets(self):
self.root = Tk()
self.root.title("Simple Prog")
If you don't create a root window, Tkinter will create one for you when you try to create any other widget. Thus, in your __init__, because you haven't yet created a root window when you initialize the frame, Tkinter will create one for you. Then, you call make_widgets which creates a second root window. That is why you are seeing two windows.
A well-written Tkinter program should always explicitly create a root window before creating any other widgets.
When you modify your code to explicitly create the root window, you'll end up with one window with the expected title.
Example:
from tkinter import Tk, Button, Frame, Entry, END
class ABC(Frame):
def __init__(self,parent=None):
Frame.__init__(self,parent)
self.parent = parent
self.pack()
self.make_widgets()
def make_widgets(self):
# don't assume that self.parent is a root window.
# instead, call `winfo_toplevel to get the root window
self.winfo_toplevel().title("Simple Prog")
# this adds something to the frame, otherwise the default
# size of the window will be very small
label = Entry(self)
label.pack(side="top", fill="x")
root = Tk()
abc = ABC(root)
root.mainloop()
Also note the use of self.make_widgets() rather than ABC.make_widgets(self). While both end up doing the same thing, the former is the proper way to call the function.
Here it is nice and simple.
root = tkinter.Tk()
root.title('My Title')
root is the window you create and root.title() sets the title of that window.
Try something like:
from tkinter import Tk, Button, Frame, Entry, END
class ABC(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
root = Tk()
app = ABC(master=root)
app.master.title("Simple Prog")
app.mainloop()
root.destroy()
Now you should have a frame with a title, then afterwards you can add windows for
different widgets if you like.
One point that must be stressed out is:
The .title() method must go before the .mainloop()
Example:
from tkinter import *
# Instantiating/Creating the object
main_menu = Tk()
# Set title
main_menu.title("Hello World")
# Infinite loop
main_menu.mainloop()
Otherwise, this error might occur:
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/tkinter/__init__.py", line 2217, in wm_title
return self.tk.call('wm', 'title', self._w, string)
_tkinter.TclError: can't invoke "wm" command: application has been destroyed
And the title won't show up on the top frame.
Example of python GUI
Here is an example:
from tkinter import *;
screen = Tk();
screen.geometry("370x420"); //size of screen
Change the name of window
screen.title('Title Name')
Run it:
screen.mainloop();
I found this works:
window = Tk()
window.title('Window')
Maybe this helps?
Easy method:
root = Tk()
root.title('Hello World')
Having just done this myself you can do it this way:
from tkinter import Tk, Button, Frame, Entry, END
class ABC(Frame):
def __init__(self, parent=None):
Frame.__init__(self, parent)
self.parent = parent
self.pack()
ABC.make_widgets(self)
def make_widgets(self):
self.parent.title("Simple Prog")
You will see the title change, and you won't get two windows. I've left my parent as master as in the Tkinter reference stuff in the python library documentation.
For anybody who runs into the issue of having two windows open and runs across this question, here is how I stumbled upon a solution:
The reason the code in this question is producing two windows is because
Frame.__init__(self, parent)
is being run before
self.root = Tk()
The simple fix is to run Tk() before running Frame.__init__():
self.root = Tk()
Frame.__init__(self, parent)
Why that is the case, I'm not entirely sure.
self.parent is a reference to the actual window, so self.root.title should be self.parent.title, and self.root shouldn't exist.
widget.winfo_toplevel().title("My_Title")
changes the title of either Tk or Toplevel instance that the widget is a child of.
I found a solution that should help you:
from tkinter import Tk, Button, Frame, Entry, END
class ABC(Frame):
def __init__(self,master=None):
super().__init__(master)
self.pack()
self.master.title("Simple Prog")
self.make_widgets()
def make_widgets(self):
pass
root = Tk()
app = ABC(master=root)
app.mainloop()
Found at: docs.python.org