Deciding between Xcode and QtCreator for packaging PyQt apps for OSX

Suppose you have pyqtdeploy’ed a PyQt project to OSX.  Now you want to package it for distribution.  You are trying to decide what packaging tools to use.

What pyqtdeploy produces

  • .pro file ( a qmake project)
  • .app bundle (a minimal package for the Mac platform)
  • a static built executable (that includes the Python interpreter and all dependencies except Qt libraries/frameworks)

Packaging Tool Choices

  • Xcode
  • QtCreator (and qmake)
  • your own scripts invoking qmake and Xcode command line tools
  • other third party packagers

Advantages of switching to Xcode

  • Xcode understands Mac packaging better
  • Xcode is better integrated with the Mac Store
  • Xcode is more GUI friendly (drag and drop, point-and-click, forms)

Xcode understands Mac packaging better

  • code signing
  • sandboxing
  • icons and document types (mimetypes)
  • the bundle (package) structure

Xcode is better integrated with the Mac Store

  • it remembers your AppleID
  • it knows the steps to submit (validating, submitting a debug version, final uploading)
  • it knows how to validate
  • the Mac Store won’t accept other packagers

Disadvantages of switching to Xcode

  • learning curve
  • not cross-platform (whatever you learn doesn’t apply to other platforms)

Sandboxing as a determiner

The store requires sandboxing of most apps.

Otherwise, sandboxing is optional.  It’s benefit is that it gives more comfort to your users, because it doesn’t give a warning when the app is installed (used?)

Summary

If you are distributing in the Mac Store you probably must use Xcode.

Otherwise, using Xcode is better only if you prefer a more user-friendly, less scripted process.

 

 

 

 

Using pyqtdeploy

About this blog

THIS IS A DRAFT.  It is still being edited.  It is more or less complete for OSX and Ubuntu, but I hope to update it for Windows.

This is programming adventure blog: a record of installing and learning a new tool, pyqtdeploy.  Typically, it just records the correct order of doing things, and pitfalls.

This is the environment, but you might adapt snippets to other environments:

  • OSX 10.9 or Ubuntu14.04
  • Python3.4
  • Qt5.3 and PyQt5

Context: I was using pyinstaller, but now want to try pyqtdeploy.  See my other blog about choosing between them.

Audience: moderately experienced software developers.  You should know the shell, make, compilers, linkers, etc.

About pyqtdeploy

pyqtdeploy is a tool for preparing Python apps for distribution to many platforms.  Its a developer’s tool, like an IDE or a build system.

It freezes your app into one executable file ( a .exe  on Linux or a .app  on OSX).  Your app will not depend on the user having Python installed or bundled/packaged.  Your app will still depend on the user having Qt (library) installed or bundled/packaged.

After you use pyqtdeploy, you usually  use other packaging tools, for example to bundle and code sign.  For example, you can use Xcode on OSX or QtCreator (on any platform) to package your app.

More about pyqtdeploy

It is a cross-platform build tool.  Which is also what qmake is.  The layers pile up.  pyqtdeploy generates a .pro file which is input to qmake which generates a Makefile which is input to make.

They are similar to the gnu build system and to older cross-platform tools such as imake.   They ‘understand’ some of the differences among platforms for compiling apps.

General warnings about using pyqtdeploy now

pyqtdeploy is in development, try it at your own risk. The version number less than 1.0 conventionally indicates a very early release undergoing rapid change.  Not quite the same as a beta release, which might indicate  buggy, but indicating missing features (support for mobile platforms) and limited testing.

Trying it and reporting bugs may contribute to its development, but you should not expect timely support on bugs, because the development of other features (mainly support for the mobile platforms) takes precedence.

If you do get it to work, its not clear whether you can ship your deployed app and expect support.  (Besides, its open source, you might be expected to fix things yourself.)

If you do try it, be sure to uses the very latest (‘Development Snapshot’, not ‘Stable’) release of PyQt and SIP, since the development of pyqtdeploy might entail changes to those packages also.

