marcomusy / vedo

A python module for scientific analysis of 3D data based on VTK and Numpy
https://vedo.embl.es
MIT License
2.05k stars 266 forks source link

addSlider2D: TypeError: DestroyTimer argument 1: an integer is required (got type NoneType) #505

Closed XushanLu closed 2 years ago

XushanLu commented 3 years ago

Hi @marcomusy,

Here is another error that I could not figure out by myself. I am still trying to play with the same data as I used in Issue #504, and this time I am playing with the slider.

Here is the code I used:

#!/usr/bin/env python3

import numpy as np
from vedo import TetMesh, show, screenshot, settings, Picture, buildLUT, Box, \
    Plotter, Axes
from vedo.applications import Animation

import time as tm

def slider(widget, event):
    region = widget.GetRepresentation().GetValue()
    plot_mesh(region)

def plot_mesh(max_reg):

    """Plots the mesh based on the maximum region index"""

    # This will get rid of the background Earth unit and air unit in the model
    # which leaves us with the central part of the model
    tet_tmp = tet.clone().threshold(name='cell_scalars', above=0, below=max_reg)

    # So, we now cut the TetMesh object with a mesh (that Box object)
    tet_tmp.cutWithMesh(box, wholeCells=True)

    # And we need to convert it to a mesh object for later plotting
    msh = tet_tmp.tomesh().lineWidth(1).lineColor('w')

    # We need to build a look up table for our color bar, and now it supports
    # using category names as labels instead of the numerical values
    # This was implemented upon my request
    lut_table = [
        # Value, color, alpha, category
        (12.2, 'dodgerblue', 1, 'C1'),
        (15.2, 'skyblue', 1, 'C1-North'),
        (16.2, 'lightgray', 1, 'Overburden'),
        (18.2, 'yellow', 1, 'MFc'),
        (19.2, 'gold', 1, 'MFb'),
        (21.2, 'red', 1, 'PW'),
        (23.2, 'palegreen', 1, 'BSMT1'),
        (25.2, 'green', 1, 'BSMT2'),
    ]
    lut = buildLUT(lut_table)

    msh.cmap(lut, 'cell_scalars', on='cells')
    # msh.cmap("coolwarm", 'cell_scalars', on='cells')
    msh.addScalarBar3D(
        categories=lut_table,
        pos=(508000, 6416500, -1830),
        title='Units',
        titleSize=1.5,
        sx=100,
        sy=4000,
        titleXOffset=-2,
    )

    zlabels = [(500, '500'), (0, '0'), (-500, '-500'), (-1000, '-1000'),
            (-1500, '-1500')]
    axes = Axes(msh,
                xtitle='Easting (m)',
                ytitle='Northing (m)',
                ztitle='Elevation (m)',
                xLabelSize=0.015,
                xTitlePosition=0.65,
                yTitlePosition=0.65,
                yTitleOffset=-1.18,
                yLabelRotation=90,
                yLabelOffset=-1.6,
                # yShiftAlongX=1,
                zTitlePosition=0.85,
                # zTitleOffset=0.04,
                zLabelRotation=90,
                zValuesAndLabels=zlabels,
                # zShiftAlongX=-1,
                axesLineWidth=3,
                yrange=msh.ybounds(),
                xTitleOffset=0.02,
                # yzShift=1,
                tipSize=0.,
                yzGrid=True,
                xyGrid=True,
                gridLineWidth=5,
                )

    plt.show(msh, size=size)
    # screenshot('model_mesh_vedo.png')

    # from wand import image

    # with image.Image(filename='model_mesh_vedo.png') as imag:
    #     imag.trim(color=None, fuzz=0)
    #     imag.save(filename='model_mesh_vedo.png')

# Do some settings
settings.useDepthPeeling=False  # Useful to show the axes grid
font_name = 'Theemim'
settings.defaultFont = font_name
settings.multiSamples=8
# settings.useParallelProjection = True # avoid perspective parallax

# Create a TetMesh object form the vtk file
tet = TetMesh('final_mesh.vtk')

# This will get rid of the background Earth unit and air unit in the model
# which leaves us with the central part of the model
tet_tmp = tet.clone().threshold(name='cell_scalars', above=0, below=25)
msh = tet_tmp.tomesh().lineWidth(1).lineColor('w')

# Set the camera position
plt = Plotter()
plt.camera.SetPosition( [513381.314, 6406469.652, 6374.748] )
plt.camera.SetFocalPoint( [505099.133, 6415752.321, -907.462] )
plt.camera.SetViewUp( [-0.4, 0.318, 0.86] )
plt.camera.SetDistance( 14415.028 )
plt.camera.SetClippingRange( [7259.637, 23679.065] )

# Crop the entire mesh using a Box object (which is considered to be a mesh
# object in vedo)
# First build a Box object with its centers and dimensions
cent = [504700, 6416500, -615]
box = Box(pos=cent, size=(3000, 5000, 2430))

