FEM: Handle python vtk user installs that conflict with FreeCAD VTK
This commit is contained in:
236
src/Mod/Fem/femguiutils/vtk_module_handling.py
Normal file
236
src/Mod/Fem/femguiutils/vtk_module_handling.py
Normal file
@@ -0,0 +1,236 @@
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net> *
|
||||
# * *
|
||||
# * This file is part of the FreeCAD CAx development system. *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * 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 Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
"""Methods to verify if the python VTK module is the correct one
|
||||
|
||||
FreeCAD is linked with VTK libraries during its build process. To use the VTK
|
||||
python module and pass objects between python and c++ the compiled module library
|
||||
needs to be linked to the exact same vtk library as FreeCAD is. This is ensured by
|
||||
installing VTK via linux app managers: All known distros install the python side
|
||||
packages together with vtk libs. Libpack and other OS systems ensure this too.
|
||||
|
||||
However, if a vtk python package is installed manually, e.g. by "pip install vtk",
|
||||
it could be found instead of the system module. This python API brings its own
|
||||
set of VTK libs, and hence object passing in FreeCAD fails. (Note: import and
|
||||
pure vtk python code still works, only passing to c++ fails)
|
||||
|
||||
This file provides functions that detect this situation and inform the user.
|
||||
Additionally we try to find the correct module in the path and offer to use
|
||||
it instead.
|
||||
|
||||
Note that this problem occurs with all "compiled binary" python APIs, also
|
||||
with PySide. It is the users responsibility to handle his python path and keep
|
||||
it clean/working. The functions provided here are a workaround only.
|
||||
"""
|
||||
|
||||
__title__ = "FEM GUI vtk python module check"
|
||||
__author__ = "Stefan Tröger"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
__user_input_received = False
|
||||
|
||||
def vtk_module_compatible():
|
||||
# checks if the VTK library FreeCAD is build against is the one used by
|
||||
# the python module
|
||||
|
||||
# make sure we do not contaminate the modules with vtk to not trick
|
||||
# the check later
|
||||
unload = not _vtk_is_loaded()
|
||||
|
||||
import Fem
|
||||
from vtkmodules.vtkCommonCore import vtkVersion, vtkBitArray
|
||||
|
||||
# simple version check
|
||||
if Fem.getVtkVersion() != vtkVersion.GetVTKVersion():
|
||||
return False
|
||||
|
||||
# check binary compatibility
|
||||
result = Fem.isVtkCompatible(vtkBitArray())
|
||||
|
||||
if unload:
|
||||
# cleanup our own import
|
||||
_unload_vtk_modules()
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _vtk_is_loaded():
|
||||
import sys
|
||||
return any("vtkmodules" in module for module in sys.modules)
|
||||
|
||||
|
||||
def _unload_vtk_modules():
|
||||
# unloads all loaded vtk modules
|
||||
# NOTE: does not remove any stored references in objects
|
||||
|
||||
import sys
|
||||
for module in sys.modules.copy():
|
||||
if "vtkmodules" in module:
|
||||
del sys.modules[module]
|
||||
|
||||
|
||||
def _find_compatible_module():
|
||||
# Check all python path folders if they contain a vtk module
|
||||
|
||||
import Fem
|
||||
import sys
|
||||
|
||||
# remove module from runtime
|
||||
_unload_vtk_modules()
|
||||
|
||||
path = sys.path.copy()
|
||||
|
||||
for folder in reversed(path):
|
||||
try:
|
||||
# use a single folder as path and try to load vtk
|
||||
sys.path = [folder]
|
||||
if vtk_module_compatible():
|
||||
# we do still unload, to let the user descide if he wants to use it
|
||||
_unload_vtk_modules()
|
||||
sys.path = path
|
||||
return folder
|
||||
|
||||
except:
|
||||
continue
|
||||
|
||||
# reset the correct path and indicate that we failed
|
||||
sys.path = path
|
||||
return None
|
||||
|
||||
|
||||
def _load_vtk_from(folder):
|
||||
|
||||
import sys
|
||||
|
||||
path = sys.path
|
||||
try:
|
||||
sys.path = [folder]
|
||||
import vtkmodules
|
||||
finally:
|
||||
sys.path = path
|
||||
|
||||
|
||||
# If FreeCAD is build with VTK python support this function checks if the
|
||||
# used python module is compatible with the c++ lib. Does inform the user
|
||||
# if not so and offers the correct module, if available
|
||||
#
|
||||
# Note: Call this also from Python feature module, as on document load
|
||||
# this can be loaded before initializing FEM workbench.
|
||||
def vtk_module_handling():
|
||||
|
||||
import sys
|
||||
import FreeCAD
|
||||
|
||||
if not "BUILD_FEM_VTK_PYTHON" in FreeCAD.__cmake__:
|
||||
# no VTK python api support in FreeCAD
|
||||
return
|
||||
|
||||
# only ask user once per session
|
||||
global __user_input_received
|
||||
if __user_input_received:
|
||||
return
|
||||
__user_input_received = True
|
||||
|
||||
loaded = _vtk_is_loaded()
|
||||
|
||||
# check if we are compatible
|
||||
if not vtk_module_compatible():
|
||||
|
||||
if not FreeCAD.GuiUp:
|
||||
FreeCAD.Console.PrintError("FEM: vtk python module is not compatible with internal vtk library")
|
||||
return
|
||||
|
||||
import FreeCAD, Fem
|
||||
from vtkmodules.vtkCommonCore import vtkVersion
|
||||
import inspect
|
||||
from PySide import QtGui
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
path = inspect.getfile(vtkVersion)
|
||||
path = path[:path.find("vtkmodules")]
|
||||
|
||||
message = translate("FEM", ("FreeCAD is linked to a different VTK library then the imported "
|
||||
"VTK python module. This is incompatible and will lead to errors."
|
||||
"\n\nWrong python module is imported from: \n{}")).format(path)
|
||||
|
||||
buttons = QtGui.QMessageBox.Discard
|
||||
|
||||
# check if there is any compatible vtk module
|
||||
compatible_module = _find_compatible_module()
|
||||
|
||||
if compatible_module:
|
||||
# there is a compatible module of VTK available.
|
||||
message += translate("FEM", "\n\nCorrect module found in: \n{}").format(compatible_module)
|
||||
|
||||
if not loaded:
|
||||
# vtk was not loaded beforehand, therefore we can realistically reload
|
||||
message += translate("FEM", "\n\nShould this module be loaded instead?")
|
||||
|
||||
buttons = QtGui.QMessageBox.Yes | QtGui.QMessageBox.No
|
||||
|
||||
else:
|
||||
message += translate("FEM", ("\n\nAs the wrong module was already loaded, a reload is not possible. "
|
||||
"Restart FreeCAD to get the option for loading this module."))
|
||||
|
||||
else:
|
||||
message += translate("FEM", "\n\nNo matching module was found in the current python path.")
|
||||
|
||||
|
||||
# raise a dialog to the user
|
||||
import FreeCADGui
|
||||
button = QtGui.QMessageBox.critical(
|
||||
FreeCADGui.getMainWindow(),
|
||||
translate("FEM", "VTK module conflict"),
|
||||
message,
|
||||
buttons=buttons,
|
||||
)
|
||||
|
||||
if button == QtGui.QMessageBox.Yes:
|
||||
# try to reload the correct vtk module
|
||||
_load_vtk_from(compatible_module)
|
||||
|
||||
|
||||
# Returns if vtk python is incompatible and hence operations need to be aborted.
|
||||
# If inform=True the user gets informed by dialog about incompatibilities
|
||||
def vtk_compatibility_abort(inform=True):
|
||||
|
||||
if not vtk_module_compatible():
|
||||
|
||||
if inform:
|
||||
# raise a dialog to the user that this functionality is not available
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
from PySide import QtGui
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
button = QtGui.QMessageBox.critical(
|
||||
FreeCADGui.getMainWindow(),
|
||||
translate("FEM", "VTK module conflict"),
|
||||
translate("FEM", "This functionality is not available due to VTK python module conflict"),
|
||||
buttons=QtGui.QMessageBox.Discard,
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
return False
|
||||
Reference in New Issue
Block a user