Running wxPython Inside a Python3 VirtualEnv

wxPython is notoriously difficult to get running inside a VirtualEnv, where instructions exist they focus on Python2 and tend to be (or at least appear) long and complicated. After a **lot** of fighting this is the simplest solution I came up with.

This post follows (mostly) the instructions provided by Ingmar Steen on Blurring Existence, with a few bits re-written to work with Python3 and quite a lot of steps skipped as they're not necessary.

I am running: Python 3.6 on macOS High Sierra (10.13.6).

I initially tried blithely creating a VirtualEnv (I use VirtualEnvWrapper) and installing wxPython in it. I then created a basic script (following the instructions on the wxPython overview):

import wx

app = wx.App()
frm = wx.Frame(None, title="Hello World")
frm.Show()
app.MainLoop()

and ran it which got me this:

This program needs access to the screen. Please run with a
Framework build of python, and only when you are logged in
on the main display of your Mac.

It turns out that this is because of

the way that the virtualenv is constructed the Python that is there sort of loses its connection with the framework that it comes from, and so using it directly triggers that security mechanism and the wx code is not able to get full access to the screen.
Source: https://stackoverflow.com/a/15754033

So, how do we fix this? Having read a LOT of solutions and tried various ways of hacking my VirtualEnv I gave up and went back and reread the initial instructions I found along with Ingmar's blog post. What my hacking had proved to me (other than that it's surprisingly easy to crash system Python) was that simply ignoring most of the instructions didn't actually work.

Having recreated my VirtualEnv many times in the process here's the most minimal set of instructions that did:

# install wx-python globally (yeah I hate this too but at least it's only one thing)
pip3 install wxpython

# create a clean virtual environment for your app
mkvirtualenv mywxapp

# deactivate it
deactivate

# replace the python wrapper created by the VirtualEnv with system python
cd mywxapp/bin/
mv python python_orig
ln -s /usr/local/bin/python3 python

Edit your activate file to include unset PYTHONHOME on line 15 above:

if ! [ -z "${_OLD_VIRTUAL_PYTHONHOME+_}" ] ; then
    PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME"
    export PYTHONHOME
    unset _OLD_VIRTUAL_PYTHONHOME
fi

and add PYTHONHOME="$VIRTUAL_ENV" on line 57 below:

if ! [ -z "${PYTHONHOME+_}" ] ; then
    _OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME"
    unset PYTHONHOME
fi

You can also put these into postactivate and predeactivate as Ingmar does, however if you do that you will get VirtualEnvWrapper errors, which don't appear to actually affect functionality but are annoying.

# restart your VirtualEnv
workon mywxapp

And check that it works by running hello_world.py again:

hello_world.py running from a VirtualEnv on my computer

Comments powered by Talkyard.