Creating a pyqtdeploy Project

The next stage of deploying a PyQt application is to create a pyqtdeploy project for it by running:

pyqtdeploy pyqt-demo.pdy

This will create a new project, or open an exiting one if the file pyqt-demo.pdy already exists. A project is simply a file with a .pdy extension.

A GUI will be displayed which consists of a File menu and a set of tabbed pages that handle different aspects of the application’s specification and building.

The File menu contains the usual set of options to create a new project, open an existing project, save a project and rename a project.

Defining the Application Source

Python applications are usually structured in one of the following ways:

  • a single standalone executable script that is installed in a bin directory somewhere
  • a package installed in site-packages with a separate executable script installed in a bin directory
  • a package installed in site-packages with a separate executable script that is automatically generated by setuptools. The script calls an entry point in the package. An entry point is the name of a module in the package and the name of a callable within the module separated by a :.

pyqt-demo is a single standalone script. pyqtdeploy itself uses the setuptools based structure.

The tab for defining the application source is shown below.

_images/application_source_tab.png
Name
is used to specify the name of the final application executable. If it is not specified then it is derived from the name of the main script file. If the application does not have a main script file then the name is derived from the name of the application package.
Main script file

is used to specify the name of the executable Python script used to invoke the application. It should not be specified if an entry point is specified.

Note

Whenever a file or directory is specified, pyqtdeploy always saves its name relative to the directory containing the project file if possible. In this particular example the pyqt-demo.py script is in the same directory as the pyqt-demo.pdy project file. Also, whenever a file or directory name is entered, pyqtdeploy allows the embedding of environment variables which will be expanded when necessary. The PDY_PY_MAJOR, PDY_PY_MINOR and PDY_PY_MICRO pseudo-environment variables can also be used which will be expanded to the relevant parts of the target Python version.

Entry Point
is used to specify the entry point of a setuptools-based application. It should not be specified if a main script file is specified.
sys.path

is used to specify additional directories, ZIP files and eggs that will be added to sys.path. By default pyqtdeploy generates an application that does not support the importing of packages or extension modules that are not embedded in the application. However there are circumstances where this ability is desired:

  • you need to use an extension module that does not support being statically compiled
  • you allow users to write Python code that is imported by the application to configure or extend its functionality.

The path is specified as a space separated list of directories. Single or double quotes may be used to embed spaces in directory names. Environment variables (specified using the standard UNIX notation) are expanded when the application is run. Any relative directory names are assumed to be relative to the directory containing the application executable. UNIX path separators should be used - the application will automatically do the appropriate conversions when running on Windows.

pyqtdeploy also supports the dynamic loading of extension modules that are installed adjacent to the application executable (i.e. in the same directory as the application executable). This is useful for packages that are implemented as a combination of pure Python modules and extension modules where the extension modules do not support being statically compiled. The pure Python modules would be added just like any other Python package (see Adding Other Packages). The extension module must be named according to its normal position in the package structure.

For example, suppose we have a package called foo which contains pure Python modules __init__.py and bar.py and a dynamically loaded extension module baz.so. (The extension module would be called baz.pyd on Windows.) When used with the deployed application the module must be installed as foo.baz.so (or foo.baz.pyd) in the same directory as the application executable.

On macOS an extension module will also be searched for in the PlugIns and Frameworks sub-directories of the directory containing the application executable.

Adjacent extension modules do not require sys.path to be set.

Note

If you wish to allow the importing of external extension modules then you will also need to ensure that Python has been built with this enabled.

Target Python version
is used to specify version of Python that you are targeting.
Target PyQt version
is used to specify that the application is either a PyQt4 or a PyQt5 application. This is ignored if the application doesn’t use PyQt.
Use console (Windows)
is checked if the application should use a console. Specifically it adds console to the value of CONFIG in the generated .pro file and only affects Windows applications. Normally this is handled automatically and a console is used only if the application does not use any GUI related PyQt module. However it may be usefull during testing of deployed GUI applications to ensure that tracebacks (e.g. about missing modules) are captured and displayed.
Application bundle (macOS)
is checked if the application should be built as a bundle and only affects macOS targets. It would normally be unchecked for command line (i.e. non-GUI) applications.
Application Package Directory

