oscillode/src/blender_maxwell/nodeps/operators/manage_pydeps.py

141 lines
3.5 KiB
Python

from pathlib import Path
import bpy
from blender_maxwell import contracts as ct
from ..utils import pip_process, pydeps, simple_logger
log = simple_logger.get(__name__)
class ManagePyDeps(bpy.types.Operator):
bl_idname = ct.OperatorType.ManagePyDeps
bl_label = 'Blender Maxwell Python Dependency Manager'
bl_options = {'REGISTER'}
show_pydeps_conflicts: bpy.props.BoolProperty(
name='Show Conflicts',
description='Show the conflicts between installed and required packages.',
default=False,
)
####################
# - Property: PyDeps Path
####################
bl__pydeps_path: bpy.props.StringProperty(
default='',
)
@property
def pydeps_path(self):
return Path(bpy.path.abspath(self.bl__pydeps_path))
@pydeps_path.setter
def pydeps_path(self, path: Path) -> None:
self.bl__pydeps_path = str(path.resolve())
####################
# - UI
####################
def draw(self, _: bpy.types.Context) -> None:
layout = self.layout
## Row: Toggle Default PyDeps Path
row = layout.row()
row.alignment = 'CENTER'
row.label(
text="Blender Maxwell relies on Python dependencies that aren't currently satisfied."
)
row.prop(
self,
'show_pydeps_conflicts',
text=f'Show Conflicts ({len(pydeps.DEPS_ISSUES)})',
toggle=True,
)
## Grid: Issues Panel
if self.show_pydeps_conflicts:
grid = layout.grid_flow()
grid.alignment = 'CENTER'
for issue in pydeps.DEPS_ISSUES:
grid.label(text=issue)
# Row: Install Deps
row = layout.row(align=True)
op = row.operator(
ct.OperatorType.InstallPyDeps,
text='Install Python Dependencies (requires internet)',
)
op.bl__pydeps_path = str(self.pydeps_path)
## Row: Uninstall Deps
row = layout.row(align=True)
op = row.operator(
ct.OperatorType.UninstallPyDeps,
text='Uninstall Python Dependencies',
)
op.bl__pydeps_path = str(self.pydeps_path)
## Row: Deps Install Progress
row = layout.row()
num_req_deplocks = len(pydeps.DEPS_REQ_DEPLOCKS)
if pydeps.DEPS_OK:
row.progress(
text=f'{num_req_deplocks}/{num_req_deplocks} Installed',
factor=1.0,
)
elif pip_process.PROGRESS is not None:
row.progress(
text='/'.join(pip_process.PROGRESS_FRAC) + ' Installed',
factor=float(pip_process.PROGRESS),
)
else:
row.progress(
text=f'0/{num_req_deplocks} Installed',
factor=0.0,
)
## Row: Toggle Default PyDeps Path
row = layout.row()
row.alignment = 'CENTER'
row.label(
text='After installation, the addon is ready to use. For more details, please refer to the addon preferences.'
)
####################
# - Execute
####################
def invoke(self, context: bpy.types.Context, event: bpy.types.Event):
if not bpy.app.background:
# Force-Move Mouse Cursor to Window Center
## This forces the popup dialog to spawn in the center of the screen.
context.window.cursor_warp(
context.window.width // 2,
context.window.height // 2 + 2 * bpy.context.preferences.system.dpi,
)
# Spawn Popup Dialogue
return context.window_manager.invoke_props_dialog(
self, width=8 * bpy.context.preferences.system.dpi
)
log.info('Skipping ManagePyDeps popup, since Blender is running without a GUI')
return {'INTERFACE'}
def execute(self, _: bpy.types.Context):
if not pydeps.DEPS_OK:
self.report(
{'ERROR'},
f'Python Dependencies for "{ct.addon.NAME}" were not installed. Please refer to the addon preferences.',
)
return {'FINISHED'}
####################
# - Blender Registration
####################
BL_REGISTER = [
ManagePyDeps,
]
BL_HOTKEYS = []