How to create a shortcut in startmenu using setuptools windows installer

前端 未结 3 2129
说谎
说谎 2021-02-15 23:13

I want to create a start menu or Desktop shortcut for my Python windows installer package. I am trying to follow https://docs.python.org/3.4/distutils/builtdist.html#the-postins

相关标签:
3条回答
  • 2021-02-15 23:51

    Like others have commented here and elsewhere the support functions don't seem to work at all (at least not with setuptools). After a good day's worth of searching through various resources I found a way to create at least the Desktop shortcut. I'm sharing my solution (basically an amalgam of code I found here and here). I should add that my case is slightly different from yasar's, because it creates a shortcut to an installed package (i.e. an .exe file in Python's Scripts directory) instead of a script.

    In short, I added a post_install function to my setup.py, and then used the Python extensions for Windows to create the shortcut. The location of the Desktop folder is read from the Windows registry (there are other methods for this, but they can be unreliable if the Desktop is at a non-standard location).

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

    Here's a link to a full setup script in which I used this:

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

    0 讨论(0)
  • 2021-02-16 00:05

    If you want to confirm whether the script is running or not, you can print to a file instead of the console. Looks like text you print to console in the post-install script won't show up.

    Try this:

    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)
    
    0 讨论(0)
  • 2021-02-16 00:10

    At least with Python 3.6.5, 32bit on Windows, setuptools does work for this. But based on the accepted answer, by trial and error I found some issues that may have caused your script to fail to do what you wanted.

    1. create_shortcut does not accept keyword arguments, only positional, so its usage in your code is invalid
    2. You must add a .lnk extension for Windows to recognise the shortcut
    3. I found sys.executable will be the name of the installer executable, not the python executable
    4. As mentioned, you can't see stdout or stderr so you might want to log to a text file. I would suggest also redirecting sys.stdout and sys.stderr to the log file.
    5. (Maybe not relevant) as mentioned in this question there appears to be a bug with the version string generated by bdist_wininst. I used the hexediting hack from an answer there to work around this. The location in the answer is not the same, you have to find the -32 yourself.

    Full example script:

    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)
    
    0 讨论(0)
提交回复
热议问题