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.

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:

  1. Assume that the VisIt installer has been run so VisIt is on the system
  2. Make an application-specific installation directory to contain:
    1. The program's Python file
    2. The program's resources: (icon, help files, UI file)
  3. 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.

Makensisw.png

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.

  1. 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.
  2. 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.