TkInter Menubar causes program to not run - python

I am trying to write a simple text editor using TkInter. I want it to have a menu bar like other text editors, where you can save, open another file, etc.
However, whenever I try to add a menu bar to my class, the program simply starts, hangs for about half a second, then exits. I have no idea why this is happening, or how to debug it. Here is my code.
#!/usr/bin/env python3
import functools
from tkinter import *
class mainWindow(Tk):
def initiate(self):
menuBarFrame = Frame(self).pack(side=TOP)
menubar = Menu(menuBarFrame)
menubar.add_command(label='Exit', command=quit())
root.config(menu=menubar)
mainloop()
win = mainWindow().initiate()
I tried adding .pack() to the line
menubar = Menu(menuBarFrame)
but it gives me the following traceback:
File "XML.py", line 14, in <module>
win = mainWindow().initiate()
File "XML.py", line 9, in initiate
menubar = Menu(menuBarFrame).pack()
File "/usr/lib/python3.4/tkinter/__init__.py", line 1977, in pack_configure
+ self._options(cnf, kw))
_tkinter.TclError: can't pack ".140664986043280": it's a top-level window
When I remove the code for the menubar, and just replace it with a simple button, the application works and starts fine. What could be causing the problem?

The menu needs to be a child of the root window, rather than a child of a frame. You don't need MenuBarFrame at all.
Also, take a look at this line:
menubar.add_command(label='Exit', command=quit())
You are instructing Tkinter to immediately call the quit() function, and assign the result to the command attribute of the menu command. I'm guessing that quit() actually quits rather than returning a reference to some other function. You need to change it to this:
menubar.add_command(label='Exit', command=quit)
Of course, the other glaring problem is that you don't actually define root anywhere.
You definitely don't want to call pack() on the instance of Menu. The correct way to attach the menu to the window is with root.config(menu=menubar), like you're already doing.

Related

How to I combine two Tkinter "Files"

