How do you focus a window after launching another application in tkinter? - python

In my app, I have a callback that simply opens a file. The problem is that once python opens that file, my application loses focus. This behavior is going to really slow down my process. Here is an example of my issue:
import os
from tkinter import *
class App(Frame):
def __init__(self, *args, **kwargs):
Frame.__init__(self, *args, **kwargs)
Button(self, text='Open File', bg=self['bg'],
command=lambda: self.open_file('path_to_movie_or_PDF_or_anything_that_launches_application'),
relief=GROOVE).pack(padx=10, pady=10)
self.entry_field = Entry(self, bg=self['bg'], width=20)
self.entry_field.pack(padx=10, pady=10)
self.entry_field.focus()
def open_file(self, some_path):
os.startfile(some_path)
self.handle_focus()
def handle_focus(self):
# ATTEMPT 1
# self.master.after(1, lambda: self.master.focus_force())
# self.entry_field.focus()
# ATTEMPT 2
# self.master.attributes("-topmost", True)
# self.master.lift()
# self.entry_field.focus()
# ATTEMPT 3
# self.master.focus_set()
# self.entry_field.focus_set()
# ATTEMPT 4
# self.master.focus_force()
# self.master.lift()
# self.master.update()
# self.entry_field.focus()
pass
if __name__ == '__main__':
root = Tk()
root.config(bg='white')
App(root, bg='white').pack()
root.mainloop()
I am trying to open the file with the push button, then immediately turn the focus to the entry bar. How can this be achieved?
I have already looked to Tkinter main window focus for the answer, but focus_force() is not fixing the issue although it's function is exactly what I need. The documentation for focus_force() states that "Direct input focus to this widget even if the application does not have the focus. Use with caution!" For some reason, this does not work.

As you stated you wanted to set focus on the input field
inputfield.focus_set()
thats all

Related

Python Tkimport how to make one from several windows when importing from a module?

I am writing an application in tkinter consisting of several modules in which there are classes. Each module to a separate page of the app. As I move the buttons between the pages "next", "previous" it opens a new window for me every time. How do I make it so that each time calling pages opens in the same window?
I give draft code.
thank you for your help :D
task1.py
import tkinter as tk
from Test.modul.task1 import FirstPage1
class FirstPage0:
def __init__(self, root):
self.root = root
def get_settings(self):
# Window settings
self.root.geometry("100x200")
def get_second_page(self):
FirstPage1(tk.Toplevel()).get_run_first_page()
def get_button(self):
# Add buttons
tk.Button(self.root, text="Start page", command=self.get_second_page).pack()
tk.Button(self.root, text="Exit", command=self.root.destroy).pack()
def get_run_first_page(self):
# Launching the application
self.get_settings()
self.get_button()
self.root.mainloop()
if __name__ == '__main__':
first = FirstPage0(tk.Tk())
first.get_run_first_page()
task2.py
import tkinter as tk
class FirstPage1:
def __init__(self, root):
self.root = root
def get_settings(self):
# Window settings
self.root.geometry("100x200")
def get_second_page1(self):
from Test.task import FirstPage0
FirstPage0(tk.Toplevel()).get_run_first_page()
def get_button(self):
# Add buttons
tk.Button(self.root, text="Back", command=self.get_second_page1).pack()
tk.Button(self.root, text="Exit", command=self.root.destroy).pack()
def get_run_first_page(self):
# Launching the application
self.get_settings()
self.get_button()
self.root.mainloop()
if __name__ == '__main__':
first = FirstPage1(tk.Tk())
first.get_run_first_page()
Solution
#AdrianSz, you wanted to make the buttons not stack under each other. There are three ways to do so. One, is to keep only one button and change its command and text parameters each time when the frames change. Another would be to unpack the button not needed and pack the button needed. The third would be to pack the buttons in the root window instead of frame and change the text and command parameters. I would recommend the second method as it is easier and less prone to errors.
Code
task1.py
import tkinter as tk
from Test.modul.task1 import FirstPage1
class FirstPage0:
def __init__(self, root):
self.root = root
def get_settings(self):
# Window settings
self.root.geometry("100x200")
def get_second_page(self):
self.root.pg_0_btn_start.pack_forget()
self.root_pg_0_btn_exit.pack_forget()
FirstPage1(tk.Toplevel()).get_run_first_page()
def get_button(self):
# Add buttons
self.root.pg_0_btn_start = tk.Button(self.root, text="Start page",
command=self.get_second_page).pack()
self.root_pg_0_btn_exit = tk.Button(self.root, text="Exit",
command=self.root.destroy).pack()
def get_run_first_page(self):
# Launching the application
self.get_settings()
self.get_button()
self.root.mainloop()
if __name__ == '__main__':
first = FirstPage0(tk.Tk())
first.get_run_first_page()
task2.py
import tkinter as tk
class FirstPage1:
def __init__(self, root):
self.root = root
def get_settings(self):
# Window settings
self.root.geometry("100x200")
def get_second_page1(self):
from Test.task import FirstPage0
self.root.pg_1_btn_back.pack_forget()
self.root_pg_1_btn_exit.pack_forget()
FirstPage0(tk.Toplevel()).get_run_first_page()
def get_button(self):
# Add buttons
self.root.pg_1_btn_back = tk.Button(self.root, text="Back", command=self.get_second_page1).pack()
self.root.pg_1_btn_exit = tk.Button(self.root, text="Exit", command=self.root.destroy).pack()
def get_run_first_page(self):
# Launching the application
self.get_settings()
self.get_button()
self.root.mainloop()
if __name__ == '__main__':
first = FirstPage1(tk.Tk())
first.get_run_first_page()
Note: I couldn't test this code from my side so if there are any errors, please comment on this answer
Suggestions
Since you are using modules for these, just make them inherit a class specified in a different file and operate them both from that file. There you can use self to access the methods of a subclass because the subclasses instantiate the base class and thus the self is a object of the subclass and is passed to the base class. The type of code you used is quite confusing too. I have added code to give you your wanted output using the principles I mentioned here. Hope this helped!

