Set focus on Tkinter window (depends on platform?) - python

I have a Tkinter program and running it like: python myWindow.py starts it all right, but the window is behind the terminal that I use to start it.
Is there a way to make it grab the focus and be the foreground application? Does it depend on the platform?

This might be a feature of your particular window manager. One thing to try is for your app to call focus_force at startup, after all the widgets have been created.

Have you tried this at the end of your script ?
root.iconify()
root.update()
root.deiconify()
root.mainloop()

Somewhat of a combination of various other methods found online, this works on OS X 10.11, and Python 3.5.1 running in a venv, and should work on other platforms too. On OS X, it also targets the app by process id rather than app name.
from tkinter import Tk
import os
import subprocess
import platform
def raise_app(root: Tk):
root.attributes("-topmost", True)
if platform.system() == 'Darwin':
tmpl = 'tell application "System Events" to set frontmost of every process whose unix id is {} to true'
script = tmpl.format(os.getpid())
output = subprocess.check_call(['/usr/bin/osascript', '-e', script])
root.after(0, lambda: root.attributes("-topmost", False))
You call it right before the mainloop() call, like so:
raise_app(root)
root.mainloop()

Related

Python tk - hide console window

I have prepared some tk application. It could be really simple like:
from tkinter import *
# create root window
root = Tk()
# root window title and dimension
root.title("Welcome to GeekForGeeks")
# Set geometry (widthxheight)
root.geometry('350x200')
# all widgets will be here
# Execute Tkinter
root.mainloop()
I have using some method to convert the app to the exe file.
What is important,
I'm not using and I cannot do it with pyinstaller py2exe etc. I also cannot use method with changing my app.py to app.pyw.
But my conversion to .exe is working correctly.
The question - is it even possible to hide/disable/resize(reduce the size) of my console window and make the application still working?
I'm not exactly sure how is it done in pyinstaller py2exe etc, so maybe is it possible to do it inside an application?
All right, to solve above problem install:
pip install pywin32
and add code before running your tk gui application:
import win32gui
import win32.lib.win32con as win32con
the_program_to_hide = win32gui.GetForegroundWindow()
win32gui.ShowWindow(the_program_to_hide , win32con.SW_HIDE)
Then you can run the main.py in console, the console will disappear and the gui app will be still visible.
In case when you use pyinstaller etc - you can convert the application without "--noconsole" argument.
When you run the .exe file the console will appear for a second, and disappear. But the gui app will be still visible and usable.
Hope it help somebody somehow :)
I think you should run your script using pythonw.exe instead of python.exe. See .pyw files in python program
Does this help if using Toplevel?
from tkinter import *
root = Tk()
root.title("Main Window")
root.geometry("200x200")
def launch():
global second
second = Toplevel()
second.title("Child Window")
second.geometry("400x400")
def show():
second.deiconify()
def hide():
second.withdraw()
Button(root, text="launch Window", command=launch).pack(pady=10)
Button(root, text="Show", command=show).pack(pady=10)
Button(root, text="Hide", command=hide).pack(pady=10)
root.mainloop()

How to close a running windows OS program using a button?

I'm making a simple GUI using Python 3.7.3 and tkinter to open and close windows applications. I'm not able to find a way to close a running program using an onscreen button. I need the 'close' button to do something else as well, hence the simply using 'x' button (which is next to the minimize and maximize) won't work for my case.
from tkinter import *
import os, subprocess
root = Tk()
root.geometry("300x300")
def OpenCalc():
app1 = os.startfile("C:\Windows\System32\calc.exe")
def CloseCalc():
os.close(app1)
# or
# os.closefile("C:\Windows\System32\calc.exe")
b1=Button(root, text="Open Calc", command=OpenCalc).pack()
b2=Button(root, text="Close Calc", command=CloseCalc).pack()
root.mainloop()

Restart program tkinter

I am wondering on how I can create a restart button that once clicked, can restart the entire script. What I thought was that you destroy the window then un-destroy it but apparently there is no un-destroy function.
I found a way of doing it for a generic python program on this website: https://www.daniweb.com/programming/software-development/code/260268/restart-your-python-program. I wrote an example with a basic tkinter GUI to test it:
import sys
import os
from tkinter import Tk, Label, Button
def restart_program():
"""Restarts the current program.
Note: this function does not return. Any cleanup action (like
saving data) must be done before calling this function."""
python = sys.executable
os.execl(python, python, * sys.argv)
root = Tk()
Label(root, text="Hello World!").pack()
Button(root, text="Restart", command=restart_program).pack()
root.mainloop()
The following solution works as well but is quite harsh, i.e. the entire environment is lost.
# kills the whole application and starts a fresh one
def restart():
root.destroy()
root = Tk()
root.mainloop()
I would Like to Use this Function:-
First of All Import os Module
import os
Then Use this Code:-
# Restarts the Whole Window
def restart():
root.destroy()
os.startfile("main.py")
Or if You want no console behind then Simply Change the extension of the file to .pyw
And Run this Code:-
# Restarts the Whole Window
def restart():
root.destroy()
os.startfile("main.pyw")