So I made the Main Menu in one Tkinter File and I made an app in another file. In the main menu, I have a button, when I click that button it should open the app but NOT SHOW THE MAIN MENU.
File 1
from tkinter import *
root = Tk()
root.geometry("600x600")
btn = Button(root, command=#add one later)
And in file 2 I have my app and when I click on the button it should open the app and hide the main menu. Can I do this through frames?
NOTE: THE APP IS ANOTHER FILE
As far as I have understood your question, there could be 2 things that you might be asking, 1st if you want to launch a new window and destroy this, you can call root.destroy() followed by calling the other file by using import file_name that has another Tk(). 2nd if you want to pack/update a Frame then make sure that the other file is within a definition which is not called there, also in the second file use the same terminology as you have used in the first file or you can pass these parameters through the function call. Now you can do
menu.destroy()
import file_name
file_name.function_name(arguments)

Opening another tkinter window from one tkinter window

I have a Tkinter window in the file gui.py. Upon the press of the Spacebar, I want to open another Tkinter window which is used to obtain an input from the user via the file imageinput.py.
So, I wrote the code to execute the run function of imageinput.py
def keyPressed(event, data):
if event.keysym == "space": image_run()
When I run this, I get the following error:
What is the best way to open such another Tkinter window this way?
Without knowing more of your code, you would create a new "top level" widget and use that widget like you used the original root top level window ( root = tkinter.Tk()) as the parent of whatever widget hierarchy you create. So...
def image_run(parent, *args, **kwargs):
top = tkinter.Toplevel(parent)
top.transient(parent)
canvas = tkinter.Canvas(top, ...)
:
:
Hope that helps!

Button behaviour

I work with Python 3.5 and TKinter.
I defined a label and file dialog that updates this label.
A button is responsible to launch this dialog.
self.sel_folder_val = the label that will be updated.
The code:
self.sel_folder_val['text']=filedialog.askdirectory()
After pressing the button in order to launch this dialog, the button stays pressed. Any dialog that a button is responsible to open cause the button to stay low (pressed) after closing this dialog.
I have tried this also with no help...:
self.select_folder_btn.config(relief=RAISED)
Code example:
self.select_folder_btn = Button(self.top)
self.select_folder_btn.place(relx=0.07, rely=0.57, height=34, width=187)
self.select_folder_btn.configure(activebackground="#d9d9d9")
self.select_folder_btn.configure(activeforeground="#000000")
self.select_folder_btn.configure(background="#d9d9d9")
self.select_folder_btn.configure(disabledforeground="#a3a3a3")
self.select_folder_btn.configure(font=self.font3)
self.select_folder_btn.configure(foreground="#000000")
self.select_folder_btn.configure(highlightbackground="#d9d9d9")
self.select_folder_btn.configure(highlightcolor="black")
self.select_folder_btn.configure(pady="0")
self.select_folder_btn.configure(text='''Select destination folder''')
self.select_folder_btn.bind('<Button-1>',self.update_folder_value)
def update_folder_value(self,event):
self.sel_folder_val['text']=filedialog.askdirectory()
return
After executing update_folder_value() function, self.select_folder_btn stays down.
I used the command:
self.select_folder_btn.configure(command=self.update_folder_value)
Instead of bind:
self.select_folder_btn.bind('<Button-1>',self.update_folder_value)
It solved my problem.
Thanks
First for future reference this is a minimal working example:
from Tkinter import *
import tkFileDialog as filedialog
class app:
def __init__(self):
self.top = Tk()
self.select_folder_btn = Button(self.top)
self.select_folder_btn.place(relx=0.07, rely=0.57, height=34, width=187)
self.select_folder_btn.configure(activebackground="#d9d9d9")
self.select_folder_btn.configure(activeforeground="#000000")
self.select_folder_btn.configure(background="#d9d9d9")
self.select_folder_btn.configure(disabledforeground="#a3a3a3")
#self.select_folder_btn.configure(font=self.font3)
self.select_folder_btn.configure(foreground="#000000")
self.select_folder_btn.configure(highlightbackground="#d9d9d9")
self.select_folder_btn.configure(highlightcolor="black")
self.select_folder_btn.configure(pady="0")
self.select_folder_btn.configure(text='''Select destination folder''')
self.select_folder_btn.configure(command=self.update_folder_value)
self.sel_folder_val = {}
self.top.mainloop()
def update_folder_value(self):
self.sel_folder_val['text']=filedialog.askdirectory()
self.top.update_idletasks()
app()
and even that's not minimal. Second your problem is hard to find since this isn't minimal- you're doing something really weird - binding the button to a click. You're overriding the built-in binding, and apparently it still affects the state of the button on press, but not going back. What you wanted is:
self.select_folder_btn.configure(command=self.update_folder_value)
instead of your:
self.select_folder_btn.bind('<Button-1>',self.update_folder_value)
You could also define that in the Button command. What you did is bypassed the button mechanism, so apparently only half of it is executed, and the relief is not raised. Note you have to remove the event parameter your method accepts.

Procedure executes differently when imported than when run in its native module

I have this code:
def Annabeth():
Annabeth= Tk()
Annabeth.geometry('450x450')
says = Label(Annabeth,text ='I was just making a general statement!')
says.pack(side=BOTTOM)
img = ImageTk.PhotoImage(Image.open('C:/Users/Geekman2/Pictures/Pictures/Annabeth.jpg'))
image1 = Label(Annabeth,image=img)
image1.pack()
Annabeth.mainloop()
it resides in the module
rox
when I invoke
Annabeth()
my window comes up, it displays the image, and everything works fine.
and when, from another module I use the code
from rox import*
Annabeth()
it works just fine
but when I use this code
def callback():
Annabeth()
game = Tk()
game.geometry('50x50+700+100')
Button1 = Button(game,text = '1',command =callback )
Button1.pack(side=LEFT)
game.mainloop()
The window displays, but the picture does not show up and I get the error
File "C:\Python27\lib\lib-tk\Tkinter.py", line 1974, in __init__
(widgetName, self._w) + extra + self._options(cnf))
TclError: image "pyimage1" doesn't exist
And for the life of me I cannot figure out what is causing this, apparently I'm not supposed to have more than one mainloop in a GUI program, but how am I supposed to do this then?
You are not supposed to instanciate Tk() several times in your program. You might consider using Toplevel if you want multiple windows.
By the way, it will solve your several mainloop issue since Toplevel instances will run in the same mainloop as game.
What happens exactly is that ImageTk.PhotoImage creates the image in the first Tcl/Tk interpreter that has been created. Thus, your label image1 that run in a second Tcl/Tk instance can not reach the picture.