About freezing and static linking

People use ‘freeze’ very loosely.  There are many different ‘freezing’ tools on different platforms.  They vary in the kind and degree of freeze.  (More or less, how many files are distributed and how much code is statically linked.)

pyqtdeploy uses THE freeze utility distributed with Python.  So in this case, ‘freeze’ has a specific meaning, the kind of freeze implemented by THE freeze utility.  That kind of freeze DOES NOT freeze dynamically linked libraries.  It does freeze pure python modules (suffix .py).

So pyqtdeploy requires these to be compiled ready for static linking:

  • python interpreter (library)
  • sip module (an extension module, written in C.)
  • pyqt module (an extension module, written in C.)
  • any other extension modules your app uses

(Side note: the freeze utility might not actually be distributed in the Mac packaging of Python.  It doesn’t matter, pyqtdeploy includes a copy, and invokes that copy.  AFAIK the copy that pyqtdeploy includes is unmodified.)

Statically linked Python

Python is versatile:

  • extension: call C programs from Python, Python interpreter is main
  • embedding: call Python from C programs, C program is main

pyqtdeploy embeds the statically linked Python library, which is slightly different from the command line Python interpreter, in its entry point, which is not a main.  See any good book about Python, or the Python docs about embedding.

pyqtdeploy documents more or less assume you build the static Python library yourself.  This might not be necessary when your platform distributes  the static Python library.  But for most platforms (especially mobile platforms which are in a state of flux), and for complete control, you do build it yourself.

Ubuntu

a statically linked library is installed with the python-dev or python3-dev package on Debian or Ubuntu.  It is installed to /lib/i386-linux-gnu/libpython3.4m.a (or similar.)  I’m not sure which Python extension (.c) modules are linked in, but it seemed to have the ones that my app used.

OSX

You need to build the static Python library yourself.  Because Apple doesn’t ship Python3, and they don’t ship the static library for Python2.7?  See instructions below for building.

Windows

You need to build the static Python library yourself.  See instructions in the pyqtdeploy tutorial.

 

Requirements

This is a dependency tree, linking to places to download:

pyqtdeploy

– compilers   OSX: Xcode, Ubuntu: the omnibus ‘build-essential’ package.  Windows: Microsoft Visual Studio Express

Python3

– PyQt (PyQt5)

SIP

Qt (5.3)

The tree means, for example, that pyqtdeploy requires Python3 and PyQt, and that PyQt requires SIP and Qt.

(Really, it is not a tree: pyqtdeploy does compiling, which requires Xcode’s compilers, but installing SIP etc. also requires compiling so it depends on Xcode also.  pyqtdeploy also uses Qt build tools such as qmake.)

Installing the requirements

Roughly, start at the leaves of the tree, working toward the root until pyqtdeploy is installed.  Specifically, do it in this order:

  1. compilers.  OSX: Xcode is self-installing.  (It used to be that you could download the command line tools separately.  Just install all of Xcode.)
  2. Python3.  Also self-installing, but evidently not in a static compiled version, see below
  3. Qt.  Also self-installing.  (You can build it from source, but that’s difficult.)
  4. SIP.  You must build it following specific instructions for use with pyqtdeploy, see below.
  5. PyQt.  You must build it following specific instructions for use with pyqtdeploy, see below.

You install compilers first, because they are needed by the following steps.

OSX: Installing Xcode

It’s in the AppStore, and free.

Windows: Installing Microsoft Visual Studio Express

Its free (its not the professional version, but it suffices.  Microsoft wants you to develop apps for their platform, so they provide tools free.)

