Skip to content

Commit d09495a

Browse files
authored
Fix installer and update the UI (#9)
1 parent e4bbdc8 commit d09495a

File tree

17 files changed

+671
-200
lines changed

17 files changed

+671
-200
lines changed

.github/workflows/ci_cd.yml

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ jobs:
5454

5555
- uses: pyansys/actions/tests-pytest@v4
5656
with:
57-
pytest-extra-args: "--cov=ansys --cov-report=term"
57+
python-version: ${{ matrix.python-version }}
5858

5959
doc-build:
6060
name: "Build documentation"
@@ -119,12 +119,7 @@ jobs:
119119
run: pyinstaller frozen.spec
120120

121121
- name: Install NSIS
122-
run: |
123-
iwr -useb get.scoop.sh -outfile 'install.ps1'
124-
.\install.ps1 -RunAsAdmin
125-
scoop update
126-
scoop bucket add extras
127-
scoop install nsis
122+
run: choco install nsis -y
128123

129124
- name: Print NSIS version
130125
run: makensis -VERSION
@@ -159,12 +154,8 @@ jobs:
159154
- name: Display structure of downloaded files
160155
run: ls -R
161156

162-
- uses: montudor/action-zip@v1
163-
with:
164-
args: zip -qq -r python-installer-gui.zip installer
165-
166157
- name: "Release to GitHub"
167158
uses: softprops/action-gh-release@v1
168159
with:
169-
files: python-installer-gui.zip
160+
files: installer/*.exe
170161
generate_release_notes: true

frozen.spec

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ except NameError:
1515

1616
OUT_PATH = 'ansys_python_manager'
1717
APP_NAME = 'Ansys Python Manager'
18-
ASSETS_PATH = os.path.join(THIS_PATH, 'src/ansys/tools/installer/assets')
18+
INSTALLER_PATH = os.path.join(THIS_PATH, 'src/ansys/tools/installer')
19+
ASSETS_PATH = os.path.join(INSTALLER_PATH, 'assets')
1920
ICON_FILE = os.path.join(ASSETS_PATH, 'pyansys_icon.ico')
2021

2122
# consider testing paths
@@ -28,13 +29,14 @@ added_files = [
2829
(os.path.join(ASSETS_PATH, 'pyansys-light-crop.png'), 'assets'),
2930
(os.path.join(ASSETS_PATH, 'ansys-favicon.png'), 'assets'),
3031
(os.path.join(ASSETS_PATH, 'pyansys_icon.ico'), 'assets'),
32+
(os.path.join(INSTALLER_PATH, 'VERSION'), '.'),
3133
]
3234

3335
a = Analysis([main_py],
3436
pathex=[],
3537
binaries=[],
3638
datas=added_files,
37-
hiddenimports=[],
39+
hiddenimports=['_cffi_backend'],
3840
hookspath=[],
3941
runtime_hooks=[],
4042
excludes=[],

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ maintainers = [
1515
{name = "PyAnsys Developers", email = "[email protected]"},
1616
]
1717
dependencies = [
18+
"packaging",
19+
"PyGitHub",
1820
"appdirs",
1921
"requests",
2022
"PySide6",

setup.nsi

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,79 @@
11
; NSIS script for Ansys Python Manager installer
22

3-
43
; Set the name, version, and output path of the installer
4+
!define VERSION_FILE "src/ansys/tools/installer/VERSION"
55
!define PRODUCT_NAME "Ansys Python Manager"
6-
!define PRODUCT_VERSION "0.1.0-beta0"
7-
!define OUTFILE_NAME "Ansys Python Manager Setup-v${PRODUCT_VERSION}.exe"
6+
!define /file PRODUCT_VERSION "src/ansys/tools/installer/VERSION"
7+
!define OUTFILE_NAME "Ansys-Python-Manager-Setup-v${PRODUCT_VERSION}.exe"
8+
89
Name "${PRODUCT_NAME}"
910
VIProductVersion "${PRODUCT_VERSION}"
1011
OutFile "dist\${OUTFILE_NAME}"
1112

13+
14+
!include "MUI2.nsh"
15+
!include "InstallOptions.nsh"
16+
!define MUI_PAGE_CUSTOMFUNCTION_PRE oneclickpre
17+
!define MUI_PAGE_CUSTOMFUNCTION_LEAVE oneclickleave
18+
!insertmacro MUI_PAGE_INSTFILES
19+
!include "uninstall.nsi"
20+
1221
; Define the installer sections
1322
Section "Ansys Python Manager" SEC01
1423
; Set the installation directory to the program files directory
1524
SetOutPath "$PROGRAMFILES64\ANSYS Inc\Ansys Python Manager"
16-
25+
1726
; Copy the files from the dist\ansys_python_manager directory
27+
; File /r /oname=ignore "dist\ansys_python_manager\*"
1828
File /r "dist\ansys_python_manager\*"
19-
29+
2030
; Create the start menu directory
2131
CreateDirectory "$SMPROGRAMS\Ansys Python Manager"
22-
32+
2333
; Create the start menu shortcut
2434
CreateShortCut "$SMPROGRAMS\Ansys Python Manager\Ansys Python Manager.lnk" "$INSTDIR\Ansys Python Manager.exe"
35+
36+
; Add the program to the installed programs list
37+
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayName" "${PRODUCT_NAME}"
38+
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
39+
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayIcon" "$\"$INSTDIR\Ansys Python Manager.exe$\""
40+
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "Publisher" "ANSYS Inc"
41+
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "Version" "${PRODUCT_VERSION}"
42+
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayVersion" "${PRODUCT_VERSION}"
43+
44+
WriteUninstaller "$INSTDIR\uninstall.exe"
45+
46+
; start after install
47+
Exec "$INSTDIR\Ansys Python Manager.exe"
48+
2549
SectionEnd
2650

2751
; Define the uninstaller section
2852
Section "Uninstall" SEC02
29-
; Remove the installed files
53+
3054
Delete "$PROGRAMFILES64\Ansys Python Manager\*.*"
3155
RMDir "$PROGRAMFILES64\Ansys Python Manager"
32-
33-
; Remove the registry keys
34-
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Ansys Python Manager"
35-
36-
; Remove the start menu shortcut and directory
56+
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
3757
Delete "$SMPROGRAMS\Ansys Python Manager\Ansys Python Manager.lnk"
3858
RMDir "$SMPROGRAMS\Ansys Python Manager"
3959
SectionEnd
4060

41-
; Set the installer properties
42-
Name "${PRODUCT_NAME}"
4361
Icon "dist\ansys_python_manager\assets\pyansys_icon.ico"
4462
InstallDir "$PROGRAMFILES64\ANSYS Inc\Ansys Python Manager"
4563

46-
; Simplify the installer GUI
64+
; Define the custom functions for the MUI2 OneClick plugin
4765
InstProgressFlags smooth
66+
Function oneclickpre
67+
!insertmacro MUI_HEADER_TEXT "Installing ${PRODUCT_NAME}" "Please wait while the installation completes."
68+
; !define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"
69+
HideWindow
70+
FunctionEnd
71+
72+
Function oneclickleave
73+
Quit
74+
FunctionEnd
75+
76+
; Call the MUI2 OneClick plugin
77+
!insertmacro MUI_UNPAGE_CONFIRM
78+
!insertmacro MUI_UNPAGE_INSTFILES
79+

src/ansys/tools/installer/VERSION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0.1.0-beta1
Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
11
"""
22
Ansys Python Manager
33
"""
4-
5-
__version__ = "0.0.dev0"
6-
7-
import logging
84
import os
5+
import sys
96
import warnings
107

118
from appdirs import user_cache_dir
129

13-
from ansys.tools.installer.main import AnsysPythonInstaller, open_gui
10+
if getattr(sys, "frozen", False):
11+
# If the application is run as a bundle, the PyInstaller bootloader
12+
# extends the sys module by a flag frozen=True and sets the app
13+
# path into variable _MEIPASS'.
14+
try:
15+
_THIS_PATH = sys._MEIPASS
16+
except:
17+
# this might occur on a single file install
18+
os.path.dirname(sys.executable)
19+
else:
20+
_THIS_PATH = os.path.dirname(os.path.abspath(__file__))
21+
22+
# Read in version programmatically from plain text
23+
# this is done so NSIS can also link to the same version
24+
with open(os.path.join(_THIS_PATH, "VERSION")) as fid:
25+
__version__ = fid.read()
1426

1527
CACHE_DIR = user_cache_dir("ansys_python_installer")
1628

@@ -23,16 +35,8 @@
2335
warnings.warn(f"Unable create cache at {CACHE_DIR}. Using temporary directory")
2436
CACHE_DIR = tempdir.gettempdir()
2537

26-
ENABLE_LOGGING = True
27-
if ENABLE_LOGGING:
28-
logger = logging.getLogger()
29-
logger.setLevel(logging.DEBUG)
30-
31-
# Create a console handler that writes to stdout
32-
console_handler = logging.StreamHandler()
33-
console_handler.setLevel(logging.DEBUG)
34-
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
35-
console_handler.setFormatter(formatter)
3638

37-
# Add the console handler to the logger
38-
logger.addHandler(console_handler)
39+
try:
40+
from ansys.tools.installer.main import open_gui # place this at end to allow import
41+
except ModuleNotFoundError: # encountered during install
42+
pass
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Main entrypoint for this module."""
22

3-
from ansys.tools.installer import open_gui
3+
from ansys.tools.installer.main import open_gui
44

55
if __name__ == "__main__":
66
open_gui()
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"""
2+
Check for updates.
3+
"""
4+
5+
from github import Github
6+
from packaging import version
7+
8+
# Readonly on this repo
9+
# This repository will be released to the public, there's no issue with this token.
10+
# Exp Mon, Jan 1 2024, should be able to use public unauth by then
11+
READ_ONLY_PAT = "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"
12+
13+
14+
def query_gh_latest_release():
15+
"""Check GitHub for updates.
16+
17+
Compares the current version with the version on GitHub.
18+
19+
Returns the version of the latest release and the download url of
20+
the executable installer.
21+
22+
Returns
23+
-------
24+
str
25+
Tag of the latest version.
26+
27+
str
28+
Url of the latest release installer.
29+
30+
"""
31+
gh = Github(READ_ONLY_PAT)
32+
repo = gh.get_repo(f"pyansys/python-installer-qt-gui")
33+
34+
# Get the latest release and its tag name
35+
latest_release = repo.get_latest_release()
36+
latest_version_tag = latest_release.tag_name
37+
38+
download_asset = None
39+
for asset in latest_release.get_assets():
40+
if asset.name.endswith(".exe"):
41+
download_asset = asset
42+
43+
download_url = None if download_asset is None else download_asset.url
44+
45+
return version.parse(latest_version_tag), download_url

src/ansys/tools/installer/common.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1+
from functools import wraps
2+
import logging
3+
import sys
14
from threading import Thread
5+
import traceback
6+
7+
LOG = logging.getLogger(__name__)
8+
LOG.setLevel("DEBUG")
29

310

411
def threaded(fn):
@@ -10,3 +17,34 @@ def wrapper(*args, **kwargs):
1017
return thread
1118

1219
return wrapper
20+
21+
22+
def protected(fn):
23+
"""Captures any exceptions from a function and passes it to the gui.
24+
25+
Attempts to display the error using ``show_error`` and protects
26+
the main application from segmentation faulting.
27+
"""
28+
29+
@wraps(fn)
30+
def wrapper(*args, **kwargs):
31+
self = args[0]
32+
try:
33+
return fn(*args, **kwargs)
34+
except Exception as exception:
35+
exc_info = sys.exc_info()
36+
traceback.print_exception(*exc_info)
37+
LOG.error(exception)
38+
if hasattr(self, "exceptions"):
39+
self._exceptions.append(exception)
40+
41+
# Visual error handing
42+
if hasattr(self, "parent"):
43+
if hasattr(self.parent, "show_error"):
44+
self.parent.show_error(exception)
45+
return wrapper
46+
47+
if hasattr(self, "_show_error"):
48+
self._show_error(exception)
49+
50+
return wrapper

0 commit comments

Comments
 (0)