contains the hierachy of files and directories that implement the application package and any associated data. It is populated by clicking the Scan… button. Each file or directory can then be checked if it is to be included in the package. Note that if the main script file is a part of the application package then it’s entry must be explicitly unchecked (i.e. excluded).

Note

Non-Python (i.e. data) files can also be included in the package. An application typically accesses such files by using the QFileInfo.absolutePath() function on the __file__ attribute of a module to obtain the name of the directory containing the data file. This approach will also work with deployed applications so long as the file is accessed using the QFile class (rather than the standard Python file access functions). However QML files require slightly different treatment as their locations are specified using a URL.

Scan…
is clicked to specify the name of the directory containing the Python package that implements the application. (If the application consists of a single script then you would not use this.) The hierachy will be scanned for all files and directories that don’t match any of the specified exclusions and will be displayed in the Application Package Directory.
Remove all
is clicked to remove all the scanned files and directories.
Include all
is clicked to check all scanned files and directories so that they are included in the application package.
Exclude all
is clicked to uncheck all scanned files and directories so that they are excluded from the application package.
Exclusions
is used to specify the list of glob-style patterns that are applied to the scanned files and directories. Those items that match are then completely ignored. To edit the list just double-click on the entry to modify or delete. To add a new entry just double-click the list after the last entry.

Additional qmake Configuration

The tab for defining additional qmake configuration information is shown below.

_images/qmake_tab.png

Any text entered here is added to the end of the .pro file generated by pyqtdeploy.

Defining the PyQt Modules

The tab for defining any PyQt modules used by the application is shown below. If the application is a PyQt4 application then the PyQt4 modules will be shown instead.

_images/pyqt_modules_tab.png

Simply check all the PyQt modules that are imported by the application.

pyqtdeploy understands the dependencies between the different PyQt modules and will automatically check any additional modules that are required. It is recommended that modules explicitly imported by the application are checked even if they are also implicity imported.

Note

These modules must be compiled statically. If you plan to use a separately deployed copy of PyQt that will be dynamically loaded by your application then do not specify any modules here.

Adding Standard Library Packages

The tab for defining the Python standard library packages used by the application is shown below.

_images/stdlib_packages_tab.png
Package

is used to specify each of the target Python version’s standard library packages that is explicitly imported by the application. Each package can be in one of three states:

  • checked meaning it is explicitly imported by the application
  • partially checked meaning it is implicitly imported by the application because another imported package depends on it, or the Python interpreter itself imports it
  • unchecked meaning it is not needed by the application.

You should always check a package if the application explicitly imports it, even if it is already shown as partially checked. When a package is checked (or unchecked) then any sub-packages are automatically checked (or unchecked).

Here we have checked the ssl and sysconfig modules and the socket, stat, string, struct and subprocess modules (amongst others) have been partially checked automatically.

The remaining part of the tab relates to additional libraries that may need to be linked with the application. Typically they correspond to packages in the standard library that wrap them. A tab is provided for each target platform so that a library can be handled in a platform-specific manner. If a library is required, because a package that uses it is required, then the entry for the library will be enabled. The corresponding DEFINES, INCLUDEPATH and LIBS fields will also be editable allowing those values to be set appropriately. If all of those fields are left blank then the external library is effectively disabled. This can be useful if, for example, the original Python package is written to use an external library if it is available but will fall back to another implementation if not.

For example, if you have built a static copy of the library then you may need to specify the location of the library’s header files in the INCLUDEPATH field and add a -L flag to the LIBS field if the library is not installed in locations that will be found automatically by the compiler and linker.

pyqt-demo imports the ssl module from the standard library and so the corresponding entry is enabled. On macOS pyqt-demo links against a static version of OpenSSL. We use INCLUDEPATH to specify where pyqtdeploy-sysroot has installed the OpenSSL header files. Because the OpenSSL libraries are statically linked as part of the static build of Qt, there is no need to specify a value for LIBS. You should click on the other platform tabs to see how SSL is configured for those.

Use standard Python shared library
is used to specify if the standard Python shared library is to be used instead of a specially compiled shared or static library. When selected pyqtdeploy assumes that all of the Python standard library that is implemented as C extension modules is implemented in the shared library. The default is to enable this for Windows. See also Windows and Dynamic Loading of Extension Modules.