The tool package is generically called ‘Visual Studio’.  There are specific versions (2008, 2010, 2013) for each major API change in the Windows platform (but older versions are compatible with newer platforms.)  There are specific tools for each language (Basic, C++, C#) but you need the C++ tool, i.e. Visual C++, abbreviated as VC++.   “Express” means free (and limited), contrasted with “Professional” which has a price.

If you are using:

Microsoft doesn’t advertise the link to  MS VC++2008, but it is still available, see this StackOverflow post.

Building static Python library: Generic

You do this step second, because the following steps invoke Python, and you should use the same (statically compiled) version of Python throughout.

pyqtdeploy requires a statically compiled library form of the Python interpreter.  Again, note that there is a subtle difference between building a static command-line interpreter (which can be invoked at the command line) and building a library interpreter (which can be linked into an app and called from the app.)  Both are built when you build Python.

This SO post describes the process.   But it somewhat garbles the difference between the static command-line interpreter and the static interpreter library.   The answer by M. Loewis is most pertinent (except  that we are using pyqtdeploy to invoke freeze, instead of invoking it directly.)

The generic steps (this is a very common sequence, except that here you edit a file after you configure) :

  • ./configure
  • edit ~/Downloads/Python3.4/Modules/Setup (in the Python build directory)
  • make
  • make install
Editing Modules/Setup

This is a text file.  The file is human readable and contains good comments and instructions.  The build system for Python generates other files from this file.  The file basically tells the Python build system whether modules should be compiled into the one static Python library or compiled into separate dynamic libraries.  Both are used by the command-line interpreter, it’s largely a matter of when modules are loaded.  For our purposes, we don’t want any dynamic module libraries.  But if you read the default Modules/Setup script, you may see that most modules default to building as shared dynamic libraries (depending on the platform?).

Editing entails uncommenting lines for modules.  Search for the module name, and uncomment the line.

How to know which modules you need?  As elsewhere, you can use an iterative process.  That is, pyqtdeploy your app and run it.  When you get an import error, determine whether the missing module is a pure Python module (e.g. struct), or an extension module distributed with Python e.g. _struct), or a module of your app.  In the second case, you fix it by editing Modules/Setup, rebuilding Python, etc.

Note that some pure Python modules (e.g. pickle) invoke an extension module (e.g. _pickle in Python3, written in C) if it is available, while other pure Python modules (e.g. struct) always invoke an extension module (e.g. _struct).  Thus for our purposes, some modules are optional for speed (e.g. _pickle) while other modules (e.g. _struct) are NOT optional if your app imports the corresponding pure Python module (struct.)

Note that configure creates the Modules/Setup file (see the last few lines of output from configure.)  So after ‘make distclean’ you will need to configure again, and edit Modules/Setup again.

While iterating and editing Modules/Setup, you might get by with just make, make install   of python.

For my app I needed:

  • _struct
  • math (I don’t know why it doesn’t start with ‘_’)
  • time
  • unicodedata
  • syslogmodule

I also used _pickle for performance (but the pickle module will work without it, in Python3)

 

Building static Python library: OSX

In my experience, a binary installer for Python on OSX (a .dmg) does NOT install a statically linked library version of Python.  The pyqtdeploy docs say “Note that a static, native build of Python is the default on these platforms.”  The interpretation is: a build is static by default, you don’t need to pass any options such as “–static”. The interpretation is NOT: a binary installer will install a static version of the library by default.

Here, we will install to a directory ~/python.

cd ~
mkdir python

Download the source tarball for the 3.4 version of python, say to ~/Downloads, unzip and untar it in the same directory ( to ~/Downloads/python3.4 ).

Build it ( it defaults to building static).

export MACOSX_DEPLOYMENT_TARGET=10.8
cd ~/Downloads/Python-3.4.0
./configure --prefix /Users/bootch/python
(edit Modules/Setup)
make
make install

Note that building occurs in the Downloads directory, installing occurs to the prefix directory.

Pointing to your static Python, OSX

Now ensure that ‘python3’ on the command line finds the static python you built:  (this is for OSX, I assume that on Ubuntu, you did not build static Python yourself, but are using the one included with the python-3 package.)

cd /usr/local/bin
sudo unlink python3
sudo ln -s /Users/bootch/python/bin/python3.4 python3

(This might be wrong.  There are other ways to do it, and it easy to get things snarled up.  For example, you might have futzed with PATH in your ~/.bash_profile, or possibly the python makefile already does this.  The bottom line is, when you start python3 in a terminal, it should start and print the version and date of your build.)

