diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..d57929b9e --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + groups: + actions: + patterns: + - "*" diff --git a/.github/workflows/headless-tests.yml b/.github/workflows/headless-tests.yml index 96c0ca2fd..8ed9362a8 100644 --- a/.github/workflows/headless-tests.yml +++ b/.github/workflows/headless-tests.yml @@ -31,10 +31,10 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip setuptools wheel - python -m pip install numpy "vtk<9.3" pillow pytest traitsui + python -m pip install numpy "vtk<9.3" pillow pytest pytest-timeout traitsui - name: Install mayavi and tvtk - run: python -m pip install -v . - - name: Test tvtk package - run: pytest -v --pyargs tvtk + run: python -m pip install --no-build-isolation -v . - name: Test Mayavi package - run: pytest -v --pyargs mayavi + run: pytest -v --timeout=10 --pyargs mayavi + - name: Test tvtk package + run: pytest -v --timeout=60 --pyargs tvtk diff --git a/.github/workflows/run-mayavi-tests.yml b/.github/workflows/run-mayavi-tests.yml index 8d4553fca..af3465834 100644 --- a/.github/workflows/run-mayavi-tests.yml +++ b/.github/workflows/run-mayavi-tests.yml @@ -10,39 +10,68 @@ jobs: tests: strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-13] - python-version: ['3.9'] - qt-api: ['pyqt5'] - vtk: ['vtk<9.3'] + # Always test against the latest VTK, NumPy, and Qt bindings + os: [ubuntu-latest, windows-latest, macos-13, macos-15] + python-version: ['3.12'] + qt-api: ['pyside6'] # prefer the modern bindings + vtk: ['vtk>=9.4'] # always prefer the latest + # Then add some backward compat checks include: - - python-version: '3.12' + # PyQt6 and vtk 9.3 + - python-version: '3.10' qt-api: 'pyqt6' os: ubuntu-latest - vtk: 'vtk>=9.3' - - python-version: '3.12' + vtk: 'vtk==9.3' + - python-version: '3.10' qt-api: 'pyqt6' os: macos-14 # arm64 - vtk: 'vtk>=9.3' - - python-version: '3.12' + vtk: 'vtk==9.3' + - python-version: '3.10' qt-api: 'pyqt6' os: windows-latest - vtk: 'vtk>=9.3' + vtk: 'vtk==9.3' - python-version: '3.11' qt-api: 'pyqt6' os: ubuntu-latest - vtk: 'vtk>=9.3' - - python-version: '3.10' - qt-api: 'pyside6' + vtk: 'vtk==9.3' + # PyQt5 and vtk 9.2 (and one oldest Python) + - python-version: '3.9' + qt-api: 'pyqt5' os: ubuntu-latest - vtk: 'vtk<9.3' + vtk: 'vtk==9.2.5' - python-version: '3.10' - qt-api: 'pyside6' + qt-api: 'pyqt5' os: windows-latest - vtk: 'vtk<9.3' + vtk: 'vtk==9.2.5' - python-version: '3.10' qt-api: 'pyside6' os: macos-13 - vtk: 'vtk<9.3' + vtk: 'vtk==9.2.5' + # Some old NumPys + - python-version: '3.12' + qt-api: 'pyside6' + os: ubuntu-latest + vtk: 'vtk==9.3' + numpy: 'numpy==1.26.4' + - python-version: '3.12' + qt-api: 'pyside6' + os: windows-latest + vtk: 'vtk==9.3' + numpy: 'numpy==1.26.4' + # Older Python and VTKs + - python-version: '3.10' + qt-api: 'pyqt5' + os: ubuntu-latest + vtk: 'vtk==9.2.2' + # TVTK tests intermittently segfault on 9.1 + # - python-version: '3.9' + # qt-api: 'pyqt5' + # os: ubuntu-latest + # vtk: 'vtk==9.1.0' + - python-version: '3.9' + qt-api: 'pyqt5' + os: ubuntu-latest + vtk: 'vtk==9.0.2' # oldest available on 3.9 which is the oldest Python we support fail-fast: false defaults: run: @@ -56,11 +85,14 @@ jobs: QT_API: ${{ matrix.qt-api }} TVTK_VERBOSE: 'true' VTK_PARSER_VERBOSE: 'true' + PYTHONUNBUFFERED: '1' steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install Linux packages for Qt5/Qt6 support and start Xvfb - uses: pyvista/setup-headless-display-action@main + uses: pyvista/setup-headless-display-action@v3 with: qt: true if: startsWith(matrix.os, 'ubuntu') @@ -69,14 +101,13 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Install dependencies - shell: bash run: | set -exo pipefail python -m pip install --upgrade pip setuptools wheel - python -m pip install --upgrade "${{ matrix.qt-api }}" numpy "${{ matrix.vtk }}" pillow pytest traits traitsui + python -m pip install --upgrade "${{ matrix.qt-api }}" "${{ matrix.numpy || 'numpy' }}" "${{ matrix.vtk }}" pillow pytest pytest-timeout traits traitsui --only-binary="numpy,vtk" - name: Install mayavi and tvtk - run: python -um pip install -ve .[app] + run: python -um pip install --no-build-isolation -ve .[app] - name: Test Mayavi package - run: pytest -v mayavi + run: pytest -v --timeout=10 mayavi - name: Test tvtk package - run: pytest -sv tvtk + run: pytest -sv --timeout=60 tvtk diff --git a/.gitignore b/.gitignore index a57949113..3b903dad4 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ docs/build/ docs/html.zip tvtk/tvtk_classes.zip +tvtk/tvtk_classes/ mayavi/images/m2_about.jpg diff --git a/README.rst b/README.rst index 5d8426703..f6829bd14 100644 --- a/README.rst +++ b/README.rst @@ -86,12 +86,11 @@ By itself Mayavi is not a difficult package to install but its dependencies are unfortunately rather heavy. However, many of these dependencies are now available as wheels on PyPI. The two critical dependencies are, - 1. VTK_ - 2. A GUI toolkit, either PyQt4_, PySide_, PySide2_, PyQt5_ or wxPython_. + 1. VTK_ >= 9.0 + 2. A GUI toolkit, either PySide6_, PyQt6_, PySide2_, PyQt5_, or wxPython_. The latest VTK wheels are available on all the major platforms (Windows, -MacOS, and Linux), but only for 64 bit machines. Python 3.x is fully supported -on all these operating systems and Python 2.7.x on MacOS and Linux. If you are +MacOS, and Linux). If you are out of luck, and your platform is not supported then you will need to install VTK yourself using your particular distribution as discussed in the `General Build and Installation instructions @@ -133,7 +132,7 @@ If you are unable to do this, read the documentation above and find a way to install VTK and a suitable UI toolkit and then repeat the above. If you are interested in the jupyter notebook support as well, do the -following (after ensuring that you have jupyter installed of course. +following (after ensuring that you have jupyter installed of course. **Note, the Jupyter notebook function is only supported starting mayavi version 4.5.0**):: @@ -200,8 +199,8 @@ Test suite The basic test suites for tvtk and mayavi can be run using nose:: - nosetests -v tvtk/tests - nosetests -v mayavi + pytest -v tvtk/tests + pytest -v mayavi The integration tests:: diff --git a/docs/source/mayavi/application.rst b/docs/source/mayavi/application.rst index dc869daec..97ce25ed4 100644 --- a/docs/source/mayavi/application.rst +++ b/docs/source/mayavi/application.rst @@ -94,7 +94,7 @@ In addition, you can save different layouts into different Shown below is a specifically configured Mayavi user interface view. In this view the size of the various parts are changed. -.. image:: images/mayavi_ui_second.jpg +.. image:: images/mayavi_ui_second.png :alt: Figure of Mayavi's UI after being configured by a user. :align: center diff --git a/docs/source/mayavi/auto/example_mlab_interactive_dialog.rst b/docs/source/mayavi/auto/example_mlab_interactive_dialog.rst index 86645a4ed..3be5c2495 100644 --- a/docs/source/mayavi/auto/example_mlab_interactive_dialog.rst +++ b/docs/source/mayavi/auto/example_mlab_interactive_dialog.rst @@ -27,7 +27,7 @@ This example is discussed in details in the section :ref:`embedding_mayavi_traits`. -.. image:: ../images/example_mlab_interactive_dialog.jpg +.. image:: ../images/example_mlab_interactive_dialog.png :align: center diff --git a/docs/source/mayavi/auto/examples.rst b/docs/source/mayavi/auto/examples.rst index 600e41578..bf26f4b57 100644 --- a/docs/source/mayavi/auto/examples.rst +++ b/docs/source/mayavi/auto/examples.rst @@ -363,7 +363,7 @@ applications.
-.. |0106| image:: ../images/example_mlab_interactive_dialog.jpg +.. |0106| image:: ../images/example_mlab_interactive_dialog.png :width: 150 diff --git a/docs/source/mayavi/building_applications.rst b/docs/source/mayavi/building_applications.rst index ec8930b35..5e70fc90e 100644 --- a/docs/source/mayavi/building_applications.rst +++ b/docs/source/mayavi/building_applications.rst @@ -194,7 +194,7 @@ In a dialog, this would be:: This code creates the following dialog: -.. image:: images/example_mlab_interactive_dialog.jpg +.. image:: images/example_mlab_interactive_dialog.png :align: center A complete, runnable, code based on the above comments is given in the diff --git a/docs/source/mayavi/example_exploring_a_vector_field.rst b/docs/source/mayavi/example_exploring_a_vector_field.rst index e06f71ec6..fa9b0d68a 100644 --- a/docs/source/mayavi/example_exploring_a_vector_field.rst +++ b/docs/source/mayavi/example_exploring_a_vector_field.rst @@ -41,7 +41,7 @@ vector field is still too dense, therefore we go to the `Masking` tab to enable masking, mask with an `on ratio` of 6 (one arrow out of 6 is masked) and turn off the random mode. -.. image:: example_vector_cut_plane.jpg +.. image:: example_vector_cut_plane.png :scale: 50 diff --git a/docs/source/mayavi/example_fire.rst b/docs/source/mayavi/example_fire.rst index c692e07a1..d61f4c574 100644 --- a/docs/source/mayavi/example_fire.rst +++ b/docs/source/mayavi/example_fire.rst @@ -115,7 +115,7 @@ As can be seen from the example, it is quite easy to script mayavi to visualize data. An image of a resulting visualization generated from this script is shown below. -.. image:: images/streamline.jpg +.. image:: images/streamline.png :alt: Sample visualization of the ``fire_ug.vtu`` dataset. diff --git a/docs/source/mayavi/example_heart.rst b/docs/source/mayavi/example_heart.rst index 96c900639..432f7b3f1 100644 --- a/docs/source/mayavi/example_heart.rst +++ b/docs/source/mayavi/example_heart.rst @@ -96,7 +96,7 @@ if you'd like to. You should have a visualization that looks something like the one shown below. -.. image:: images/heart.jpg +.. image:: images/heart.png :alt: Sample visualization of the ``heart.vtk`` dataset. The nice thing about mayavi is that although in this case all of the diff --git a/docs/source/mayavi/example_parametric_surface.rst b/docs/source/mayavi/example_parametric_surface.rst index b9b8b9674..3f2ce5328 100644 --- a/docs/source/mayavi/example_parametric_surface.rst +++ b/docs/source/mayavi/example_parametric_surface.rst @@ -87,7 +87,7 @@ source. Note that the positioning of the different surfaces were effected by mo scene via the 'a' key. For more details on this see the section on :ref:`interaction-with-the-scene`. -.. image:: images/param.jpg +.. image:: images/param.png :alt: Sample visualization using parametric surfaces. diff --git a/docs/source/mayavi/example_vector_cut_plane.jpg b/docs/source/mayavi/example_vector_cut_plane.png similarity index 100% rename from docs/source/mayavi/example_vector_cut_plane.jpg rename to docs/source/mayavi/example_vector_cut_plane.png diff --git a/docs/source/mayavi/images/example_mlab_interactive_dialog.jpg b/docs/source/mayavi/images/example_mlab_interactive_dialog.png similarity index 100% rename from docs/source/mayavi/images/example_mlab_interactive_dialog.jpg rename to docs/source/mayavi/images/example_mlab_interactive_dialog.png diff --git a/docs/source/mayavi/images/heart.jpg b/docs/source/mayavi/images/heart.png similarity index 100% rename from docs/source/mayavi/images/heart.jpg rename to docs/source/mayavi/images/heart.png diff --git a/docs/source/mayavi/images/mayavi_ui_second.jpg b/docs/source/mayavi/images/mayavi_ui_second.png similarity index 100% rename from docs/source/mayavi/images/mayavi_ui_second.jpg rename to docs/source/mayavi/images/mayavi_ui_second.png diff --git a/docs/source/mayavi/images/param.jpg b/docs/source/mayavi/images/param.png similarity index 100% rename from docs/source/mayavi/images/param.jpg rename to docs/source/mayavi/images/param.png diff --git a/docs/source/mayavi/images/streamline.jpg b/docs/source/mayavi/images/streamline.png similarity index 100% rename from docs/source/mayavi/images/streamline.jpg rename to docs/source/mayavi/images/streamline.png diff --git a/docs/source/tvtk/README.txt b/docs/source/tvtk/README.txt index 8d71456de..0aa39ee53 100644 --- a/docs/source/tvtk/README.txt +++ b/docs/source/tvtk/README.txt @@ -260,19 +260,17 @@ tvtk wrapper object is created. The following illustrates this:: >>> cs = tvtk.ConeSource() >>> o = cs.output >>> m = tvtk.PolyDataMapper() - >>> m.input = o - >>> print(hash(o)) - 1109012188 - >>> print(hash(m.input)) - 1109012188 + >>> m.input_connection = cs.output_port + >>> print(id(o)) + 126526931186080 + >>> print(id(m.input)) + 126526931186080 >>> del o - >>> print(hash(m.input)) - 1119694156 + >>> print(id(m.input)) + 126526931186080 Thus, after `o` is garbage collected `m.input` no longer refers to the -original tvtk object and a new one is created. This is very similar -to VTK's behaviour. Changing this behaviour is tricky and there are no -plans currently to change this. +original tvtk object the old one is cached and returned. tvtk and traits diff --git a/mayavi/__init__.py b/mayavi/__init__.py index 8e89a722c..27c028128 100644 --- a/mayavi/__init__.py +++ b/mayavi/__init__.py @@ -9,6 +9,7 @@ __requires__ = [ 'apptools', + 'configobj', 'envisage', 'numpy', 'pyface>=6.1.1', @@ -16,6 +17,7 @@ 'traits>=6.0.0', 'traitsui>=7.0.0', 'packaging', + 'importlib_resources; python_version<"3.11"', 'vtk' ] diff --git a/mayavi/components/actor.py b/mayavi/components/actor.py index 97bc06327..6fc43c8fb 100644 --- a/mayavi/components/actor.py +++ b/mayavi/components/actor.py @@ -115,14 +115,7 @@ def update_data(self): sends a `data_changed` event. """ # Invoke render to update any changes. - from mayavi.modules.outline import Outline - from mayavi.components.glyph import Glyph - #FIXME: A bad hack, but without these checks results in seg fault - input = self.inputs[0] - if isinstance(input, Outline) or isinstance(input, Glyph): - self.mapper.update(0) - else: - self.mapper.update() + self.mapper.update() self.render() ###################################################################### diff --git a/mayavi/components/ui/actor.py b/mayavi/components/ui/actor.py index d58386749..7f0c6899f 100644 --- a/mayavi/components/ui/actor.py +++ b/mayavi/components/ui/actor.py @@ -14,8 +14,6 @@ from traitsui.api import (View, Group, Item, InstanceEditor, DropEditor, Tabbed) -from tvtk.api import tvtk -from tvtk.common import vtk_major_version # The properties view group. _prop_base_group = Group(Item(name='representation'), @@ -48,16 +46,10 @@ ) # The Texture's view group -if vtk_major_version > 7: - _texture_group = Group(Item(name='interpolate'), - Item(name='color_mode'), - Item(name='repeat'), - show_border=True) -else: - _texture_group = Group(Item(name='interpolate'), - Item(name='map_color_scalars_through_lookup_table'), - Item(name='repeat'), - show_border=True) +_texture_group = Group(Item(name='interpolate'), + Item(name='color_mode'), + Item(name='repeat'), + show_border=True) # The Actor's view group. _actor_base_group = Group(Item(name='visibility')) diff --git a/mayavi/preferences/preference_manager.py b/mayavi/preferences/preference_manager.py index efe68ef9e..bf27a046d 100644 --- a/mayavi/preferences/preference_manager.py +++ b/mayavi/preferences/preference_manager.py @@ -21,7 +21,7 @@ # Standard library imports from os.path import join -import pkg_resources +import importlib.resources # Enthought library imports. from traits.etsconfig.api import ETSConfig @@ -106,12 +106,11 @@ def _load_preferences(self): for pkg in ('mayavi.preferences', 'tvtk.plugins.scene'): pref = 'preferences.ini' - pref_file = pkg_resources.resource_stream(pkg, pref) - + pref_file = importlib.resources.files(pkg).joinpath(pref) preferences = self.preferences default = preferences.node('default/') - default.load(pref_file) - pref_file.close() + with open(pref_file, 'rb') as fid: + default.load(fid) finally: # Set back the application home. ETSConfig.application_home = app_home @@ -126,4 +125,3 @@ def _preferences_changed(self, preferences): # A Global preference manager that all other modules can use. preference_manager = PreferenceManager() - diff --git a/mayavi/tests/test_builtin_image.py b/mayavi/tests/test_builtin_image.py index 0af5a6eca..0ba015076 100644 --- a/mayavi/tests/test_builtin_image.py +++ b/mayavi/tests/test_builtin_image.py @@ -11,7 +11,6 @@ from numpy import array # Enthought library imports. -from tvtk.common import vtk_major_version from mayavi.core.null_engine import NullEngine from mayavi.sources.builtin_image import BuiltinImage from mayavi.modules.surface import Surface @@ -40,8 +39,6 @@ def setUp(self): image_data.data_source.radius = array([80., 80., 80.]) image_data.data_source.center = array([150., 150., 0.]) image_data.data_source.whole_extent = array([10, 245, 10, 245, 0, 0]) - if vtk_major_version < 8: - image_data.data_source.set_update_extent_to_whole_extent() self.e = e self.scene = e.current_scene diff --git a/mayavi/tests/test_csv_sniff.py b/mayavi/tests/test_csv_sniff.py index 30e2802d2..f50a573fe 100644 --- a/mayavi/tests/test_csv_sniff.py +++ b/mayavi/tests/test_csv_sniff.py @@ -12,7 +12,7 @@ import tempfile from unittest import SkipTest -from numpy import array, ndarray +from numpy import array, ndarray, isnan from mayavi.tools.data_wizards.csv_sniff import \ Sniff, loadtxt, loadtxt_unknown, array2dict @@ -33,8 +33,8 @@ def assertAllClose(self, x, y): def assertClose(self, a, b): if isinstance(a, (int, float)): - if repr(a) == 'nan': - self.assertTrue(repr(b) == 'nan') + if isnan(a): + self.assertTrue(isnan(b), '%r != %r' % (a ,b)) else: self.assertTrue(abs(a - b) < 1e-6 * max(1, abs(a)), '%r != %r %r' % (a, b, abs(a - b))) diff --git a/mayavi/tests/test_mlab_source.py b/mayavi/tests/test_mlab_source.py index 49c6e87c2..ca574e1dd 100644 --- a/mayavi/tests/test_mlab_source.py +++ b/mayavi/tests/test_mlab_source.py @@ -171,7 +171,7 @@ def test_set(self): self.check_traits() self.check_dataset() - def test_strange_shape(self): + def test_basic_strange_shape(self): " Test the MGlyphSource with strange shapes for the arguments " x, y, z, v, s, src = self.get_data() x = y = z = v = s = 0 diff --git a/mayavi/tools/data_wizards/loadtxt.py b/mayavi/tools/data_wizards/loadtxt.py index 80702b0e0..b4677dee8 100644 --- a/mayavi/tools/data_wizards/loadtxt.py +++ b/mayavi/tools/data_wizards/loadtxt.py @@ -23,7 +23,7 @@ def _getconv(dtype): return lambda x: int(float(x)) elif issubclass(typ, np.floating): return float - elif issubclass(typ, np.complex_): + elif issubclass(typ, np.complexfloating): return complex else: return str diff --git a/mayavi/tools/figure.py b/mayavi/tools/figure.py index a196f1fd9..8890b470c 100644 --- a/mayavi/tools/figure.py +++ b/mayavi/tools/figure.py @@ -19,7 +19,6 @@ # imports from tvtk.api import tvtk -from tvtk.common import vtk_major_version from mayavi.core.scene import Scene from mayavi.core.registry import registry from .camera import view @@ -313,19 +312,13 @@ def screenshot(figure=None, mode='rgb', antialiased=False): out = tvtk.UnsignedCharArray() shape = (y, x, 3) pixel_getter = figure.scene.render_window.get_pixel_data - if vtk_major_version > 7: - pg_args = (0, 0, x - 1, y - 1, 1, out, 0) - else: - pg_args = (0, 0, x - 1, y - 1, 1, out) + pg_args = (0, 0, x - 1, y - 1, 1, out, 0) elif mode == 'rgba': out = tvtk.FloatArray() shape = (y, x, 4) pixel_getter = figure.scene.render_window.get_rgba_pixel_data - if vtk_major_version > 7: - pg_args = (0, 0, x - 1, y - 1, 1, out, 0) - else: - pg_args = (0, 0, x - 1, y - 1, 1, out) + pg_args = (0, 0, x - 1, y - 1, 1, out, 0) else: raise ValueError('mode type not understood') diff --git a/pyproject.toml b/pyproject.toml index 9da1141ed..d6cad3fee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,6 +2,6 @@ requires = [ "numpy>=2.0.0rc2,<3", "setuptools", - "vtk", + "vtk>=9", "wheel" ] diff --git a/setup.cfg b/setup.cfg index 212a94acb..699717f82 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,5 +4,7 @@ addopts = filterwarnings = # Currently unsatisfiable ignore:Workbench will be moved from pyface:PendingDeprecationWarning + # Should be fixed in traits + ignore: module 'sre_.+' is deprecated:DeprecationWarning # We call deprecated methods and classes in our tests, and there are many variants for how the parentheticals are formatted ignore:Call to deprecated .*. \((This|Use|Please|Deprecated|Deprecating|Removed|Part|no|renamed) .*\) -- Deprecated since version.*:DeprecationWarning diff --git a/tvtk/array_handler.py b/tvtk/array_handler.py index 6f42b2e89..1e1a1db68 100644 --- a/tvtk/array_handler.py +++ b/tvtk/array_handler.py @@ -197,9 +197,9 @@ def get_vtk_array_type(numeric_array_type): numpy.dtype(ULONG_TYPE_CODE): vtkConstants.VTK_UNSIGNED_LONG, numpy.dtype(LONG_TYPE_CODE): vtkConstants.VTK_LONG, } - for t in _extra: + for t, val in _extra.items(): if t not in _arr_vtk: - _arr_vtk[t] = _extra[t] + _arr_vtk[t] = val try: return _arr_vtk[numeric_array_type] diff --git a/tvtk/code_gen.py b/tvtk/code_gen.py index 8ad3ab6a9..3f770291c 100644 --- a/tvtk/code_gen.py +++ b/tvtk/code_gen.py @@ -1,13 +1,18 @@ """This module generates tvtk (Traited VTK) classes from the VTK-Python API. -This can be evoked for example by: +This can be evoked for example by running from the ``mayavi`` root: ..code-block:: console - $ python -ic "from tvtk.code_gen import main; main()" -szv + $ VTK_PARSER_VERBOSE=1 python -m tvtk.code_gen -szvno $PWD/tvtk On failures you can then for example do ``import pdb; pdb.pm()`` to do -post-mortem debugging. +post-mortem debugging. If there are segfaults, the VTK_PARSER_VERBOSE=1 should help +point to the culprit, which often needs to be worked around in +``vtk_parser.py::VTKMethodParser._find_get_set_methods``. + +Exceptions to behaviors based on VTK versions and bugs etc. live in ``wrapper_gen.py`` +and ``tvtk_parser.py``. """ # Author: Prabhu Ramachandran # Copyright (c) 2004-2020, Enthought, Inc. @@ -107,7 +112,7 @@ def generate_code(self): # Write the wrapper files. tree = wrap_gen.get_tree().tree - classes = [] + classes = ['vtkObjectBase'] # This is another class we should not wrap and exists # in version 8.1.0. ignore = ['vtkOpenGLGL2PSHelperImpl'] + [ @@ -121,11 +126,12 @@ def generate_code(self): if (name not in include and not name.startswith('vtk')) or \ name.startswith('vtkQt'): continue - if not hasattr(vtk, name) or not hasattr(getattr(vtk, name), 'IsA'): # noqa + if not hasattr(vtk, name) or \ + not hasattr(getattr(vtk, name), 'AddObserver'): # noqa # We need to wrap VTK classes that are derived # from vtkObjectBase, the others are # straightforward VTK classes that can be used as - # such. All of these have an 'IsA' method so we + # such. All of these have an 'AddObserver' method so we # check for that. Only the vtkObjectBase # subclasses support observers etc. and hence only # those make sense to wrap into TVTK. diff --git a/tvtk/common.py b/tvtk/common.py index bad08ce2c..0b6a618d1 100644 --- a/tvtk/common.py +++ b/tvtk/common.py @@ -5,7 +5,6 @@ # License: BSD Style. from contextlib import contextmanager -import string import re import vtk @@ -67,10 +66,6 @@ def _sanitize_name(name): return name -def is_version_9(): - return vtk_major_version > 8 - - def configure_connection(obj, inp): """ Configure topology for vtk pipeline obj.""" if hasattr(inp, 'output_port'): diff --git a/tvtk/messenger.py b/tvtk/messenger.py index a11f84722..63f495251 100644 --- a/tvtk/messenger.py +++ b/tvtk/messenger.py @@ -145,7 +145,7 @@ def connect(self, obj, event, callback): """ typ = type(callback) - key = hash(obj) + key = id(obj) if not key in self._signals: self._signals[key] = {} signals = self._signals[key] @@ -200,7 +200,7 @@ def disconnect(self, obj, event=None, callback=None, obj_is_hash=False): if obj_is_hash: key = obj else: - key = hash(obj) + key = id(obj) if not key in signals: return if callback is None: @@ -282,11 +282,11 @@ def _get_signals(self, obj): object. """ - ret = self._signals.get(hash(obj)) + ret = self._signals.get(id(obj)) if ret is None: raise MessengerError( - "No such object: %s, has registered itself "\ - "with the messenger."%obj + "No such object: %s, has registered itself " + "with the messenger." % obj ) else: return ret diff --git a/tvtk/pipeline/browser.py b/tvtk/pipeline/browser.py index d2afb5b4f..5c8c8a425 100644 --- a/tvtk/pipeline/browser.py +++ b/tvtk/pipeline/browser.py @@ -447,7 +447,7 @@ class TVTKLeafNode(TreeNodeObject): __ = Python def __hash__(self): - return hash(tvtk.to_vtk(self.object)) + return id(tvtk.to_vtk(self.object)) def _get_name(self): return self.object.__class__.__name__ @@ -496,7 +496,7 @@ def __del__(self): pass def __hash__(self): - return hash(tvtk.to_vtk(self.object)) + return id(tvtk.to_vtk(self.object)) def _get_children_from_cache(self): return [x for x in self.children_cache.values() if x is not None] diff --git a/tvtk/tests/test_array_ext.py b/tvtk/tests/test_array_ext.py index cbb03c743..4320267e8 100644 --- a/tvtk/tests/test_array_ext.py +++ b/tvtk/tests/test_array_ext.py @@ -5,12 +5,16 @@ # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. +import pytest import unittest import numpy from tvtk.array_handler import ID_TYPE_CODE, set_id_type_array_py -from tvtk.array_ext import set_id_type_array +try: + from tvtk.array_ext import set_id_type_array +except ModuleNotFoundError: # not compiled + pytest.skip("array_ext not found", allow_module_level=True) class TestArrayExt(unittest.TestCase): diff --git a/tvtk/tests/test_array_handler.py b/tvtk/tests/test_array_handler.py index 556ad0483..bc4588dec 100644 --- a/tvtk/tests/test_array_handler.py +++ b/tvtk/tests/test_array_handler.py @@ -29,6 +29,7 @@ def mysum(arr): class TestArrayHandler(unittest.TestCase): def _check_arrays(self, arr, vtk_arr): self.assertEqual(vtk_arr.GetNumberOfTuples(), len(arr)) + msg = f"\n{vtk_arr}" if len(arr.shape) == 2: dim1 = arr.shape[1] self.assertEqual(vtk_arr.GetNumberOfComponents(), dim1) @@ -45,8 +46,7 @@ def _check_arrays(self, arr, vtk_arr): self.assertEqual(chr(int(vtk_arr.GetTuple1(i))), arr[i]) else: for i in range(len(arr)): - self.assertEqual(vtk_arr.GetTuple1(i), arr[i]) - + self.assertEqual(vtk_arr.GetTuple1(i), arr[i], msg=msg) def test_array2vtk(self): """Test Numeric array to VTK array conversion and vice-versa.""" @@ -63,6 +63,7 @@ def test_array2vtk(self): t_z.append(numpy.array([-2147483648, 0, 2147483647], numpy.int32)) t_z.append(numpy.array([ -9223372036854775808, 0, 9223372036854775807], numpy.int64)) + assert t_z[-1][0] == -9223372036854775808 t_z.append(numpy.array([0, 255], numpy.uint8)) t_z.append(numpy.array([0, 65535], numpy.uint16)) t_z.append(numpy.array([0, 4294967295], numpy.uint32)) @@ -160,13 +161,16 @@ def test_array2vtk(self): self.assertEqual(vtk_arr.GetValue(2), 0) self.assertEqual(vtk_arr.GetValue(3), 1) - # Make sure the code at least runs for all the non-complex - # numerical dtypes in numpy. - float_types = [x for x in numpy.sctypes['float'] - if x().dtype.name not in ('float16', 'float128')] - for dtype in (numpy.sctypes['int'] + numpy.sctypes['uint'] + - float_types): - array_handler.array2vtk(numpy.zeros((1,), dtype=dtype)) + # Make sure the code at least runs for all + # numerical dtypes in numpy + # except for half, longdouble and complexfloating + int_types = ['byte', 'short', 'int', 'intc', 'int_', 'long', 'longlong'] + uint_types = ['ubyte', 'ushort', 'uintc', 'uint', 'ulong', + 'ulonglong'] + float_types = ['single', 'double'] + for dtype in int_types + uint_types + float_types: + array_handler.array2vtk(numpy.zeros((1,), + dtype=numpy.dtype(dtype))) def test_arr2cell_array(self): """Test Numeric array to vtkCellArray conversion.""" @@ -201,8 +205,7 @@ def test_arr2cell_array(self): cells = array_handler.array2vtkCellArray(a) arr = array_handler.vtk2array(cells.GetData()) expect = numpy.array([3, 0, 1, 2]*3, int) - self.assertEqual(numpy.alltrue(numpy.equal(arr, expect)), - True) + self.assertTrue(numpy.all(numpy.equal(arr, expect))) self.assertEqual(cells.GetNumberOfCells(), N) # Test if a list of Numeric arrays of different cell lengths works. @@ -210,8 +213,7 @@ def test_arr2cell_array(self): cells = array_handler.array2vtkCellArray(l_a) arr = array_handler.vtk2array(cells.GetData()) expect = numpy.array([1, 0]*3 + [3, 0, 1, 2]*3 + [2, 0,1]*2, int) - self.assertEqual(numpy.alltrue(numpy.equal(arr, expect)), - True) + self.assertTrue(numpy.all(numpy.equal(arr, expect))) self.assertEqual(cells.GetNumberOfCells(), N*2 + 2) # This should not take a long while. This merely tests if a diff --git a/tvtk/tests/test_class_tree.py b/tvtk/tests/test_class_tree.py index f71e43bf2..5d9ad976f 100644 --- a/tvtk/tests/test_class_tree.py +++ b/tvtk/tests/test_class_tree.py @@ -7,7 +7,6 @@ """ import builtins -import sys import unittest from contextlib import contextmanager @@ -19,8 +18,6 @@ _cache = class_tree.ClassTree(vtk) _cache.create() -vtk_major_version = vtk.vtkVersion.GetVTKMajorVersion() - def get_level(klass): """Gets the inheritance level of a given class.""" if not klass.__bases__: @@ -54,15 +51,7 @@ def test_basic_vtk(self): if (hasattr(vtk, 'vtkTuple')): names = [x.name for x in t.tree[0]] names.sort() - if vtk_major_version == 7: - expect = ['object', 'vtkColor3', 'vtkColor4', 'vtkDenseArray', - 'vtkQuaternion', 'vtkRect', - 'vtkSparseArray', 'vtkTuple', - 'vtkTypedArray','vtkVector', - 'vtkVector2', 'vtkVector3'] - else: - self.assertGreaterEqual(vtk_major_version, 8) - expect = ['object'] + expect = ['object'] self.assertEqual(names, expect) elif (hasattr(vtk, 'vtkVector')): self.assertEqual(len(t.tree[0]), 11) diff --git a/tvtk/tests/test_messenger.py b/tvtk/tests/test_messenger.py index 66d497953..a92f2344a 100644 --- a/tvtk/tests/test_messenger.py +++ b/tvtk/tests/test_messenger.py @@ -133,12 +133,12 @@ def foo(self, o, e): # Test if things behave sanely if a message was sent and one # of the callbacks has been gc'd. m = messenger.Messenger() - l1 = len(m._signals[hash(c1)]['foo']) + l1 = len(m._signals[id(c1)]['foo']) # del c messenger.send(c1, 'foo') # - l2 = len(m._signals[hash(c1)]['foo']) + l2 = len(m._signals[id(c1)]['foo']) # Since 'c' is gc'd this callback should have been cleared # out. self.assertEqual(l2, l1 - 1) diff --git a/tvtk/tests/test_tvtk.py b/tvtk/tests/test_tvtk.py index 3cba92c56..8fe7f5b60 100644 --- a/tvtk/tests/test_tvtk.py +++ b/tvtk/tests/test_tvtk.py @@ -8,6 +8,7 @@ # Copyright (c) 2004-2020, Enthought, Inc. # License: BSD Style. +import os import unittest import pickle import weakref @@ -43,6 +44,9 @@ from tvtk.tvtk_classes import tvtk_helper +on_gha = os.getenv("GITHUB_ACTION", None) is not None + + def mysum(arr): val = arr while type(val) == numpy.ndarray: @@ -202,22 +206,22 @@ def test_help_trait(self): def test_object_cache(self): """Test if object cache works.""" cs = tvtk.ConeSource() - hash1 = hash(cs) + hash1 = id(cs) o = cs.output if hasattr(o, 'producer_port'): src = o.producer_port.producer else: src = cs.executive.algorithm self.assertEqual(src, cs) - self.assertEqual(hash1, hash(src)) + self.assertEqual(hash1, id(src)) del cs, src gc.collect() # The test sometimes fails as VTK seems to generate objects with the - # same memory address and hash, we try to force it to allocate more - # objects so as to not end up reusing the same address and hash. + # same memory address and hash/id, we try to force it to allocate more + # objects so as to not end up reusing the same address and id. junk = [tvtk.ConeSource() for i in range(50)] - # Now get another ConeSource and ensure the hash is different. + # Now get another ConeSource and ensure the id is different. cs = tvtk.ConeSource() o = cs.output if hasattr(o, 'producer_port'): @@ -230,8 +234,8 @@ def test_object_cache(self): # For VTK 5.x this test is inconsistent, hence skipeed for 5.x # See http://review.source.kitware.com/#/c/15095/ ############################################################## - self.assertEqual(hash1 != hash(src), True) - self.assertEqual(hash(cs), hash(src)) + self.assertEqual(hash1 != id(src), True) + self.assertEqual(id(cs), id(src)) # Test for a bug with collections and the object cache. r = tvtk.Renderer() @@ -565,7 +569,7 @@ def test_information_keys(self): s = tvtk.StructuredPoints() x = s.FIELD_ARRAY_TYPE() y = tvtk.Information() - x.get(y) + y.get(x) def test_parent_child_bounds(self): """CubeAxesActor2D's bounds should be writable.""" @@ -818,14 +822,20 @@ def setUpClass(cls): def test_all_instantiable(self): """Test if all the TVTK classes can be instantiated""" errors = [] + if on_gha: + print("\n::group::Instantiating TVTK classes") for name in self.names: tvtk_name = get_tvtk_name(name) tvtk_klass = getattr(tvtk, tvtk_name, None) + if on_gha: + print(tvtk_name) try: - obj = tvtk_klass() + tvtk_klass() # TypeError: super(type, obj): obj must be an instance or subtype of type except (TraitError, KeyError, TypeError): errors.append(f"\n{name}:\n{indent(traceback.format_exc(), ' ')}") + if on_gha: + print("\n::endgroup::") if len(errors) > 0: message = "Not all classes could be instantiated:\n{0}\n" raise AssertionError(message.format(''.join(errors))) @@ -855,15 +865,27 @@ def get_min_max_value(vtk_klass, vtk_attr_name): except AttributeError: return None, None + if on_gha: + print("\n::group::TVTK trait ranges") for name in self.names: vtk_klass = getattr(vtk, name) tvtk_klass_name = get_tvtk_name(name) + if vtk.vtk_version == '9.4.0': + if tvtk_klass_name.endswith('View'): + continue + if tvtk_klass_name in ['ImageViewer', 'ImageViewer2', + 'OpenGLRenderWindow', + 'RenderWindow']: + continue + + if on_gha: + print(tvtk_klass_name) try: obj = getattr(tvtk, tvtk_klass_name)() except Exception: # testing for instantiation is above - pass + continue for trait_name in obj.editable_traits(): if trait_name in ['_in_set', '_vtk_obj']: @@ -882,13 +904,14 @@ def get_min_max_value(vtk_klass, vtk_attr_name): # tvtk.tvtk_classes.open_gl_cell_grid_render_request.shapes_to_draw # uses strings if isinstance(min_value, str): - name = "tvtk.tvtk_classes.open_gl_cell_grid_render_request" - assert name in repr(obj), (obj, trait_name) + assert 'cell_grid_render_request' in repr(obj), (obj, trait_name) continue with self.assertRaises(TraitError): setattr(obj, trait_name, (min_value-1, max_value)) with self.assertRaises(TraitError): setattr(obj, trait_name, (min_value, max_value+1)) + if on_gha: + print("::endgroup::") def test_no_trait_has_ptr_address_as_value(self): '''Test if none of the TVTK classes' traits has a value of "*_p_void" @@ -933,7 +956,7 @@ def test_all_traits_can_be_obtained(self): obj = getattr(tvtk, tvtk_klass_name)() except Exception: # testing for instantiation is above - pass + continue for trait_name in obj._full_traitnames_list_: try: diff --git a/tvtk/tests/test_vtk_parser.py b/tvtk/tests/test_vtk_parser.py index c97635f58..ce88ce9e8 100644 --- a/tvtk/tests/test_vtk_parser.py +++ b/tvtk/tests/test_vtk_parser.py @@ -16,6 +16,7 @@ import unittest from tvtk import vtk_parser from tvtk import vtk_module as vtk +from tvtk.common import vtk_major_version, vtk_minor_version import time # Only used when timing. import sys # Only used when debugging. @@ -23,8 +24,7 @@ # This is a little expensive to create so we cache it. _cache = vtk_parser.VTKMethodParser() -vtk_major_version = vtk.vtkVersion.GetVTKMajorVersion() -vtk_minor_version = vtk.vtkVersion.GetVTKMinorVersion() + class TestVTKParser(unittest.TestCase): def setUp(self): @@ -44,13 +44,9 @@ def test_parse(self): p = self.p # Simple case of a vtkObject. p.parse(vtk.vtkObject()) - if vtk_major_version < 9: - self.assertEqual(p.get_toggle_methods(), - {'Debug': 0, 'GlobalWarningDisplay': 1}) - else: - self.assertIn(p.get_toggle_methods(), - [{'Debug': False, 'GlobalWarningDisplay': 1}, - {'Debug': False, 'GlobalWarningDisplay': 0}]) + self.assertIn(p.get_toggle_methods(), + [{'Debug': False, 'GlobalWarningDisplay': 1}, + {'Debug': False, 'GlobalWarningDisplay': 0}]) self.assertEqual(p.get_state_methods(), {}) self.assertEqual(p.get_get_methods(), ['GetCommand', 'GetMTime']) @@ -87,8 +83,7 @@ def test_parse(self): ['Gouraud', 1], ['Phong', 2]], 'Representation': [['Surface', 2], ['Points', 0], ['Surface', 2], ['Wireframe', 1]]} - if vtk_major_version >= 9: - res['Interpolation'].insert(-1, ['PBR', 3]) + res['Interpolation'].insert(-1, ['PBR', 3]) self.assertEqual(p.get_state_methods(), res) self.assertEqual(p.state_meths, p.get_state_methods()) @@ -118,15 +113,13 @@ def test_parse(self): 'SpecularPower': (1.0, (0.0, 100.0))} if ('ReferenceCount' not in p.get_get_set_methods()): del res['ReferenceCount'] - if vtk_major_version > 7: - res['MaterialName'] = (None, None) - res['VertexColor'] = ((0.5, 1.0, 0.5), None) - if vtk_major_version >= 9: - res['EmissiveFactor'] = ((1.0, 1.0, 1.0), None) - res['Metallic'] = (0., float_max) - res['NormalScale'] = (1., None) - res['OcclusionStrength'] = (1., float_max) - res['Roughness'] = (0.5, float_max) + res['MaterialName'] = (None, None) + res['VertexColor'] = ((0.5, 1.0, 0.5), None) + res['EmissiveFactor'] = ((1.0, 1.0, 1.0), None) + res['Metallic'] = (0., float_max) + res['NormalScale'] = (1., None) + res['OcclusionStrength'] = (1., float_max) + res['Roughness'] = (0.5, float_max) if (vtk_major_version, vtk_minor_version) >= (9, 1): res['Anisotropy'] = (0.0, (0.0, 1.0)) res['AnisotropyRotation'] = (0.0, (0.0, 1.0)) @@ -180,12 +173,8 @@ def test_parse(self): res = ['AddShaderVariable', 'BackfaceRender', 'DeepCopy', 'ReleaseGraphicsResources', 'RemoveAllTextures', 'RemoveTexture', 'Render'] - if (vtk_major_version >= 7 or vtk_minor_version >= 2) and \ - vtk_major_version < 9: - res.append('VTKTextureUnit') - if vtk_major_version == 9: - res.extend(['SetBaseColorTexture', 'SetEmissiveTexture', - 'SetNormalTexture', 'SetORMTexture']) + res.extend(['SetBaseColorTexture', 'SetEmissiveTexture', + 'SetNormalTexture', 'SetORMTexture']) if vtk_major_version == 9 and vtk_minor_version > 0: res.extend([ 'ComputeIORFromReflectance', 'ComputeReflectanceFromIOR', diff --git a/tvtk/vtk_module.py b/tvtk/vtk_module.py index 69dad0eb1..36aaa5249 100644 --- a/tvtk/vtk_module.py +++ b/tvtk/vtk_module.py @@ -52,4 +52,12 @@ try: del vtkDGBoundsResponder, vtkDGOpenGLRenderer, vtkDGSidesResponder except NameError: - pass \ No newline at end of file + pass + +if vtk_version == '9.4.0': + # Instantiating these using TVTK causes a crash on VTK 9.4.0 so skipping. + SKIP = ['vtkIOSSReader', 'vtkIOSSCellGridReader'] + try: + del vtkIOSSReader, vtkIOSSCellGridReader + except NameError: + pass diff --git a/tvtk/vtk_parser.py b/tvtk/vtk_parser.py index 031af8f33..b14b197d6 100644 --- a/tvtk/vtk_parser.py +++ b/tvtk/vtk_parser.py @@ -8,13 +8,12 @@ import collections.abc import re -import types import os # Local imports (these are relative imports for a good reason). from . import class_tree from . import vtk_module as vtk -from .common import is_version_9 +from .common import vtk_major_version, vtk_minor_version class VTKMethodParser: @@ -632,18 +631,6 @@ def _find_get_set_methods(self, klass, methods): # vtkProp.Get/SetAllocatedRenderTime is private and # SetAllocatedRenderTime takes two args, don't wrap it. continue - elif (not is_version_9()) and ( - (klass_name == 'vtkGenericAttributeCollection' and - method[3:] == 'AttributesToInterpolate') or - (klass_name == 'vtkOverlappingAMR' and - method[3:] == 'Origin') or - (klass_name == 'vtkOrientationMarkerWidget' and - method[3:] in ['OutlineColor', 'Viewport']) or - (klass_name == 'vtkImageDataGeometryFilter' and - method[3:] == 'Extent') or - (klass_name == 'vtkVolumeMapper' and - method[3:] == 'CroppingRegionPlanes')): - continue elif (klass_name == 'vtkContextMouseEvent' and method[3:] == 'Interactor'): continue @@ -668,54 +655,46 @@ def _find_get_set_methods(self, klass, methods): # Find the default and range of the values. if gsm: - if self._verbose: - print(f'Instantiating {klass}') obj = self._get_instance(klass) - # print('got instance', obj.__class__) if obj: for key, value in gsm.items(): - if not is_version_9() and ( - # Evil hack, these classes segfault! - (klass_name in ['vtkPolyData', 'vtkContext2D']) or - # On VTK 8.1.0 this segfaults when uninitialized. - (klass_name == 'vtkContextMouseEvent' and - key == 'Interactor')): + # Broken in <= 9.3 + if ( + (vtk_major_version, vtk_minor_version) <= (9, 3) + and f"{klass_name}.Get{key}" in ( + "vtkGenericAttributeCollection.GetAttributesToInterpolate", + "vtkPlotBar.GetLookupTable", + "vtkLagrangianParticleTracker.GetIntegrationModel", + ) + ): + default = None + # Broken in <= 9.4 + # https://gitlab.kitware.com/vtk/vtk/-/merge_requests/6729#note_732848 + elif ( + (vtk_major_version, vtk_minor_version) <= (9, 4) + and f"{klass_name}.Get{key}" in ( + "vtkHigherOrderTetra.GetParametricCoords", + ) + ): default = None - elif not is_version_9() and ( - klass_name == 'vtkHyperOctree' and - key == 'Dimension'): - # This class breaks standard VTK conventions. - gsm[key] = (3, (1, 3)) - continue - # On VTK 9.0.0 vtkHigherOrderTetra.GetParametricCorods - # segfauts when uninitialized, see: - # - # https://gitlab.kitware.com/vtk/vtk/-/merge_requests/6729#note_732848 # noqa: E501 - # - # vtkGenericAttributeCollection.GetAttributesToInterpolate - # might only be a problem if VTK is built in debug mode, - # but let's keep it just to be safe. - elif is_version_9() and ( - (klass_name == 'vtkHigherOrderTetra' and - key == 'ParametricCoords') or - (klass_name == 'vtkGenericAttributeCollection' and - key == 'AttributesToInterpolate') or - (klass_name == 'vtkPlotBar' and - key == 'LookupTable') or - (klass_name == 'vtkLagrangianParticleTracker' and - key == 'IntegrationModel') or - False): # just to simplify indentation/updates + # Broken in 9.4 + elif ( + (vtk_major_version, vtk_minor_version) == (9, 4) + and f"{klass_name}.Get{key}" in ( + "vtkGenericCell.GetCellFaces", + ) + ): default = None else: try: if self._verbose: - print(f' Calling {klass_name}.Get{key}()') - default = getattr(obj, 'Get%s' % key)() + print(f" Calling {klass_name}.Get{key}()") + default = getattr(obj, f"Get{key}")() except TypeError: default = None # If we don't turn these into integers, they won't instantiate - if is_version_9(): + if vtk_major_version == 9: if klass_name == "vtkAxisActor": if key in ( "AxisOnOrigin", "Use2DMode", "UseTextActor3D", @@ -765,7 +744,7 @@ def _find_get_methods(self, klass, methods): meths.remove(method) return meths - def _get_instance(self, klass): + def _get_instance(self, klass, *, do_print=True): """Given a VTK class, `klass`, returns an instance of the class. @@ -774,6 +753,8 @@ def _get_instance(self, klass): the 'state' methods and the ranges for the Get/Set methods. """ + if self._verbose and do_print: + print(f'Instantiating {klass}') obj = None try: obj = klass() @@ -783,7 +764,8 @@ def _get_instance(self, klass): n = t.get_node(klass.__name__) if n is not None: for c in n.children: - obj = self._get_instance(t.get_class(c.name)) + obj = self._get_instance(t.get_class(c.name), do_print=False) if obj: + print(f" Using super {t.get_class(c.name)} instead of {klass}") break return obj diff --git a/tvtk/wrapper_gen.py b/tvtk/wrapper_gen.py index 1cf9f8b28..1c3cb0d25 100644 --- a/tvtk/wrapper_gen.py +++ b/tvtk/wrapper_gen.py @@ -17,7 +17,8 @@ # Local imports (these are relative imports because the package is not # installed when these modules are imported). -from .common import get_tvtk_name, camel2enthought, vtk_major_version, _sanitize_name +from .common import get_tvtk_name, camel2enthought, _sanitize_name +from .common import vtk_major_version, vtk_minor_version from . import vtk_parser from . import indenter @@ -54,7 +55,7 @@ def get_trait_def(value, **kwargs): Example ------- >>> get_trait_def([100., 200.], enter_set=True, auto_set=False) - ('traits.Array', '', 'auto_set=False, enter_set=True, shape=(2,), dtype=float, value=[100.0, 200.0], cols=2') + ('traits.Array', '', 'auto_set=False, enter_set=True, shape=(None,), dtype=float, value=[100.0, 200.0], cols=2') >>> get_trait_def(100, enter_set=True, auto_set=False) ('traits.Int', '100', 'auto_set=False, enter_set=True') >>> get_trait_def(u'something', enter_set=True, auto_set=False) @@ -80,7 +81,7 @@ def get_trait_def(value, **kwargs): return 'traits.String', '{!r}'.format(value), kwargs_code elif type_ in (tuple, list): - shape = (len(value),) + shape = (None,) dtypes = set(type(element) for element in value) dtype = dtypes.pop().__name__ if len(dtypes) == 1 else None if dtype == 'int' and sys.platform.startswith('win'): @@ -679,6 +680,10 @@ def _gen_state_methods(self, klass, out): if not vtk_val: default = self._reform_name(meths[m][0][0]) + # Weirdness on NumPy 2.1 and vtk >= 9.3 that this does not show up as + # an option and creates problems + if klass.__name__ == "vtkPoints" and m == "DataType" and sys.platform == "win32": + d["int32"] = vtk.VTK_ID_TYPE if extra_val is None: t_def = """tvtk_base.RevPrefixMap(%(d)s, default_value='%(default)s')""" % locals() elif hasattr(extra_val, '__iter__'): @@ -1650,6 +1655,11 @@ def _write_trait_with_range(self, klass, out, vtk_attr_name): 'vtkLineIntegralConvolution2D.MaxNoiseValue$': ( True, True, '_write_line_integral_conv_2d_max_noise_value' ), + # In VTK 9.4, CellGridSidesQuery's Get/OutputDimensionControl is initialized + # to some random value this happens mostly on MacOS. + 'vtkCellGridSidesQuery.OutputDimensionControl$': ( + True, True, '_write_cell_grid_sides_query_od_control' + ), # In VTK 9.3, vtkCylinderSource's GetLatLongTesselation gives random values # https://gitlab.kitware.com/vtk/vtk/-/issues/19252 'vtkCylinderSource.LatLongTessellation$': ( @@ -1827,12 +1837,12 @@ def _write_smart_volume_mapper_vector_component(self, klass, out, default, rng = self.parser.get_get_set_methods()[vtk_attr_name] - if vtk_major_version >= 8: - message = ("vtkSmartVolumeMapper: " - "VectorComponent not updatable " - "(VTK 8.x bug - value not properly initialized)") - print(message) - default = rng[0] + # TODO: Still an issue in 9.x? + message = ("vtkSmartVolumeMapper: " + "VectorComponent not updatable " + "(VTK 8.x bug - value not properly initialized)") + print(message) + default = rng[0] t_def = ('traits.Trait({default}, traits.Range{rng}, ' 'enter_set=True, auto_set=False)').format(default=default, rng=rng) @@ -1848,12 +1858,6 @@ def _write_span_space_resolution(self, klass, out, default, rng = self.parser.get_get_set_methods()[vtk_attr_name] - if vtk_major_version == 7: - message = ("vtkSpanSpace: " - "Resolution not updatable " - "(VTK 7.x bug - value not properly initialized)") - print(message) - default = rng[0] t_def = ('traits.Trait({default}, traits.Range{rng}, ' 'enter_set=True, auto_set=False)').format(default=default, rng=rng) @@ -1867,11 +1871,11 @@ def _write_hyper_tree_grid_cell_centers_vertex_cells(self, klass, out, raise RuntimeError("Not sure why you ask for me! " "I only deal with VertexCells. Panicking.") - if vtk_major_version >= 8: - message = ("vtkHyperTreeGridCellCenters: " - "VertexCells not updatable " - "(VTK 8.x bug - value not properly initialized)") - print(message) + # TODO: Still an issue in 9.x? + message = ("vtkHyperTreeGridCellCenters: " + "VertexCells not updatable " + "(VTK 8.x bug - value not properly initialized)") + print(message) t_def = 'tvtk_base.true_bool_trait' @@ -1888,12 +1892,13 @@ def _write_euclidean_cluster_extraction_radius( default, rng = self.parser.get_get_set_methods()[vtk_attr_name] - if vtk_major_version >= 8: - message = ("vtkEuclideanClusterExtraction: " - "Radius not updatable " - "(VTK 9.1 bug - value not properly initialized)") - print(message) - default = rng[0] + # TODO: Still an issue in 9.x? + message = ("vtkEuclideanClusterExtraction: " + "Radius not updatable " + "(VTK 9.1 bug - value not properly initialized)") + print(message) + default = rng[0] + t_def = ('traits.Trait({default}, traits.Range{rng}, ' 'enter_set=True, auto_set=False)').format(default=default, rng=rng) @@ -1923,12 +1928,25 @@ def _write_line_integral_conv_2d_max_noise_value( vtk_set_meth = getattr(klass, 'Set' + vtk_attr_name) self._write_trait(out, name, t_def, vtk_set_meth, mapped=False) + def _write_cell_grid_sides_query_od_control(self, klass, out, vtk_attr_name): + if vtk_attr_name != 'OutputDimensionControl': + raise RuntimeError(f"Wrong attribute name: {vtk_attr_name}") + if vtk_major_version >= 9: + message = ("vtkCellGridSidesQuery: " + "OutputDimensionControl not updatable " + "(VTK 9.4 bug - value not properly initialized)") + print(message) + t_def = 'tvtk_base.true_bool_trait' + name = self._reform_name(vtk_attr_name) + vtk_set_meth = getattr(klass, 'Set' + vtk_attr_name) + self._write_trait(out, name, t_def, vtk_set_meth, mapped=True) + def _write_cylinder_source_lat_long_tessellation( self, klass, out, vtk_attr_name ): if vtk_attr_name != 'LatLongTessellation': raise RuntimeError(f"Wrong attribute name: {vtk_attr_name}") - if vtk_major_version >= 9: + if (vtk_major_version, vtk_minor_version) <= (9, 3): message = ("vtkCylinderSource: " "LatLongTesselation not updatable " "(VTK 9.3 bug - value not properly initialized)")