Tkinter Keyboard Binds

I'm working on an interface using Tkinter and the canvas widget, and so far have found answers to issues I have had from others questions and the answers posted, but I am stumped on this one.
I have several keyboard binds in the class where my GUI elements are created, and they all work fine when the program is started. The binds looks something like this:
self.canvas.get_tk_widget().bind("<Control-o>",self.flash_open)
and are within the __init__ function of the class. As of yesterday, I initialized this class
to start the program, then waited for the user to select open from a menu, which then opened (among other things) a tkmessagebox
self.specfilename =askopenfilename(filetypes=[("spec", "")],initialdir= self.pathname)
With this filename I am able to retrieve my required variable names from a certain filetype (inconsequential to the problem). Today I modified the __init__ function to call the open function when the program starts. Since nothing else can be done until this file is opened, it would make sense to open it first thing. Once the file is selected and the Tkmessagebox is closed, the root window is active, but none of the keyboard binds work. My functions still work using the menu/buttons assigned to them, just not the binds. I have tried binding the shortcuts to the root, with the same result, and am now thinking it may be an issue with the order I am calling them
def __init__(self):
...
self.openfile() #calls the tkmessagebox
self.root.mainloop() #starts gui
I had actually run into this issue before, where a toplevel() instance was closed/destroyed and disabled the binds of the parent window. There isn't any error message to speak of, the binds just don't do anything. I should also mention I have tried to focus on the root window again using
self.openfile()
self.root.mainloop()
self.root.focus_set()
I got around it before by using the wm_withdraw() and wm_deiconify() functions to simply hide the child window, then close it after the program is complete. This fix is a little more difficult to apply in this case however. If anyone can shed some light on the cause of the problem I'd appreciate it.
Edit:
I've written up a runable code segment to show exactly what my issue is.
import os
from tkFileDialog import askopenfilename
from Tkinter import *
class Start:
def __init__(self):
self.root = Tk()
self.root.title('Binding Troubles')
menubar = Menu(self.root)
#add items and their commands to the menubar
filemenu = Menu(menubar, tearoff=0)
filemenu.add_command(label="Do work", command=self.do_work)
filemenu.add_command(label="Open File",command=self.openfile)
menubar.add_cascade(label="File", menu=filemenu)
#bind control-o to perform the do work function
self.root.bind("<Control-o>",self.flash_do_work)
self.root.bind("<Control-O>",self.flash_do_work)
#add the menubar to the GUI
self.root.config(menu=menubar)
#initially open a tkdialog to open a file
self.openfile()#comment out this line to make the bind work
self.root.focus()#also tried self.root.focus_set()
self.root.mainloop()
def flash_do_work(self,event):
#indirect tie to the do_work() function, I'm don't know a
#proper way to make functions handle calls from both events and non-events
self.do_work()
def openfile(self):
#gets current path
self.pathname = os.getcwd()
#Requests filename using a tkdialog
self.filename =askopenfilename(initialdir= self.pathname)
print self.filename
def do_work(self):
#placeholder for actual function; shows whether the bind is working or not
print "work"
Start()
The bind will work if self.openfile() is removed from __init__, and used only from the menu
Another Edit: I've updated the example again, giving a menu option to run the openfile() function. I noticed that if openfile() is called in __init__, the bind will not work. But if next the openfile function is called again, this time manually from the menu, the bind will start working again. Not exactly sure what to take from this. Also, my apologies for the post getting so long.
Change
self.openfile()
to
self.root.after(1, self.openfile)
This moves the call to askopenfilename into the main event loop. Having it outside the main event loop is somehow clobbering your event bindings.
I had this kind of problem a couple of times and it took quite a while until I found a solution I was comfortable with. As #Steven Rumbalski suggests I tried with delaying the application, which works but seems shaky.
Then I found the functions for waiting until something is complete, in this case wait_visibility(widget). This will delay execution until the widget is visible, which seems to be the thing to be waiting for. Try this:
self.root.wait_visibility(self.root) # Wait for root to be displayed
self.openfile()
Now; I'm not sure why this is so, and it seems that there may be differences depending on platform: Tkinter window event . This has nevertheless worked for me on Windows10 and Python 3.10.5.

Categories

Resources