size = [3940, 2160]

# Add a slider to control the maximum region number in the mesh that we want
# to show
plt.addSlider2D(slider,
                2, 25,
                value=2,
                pos=([0.1,0.1],
                    [0.4,0.1]),
                title="Maximum region number",)

plt.show(interactive=1).close()

While dragging the slider, I get this error: "TypeError: DestroyTimer argument 1: an integer is required (got type NoneType)".

Any idea? Thanks again!

Xushan

marcomusy commented 3 years ago

You are trying to do too many things in the callback function. Especially the show() is problematic because it needs to initialize a few things. Better use render() if needed. Also the mesh doesn't need to be "filled" if it's opaque. Consider this:

#!/usr/bin/env python3
#
import numpy as np
from vedo import TetMesh, show, screenshot, settings, buildLUT, Box
from vedo import Plotter, Axes
from vedo.applications import Animation
import time as tm

def slider(widget, event):
    global acts
    region = widget.GetRepresentation().GetValue()
    plt.remove(acts)
    acts = [gen_mesh(region), sb]
    plt.add(acts)

def gen_mesh(max_reg):
    """Generate the mesh based on the maximum region index"""
    # This will get rid of the background Earth unit and air unit in the model
    # which leaves us with the central part of the model
    tet_tmp = tet.clone().threshold(name='cell_scalars', above=0, below=max_reg)

    # So, we now cut the TetMesh object with a mesh (that Box object)
    tet_tmp.cutWithMesh(box, wholeCells=True)

    # And we need to convert it to a mesh object for later plotting
    msh = tet_tmp.tomesh(fill=False).lineWidth(1).lineColor('w')
    msh.cmap(lut, 'cell_scalars', on='cells')
    return msh

# Do some settings
settings.multiSamples = 8
settings.defaultFont = 'Theemim'

# Load a TetMesh object form the vtk file
tet = TetMesh('final_mesh.vtk')

# This will get rid of the background Earth unit and air unit in the model
# which leaves us with the central part of the model
tet_tmp = tet.clone().threshold(name='cell_scalars', above=0, below=25)
msh = tet_tmp.tomesh().lineWidth(1).lineColor('w')

plt = Plotter(size=[2000, 1500])
# Set the camera position
plt.camera.SetPosition( [513381.314, 6406469.652, 6374.748] )
plt.camera.SetFocalPoint( [505099.133, 6415752.321, -907.462] )
plt.camera.SetViewUp( [-0.4, 0.318, 0.86] )
plt.camera.SetDistance( 14415.028 )
plt.camera.SetClippingRange( [7259.637, 23679.065] )

# We need to build a look up table for our color bar, and now it supports
# using category names as labels instead of the numerical values
# This was implemented upon my request
lut_table = [
    # Value, color, alpha, category
    (12.2, 'dodgerblue', 1, 'C1'),
    (15.2, 'skyblue', 1, 'C1-North'),
    (16.2, 'lightgray', 1, 'Overburden'),
    (18.2, 'yellow', 1, 'MFc'),
    (19.2, 'gold', 1, 'MFb'),
    (21.2, 'red', 1, 'PW'),
    (23.2, 'palegreen', 1, 'BSMT1'),
    (25.2, 'green', 1, 'BSMT2'),
]
lut = buildLUT(lut_table)

# Crop the entire mesh using a Box object 
# (which is considered to be a mesh object in vedo)
# First build a Box object with its centers and dimensions
cent = [504700, 6416500, -615]
box = Box(pos=cent, size=(3000, 5000, 2430))

# Add a slider to control the maximum region number
plt.addSlider2D(slider,
                2, 25,
                value=25,
                pos=([0.1,0.1],[0.4,0.1]),
                title="Maximum region number",
                delayed=True,
)

msh = gen_mesh(25)
acts = [msh]

msh.addScalarBar3D(
    categories=lut_table,
    pos=(508000, 6416500, -1830),
    title='Units',
    titleSize=1.5,
    sx=100,
    sy=4000,
    titleXOffset=-2,
)
sb = msh.scalarbar # otherwise will be automatically removed

axes = Axes(msh,
            xtitle='Easting (m)',
            ytitle='Northing (m)',
            ztitle='Elevation (m)',
            xLabelSize=0.015,
            xTitlePosition=0.65,
            yTitlePosition=0.65,
            yTitleOffset=-.18,
            yShiftAlongX=1,
            yLabelRotation=90,
            yLabelOffset=-1.6,
            zTitlePosition=0.85,
            zLabelRotation=90,
            axesLineWidth=3,
            xTitleOffset=0.02,
            tipSize=0.,
            xyGrid=True,
            gridLineWidth=5,
)

plt.show(msh, axes).close()
XushanLu commented 3 years ago

Worked like a charm! It is not very responsive on my laptop but that is probably because of the large mesh size! Thank you very much for your super quick reply!