Skip to content
This repository has been archived by the owner on Oct 4, 2023. It is now read-only.

Commit

Permalink
Improved dependency aviability check
Browse files Browse the repository at this point in the history
Moved the bootstrap code to an appropriate files
Checking if the packages are installed without importing the modules.
Also add a check on animate object because auto-install doesn't call register/unregister.
Added uninstallation of packages on unregistration.
Added Exception on some errors so they are displayer as a Blender popup instead on only a print on console.
Increased version.
  • Loading branch information
federicoB committed Sep 3, 2018
1 parent 180bd6b commit 24436e6
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 58 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ The sensors data must be in [this format (FullInertial)](https://github.com/phys
## Install

1. Download latest .zip release from [here](https://github.com/physycom/vehicle_dynamics_Blender/releases)
2. Install .zip as a blender common add-on as explained also [here](https://docs.blender.org/manual/en/dev/preferences/addons.html)
2. Install .zip as a blender common add-on as explained also [here](https://docs.blender.org/manual/en/dev/preferences/addons.html).
During the add-on activation / deactivation dependencies are installed/uninstalled so you could have to wait a while.

## Usage

Expand Down
64 changes: 8 additions & 56 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"location": "View3D > Tools",
"description": "Blender simulation generator from inertial sensor data on car",
"category": "Object", # TODO other possible categories are Animation and Physics
"version": (1,1,0),
"version": (1,1,1),
"tracker_url" : "https://github.com/physycom/inertial_to_blender/issues"
}

Expand All @@ -14,10 +14,7 @@
sys.path.append(str(Path(__file__).parent))
# different name from project and deployed zip
from . import addon_updater_ops
import os
import urllib.request

from subprocess import call
from blender import bootstrap

from bpy.props import StringProperty

Expand Down Expand Up @@ -89,6 +86,7 @@ class AnimateObject(bpy.types.Operator):
bl_options = {'REGISTER', 'UNDO'}

def execute(self, context):
bootstrap.check_modules_existence()
from src import get_trajectory_from_path
scene = context.scene
# get current frame per seconds value
Expand Down Expand Up @@ -138,60 +136,13 @@ def execute(self, context):
return {'FINISHED'}


def call_system_command(command):
try:
retcode = call(command, shell=True)
if retcode < 0:
print("Child was terminated by signal", -retcode, file=sys.stderr)
else:
print("Child returned", retcode, file=sys.stderr)
except OSError as e:
print("Execution failed:", e, file=sys.stderr)


def register():
# register auto-update module
# placed this on top so the plugin degenerate to a non working version
# this can be fixed by a new release
addon_updater_ops.register(bl_info)
bpy.utils.register_class(AutoUpdatePreferences)

# TODO handle permission errors
addon_path = str(Path(__file__).parent)
print("Addon path " + addon_path)
blender_path = str(Path(sys.executable).parent)
print("Blender path " + blender_path)
# TODO detect version, make more flexible
# _, dirs, _ = next(os.walk(blender_path))
# blender_version_dir = dirs[0]
blender_version_dir = "2.79"
blender_python_dir = os.path.join(blender_path, blender_version_dir, "python")
posix_pip_location = os.path.join(blender_python_dir, "bin", "pip")
windows_pip_location = os.path.join(blender_python_dir, "scripts", "pip3.exe")
if not (os.path.exists(posix_pip_location) or os.path.exists(windows_pip_location)):
print("Downloading pip")
# download get pip
pip_download_location = os.path.join(addon_path, "get_pip.py")
urllib.request.urlretrieve("https://bootstrap.pypa.io/get-pip.py",
filename=pip_download_location)
python_bin = os.path.join(blender_python_dir, "bin")
if (os.path.exists(os.path.join(python_bin, "python3.5m"))):
python_interpreter = os.path.join(python_bin, "python3.5m")
elif os.path.exists(os.path.join(python_bin, "python.exe")):
python_interpreter = os.path.join(python_bin, "python.exe")
command = r'"{}" "{}"'.format(python_interpreter, pip_download_location)
print("Command: " + command)
call_system_command(command)
try:
import scipy, pyquaternion
except ImportError:
print("Installing packets")
if (os.path.exists(posix_pip_location)):
command = r'"{}" install -r "{}"'.format(posix_pip_location, os.path.join(addon_path, "requirements.txt"))
elif os.path.exists(windows_pip_location):
command = r'"{}" install -r "{}"'.format(windows_pip_location, os.path.join(addon_path, "requirements.txt"))
call_system_command(command)
# install
bootstrap.install_dependencies()
bpy.utils.register_class(LoadDataset)
bpy.utils.register_class(AnimateObject)
bpy.utils.register_class(InertialBlenderPanel)
Expand All @@ -202,11 +153,12 @@ def unregister():
# TODO move to implicit unregistration (module)
addon_updater_ops.unregister()
bpy.utils.unregister_class(AutoUpdatePreferences)
bpy.utils.unregister_class(InertialBlenderPanel)
bpy.utils.unregister_class(AnimateObject)
bootstrap.uninstall_packages_from_requirements_file()
bpy.utils.unregister_class(LoadDataset)
bpy.utils.unregister_class(AnimateObject)
bpy.utils.unregister_class(InertialBlenderPanel)


# demo bare-bones preferences
class AutoUpdatePreferences(bpy.types.AddonPreferences):
bl_idname = __package__

Expand Down
121 changes: 121 additions & 0 deletions blender/bootstrap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
"""
Auto-installation of dependencies that blender-addon needs
This file is part of inertial_to_blender project,
a Blender simulation generator from inertial sensor data on cars.
Copyright (C) 2018 Federico Bertani
Author: Federico Bertani
Credits: Federico Bertani, Stefano Sinigardi, Alessandro Fabbri, Nico Curti
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""

import os
import sys
import urllib.request
import subprocess
import importlib
from pathlib import Path

addon_path = str(Path(__file__).parent.parent)
blender_path = str(Path(sys.executable).parent)
# TODO detect version, make more flexible
# _, dirs, _ = next(os.walk(blender_path))
# blender_version_dir = dirs[0]
blender_version_dir = "2.79"
blender_python_dir = os.path.join(blender_path, blender_version_dir, "python")
posix_pip_location = os.path.join(blender_python_dir, "bin", "pip")
windows_pip_location = os.path.join(blender_python_dir, "scripts", "pip3.exe")
pip_location = {
'posix':posix_pip_location,
'windows':windows_pip_location
}
requirements_file_position = os.path.join(addon_path, "requirements.txt")


def call_system_command(command):
try:
retcode = subprocess.call(command, shell=True)
if retcode < 0:
print("Child was terminated by signal", -retcode, file=sys.stderr)
else:
return retcode
except OSError as e:
print("Execution failed:", e, file=sys.stderr)

def install_packages_from_requirements_file():
command = None
if (os.path.exists(pip_location['posix'])):
command = r'"{}" install -r "{}"'.format(pip_location['posix'], requirements_file_position)
elif os.path.exists(pip_location['windows']):
command = r'"{}" install -r "{}"'.format(pip_location['windows'], requirements_file_position)
if command:
ret_code = call_system_command(command)
if ret_code>0:
raise Exception("Error installing dependencies")
else:
raise Exception("Error on finding pip location")

def uninstall_packages_from_requirements_file():
command = None
if (os.path.exists(pip_location['posix'])):
command = r'"{}" uninstall -y -r "{}"'.format(pip_location['posix'], requirements_file_position)
elif os.path.exists(pip_location['windows']):
command = r'"{}" uninstall -y -r "{}"'.format(pip_location['windows'], requirements_file_position)
if command:
ret_code = call_system_command(command)
if ret_code>0:
raise Exception("Error uninstalling dependencies")
else:
raise Exception("Error on finding pip location")


def check_modules_existence():
# made a list instead of using requirements.txt because packages and modules name can differ
# also reading the output of pip freeze in blender is tricky because it's open me another instance of blender
required_modules = ['numpy','numba','quaternion']
there_is_a_missing_package = False
for required_module in required_modules:
if not importlib.util.find_spec(required_module):
there_is_a_missing_package = True
if there_is_a_missing_package:
return install_packages_from_requirements_file()
else:
return 0

def install_dependencies():
# TODO handle permission errors

print("Addon path " + addon_path)

print("Blender path " + blender_path)

if not (os.path.exists(posix_pip_location) or os.path.exists(windows_pip_location)):
print("Downloading pip")
# download get pip
pip_download_location = os.path.join(addon_path, "get_pip.py")
urllib.request.urlretrieve("https://bootstrap.pypa.io/get-pip.py",
filename=pip_download_location)
python_bin = os.path.join(blender_python_dir, "bin")
if (os.path.exists(os.path.join(python_bin, "python3.5m"))):
python_interpreter = os.path.join(python_bin, "python3.5m")
elif os.path.exists(os.path.join(python_bin, "python.exe")):
python_interpreter = os.path.join(python_bin, "python.exe")
command = r'"{}" "{}"'.format(python_interpreter, pip_download_location)
print("Command: " + command)
call_system_command(command)
return_code = check_modules_existence()
return return_code
5 changes: 4 additions & 1 deletion deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,18 @@ def zipdir(path, ziph):
# prepare other path strings
addon_updater_path = os.path.join(my_path, "blender-addon-updater", "addon_updater.py")
src_full_path = os.path.join(my_path, "src")
new_src = os.path.join(path, "src")
blender_utils_full_path = os.path.join(my_path,"blender")
init_full_path = os.path.join(my_path, "__init__.py")
requirements_full_path = os.path.join(my_path, "requirements.txt")
addon_updater_ops_path = os.path.join(my_path, "my_addon_updater_ops.py")
new_src = os.path.join(path, "src")
new_blender_utils = os.path.join(path,"blender")
addon_updater_ops_new_path = os.path.join(path, "addon_updater_ops.py")
# TODO if dir exist remove it
os.makedirs(path, exist_ok=True)
# copy necessary project file to temp directory
shutil.copytree(src_full_path, new_src)
shutil.copytree(blender_utils_full_path,new_blender_utils)
shutil.copy(init_full_path, path)
shutil.copy(addon_updater_ops_path, addon_updater_ops_new_path)
shutil.copy(addon_updater_path, path)
Expand Down

0 comments on commit 24436e6

Please sign in to comment.