Ir al contenido principal

Ralsina.Me — El sitio web de Roberto Alsina

Shipping your PyQt app for windows


I ha­ve wri­tten about this in the pas­t, wi­th the ge­ne­ral con­clu­sion being "i­t's a pain in the ass".

So, no­w, he­re is how it's do­ne.

  1. Start wi­­th a wo­­­rking Py­­Qt appli­­ca­­tio­­n. In this exa­m­­ple, I wi­­ll use de­­vi­­cen­­zo­­.­­py mo­s­­tly be­­­cau­se:

    1. It is a wo­­­­­rking Py­­­Qt appli­­­ca­­­tio­­­n.

    2. It uses a big chunk of Py­­­Qt

    3. It's ea­sy to test

  2. Now you need a se­tu­p.­py. He­re's one that wo­rks, wi­th ex­ten­si­ve co­m­m­men­ts.

# We will be using py2exe to build the binaries.
# You may use other tools, but I know this one.

from distutils.core import setup
import py2exe

# Now you need to pass arguments to setup
# windows is a list of scripts that have their own UI and
# thus don't need to run in a console.

setup(windows=['devicenzo.py'],
      options={

# And now, configure py2exe by passing more options;

          'py2exe': {

# This is magic: if you don't add these, your .exe may
# or may not work on older/newer versions of windows.

              "dll_excludes": [
                  "MSVCP90.dll",
                  "MSWSOCK.dll",
                  "mswsock.dll",
                  "powrprof.dll",
                  ],

# Py2exe will not figure out that you need these on its own.
# You may need one, the other, or both.

              'includes': [
                  'sip',
                  'PyQt4.QtNetwork',
                  ],

# Optional: make one big exe with everything in it, or
# a folder with many things in it. Your choice
#             'bundle_files': 1,
          }
      },

# Qt's dynamically loaded plugins and py2exe really don't
# get along.

data_files = [
            ('phonon_backend', [
                'C:\Python27\Lib\site-packages\PyQt4\plugins\phonon_backend\phonon_ds94.dll'
                ]),
            ('imageplugins', [
            'c:\Python27\lib\site-packages\PyQt4\plugins\imageformats\qgif4.dll',
            'c:\Python27\lib\site-packages\PyQt4\plugins\imageformats\qjpeg4.dll',
            'c:\Python27\lib\site-packages\PyQt4\plugins\imageformats\qsvg4.dll',
            ]),
],

# If you choose the bundle above, you may want to use this, too.
#     zipfile=None,
)
  1. Run py­thon se­tu­p.­py py2exe and get a dist fol­der fu­ll of bi­na­ry good­ness.

And tha­t's it. Ex­cept of cour­se, tha­t's not it.

What this wi­ll do is crea­te a bi­na­ry se­t, ei­ther a fol­der fu­ll of things, or a sin­gle EXE fi­le. And tha­t's not enou­gh. You ha­ve to con­si­der at least the fo­llo­win­g:

  1. Put eve­­r­­y­­thing in re­­sou­r­­ce fi­­le­s: ima­­ges, qss fi­­le­s, ico­n­s, etc. Eve­­ry fi­­le your app nee­­d­s? Put it in a re­­sou­r­­ce fi­­le and load it from the­­re. That way you do­­n't ha­­ve to ca­­re about them if you go the "o­­­ne exe" road.

  2. Co­m­­pi­­le .ui fi­­les to .py (s­a­­me rea­­so­­n)

  3. Fi­­gu­­re out if you use Qt's plu­­gi­n­s, and make them wo­­­rk. This in­­clu­­des: using Pho­­­no­­n, using QtS­­Q­­L, and using any ima­­ge fo­r­­ma­­ts other than PN­­G.

After you ha­ve tha­t, are you do­ne? NO!

Your win­do­ws user wi­ll want an ins­ta­lle­r. I am not going to go in­to de­tail­s, but I had a good ti­me using Bi­tRo­ck's Ins­ta­ll­Buil­der for Qt. It's a ni­ce tool, and it wo­rks. Tha­t's a lot in this fiel­d.

But is that all? NO!

You ha­ve to take ca­re of the Vi­sual Stu­dio Runti­me. My su­gges­tio­n? Get a co­py of the 1.1MB vcre­dis­t_­x86.exe (not the lar­ger one, the 1.1MB one), and ei­ther te­ll peo­ple to ins­ta­ll it ma­nua­ll­y, or add it to your ins­ta­lle­r. You are le­ga­lly allo­wed (A­FAIK) to re­dis­tri­bu­te that thing as a who­le. But not wha­t's in it (un­le­ss you ha­ve a VS li­cen­se).

And we are do­ne? NO!

On­ce you run your app "ins­ta­lle­d", if it ever prin­ts an­y­thing to stde­rr, you wi­ll get ei­ther a dia­log te­lling you it di­d, or wor­se (if you are in ay­thing newer than XP), a dia­log te­lling you it can't wri­te to a log fi­le, and the app wi­ll ne­ver wo­rk agai­n.

This is be­cau­se py2exe ca­tches stde­rr and tries to save it on a lo­gfi­le. Whi­ch it tries to crea­te in the sa­me fol­der as the bi­na­r­y. Whi­ch is usua­lly not allo­wed be­cau­se of per­mis­sion­s.

So­lu­tio­n? Your app should ne­ver wri­te to stde­rr. Wri­te an ex­cep­thook and ca­tch tha­t. And then re­mo­ve stde­rr or re­pla­ce it wi­th a log fi­le, or so­me­thin­g. Just do­n't let py2exe do it, be­cau­se the way py2exe does it is bro­ken.

And is that it?

We­ll, ba­si­ca­lly ye­s. Of cour­se you should get 4 or 5 di­ffe­rent ver­sions of win­do­ws to test it on, but you are pre­tty mu­ch free to ship your app as you wis­h. Oh, mind you, do­n't upload it to do­wn­load­s.­com be­cau­se they wi­ll wrap your ins­ta­ller in a lar­ger one that ins­ta­lls bloa­twa­re and cra­p.

So, the­re you go.


Contents © 2000-2023 Roberto Alsina