0

I've developed a QGIS plugin that needs to have installed external libraries to work. While I was working I've installed those libraries with pip command via osgeo4W shell but I don't want to each one on my company that want tot use the plugin have to go to the osgeo4W shell to install it. I'm not able to make my code running correctly to automatically install libraries when install the plugin. I've tried with multiple ways, last one is that but seems to do nothing... and by now I'm a little bit lost on my purpose.

main plugin file:

if os.path.isfile('install_deps.py'):
        print('WARNING: new dependies will be installed....')
        import install_deps
        install_deps.installer_func
        os.rename('install_deps.py', 'install_deps.installed')
    else:
        pass

install_deps.py file

import os

def installer_func():
    plugin_dir = pathlib.Path(__file__).parent.parent

    try:
        import pip
    except ImportError:
        exec(
            open(str(pathlib.Path(plugin_dir, 'scripts', 'get_pip.py'))).read()
        )
        import pip
        # just in case the included version is old
        pip.main(['install', '--upgrade', 'pip'])

    sys.path.append(plugin_dir)

    with open(str(plugin_dir / 'requirements.txt'), "r") as requirements:
        for dep in requirements.readlines():
            dep = dep.strip().split("==")[0]
            try:
                __import__(dep)
            except ImportError as e:
                print("{} not available, installing".format(dep))
                pip.main(['install', dep])

requirements.txt file:

fuzzywuzzy==0.19.0
Unidecode==1.3.2

Can you help me?

2

I had exactly the same problem. When you use pip.main, you should get a warning saying that you are using an old script that will be removed in future versions of pip, so I would not recommend using this. What I did was to deal with requirements.txt through a bat file. I give you an example of a small script to generate and run the bat on the fly. You can adapt this to your case and run it at the start or installation of the plugin.

Code

import os
import sys
import subprocess
from pathlib import Path

#definition of the bat file as as string
#it will be equivalent to manually type these commands line by line
batF="""@echo off
    call "->QGISPATH<-\\o4w_env.bat"
    call "py3_env"
    call python -m pip install -r \"->HOMEPATH<-requirements.txt\"
    call exit
    @echo on
    """

# then the idea is to find the osgeo4w shell path
# sys.executable is the base execution path and contains o4w_env.bat
# o4w_env.bat setup the osgeo4w shell
qgispath = str(os.path.dirname(sys.executable))

# here you replace the string ->QGISPATH<- with qgis path
# so that the script is installation independant
batF = batF.replace("->QGISPATH<-",qgispath)

# and the string ->HOMEPATH<- with the path to your requirements.txt
batF = batF.replace("->HOMEPATH<-",yourRequirementsPath)

# then you write it to a .bat file and run it

with open("pathToTheBat.bat","w") as f:
    f.write(batF)

subprocess.run(["pathToTheBat.bat"])

Comments

It is equivalent to open osgeo4w and install requirements.

It may not be what your are looking for. Specifically, you cannot properly handle the errors that could occur during the package installation as you are running python through a batch file.

0

Thank'u Thomas for your answer, in the end I came up with a similar solution:

I've created a .bat file

@echo off
set "input=C:\Program Files\QGIS 3.16"
echo %input%

@echo ON

cd /d %~dp0

call py3-env.bat "%input%"

python3 -m pip install --upgrade pip
python3 -m pip install fuzzywuzzy
python3 -m pip install Unidecode

and I run it from main plugin file as

import subprocess
install_modules = location of my "pip_install_packages.bat" file
subprocess.run([install_modules], shell=True)

It works for me :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.