I get the error _tkinter.TclError: bad window path name ".!button" when I destroy the button

from tkinter import *
master=Tk()
class check:
def __init__(self,root):
self.root=root
self.b1=Button(root,text="Click me",command=self.undo)
self.b2=Button(root,text="Again",command=self.click)
def click(self):
self.b1.place(relx=0.5,rely=0.5)
def undo(self):
self.b1.destroy()
self.b2.place(relx=0.2,rely=0.2)
c=check(master)
c.click()
master.mainloop()
This is my code. I get _tkinter.TclError: bad window path name ".!button" error only when I use destroy method. But I want to delete previous button when another button appears.What should I do?
What are you doing? When you click the "Click me" button (and call the self.undo method, where the self.b1 button is destroyed) and then click the "Again" button (and call the self.click method, which tries to place already destroyed self.b1 button), you get the error, that the button does not exist. Of course, it doesn't because you have destroyed it.
It looks like you meant to hide the button. If you intended to do this, then you could just use .place_forget() method (there are also .pack_forget() and .grid_forget() methods for pack and grid window managers, respectively), that hides the widget, but not destroys it, and hence you would be able to restore it again when you need.
Here is your fixed code:
from tkinter import *
master = Tk()
class check:
def __init__(self, root):
self.root = root
self.b1 = Button(root, text="Click me", command=self.undo)
self.b2 = Button(root, text="Again", command=self.click)
def click(self):
self.b2.place_forget()
self.b1.place(relx=0.5, rely=0.5)
def undo(self):
self.b1.place_forget()
self.b2.place(relx=0.2, rely=0.2)
c = check(master)
c.click()
master.mainloop()
I can also give you a piece of advice about the implementation:
1) You should write the code according to the PEP8 style; classes should be named in the CamelCase.
2) You should inherit your Tkinter app class(es) either from Tk (usage is shown below) Toplevel(the same as Tk, but use ONLY for child windows), Frame class (almost the same as for Tk, but you need to pack/grid/place that Frame in a window).
3) It's better to create the widgets in a separate function (it helps while developing complex and big apps).
4) It's recommended to write if __name__ == "__main__": condition before creating the window (if you do like this, you will be able to import this code from other modules, and the window won't open in that case).
Here is an example:
from tkinter import *
class Check(Tk):
def __init__(self):
super().__init__()
self.create_widgets()
self.click()
def create_widgets(self):
self.b1 = Button(self, text="Click me", command=self.undo)
self.b2 = Button(self, text="Again", command=self.click)
def click(self):
self.b2.place_forget()
self.b1.place(relx=0.5, rely=0.5)
def undo(self):
self.b1.place_forget()
self.b2.place(relx=0.2, rely=0.2)
if __name__ == "__main__":
Check().mainloop()
After you destroyed button b1 in the undo(self) function tkinter cannot access it anymore and will be confused when you try to place is somewhere in the click(self) function.
To make button b1 only disappear visually you could place it outside of the window instead of destroying it. To do so replace
self.b1.destroy()
with
self.b1.place(relx=-5, rely=0)
This will move the button b1 far to the left, where it cannot be seen.
When calling the click(self) function, the button will reappear, because it will be moved inside the window again.