I think this step is only vital to the subsequent steps of building static sip and pyqt, or to insure that when you are debugging by executing your app as a script, you are using the same python as the one you built.  When you use pyqtdeploy, you explicitly tell it which python to use, and where to find site-packages.  To be sure, you can always enter a full path to your python executable.

When you later build SIP, it will install the sip executable to the bin directory in this directory where you have built static Python.  And that bin directory is not in your path.  So you will need to explicitly configure the PyQt build, passing the –sip=/Users/bootch/python/bin/sip.

About  MACOSX_DEPLOYMENT_TARGET

This environment variable controls the build of Python on OSX.  (A similar name is used by qmake, in its conf files.)  You should see the effect in the console output of configure.

I tried to build without this on Mavericks(10.9) and the resulting Python library referenced __sincos__stret, which was undefined during the linking phase of pyqtdeploy. It seems like an optimization introduced with 10.9.

This seems like an example of the generic advice to build on the oldest system you have, since backward compatibility exists much more than forward compatibility (older apps run on newer computers, but not vice versa.)

(On my 10.9 machine, I tried to build Python3.4 for 10.7 by setting MACOSX_DEPLOYMENT_TARGET=10.7.  The make seemed to compile, but crashed in post compile steps with a segfault at something like ‘./python.exe ….sysconfig…. posix vars’.)

Building SIP and PyQt

Again, be sure to use the very latest (later than the latest stable release.)

GENERICALLY, both SIP and PyQt are basically built using:

cd <install dir>
python configure.py
make -j4
sudo make install

But SPECIFICALLY for (pyqtdeploy, OSX, and Python3 ) use options:

  • invoke the target Python version you want distributed by pqqtdeploy (where SIP and PyQt as Python modules will be installed.)
  •  for PyQt you also need the option ‘python configure.py  –qmake ~/Qt5.3.0/5.3/clang_64/bin/qmake’.  (Suitably altered.  You are telling configure.py where the qmake executable is.  configure.py then runs qmake to know details of the Qt installation.)
  • build as static libraries (pyqtdeploy requires it.)

In this example, I want pyqtdeploy to distribute Python3 (actually version 3.4, which python3 is linked to)

SIP:

cd ~/Downloads/sip*
python3 configure.py --static
make -j4
sudo make install

PyQt:

cd ~/Downloads/PyQt*
python3 configure.py --static
make -j4
sudo make install

OSX:

On OSX, also:

python3 configure.py --static --qmake ~/Qt5.3.0/5.3/clang_64/bin/qmake --sip ~/python/bin/sip

If you omit –qmake, it complains that it can’t find qmake.

If you omit –sip, it might find a sip installed earlier and complain that a newer version of sip is required.

You can determine which sip is in your path:

which sip
sip -V
Building sip and PyQt both ways

Building sip and PyQt static is in addition to building dynamic libraries (.a as well as .so forms.)  You probably want to configure and build them twice, once statically and once dynamically, so that in any debugging you do, when you start python, it finds the same version of the PyQt modules as you are linking using pyqtdeploy.

To know what dynamically built modules you are using:

# PyQt module version as reported by 
$(PYTHON) -c "from PyQt5.QtCore import PYQT_VERSION_STR; print(PYQT_VERSION_STR)"
# sip version as reported by SIP executable
$(SIP) -V
# sip module version as reported by Python
$(PYTHON) -c "import sip; print(sip.SIP_VERSION_STR)"

To check that you built both statically and dynamically, you can also compare the modification dates on the .a and .so files in your ~/python/lib/Python3.4/site-packages/Qt5 directory

Fixing a bug in PyQt on OSX

NEVERMIND: This is here for historical reasons.  If you have this situation, it is because you didn’t use the very latest snapshot of PyQt.

If PyQt won’t compile because of this error:

