The following code exhibits a problem I do not understand:
from Tkinter import *
root = Tk()
cheese_var = IntVar()
parrot_var = IntVar(value=1)
check_menu = Menu(tearoff=0)
check_menu.add_checkbutton(label="Cheese", variable=cheese_var)
check_menu.add_checkbutton(label="Parrot", variable=parrot_var)
count = 0
class Top():
def __init__(self):
global count
count += 1
self.tl = Toplevel(root)
Label(self.tl, text="Window " + str(count)).pack()
self.mb = Menubutton(self.tl, text="Push Me", bg='pink')
self.menu = Menu(self.mb, tearoff=0)
self.menu.add_cascade(label="Choices", menu=check_menu)
self.menu.add_command(label="New Window", command=new_top)
self.mb.config(menu=self.menu)
self.mb.pack()
def new_top():
Top()
Top()
root.mainloop()
The menu brought up by the menu button in the created top level window initially behaves as expected. Clicking on the New Window command there creates a new such window, which also behaves as expected. Indeed, as long as you keep creating new top level windows, everything continues to work as expected. However, once you delete (close) any one of those windows, then, in a subsequently created new window, the Choices cascade on the new menu is not functional. (It is still OK in the windows created before the closing of one.)
The situation in which I initially encountered this symptom was much more complex, but I was able to simplify it down to the above example which exhibits the issue. I have discovered that I can avoid the problem by having each instance of Top create its own check_menu as an attribute; but I do not understand why this should be necessary. Please point me the way if there is one to avoid the problem without such replication of a cascade menu used in multiple windows.
Unfortunately, I don't think it is possible to do what you want. I'll try to explain as best as I can.
When you first run the script, check_menu is created and works fine for the first window. As you create more windows, check_menu is simply shared between them. However, when you close one of them, check_menu (and everything under it) is destroyed. So, when you create a new window after that, check_menu no longer exists and it doesn't show.
However, the script doesn't throw an error because, for some reason, Tkinter allows you to assign menus to things that aren't menus. Believe it or not, none of the following code:
self.menu.add_cascade(label="Choices", menu=None)
self.menu.add_cascade(label="Choices", menu=1)
self.menu.add_cascade(label="Choices", menu="")
will break the script. Each line simply does nothing but create an empty cascade "Choices".
That is basically what is happening. After closing one window, check_menu and everything under it is destroyed. Yet, Tkinter doesn't throw an error but instead assigns a menu to something that is no longer a menu (as far as what it is assigning the menu to, I believe it is using the old instance of check_menu, which was destroyed).
To solve this problem, recreate check_menu and everything under it each time you call Top. In other words, put the code for check_menu (and its options) in the __init__ method of Top. That way, each time Top is called, check_menu will exist.
Hope this helps (and that I explained it sufficiently :)
Related
I am trying to implement a little application that logs in a user and the user after log in can add/update/delete contents to/of a textfile. Here's a rough sketch of the code I have so far:
class admin():
def __init__(self):
self.app = Tk()
.
self.name=StringVar()
update = Button(self.app,....,command=self.update)
.
.
.
def update():
#Function to take different entries using Entry widget of tkinter in another window
anotherapp = Tk()
nameentry = Entry(anotherapp,textvariable = self.name)
submitbutton = Button(anotherapp,....,command=submit)
.
.
def submit():
#Opens a file and adds entries to a textfile.
namevar = self.name.get()
# code to append to file
To explain the above, I have a class admin. Creating an instance of this class would open a window with buttons that say create, update, delete and so on. On clicking one of the buttons, the respective functions (defined in the same class) would be called (I use lambda: in case the function has arguments, but so far, it doesn't).
So in the code I've mentioned, say I click on the Update button, it should call the update function which opens another window and takes the text that has to be updated in the text file (via the Entry widget). So according to the code it'll update the value of name. On clicking submit, the function submit uses .get() to get the string value of name, and appends it to the text file.
The code executes with no error but it DOES NOT read the input from the user. Blank lines get appended to my textfile when I click on the submit button.
Now I don't understand why this isn't working. The name variable is defined in init and can be updated by the functions of the same class. I have tried a lot of things to make this work, including adding parameters in the button commands, defining name elsewhere, etc. Even though I've solved the error, I get the same result: the file gets appended with blank lines. I've also tried to make name a class variable but that doesn't work since it is declared using StringVar() which needs it to be part of a tkinter window. I think I've also tried nesting submit function inside the update function, but I don't know why that didn't work out or if I hadn't implemented it correctly.
I don't know if it has to do with the working of tkinter's StringVar() and .get() function.
I can't think of any other way to implement the situation I have at hand. I am open to taking suggestions on changing the structure of the code, as long as it is not something major major and manages to achieve the functionality that I've described.
I am sorry if I've missed something basic, cause I've only started trying out OOP in python recently. And thanks in advance for any help.
I created a Frame, gave it a menubar. Works just fine. The purpose of the entry in the menubar is to open a new frame, in which u can change some settings. The creation of the new Window works also. However I can't create widgets on the new created window. I tried it with a Button and got a
TclError: can't invoke "button" command: application has been destroyed
I tried to google it and found Cannot invoke button command: application has been destroyed which didn't quite helped me.
Further I found a solution were u have to create a parent class (which inherrits from Frame) and than create all other Frames within it, but on the first view it looked pretty complicated. Especially because the creation of the second window seems to work in the first place.
I know this is probably a really basic question, so thanks in advance for your time
def perfSettings():
perfFrame = Tk(className=" Performanz Einstellungen")
perfFrame.configure(bg='#F2F2F2')
perfFrame.geometry("300x300")
perfFrame.mainloop()
btn = Button(master=perfFrame, text='Speichern', command=myPerfSettingValue.getValues, width=37)
btn.pack()
# Button(perfFrame, text='Abbrechen', command=perfFrame.destroy, width=37).grid(row=0 ,column=1 )
class perfSettingsValue:
def __init__(self):
self.bvhSteps = 0
def getValues(self):
pass
#Hauptfenster
root = Tk(className="BoneMapping & SkeletonEstimation")
root.configure(bg='#F2F2F2')
root.geometry("1300x600")
myPerfSettingValue = perfSettingsValue()
menubar = Menu(root)
sdmenu = Menu(menubar, tearoff=0)
sdmenu.add_command(label="Performanz", command=perfSettings)
menubar.add_cascade(label='Einstellungen',menu=sdmenu)
root.config(menu=menubar)
The key problem here is that you are trying to add a button after starting the mainloop which effectively blocks the execution of the program. The error you are getting is because the line that adds the button gets executed after the window has been closed.
Your problem will be solved if you modify your function like this:
def perfSettings():
perfFrame = Tk(className=" Performanz Einstellungen")
perfFrame.configure(bg='#F2F2F2')
perfFrame.geometry("300x300")
btn = Button(master=perfFrame, text='Speichern', command=myPerfSettingValue.getValues, width=37)
btn.pack()
perfFrame.mainloop()
This is not the only problem though. Instead of creating a new instance of Tk, you should create a new Toplevel instance, which will, in your case, act just as a Tk instance, but have a lot less tendency to cause trouble.
Finally, you should consider reading on the object oriented approach to designing tkinter applications. There are far too many variants of that to be appropriately elaborated here but I certainly recommend you take the effort to learn to use one of them. It will make your code more comprehensible and maintainable. My usual approach is to create a class that inherits from Toplevel or Tk for every type of window I am going to use.
This is my first Question here at StackOverflow, so please be patient with me if some info isn't present or I missed something important, but anyways i'll do my best :)
Recently I started to code in Python2.7, so I'm not very good at it. While playing with PyGtk, PyGObject, Glade, etc I found something particular about switches (Haven't tried with any other widget, so I don't know if it happens somewhere else. Most likely it doesn't, I hope...)
I made a very basic GUI with a single "window" plus a "switch" using Glade
My objective was to deactivate switch after user tried to activate it if some exeption raised up before, something like:
Activate it --> * Found error --> * Deactivate it
I made some code, and after a while, I noted that THIS piece of code created a loop-like block, blocking GUI's window afterwards:
builder = Gtk.Builder()
window1 = builder.get_object('window')
switchie = builder.get_object('switchie')
switchie.set_active(False)
def Hi(switch, active):
print switchie.get_active()
switchie.set_active(not switchie.get_active())
switchie.connect("""notify::active""", Hi)
window1.set_position(Gtk.WindowPosition.CENTER)
window1.connect("delete-event", Gtk.main_quit)
window1.show_all()
If i'm right, "switchie.connect" links "switchie" object with "Hi" func whenever "switchie" gets clicked.
But if I execute this and try to turn switch on, GUI hangs up. I did try to execute this via script & command-line and adding the "print switch state", resulting in an endless loop (True & False)
I tried with many other funcs I made, but neither of them could solve this issue. In fact, this is the "essence" of all the other funcs I made.
Why does this happen?
Where's the loop?
Am I wrong in some line?
Help is appreciated!
(If you need to see the rest of my faulty funcs, just ask for 'em, but I don't think they'll help...)
You want to hook up the switch like this:
switchie.connect("""activate""", Hi)
This will only get called once for every time it is clicked. What you were doing is hooking up to the signal after it changed, so it was constantly changing, and never catching up. You will also want to change
def Hi(switch, active):
to
def Hi(switch, active = None):
for keyboard support.
I'm trying to remove a Tkinter progress bar widget from an active window (after the GUI window using Tkinter has been initialized). I'm using a Tkinter Frame for my window. I've initialized the progress bar as pb, as below.
pb = ttk.Progressbar(root,orient ="horizontal",length = 540, mode ="determinate")
And then I've tried two different methods to get rid of the progress bar. The line below causes the window to freeze and stop responding when I try to use it after the GUI is initialized.
pb.pack_forget()
The line below causes only a middle section of the progress bar to disappear, but you can still see the two sides of it.
pb.destroy()
Is there any way I could get this widget to disappear after the Frame has been initialized?
The specific answer to your question is that pack_forget, grid_forget or grid_remove are what you want if you want to make a widget temporarily invisible. Which one you choose depends on if you're using grid or pack, and whether or not you want grid to remember where it was so you can later put it back in the same spot.
destroy is what you want to call if you want to literally destroy the widget.
When used properly, none of those methods will cause your program to freeze. Without seeing your code, it's impossible to know what the root cause of the problem is.
Sorry for my bad English.
This code worked for me. I simply follow Oakley's instruction.
def progressBar(*args, **kwargs):
def progress(currentValue):
progressbar["value"] = currentValue
maxValue = 100
progressbar = ttk.Progressbar((kwargs), orient="horizontal", length=150, mode="determinate", takefocus=True)
progressbar.pack(side=tk.BOTTOM)
currentValue = 0
progressbar["value"] = currentValue
progressbar["maximum"] = maxValue
divisions = 10
for i in range(divisions):
currentValue = currentValue + 10
progressbar.after(500, progress(currentValue))
progressbar.update() # Force an update of the GUI
progressbar.destroy()
Hence I simply tried progressbar.destroy() outside of the loader loop. So after
complete the loading, it will disappear from the main App window.
Thank you, Bryan Oakley sir.
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.