I would like to get the active window on the screen using python.
For example, the management interface of the router where you enter the username and password as admin
That admin interface is what I want to capture using python to automate the entry of username and password.
What imports would I require in order to do this?
On windows, you can use the python for windows extensions (http://sourceforge.net/projects/pywin32/):
from win32gui import GetWindowText, GetForegroundWindow
print GetWindowText(GetForegroundWindow())
Below code is for python 3:
from win32gui import GetWindowText, GetForegroundWindow
print(GetWindowText(GetForegroundWindow()))
(Found this on http://scott.sherrillmix.com/blog/programmer/active-window-logger/)
Thanks goes to the answer by Nuno André, who showed how to use ctypes to interact with Windows APIs. I have written an example implementation using his hints.
The ctypes library is included with Python since v2.5, which means that almost every user has it. And it's a way cleaner interface than old and dead libraries like win32gui (last updated in 2017 as of this writing). ((Update in late 2020: The dead win32gui library has come back to life with a rename to pywin32, so if you want a maintained library, it's now a valid option again. But that library is 6% slower than my code.))
Documentation is here: https://docs.python.org/3/library/ctypes.html (You must read its usage help if you wanna write your own code, otherwise you can cause segmentation fault crashes, hehe.)
Basically, ctypes includes bindings for the most common Windows DLLs. Here is how you can retrieve the title of the foreground window in pure Python, with no external libraries needed! Just the built-in ctypes! :-)
The coolest thing about ctypes is that you can Google any Windows API for anything you need, and if you want to use it, you can do it via ctypes!
Python 3 Code:
from typing import Optional
from ctypes import wintypes, windll, create_unicode_buffer
def getForegroundWindowTitle() -> Optional[str]:
hWnd = windll.user32.GetForegroundWindow()
length = windll.user32.GetWindowTextLengthW(hWnd)
buf = create_unicode_buffer(length + 1)
windll.user32.GetWindowTextW(hWnd, buf, length + 1)
# 1-liner alternative: return buf.value if buf.value else None
if buf.value:
return buf.value
else:
return None
Performance is extremely good: 0.01 MILLISECONDS on my computer (0.00001 seconds).
Will also work on Python 2 with very minor changes. If you're on Python 2, I think you only have to remove the type annotations (from typing import Optional and -> Optional[str]). :-)
Enjoy!
Win32 Technical Explanations:
The length variable is the length of the actual text in UTF-16 (Windows Wide "Unicode") CHARACTERS. (It is NOT the number of BYTES.) We have to add + 1 to add room for the null terminator at the end of C-style strings. If we don't do that, we would not have enough space in the buffer to fit the final real character of the actual text, and Windows would truncate the returned string (it does that to ensure that it fits the super important final string Null-terminator).
The create_unicode_buffer function allocates room for that many UTF-16 CHARACTERS.
Most (or all? always read Microsoft's MSDN docs!) Windows APIs related to Unicode text take the buffer length as CHARACTERS, NOT as bytes.
Also look closely at the function calls. Some end in W (such as GetWindowTextLengthW). This stands for "Wide string", which is the Windows name for Unicode strings. It's very important that you do those W calls to get proper Unicode strings (with international character support).
PS: Windows has been using Unicode for a long time. I know for a fact that Windows 10 is fully Unicode and only wants the W function calls. I don't know the exact cutoff date when older versions of Windows used other multi-byte string formats, but I think it was before Windows Vista, and who cares? Old Windows versions (even 7 and 8.1) are dead and unsupported by Microsoft.
Again... enjoy! :-)
UPDATE in Late 2020, Benchmark vs the pywin32 library:
import time
import win32ui
from typing import Optional
from ctypes import wintypes, windll, create_unicode_buffer
def getForegroundWindowTitle() -> Optional[str]:
hWnd = windll.user32.GetForegroundWindow()
length = windll.user32.GetWindowTextLengthW(hWnd)
buf = create_unicode_buffer(length + 1)
windll.user32.GetWindowTextW(hWnd, buf, length + 1)
return buf.value if buf.value else None
def getForegroundWindowTitle_Win32UI() -> Optional[str]:
# WARNING: This code sometimes throws an exception saying
# "win32ui.error: No window is is in the foreground."
# which is total nonsense. My function doesn't fail that way.
return win32ui.GetForegroundWindow().GetWindowText()
iterations = 1_000_000
start_time = time.time()
for x in range(iterations):
foo = getForegroundWindowTitle()
elapsed1 = time.time() - start_time
print("Elapsed 1:", elapsed1, "seconds")
start_time = time.time()
for x in range(iterations):
foo = getForegroundWindowTitle_Win32UI()
elapsed2 = time.time() - start_time
print("Elapsed 2:", elapsed2, "seconds")
win32ui_pct_slower = ((elapsed2 / elapsed1) - 1) * 100
print("Win32UI library is", win32ui_pct_slower, "percent slower.")
Typical result after doing multiple runs on an AMD Ryzen 3900x:
My function: 4.5769994258880615 seconds
Win32UI library: 4.8619983196258545 seconds
Win32UI library is 6.226762715455125 percent slower.
However, the difference is small, so you may want to use the library now that it has come back to life (it had previously been dead since 2017). But you're going to have to deal with that library's weird "no window is in the foreground" exception, which my code doesn't suffer from (see the code comments in the benchmark code).
Either way... enjoy!
The following script should work on Linux, Windows and Mac. It is currently only tested on Linux (Ubuntu Mate Ubuntu 15.10).
Prerequisites
For Linux:
Install wnck (sudo apt-get install python-wnck on Ubuntu, see libwnck.)
For Windows:
Make sure win32gui is available
For Mac:
Make sure AppKit is available
The script
#!/usr/bin/env python
"""Find the currently active window."""
import logging
import sys
logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s',
level=logging.DEBUG,
stream=sys.stdout)
def get_active_window():
"""
Get the currently active window.
Returns
-------
string :
Name of the currently active window.
"""
import sys
active_window_name = None
if sys.platform in ['linux', 'linux2']:
# Alternatives: https://unix.stackexchange.com/q/38867/4784
try:
import wnck
except ImportError:
logging.info("wnck not installed")
wnck = None
if wnck is not None:
screen = wnck.screen_get_default()
screen.force_update()
window = screen.get_active_window()
if window is not None:
pid = window.get_pid()
with open("/proc/{pid}/cmdline".format(pid=pid)) as f:
active_window_name = f.read()
else:
try:
from gi.repository import Gtk, Wnck
gi = "Installed"
except ImportError:
logging.info("gi.repository not installed")
gi = None
if gi is not None:
Gtk.init([]) # necessary if not using a Gtk.main() loop
screen = Wnck.Screen.get_default()
screen.force_update() # recommended per Wnck documentation
active_window = screen.get_active_window()
pid = active_window.get_pid()
with open("/proc/{pid}/cmdline".format(pid=pid)) as f:
active_window_name = f.read()
elif sys.platform in ['Windows', 'win32', 'cygwin']:
# https://stackoverflow.com/a/608814/562769
import win32gui
window = win32gui.GetForegroundWindow()
active_window_name = win32gui.GetWindowText(window)
elif sys.platform in ['Mac', 'darwin', 'os2', 'os2emx']:
# https://stackoverflow.com/a/373310/562769
from AppKit import NSWorkspace
active_window_name = (NSWorkspace.sharedWorkspace()
.activeApplication()['NSApplicationName'])
else:
print("sys.platform={platform} is unknown. Please report."
.format(platform=sys.platform))
print(sys.version)
return active_window_name
print("Active window: %s" % str(get_active_window()))
For Linux users:
All the answers provided required additional modules like "wx" that had numerous errors installing ("pip" failed on build), but I was able to modify this solution quite easily -> original source. There were bugs in the original (Python TypeError on regex)
import sys
import os
import subprocess
import re
def get_active_window_title():
root = subprocess.Popen(['xprop', '-root', '_NET_ACTIVE_WINDOW'], stdout=subprocess.PIPE)
stdout, stderr = root.communicate()
m = re.search(b'^_NET_ACTIVE_WINDOW.* ([\w]+)$', stdout)
if m != None:
window_id = m.group(1)
window = subprocess.Popen(['xprop', '-id', window_id, 'WM_NAME'], stdout=subprocess.PIPE)
stdout, stderr = window.communicate()
else:
return None
match = re.match(b"WM_NAME\(\w+\) = (?P<name>.+)$", stdout)
if match != None:
return match.group("name").strip(b'"')
return None
if __name__ == "__main__":
print(get_active_window_title())
The advantage is it works without additional modules. If you want it to work across multiple platforms, it's just a matter of changing the command and regex strings to get the data you want based on the platform (with the standard if/else platform detection shown above sys.platform).
On a side note: import wnck only works with python2.x when installed with "sudo apt-get install python-wnck", since I was using python3.x the only option was pypie which I have not tested. Hope this helps someone else.
There's really no need to import any external dependency for tasks like this. Python comes with a pretty neat foreign function interface - ctypes, which allows for calling C shared libraries natively. It even includes specific bindings for the most common Win32 DLLs.
E.g. to get the PID of the foregorund window:
import ctypes
from ctypes import wintypes
user32 = ctypes.windll.user32
h_wnd = user32.GetForegroundWindow()
pid = wintypes.DWORD()
user32.GetWindowThreadProcessId(h_wnd, ctypes.byref(pid))
print(pid.value)
In Linux under X11:
xdo_window_id = os.popen('xdotool getactivewindow').read()
print('xdo_window_id:', xdo_window_id)
will print the active window ID in decimal format:
xdo_window_id: 67113707
Note xdotool must be installed first:
sudo apt install xdotool
Note wmctrl uses hexadecimal format for window ID.
This only works on windows
import win32gui
import win32process
def get_active_executable_name():
try:
process_id = win32process.GetWindowThreadProcessId(
win32gui.GetForegroundWindow()
)
return ".".join(psutil.Process(process_id[-1]).name().split(".")[:-1])
except Exception as exception:
return None
I'll recommend checking out this answer for making it work on linux, mac and windows.
I'd been facing same problem with linux interface (Lubuntu 20).
What I do is using wmctrl and execute it with shell command from python.
First, Install wmctrl
sudo apt install wmctrl
Then, Add this code :
import os
os.system('wmctrl -a "Mozilla Firefox"')
ref wmctrl :
https://askubuntu.com/questions/21262/shell-command-to-bring-a-program-window-in-front-of-another
In Linux:
If you already have installed xdotool, you can just use:
from subprocess import run
def get__focused_window():
return run(['xdotool', 'getwindowfocus', 'getwindowpid', 'getwindowname'], capture_output=True).stdout.decode('utf-8').split()
While I was writing this answer I've realised that there were also:
A reference about "xdotool" on comments
& another slightly similar "xdotool" answer
So, I've decided to mention them here, too.
Just wanted to add in case it helps, I have a function for my program (It's a software for my PC's lighting I have this simple few line function:
def isRunning(process_name):
foregroundWindow = GetWindowText(GetForegroundWindow())
return process_name in foregroundWindow
Try using wxPython:
import wx
wx.GetActiveWindow()
Related
I have a simple script which parses a file and loads it's contents to a database. I don't need a UI, but right now I'm prompting the user for the file to parse using raw_input which is most unfriendly, especially because the user can't copy/paste the path. I would like a quick and easy way to present a file selection dialog to the user, they can select the file, and then it's loaded to the database. (In my use case, if they happened to chose the wrong file, it would fail parsing, and wouldn't be a problem even if it was loaded to the database.)
import tkFileDialog
file_path_string = tkFileDialog.askopenfilename()
This code is close to what I want, but it leaves an annoying empty frame open (which isn't able to be closed, probably because I haven't registered a close event handler).
I don't have to use tkInter, but since it's in the Python standard library it's a good candidate for quickest and easiest solution.
Whats a quick and easy way to prompt for a file or filename in a script without any other UI?
Tkinter is the easiest way if you don't want to have any other dependencies.
To show only the dialog without any other GUI elements, you have to hide the root window using the withdraw method:
import tkinter as tk
from tkinter import filedialog
root = tk.Tk()
root.withdraw()
file_path = filedialog.askopenfilename()
Python 2 variant:
import Tkinter, tkFileDialog
root = Tkinter.Tk()
root.withdraw()
file_path = tkFileDialog.askopenfilename()
You can use easygui:
import easygui
path = easygui.fileopenbox()
To install easygui, you can use pip:
pip3 install easygui
It is a single pure Python module (easygui.py) that uses tkinter.
Try with wxPython:
import wx
def get_path(wildcard):
app = wx.App(None)
style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST
dialog = wx.FileDialog(None, 'Open', wildcard=wildcard, style=style)
if dialog.ShowModal() == wx.ID_OK:
path = dialog.GetPath()
else:
path = None
dialog.Destroy()
return path
print get_path('*.txt')
pywin32 provides access to the GetOpenFileName win32 function. From the example
import win32gui, win32con, os
filter='Python Scripts\0*.py;*.pyw;*.pys\0Text files\0*.txt\0'
customfilter='Other file types\0*.*\0'
fname, customfilter, flags=win32gui.GetOpenFileNameW(
InitialDir=os.environ['temp'],
Flags=win32con.OFN_ALLOWMULTISELECT|win32con.OFN_EXPLORER,
File='somefilename', DefExt='py',
Title='GetOpenFileNameW',
Filter=filter,
CustomFilter=customfilter,
FilterIndex=0)
print 'open file names:', repr(fname)
print 'filter used:', repr(customfilter)
print 'Flags:', flags
for k,v in win32con.__dict__.items():
if k.startswith('OFN_') and flags & v:
print '\t'+k
Using tkinter (python 2) or Tkinter (python 3) it's indeed possible to display file open dialog (See other answers here). Please notice however that user interface of that dialog is outdated and does not corresponds to newer file open dialogs available in Windows 10.
Moreover - if you're looking on way to embedd python support into your own application - you will find out soon that tkinter library is not open source code and even more - it is commercial library.
(For example search for "activetcl pricing" will lead you to this web page: https://reviews.financesonline.com/p/activetcl/)
So tkinter library will cost money for any application wanting to embedd python.
I by myself managed to find pythonnet library:
Overview here: http://pythonnet.github.io/
Source code here: https://github.com/pythonnet/pythonnet
(MIT License)
Using following command it's possible to install pythonnet:
pip3 install pythonnet
And here you can find out working example for using open file dialog:
https://stackoverflow.com/a/50446803/2338477
Let me copy an example also here:
import sys
import ctypes
co_initialize = ctypes.windll.ole32.CoInitialize
# Force STA mode
co_initialize(None)
import clr
clr.AddReference('System.Windows.Forms')
from System.Windows.Forms import OpenFileDialog
file_dialog = OpenFileDialog()
ret = file_dialog.ShowDialog()
if ret != 1:
print("Cancelled")
sys.exit()
print(file_dialog.FileName)
If you also miss more complex user interface - see Demo folder
in pythonnet git.
I'm not sure about portability to other OS's, haven't tried, but .net 5 is planned to be ported to multiple OS's (Search ".net 5 platforms", https://devblogs.microsoft.com/dotnet/introducing-net-5/ ) - so this technology is also future proof.
If you don't need the UI or expect the program to run in a CLI, you could parse the filepath as an argument. This would allow you to use the autocomplete feature of your CLI to quickly find the file you need.
This would probably only be handy if the script is non-interactive besides the filepath input.
Another os-agnostic option, use pywebview:
import webview
def webview_file_dialog():
file = None
def open_file_dialog(w):
nonlocal file
try:
file = w.create_file_dialog(webview.OPEN_DIALOG)[0]
except TypeError:
pass # user exited file dialog without picking
finally:
w.destroy()
window = webview.create_window("", hidden=True)
webview.start(open_file_dialog, window)
# file will either be a string or None
return file
print(webview_file_dialog())
Environment: python3.8.6 on Mac - though I've used pywebview on windows 10 before.
I just stumbled on this little trick for Windows only: run powershell.exe from subprocess.
import subprocess
sys_const = ssfDESKTOP # Starts at the top level
# sys_const = 0x2a # Correct value for "Program Files (0x86)" folder
powershell_browse = "(new-object -COM 'Shell.Application')."
powershell_browse += "BrowseForFolder(0,'window title here',0,sys_const).self.path"
ret = subprocess.run(["powershell.exe",powershell_browse], stdout=subprocess.PIPE)
print(ret.stdout.decode())
Note the optional use of system folder constants. (There's an obscure typo in shldisp.h that the "Program Files (0x86)" constant was assigned wrong. I added a comment with the correct value. Took me a bit to figure that one out.)
More info below:
System folder constants
I have a simple script which parses a file and loads it's contents to a database. I don't need a UI, but right now I'm prompting the user for the file to parse using raw_input which is most unfriendly, especially because the user can't copy/paste the path. I would like a quick and easy way to present a file selection dialog to the user, they can select the file, and then it's loaded to the database. (In my use case, if they happened to chose the wrong file, it would fail parsing, and wouldn't be a problem even if it was loaded to the database.)
import tkFileDialog
file_path_string = tkFileDialog.askopenfilename()
This code is close to what I want, but it leaves an annoying empty frame open (which isn't able to be closed, probably because I haven't registered a close event handler).
I don't have to use tkInter, but since it's in the Python standard library it's a good candidate for quickest and easiest solution.
Whats a quick and easy way to prompt for a file or filename in a script without any other UI?
Tkinter is the easiest way if you don't want to have any other dependencies.
To show only the dialog without any other GUI elements, you have to hide the root window using the withdraw method:
import tkinter as tk
from tkinter import filedialog
root = tk.Tk()
root.withdraw()
file_path = filedialog.askopenfilename()
Python 2 variant:
import Tkinter, tkFileDialog
root = Tkinter.Tk()
root.withdraw()
file_path = tkFileDialog.askopenfilename()
You can use easygui:
import easygui
path = easygui.fileopenbox()
To install easygui, you can use pip:
pip3 install easygui
It is a single pure Python module (easygui.py) that uses tkinter.
Try with wxPython:
import wx
def get_path(wildcard):
app = wx.App(None)
style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST
dialog = wx.FileDialog(None, 'Open', wildcard=wildcard, style=style)
if dialog.ShowModal() == wx.ID_OK:
path = dialog.GetPath()
else:
path = None
dialog.Destroy()
return path
print get_path('*.txt')
pywin32 provides access to the GetOpenFileName win32 function. From the example
import win32gui, win32con, os
filter='Python Scripts\0*.py;*.pyw;*.pys\0Text files\0*.txt\0'
customfilter='Other file types\0*.*\0'
fname, customfilter, flags=win32gui.GetOpenFileNameW(
InitialDir=os.environ['temp'],
Flags=win32con.OFN_ALLOWMULTISELECT|win32con.OFN_EXPLORER,
File='somefilename', DefExt='py',
Title='GetOpenFileNameW',
Filter=filter,
CustomFilter=customfilter,
FilterIndex=0)
print 'open file names:', repr(fname)
print 'filter used:', repr(customfilter)
print 'Flags:', flags
for k,v in win32con.__dict__.items():
if k.startswith('OFN_') and flags & v:
print '\t'+k
Using tkinter (python 2) or Tkinter (python 3) it's indeed possible to display file open dialog (See other answers here). Please notice however that user interface of that dialog is outdated and does not corresponds to newer file open dialogs available in Windows 10.
Moreover - if you're looking on way to embedd python support into your own application - you will find out soon that tkinter library is not open source code and even more - it is commercial library.
(For example search for "activetcl pricing" will lead you to this web page: https://reviews.financesonline.com/p/activetcl/)
So tkinter library will cost money for any application wanting to embedd python.
I by myself managed to find pythonnet library:
Overview here: http://pythonnet.github.io/
Source code here: https://github.com/pythonnet/pythonnet
(MIT License)
Using following command it's possible to install pythonnet:
pip3 install pythonnet
And here you can find out working example for using open file dialog:
https://stackoverflow.com/a/50446803/2338477
Let me copy an example also here:
import sys
import ctypes
co_initialize = ctypes.windll.ole32.CoInitialize
# Force STA mode
co_initialize(None)
import clr
clr.AddReference('System.Windows.Forms')
from System.Windows.Forms import OpenFileDialog
file_dialog = OpenFileDialog()
ret = file_dialog.ShowDialog()
if ret != 1:
print("Cancelled")
sys.exit()
print(file_dialog.FileName)
If you also miss more complex user interface - see Demo folder
in pythonnet git.
I'm not sure about portability to other OS's, haven't tried, but .net 5 is planned to be ported to multiple OS's (Search ".net 5 platforms", https://devblogs.microsoft.com/dotnet/introducing-net-5/ ) - so this technology is also future proof.
If you don't need the UI or expect the program to run in a CLI, you could parse the filepath as an argument. This would allow you to use the autocomplete feature of your CLI to quickly find the file you need.
This would probably only be handy if the script is non-interactive besides the filepath input.
Another os-agnostic option, use pywebview:
import webview
def webview_file_dialog():
file = None
def open_file_dialog(w):
nonlocal file
try:
file = w.create_file_dialog(webview.OPEN_DIALOG)[0]
except TypeError:
pass # user exited file dialog without picking
finally:
w.destroy()
window = webview.create_window("", hidden=True)
webview.start(open_file_dialog, window)
# file will either be a string or None
return file
print(webview_file_dialog())
Environment: python3.8.6 on Mac - though I've used pywebview on windows 10 before.
I just stumbled on this little trick for Windows only: run powershell.exe from subprocess.
import subprocess
sys_const = ssfDESKTOP # Starts at the top level
# sys_const = 0x2a # Correct value for "Program Files (0x86)" folder
powershell_browse = "(new-object -COM 'Shell.Application')."
powershell_browse += "BrowseForFolder(0,'window title here',0,sys_const).self.path"
ret = subprocess.run(["powershell.exe",powershell_browse], stdout=subprocess.PIPE)
print(ret.stdout.decode())
Note the optional use of system folder constants. (There's an obscure typo in shldisp.h that the "Program Files (0x86)" constant was assigned wrong. I added a comment with the correct value. Took me a bit to figure that one out.)
More info below:
System folder constants
I have a simple script which parses a file and loads it's contents to a database. I don't need a UI, but right now I'm prompting the user for the file to parse using raw_input which is most unfriendly, especially because the user can't copy/paste the path. I would like a quick and easy way to present a file selection dialog to the user, they can select the file, and then it's loaded to the database. (In my use case, if they happened to chose the wrong file, it would fail parsing, and wouldn't be a problem even if it was loaded to the database.)
import tkFileDialog
file_path_string = tkFileDialog.askopenfilename()
This code is close to what I want, but it leaves an annoying empty frame open (which isn't able to be closed, probably because I haven't registered a close event handler).
I don't have to use tkInter, but since it's in the Python standard library it's a good candidate for quickest and easiest solution.
Whats a quick and easy way to prompt for a file or filename in a script without any other UI?
Tkinter is the easiest way if you don't want to have any other dependencies.
To show only the dialog without any other GUI elements, you have to hide the root window using the withdraw method:
import tkinter as tk
from tkinter import filedialog
root = tk.Tk()
root.withdraw()
file_path = filedialog.askopenfilename()
Python 2 variant:
import Tkinter, tkFileDialog
root = Tkinter.Tk()
root.withdraw()
file_path = tkFileDialog.askopenfilename()
You can use easygui:
import easygui
path = easygui.fileopenbox()
To install easygui, you can use pip:
pip3 install easygui
It is a single pure Python module (easygui.py) that uses tkinter.
Try with wxPython:
import wx
def get_path(wildcard):
app = wx.App(None)
style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST
dialog = wx.FileDialog(None, 'Open', wildcard=wildcard, style=style)
if dialog.ShowModal() == wx.ID_OK:
path = dialog.GetPath()
else:
path = None
dialog.Destroy()
return path
print get_path('*.txt')
pywin32 provides access to the GetOpenFileName win32 function. From the example
import win32gui, win32con, os
filter='Python Scripts\0*.py;*.pyw;*.pys\0Text files\0*.txt\0'
customfilter='Other file types\0*.*\0'
fname, customfilter, flags=win32gui.GetOpenFileNameW(
InitialDir=os.environ['temp'],
Flags=win32con.OFN_ALLOWMULTISELECT|win32con.OFN_EXPLORER,
File='somefilename', DefExt='py',
Title='GetOpenFileNameW',
Filter=filter,
CustomFilter=customfilter,
FilterIndex=0)
print 'open file names:', repr(fname)
print 'filter used:', repr(customfilter)
print 'Flags:', flags
for k,v in win32con.__dict__.items():
if k.startswith('OFN_') and flags & v:
print '\t'+k
Using tkinter (python 2) or Tkinter (python 3) it's indeed possible to display file open dialog (See other answers here). Please notice however that user interface of that dialog is outdated and does not corresponds to newer file open dialogs available in Windows 10.
Moreover - if you're looking on way to embedd python support into your own application - you will find out soon that tkinter library is not open source code and even more - it is commercial library.
(For example search for "activetcl pricing" will lead you to this web page: https://reviews.financesonline.com/p/activetcl/)
So tkinter library will cost money for any application wanting to embedd python.
I by myself managed to find pythonnet library:
Overview here: http://pythonnet.github.io/
Source code here: https://github.com/pythonnet/pythonnet
(MIT License)
Using following command it's possible to install pythonnet:
pip3 install pythonnet
And here you can find out working example for using open file dialog:
https://stackoverflow.com/a/50446803/2338477
Let me copy an example also here:
import sys
import ctypes
co_initialize = ctypes.windll.ole32.CoInitialize
# Force STA mode
co_initialize(None)
import clr
clr.AddReference('System.Windows.Forms')
from System.Windows.Forms import OpenFileDialog
file_dialog = OpenFileDialog()
ret = file_dialog.ShowDialog()
if ret != 1:
print("Cancelled")
sys.exit()
print(file_dialog.FileName)
If you also miss more complex user interface - see Demo folder
in pythonnet git.
I'm not sure about portability to other OS's, haven't tried, but .net 5 is planned to be ported to multiple OS's (Search ".net 5 platforms", https://devblogs.microsoft.com/dotnet/introducing-net-5/ ) - so this technology is also future proof.
If you don't need the UI or expect the program to run in a CLI, you could parse the filepath as an argument. This would allow you to use the autocomplete feature of your CLI to quickly find the file you need.
This would probably only be handy if the script is non-interactive besides the filepath input.
Another os-agnostic option, use pywebview:
import webview
def webview_file_dialog():
file = None
def open_file_dialog(w):
nonlocal file
try:
file = w.create_file_dialog(webview.OPEN_DIALOG)[0]
except TypeError:
pass # user exited file dialog without picking
finally:
w.destroy()
window = webview.create_window("", hidden=True)
webview.start(open_file_dialog, window)
# file will either be a string or None
return file
print(webview_file_dialog())
Environment: python3.8.6 on Mac - though I've used pywebview on windows 10 before.
I just stumbled on this little trick for Windows only: run powershell.exe from subprocess.
import subprocess
sys_const = ssfDESKTOP # Starts at the top level
# sys_const = 0x2a # Correct value for "Program Files (0x86)" folder
powershell_browse = "(new-object -COM 'Shell.Application')."
powershell_browse += "BrowseForFolder(0,'window title here',0,sys_const).self.path"
ret = subprocess.run(["powershell.exe",powershell_browse], stdout=subprocess.PIPE)
print(ret.stdout.decode())
Note the optional use of system folder constants. (There's an obscure typo in shldisp.h that the "Program Files (0x86)" constant was assigned wrong. I added a comment with the correct value. Took me a bit to figure that one out.)
More info below:
System folder constants
Several months ago, I wrote a blog post detailing how to achieve tab-completion in the standard Python interactive interpreter--a feature I once thought only available in IPython. I've found it tremendously handy given that I sometimes have to switch to the standard interpreter due to IPython unicode issues.
Recently I've done some work in OS X. To my discontent, the script doesn't seem to work for OS X's Terminal application. I'm hoping some of you with experience in OS X might be able to help me trouble-shoot it so it can work in Terminal, as well.
I am reproducing the code below
import atexit
import os.path
try:
import readline
except ImportError:
pass
else:
import rlcompleter
class IrlCompleter(rlcompleter.Completer):
"""
This class enables a "tab" insertion if there's no text for
completion.
The default "tab" is four spaces. You can initialize with '\t' as
the tab if you wish to use a genuine tab.
"""
def __init__(self, tab=' '):
self.tab = tab
rlcompleter.Completer.__init__(self)
def complete(self, text, state):
if text == '':
readline.insert_text(self.tab)
return None
else:
return rlcompleter.Completer.complete(self,text,state)
#you could change this line to bind another key instead tab.
readline.parse_and_bind('tab: complete')
readline.set_completer(IrlCompleter('\t').complete)
# Restore our command-line history, and save it when Python exits.
history_path = os.path.expanduser('~/.pyhistory')
if os.path.isfile(history_path):
readline.read_history_file(history_path)
atexit.register(lambda x=history_path: readline.write_history_file(x))
Note that I have slightly edited it from the version on my blog post so that the IrlCompleter is initialized with a true tab, which seems to be what is output by the Tab key in Terminal.
This should work under Leopard's python:
import rlcompleter
import readline
readline.parse_and_bind ("bind ^I rl_complete")
Whereas this one does not:
import readline, rlcompleter
readline.parse_and_bind("tab: complete")
Save it in ~/.pythonrc.py and execute in .bash_profile
export PYTHONSTARTUP=$HOME/.pythonrc.py
here is a full cross platform version of loading tab completion for Windows/OS X/Linux in one shot:
#Code UUID = '9301d536-860d-11de-81c8-0023dfaa9e40'
import sys
try:
import readline
except ImportError:
try:
import pyreadline as readline
# throw open a browser if we fail both readline and pyreadline
except ImportError:
import webbrowser
webbrowser.open("http://ipython.scipy.org/moin/PyReadline/Intro#line-36")
# throw open a browser
#pass
else:
import rlcompleter
if(sys.platform == 'darwin'):
readline.parse_and_bind ("bind ^I rl_complete")
else:
readline.parse_and_bind("tab: complete")
From http://www.farmckon.net/?p=181
To avoid having to use more GPL code, Apple doesn't include a real readline. Instead it uses the BSD-licensed libedit, which is only mostly-readline-compatible. Build your own Python (or use Fink or MacPorts) if you want completion.
This works for me on both Linux bash and OS X 10.4
import readline
import rlcompleter
readline.parse_and_bind('tab: complete')
If after trying the above, it still doesn't work, then try to execute in the shell:
sudo easy_install readline
Then, create ~/.profile file with the content:
export PYTHONSTARTUP=$HOME/.pythonrc.py
and a ~/.pythonrc.py file with the content:
try:
import readline
except:
print ("Module readline is not available.")
else:
import rlcompleter
readline.parse_and_bind("tab: complete")
Thanks to Steven Bamford for the easy_install tip, and Nicolas for the file content.
The documented way to tell libedit (the Mac OS semi-readline) from the real one is:
if "libedit" in readline.doc:
pass # Mac case
else:
pass # GNU readline case
After crashing into many issues dealing with Python (2 and 3) on FreeBSD, I finally got a proper extension to work using libedit directly as the completer for Python.
The basic issue with libedit/readline is that Python's completion and input was heavily bent towards GNU readline... Sadly, this is actually not a particularly good interface. It requires a giant number of globals in C and does not work well on an "instance" basis.
Solution:
https://github.com/mark-nicholson/python-editline
This is a true separate python extension which directly links to libedit using the actual "libedit" interface -- not the readline glue on the side.
I have tested it pretty thoroughly on Ubuntu, FreeBSD, OpenBSD, NetBSD and MacOS -- results are posted in the readme. The c-code is very clean and has virtually no platform dependent bits -- unlike the readline.c module in Python.
Notes:
It works on Python3 > 3.2.
It is NOT a drop-in replacement for 'import readline' in other scripts, but those scripts can be adjusted easily.
It can co-exist with readline.so -- there is code for a sitecustomize.py file which enables the selection.
It can use a distribution 'libedit.so', a custom built one or libedit built into the extension itself.
I need a way to determine the space remaining on a disk volume using python on linux, Windows and OS X. I'm currently parsing the output of the various system calls (df, dir) to accomplish this - is there a better way?
import ctypes
import os
import platform
import sys
def get_free_space_mb(dirname):
"""Return folder/drive free space (in megabytes)."""
if platform.system() == 'Windows':
free_bytes = ctypes.c_ulonglong(0)
ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(dirname), None, None, ctypes.pointer(free_bytes))
return free_bytes.value / 1024 / 1024
else:
st = os.statvfs(dirname)
return st.f_bavail * st.f_frsize / 1024 / 1024
Note that you must pass a directory name for GetDiskFreeSpaceEx() to work
(statvfs() works on both files and directories). You can get a directory name
from a file with os.path.dirname().
Also see the documentation for os.statvfs() and GetDiskFreeSpaceEx.
Install psutil using pip install psutil. Then you can get the amount of free space in bytes using:
import psutil
print(psutil.disk_usage(".").free)
You could use the wmi module for windows and os.statvfs for unix
for window
import wmi
c = wmi.WMI ()
for d in c.Win32_LogicalDisk():
print( d.Caption, d.FreeSpace, d.Size, d.DriveType)
for unix or linux
from os import statvfs
statvfs(path)
If you're running python3:
Using shutil.disk_usage()with os.path.realpath('/') name-regularization works:
from os import path
from shutil import disk_usage
print([i / 1000000 for i in disk_usage(path.realpath('/'))])
Or
total_bytes, used_bytes, free_bytes = disk_usage(path.realpath('D:\\Users\\phannypack'))
print(total_bytes / 1000000) # for Mb
print(used_bytes / 1000000)
print(free_bytes / 1000000)
giving you the total, used, & free space in MB.
If you dont like to add another dependency you can for windows use ctypes to call the win32 function call directly.
import ctypes
free_bytes = ctypes.c_ulonglong(0)
ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(u'c:\\'), None, None, ctypes.pointer(free_bytes))
if free_bytes.value == 0:
print 'dont panic'
From Python 3.3 you can use shutil.disk_usage("/").free from standard library for both Windows and UNIX :)
A good cross-platform way is using psutil: http://pythonhosted.org/psutil/#disks
(Note that you'll need psutil 0.3.0 or above).
You can use df as a cross-platform way. It is a part of GNU core utilities. These are the core utilities which are expected to exist on every operating system. However, they are not installed on Windows by default (Here, GetGnuWin32 comes in handy).
df is a command-line utility, therefore a wrapper required for scripting purposes.
For example:
from subprocess import PIPE, Popen
def free_volume(filename):
"""Find amount of disk space available to the current user (in bytes)
on the file system containing filename."""
stats = Popen(["df", "-Pk", filename], stdout=PIPE).communicate()[0]
return int(stats.splitlines()[1].split()[3]) * 1024
Below code returns correct value on windows
import win32file
def get_free_space(dirname):
secsPerClus, bytesPerSec, nFreeClus, totClus = win32file.GetDiskFreeSpace(dirname)
return secsPerClus * bytesPerSec * nFreeClus
The os.statvfs() function is a better way to get that information for Unix-like platforms (including OS X). The Python documentation says "Availability: Unix" but it's worth checking whether it works on Windows too in your build of Python (ie. the docs might not be up to date).
Otherwise, you can use the pywin32 library to directly call the GetDiskFreeSpaceEx function.
I Don't know of any cross-platform way to achieve this, but maybe a good workaround for you would be to write a wrapper class that checks the operating system and uses the best method for each.
For Windows, there's the GetDiskFreeSpaceEx method in the win32 extensions.
Most previous answers are correct, I'm using Python 3.10 and shutil.
My use case was Windows and C drive only ( but you should be able to extend this for you Linux and Mac as well (here is the documentation)
Here is the example for Windows:
import shutil
total, used, free = shutil.disk_usage("C:/")
print("Total: %d GiB" % (total // (2**30)))
print("Used: %d GiB" % (used // (2**30)))
print("Free: %d GiB" % (free // (2**30)))