/Users/bootch/Downloads/PyQt-gpl-5.2.1/sip/QtCore/qglobal.sip:132:48: error: conversion from 'long' to 'QFlag'
is ambiguous
*sipCppPtr = new QByteArray::Base64Options(SIPLong_AsLong(sipPy));
/Users/bootch/qt5/qtbase/lib/QtCore.framework/Headers/qflags.h:60:29: note: candidate constructor
Q_DECL_CONSTEXPR inline QFlag(int ai) : i(ai) {}

Then patch Qt header file ~/Qt5.3.0/5.3/clang_64/lib/QtCore.framework/Headers/qflags.h

and remove one ‘!’ (not sign) so you get:

#if defined(_LP64_) && !defined(Q_QDOC)
Q_DECL_CONSTEXPR inline QFlag(long ai) : i(int(ai)) {}

See my bug report QTBUG 38852.  Its actually a bug in PyQt, not in Qt.  The patch above is a hack, it doesn’t fix anything, it just lets you get past the compile error.  If your app is using QFlags, and passing values greater than the max long int in its constructors, you need to properly fix your app.

Installing pyqtdeploy

Installing pip3

pip3 is needed to install pyqtdeploy

  • OSX: pip3 is installed with Python3.
  • Ubuntu: if pip3 is not already installed:
sudo apt-get install python3-pip
Installing pyqtdeploy itself

The pyqtdeploy download page is not really a place to download.  It says (to install from the PyPi website):

pip3 install pyqtdeploy

(If that fails on permissions: sudo pip3…. )

If pyqtdeploy cannot be found

Enter ‘pyqtdeploy’ at a command prompt.  pyqtdeploy will start if it is in your PATH.

But suppose pip3 is the one that was installed with the binary download of Python, and your PATH includes ‘/usr/local/bin’ which contains links to many executables in ‘/Library/Frameworks/….Python3…/bin’  (which I think is done by the binary installer of Python3.)  Then, that is the pip3 that will run, and that’s where pyqtdeploy executable (and the python module named ‘pyqtdeploy’) is installed.  Which is fine,  in that pyqtdeploy can use that instance of python3 to run itself, and use the different statically installed instance of Python to link.

But in my flailing I (later?) changed my PATH so that it did not start with ‘/Library/Frameworks/…Python3/bin’.  Then pyqtdeploy was not found.  So I ended up creating a link:

cd /usr/local/bin
sudo ln -s /Library/Frameworks/Python.framework/Versions/3.4/bin/pyqtdeploy    pyqtdeploy
which pyqtdeploy

Setting PYTHONPATH on Ubuntu

PyQt5 installs itself to a ‘site-packages’ directory.   That directory is not automatically searched by Python on some platforms.  (More specifically, Python documentation says which directories are searched is installation dependent.  On some platforms, ‘site-packages’ is not searched.)

The traditional way to add a directory to the list that Python searches is to define the environment variable PYTHONPATH.

On Ubuntu (either temporarily in the terminal where you start pyqtdeploy, or more permanently in your environment):

export PYTHONPATH=/usr/lib/python3.4/site-packages

Starting pyqtdeploy

pyqtdeploy will create files in the current working directory.  So you probably want to make a special directory for it to work in:

cd ~
mkdir myAppBuild
cd myAppBuild

Then:
pyqtdeploy myApp.pdy

pyqtdeploy is a GUI app, so a window should open.

Using pyqtdeploy

pyqtdeploy is an IDE.  It supports the notion of a project (which is what a .pdy file is.)

pyqtdeploy is also like a ‘build system’.  It generates makefiles and source files and optionally compiles them.

Your major steps are:

  1. configure your project, telling pyqtdeploy the components of your app, and telling pyqtdeploy its environment (where to find other tools.)
  2. pyqtdeploy build your project.  (A partial result can be a .pro file and source files.)
  3. compile your project (using QCreator or Qmake)

(Side note: its not clear to me yet why you would want to invoke QCreator yourself, instead of letting pyqtdeploy invoke it.   Possibly QtCreator includes additional tools for packaging.   Also, if you are cross compiling for mobile platforms??)

