I have created shortcuts for executables and it works, but when I try to create one for a folder it does not work.
It does create a shortcut, it is just not the right 'Target Type'. Please take a look at the image below.
Instead of 'File', the target type should be 'File folder'. The problem is that when I open the shortcut it asks me which program do I want to open the File with and it does not open the folder.
The function I'm using to create the shortcuts is the following
from win32com.client import Dispatch
import winshell
import os
def create_shortcuts(self, tool_name, exe_path, startin, icon_path):
shell = Dispatch('WScript.Shell')
shortcut_file = os.path.join(winshell.desktop(), tool_name + '.lnk')
shortcut = shell.CreateShortCut(shortcut_file)
shortcut.Targetpath = exe_path
shortcut.WorkingDirectory = startin
shortcut.IconLocation = icon_path
shortcut.save()
I don't know if it's possible to set the 'Target Type'. I couldn't find a way to do it, but I do know there must be a way.
If you want to use .Net "clr" (especially if you already require it):
First run this... you will have to ship the output of this command with your application:
"c:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\TlbImp.exe" %SystemRoot%\system32\wshom.ocx /out:Interop.IWshRuntimeLibrary.dll
tlbimp.exe might even be in the path if you installed the Windows SDK in a fairly standard way. But if not, it's OK, you'll just ship the "assembly" (fancy word for interface-providing dll in .Net land) with your application.
Then this code will work in python:
import clr
sys.path.append(DIRECTORY_WHERE_YOU_PUT_THE_DLL)
clr.AddReference('Interop.IWshRuntimeLibrary')
import Interop.IWshRuntimeLibrary
sc = Interop.IWshRuntimeLibrary.WshShell().CreateShortcut("c:\\test\\sc.lnk")
isc = Interop.IWshRuntimeLibrary.IWshShortcut(sc)
isc.set_TargetPath("C:\\")
isc.Save()
.... the above code, with far too much modification and preamble, might even work with Mono.
For future reference: I observed the described behavior in python 3.9.6 when creating a shortcut to a non-existing directory, which was easily fixed by incorporating os.makedirs() into the method.
I've added a method parameter to the version I'm using, so it can handle shortcuts to files and directories:
def create_shortcuts(self, tool_name, exe_path, startin, icon_path, is_directory=False):
if is_directory:
os.makedirs(exe_path, exist_ok=True)
shell = Dispatch('WScript.Shell')
shortcut_file = os.path.join(winshell.desktop(), tool_name + '.lnk')
shortcut = shell.CreateShortCut(shortcut_file)
shortcut.Targetpath = exe_path
shortcut.WorkingDirectory = startin
shortcut.IconLocation = icon_path
shortcut.save()
Related
I'm writing an installer for a Python app I'm making, and needed to have paths for different file locations. These are what I have so far, and so wondered if these were correct of if I should change any of them (particularly the Linux/macOS ones)
I have a path for:
the actual binary
for the config file
the README
the start menu, should the user want to add a shortcut (this will create the .desktop file on Linux/macOS, a shortcut on Windows)
the desktop, should the user want to add a shortcut
Can anyone let me know if there should be changes to these to make them work on all devices etc
Windows
binary = '%ProgramFiles%'
configs = '%AppData%'
README = '%ProgramFiles%'
start menu = '%AppData%\Microsoft\Windows\Start Menu'
desktop = os.path.expanduser('~\Desktop')
Linux
binary = '/usr/local/bin'
config = os.path.expanduser('~/.config')
README = '/usr/local/share/doc/packages'
start menu = os.path.expanduser('~/.local/share/applications')
desktop = os.path.expanduser('~/Desktop')
macOS
binary = '/usr/local/bin'
config = os.path.expanduser('~/.config')
README = '/usr/local/share/doc/packages'
start menu = os.path.expanduser('~/.local/share/applications')
desktop = os.path.expanduser('~/Desktop')
Thanks in advance
EDIT: I have added the option for the user to change the paths anyway so it doesn't have to be perfect but I would like the best 'normal' paths
My recommendation is to use sysconfig as much as possible.
For example on a POSIX system get_paths() returns:
In [5]: sysconfig.get_paths()
Out[5]:
{'stdlib': '/usr/local/lib/python3.9',
'platstdlib': '/usr/local/lib/python3.9',
'purelib': '/usr/local/lib/python3.9/site-packages',
'platlib': '/usr/local/lib/python3.9/site-packages',
'include': '/usr/local/include/python3.9',
'platinclude': '/usr/local/include/python3.9',
'scripts': '/usr/local/bin',
'data': '/usr/local'}
Note that sysconfig supports different schemes:
In [6]: sysconfig.get_scheme_names()
Out[6]:
('nt',
'nt_user',
'osx_framework_user',
'posix_home',
'posix_prefix',
'posix_user')
The home or user schemes are handy when the installer doesn't have root or administrator privileges.
My problem is that i can't find a solution to create a shortcut to an folder with python. Only to a file, code example:
shell = Dispatch('WScript.Shell')
shortcut = shell.CreateShortCut(path)
shortcut.Targetpath = target
shortcut.save()
But I need a shortcut to a whole folder.
My target example is:
C:/Usersand C:/Users/user/Downloads
You can use the below code to create shortcut to a directory
from win32com.client import Dispatch
path = r"C:\Users\user\Downloads\shortcut.lnk" #This is where the shortcut will be created
target = r"C:\Users\user\Downloads" # directory to which the shortcut is created
shell = Dispatch('WScript.Shell')
shortcut = shell.CreateShortCut(path)
shortcut.Targetpath = target
shortcut.save()
Backslashes (\) must be used in shortcut.Targetpath for shortcuts to work correctly for folders.
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'm currently writing a plugin for Sublime Text 3, which aims to offer the user a more flexible session management.
As it seems the API doesn't offer a way to open a .sublime-project file. I'm obviously able to open files as usual - using window.open_file - but not to tell Sublime to open a specific project file.
It will just open it in a new tab, which isn't exactly what I was hoping for.
I'm able to access and set the project_data using window.project_data and window.set_project_data, but while there is a window.project_file_name method it has no counterpart.
This is problematic since the project_data often contains relative paths, which need to be interpreted relative to the .sublime-project files location. If I just dump the data as found into a new window (set_project_data), all relative paths will be interpreted as relative to root (at least on my Ubuntu system).
I can handle the relative paths myself and modify the project_data accordingly but that's hacky.
Is there any undocumented method or something I missed?
EDIT: The plugin in question.
Try to open the file with suffix ':1' - meaning 'line number #1':
This works for me:
$ subl projectname.sublime-project:1
Found a method to do this in a Sublime Plugin called ProjectManager. You'll find the code in this file...
https://github.com/randy3k/ProjectManager/blob/master/pm.py
# Code lifted from https://github.com/randy3k/ProjectManager/blob/master/pm.py
def subl(args=[]):
# learnt from SideBarEnhancements
executable_path = sublime.executable_path()
if sublime.platform() == 'linux':
subprocess.Popen([executable_path] + [args])
if sublime.platform() == 'osx':
app_path = executable_path[:executable_path.rfind(".app/") + 5]
executable_path = app_path + "Contents/SharedSupport/bin/subl"
subprocess.Popen([executable_path] + args)
if sublime.platform() == "windows":
def fix_focus():
window = sublime.active_window()
view = window.active_view()
window.run_command('focus_neighboring_group')
window.focus_view(view)
sublime.set_timeout(fix_focus, 300)
subl(project_file) # The something.sublime-project file.