PySide Application Deployment
This page describes how to deploy applications that are written using PySide/VisIt. To learn some quick recipes that can be used to create PySide applications, see PySide Recipes.
Contents
Linux
Linux-based PySide applications may consist of a command line script. The script runs VisIt's Python interface, telling it to run using a particular PySide command script. The -resourcedir argument is a script-specific argument that can be used to tell the PySide script where to locate resources such as its UI file.
myapp command:
#!/bin/sh
exec visit -cli -pysideviewer -uifile $(dirname $0)/myapp.py -resourcedir $(dirname $0) $@
If the application was installed into /usr/local/apps/myapp then the installation directory would contain:
- /usr/local/apps/myapp/myapp
- /usr/local/apps/myapp/myapp.py
- /usr/local/apps/myapp/myapp.ui
- /usr/local/apps/myapp/myapp.png
MacOS X Bundle
It is possible to copy a VisIt application bundle for Mac and make some slight changes to evolve it into a self-contained custom PySide application. You can override the Info.plist, icon file, launch script. You can also include your PySide scripts and UI file into the bundle's Resources directory where the VisIt binaries are located. You can also remove some files that you won't need from the bundle.
Info.plist
This file tells the Mac launcher the name and version of your application as well as icon, etc. You will replace this to give the VisIt bundle its new name. The Info.plist file goes in your new bundle's Contents directory. This Info.plist file is for a PySide/VisIt application called MacBurn.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>MacBurn</string>
<key>CFBundleGetInfoString</key>
<string>MacBurn Post-Processing Tool</string>
<key>CFBundleIconFile</key>
<string>MacBurn.icns</string>
<key>CFBundleIdentifier</key>
<string>MACB</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLongVersionString</key>
<string>MacBurn 0.1</string>
<key>CFBundleName</key>
<string>MacBurn 0.1</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>MacBurn 0.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>0.1</string>
<key>CSResourcesFileMapped</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>Copyright (c) 2000 - 2013, Lawrence Livermore National Security, LLC</string>
</dict>
</plist>
Resources
Your custom application will install some other custom resources to the application bundle. In this example, we'll install a custom icon, a PySide script, and a UI file. The resources go into the following respective locations:
- Contents/Resources/app.icns
- Contents/Resources/myapp.py
- Contents/Resources/myapp.ui
Launch script
The launch script for your application must match the name of your new bundle (minus the .app suffix). In this case, the launch script will run VisIt's cli in an xterm using PySide/Viewer mode. The launcher tells the CLI where to locate the PySide script in the bundle's Resources directory and tells the program to also to find its other resources in the same directory. The latter is accomplished using a -resourcedir command line argument, which the script will have to understand. The script will use the directory provided by -resourcedir to prepend to the resource files that it wants (e.g. UI files, help files, etc.).
- The CLI is run under an xterm to ensure that Python gets a terminal that it can use. This avoids a bug that starts up the user interface but makes it hang. It also is better than other methods that work with the normal terminal since this method lets the application retain its custom icon in the dock.
- We look for xterm in a couple of places because its location depends on the version of MacOS X.
Contents of file Contents/MacOS/MacBurn:
#!/bin/sh
if test -x /usr/X11/bin/xterm ; then
# Run in an xterm so the Python interpreter is happy. This also keeps the right icon.
/usr/X11/bin/xterm -title MacBurn -e $(dirname $0)/../Resources/bin/visit -cli -pysideviewer \
-uifile $(dirname $0)/../Resources/macburn.py -resourcedir $(dirname $0)/../Resources
elif test -x /opt/X11/bin/xterm ; then
# Run in an xterm so the Python interpreter is happy. This also keeps the right icon.
/opt/X11/bin/xterm -title MacBurn -e $(dirname $0)/../Resources/bin/visit -cli -pysideviewer \
-uifile $(dirname $0)/../Resources/macburn.py -resourcedir $(dirname $0)/../Resources
else
# This keeps the right logo but it beachballs. Python waits on a pthread condition var.
$(dirname $0)/../Resources/bin/visit -cli -pysideviewer \
-uifile $(dirname $0)/../Resources/macburn.py -resourcedir $(dirname $0)/../Resources
fi
Bundle Conversion Script
Here is a bundle conversion script that can be used to copy a VisIt.app application bundle and evolve it into a MacBurn.app application bundle that contains a self-contained PySide/VisIt application. This script can serve as a template for deployment of other PySide/VisIt applications.
- Note that the resources that we copy into the new bundle exist in a comparable directory structure called bundle.
- Note that we remove a lot of stuff from the new bundle to trim it down.
#/bin/bash
###############################################################################
# Program: makebundle
#
# Purpose: This script copies a VisIt.app bundle and turns it into a MacBurn
# app bundle that we can move to different machines.
#
# Programmer: Brad Whitlock
# Date: Wed Jan 16 15:16:52 PST 2013
#
###############################################################################
# Copy a VisIt.app directory to MacBurn.app
cp -R $1 MacBurn.app
# Copy some MacBurn app stuff into the bundle.
cp bundle/Contents/Info.plist MacBurn.app/Contents
cp bundle/Contents/Resources/MacBurn.icns MacBurn.app/Contents/Resources
cp bundle/Contents/MacOS/MacBurn MacBurn.app/Contents/MacOS
cp macburn.py MacBurn.app/Contents/Resources
cp macburn.ui MacBurn.app/Contents/Resources
cp running_macburn.html MacBurn.app/Contents/Resources
cp macburn.png MacBurn.app/Contents/Resources
# Set execute permission on the script.
chmod 755 MacBurn.app/Contents/MacOS/MacBurn
# Delete some of the VisIt stuff from the MacBurn app.
rm -f MacBurn.app/Contents/MacOS/VisIt
rm -f MacBurn.app/Contents/Resources/VisIt.icns
rm -rf MacBurn.app/Contents/Resources/data
rm -rf MacBurn.app/Contents/Resources/2.6.0/darwin-x86_64/archives
rm -rf MacBurn.app/Contents/Resources/2.6.0/darwin-x86_64/include
rm -rf MacBurn.app/Contents/Resources/2.6.0/darwin-x86_64/libsim
rm -rf MacBurn.app/Contents/Resources/2.6.0/darwin-x86_64/resources/help/images
rm -rf MacBurn.app/Contents/Resources/2.6.0/darwin-x86_64/resources/clients
# Delete most database readers
set PLUGINDIR="MacBurn.app/Contents/Resources/2.6.0/darwin-x86_64/plugins"
mkdir $PLUGINDIR/databases_keep
cp $PLUGINDIR/databases/*Silo* $PLUGINDIR/databases_keep
cp $PLUGINDIR/databases/*RAW* $PLUGINDIR/databases_keep
cp $PLUGINDIR/databases/*VTK* $PLUGINDIR/databases_keep
cp $PLUGINDIR/databases/*PlainText* $PLUGINDIR/databases_keep
cp $PLUGINDIR/databases/*Curve2D* $PLUGINDIR/databases_keep
cp $PLUGINDIR/databases/*Shapefile* $PLUGINDIR/databases_keep
rm -rf $PLUGINDIR/databases
mv $PLUGINDIR/databases_keep $PLUGINDIR/databases
Windows
We're going to take the following strategy for installing our PySide application for Windows:
- Assume that the VisIt installer has been run so VisIt is on the system
- Make an application-specific installation directory to contain:
- The program's Python file
- The program's resources: (icon, help files, UI file)
- Make an installer that will create a custom shortcut to VisIt, using our -pysideviewer command, etc. We'll use NSIS.
Icon
There are many programs that can make Windows icons. If you have the Gimp image editing program then you can convert a suitable PNG file to a Windows .ICO file. I selected the 256x256 transparent background PNG file that I made for Mac and I was able to Save as ICO in the Gimp.
Install Script
You can use NSIS to create an installer script for your PySide application.
If you don't see any application shortcuts for NSIS, you can look for C:\Program Files (x86)\NSIS\makensisw.exe. This is the Windows-gui version of the NSIS installer compiler. the program lets you select the NSI script that you write either by File->Open or by dragging your script onto the application's main window. Once you have opened your NSI script, you can compile it to create your installer.
The general approach we'll take is to install several files that comprise our PySide application to an install directory. Then we'll create a desktop shortcut that contains the VisIt command line that will run the PySide scripting.
- application.py
- application.ui
- help.html
- application.ico
- application.png
Here is an example NSIS script:
# All the other settings can be tweaked by editing the !defines at the top of this script
!define APPNAME "MacBurn"
!define APPFILE "macburn"
!define COMPANYNAME "LLNL"
!define DESCRIPTION "MacBurn includes post-processing software based on VisIt"
# These three must be integers
!define VERSIONMAJOR 0
!define VERSIONMINOR 1
!define VERSIONBUILD 1
# This is the size (in kB) of all the files copied into the install location.
!define INSTALLSIZE 250
# Let users run the installer
RequestExecutionLevel user
# Set a default installation directory
InstallDir "$PROGRAMFILES\${COMPANYNAME}\${APPNAME}"
# rtf or txt file - remember if it is txt, it must be in the DOS text format (\r\n)
LicenseData "license.rtf"
# This will be in the installer/uninstaller's title bar
Name "${COMPANYNAME} - ${APPNAME}"
Icon "${APPFILE}.ico"
outFile "${APPFILE}-installer.exe"
!include LogicLib.nsh
# Just three pages - license agreement, install location, and installation
page license
page directory
Page instfiles
function .onInit
setShellVarContext current
functionEnd
# This section detects VisIt installations in the registry and sets the visithome var.
var visithome
section "detectVisIt"
var /GLOBAL major
var /GLOBAL minor
var /GLOBAL patch
var /GLOBAL current
# Let's start looking at version 2.6.0
IntOp $major 0 + 2
IntOp $minor 0 + 6
IntOp $patch 0 + 0
loop:
# Check HKCR, HKLM, HKCU in turn for VISITHOME
ClearErrors
ReadRegStr $current HKCR "Software\Classes\VISIT$major.$minor.$patch" "VISITHOME"
IfErrors checkhklm
Strcpy $visithome $current
goto versioncheckdone
checkhklm:
ClearErrors
ReadRegStr $current HKLM "Software\Classes\VISIT$major.$minor.$patch" "VISITHOME"
IfErrors checkhkcu
Strcpy $visithome $current
goto versioncheckdone
checkhkcu:
ClearErrors
ReadRegStr $current HKCU "Software\Classes\VISIT$major.$minor.$patch" "VISITHOME"
IfErrors versioncheckdone
Strcpy $visithome $current
goto versioncheckdone
versioncheckdone:
# Increment patch
IntOp $patch $patch + 1
IntCmp $patch 4 patch_is_4 patch_lt_4
patch_is_4:
IntOp $patch 0 + 0
goto patch_done
patch_lt_4:
goto loop
patch_done:
# Increment minor
IntOp $minor $minor + 1
IntCmp $minor 10 minor_is_10 minor_lt_10
minor_is_10:
IntOp $minor 0 + 0
goto minor_done
minor_lt_10:
goto loop
minor_done:
# Increment major
IntOp $major $major + 1
IntCmp $major 4 major_is_4 major_lt_4
major_is_4:
goto major_done
major_lt_4:
goto loop
major_done:
StrCmp $visithome "" novisit havevisit
novisit:
MessageBox MB_OK "No VisIt installation was detected! Please quit the installer."
Abort
havevisit:
sectionEnd
section "install"
# Files for the install directory - to build the installer, these should be in the same directory as the install script (this file)
setOutPath $INSTDIR
# Files added here should be removed by the uninstaller (see section "uninstall")
file "${APPFILE}.ui"
file "${APPFILE}.py"
file "running_${APPFILE}.html"
file "${APPFILE}.ico"
file "${APPFILE}.png"
# Add any other files for the install directory (license files, app data, etc) here
# Uninstaller - See function un.onInit and section "uninstall" for configuration
writeUninstaller "$INSTDIR\uninstall.exe"
# Desktop shortcut
setOutPath $DESKTOP
createShortCut "$DESKTOP\${APPNAME}.lnk" "$visithome\visit.exe" "-cli -pysideviewer -uifile $INSTDIR\${APPFILE}.py -resourcedir $INSTDIR" "$INSTDIR\${APPFILE}.ico"
# Registry information for add/remove programs
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayName" "${COMPANYNAME} - ${APPNAME} - ${DESCRIPTION}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "InstallLocation" "$\"$INSTDIR$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayIcon" "$\"$INSTDIR\${APPFILE}.ico$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "Publisher" "$\"${COMPANYNAME}$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayVersion" "$\"${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONBUILD}$\""
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "VersionMajor" ${VERSIONMAJOR}
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "VersionMinor" ${VERSIONMINOR}
# There is no option for modifying or repairing the install
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "NoRepair" 1
# Set the INSTALLSIZE constant (!defined at the top of this script) so Add/Remove Programs can accurately report the size
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "EstimatedSize" ${INSTALLSIZE}
sectionEnd
# Uninstaller
function un.onInit
SetShellVarContext current
#Verify the uninstaller - last chance to back out
MessageBox MB_OKCANCEL "Permanantly remove ${APPNAME}?" IDOK next
Abort
next:
functionEnd
section "uninstall"
# Remove Desktop launcher
delete "$DESKTOP\${APPNAME}.lnk"
# Remove files
delete $INSTDIR\${APPFILE}.py
delete $INSTDIR\${APPFILE}.ui
delete $INSTDIR\${APPFILE}.ico
delete $INSTDIR\${APPFILE}.png
delete $INSTDIR\running_${APPFILE}.html
# Always delete uninstaller as the last action
delete $INSTDIR\uninstall.exe
# Try to remove the install directory - this will only happen if it is empty
rmDir $INSTDIR
# Remove uninstaller information from the registry
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}"
sectionEnd
Issues
After using this script to deploy an application to another Windows 7 box, a few issues cropped up.
- If VisIt is installed for all users, the installer can't locate it because the registry keys will be different. We probably need to look in HKCR too.
- The shortcut uses filenames that contain all back-slashes. This ended up causing a weird exception in the PySide program as backslash-containing paths were passed as command line arguments to the PySide application. The backslashes were in this case treated as escape characters. Switching the shortcut to use forward slashes in the paths fixed the problem.