Tkinter window focus on Mac OS X

I'm writing an application in Python with the Tkinter GUI framework. It listens for keyboard and mouse events, so it must have focus. When it is launched from a terminal in Ubuntu, the following code works:
from Tkinter import *
root = Tk()
root.focus_force()
def key(event):
print "pressed", event.char
def callback(event):
print "clicked at", event.x, event.y
frame = Frame(root, width=100, height=100)
frame.bind("<Key>", key)
frame.bind("<Button-1>", callback)
frame.pack()
frame.focus_force()
root.mainloop()
However, when launched from a terminal in Mac OS X 10.8.4 (stock Python 2.7.2), focus is retained by the terminal emulator until the user clicks on the window. Does anyone know of a workaround for this?
I tried this and it worked well for me:
from os import system
from platform import system as platform
# set up your Tk Frame and whatnot here...
if platform() == 'Darwin': # How Mac OS X is identified by Python
system('''/usr/bin/osascript -e 'tell app "Finder" to set frontmost of process "Python" to true' ''')
Of course, that'll bring your entire application to the front, not just a specific window, but after you do that, you can then use focus_force() on a specific frame or window and that'll get moved to become the frontmost of all your application windows.
For those interested, I didn't write the system() call myself. I found it in this thread on SourceForge.
The fact that I put the system() call within an if block that verifies this is running on OS X makes the solution cross platform - my understanding is that focus_force() works on all other platforms exactly as you want, and just performing it after the system() invocation wouldn't cause any problems in OS X, either.
came here wondering the same question, but I found this wise sounding answer from Kevin Walzer which suggests to use py2app:
Yes, this is standard behavior for OS X. Running an app in Terminal
keeps focus in the Terminal unless you switch by clicking windows. The
Command-Tab behavior is determined by the windowing system, not by a
newly spawned process.
The way around this is to wrap your application up in a standard Mac app
bundle using py2app. The average Mac user isn't going to launch a
Python-based game from the command line.
Kevin
(from https://groups.google.com/forum/#!topic/comp.lang.python/ZPO8ZN5dx3M)
Do wait_visibility and event_generate help ?
eg. something like -
from Tkinter import *
root = Tk()
def key(event):
print "pressed", event.char
def callback(event):
print "clicked at", event.x, event.y
frame = Frame(root, width=100, height=100)
frame.bind("<Key>", key)
frame.bind("<Button-1>", callback)
frame.pack()
frame.focus_set()
root.wait_visibility()
root.event_generate('<Button-1>', x=0, y=0)
root.mainloop()

How to make a Tkinter window jump to the front?

How do I get a Tkinter application to jump to the front? Currently, the window appears behind all my other windows and doesn't get focus.
Is there some method I should be calling?
Assuming you mean your application windows when you say "my other windows", you can use the lift() method on a Toplevel or Tk:
root.lift()
If you want the window to stay above all other windows, use:
root.attributes("-topmost", True)
Where root is your Toplevel or Tk. Don't forget the - infront of "topmost"!
To make it temporary, disable topmost right after:
def raise_above_all(window):
window.attributes('-topmost', 1)
window.attributes('-topmost', 0)
Just pass in the window you want to raise as a argument, and this should work.
Add the following lines before the mainloop():
root.lift()
root.attributes('-topmost',True)
root.after_idle(root.attributes,'-topmost',False)
It works perfectly for me. It makes the window come to the front when the window is generated, and it won't keep it always be in the front.
If you're doing this on a Mac, use AppleEvents to give focus to Python. Eg:
import os
os.system('''/usr/bin/osascript -e 'tell app "Finder" to set frontmost of process "Python" to true' ''')
Regarding the Mac, I noticed there can be a problem in that if there are multiple python GUIs running, every process will be named "Python" and AppleScript will tend to promote the wrong one to the front. Here's my solution. The idea is to grab a list of running process IDs before and after you load Tkinter. (Note that these are AppleScript process IDs which seem to bear no relation to their posix counterparts. Go figure.) Then the odd man out will be yours and you move that one to frontmost. (I didn't think that loop at the end would be necessary, but if you simply get every process whose ID is procID, AppleScript apparently returns the one object identified by name, which of course is that non-unique "Python", so we are back to square one unless there's something I'm missing.)
import Tkinter, subprocess
def applescript(script):
return subprocess.check_output(['/usr/bin/osascript', '-e', script])
def procidset():
return set(applescript(
'tell app "System Events" to return id of every process whose name is "Python"'
).replace(',','').split())
idset = procidset()
root = Tkinter.Tk()
procid = iter(procidset() - idset).next()
applescript('''
tell app "System Events"
repeat with proc in every process whose name is "Python"
if id of proc is ''' + procid + ''' then
set frontmost of proc to true
exit repeat
end if
end repeat
end tell''')
On Mac OS X PyObjC provides a cleaner and less error prone method than shelling out to osascript:
import os
from Cocoa import NSRunningApplication, NSApplicationActivateIgnoringOtherApps
app = NSRunningApplication.runningApplicationWithProcessIdentifier_(os.getpid())
app.activateWithOptions_(NSApplicationActivateIgnoringOtherApps)
Recently, I had the same question on the Mac. I have combined several answers using #MagerValp for the Mac and #D K for other systems:
import platform
if platform.system() != 'Darwin':
root.lift()
root.call('wm', 'attributes', '.', '-topmost', True)
root.after_idle(root.call, 'wm', 'attributes', '.', '-topmost', False)
else:
import os
from Cocoa import NSRunningApplication, NSApplicationActivateIgnoringOtherApps
app = NSRunningApplication.runningApplicationWithProcessIdentifier_(os.getpid())
app.activateWithOptions_(NSApplicationActivateIgnoringOtherApps)
root.mainloop()
Somewhat of a combination of various other methods, this works on OS X 10.11, and Python 3.5.1 running in a venv, and should work on other platforms too. It also targets the app by process id rather than app name.
from tkinter import Tk
import os
import subprocess
import platform
def raise_app(root: Tk):
root.attributes("-topmost", True)
if platform.system() == 'Darwin':
tmpl = 'tell application "System Events" to set frontmost of every process whose unix id is {} to true'
script = tmpl.format(os.getpid())
output = subprocess.check_call(['/usr/bin/osascript', '-e', script])
root.after(0, lambda: root.attributes("-topmost", False))
You call it right before the mainloop() call, like so:
raise_app(root)
root.mainloop()
There's a hint on how to make the Tkinter window take focus when you call mainloop() in the Tkinter._test() function.
# The following three commands are needed so the window pops
# up on top on Windows...
root.iconify()
root.update()
root.deiconify()
root.mainloop()
This is the cleanest most proper way I've found to do this, but it's only needed for Windows systems.
This answer is to make one Tkinter Window pop up overtop of other Tkinter windows.
In my app I have a large window toplevel which calls a much smaller window top2 which initially appears on top of toplevel.
If user clicks within toplevel window it gains focus and smothers much smaller top2 window until toplevel window is dragged off of it.
The solution is to click the button in toplevel to launch top2 again. The top2 open function knows it is already running so simply lifts it to the top and gives it focus:
def play_items(self):
''' Play 1 or more songs in listbox.selection(). Define buttons:
Close, Pause, Prev, Next, Commercial and Intermission
'''
if self.top2_is_active is True:
self.top2.focus_force() # Get focus
self.top2.lift() # Raise in stacking order
root.update()
return # Don't want to start playing again
On macOS High Sierra, py3.6.4, here is my solution:
def OnFocusIn(event):
if type(event.widget).__name__ == 'Tk':
event.widget.attributes('-topmost', False)
# Create and configure your root ...
root.attributes('-topmost', True)
root.focus_force()
root.bind('<FocusIn>', OnFocusIn)
The idea is to bring it to the front until user interacts with it, i.e., taking focus.
I tried the accepted answer, .after_idle(), and .after(). They all fail in one case: When I run my script directly from an IDE like PyCharm, the app window will stay behind.
My solution works in all the cases that I encountered.
One more line (needed for Python 3.11 and tkinter 8.6):
def lift_window(window):
window.attributes('-topmost', True)
window.update_idletasks() # get window on top
window.attributes('-topmost', False) # prevent permanent focus
window.focus_force() # focus to the window
This will lift the window to the front, and also focus on the window.
def lift_window(window):
window.attributes('-topmost',True)
window.attributes('-topmost',False) # disable the topmost attribute after it is at the front to prevent permanent focus
window.focus_force() # focus to the window

Categories