Adding Other Packages

The tab for defining additional packages used by the application is shown below.

_images/other_packages_tab.png

This tab is used to scan a number of directories containing additional Python packages (i.e. other than those that are part of the Python standard library). You then specify which individual modules are needed, either implicitly or explicitly, by the application.

To edit the list of directories just double-click on the entry to modify or delete. To add a new entry just double-click the list after the last entry.

In many cases you will want to add the site-packages directory of your host Python interpreter, i.e. the interpreter being used to develop the application, where all the additional packages required by your application are already installed.

pyqt-demo does not use any additional Python packages.

Adding Other Extension Modules

The tab for defining additional C extension modules used by the application is shown below.

_images/other_extension_modules_tab.png

This tab is used to specify any additional C extension modules (i.e. other than those that are part of the Python standard library or PyQt) that will be statically linked into the application.

These extension modules may already have been built, but this tab can also be used to compile them from source. You would only normally do this for simple extension modules typically made up of a single source file.

Name
is the full (dot separated) package name of the extension module.
QT
are the values added to the qmake QT variable.
CONFIG
are the values added to the qmake CONFIG variable.
SOURCES

are the names of the source files that will be compiled to create the extension module. The file name extension determines which qmake variable the file is added to:

  • .asm files are added to MASMSOURCES
  • .h files are added to HEADERS
  • .java files are added to JAVASOURCES
  • .l files are added to LEXSOURCES
  • .pyx files are added to CYTHONSOURCES
  • .y files are added to YACCSOURCES.

Files with any other extension are added to SOURCES.

DEFINES
are the values added to the qmake DEFINES variable.
INCLUDEPATH
are the values added to the qmake INCLUDEPATH variable.
LIBS
are the values added to the qmake LIBS variable. If the extension module has already been compiled then this is used to link it with the application.

pyqtdeploy goes to some trouble to make it possible to create project files that can be used without modification across all supported targets. To this end the values of these qmake variables may be scoped with any supported target architecture or platform name.

The scope is specified immediately before the value and separated from it by a #. A scope can take one of the following forms (where target is either a target architecture or platform):

  • target where the value applies for the specified target only
  • !target where the values applies to all targets except the one specified
  • target|target|… where the value applies to all of the targets specified.

The most common requirement is to distinguish between Windows and non-Windows targets, therefore the most common scopes used will be win and !win.

To edit the list just double-click on the entry to modify or delete. To add a new entry just double-click the list after the last entry.

pyqt-demo does not use any additional C extension modules.

Defining File and Directory Locations

The tab for defining the locations of various files and directories needed by pyqtdeploy is shown below.

_images/locations_tab.png
Interpreter
is used to specify the host Python interpreter used to compile all of the Python modules used by the application. This must be the same version as the target Python installation to ensure that the compiled bytecode can be executed by the deployed application. (Of course if you are not cross-compiling the application then the host and target Python installations can be the same.) On Windows any trailing version number will be automatically stripped. This makes it easier to create project files that are portable across all platforms. If it is not specified then pyqtdeploy will use a platform-specific default. On Windows it will inspect the registry to try and find the required version of Python, on other platforms it assumes that the required version is on PATH. It can be overridden by the --interpreter command line option of pyqtdeploy-build.
Source directory
is used to specify the name of the directory containing the Python source code. It can be overridden by the --source-dir command line option of pyqtdeploy-build.
Include directory
is used to specify the name of the directory containing the target Python installation’s Python.h file. It can be overridden by the --include-dir command line option of pyqtdeploy-build.
Python library
is used to specify the name of the target Python interpreter library. Note that in this example the library is specified as a UNIX archive, however pyqtdeploy will interpret it correctly on Windows. It can be overridden by the --python-library command line option of pyqtdeploy-build.
Standard library directory
is used to specify the name of the directory containing the target Python interpreter’s standard library. It can be overridden by the --standard-library-dir command line option of pyqtdeploy-build.
Set defaults
is used to restore all the other fields to their default values. Those values correspond to the values used by pyqtdeploy-sysroot.