Search code examples
pythonlinuxpyinstallerdesktop

Can pyinstaller make a desktop file (with an icon) for an Ubuntu python app


I'm building a python app which is to pack up as a single executable and work on Windows, MacOS and Linux. Have made a lot of progress and am using a workflow on Github to build using pyinstaller for each OS. Most things are working fine.

Right now I am working on getting an icon onto the executable instead of the default system icon.

I have a spec file for pyinstaller and I have a section where the icon is mentioned:

exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.zipfiles,
    a.datas,
    [],
    name='my_app_name',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    upx_exclude=[],
    runtime_tmpdir=None,
    console=False,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
    icon='images/my_icon.ico'
)

This seems to work well for Windows and the output exe file has my icon which is great. My question is, is there a way to do this for Linux. I know that normally for Linux you need to build a .desktop file, so I guess the question is three-fold:

  1. Is there a way to give a file an icon without a desktop file (in Linux)?

or

  1. Is there a way to somehow build and connect a desktop file to my Linux file in pyinstaller?

or

  1. Is there some python way to self-create a desktop file for my python app?

Thanks


Solution

  • OK, so I found a a way to do it - not really in pyinstaller, but it works for me.

    I am building my app(s) using Github's Workflow feature - so we have a file called python_build.yml under /.github/workflows

    So, after building the executable with pyinstaller into a single file I have:

    - name: Assign artifacts
      if: github.event_name == 'release'
      uses: knicknic/os-specific-run@v1.0.4
      with:
        macos: echo "artifact_path=My_app.dmg" >> $GITHUB_ENV
        windows: echo "artifact_path=dist/my_app.exe" >> $env:GITHUB_ENV
        linux: echo "artifact_path=dist/my_app.deb" >> $GITHUB_ENV
    - name: Create DMG app (for MacOS)
      if: github.event_name == 'release' && runner.os == 'macOS'
      run: |
        rm -rf dist/my_app
        brew install create-dmg
        create-dmg --app-drop-link 100 100 "${{env.artifact_path}}" dist/
    - name: Create DEB file for Ubuntu
      if: github.event_name == 'release' && runner.os == 'linux'
      run: |
        cp dist/my_app my-app/usr/share/bin/my_app 
        rm -rf my-app/usr/share/bin/hold
        fakeroot dpkg-deb --build my-app
    - name: Sleep while DEB build happens
      if: github.event_name == 'release' && runner.os == 'linux'
      run: sleep 30s
      shell: bash
    - name: Copy over DEB to dist folder
      if: github.event_name == 'release' && runner.os == 'linux'
      run: |
        cp my-app.deb dist/my-appi.deb
        rm -rf dist/my_app
    - name: Add artifacts to release
      if: github.event_name == 'release'
      uses: ncipollo/release-action@v1.11.2
      with:
        allowUpdates: true
        replacesArtifacts: false
        artifacts: ${{env.artifact_path}} 
    

    So, there's a couple of important points to add to this. I have saved in my Git repository a small directory structure named my-app. I don't think this is the web page I used but it's similar. Basically a folder named my-app with two folders underneath it : DEBIAN and usr. In the DEBIAN there is one file called control with something like this in it:

    Package: my-app
    Version: 1.1-7
    Section: Utility
    Priority: optional
    Architecture: i386
    Depends: 
    Homepage: https://www.ourwebsite.com/
    Maintainer: Kibi Hofmann <kibi@ourwebsite.com>
    Installed-Size: 31300
    Description: Useful app for your use
     No dependencies or significant conflicts.
     This app is used to do your stuff
    

    Under usr is a folder share which has three more folders under it applications, bin and icons. The bin folder has (in my Git repository) an empty file called hold just so that the directory structure is maintained in Git - you can see that the hold file is deleted and replaced by the compiled binary after pyinstaller is finished. The Desktop file itself is under applications and looks like this:

    [Desktop Entry]
    Type=Application
    Name=My App
    GenericName=My cool app
    Comment=Keep your computer happy
    Exec=/usr/share/bin/my-app
    Icon=myr_icon
    Terminal=false
    Categories=Application
    

    Hope this helps someone!