Entry widget contents not changing with function call

I need to change the content of an entry whenever the tkinter frame is shown. Below is what I have so far, and it doesn't seem to work. I have tried to use data = self.read() and then now.insert(0, data) and that has not worked either. If the value is displayed then it doesn't get changed every time the class ReadLabel1 is called.
class ReadLabel1(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent, bg="blue")
label = tk.Label(self, text="SomeData:", font = "Times 12", bg="blue")
label.place(x=10, y=100) #ack(pady=5,padx=30)
self.smStr = tk.StringVar()
now=tk.Entry(self, width=22, textvariable=self.read())
now.place(x=120, y=103)
def read(self):
# code to get data
return data
You need to turn 'change the content of an entry' into a one parameter callback, turn 'whenever the tkinter frame is shown' into an event, and then bind together the app, the event, and the callback. Here is a minimal example.
import time
import tkinter as tk
root = tk.Tk()
now = tk.StringVar()
lab = tk.Label(root, textvariable=now)
lab.pack()
def display_now(event):
now.set(time.ctime())
root.bind('<Visibility>', display_now)
root.bind('<FocusIn>', display_now)
Minimizing the window to a icon and bringing it back up triggers the Visibility event. Covering and merely uncovering with a different window did not, at least not with Windows. Clicking on the uncovered, or merely inactivated, window triggered FocusIn. You can experiment more with your system. I used this tkinter reference

How do you validate a window in Tkinter?

