如何使用setuptools Windows安装程序在开始菜单中创建快捷方式

时间:2014-07-19 13:54:24

标签: python windows installer setuptools startmenu

我想为我的Python Windows安装程序包创建一个开始菜单或桌面快捷方式。我正在尝试关注https://docs.python.org/3.4/distutils/builtdist.html#the-postinstallation-script

这是我的剧本;

import sys

from os.path import dirname, join, expanduser

pyw_executable = sys.executable.replace('python.exe','pythonw.exe')
script_file = join(dirname(pyw_executable), 'Scripts', 'tklsystem-script.py')
w_dir = expanduser(join('~','lsf_files'))

print(sys.argv)

if sys.argv[1] == '-install':
    print('Creating Shortcut')
    create_shortcut(
        target=pyw_executable,
        description='A program to work with L-System Equations',
        filename='L-System Tool',
        arguments=script_file,
        workdir=wdir
    )

我还在脚本设置选项中指定了此脚本,如上述文档所示。

以下是我用来创建安装程序的命令;

python setup.py bdist_wininst --install-script tklsystem-post-install.py

使用创建的Windows安装程序安装我的软件包之后,我无法找到创建快捷方式的位置,也无法确认我的脚本是否运行?

如何使setuptools生成Windows安装程序以创建桌面或开始菜单快捷方式?

3 个答案:

答案 0 :(得分:1)

如果要确认脚本是否正在运行,可以打印到文件而不是控制台。看起来在安装后脚本中打印到控制台的文本不会显示。

试试这个:

import sys
from os.path import expanduser, join

pyw_executable = join(sys.prefix, "pythonw.exe")
shortcut_filename = "L-System Toolsss.lnk"
working_dir = expanduser(join('~','lsf_files'))
script_path = join(sys.prefix, "Scripts", "tklsystem-script.py")

if sys.argv[1] == '-install':
    # Log output to a file (for test)
    f = open(r"C:\test.txt",'w')
    print('Creating Shortcut', file=f)

    # Get paths to the desktop and start menu
    desktop_path = get_special_folder_path("CSIDL_COMMON_DESKTOPDIRECTORY")
    startmenu_path = get_special_folder_path("CSIDL_COMMON_STARTMENU")

    # Create shortcuts.
    for path in [desktop_path, startmenu_path]:
        create_shortcut(pyw_executable,
                    "A program to work with L-System Equations",
                    join(path, shortcut_filename),
                    script_path,
                    working_dir)

答案 1 :(得分:1)

就像其他人在这里和其他地方评论过的那样,支持功能似乎根本不起作用(至少没有使用setuptools)。经过一天的搜索各种资源后,我找到了至少创建桌面快捷方式的方法。我正在分享我的解决方案(基本上是我发现herehere的代码混合。我应该补充一点,我的情况与yasar略有不同,因为它创建了一个已安装软件包的快捷方式(即Python的 Scripts 目录中的.exe文件)而不是脚本。

简而言之,我在我的setup.py中添加了 post_install 函数,然后使用Python extensions for Windows创建了快捷方式。从Windows注册表中读取Desktop文件夹的位置(还有其他方法,但如果桌面位于非标准位置,则它们可能不可靠)。

#!/usr/bin/env python

import os
import sys
import sysconfig
if sys.platform == 'win32':
    from win32com.client import Dispatch
    import winreg

def get_reg(name,path):
    # Read variable from Windows Registry
    # From https://stackoverflow.com/a/35286642
    try:
        registry_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, path, 0,
                                       winreg.KEY_READ)
        value, regtype = winreg.QueryValueEx(registry_key, name)
        winreg.CloseKey(registry_key)
        return value
    except WindowsError:
        return None

def post_install():
    # Creates a Desktop shortcut to the installed software

    # Package name
    packageName = 'mypackage'

    # Scripts directory (location of launcher script)
    scriptsDir = sysconfig.get_path('scripts')

    # Target of shortcut
    target = os.path.join(scriptsDir, packageName + '.exe')

    # Name of link file
    linkName = packageName + '.lnk'

    # Read location of Windows desktop folder from registry
    regName = 'Desktop'
    regPath = r'Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders'
    desktopFolder = os.path.normpath(get_reg(regName,regPath))

    # Path to location of link file
    pathLink = os.path.join(desktopFolder, linkName)
    shell = Dispatch('WScript.Shell')
    shortcut = shell.CreateShortCut(pathLink)
    shortcut.Targetpath = target
    shortcut.WorkingDirectory = scriptsDir
    shortcut.IconLocation = target
    shortcut.save()

setup(name='mypackage',
      ...,
      ...)

if sys.argv[1] == 'install' and sys.platform == 'win32':
    post_install()

这是一个完整设置脚本的链接,我在其中使用了这个:

https://github.com/KBNLresearch/iromlab/blob/master/setup.py

答案 2 :(得分:0)

至少使用Python 3.6.5,Windows上的32位,setuptools 为此工作。但根据接受的答案,通过反复试验,我发现了一些可能导致您的脚本无法按照您的意愿行事的问题。

  1. create_shortcut不接受关键字参数,只接受位置,因此在代码中的使用无效
  2. 您必须为Windows添加.lnk扩展名才能识别快捷方式
  3. 我发现sys.executable将是安装程序可执行文件的名称,而不是python可执行文件
  4. 如上所述,您无法查看stdoutstderr,因此您可能希望登录到文本文件。我建议还将sys.stdoutsys.stderr重定向到日志文件。
  5. (可能不相关)如上所述[{3}},bdist_wininst生成的版本字符串似乎存在错误。我使用了来自答案的hexediting hack来解决这个问题。答案中的位置不一样,您必须自己找到-32
  6. 完整示例脚本:

    import sys
    import os
    import datetime
    global datadir
    datadir = os.path.join(get_special_folder_path("CSIDL_APPDATA"), "mymodule")
    def main(argv):
        if "-install" in argv:
            desktop = get_special_folder_path("CSIDL_DESKTOPDIRECTORY")
            print("Desktop path: %s" % repr(desktop))
            if not os.path.exists(datadir):
                os.makedirs(datadir)
                dir_created(datadir)
                print("Created data directory: %s" % repr(datadir))
            else:
                print("Data directory already existed at %s" % repr(datadir))
    
            shortcut = os.path.join(desktop, "MyModule.lnk")
            if os.path.exists(shortcut):
                print("Remove existing shortcut at %s" % repr(shortcut))
                os.unlink(shortcut)
    
            print("Creating shortcut at %s...\n" % shortcut)
            create_shortcut(
                r'C:\Python36\python.exe',
                "MyModuleScript",
                shortcut, 
                "",
                datadir)
            file_created(shortcut)
            print("Successfull!")
        elif "-remove" in sys.argv:
            print("Removing...")
            pass
    
    
    if __name__ == "__main__":
        logfile = r'C:\mymodule_install.log' # Fallback location
        if os.path.exists(datadir):
            logfile = os.path.join(datadir, "install.log")
        elif os.environ.get("TEMP") and os.path.exists(os.environ.get("TEMP"),""):
            logfile = os.path.join(os.environ.get("TEMP"), "mymodule_install.log")
    
        with open(logfile, 'a+') as f:
            f.write("Opened\r\n")
            f.write("Ran %s %s at %s" % (sys.executable, " ".join(sys.argv), datetime.datetime.now().isoformat()))
            sys.stdout = f
            sys.stderr = f
            try:
                main(sys.argv)
            except Exception as e:
                raise
            f.close()
    
        sys.exit(0)