Telling pyqtdeploy its environment

Here you tell pyqtdeploy where Python components are located.

You should do this first, before specifying your app components, since many candidate components (that pyqtdeploy shows you so you can choose from them) are Python components.

Choose the ‘Locations’ tab.

‘Host Python Locations>Interpreter’

Click on the ‘Choose File’ button (to the right) and choose:

OSX:   /Users/bootch/python/bin/python3
Ubuntu: /usr/bin/python3

That is usually a link.  It resolves to:

OSX:  /Users/bootch/python/bin/python3.4
Ubuntu: /usr/bin/python3.4

(Note: don’t choose the library version of python.  If you do, when you try to build, you get “OSError: [Errno 8] Exec format error”.  Python binaries include the command line interpreter, which has a user interface to the command line, and a library, which can be called from a c program but has no user interface.)

Note that pyqtdeploy runs under python3, but it can target a different version of the interpreter.  In this tutorial, I assume python3 is also the target version

‘Target Python Locations’

‘Include directory’

These are the c header files (.h) for the statically built python interpreter library.

Ubuntu: /usr/include/python3.4
OSX:/Users/bootch/python/include/python3.4m
‘Python library’

This points to the statically built Python interpreter library (to embed in a c program i.e. the pyqtdeploy main.)

Ubuntu: /usr/lib/i386-linux-gnu/libpython3.4m.a

This location is the one that the Ubuntu package python3-dev installs to.  If you compile the static Python interpreter library yourself, use that location.

I don’t know what the ‘m’ means, but the .a means a static library.

OSX: /Users/bootch/python/lib/python3.4m.a
‘Standard library directory’

This points to the directory of Python modules that are standard parts of a Python distribution.  These are pure Python modules (not compiled into the Python interpreter).  pyqtdeploy freezed them.

(I’m not sure how standard Python modules written in C are handled e.g. in Python3 the _pickle module, which is equivalent to the Python2 cpickle module.)

Ubuntu: /usr/lib/python3.4
OSX: /Users/bootch/python/lib/python3.4
‘Build directory’

This points to the directory where pyqtdeploy outputs your deployed app (as well as some other intermediate files that I did not find useful to read.)  I just accept ‘build’ in the current directory where you started pyqtdeploy.

‘qmake’

This points to Qt’s qmake program, which pyqtdeploy invokes to build your app.

Ubuntu: /usr/bin/qmake
OSX: /Users/bootch/Qt5.3.0/5.3/clang_64/bin/qmake

(As I recall, Ubuntu installs it  here when you install the qt5-default package, described as the ‘Qt 5 development defaults package’)

Telling pyqtdeploy the components of your app

pyqtdeploy doesn’t analyze your app’s imports: you must tell pyqtdeploy which packages/modules your app uses.  pyqtdeploy helps you by scanning directories and offering the contents of the directories as choices.  But you must choose among them (by checking checkboxes.)

These tabs in pyqtdeploy GUI let you specify the components of your app:

  • Application Source
  • PyQt Modules
  • Standard Library
  • Site Packages

They are the various (some well-known) locations for Python modules.

‘Application Source’ Tab

 ‘Main script file’ text box

Enter the name of your ‘main’ script.  You can click on the button to the right to browse.  (The ‘main’ script is a Python file that, for a GUI Qt program, creates an instance of your ‘Application’ class that is a subclass of QApplication, and calls its exec() method, which enters an event loop. )

Note that by convention, the main of your app is outside the source directory.  You should:

  • enter the main script of your app in the “Main script file” textbox of the pyqtdeploy “Application Source” tab, and
  • do NOT check your main script in the list of modules in the “Application Package” list widget (if your main script IS in your source directory.)

“Application Package” tree widget

Click on the ‘Scan’ button.  A file browser opens.

Browse to the ‘source’ directory of your project.  Usually the ‘source’ directory contains all your Python modules except the ‘main’ script.  Typically you have a Python module for each class, including your Application class and all the classes that it instantiates.as

