Tkinter: how to open a Toplevel window from another script - python

I'm a newbie in programming and I'm struggling a bit (a lot actually) with my first "program". Here's what I did:
since I want the code not to be too long, I want to split it in two files. In the first one I call the main window, where I put a button. If I click this button, I run the second script, that should hide the main window and make appear a new window (toplevel). The problem is that if I click the button nothing happens.
A thing I noticed is that if in the second script I add the strings "if name=="main": main.mainloop()", then this new window does appear, but a second main window appears too, as if I re-runned the main code.
Script one:
from tkinter import *
import os
main=Tk()
def sw():
os.system("python3 ./script_two.py")
my_button=Button(main,text="NEW",command=sw)
my_button.grid(row=0,column=0)
if__name__="__main__":
main.mainloop()
Script two:
from tkinter import *
import os
import script_one
script_one.main.withdraw()
second_window=Toplevel()
def Close():
second_window.destroy()
script_one.main.deiconify()
second_button = Button(second_window,text="Exit",command=Close)
second_button.grid(row=0,column=0)
If I put together the scripts (inserting the code of "script_two" in 'def sw()'), I get exactly what I want, but if I split them as showed, I get the problem.
Obviously for a code this short it would be easier and faster to write a single script, what I actually have is a longer code, but the problem is the same.
Any idea? Thank you very much

Related

tkinter checkbutton not displaying correct value when class imported from other file

I have a main program which does some cool stuff and I am currently setting up a 'settings editor' to let the user change some GUI related stuff and default values. It reads values from a text file, which is read in correctly and saves them to a dictionary self.propertiesDict. Some of the options are on/off switches, so I use checkbuttons for them. What is puzzling me is following behavior: the code works perfectly fine, when I execute the settingsEditor.py (the script creating the settings window) directly. All the checkbuttons are set to active / True. However, when I include my settingsEditor in my main program and call it, it creates fine but all the checkbuttons show the wrong value: False. I read a lot of topics here to find an answer, but I think I avoided the most common errors:
I use the tk variables
tk variables are created and set prior to the buttons
variables are not only in local scope (prefixed self.)
As you can see, I tried with an IntVar and a BooleanVar, but neither is working correctly. Something else is strange, when I use ttk.checkbuttons, I get the issue described here. I use Visual Studio for debugging and I can't see any difference in the process when going trough line by line, except for the wrong display result. I am happy for any suggestion. Sorry for not providing a full MWE, I will do, if nobody can help me from this here.
settingsEditor.py
import tkinter as tk
from tkinter import ttk
...
class mySettingsEditor:
def __init__(self):
...
def createGUI(self):
# Show main options on startup on/off
self.showOptionsVar = tk.IntVar()
self.showOptionsVar.set(str2int(self.propertiesDict['showMainOptionsExpanded']))
print(self.showOptionsVar.get())
self.checkBtn1 = tk.Checkbutton(Frame, text='Main Options Section', variable=self.showOptionsVar)
self.checkBtn1.grid(column=0,row=2)
# Show main STL section on startup on/off
self.showMainSTLVar = tk.BooleanVar()
self.showMainSTLVar.set(str2bool(self.propertiesDict['showMainSTLSectionExpanded']))
print(self.showMainSTLVar.get())
self.checkBtn2 = tk.Checkbutton(Frame, text='Main STL Section', variable=self.showMainSTLVar)
self.checkBtn2.grid(column=0,row=3)
main.py
from settingsEditor import mySettingsEditor
...
settEditor = mySettingsEditor()
This is how it looks in the GUI when executed separately (terminal with print output to the left):
Thats the result when I add it in main.py. The boxes are unchecked, but .get() tells me the values are correctly assigned to the tk variables.
As suggested by jasonharper, switching to Toplevel() for the child windows fixed the issue. Thanks alot!

Tkinter w/ Python 2.7.10: Why does my GUI ignore event callbacks until a second event occurs?

Currently, I'm working with Python 2.7.10, specifically the Tkinter module, to create a GUI (I'm relatively new to Tkinter). As such, while my goal is to have the GUI print figures with matplotlib, I'm currently just testing the basics with print() to my iPython console (in Spyder). Anyways, a simple version of my code follows:
import Tkinter as tk
def run_file(i,f):
print 'running...'+`i`+', '+`f`
def select_all():
print('run')
run_file(1,97)
root = tk.Tk()
root.geometry('700x100')
singleframe = tk.Frame(root, bd = 5)
singleframe.pack()
button_fig = tk.Button(singleframe, text='Display All Figures', fg =
'black', padx = 2, command = select_all)
button_fig.pack(side = tk.LEFT)
root.mainloop()
Here's my problem:
Whenever I click my 'Display All Figures' button, the console remains blank 1, however, if I click it a second time, it displays 'run', and 'running...1, 97' (for my first click), and then displays 'run' (for the second click) 2, waiting for me to click it a third time (where it will display the second click's 'running...1, 97' result along with the third click's 'run'). Finally, if I close the program, it will display the 'running...1, 97' from my third click. (My apologies for not including more pictures; somehow I apparently have reputation below 10)
I've been trying to search the web for similar problems; several posts suggest that problems like this occur when too many calculations are occurring, so the GUI freezes up, but my function is very simple, so that seems unlikely; still others suggest the after() function, but I don't see how creating timed delays would help either. Does anyone have any suggestions as to how to solve this problem?

