7

我想为我的 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 安装程序来创建桌面或开始菜单快捷方式?

4

3 回答 3

2

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

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

#!/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

于 2017-03-10T16:16:54.960 回答
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)
于 2014-07-25T03:44:17.837 回答
1

至少对于 Windows 上的 32 位 Python 3.6.5,setuptools 确实可以做到这一点。但是根据接受的答案,通过反复试验,我发现了一些可能导致您的脚本无法执行您想要的操作的问题。

  1. create_shortcut不接受关键字参数,只接受位置参数,因此它在您的代码中的使用无效
  2. 您必须.lnk为 Windows 添加扩展程序才能识别快捷方式
  3. 我发现sys.executable将是安装程序可执行文件的名称,而不是 python 可执行文件的名称
  4. 如前所述,您看不到stdout或者stderr您可能想要登录到文本文件。我建议还重定向sys.stdoutsys.stderr日志文件。
  5. (可能不相关)正如这个问题中提到的,似乎存在由 . 生成的版本字符串的错误bdist_wininst。我使用那里的答案中的 hexediting hack 来解决这个问题。答案中的位置不一样,你必须找到-32自己。

完整的示例脚本:

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)
于 2018-04-19T05:53:53.007 回答