Once you select your ‘source’ directory and choose the OK button, pyqtdeploy scans that directory and shows its contents in the scrolling, hierarchical tree widget (with checkboxes) labeled ‘Application Package.’

In this widget you check off (make a check mark by) all the components of your app.  Usually it is every module in the source directory.

If there is no cruft (unused, broken modules) in the source directory, you can choose the “Include all” button to check all the components.  If there is cruft, you can uncheck it.  If you don’t uncheck the crufty components, you might get syntax errors later.

Note that the GUI does not automatically check the submodules of a package when you check a package (a directory, which in the GUI appears with a + icon next to it.)  Also note that it is not enough to check all the submodules, you must also check the package.  I.e. usually there is a check in every checkbox.

‘PyQt Modules’ Tab

This lists the modules of Qt (for whatever version of Qt you are using.)  You must know which your app imports.  Check them.

If you fail to include a module that your app does import, you get an error when running your program, e.g.

File "bootstrap_py3.py", line 2203, in _find_and_load_unlocked
ImportError: No module named 'PyQt5.QtNetwork'

‘Standard Library’ Tab

This lists the ‘standard’ modules distributed with Python.

Some are already checked: pyqtdeploy uses them during the bootstrapping of your app; they are required.

You must know the standard modules that your app imports, e.g. ‘copy’ or ‘os’ or ‘pickle’

If you fail to check some package that your app does use:

  • the pyqtdeploy build will succeed, but
  • running your app yields e.g. ImportError: No module named ‘os’

The GUI doesn’t understand when you check a  package that submodules should also be checked.  For example, checking ‘logging’ is not sufficient, since your app might also need it’s ‘config’ submodule.

Strategies for discovering which standard modules your app imports

There are several ways to go about this.

It is not enough to know which standard Python modules you explicitly import in your app, since a standard Python module may recursively import other standard Python modules.

Include all

You might choose the “Include all” button.  When I did, the build failed, running out of virtual memory.  I then tried to exclude what I guessed were the larger packages, and the build still ran out of memory.

Using modulefinder or freeze utility

The modulefinder package.

This did not seem to work for me.  It listed many more modules than my app actually needed to get pyqtdeploy to build.  E.g. _strtime.py.

The freeze utility will list to the console all the packages that it thinks are needed (which still may be more than necessary.)  (To find the freeze package on a Debian based system, it has been reported that ‘dpkg -S freeze.pywill tell you, but it said, ‘…pip/commands/freeze.py’ and that particular incarnation did not work.) Ubuntu: package named python3.4-examples contains examples and tools, including freeze.py (installed to /usr/share/doc/python3.4/examples/freeze/freeze.py)  I did not try freeze, and note that it also generates source code files to the current directory.

Iterative

I finally just iterated: build, run your app, see the import error, check the missing standard module, and iterate.  In my experience, it took about 30 iterations.

Other

If you import all modules statically (at the top of your code) and do not import any dynamically (inside a function or method) then you should be able to determine which modules are imported just after your app enters its main event loop?

Site Packages

This lets you specify any ‘third-party’ modules that your app uses.

‘Third-party’ modules are not distributed with Python, but distributed by a third party e.g. a module from PyPi, or that you have written as a separate project but that your app imports and that you install to site-packages.

pyqtdeploy seems to scan a ‘site-packages’ directory rooted in the same place as the target Python library, e.g.

On Ubuntu, when you install such packages use:

python3 setup.py install --prefix=/usr

This installs them to /usr/lib/…./site-packages.   Otherwise (without –prefix=/usr), they are installed to /usr/local/lib/…/dist-packages, where pyqtdeploy does NOT yet look.

Another strategy is to install them to the default location, and soft link them to the location that pyqtdeploy expects.

‘Extension Modules’ Tab

Extension modules are written in C and callable from Python.

I didn’t use any.

But I mistakenly added an entry here (you click in the widget to add an entry) for a pure Python module, and could not figure out how to delete an entry.  But it doesn’t seem to matter (I suppose pyqtdeploy ignored what I had entered.)