Opening a new Tkinter window while continuing to run a loop in another

I am working on a large program that opens new windows from a desktop widget. The desktop widget has a 'ticker' style label that displays a piece of text representing an iteration through a list. My problem is when I first wrote the program I called mainloop() with each new window I opened. The result was the new window and program would run as designed, but the ticker would freeze. Even upon closing the newly created window, the ticker would not restart. So I removed the mainloop() line. The result of this is the ticker continues to run and I can work within the new window, but everything is soooo laggy. I suspect this has something to do with the after() method?
Attached is a test code that I am using to try to sort this out before applying the correct code to my program. And I'm sure you can tell by reading the code, but I am self taught and an absolute newb, so please dumb down the explanations if possible. Thank so much!
from tkinter import *
def new_window():
nw = Tk()
item = Text(nw)
item.grid()
L = [1, 2, 3, 4, 5]
root = Tk()
Button(root, text = 'Open', command = new_window).grid(row = 1)
while True:
for i in L:
num = Label(root, text = i)
num.grid(row = 0)
root.after(2500)
num.update()
root.mainloop()
A tkinter application should always have exactly one instance ofTk, and you should call mainloop exactly once. If you have more than one instance the program will not likely work the way you expect. It's possible to make it work, but unless you understand exactly what is happening under the hood you should stick to this rule of thumb.
If you need more windows, create instances of Toplevel. You should not call mainloop for each extra window.
Also, you shouldn't have an infinite loop where you call after the way that you do. mainloop is already an infinite loop, you don't need another. There are several examples on this website of using after to call a function at regular intervals without creating a separate loop.

Event handler for left-click on Notebook tabs in Tkinter

In Tkinter, I want to make it so when I click one or more times on an already open/selected tab of a Notebook object so that it does not take the focus (or so that it gives the focus back to the Text widget in the window).
How do I do this?
If there's an event handler for clicking on tabs, that would more than suffice.
Also, if there's an ability to make it so widgets will take events, but not take the focus, that would be great.
I already know about the virtual event <<NotebookTabChanged>>. However, I'm talking about when you click on an already selected tab. So, the tab isn't changing. I tried just binding the <Button-1> event to the Notebook widget, but it didn't do anything.
Since making Notebooks isn't common knowledge to everyone who uses Tkinter, here's an example of how to make a minimal Notebook with tabs. I don't know why the text is cut off in the final tab here, though (but it's not in my full code):
from tkinter import *;
from tkinter.ttk import *; #Notebook comes from this
class Editor:
def __init__(self):
self.tk=Tk();
self.tabs=0;
self.frame=Frame(self.tk);
self.nb=Notebook(self.frame);
self.frame.pack();
for x in range(5):
self.add_tab();
self.nb.pack();
self.tk.mainloop();
def add_tab(self):
newTabFrame=Frame(self.nb);
text=Text(newTabFrame); #Just a sample Text widget to go in each tab
text.pack();
if self.tabs==0:
self.nb.add(newTabFrame, text=str(self.tabs), compound=TOP);
else:
self.nb.add(newTabFrame, text=str(self.tabs));
self.tabs+=1;
if __name__ == '__main__':
e=Editor();
try:
e.tk.destroy();
except:
pass;
Okay, obviously I was doing something wrong, because when I tested out what I thought I already tested out on my larger code on the example code I used in my question, it actually worked. Then, I discovered that this works on my larger code, too. Huh. I think I must have been using bind_all instead of bind (whereas I thought I had attempted both), because that really doesn't work. EDIT: Actually, I was attempting to use it without return "break" and while that partially works, it doesn't work after you click on a new tab when you directly click on it again. Plus, setting the focus in my answer here with return "break" won't solve the problem (although it does answer my question), because it will set the focus on the current tab before it switches tabs. I'll figure something out, I'm sure. EDIT: Changing Button-1 to ButtonRelease-1 (or some such) fixes the problem. The Motion binding with the same handler, has potential, too, but if you have Toplevel windows popping up, this will take the focus from them if you move the mouse pointer over your tab.
Here's the working code:
from tkinter import *;
from tkinter.ttk import *; #Notebook comes from this
from tkinter.messagebox import *;
class Editor:
def __init__(self):
self.tk=Tk();
self.tabs=0;
self.frame=Frame(self.tk);
self.nb=Notebook(self.frame);
self.nb.bind("<ButtonRelease-1>", self.test);
self.frame.pack();
for x in range(5):
self.add_tab();
self.nb.pack();
self.tk.mainloop();
def add_tab(self):
newTabFrame=Frame(self.nb);
text=Text(newTabFrame);
text.pack();
if self.tabs==0:
self.nb.add(newTabFrame, text=str(self.tabs), compound=TOP);
else:
self.nb.add(newTabFrame, text=str(self.tabs));
self.tabs+=1;
def test(self, event=None):
showinfo("Success", "It works!");
#Imagine the code for selecting the text widget is here.
return "break";
if __name__ == '__main__':
e=Editor();
try:
e.tk.destroy();
except:
pass;

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