If have this piece of code:
import Tkinter as tk
import tkFileDialog
menu = tk.Tk()
res = tkFileDialog.askopenfilename() # un-/comment this line
label = tk.Label(None, text="abc")
label.grid(row=0, column=0, sticky=tk.W)
entry = tk.Entry(None)
entry.grid(row=0, column=1, sticky=tk.EW)
res = menu.mainloop()
Note: the askopenfilename is just a dummy input. So Just close it to get to the (now blocked) main window of TK.
When I comment the askopenfilename everything works fine. But with the it, I can not enter data in the entry.
This only happens with Windoze environments. The askopenfilename seems to steal the focus for the main TK window. After clicking a totally different window and back again in the TK window, input is possible.
I've seen reports of this before, I think it's a known bug on windows. You need to let mainloop start before you open a dialog.
If you want the dialog to appear when the app first starts up you can use after or after_idle to have it run after mainloop starts up.
For example:
menu = tk.Tk()
...
def on_startup():
res = tkFileDialog.askopenfilename()
menu.after_idle(on_startup)
menu.mainloop()
If you don't want any other GUI code to execute until after the dialog, move all your code except for the creation of the root window and call to mainloop into on_startup or some other function.
For example:
def main(filename):
label = tk.Label(None, text="abc")
label.grid(row=0, column=0, sticky=tk.W)
entry = tk.Entry(None)
entry.grid(row=0, column=1, sticky=tk.EW)
def on_startup():
res = tkFileDialog.askopenfilename()
main(filename)
root = Tk()
root.after_idle(on_startup)
askopenfilenamehas it's own event loop. The programm stops, until you selected a filename, and continues afterwards.
Related
There have already been several topics on Python/Tkinter, but I did not find an answer in them for the issue described below.
The two Python scripts below are reduced to the bare essentials to keep it simple. The first one is a simple Tkinter window with a button, and the script needs to wait till the button is clicked:
from tkinter import *
windowItem1 = Tk()
windowItem1.title("Item1")
WaitState = IntVar()
def submit():
WaitState.set(1)
print("submitted")
button = Button(windowItem1, text="Submit", command=submit)
button.grid(column=0, row=1)
print("waiting...")
button.wait_variable(WaitState)
print("done waiting.")
windowItem1.mainloop()
This works fine, and we see the printout “done waiting” when the button is clicked.
The second script adds one level: we first have a menu window, and when clicking the select button of the first presented item, we have a new window opening with the same as above. However, when clicking the submit button, I don’t get the “Done waiting”. I’m stuck on the wait_variable.
from tkinter import *
windowMenu = Tk()
windowMenu.title("Menu")
def SelectItem1():
windowItem1 = Tk()
windowItem1.title("Item1")
WaitState = IntVar()
def submit():
WaitState.set(1)
print("submitted")
button = Button(windowItem1, text="Submit", command=submit)
button.grid(column=0, row=1)
print("waiting...")
button.wait_variable(WaitState)
print("done waiting")
lblItem1 = Label(windowMenu, text="Item 1 : ")
lblItem1.grid(column=0, row=0)
btnItem1 = Button(windowMenu, text="Select", command=SelectItem1)
btnItem1.grid(column=1, row=0)
windowMenu.mainloop()
Can you explain it?
Inside your SelectItem1 function, you do windowItem1 = Tk(). You shouldn't use Tk() to initialize multiple windows in your application, the way to think about Tk() is that it creates a specialized tkinter.Toplevel window that is considered to be the main window of your entire application. Creating multiple windows using Tk() means multiple main windows, and each one would need its own mainloop() invokation, which is... yikes.
Try this instead:
windowItem1 = Toplevel()
import tkinter as tk
from tkinter import filedialog, Text
from subprocess import call
import os
root = tk.Tk()
def buttonClick():
print('Button is clicked')
def openAgenda():
call("cd '/media/emilia/Linux/Programming/PycharmProjects/SmartschoolSelenium' && python3 SeleniumMain.py",
shell=True)
return
canvas = tk.Canvas(root, height=700, width=700, bg='#263D42')
canvas.pack()
frame = tk.Frame(root, bg='white')
frame.place(relwidth=0.8, relheight=0.8, relx=0.1, rely=0.1)
openFile = tk.Button(root, text='Open file', padx=10,
pady=5, fg="white", bg='#263D42', command=openAgenda)
openFile.pack()
root.mainloop()
the script it calls opens a new browser window, after finishing entering text in that window, it opens a new browser windows and loops.
meanwhile the tkinter button stays clicked, visually.
the reason your Tk GUI freezes is because you have everything running on 1 thread. The mainloop is haulted by the submit function call which must be taking a "long time", so you probably see "Not Responding" appear in your Tk window when you click the button. To fix this, you need spawn a separate thread for submit to run in, so that the mainloop can keep doing it's thing and keep your Tk window from freezing.
this is done using threading. Instead of your button directly calling submit, have the button call a function that starts a new thread which then starts submit. Then create another functions which checks on the status of the submit thread. You can add a status bar too
import tkinter as tk
from tkinter import filedialog, Text
from subprocess import call
import os
import threading
root = tk.Tk()
def buttonClick():
print('Button is clicked')
def openAgenda():
call("cd ' /media/emilia/Linux/Programming/PycharmProjects/SmartschoolSelenium' && python3 SeleniumMain.py",
shell=True)
canvas.update()
return
def start_Agenda_thread(event):
global Agenda_thread
Agenda_thread = threading.Thread(target=openAgenda)
Agenda_thread.daemon = True
Agenda_thread.start()
canvas = tk.Canvas(root, height=700, width=700, bg='#263D42')
canvas.pack()
frame = tk.Frame(root, bg='white')
frame.place(relwidth=0.8, relheight=0.8, relx=0.1, rely=0.1)
openFile = tk.Button(root, text='Open file', padx=10,
pady=5, fg="white", bg='#263D42', command=lambda:start_Agenda_thread(None))
openFile.pack()
root.mainloop()
Tkinter is single-threaded: it can only do one thing at a time. While the script is running, the GUI will be frozen. You'll need to do threading, multiprocessing, or find some other way to incorporate that other script in your GUI.
I am trying to click a tkinter button to open up a new tkinter window to execute a script within it all the way up to the end with a scroll bar if necessary. However, I have only succeeded this far in getting it to run in multitude of ways in a linux window and not within a tkinter window. Can someone help me with redirecting the output of this script into the toplevel window?
self.button_run = Button(self, text="RUN", width=16, command=self.callpy)
self.button_run.grid(row=25, column=0, columnspan=2, sticky=(W + E + N + S))
def run_robbot(self):
new = Toplevel(self)
new.geometry('500x200')
label = Message(new, textvariable=self.callpy, relief=RAISED)
label.pack()
def callpy(self):
pyprog = 'check_asim.robot'
call(['robot', pyprog])
In the snippet above, if I pass callpy to command in Button it runs the robot script in a linux window. If I replace it to call run_robbot which is what I want and expect, it just pops up a new window with a Message Box without running the same script passed to textvariable. I have tried Enter in place of Message Box as well.
I want callpy to be executed in Toplevel tkinter window at the click of the button. How do I do it? Any tkinter operator is fine as long as it confines to the tkinter window.
If you want to capture the output of the command, you should use subprocess.run(cmd,capture_output=True) instead. Below is an sample code:
import subprocess
from tkinter import *
class App(Tk):
def __init__(self):
Tk.__init__(self)
Button(self, text='Run', command=self.run_robot).pack()
def run_robot(self):
win = Toplevel(self)
win.wm_attributes('-topmost', True)
output = Text(win, width=80, height=20)
output.pack()
output.insert(END, 'Running ....')
output.update()
result = self.callpy()
output.delete(1.0, END)
output.insert(END, result)
def callpy(self):
pyprog = 'check_asim.robot'
return subprocess.run(['robot', pyprog], capture_output=True).stdout
App().mainloop()
I want to close two windows at the same time when user click the Start button, the new window will pop-up and when the user clicks the Exit button on the Second pop-up window than both the window should Close at a time.
I know that for a different window I have to create a separate function to exit windows But I want to close more than one window with a single click.
I'm using python 3.7!
import tkinter
def NewWindow():
def qExit():
root.destroy()
root = tkinter.Tk()
root.title("New Window")
newButton = tkinter.Button(root, text=" Click here to Exit:",
command=qExit)
newButton.pack()
root.geometry("300x200")
root.mainloop()
Window = tkinter.Tk()
Window.title("hello")
eButton = tkinter.Button(Window, text="Start", command=NewWindow)
eButton.pack()
Window.geometry("200x200")
Window.mainloop()
You shouldn't call tkinter.Tk() more than once in a tkinter application. Call Toplevel() if you want to create a new window.
You also generally don't need to call mainloop() more than once.
To close both the new window and the main window, you can pass the latter to the former when you create it, and then destroy() that in your qExit() function (as well as the new window itself).
Note I changed some of the function and variable names to conform more to the PEP 8 - Style Guide for Python Code guidelines.
import tkinter
def makeWindow(parent):
def qExit():
newWindow.destroy()
parent.destroy()
newWindow = tkinter.Toplevel()
newWindow.geometry("300x200")
newWindow.title("New Window")
newButton = tkinter.Button(newWindow, text=" Click here to Exit",
command=qExit)
newButton.pack()
root = tkinter.Tk()
root.title("hello")
eButton = tkinter.Button(root, text="Start", command=lambda: makeWindow(root))
eButton.pack()
root.geometry("200x200")
root.mainloop()
A simple solution would be to just do exit() to stop the program, which will close all windows. alternatively you can make a list of all open window objects and call destroy on all of them.
No need description
def qExit():
root.destroy()
Window.destroy()
When I make a button that closes the current window and opens another, the current window doesn't close.
from tkinter import *
root = Tk()
def new_window():
root.quit()
new_window = Tk()
new_window.mainloop()
Button(root, text="Create new window", command=new_window).pack()
root.mainloop()
(This isn't my program, it's just an example)
You should be able to do it like this:
import tkinter as tk
root = tk.Tk()
def new_window():
root = tk.Tk()
test = tk.Button(root, text="Create new window", command= lambda:[root.destroy(), new_window()]).pack()
root.mainloop()
test = tk.Button(root, text="Create new window", command= lambda:[root.destroy(), new_window()]).pack()
root.mainloop()
This will literally keep opening the exact same window with a button. The lambda allows you to call multiple functions. By calling .destroy() on your root window, it destroys your window, but doesn’t stop the program. Then you create a new root window with your function.
You can use this technique on your actual script.