More tips for using pyqtdeploy

Under the ‘Build’ tab, always check all the options.   While you are learning pyqtdeploy, you want all the output you can get.  Also, these options are not settings: pyqtdeploy does not remember them from session to session.

pyqtdeploy does not seem to capture your app’s console output.  So to test and see what your app is printing to the console (stdout or stderr?), you need to run your app from another terminal.

Miscellaneous

At some point I got the error:

Error: needed directory /usr/lib/python3.4/config-3.4m not found

but it mysteriously went away.

pyqtdeploy does not deploy Qt

pyqtdeploy makes your app self-contained except for the Qt libraries.  In other words, your app still depends on the Qt libraries.

Packaging your app for distribution

To distribute your app, you also need to package it.

This is one difference between pyinstaller and pyqtdeploy.  Pyinstaller attempts to create a distributable package, pyqtdeploy only attempts to make an executable binary.

OSX and Win: package your deployment with Qt libraries, bundle app icons, and code sign.

Ubuntu: package your deployment declaring a dependency on Qt libraries, bundle app icons, etc.

Size of pyqtdeploy deployment

My app was about 15M, uncompressed.  Roughly speaking, that is comparable to a pyinstaller’ed app (which contains the Qt library but is compressed.)

Optical mouse erratic on pebbly surface

I could be wrong, but it seems like an optical mouse is erratic on a pebbly, shiny surface.

Sanding the surface seems to help.

In my experience,  the pebbly surface was the top of a cheap desk.  Composed of particle board (also known as medium-density fiberboard i.e. sawdust with a binder) with a plastic covering made to look like wood grain.

As far as I know, an optical mouse works by bouncing a laser off the surface.  It makes sense that as the laser scans hills of the pebbles, it has different angles of reflection as it hits the front side of a pebble, then the back side of a pebble.  Unstable oscillation in the fugoid mode (thats a joke, but something you can look up.)

Comparing pyinstaller and pyqtdeploy

Disclaimer: I just started to explore this topic myself, so what follows could be wrong.  I have tested very little of what follows.

Similarities

They both prepare a Python app for distribution.

Also known as freezing.

Differences

general purpose   vs special purpose

pyinstaller works with many Python apps that use many Python packages, not necessarily PyQt (although it works with PyQt.)

pyqtdeploy is for Python apps that use PyQt (and thus Qt).  Apps can use other Python packages, but they must use PyQt.

Static versus dynamic linking

pyinstaller uses dynamic linked libraries.  pyinstaller archives said libraries and fiddles with environment variables so that the frozen app (which is really a boot loader that forks to a Python interpreter which interprets your app) finds the archived libraries instead of any other libraries of the same name on the destination computer.

pyqtdeploy statically links libraries into one big executable.   Again, the executable is basically a Python interpreter interpreting your app, but it all loads at once when the app starts.

Dependency discovery

pyinstaller discovers the Python packages your app uses.

pyqtdeploy requires you to tell it the same information (in a GUI interaction.)

 Python version

pyinstaller seems stuck at Python version less than Python 3.  (The project is working at supporting Python 3, but they are not there yet.)

pyqtdeploy freezes Python2.6 or Python3 apps, but requires Python3 to run.

Target Platforms

pyinstaller supports the desktop platforms (Linux, OSX, Win).  pyinstaller is not a cross compiler.

pyqtdeploy supports the same, and also the major mobile platforms (iOS and Android) (although, currently you must do much work yourself i.e. cross compile libraries for the mobile platforms.   pyqtdeploy might get better in this regard?)

Project organization and licensing

pyinstaller seems to be an open source project with several contributors.

pyqtdeploy is open source but seems to be mainly the contribution of one person.  PyQt itself is licensed for GPL use, but has a commercial license for non open source use.  Since pyqtdeploy depends on PyQt, it really depends on the licences or PyQt (no matter how pyqtdeploy is licensed.)