Checkbuttons with Multiple Tkinter Windows - python

Specs:
Python2.7.1
Tkinter (Tk version 8.5)
Windows7
IDLE 2.7.1
I'm coding a program that 'spawns' two windows, withdraws both, destroys one and then deiconifies the other (which then enters a mainloop).
This arrangement is interfering with a Checkbutton on the remaining window.
eg:
temp = Tk()
temp.withdraw()
root = Tk()
root.withdraw()
temp.destroy()
root.mainloop()
(It seems unusual, but it is set up this way so that the 'temp' window will display the problems that arose, during building of the root window).
However,
it seems that as soon as a single program deals with two Tkinter windows,
functionality of a Checkbutton (in root) goes out the window.
def ClickAButton():
print Toggle.get()
Toggle = IntVar()
Checkbutton(root, text = "Me is broke", variable = Toggle).pack()
ClickAButton
Toggle.get() should return a 1 if the Checkbutton is ticked, otherwise a 0.
However, since adding the new window, Toggle.get always returns a 0.
(I've tried reformatting code {this brings up strange erros of it's own},
renaming variables, etc.
The Checkbutton works just fine without the 'temp' window.
The 'temp' window is destroyed before the Checkbutton is even assigned, packed,
or 'root' even enters a mainloop!)
Entire eg:
temp = Tk()
temp.withdraw()
root = Tk()
root.withdraw()
if 'certain condition':
root.destroy()
temp.deiconify()
temp.mainloop()
else:
temp.destroy()
Toggle = IntVar()
Checkbutton(root, text = "Why I only return 0?", variable = Toggle).pack()
root.deiconify()
root.mainloop()
For some reason,
the Checkbutton is always returns 0, even when checked.
I suspect it's a multi-threading issue with Tkinter.
Is there anything at all I can do here?
(The actual coding is HUGE. I'm not eager to switch it all to another GUI module)
:|
Greatly appreciated!
(I only started programming the start of this year.
Please forgive me if I've made some horribly noobish mistake!)

Tkinter isn't designed to have two root windows. I'm amazed your code works at all. This has nothing to do with multi-threading -- Tkinter is single threaded and you don't appear to be creating any new threads (though if you are, that might contribute to the problem)/
You need to create a single root window with a single mainloop. If you need another window, create a Toplevel window -- that's precisely what that widget is for.

Related

Python tkinter Checkbutton not appearing selected in Topframe window

When programming with tkinter I have found a very strange behaviour of the Checkbutton widget. I have re-created the bug with the code below:
import tkinter
from tkinter import *
def displayWelcomeScreen(root):
root2 = Toplevel(root)
root2.geometry('600x380')
root2.focus_set()
Checked = IntVar()
CheckButton1 = Checkbutton(root2, variable=Checked)
CheckButton1.place(relx=0.5, rely=0.5, anchor=CENTER)
CheckButton1.select()
# Create a dummy button that makes the Checkbutton appear checked to the user
#Button(root2, command= lambda event: Checked.get())
root = Tk()
root.geometry('700x400')
displayWelcomeScreen(root)
root.mainloop()
When a new window is created with Toplevel(root) and I put a Checkbutton inside it, it does not appear checked to the user even though I use the .select() method.
However, when I create a dummy button whose command mentions the IntVar associated with my Checkbutton, somehow it is initialised as checked properly. It's almost as if the compiler checks whether the Checkbutton will be useful and decides based on that whether it will display it as selected or not.
EDIT: The Checkbutton is definitely checked under the hood because if I run print(Checked.get()) before and after the CheckButton1.select() command, the value is changed, it just doesn't appear to the user.
Does anyone know why this happens?
EDIT 2: Thanks to jasonharper's explanation, I have added the line CheckButton1.intvar = Checked and it worked without needing the dummy button. When the function went out of scope, the Checked variable got lost so the Checkbutton had nowhere to store its state, therefore we needed to keep a reference to it so it didn't disappear.

Tkinter menu bindings and accelerators

I've come across a weird problem and I can't work out what is happening. I am working on a Tkinter application which utilises a menu. I have found that while creating the menu, in which all items have accelerators, some items require additional bindings to make the accelerators work (unless the menu bars are already selected) and some don't.
When an additional binding is required, I have a problem with double entry key strokes. For example, if I open a Toplevel window, I get double entries every time I type a character in an Entry box, both in the Toplevel and in the main window. This only happens if the menu item is called via the key command.
Most of the time this is not a problem, although I'd really like to know what the underlying cause is because it just seems wrong, but this particularly came to my attention recently when I implemented the built-in OS X Preferences menu, using the following code:
self.window.createcommand('::tk::mac::ShowPreferences', self._settings)
Now when I call the settings function from the build-in Preferences key command Command-,, which instantiates a new Toplevel window, this double entry is what happens. It does not happen if I navigate to the menu and open it with the mouse.
The example below recreates the problem for me. The menu bar is not strictly necessary, but the problem occurs with both the menu bar and the OS X built-in Preferences item. Interestingly, 'Settings A' which does not require a binding reproduces the problem, but 'Settings B', which does require a binding, works fine. And again, only with key commands.
import Tkinter
def settings(event = None):
top = Tkinter.Toplevel()
Tkinter.Entry(top).pack()
top.mainloop()
root = Tkinter.Tk()
root.createcommand('::tk::mac::ShowPreferences', settings)
menuBar = Tkinter.Menu(root)
fileMenu = Tkinter.Menu(menuBar)
fileMenu.add_command(label = 'Settings A', accelerator = 'Command-Shift-a', command = settings) # Does not require binding
fileMenu.add_command(label = 'Settings B', accelerator = 'Command-b', command = settings) # Requires binding
menuBar.add_cascade(label = 'File', menu = fileMenu)
root.config(menu = menuBar)
root.bind('<Command-b>', settings)
Tkinter.Entry(root).pack()
root.mainloop()
I suspected it may be a computer issue but I have tried it on another machine and I get the same result. Does anybody have any idea what is happening here and how I can prevent it?
In case anybody's interested, I think I've got to the bottom of this. I believe it was caused by the version of tkinter I had. Today I updated to Python 3 from the OS X bundled version 2.7, and the problem remained. Then I updated tkinter to ActiveTcl 8.5.18.0 and the problem seems to have disappeared.
IDLE and tkinter with Tcl/Tk on macOS

Remove window title bar in TKinter Python

How do I remove the title bar from a Toplevel() window in Tkinter.
Right now I for my main I have
self.master.title("Subtest")
self.master.geometry("400x200")
self.alertwindow()
Label(self.master,textvariable=self.connected,height=4).grid(row=0,column=0)
Button(self.master,text="Monitor",command= lambda: self.startnewthread(1),width=10).grid(row=6,column=1)
Button(self.master,text="Quit",command=self.haltprogram).grid(row=6,column=0)
And for my alert window function I have
def alertwindow(self):
self.listbox=Listbox(Toplevel(self.master,width=150).overrideredirect(True),width=150).pack)
I was wanting the program to open up a root window, and then a toplevel listbox without a title bar; however, the only thing the program is doing right now is freezing, and when I remove the .overrideredirect(True), the program launches two listbox windows. How can I have the program open only one listbox without a title bar on windows? Thanks
Looking at this line
self.listbox=Listbox(Toplevel(self.master,width=150).overrideredirect(True),width=150).pack)
It's pretty clear you're trying to do WAY too much on 1 line. (Your parenthesis don't even match). Let's break it up, shall we?
new_top = Toplevel(self.master,width=150)
new_top.overrideredirect(True)
self.listbox = Listbox(new_top,width=150)
self.listbox.pack()
Also note that you seem to be using .grid and .pack -- Generally that's ill advised and Tkinter will happily spend all of eternity trying to negotiate a proper placement of a widget when you try to use them together.
My guess about what's happening:
your actual code has properly balanced parenthesis so there is no SyntaxError
Toplevel.overrideredirct returns None
Listbox sees None as the parent widget and substitutes the root widget (Tk)
Then you're using .grid and .pack both on the root widget which causes your program to hang.

tk destroy() and grid_forget() don't always work

i'm hoping anyone can help me out here. i'm having an issue with a tkinter gui i built. the issue only happens in windows. My GUI creates a results frame with some labels in it, when it's time to calculate something else, the user clicks on the "newPort" button and that button is supposed to remove the results frame and set to False some instance attributes internal to the calculation. The issue i'm having, which is apparent only in windows is that sometimes the results frame, and its descendant labels don't disappear every time. Sometimes they do, sometimes they don't. The instance variable is correctly set to False but the widgets are still visible on the main GUI. The GUI also contains a couple checkboxes and radiobuttons but they don't impact the creation of the results frame nor its expected destruction. I have not been able to pin point a pattern of actions the user takes before clicking on the newPort button which causes the frame and labels to not get destroyed. This happens when i freeze my app with py2exe, as well as running the app from the python interpreter within the eclipse IDE. I have not tried running the app from the python interpreter directly (i.e. without the IDE) and this problem does not happen on my Mac when i run the app using the eclipse python interpreter. Thanks very much all! My code looks like this:
import Tkinter as TK
class widget(object):
def __init__(self,parent=None):
self.parent = TK.Frame(parent)
self.parent.grid()
self.frame = TK.Frame(self.parent)
self.frame.grid()
newLedger = TK.Button(self.parent,command=self.newPort).grid()
self.calcButton = TK.Button(self.frame,command=self.showResults)
self.calcButton.grid()
self.calcVariable = True
def newPort(self):
self.calcVariable = False
try:
self.second.grid_forget()
self.first.grid_forget()
self.resultsFrame.grid_forget()
self.second.destroy()
self.first.destroy()
self.resultsFrame.destroy()
except:
raise
self.frame.update_idletasks()
def showResults(self):
self.resultsFrame = TK.Frame(self.frame)
self.resultsFrame.grid()
self.first = TK.Label(self.resultsFrame,text='first')
self.first.grid()
self.second = TK.Label(self.resultsFrame,text='second')
self.second.grid()
if __name__ == '__main__':
root = TK.Tk()
obj = widget(root)
root.mainloop()
You don't need to destroy or call grid_forget on the labels, and you don't need to call grid_forget on the resultsFrame; when you destroy the resultsFrame it will cause all off its children to be destroyed, and when these widgets are destroyed they will no longer be managed by grid.
The only way I can get widgets to not be destroyed is if I click on the "calc" button twice in a row without clicking on the "new" button in-between. I'm doing this by running your program from the command line.

Determining what tkinter window is currently on top

I have written an application in python 2.7 and tkinter. I created a tool bar with several buttons that open up respective top windows that display various options. I used ttk.Checkbutton with the 'toolbutton' style as an indicator to show whether the option windows are open or closed.
The problem is that the option windows will go to the back if another window is selected. Currently, if one selects the toolbutton again, the option window will close. However, I only want to close the window if it is on top. If the option window is not on top, I want the window to moved to the front.
Some of the code I have working:
class MainWindow:
def __init__(self,application):
self.mainframe=tk.Frame(application)
application.geometry("900x600+30+30")
self.otherOptionsSelect=tk.IntVar()
self.otherOptions_Button=ttk.Checkbutton(application,style='Toolbutton',variable=self.otherOptionsSelect,
onvalue=1, offvalue=0,image=self.optionsIcon, command=self.otherOptions)
def otherOptions(self):
if self.otherOptionsSelect.get()==0:
self.otherOptions.destroy()
return
self.otherOptions=tk.Toplevel()
self.otherOptions.title("IsoSurface Options")
self.otherOptions.geometry("200x165+"+str(int(application.winfo_x())+555)+"+"+str(int(application.winfo_y())+230))
self.otherOptApply_button=ttk.Button(self.otherOptions,text="Apply",command=self.showFrame)
self.otherOptApply_button.place(x=20,y=80,width=50,height=30)
self.otherOptClose_button=ttk.Button(self.otherOptions,text="Close",command=self.otherOptionsClose)
self.otherOptClose_button.place(x=80,y=80,width=50,height=30)
def otherOptionsClose(self):
self.otherOptionsSelect.set(0)
self.otherOptions.destroy()
Here is a picture of the entire application I have written:
In the above image, each window has their respective ttk.checkbutton. At the moment, toggling the checkbutton either opens or closes the window. However, what I really want it to do is close the window if the window is in front of the application, or bring the window to the front if it is behind the application.
Hopefully this clears some things up.
Thanks in advance!
It is in fact possible to check stacking order of windows. Using Tkinter, you have to do some funny tcl evals to get at the information. I found the answer at TkDoc in the section on Windows and Dialogs, scroll down until you get to "Stacking Order". The code baffled me until I started playing around with it interactively. My test code was:
import Tkinter as tk
root = tk.Tk()
root.title('root')
one = tk.Toplevel(root)
one.title('one')
two = tk.Toplevel(root)
two.title('two')
I then manipulated the windows so that two was on top, one under that and root below them all. In that configuration, the following weirdness can tell you relative layering of windows:
root.tk.eval('wm stackorder '+str(two)+' isabove '+str(root))
returns 1, meaning "Yes, window two is above window root." While the following:
root.tk.eval('wm stackorder '+str(root)+' isabove '+str(two))
returns 0, meaning "No, window root is not above window two." You can also use the command:
root.tk.eval('wm stackorder '+str(root))
Which gives back the full window stacking order in the form of a weird string something like this:
'. .68400520L .68401032L'
Which starts to make sense when you run the commands:
str(root)
str(one)
str(two)
and figure out that root has the internal name '.', one is '.68400520L' and two is '.68401032L'. You read the output of root.tk.eval('wm stackorder '+str(root)) backwards so it's saying two is on top, one is under that and root is below both.

Categories