def createWidgets(self):
self.INSTRUCTIONS = Button(self) #creating button linked to instructions_window
self.INSTRUCTIONS["text"] = "Instructions"
self.INSTRUCTIONS["fg"] = "green"
self.INSTRUCTIONS["command"] = self.instruction_window #command which opens instructions_window
self.INSTRUCTIONS.pack({"side": "left"})
Currently, if I press the button multiple times then the instructions window will open multiple times. How do I ensure that when the button is pressed, if the window is already open then it will flash to show that the same window can't be opened. Is there a command? Or do I need to use a validation of some sort?
Here's a great article dealing with more complicated examples of dialog boxes.
Essentially what you are looking for is almost like a modal dialog window except it seems with the additional ability to still interact with the parent window to a degree. At this point it may be worth considering making it totally modal, but I do not know your use case. If not, you can definitely adapt the scripts given on the tutorial website to fit your needs.
The way I do this is to create a function that will create the window if it doesn't exist, and then display the window. Personally I don't think there's a need to flash the window, but you could do that if you want. Tkinter doesn't know how to flash a window, but you can do something simple like changing the colors briefly.
Here's an example:
import Tkinter as tk
class Example(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.instruction_window = None
self.instructions = tk.Button(self, text="Instructions", foreground="green",
command=self.show_instructions)
self.instructions.pack(side="left")
def show_instructions(self):
'''show the instruction window; create it if it doesn't exist'''
if self.instruction_window is None or not self.instruction_window.winfo_exists():
self.instruction_window = InstructionWindow(self)
else:
self.instruction_window.flash()
class InstructionWindow(tk.Toplevel):
'''A simple instruction window'''
def __init__(self, parent):
tk.Toplevel.__init__(self, parent)
self.text = tk.Text(self, width=40, height=8)
self.text.pack(side="top", fill="both", expand=True)
self.text.insert("end", "these are the instructions")
def flash(self):
'''make the window visible, and make it flash temporarily'''
# make sure the window is visible, in case it got hidden
self.lift()
self.deiconify()
# blink the colors
self.after(100, lambda: self.text.configure(bg="black", fg="white"))
self.after(500, lambda: self.text.configure(bg="white", fg="black"))
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(side="top", fill="both", expand=True)
root.mainloop()

How to close a Tkinter window by pressing a Button?

Write a GUI application with a button labeled "Good-bye". When the
Button is clicked, the window closes.
This is my code so far, but it is not working. Can anyone help me out with my code?
from Tkinter import *
window = Tk()
def close_window (root):
root.destroy()
frame = Frame(window)
frame.pack()
button = Button (frame, text = "Good-bye.", command = close_window)
button.pack()
window.mainloop()
With minimal editing to your code (Not sure if they've taught classes or not in your course), change:
def close_window(root):
root.destroy()
to
def close_window():
window.destroy()
and it should work.
Explanation:
Your version of close_window is defined to expect a single argument, namely root. Subsequently, any calls to your version of close_window need to have that argument, or Python will give you a run-time error.
When you created a Button, you told the button to run close_window when it is clicked. However, the source code for Button widget is something like:
# class constructor
def __init__(self, some_args, command, more_args):
#...
self.command = command
#...
# this method is called when the user clicks the button
def clicked(self):
#...
self.command() # Button calls your function with no arguments.
#...
As my code states, the Button class will call your function with no arguments. However your function is expecting an argument. Thus you had an error. So, if we take out that argument, so that the function call will execute inside the Button class, we're left with:
def close_window():
root.destroy()
That's not right, though, either, because root is never assigned a value. It would be like typing in print(x) when you haven't defined x, yet.
Looking at your code, I figured you wanted to call destroy on window, so I changed root to window.
You could create a class that extends the Tkinter Button class, that will be specialised to close your window by associating the destroy method to its command attribute:
from tkinter import *
class quitButton(Button):
def __init__(self, parent):
Button.__init__(self, parent)
self['text'] = 'Good Bye'
# Command to close the window (the destory method)
self['command'] = parent.destroy
self.pack(side=BOTTOM)
root = Tk()
quitButton(root)
mainloop()
This is the output:
And the reason why your code did not work before:
def close_window ():
# root.destroy()
window.destroy()
I have a slight feeling you might got the root from some other place, since you did window = tk().
When you call the destroy on the window in the Tkinter means destroying the whole application, as your window (root window) is the main window for the application. IMHO, I think you should change your window to root.
from tkinter import *
def close_window():
root.destroy() # destroying the main window
root = Tk()
frame = Frame(root)
frame.pack()
button = Button(frame)
button['text'] ="Good-bye."
button['command'] = close_window
button.pack()
mainloop()
You can associate directly the function object window.destroy to the command attribute of your button:
button = Button (frame, text="Good-bye.", command=window.destroy)
This way you will not need the function close_window to close the window for you.
from tkinter import *
window = tk()
window.geometry("300x300")
def close_window ():
window.destroy()
button = Button ( text = "Good-bye", command = close_window)
button.pack()
window.mainloop()
You can use lambda to pass a reference to the window object as argument to close_window function:
button = Button (frame, text="Good-bye.", command = lambda: close_window(window))
This works because the command attribute is expecting a callable, or callable like object.
A lambda is a callable, but in this case it is essentially the result of calling a given function with set parameters.
In essence, you're calling the lambda wrapper of the function which has no args, not the function itself.
from tkinter import *
def close_window():
import sys
sys.exit()
root = Tk()
frame = Frame (root)
frame.pack()
button = Button (frame, text="Good-bye", command=close_window)
button.pack()
mainloop()

Categories

Resources