I think you're asking two questions:
Why use tox (or virtualenvs) rather than containers, and if I use both, isn't this unnecessary duplication?
How to I best integrate gitlab runners and tox?
For 1, unless you expect to locally test inside docker, using tox makes it much easier to test your code locally. Therefore, to keep your CI as close to your local ...
Our team is using poetry with configurations included in pyproject.toml for dependency management, and the Dockerfile looks like
FROM some image
COPY pyproject.toml .
COPY poetry.lock .
RUN poetry install --no-interaction --no-ansi --no-dev
COPY our_application_code /app/our_application_code
and this generated image is loaded in gitlab CI for testing ...