From 051365ee5cf4e0a70d73e06d011234060e70045e Mon Sep 17 00:00:00 2001 From: maharshigor Date: Fri, 13 Dec 2024 20:13:24 +0530 Subject: [PATCH] RF: Update for code spell. - Fixed all the pre-commit issues. --- docs/examples/example.ipynb | 4 +- docs/examples/example.py | 10 +- docs/examples/example_offscreen.py | 4 +- docs/examples/example_streamlines.py | 18 +- docs/examples/example_ui.py | 4 +- fury/__init__.pyi | 963 ++- fury/animation/__init__.pyi | 4 +- fury/animation/timeline.py | 1003 +-- fury/convert.py | 120 +- fury/data/__init__.py | 1 - fury/data/fetcher.py | 2 +- fury/io.py | 976 +-- fury/io/__init__.py | 4 +- fury/io/__init__.pyi | 6 +- fury/io/_texture.py | 32 +- fury/lib.py | 0 fury/material.py | 0 fury/ui/__init__.pyi | 136 +- fury/ui/containers.py | 2473 +++---- fury/ui/core.py | 2894 ++++---- fury/ui/elements.py | 9394 +++++++++++++------------- fury/window/__init__.pyi | 2 - fury/window/show_manager.py | 4 +- requirements/default.txt | 2 +- 24 files changed, 9031 insertions(+), 9025 deletions(-) delete mode 100644 fury/lib.py delete mode 100644 fury/material.py diff --git a/docs/examples/example.ipynb b/docs/examples/example.ipynb index df4b4527d..f58670335 100644 --- a/docs/examples/example.ipynb +++ b/docs/examples/example.ipynb @@ -53,7 +53,7 @@ "\n", "\n", "def screenshot(event):\n", - " show_m.snapshot('snapshot.png')\n", + " show_m.snapshot(\"snapshot.png\")\n", "\n", "\n", "cube.add_event_handler(screenshot, \"click\")\n", @@ -87,7 +87,7 @@ " gfx.MeshPhongMaterial(color=\"blue\", pick_write=True),\n", ")\n", "\n", - "show_m.scene.add(cube2)\n" + "show_m.scene.add(cube2)" ] }, { diff --git a/docs/examples/example.py b/docs/examples/example.py index 8066f9ec1..d0de33ae0 100644 --- a/docs/examples/example.py +++ b/docs/examples/example.py @@ -11,17 +11,17 @@ s = sphere(15, color=(1, 0, 1, 1), position=(25, 25, 25)) -point_cloud = points(3, - point_positions=[(5, -5, 5), (-5, 5, 5), (5, 5, -5)], - colors=[(1, 1, 1, 1), (1, 1, 0, 1), (1, 0, 0, 1)]) +point_cloud = points( + 3, + point_positions=[(5, -5, 5), (-5, 5, 5), (5, 5, -5)], + colors=[(1, 1, 1, 1), (1, 1, 0, 1), (1, 0, 0, 1)], +) # show_m.scene.add(cube) show_m.scene.add(s) show_m.scene.add(point_cloud) - - # def screenshot(event): # show_m.snapshot('snapshot.png') diff --git a/docs/examples/example_offscreen.py b/docs/examples/example_offscreen.py index 01cc57cdf..8f29db367 100644 --- a/docs/examples/example_offscreen.py +++ b/docs/examples/example_offscreen.py @@ -12,7 +12,7 @@ def screenshot(event): - show_m.snapshot('snapshot.png') + show_m.snapshot("snapshot.png") # cube.add_event_handler(screenshot, "click") @@ -20,4 +20,4 @@ def screenshot(event): if __name__ == "__main__": show_m.render() show_m.canvas.draw() - show_m.snapshot('offscreen.png') + show_m.snapshot("offscreen.png") diff --git a/docs/examples/example_streamlines.py b/docs/examples/example_streamlines.py index 305bd6e84..182fbb1e5 100644 --- a/docs/examples/example_streamlines.py +++ b/docs/examples/example_streamlines.py @@ -7,9 +7,10 @@ from fury.colormap import distinguishable_colormap fname = os.path.expanduser( - '~/.dipy/bundle_atlas_hcp842/Atlas_80_Bundles/bundles/AC.trk') + "~/.dipy/bundle_atlas_hcp842/Atlas_80_Bundles/bundles/AC.trk" +) -sft = load_tractogram(fname, 'same', bbox_valid_check=False) +sft = load_tractogram(fname, "same", bbox_valid_check=False) streamlines = sft.streamlines # Pygfx window setup @@ -33,16 +34,11 @@ color = next(color_gen) start_idx = int(pygfx_offsets[i] + i) end_idx = int(start_idx + pygfx_lengths[i]) - pygfx_streamlines = np.insert( - pygfx_streamlines, end_idx, nan_buffer, axis=0) + pygfx_streamlines = np.insert(pygfx_streamlines, end_idx, nan_buffer, axis=0) pygfx_colors[start_idx:end_idx] = (*color, 1) # Pygfx line definition -lines = lines( - positions=pygfx_streamlines, - colors=pygfx_colors, - color_mode='vertex' -) +lines = lines(positions=pygfx_streamlines, colors=pygfx_colors, color_mode="vertex") show_m.scene.add(lines) @@ -60,7 +56,7 @@ def on_pick(event): print("Vertex Coord: ", pygfx_streamlines[vertex]) selected = find_line(vertex) color = pygfx_colors[selected[0]][:3] - pygfx_colors[selected[0]:selected[1] + 1] = (*color, 0.5) + pygfx_colors[selected[0] : selected[1] + 1] = (*color, 0.5) lines.geometry.colors.update_range() show_m.update() @@ -78,6 +74,6 @@ def find_line(vertex): return [left + 1, right - 1] -if __name__ == '__main__': +if __name__ == "__main__": show_m.render() show_m.start() diff --git a/docs/examples/example_ui.py b/docs/examples/example_ui.py index 9fb7edb8d..3f83afb31 100644 --- a/docs/examples/example_ui.py +++ b/docs/examples/example_ui.py @@ -22,8 +22,7 @@ def clicked(event): # panel.obj.add_event_handler(clicked, 'pointer_down') show_m.scene.add(panel.obj) -panel.obj.handle_event(gfx.PointerEvent(x=10, y=10, type='pointer_down')) - +panel.obj.handle_event(gfx.PointerEvent(x=10, y=10, type="pointer_down")) # geo = gfx.plane_geometry(200, 50) @@ -33,7 +32,6 @@ def clicked(event): # show_m.scene.add(obj) - if __name__ == "__main__": show_m.render() show_m.start() diff --git a/fury/__init__.pyi b/fury/__init__.pyi index 9a961b136..73a25337d 100644 --- a/fury/__init__.pyi +++ b/fury/__init__.pyi @@ -1,490 +1,489 @@ -# flake8: noqa +# # flake8: noqa -# This file is a stub type for the fury package. It provides information about types -# to help type-checking tools like mypy and improve the development experience -# with better autocompletion and documentation in code editors. +# # This file is a stub type for the fury package. It provides information about types +# # to help type-checking tools like mypy and improve the development experience +# # with better autocompletion and documentation in code editors. -__all__ = [ - "actor", - "actors", - "animation", - "colormap", - "convert", - "data", - "deprecator", - "decorators", - "gltf", - "interactor", - "io", - "layout", - "lib", - "material", - "molecular", - "optpkg", - "pick", - "pkg_info", - "primitive", - "shaders", - "stream", - "testing", - "transform", - "ui", - "utils", - "window", -] +# __all__ = [ +# "actor", +# "actors", +# "animation", +# "colormap", +# "convert", +# "data", +# "deprecator", +# "decorators", +# "gltf", +# "interactor", +# "io", +# "layout", +# "lib", +# "material", +# "molecular", +# "optpkg", +# "pick", +# "pkg_info", +# "primitive", +# "shaders", +# "stream", +# "testing", +# "transform", +# "ui", +# "utils", +# "window", +# ] -# the explicit definition of `__all__` will enable type inference for engines. +# # the explicit definition of `__all__` will enable type inference for engines. -from . import ( - actor, - actors, - animation, - colormap, - convert, - data, - decorators, - deprecator, - gltf, - interactor, - io, - layout, - lib, - material, - molecular, - optpkg, - pick, - pkg_info, - primitive, - shaders, - stream, - testing, - transform, - ui, - utils, - window, -) +# from . import ( +# actors, +# animation, +# colormap, +# convert, +# data, +# decorators, +# deprecator, +# gltf, +# interactor, +# io, +# layout, +# optpkg, +# pkg_info, +# primitive, +# shaders, +# stream, +# testing, +# ui, +# window, +# ) -from .actor import ( - Container as Container, - _color_fa as _color_fa, - _fa as _fa, - _makeNd as _makeNd, - _roll_evals as _roll_evals, - _tensor_slicer_mapper as _tensor_slicer_mapper, - _textured_sphere_source as _textured_sphere_source, - arrow as arrow, - axes as axes, - billboard as billboard, - box as box, - cone as cone, - contour_from_label as contour_from_label, - contour_from_roi as contour_from_roi, - cube as cube, - cylinder as cylinder, - disk as disk, - dot as dot, - ellipsoid as ellipsoid, - figure as figure, - frustum as frustum, - grid as grid, - line as line, - markers as markers, - octagonalprism as octagonalprism, - odf_slicer as odf_slicer, - peak as peak, - peak_slicer as peak_slicer, - pentagonalprism as pentagonalprism, - point as point, - rectangle as rectangle, - rhombicuboctahedron as rhombicuboctahedron, - scalar_bar as scalar_bar, - sdf as sdf, - slicer as slicer, - sphere as sphere, - square as square, - streamtube as streamtube, - superquadric as superquadric, - surface as surface, - tensor_slicer as tensor_slicer, - text_3d as text_3d, - texture as texture, - texture_2d as texture_2d, - texture_on_sphere as texture_on_sphere, - texture_update as texture_update, - triangularprism as triangularprism, - uncertainty_cone as uncertainty_cone, - vector_text as vector_text, -) -from .actors import ( - OdfSlicerActor as OdfSlicerActor, - PeakActor as PeakActor, - _orientation_colors as _orientation_colors, - _peaks_colors_from_points as _peaks_colors_from_points, - _points_to_vtk_cells as _points_to_vtk_cells, - double_cone as double_cone, - main_dir_uncertainty as main_dir_uncertainty, - tensor_ellipsoid as tensor_ellipsoid, -) -from .animation import ( - Animation as Animation, - Timeline as Timeline, - color_interpolator as color_interpolator, - cubic_bezier_interpolator as cubic_bezier_interpolator, - cubic_spline_interpolator as cubic_spline_interpolator, - euclidean_distances as euclidean_distances, - get_next_timestamp as get_next_timestamp, - get_previous_timestamp as get_previous_timestamp, - get_time_tau as get_time_tau, - get_timestamps_from_keyframes as get_timestamps_from_keyframes, - get_values_from_keyframes as get_values_from_keyframes, - hsv_color_interpolator as hsv_color_interpolator, - lab_color_interpolator as lab_color_interpolator, - lerp as lerp, - linear_interpolator as linear_interpolator, - slerp as slerp, - spline_interpolator as spline_interpolator, - step_interpolator as step_interpolator, - tan_cubic_spline_interpolator as tan_cubic_spline_interpolator, - xyz_color_interpolator as xyz_color_interpolator, -) -from .colormap import ( - _lab2rgb as _lab2rgb, - _lab2xyz as _lab2xyz, - _lab_delta as _lab_delta, - _rgb2lab as _rgb2lab, - _rgb2xyz as _rgb2xyz, - _rgb_lab_delta as _rgb_lab_delta, - _xyz2lab as _xyz2lab, - _xyz2rgb as _xyz2rgb, - boys2rgb as boys2rgb, - cc as cc, - colormap_lookup_table as colormap_lookup_table, - create_colormap as create_colormap, - distinguishable_colormap as distinguishable_colormap, - get_cmap as get_cmap, - get_xyz_coords as get_xyz_coords, - hex_to_rgb as hex_to_rgb, - hsv2rgb as hsv2rgb, - lab2rgb as lab2rgb, - lab2xyz as lab2xyz, - line_colors as line_colors, - orient2rgb as orient2rgb, - rgb2hsv as rgb2hsv, - rgb2lab as rgb2lab, - rgb2xyz as rgb2xyz, - ss as ss, - xyz2lab as xyz2lab, - xyz2rgb as xyz2rgb, -) -from .convert import matplotlib_figure_to_numpy as matplotlib_figure_to_numpy -from .data import ( - FetcherError as FetcherError, - _already_there_msg as _already_there_msg, - _download as _download, - _fetch_gltf as _fetch_gltf, - _get_file_data as _get_file_data, - _get_file_sha as _get_file_sha, - _make_fetcher as _make_fetcher, - _request as _request, - check_sha as check_sha, - copyfileobj_withprogress as copyfileobj_withprogress, - fetch_data as fetch_data, - fetch_gltf as fetch_gltf, - fetch_viz_cubemaps as fetch_viz_cubemaps, - fetch_viz_dmri as fetch_viz_dmri, - fetch_viz_icons as fetch_viz_icons, - fetch_viz_models as fetch_viz_models, - fetch_viz_new_icons as fetch_viz_new_icons, - fetch_viz_textures as fetch_viz_textures, - fetch_viz_wiki_nw as fetch_viz_wiki_nw, - list_gltf_sample_models as list_gltf_sample_models, - read_viz_cubemap as read_viz_cubemap, - read_viz_dmri as read_viz_dmri, - read_viz_gltf as read_viz_gltf, - read_viz_icons as read_viz_icons, - read_viz_models as read_viz_models, - read_viz_textures as read_viz_textures, - update_progressbar as update_progressbar, -) -from .decorators import ( - doctest_skip_parser as doctest_skip_parser, - warn_on_args_to_kwargs as warn_on_args_to_kwargs, -) -from .deprecator import ( - ArgsDeprecationWarning as ArgsDeprecationWarning, - ExpiredDeprecationError as ExpiredDeprecationError, - _ensure_cr as _ensure_cr, - cmp_pkg_version as cmp_pkg_version, - deprecate_with_version as deprecate_with_version, - deprecated_params as deprecated_params, - is_bad_version as is_bad_version, -) -from .gltf import ( - glTF as glTF, - _connect_primitives as _connect_primitives, - export_scene as export_scene, - get_prim as get_prim, - write_accessor as write_accessor, - write_buffer as write_buffer, - write_bufferview as write_bufferview, - write_camera as write_camera, - write_material as write_material, - write_mesh as write_mesh, - write_node as write_node, - write_scene as write_scene, -) -from .interactor import ( - CustomInteractorStyle as CustomInteractorStyle, - Event as Event, -) -from .io import ( - load_cubemap_texture as load_cubemap_texture, - load_image as load_image, - load_polydata as load_polydata, - load_sprite_sheet as load_sprite_sheet, - load_text as load_text, - save_image as save_image, - save_polydata as save_polydata, -) -from .layout import ( - GridLayout as GridLayout, - HorizontalLayout as HorizontalLayout, - Layout as Layout, - VerticalLayout as VerticalLayout, - XLayout as XLayout, - YLayout as YLayout, - ZLayout as ZLayout, -) -from .material import ( - __PBRParams as __PBRParams, - manifest_pbr as manifest_pbr, - manifest_principled as manifest_principled, - manifest_standard as manifest_standard, -) -from .molecular import ( - Molecule as Molecule, - PTable as PTable, - add_atom as add_atom, - add_bond as add_bond, - ball_stick as ball_stick, - bounding_box as bounding_box, - compute_bonding as compute_bonding, - deep_copy_molecule as deep_copy_molecule, - get_all_atomic_numbers as get_all_atomic_numbers, - get_all_atomic_positions as get_all_atomic_positions, - get_all_bond_orders as get_all_bond_orders, - get_atomic_number as get_atomic_number, - get_atomic_position as get_atomic_position, - get_bond_order as get_bond_order, - ribbon as ribbon, - set_atomic_number as set_atomic_number, - set_atomic_position as set_atomic_position, - set_bond_order as set_bond_order, - sphere_cpk as sphere_cpk, - stick as stick, -) -from .optpkg import ( - TripWire as TripWire, - TripWireError as TripWireError, - is_tripwire as is_tripwire, - optional_package as optional_package, -) -from .pick import PickingManager as PickingManager -from .pkg_info import pkg_commit_hash as pkg_commit_hash -from .primitive import ( - faces_from_sphere_vertices as faces_from_sphere_vertices, - prim_arrow as prim_arrow, - prim_box as prim_box, - prim_cone as prim_cone, - prim_cylinder as prim_cylinder, - prim_frustum as prim_frustum, - prim_icosahedron as prim_icosahedron, - prim_octagonalprism as prim_octagonalprism, - prim_pentagonalprism as prim_pentagonalprism, - prim_rhombicuboctahedron as prim_rhombicuboctahedron, - prim_sphere as prim_sphere, - prim_square as prim_square, - prim_star as prim_star, - prim_superquadric as prim_superquadric, - prim_tetrahedron as prim_tetrahedron, - prim_triangularprism as prim_triangularprism, - repeat_primitive as repeat_primitive, - repeat_primitive_function as repeat_primitive_function, -) -from .shaders import ( - add_shader_callback as add_shader_callback, - attribute_to_actor as attribute_to_actor, - compose_shader as compose_shader, - import_fury_shader as import_fury_shader, - load as load, - load_shader as load_shader, - replace_shader_in_actor as replace_shader_in_actor, - shader_apply_effects as shader_apply_effects, - shader_to_actor as shader_to_actor, -) -from .stream import ( - ArrayCircularQueue as ArrayCircularQueue, - FuryStreamClient as FuryStreamClient, - FuryStreamInteraction as FuryStreamInteraction, - GenericCircularQueue as GenericCircularQueue, - GenericImageBufferManager as GenericImageBufferManager, - GenericMultiDimensionalBuffer as GenericMultiDimensionalBuffer, - IntervalTimer as IntervalTimer, - IntervalTimerThreading as IntervalTimerThreading, - RawArrayImageBufferManager as RawArrayImageBufferManager, - RawArrayMultiDimensionalBuffer as RawArrayMultiDimensionalBuffer, - SharedMemCircularQueue as SharedMemCircularQueue, - SharedMemImageBufferManager as SharedMemImageBufferManager, - SharedMemMultiDimensionalBuffer as SharedMemMultiDimensionalBuffer, - Widget as Widget, - check_port_is_available as check_port_is_available, - interaction_callback as interaction_callback, - remove_shm_from_resource_tracker as remove_shm_from_resource_tracker, -) -from .testing import ( - EventCounter as EventCounter, - assert_arrays_equal as assert_arrays_equal, - assert_operator as assert_operator, - captured_output as captured_output, - clear_and_catch_warnings as clear_and_catch_warnings, - setup_test as setup_test, -) -from .transform import ( - apply_transformation as apply_transformation, - cart2sphere as cart2sphere, - euler_matrix as euler_matrix, - rotate as rotate, - scale as scale, - sphere2cart as sphere2cart, - transform_from_matrix as transform_from_matrix, - translate as translate, -) -from .ui import ( - UI as UI, - Button2D as Button2D, - Card2D as Card2D, - Checkbox as Checkbox, - ComboBox2D as ComboBox2D, - Disk2D as Disk2D, - DrawPanel as DrawPanel, - DrawShape as DrawShape, - FileMenu2D as FileMenu2D, - GridUI as GridUI, - ImageContainer2D as ImageContainer2D, - LineDoubleSlider2D as LineDoubleSlider2D, - LineSlider2D as LineSlider2D, - ListBox2D as ListBox2D, - ListBoxItem2D as ListBoxItem2D, - Option as Option, - Panel2D as Panel2D, - PlaybackPanel as PlaybackPanel, - RadioButton as RadioButton, - RangeSlider as RangeSlider, - Rectangle2D as Rectangle2D, - RingSlider2D as RingSlider2D, - SpinBox as SpinBox, - TabPanel2D as TabPanel2D, - TabUI as TabUI, - TextBlock2D as TextBlock2D, - TextBox2D as TextBox2D, - cal_bounding_box_2d as cal_bounding_box_2d, - check_overflow as check_overflow, - clip_overflow as clip_overflow, - rotate_2d as rotate_2d, - wrap_overflow as wrap_overflow, -) -from .utils import ( - add_polydata_numeric_field as add_polydata_numeric_field, - apply_affine as apply_affine, - apply_affine_to_actor as apply_affine_to_actor, - array_from_actor as array_from_actor, - asbytes as asbytes, - change_vertices_order as change_vertices_order, - color_check as color_check, - colors_from_actor as colors_from_actor, - compute_bounds as compute_bounds, - fix_winding_order as fix_winding_order, - get_actor_from_polydata as get_actor_from_polydata, - get_actor_from_polymapper as get_actor_from_polymapper, - get_actor_from_primitive as get_actor_from_primitive, - get_bounding_box_sizes as get_bounding_box_sizes, - get_bounds as get_bounds, - get_grid_cells_position as get_grid_cells_position, - get_polydata_colors as get_polydata_colors, - get_polydata_field as get_polydata_field, - get_polydata_lines as get_polydata_lines, - get_polydata_normals as get_polydata_normals, - get_polydata_primitives_count as get_polydata_primitives_count, - get_polydata_tangents as get_polydata_tangents, - get_polydata_tcoord as get_polydata_tcoord, - get_polydata_triangles as get_polydata_triangles, - get_polydata_vertices as get_polydata_vertices, - get_polymapper_from_polydata as get_polymapper_from_polydata, - is_ui as is_ui, - lines_to_vtk_polydata as lines_to_vtk_polydata, - map_coordinates_3d_4d as map_coordinates_3d_4d, - normalize_v3 as normalize_v3, - normals_from_actor as normals_from_actor, - normals_from_v_f as normals_from_v_f, - normals_to_actor as normals_to_actor, - numpy_to_vtk_cells as numpy_to_vtk_cells, - numpy_to_vtk_colors as numpy_to_vtk_colors, - numpy_to_vtk_image_data as numpy_to_vtk_image_data, - numpy_to_vtk_matrix as numpy_to_vtk_matrix, - numpy_to_vtk_points as numpy_to_vtk_points, - primitives_count_from_actor as primitives_count_from_actor, - primitives_count_to_actor as primitives_count_to_actor, - remove_observer_from_actor as remove_observer_from_actor, - repeat_sources as repeat_sources, - represent_actor_as_wireframe as represent_actor_as_wireframe, - rgb_to_vtk as rgb_to_vtk, - rotate as rotate, - set_actor_origin as set_actor_origin, - set_input as set_input, - set_polydata_colors as set_polydata_colors, - set_polydata_normals as set_polydata_normals, - set_polydata_primitives_count as set_polydata_primitives_count, - set_polydata_tangents as set_polydata_tangents, - set_polydata_tcoords as set_polydata_tcoords, - set_polydata_triangles as set_polydata_triangles, - set_polydata_vertices as set_polydata_vertices, - shallow_copy as shallow_copy, - tangents_from_actor as tangents_from_actor, - tangents_from_direction_of_anisotropy as tangents_from_direction_of_anisotropy, - tangents_to_actor as tangents_to_actor, - triangle_order as triangle_order, - update_actor as update_actor, - update_polydata_normals as update_polydata_normals, - update_surface_actor_colors as update_surface_actor_colors, - vertices_from_actor as vertices_from_actor, - vtk_matrix_to_numpy as vtk_matrix_to_numpy, -) -from .window import ( - Scene as Scene, - ShowManager as ShowManager, - analyze_scene as analyze_scene, - analyze_snapshot as analyze_snapshot, - antialiasing as antialiasing, - enable_stereo as enable_stereo, - gl_disable_blend as gl_disable_blend, - gl_disable_depth as gl_disable_depth, - gl_enable_blend as gl_enable_blend, - gl_enable_depth as gl_enable_depth, - gl_get_current_state as gl_get_current_state, - gl_reset_blend as gl_reset_blend, - gl_set_additive_blending as gl_set_additive_blending, - gl_set_additive_blending_white_background as gl_set_additive_blending_white_background, - gl_set_multiplicative_blending as gl_set_multiplicative_blending, - gl_set_normal_blending as gl_set_normal_blending, - gl_set_subtractive_blending as gl_set_subtractive_blending, - record as record, - release_context as release_context, - show as show, - snapshot as snapshot, -) +# # from .actor import ( +# # Container as Container, +# # _color_fa as _color_fa, +# # _fa as _fa, +# # _makeNd as _makeNd, +# # _roll_evals as _roll_evals, +# # _tensor_slicer_mapper as _tensor_slicer_mapper, +# # _textured_sphere_source as _textured_sphere_source, +# # arrow as arrow, +# # axes as axes, +# # billboard as billboard, +# # box as box, +# # cone as cone, +# # contour_from_label as contour_from_label, +# # contour_from_roi as contour_from_roi, +# # cube as cube, +# # cylinder as cylinder, +# # disk as disk, +# # dot as dot, +# # ellipsoid as ellipsoid, +# # figure as figure, +# # frustum as frustum, +# # grid as grid, +# # line as line, +# # markers as markers, +# # octagonalprism as octagonalprism, +# # odf_slicer as odf_slicer, +# # peak as peak, +# # peak_slicer as peak_slicer, +# # pentagonalprism as pentagonalprism, +# # point as point, +# # rectangle as rectangle, +# # rhombicuboctahedron as rhombicuboctahedron, +# # scalar_bar as scalar_bar, +# # sdf as sdf, +# # slicer as slicer, +# # sphere as sphere, +# # square as square, +# # streamtube as streamtube, +# # superquadric as superquadric, +# # surface as surface, +# # tensor_slicer as tensor_slicer, +# # text_3d as text_3d, +# # texture as texture, +# # texture_2d as texture_2d, +# # texture_on_sphere as texture_on_sphere, +# # texture_update as texture_update, +# # triangularprism as triangularprism, +# # uncertainty_cone as uncertainty_cone, +# # vector_text as vector_text, +# # ) +# # from .actors import ( +# # OdfSlicerActor as OdfSlicerActor, +# # PeakActor as PeakActor, +# # _orientation_colors as _orientation_colors, +# # _peaks_colors_from_points as _peaks_colors_from_points, +# # _points_to_vtk_cells as _points_to_vtk_cells, +# # double_cone as double_cone, +# # main_dir_uncertainty as main_dir_uncertainty, +# # tensor_ellipsoid as tensor_ellipsoid, +# # ) +# from .animation import ( +# Animation as Animation, +# Timeline as Timeline, +# color_interpolator as color_interpolator, +# cubic_bezier_interpolator as cubic_bezier_interpolator, +# cubic_spline_interpolator as cubic_spline_interpolator, +# euclidean_distances as euclidean_distances, +# get_next_timestamp as get_next_timestamp, +# get_previous_timestamp as get_previous_timestamp, +# get_time_tau as get_time_tau, +# get_timestamps_from_keyframes as get_timestamps_from_keyframes, +# get_values_from_keyframes as get_values_from_keyframes, +# hsv_color_interpolator as hsv_color_interpolator, +# lab_color_interpolator as lab_color_interpolator, +# lerp as lerp, +# linear_interpolator as linear_interpolator, +# slerp as slerp, +# spline_interpolator as spline_interpolator, +# step_interpolator as step_interpolator, +# tan_cubic_spline_interpolator as tan_cubic_spline_interpolator, +# xyz_color_interpolator as xyz_color_interpolator, +# ) +# from .colormap import ( +# _lab2rgb as _lab2rgb, +# _lab2xyz as _lab2xyz, +# _lab_delta as _lab_delta, +# _rgb2lab as _rgb2lab, +# _rgb2xyz as _rgb2xyz, +# _rgb_lab_delta as _rgb_lab_delta, +# _xyz2lab as _xyz2lab, +# _xyz2rgb as _xyz2rgb, +# boys2rgb as boys2rgb, +# cc as cc, +# colormap_lookup_table as colormap_lookup_table, +# create_colormap as create_colormap, +# distinguishable_colormap as distinguishable_colormap, +# get_cmap as get_cmap, +# get_xyz_coords as get_xyz_coords, +# hex_to_rgb as hex_to_rgb, +# hsv2rgb as hsv2rgb, +# lab2rgb as lab2rgb, +# lab2xyz as lab2xyz, +# line_colors as line_colors, +# orient2rgb as orient2rgb, +# rgb2hsv as rgb2hsv, +# rgb2lab as rgb2lab, +# rgb2xyz as rgb2xyz, +# ss as ss, +# xyz2lab as xyz2lab, +# xyz2rgb as xyz2rgb, +# ) +# from .convert import matplotlib_figure_to_numpy as matplotlib_figure_to_numpy +# from .data import ( +# FetcherError as FetcherError, +# _already_there_msg as _already_there_msg, +# _download as _download, +# _fetch_gltf as _fetch_gltf, +# _get_file_data as _get_file_data, +# _get_file_sha as _get_file_sha, +# _make_fetcher as _make_fetcher, +# _request as _request, +# check_sha as check_sha, +# copyfileobj_withprogress as copyfileobj_withprogress, +# fetch_data as fetch_data, +# fetch_gltf as fetch_gltf, +# fetch_viz_cubemaps as fetch_viz_cubemaps, +# fetch_viz_dmri as fetch_viz_dmri, +# fetch_viz_icons as fetch_viz_icons, +# fetch_viz_models as fetch_viz_models, +# fetch_viz_new_icons as fetch_viz_new_icons, +# fetch_viz_textures as fetch_viz_textures, +# fetch_viz_wiki_nw as fetch_viz_wiki_nw, +# list_gltf_sample_models as list_gltf_sample_models, +# read_viz_cubemap as read_viz_cubemap, +# read_viz_dmri as read_viz_dmri, +# read_viz_gltf as read_viz_gltf, +# read_viz_icons as read_viz_icons, +# read_viz_models as read_viz_models, +# read_viz_textures as read_viz_textures, +# update_progressbar as update_progressbar, +# ) +# from .decorators import ( +# doctest_skip_parser as doctest_skip_parser, +# warn_on_args_to_kwargs as warn_on_args_to_kwargs, +# ) +# from .deprecator import ( +# ArgsDeprecationWarning as ArgsDeprecationWarning, +# ExpiredDeprecationError as ExpiredDeprecationError, +# _ensure_cr as _ensure_cr, +# cmp_pkg_version as cmp_pkg_version, +# deprecate_with_version as deprecate_with_version, +# deprecated_params as deprecated_params, +# is_bad_version as is_bad_version, +# ) + +# # from .gltf import ( +# # glTF as glTF, +# # _connect_primitives as _connect_primitives, +# # export_scene as export_scene, +# # get_prim as get_prim, +# # write_accessor as write_accessor, +# # write_buffer as write_buffer, +# # write_bufferview as write_bufferview, +# # write_camera as write_camera, +# # write_material as write_material, +# # write_mesh as write_mesh, +# # write_node as write_node, +# # write_scene as write_scene, +# # ) +# # from .interactor import ( +# # CustomInteractorStyle as CustomInteractorStyle, +# # Event as Event, +# # ) +# from .io import ( +# load_cubemap_texture as load_cubemap_texture, +# load_image as load_image, +# load_polydata as load_polydata, +# load_sprite_sheet as load_sprite_sheet, +# load_text as load_text, +# save_image as save_image, +# save_polydata as save_polydata, +# ) + +# # from .layout import ( +# # GridLayout as GridLayout, +# # HorizontalLayout as HorizontalLayout, +# # Layout as Layout, +# # VerticalLayout as VerticalLayout, +# # XLayout as XLayout, +# # YLayout as YLayout, +# # ZLayout as ZLayout, +# # ) +# # from .material import ( +# # __PBRParams as __PBRParams, +# # manifest_pbr as manifest_pbr, +# # manifest_principled as manifest_principled, +# # manifest_standard as manifest_standard, +# # ) +# # from .molecular import ( +# # Molecule as Molecule, +# # PTable as PTable, +# # add_atom as add_atom, +# # add_bond as add_bond, +# # ball_stick as ball_stick, +# # bounding_box as bounding_box, +# # compute_bonding as compute_bonding, +# # deep_copy_molecule as deep_copy_molecule, +# # get_all_atomic_numbers as get_all_atomic_numbers, +# # get_all_atomic_positions as get_all_atomic_positions, +# # get_all_bond_orders as get_all_bond_orders, +# # get_atomic_number as get_atomic_number, +# # get_atomic_position as get_atomic_position, +# # get_bond_order as get_bond_order, +# # ribbon as ribbon, +# # set_atomic_number as set_atomic_number, +# # set_atomic_position as set_atomic_position, +# # set_bond_order as set_bond_order, +# # sphere_cpk as sphere_cpk, +# # stick as stick, +# # ) +# from .optpkg import ( +# TripWire as TripWire, +# TripWireError as TripWireError, +# is_tripwire as is_tripwire, +# optional_package as optional_package, +# ) + +# # from .pick import PickingManager as PickingManager +# from .pkg_info import pkg_commit_hash as pkg_commit_hash +# from .primitive import ( +# faces_from_sphere_vertices as faces_from_sphere_vertices, +# prim_arrow as prim_arrow, +# prim_box as prim_box, +# prim_cone as prim_cone, +# prim_cylinder as prim_cylinder, +# prim_frustum as prim_frustum, +# prim_icosahedron as prim_icosahedron, +# prim_octagonalprism as prim_octagonalprism, +# prim_pentagonalprism as prim_pentagonalprism, +# prim_rhombicuboctahedron as prim_rhombicuboctahedron, +# prim_sphere as prim_sphere, +# prim_square as prim_square, +# prim_star as prim_star, +# prim_superquadric as prim_superquadric, +# prim_tetrahedron as prim_tetrahedron, +# prim_triangularprism as prim_triangularprism, +# repeat_primitive as repeat_primitive, +# repeat_primitive_function as repeat_primitive_function, +# ) +# from .shaders import ( +# add_shader_callback as add_shader_callback, +# attribute_to_actor as attribute_to_actor, +# compose_shader as compose_shader, +# import_fury_shader as import_fury_shader, +# load as load, +# load_shader as load_shader, +# replace_shader_in_actor as replace_shader_in_actor, +# shader_apply_effects as shader_apply_effects, +# shader_to_actor as shader_to_actor, +# ) +# from .stream import ( +# ArrayCircularQueue as ArrayCircularQueue, +# FuryStreamClient as FuryStreamClient, +# FuryStreamInteraction as FuryStreamInteraction, +# GenericCircularQueue as GenericCircularQueue, +# GenericImageBufferManager as GenericImageBufferManager, +# GenericMultiDimensionalBuffer as GenericMultiDimensionalBuffer, +# IntervalTimer as IntervalTimer, +# IntervalTimerThreading as IntervalTimerThreading, +# RawArrayImageBufferManager as RawArrayImageBufferManager, +# RawArrayMultiDimensionalBuffer as RawArrayMultiDimensionalBuffer, +# SharedMemCircularQueue as SharedMemCircularQueue, +# SharedMemImageBufferManager as SharedMemImageBufferManager, +# SharedMemMultiDimensionalBuffer as SharedMemMultiDimensionalBuffer, +# Widget as Widget, +# check_port_is_available as check_port_is_available, +# interaction_callback as interaction_callback, +# remove_shm_from_resource_tracker as remove_shm_from_resource_tracker, +# ) + +# # from .testing import ( +# # EventCounter as EventCounter, +# # assert_arrays_equal as assert_arrays_equal, +# # assert_operator as assert_operator, +# # captured_output as captured_output, +# # clear_and_catch_warnings as clear_and_catch_warnings, +# # setup_test as setup_test, +# # ) +# # from .transform import ( +# # apply_transformation as apply_transformation, +# # cart2sphere as cart2sphere, +# # euler_matrix as euler_matrix, +# # rotate as rotate, +# # scale as scale, +# # sphere2cart as sphere2cart, +# # transform_from_matrix as transform_from_matrix, +# # translate as translate, +# # ) +# from .ui import ( +# UI as UI, +# Button2D as Button2D, +# Card2D as Card2D, +# Checkbox as Checkbox, +# ComboBox2D as ComboBox2D, +# Disk2D as Disk2D, +# DrawPanel as DrawPanel, +# DrawShape as DrawShape, +# FileMenu2D as FileMenu2D, +# GridUI as GridUI, +# ImageContainer2D as ImageContainer2D, +# LineDoubleSlider2D as LineDoubleSlider2D, +# LineSlider2D as LineSlider2D, +# ListBox2D as ListBox2D, +# ListBoxItem2D as ListBoxItem2D, +# Option as Option, +# Panel2D as Panel2D, +# PlaybackPanel as PlaybackPanel, +# RadioButton as RadioButton, +# RangeSlider as RangeSlider, +# Rectangle2D as Rectangle2D, +# RingSlider2D as RingSlider2D, +# SpinBox as SpinBox, +# TabPanel2D as TabPanel2D, +# TabUI as TabUI, +# TextBlock2D as TextBlock2D, +# TextBox2D as TextBox2D, +# cal_bounding_box_2d as cal_bounding_box_2d, +# check_overflow as check_overflow, +# clip_overflow as clip_overflow, +# rotate_2d as rotate_2d, +# wrap_overflow as wrap_overflow, +# ) + +# # from .utils import ( +# # add_polydata_numeric_field as add_polydata_numeric_field, +# # apply_affine as apply_affine, +# # apply_affine_to_actor as apply_affine_to_actor, +# # array_from_actor as array_from_actor, +# # asbytes as asbytes, +# # change_vertices_order as change_vertices_order, +# # color_check as color_check, +# # colors_from_actor as colors_from_actor, +# # compute_bounds as compute_bounds, +# # fix_winding_order as fix_winding_order, +# # get_actor_from_polydata as get_actor_from_polydata, +# # get_actor_from_polymapper as get_actor_from_polymapper, +# # get_actor_from_primitive as get_actor_from_primitive, +# # get_bounding_box_sizes as get_bounding_box_sizes, +# # get_bounds as get_bounds, +# # get_grid_cells_position as get_grid_cells_position, +# # get_polydata_colors as get_polydata_colors, +# # get_polydata_field as get_polydata_field, +# # get_polydata_lines as get_polydata_lines, +# # get_polydata_normals as get_polydata_normals, +# # get_polydata_primitives_count as get_polydata_primitives_count, +# # get_polydata_tangents as get_polydata_tangents, +# # get_polydata_tcoord as get_polydata_tcoord, +# # get_polydata_triangles as get_polydata_triangles, +# # get_polydata_vertices as get_polydata_vertices, +# # get_polymapper_from_polydata as get_polymapper_from_polydata, +# # is_ui as is_ui, +# # lines_to_vtk_polydata as lines_to_vtk_polydata, +# # map_coordinates_3d_4d as map_coordinates_3d_4d, +# # normalize_v3 as normalize_v3, +# # normals_from_actor as normals_from_actor, +# # normals_from_v_f as normals_from_v_f, +# # normals_to_actor as normals_to_actor, +# # numpy_to_vtk_cells as numpy_to_vtk_cells, +# # numpy_to_vtk_colors as numpy_to_vtk_colors, +# # numpy_to_vtk_image_data as numpy_to_vtk_image_data, +# # numpy_to_vtk_matrix as numpy_to_vtk_matrix, +# # numpy_to_vtk_points as numpy_to_vtk_points, +# # primitives_count_from_actor as primitives_count_from_actor, +# # primitives_count_to_actor as primitives_count_to_actor, +# # remove_observer_from_actor as remove_observer_from_actor, +# # repeat_sources as repeat_sources, +# # represent_actor_as_wireframe as represent_actor_as_wireframe, +# # rgb_to_vtk as rgb_to_vtk, +# # rotate as rotate, +# # set_actor_origin as set_actor_origin, +# # set_input as set_input, +# # set_polydata_colors as set_polydata_colors, +# # set_polydata_normals as set_polydata_normals, +# # set_polydata_primitives_count as set_polydata_primitives_count, +# # set_polydata_tangents as set_polydata_tangents, +# # set_polydata_tcoords as set_polydata_tcoords, +# # set_polydata_triangles as set_polydata_triangles, +# # set_polydata_vertices as set_polydata_vertices, +# # shallow_copy as shallow_copy, +# # tangents_from_actor as tangents_from_actor, +# # tangents_from_direction_of_anisotropy as tangents_from_direction_of_anisotropy, +# # tangents_to_actor as tangents_to_actor, +# # triangle_order as triangle_order, +# # update_actor as update_actor, +# # update_polydata_normals as update_polydata_normals, +# # update_surface_actor_colors as update_surface_actor_colors, +# # vertices_from_actor as vertices_from_actor, +# # vtk_matrix_to_numpy as vtk_matrix_to_numpy, +# # ) +# from .window import ( +# Scene as Scene, +# ShowManager as ShowManager, +# analyze_scene as analyze_scene, +# analyze_snapshot as analyze_snapshot, +# antialiasing as antialiasing, +# enable_stereo as enable_stereo, +# gl_disable_blend as gl_disable_blend, +# gl_disable_depth as gl_disable_depth, +# gl_enable_blend as gl_enable_blend, +# gl_enable_depth as gl_enable_depth, +# gl_get_current_state as gl_get_current_state, +# gl_reset_blend as gl_reset_blend, +# gl_set_additive_blending as gl_set_additive_blending, +# gl_set_additive_blending_white_background +# as gl_set_additive_blending_white_background, +# gl_set_multiplicative_blending as gl_set_multiplicative_blending, +# gl_set_normal_blending as gl_set_normal_blending, +# gl_set_subtractive_blending as gl_set_subtractive_blending, +# record as record, +# release_context as release_context, +# show as show, +# snapshot as snapshot, +# ) __version__: str -def disable_warnings(warnings_origin=...): ... -def enable_warnings(warnings_origin=...): ... -def get_info(verbose=False): ... +# def disable_warnings(warnings_origin=...): ... +# def enable_warnings(warnings_origin=...): ... +# def get_info(verbose=False): ... diff --git a/fury/animation/__init__.pyi b/fury/animation/__init__.pyi index daa5e4709..11d144e1e 100644 --- a/fury/animation/__init__.pyi +++ b/fury/animation/__init__.pyi @@ -4,7 +4,7 @@ __all__ = [ "Animation", "CameraAnimation", - "Timeline", + # "Timeline", "euclidean_distances", "get_next_timestamp", "get_previous_timestamp", @@ -48,4 +48,4 @@ from .interpolator import ( tan_cubic_spline_interpolator, xyz_color_interpolator, ) -from .timeline import Timeline +# from .timeline import Timeline diff --git a/fury/animation/timeline.py b/fury/animation/timeline.py index 17d342b93..a9e88e4e4 100644 --- a/fury/animation/timeline.py +++ b/fury/animation/timeline.py @@ -1,501 +1,502 @@ -import os -from time import perf_counter - -from PIL import Image -import numpy as np - -from fury import window -from fury.animation.animation import Animation -from fury.decorators import warn_on_args_to_kwargs -from fury.lib import RenderWindow, WindowToImageFilter, numpy_support -from fury.ui.elements import PlaybackPanel - - -class Timeline: - """Keyframe animation Timeline. - - Timeline is responsible for handling the playback of keyframes animations. - It has multiple playback options which makes it easy - to control the playback, speed, state of the animation with/without a GUI - playback panel. - - Attributes - ---------- - animations : Animation or list[Animation], optional, default: None - Actor/s to be animated directly by the Timeline (main Animation). - playback_panel : bool, optional - If True, the timeline will have a playback panel set, which can be used - to control the playback of the timeline. - length : float or int, default: None, optional - the fixed length of the timeline. If set to None, the timeline will get - its length from the animations that it controls automatically. - loop : bool, optional - Whether loop playing the timeline or play once. - - """ - - @warn_on_args_to_kwargs() - def __init__( - self, *, animations=None, playback_panel=False, loop=True, length=None - ): - self._scene = None - self.playback_panel = None - self._current_timestamp = 0 - self._speed = 1.0 - self._last_started_time = 0 - self._playing = False - self._animations = [] - self._loop = loop - self._length = length - self._duration = length if length is not None else 0.0 - - if playback_panel: - - def set_loop(is_loop): - self._loop = is_loop - - def set_speed(speed): - self.speed = speed - - self.playback_panel = PlaybackPanel(loop=self._loop) - self.playback_panel.on_play = self.play - self.playback_panel.on_stop = self.stop - self.playback_panel.on_pause = self.pause - self.playback_panel.on_loop_toggle = set_loop - self.playback_panel.on_progress_bar_changed = self.seek - self.playback_panel.on_speed_changed = set_speed - - if animations is not None: - self.add_animation(animations) - - def update_duration(self): - """Update and return the duration of the Timeline. - - Returns - ------- - float - The duration of the Timeline. - - """ - if self._length is not None: - self._duration = self._length - else: - self._duration = max( - [0.0] + [anim.update_duration() for anim in self._animations] - ) - if self.has_playback_panel: - self.playback_panel.final_time = self.duration - return self.duration - - @property - def duration(self): - """Return the duration of the Timeline. - - Returns - ------- - float - The duration of the Timeline. - - """ - return self._duration - - def play(self): - """Play the animation""" - if not self.playing: - if self.current_timestamp >= self.duration: - self.current_timestamp = 0 - self._last_started_time = ( - perf_counter() - self._current_timestamp / self.speed - ) - self._playing = True - - def pause(self): - """Pause the animation""" - self._current_timestamp = self.current_timestamp - self._playing = False - - def stop(self): - """Stop the animation""" - self._current_timestamp = 0 - self._playing = False - self.update(force=True) - - def restart(self): - """Restart the animation""" - self._current_timestamp = 0 - self._playing = True - self.update(force=True) - - @property - def current_timestamp(self): - """Get current timestamp of the Timeline. - - Returns - ------- - float - The current time of the Timeline. - - """ - if self.playing: - self._current_timestamp = ( - perf_counter() - self._last_started_time - ) * self.speed - return self._current_timestamp - - @current_timestamp.setter - def current_timestamp(self, timestamp): - """Set the current timestamp of the Timeline. - - Parameters - ---------- - timestamp: float - The time to set as current time of the Timeline. - - """ - self.seek(timestamp) - - def seek(self, timestamp): - """Set the current timestamp of the Timeline. - - Parameters - ---------- - timestamp: float - The time to seek. - - """ - # assuring timestamp value is in the timeline range - if timestamp < 0: - timestamp = 0 - elif timestamp > self.duration: - timestamp = self.duration - if self.playing: - self._last_started_time = perf_counter() - timestamp / self.speed - else: - self._current_timestamp = timestamp - self.update(force=True) - - def seek_percent(self, percent): - """Seek a percentage of the Timeline's final timestamp. - - Parameters - ---------- - percent: float - Value from 1 to 100. - - """ - t = percent * self.duration / 100 - self.seek(t) - - @property - def playing(self): - """Return whether the Timeline is playing. - - Returns - ------- - bool - True if the Timeline is playing. - - """ - return self._playing - - @property - def stopped(self): - """Return whether the Timeline is stopped. - - Returns - ------- - bool - True if Timeline is stopped. - - """ - return not self.playing and not self._current_timestamp - - @property - def paused(self): - """Return whether the Timeline is paused. - - Returns - ------- - bool - True if the Timeline is paused. - - """ - return not self.playing and self._current_timestamp is not None - - @property - def speed(self): - """Return the speed of the timeline's playback. - - Returns - ------- - float - The speed of the timeline's playback. - - """ - return self._speed - - @speed.setter - def speed(self, speed): - """Set the speed of the timeline's playback. - - Parameters - ---------- - speed: float - The speed of the timeline's playback. - - """ - current = self.current_timestamp - if speed <= 0: - return - self._speed = speed - self._last_started_time = perf_counter() - self.current_timestamp = current - - @property - def loop(self): - """Get loop condition of the timeline. - - Returns - ------- - bool - Whether the playback is in loop mode (True) or play one mode - (False). - - """ - return self._loop - - @loop.setter - def loop(self, loop): - """Set the timeline's playback to loop or play once. - - Parameters - ---------- - loop: bool - The loop condition to be set. (True) to loop the playback, and - (False) to play only once. - - """ - self._loop = loop - - @property - def has_playback_panel(self): - """Return whether the `Timeline` has a playback panel. - - Returns - ------- - bool: 'True' if the `Timeline` has a playback panel. otherwise, 'False' - - """ - return self.playback_panel is not None - - @warn_on_args_to_kwargs() - def record( - self, - *, - fname=None, - fps=30, - speed=1.0, - size=(900, 768), - order_transparent=True, - multi_samples=8, - max_peels=4, - show_panel=False, - ): - """Record the animation - - Parameters - ---------- - fname : str, optional - The file name. Save a GIF file if name ends with '.gif', or mp4 - video if name ends with'.mp4'. - If None, this method will only return an array of frames. - fps : int, optional - The number of frames per second of the record. - size : (int, int) - ``(width, height)`` of the window. Default is (900, 768). - speed : float, optional, default 1.0 - The speed of the animation. - order_transparent : bool, optional - Default False. Use depth peeling to sort transparent objects. - If True also enables anti-aliasing. - multi_samples : int, optional - Number of samples for anti-aliasing (Default 8). - For no anti-aliasing use 0. - max_peels : int, optional - Maximum number of peels for depth peeling (Default 4). - show_panel : bool, optional, default False - Controls whether to show the playback (if True) panel of hide it - (if False) - - Returns - ------- - ndarray: - The recorded frames. - - Notes - ----- - It's recommended to use 50 or 30 FPS while recording to a GIF file. - - """ - ext = os.path.splitext(fname)[-1] - - mp4 = ext == ".mp4" - - if mp4: - try: - import cv2 - except ImportError as err: - raise ImportError( - "OpenCV must be installed in order to " "save as MP4 video." - ) from err - fourcc = cv2.VideoWriter.fourcc(*"mp4v") - out = cv2.VideoWriter(fname, fourcc, fps, size) - - duration = self.duration - step = speed / fps - frames = [] - t = 0 - scene = self._scene - if not scene: - scene = window.Scene() - scene.add(self) - - _hide_panel = False - if self.has_playback_panel and not show_panel: - self.playback_panel.hide() - _hide_panel = True - render_window = RenderWindow() - render_window.SetOffScreenRendering(1) - render_window.AddRenderer(scene) - render_window.SetSize(*size) - - if order_transparent: - window.antialiasing(scene, render_window, multi_samples, max_peels, 0) - - render_window = RenderWindow() - render_window.SetOffScreenRendering(1) - render_window.AddRenderer(scene) - render_window.SetSize(*size) - - if order_transparent: - window.antialiasing(scene, render_window, multi_samples, max_peels, 0) - - window_to_image_filter = WindowToImageFilter() - - print("Recording...") - while t < duration: - self.seek(t) - render_window.Render() - window_to_image_filter.SetInput(render_window) - window_to_image_filter.Update() - window_to_image_filter.Modified() - vtk_image = window_to_image_filter.GetOutput() - h, w, _ = vtk_image.GetDimensions() - vtk_array = vtk_image.GetPointData().GetScalars() - components = vtk_array.GetNumberOfComponents() - snap = numpy_support.vtk_to_numpy(vtk_array).reshape(w, h, components) - corrected_snap = np.flipud(snap) - - if mp4: - cv_img = cv2.cvtColor(corrected_snap, cv2.COLOR_RGB2BGR) - out.write(cv_img) - else: - pillow_snap = Image.fromarray(corrected_snap) - frames.append(pillow_snap) - - t += step - - print("Saving...") - - if fname is None: - return frames - - if mp4: - out.release() - else: - frames[0].save( - fname, - append_images=frames[1:], - loop=0, - duration=1000 / fps, - save_all=True, - ) - - if _hide_panel: - self.playback_panel.show() - - return frames - - def add_animation(self, animation): - """Add Animation or list of Animations. - - Parameters - ---------- - animation: Animation or list[Animation] or tuple[Animation] - Animation/s to be added. - - """ - if isinstance(animation, (list, tuple)): - [self.add_animation(anim) for anim in animation] - elif isinstance(animation, Animation): - animation._timeline = self - self._animations.append(animation) - self.update_duration() - else: - raise TypeError("Expected an Animation, a list or a tuple.") - - @property - def animations(self) -> "list[Animation]": - """Return a list of Animations. - - Returns - ------- - list: - List of Animations controlled by the timeline. - - """ - return self._animations - - @warn_on_args_to_kwargs() - def update(self, *, force=False): - """Update the timeline. - - Update the Timeline and all the animations that it controls. As well as - the playback of the Timeline (if exists). - - Parameters - ---------- - force: bool, optional, default: False - If True, the timeline will update even when the timeline is paused - or stopped and hence, more resources will be used. - - """ - time = self.current_timestamp - if self.has_playback_panel: - self.playback_panel.current_time = time - if time > self.duration: - if self._loop: - self.seek(0) - else: - self.seek(self.duration) - # Doing this will pause both the timeline and the panel. - if self.has_playback_panel: - self.playback_panel.pause() - else: - self.pause() - if self.playing or force: - [anim.update_animation(time=time) for anim in self._animations] - - def add_to_scene(self, scene): - """Add Timeline and all of its Animations to the scene""" - self._scene = scene - if self.has_playback_panel: - self.playback_panel.add_to_scene(scene) - [animation.add_to_scene(scene) for animation in self._animations] - - def remove_from_scene(self, scene): - """Remove Timeline and all of its Animations to the scene""" - self._scene = None - if self.has_playback_panel: - scene.rm(*tuple(self.playback_panel.actors)) - [animation.remove_from_scene(scene) for animation in self._animations] +# import os +# from time import perf_counter + +# from PIL import Image +# import numpy as np + +# from fury import window +# from fury.animation.animation import Animation +# from fury.decorators import warn_on_args_to_kwargs + +# # from fury.lib import RenderWindow, WindowToImageFilter, numpy_support +# from fury.ui.elements import PlaybackPanel + + +# class Timeline: +# """Keyframe animation Timeline. + +# Timeline is responsible for handling the playback of keyframes animations. +# It has multiple playback options which makes it easy +# to control the playback, speed, state of the animation with/without a GUI +# playback panel. + +# Attributes +# ---------- +# animations : Animation or list[Animation], optional, default: None +# Actor/s to be animated directly by the Timeline (main Animation). +# playback_panel : bool, optional +# If True, the timeline will have a playback panel set, which can be used +# to control the playback of the timeline. +# length : float or int, default: None, optional +# the fixed length of the timeline. If set to None, the timeline will get +# its length from the animations that it controls automatically. +# loop : bool, optional +# Whether loop playing the timeline or play once. + +# """ + +# @warn_on_args_to_kwargs() +# def __init__( +# self, *, animations=None, playback_panel=False, loop=True, length=None +# ): +# self._scene = None +# self.playback_panel = None +# self._current_timestamp = 0 +# self._speed = 1.0 +# self._last_started_time = 0 +# self._playing = False +# self._animations = [] +# self._loop = loop +# self._length = length +# self._duration = length if length is not None else 0.0 + +# if playback_panel: + +# def set_loop(is_loop): +# self._loop = is_loop + +# def set_speed(speed): +# self.speed = speed + +# self.playback_panel = PlaybackPanel(loop=self._loop) +# self.playback_panel.on_play = self.play +# self.playback_panel.on_stop = self.stop +# self.playback_panel.on_pause = self.pause +# self.playback_panel.on_loop_toggle = set_loop +# self.playback_panel.on_progress_bar_changed = self.seek +# self.playback_panel.on_speed_changed = set_speed + +# if animations is not None: +# self.add_animation(animations) + +# def update_duration(self): +# """Update and return the duration of the Timeline. + +# Returns +# ------- +# float +# The duration of the Timeline. + +# """ +# if self._length is not None: +# self._duration = self._length +# else: +# self._duration = max( +# [0.0] + [anim.update_duration() for anim in self._animations] +# ) +# if self.has_playback_panel: +# self.playback_panel.final_time = self.duration +# return self.duration + +# @property +# def duration(self): +# """Return the duration of the Timeline. + +# Returns +# ------- +# float +# The duration of the Timeline. + +# """ +# return self._duration + +# def play(self): +# """Play the animation""" +# if not self.playing: +# if self.current_timestamp >= self.duration: +# self.current_timestamp = 0 +# self._last_started_time = ( +# perf_counter() - self._current_timestamp / self.speed +# ) +# self._playing = True + +# def pause(self): +# """Pause the animation""" +# self._current_timestamp = self.current_timestamp +# self._playing = False + +# def stop(self): +# """Stop the animation""" +# self._current_timestamp = 0 +# self._playing = False +# self.update(force=True) + +# def restart(self): +# """Restart the animation""" +# self._current_timestamp = 0 +# self._playing = True +# self.update(force=True) + +# @property +# def current_timestamp(self): +# """Get current timestamp of the Timeline. + +# Returns +# ------- +# float +# The current time of the Timeline. + +# """ +# if self.playing: +# self._current_timestamp = ( +# perf_counter() - self._last_started_time +# ) * self.speed +# return self._current_timestamp + +# @current_timestamp.setter +# def current_timestamp(self, timestamp): +# """Set the current timestamp of the Timeline. + +# Parameters +# ---------- +# timestamp: float +# The time to set as current time of the Timeline. + +# """ +# self.seek(timestamp) + +# def seek(self, timestamp): +# """Set the current timestamp of the Timeline. + +# Parameters +# ---------- +# timestamp: float +# The time to seek. + +# """ +# # assuring timestamp value is in the timeline range +# if timestamp < 0: +# timestamp = 0 +# elif timestamp > self.duration: +# timestamp = self.duration +# if self.playing: +# self._last_started_time = perf_counter() - timestamp / self.speed +# else: +# self._current_timestamp = timestamp +# self.update(force=True) + +# def seek_percent(self, percent): +# """Seek a percentage of the Timeline's final timestamp. + +# Parameters +# ---------- +# percent: float +# Value from 1 to 100. + +# """ +# t = percent * self.duration / 100 +# self.seek(t) + +# @property +# def playing(self): +# """Return whether the Timeline is playing. + +# Returns +# ------- +# bool +# True if the Timeline is playing. + +# """ +# return self._playing + +# @property +# def stopped(self): +# """Return whether the Timeline is stopped. + +# Returns +# ------- +# bool +# True if Timeline is stopped. + +# """ +# return not self.playing and not self._current_timestamp + +# @property +# def paused(self): +# """Return whether the Timeline is paused. + +# Returns +# ------- +# bool +# True if the Timeline is paused. + +# """ +# return not self.playing and self._current_timestamp is not None + +# @property +# def speed(self): +# """Return the speed of the timeline's playback. + +# Returns +# ------- +# float +# The speed of the timeline's playback. + +# """ +# return self._speed + +# @speed.setter +# def speed(self, speed): +# """Set the speed of the timeline's playback. + +# Parameters +# ---------- +# speed: float +# The speed of the timeline's playback. + +# """ +# current = self.current_timestamp +# if speed <= 0: +# return +# self._speed = speed +# self._last_started_time = perf_counter() +# self.current_timestamp = current + +# @property +# def loop(self): +# """Get loop condition of the timeline. + +# Returns +# ------- +# bool +# Whether the playback is in loop mode (True) or play one mode +# (False). + +# """ +# return self._loop + +# @loop.setter +# def loop(self, loop): +# """Set the timeline's playback to loop or play once. + +# Parameters +# ---------- +# loop: bool +# The loop condition to be set. (True) to loop the playback, and +# (False) to play only once. + +# """ +# self._loop = loop + +# @property +# def has_playback_panel(self): +# """Return whether the `Timeline` has a playback panel. + +# Returns +# ------- +# bool: 'True' if the `Timeline` has a playback panel. otherwise, 'False' + +# """ +# return self.playback_panel is not None + +# @warn_on_args_to_kwargs() +# def record( +# self, +# *, +# fname=None, +# fps=30, +# speed=1.0, +# size=(900, 768), +# order_transparent=True, +# multi_samples=8, +# max_peels=4, +# show_panel=False, +# ): +# """Record the animation + +# Parameters +# ---------- +# fname : str, optional +# The file name. Save a GIF file if name ends with '.gif', or mp4 +# video if name ends with'.mp4'. +# If None, this method will only return an array of frames. +# fps : int, optional +# The number of frames per second of the record. +# size : (int, int) +# ``(width, height)`` of the window. Default is (900, 768). +# speed : float, optional, default 1.0 +# The speed of the animation. +# order_transparent : bool, optional +# Default False. Use depth peeling to sort transparent objects. +# If True also enables anti-aliasing. +# multi_samples : int, optional +# Number of samples for anti-aliasing (Default 8). +# For no anti-aliasing use 0. +# max_peels : int, optional +# Maximum number of peels for depth peeling (Default 4). +# show_panel : bool, optional, default False +# Controls whether to show the playback (if True) panel of hide it +# (if False) + +# Returns +# ------- +# ndarray: +# The recorded frames. + +# Notes +# ----- +# It's recommended to use 50 or 30 FPS while recording to a GIF file. + +# """ +# ext = os.path.splitext(fname)[-1] + +# mp4 = ext == ".mp4" + +# if mp4: +# try: +# import cv2 +# except ImportError as err: +# raise ImportError( +# "OpenCV must be installed in order to " "save as MP4 video." +# ) from err +# fourcc = cv2.VideoWriter.fourcc(*"mp4v") +# out = cv2.VideoWriter(fname, fourcc, fps, size) + +# duration = self.duration +# step = speed / fps +# frames = [] +# t = 0 +# scene = self._scene +# if not scene: +# scene = window.Scene() +# scene.add(self) + +# _hide_panel = False +# if self.has_playback_panel and not show_panel: +# self.playback_panel.hide() +# _hide_panel = True +# render_window = RenderWindow() +# render_window.SetOffScreenRendering(1) +# render_window.AddRenderer(scene) +# render_window.SetSize(*size) + +# if order_transparent: +# window.antialiasing(scene, render_window, multi_samples, max_peels, 0) + +# render_window = RenderWindow() +# render_window.SetOffScreenRendering(1) +# render_window.AddRenderer(scene) +# render_window.SetSize(*size) + +# if order_transparent: +# window.antialiasing(scene, render_window, multi_samples, max_peels, 0) + +# window_to_image_filter = WindowToImageFilter() + +# print("Recording...") +# while t < duration: +# self.seek(t) +# render_window.Render() +# window_to_image_filter.SetInput(render_window) +# window_to_image_filter.Update() +# window_to_image_filter.Modified() +# vtk_image = window_to_image_filter.GetOutput() +# h, w, _ = vtk_image.GetDimensions() +# vtk_array = vtk_image.GetPointData().GetScalars() +# components = vtk_array.GetNumberOfComponents() +# snap = numpy_support.vtk_to_numpy(vtk_array).reshape(w, h, components) +# corrected_snap = np.flipud(snap) + +# if mp4: +# cv_img = cv2.cvtColor(corrected_snap, cv2.COLOR_RGB2BGR) +# out.write(cv_img) +# else: +# pillow_snap = Image.fromarray(corrected_snap) +# frames.append(pillow_snap) + +# t += step + +# print("Saving...") + +# if fname is None: +# return frames + +# if mp4: +# out.release() +# else: +# frames[0].save( +# fname, +# append_images=frames[1:], +# loop=0, +# duration=1000 / fps, +# save_all=True, +# ) + +# if _hide_panel: +# self.playback_panel.show() + +# return frames + +# def add_animation(self, animation): +# """Add Animation or list of Animations. + +# Parameters +# ---------- +# animation: Animation or list[Animation] or tuple[Animation] +# Animation/s to be added. + +# """ +# if isinstance(animation, (list, tuple)): +# [self.add_animation(anim) for anim in animation] +# elif isinstance(animation, Animation): +# animation._timeline = self +# self._animations.append(animation) +# self.update_duration() +# else: +# raise TypeError("Expected an Animation, a list or a tuple.") + +# @property +# def animations(self) -> "list[Animation]": +# """Return a list of Animations. + +# Returns +# ------- +# list: +# List of Animations controlled by the timeline. + +# """ +# return self._animations + +# @warn_on_args_to_kwargs() +# def update(self, *, force=False): +# """Update the timeline. + +# Update the Timeline and all the animations that it controls. As well as +# the playback of the Timeline (if exists). + +# Parameters +# ---------- +# force: bool, optional, default: False +# If True, the timeline will update even when the timeline is paused +# or stopped and hence, more resources will be used. + +# """ +# time = self.current_timestamp +# if self.has_playback_panel: +# self.playback_panel.current_time = time +# if time > self.duration: +# if self._loop: +# self.seek(0) +# else: +# self.seek(self.duration) +# # Doing this will pause both the timeline and the panel. +# if self.has_playback_panel: +# self.playback_panel.pause() +# else: +# self.pause() +# if self.playing or force: +# [anim.update_animation(time=time) for anim in self._animations] + +# def add_to_scene(self, scene): +# """Add Timeline and all of its Animations to the scene""" +# self._scene = scene +# if self.has_playback_panel: +# self.playback_panel.add_to_scene(scene) +# [animation.add_to_scene(scene) for animation in self._animations] + +# def remove_from_scene(self, scene): +# """Remove Timeline and all of its Animations to the scene""" +# self._scene = None +# if self.has_playback_panel: +# scene.rm(*tuple(self.playback_panel.actors)) +# [animation.remove_from_scene(scene) for animation in self._animations] diff --git a/fury/convert.py b/fury/convert.py index 3945375d7..a0ab610cb 100644 --- a/fury/convert.py +++ b/fury/convert.py @@ -1,69 +1,69 @@ -import os -from tempfile import TemporaryDirectory +# import os +# from tempfile import TemporaryDirectory -import numpy as np +# import numpy as np -from fury.decorators import warn_on_args_to_kwargs -from fury.io import load_image +# from fury.decorators import warn_on_args_to_kwargs +# # from fury.io import load_image -@warn_on_args_to_kwargs() -def matplotlib_figure_to_numpy( - fig, *, dpi=100, fname=None, flip_up_down=True, transparent=False -): - """Convert a Matplotlib figure to a 3D numpy array with RGBA channels. +# @warn_on_args_to_kwargs() +# def matplotlib_figure_to_numpy( +# fig, *, dpi=100, fname=None, flip_up_down=True, transparent=False +# ): +# """Convert a Matplotlib figure to a 3D numpy array with RGBA channels. - Parameters - ---------- - fig : obj - A matplotlib figure object - dpi : int, optional - Dots per inch - fname : str, optional - If ``fname`` is given then the array will be saved as a png to this - position. - flip_up_down : bool, optional - The origin is different from matlplotlib default and VTK's default - behaviour (default True). - transparent : bool, optional - Make background transparent (default False). +# Parameters +# ---------- +# fig : obj +# A matplotlib figure object +# dpi : int, optional +# Dots per inch +# fname : str, optional +# If ``fname`` is given then the array will be saved as a png to this +# position. +# flip_up_down : bool, optional +# The origin is different from matlplotlib default and VTK's default +# behaviour (default True). +# transparent : bool, optional +# Make background transparent (default False). - Returns - ------- - arr : ndarray - a numpy 3D array of RGBA values +# Returns +# ------- +# arr : ndarray +# a numpy 3D array of RGBA values - Notes - ----- - The safest way to read the pixel values from the figure was to save them - using savefig as a png and then read again the png. There is a cleaner - way found here http://www.icare.univ-lille1.fr/drupal/node/1141 where - you can actually use fig.canvas.tostring_argb() to get the values directly - without saving to the disk. However, this was not stable across different - machines and needed more investigation from what time permitted. +# Notes +# ----- +# The safest way to read the pixel values from the figure was to save them +# using savefig as a png and then read again the png. There is a cleaner +# way found here http://www.icare.univ-lille1.fr/drupal/node/1141 where +# you can actually use fig.canvas.tostring_argb() to get the values directly +# without saving to the disk. However, this was not stable across different +# machines and needed more investigation from what time permitted. - """ - if fname is None: - with TemporaryDirectory() as tmpdir: - fname = os.path.join(tmpdir, "tmp.png") - fig.savefig( - fname, - dpi=dpi, - transparent=transparent, - bbox_inches="tight", - pad_inches=0, - ) - arr = load_image(fname) - else: - fig.savefig( - fname, - dpi=dpi, - transparent=transparent, - bbox_inches="tight", - pad_inches=0, - ) - arr = load_image(fname) +# """ +# if fname is None: +# with TemporaryDirectory() as tmpdir: +# fname = os.path.join(tmpdir, "tmp.png") +# fig.savefig( +# fname, +# dpi=dpi, +# transparent=transparent, +# bbox_inches="tight", +# pad_inches=0, +# ) +# arr = load_image(fname) +# else: +# fig.savefig( +# fname, +# dpi=dpi, +# transparent=transparent, +# bbox_inches="tight", +# pad_inches=0, +# ) +# arr = load_image(fname) - if flip_up_down: - arr = np.flipud(arr) - return arr +# if flip_up_down: +# arr = np.flipud(arr) +# return arr diff --git a/fury/data/__init__.py b/fury/data/__init__.py index 0eb5c77fc..5b335f0de 100644 --- a/fury/data/__init__.py +++ b/fury/data/__init__.py @@ -1,6 +1,5 @@ """Read or fetch test or example data.""" - from os.path import dirname, join as pjoin import lazy_loader as lazy diff --git a/fury/data/fetcher.py b/fury/data/fetcher.py index f44bd0bf1..091eccc72 100644 --- a/fury/data/fetcher.py +++ b/fury/data/fetcher.py @@ -567,7 +567,7 @@ def fetch_gltf(*, name=None, mode="glTF"): MODEL_DATA_URL, ["utah.obj", "suzanne.obj", "satellite_obj.obj", "dragon.obj"], ["utah.obj", "suzanne.obj", "satellite_obj.obj", "dragon.obj"], - sha_list= [ + sha_list=[ "0B50F12CEDCDC27377AC702B1EE331223BECEC59593B3F00A9E06B57A9C1B7C3", "BB4FF4E65D65D71D53000E06D2DC7BF89B702223657C1F64748811A3A6C8D621", "90213FAC81D89BBB59FA541643304E0D95C2D446157ACE044D46F259454C0E74", diff --git a/fury/io.py b/fury/io.py index 8f2900014..485ce5819 100644 --- a/fury/io.py +++ b/fury/io.py @@ -1,488 +1,488 @@ -import os -from tempfile import TemporaryDirectory as InTemporaryDirectory -from urllib.request import urlretrieve -import warnings - -from PIL import Image -import numpy as np - -from fury.decorators import warn_on_args_to_kwargs -from fury.lib import ( - BMPReader, - BMPWriter, - ImageData, - ImageFlip, - JPEGReader, - JPEGWriter, - MNIObjectReader, - MNIObjectWriter, - OBJReader, - PLYReader, - PLYWriter, - PNGReader, - PNGWriter, - PolyDataReader, - PolyDataWriter, - STLReader, - STLWriter, - TIFFReader, - TIFFWriter, - Texture, - XMLPolyDataReader, - XMLPolyDataWriter, - numpy_support, -) -from fury.utils import set_input - - -@warn_on_args_to_kwargs() -def load_cubemap_texture(fnames, *, interpolate_on=True, mipmap_on=True): - """Load a cube map texture from a list of 6 images. - - Parameters - ---------- - fnames : list of strings - List of 6 filenames with bmp, jpg, jpeg, png, tif or tiff extensions. - interpolate_on : bool, optional - mipmap_on : bool, optional - - Returns - ------- - output : vtkTexture - Cube map texture. - - """ - if len(fnames) != 6: - raise IOError("Expected 6 filenames, got {}".format(len(fnames))) - texture = Texture() - texture.CubeMapOn() - for idx, fn in enumerate(fnames): - if not os.path.isfile(fn): - raise FileNotFoundError(fn) - else: - # Read the images - vtk_img = load_image(fn, as_vtktype=True) - # Flip the image horizontally - img_flip = ImageFlip() - img_flip.SetInputData(vtk_img) - img_flip.SetFilteredAxis(1) # flip y axis - img_flip.Update() - # Add the image to the cube map - texture.SetInputDataObject(idx, img_flip.GetOutput()) - if interpolate_on: - texture.InterpolateOn() - if mipmap_on: - texture.MipmapOn() - return texture - - -@warn_on_args_to_kwargs() -def load_image(filename, *, as_vtktype=False, use_pillow=True): - """Load an image. - - Parameters - ---------- - filename: str - should be png, bmp, jpeg or jpg files - as_vtktype: bool, optional - if True, return vtk output otherwise an ndarray. Default False. - use_pillow: bool, optional - Use pillow python library to load the files. Default True - - Returns - ------- - image: ndarray or vtk output - desired image array - - """ - is_url = (filename.lower().startswith("http://")) or ( - filename.lower().startswith("https://") - ) - - if is_url: - image_name = os.path.basename(filename) - - if len(image_name.split(".")) < 2: - raise IOError(f"{filename} is not a valid image URL") - - urlretrieve(filename, image_name) - filename = image_name - - if use_pillow: - with Image.open(filename) as pil_image: - if pil_image.mode in ["P"]: - pil_image = pil_image.convert("RGB") - - if pil_image.mode in ["RGBA", "RGB", "L"]: - image = np.asarray(pil_image) - elif pil_image.mode.startswith("I;16"): - raw = pil_image.tobytes("raw", pil_image.mode) - dtype = ">u2" if pil_image.mode.endswith("B") else " 3: - raise IOError("Image Dimensions should be <=3") - - if isinstance(dpi, (float, int)): - dpi = (dpi, dpi) - - d_writer = { - ".png": PNGWriter, - ".bmp": BMPWriter, - ".jpeg": JPEGWriter, - ".jpg": JPEGWriter, - ".tiff": TIFFWriter, - ".tif": TIFFWriter, - } - - extension = os.path.splitext(os.path.basename(filename).lower())[1] - - if extension.lower() not in d_writer.keys(): - raise IOError( - "Impossible to save the file {0}: Unknown extension {1}".format( - filename, extension - ) - ) - - if use_pillow: - im = Image.fromarray(arr) - im.save(filename, quality=compression_quality, dpi=dpi) - else: - warnings.warn( - UserWarning("DPI value is ignored while saving images via vtk."), - stacklevel=2, - ) - if arr.ndim == 2: - arr = arr[..., None] - - shape = arr.shape - arr = np.flipud(arr) - if extension.lower() in [ - ".png", - ]: - arr = arr.astype(np.uint8) - arr = arr.reshape((shape[1] * shape[0], shape[2])) - arr = np.ascontiguousarray(arr, dtype=arr.dtype) - vtk_array_type = numpy_support.get_vtk_array_type(arr.dtype) - vtk_array = numpy_support.numpy_to_vtk( - num_array=arr, deep=True, array_type=vtk_array_type - ) - - # Todo, look the following link for managing png 16bit - # https://stackoverflow.com/questions/15667947/vtkpngwriter-printing-out-black-images - vtk_data = ImageData() - vtk_data.SetDimensions(shape[1], shape[0], shape[2]) - vtk_data.SetExtent(0, shape[1] - 1, 0, shape[0] - 1, 0, 0) - vtk_data.SetSpacing(1.0, 1.0, 1.0) - vtk_data.SetOrigin(0.0, 0.0, 0.0) - vtk_data.GetPointData().SetScalars(vtk_array) - - writer = d_writer.get(extension)() - writer.SetFileName(filename) - writer.SetInputData(vtk_data) - if extension.lower() in [".jpg", ".jpeg"]: - writer.ProgressiveOn() - writer.SetQuality(compression_quality) - if extension.lower() in [".tif", ".tiff"]: - compression_type = compression_type or "nocompression" - l_compression = [ - "nocompression", - "packbits", - "jpeg", - "deflate", - "lzw", - ] - - if compression_type.lower() in l_compression: - comp_id = l_compression.index(compression_type.lower()) - writer.SetCompression(comp_id) - else: - writer.SetCompressionToDeflate() - writer.Write() - - -def load_polydata(file_name): - """Load a vtk polydata to a supported format file. - - Supported file formats are VTK, VTP, FIB, PLY, STL XML and OBJ - - Parameters - ---------- - file_name : string - - Returns - ------- - output : vtkPolyData - - """ - # Check if file actually exists - if not os.path.isfile(file_name): - raise FileNotFoundError(file_name) - - file_extension = file_name.split(".")[-1].lower() - - poly_reader = { - "vtk": PolyDataReader, - "vtp": XMLPolyDataReader, - "fib": PolyDataReader, - "ply": PLYReader, - "stl": STLReader, - "xml": XMLPolyDataReader, - } - - if file_extension in poly_reader.keys(): - reader = poly_reader.get(file_extension)() - elif file_extension == "obj": - # Special case, since there is two obj format - reader = OBJReader() - reader.SetFileName(file_name) - reader.Update() - if reader.GetOutput().GetNumberOfCells() == 0: - reader = MNIObjectReader() - else: - raise IOError("." + file_extension + " is not supported by FURY") - - reader.SetFileName(file_name) - reader.Update() - return reader.GetOutput() - - -@warn_on_args_to_kwargs() -def save_polydata(polydata, file_name, *, binary=False, color_array_name=None): - """Save a vtk polydata to a supported format file. - - Save formats can be VTK, FIB, PLY, STL and XML. - - Parameters - ---------- - polydata : vtkPolyData - file_name : string - binary : bool - color_array_name: ndarray - - """ - # get file extension (type) - file_extension = file_name.split(".")[-1].lower() - poly_writer = { - "vtk": PolyDataWriter, - "vtp": XMLPolyDataWriter, - "fib": PolyDataWriter, - "ply": PLYWriter, - "stl": STLWriter, - "xml": XMLPolyDataWriter, - } - - if file_extension in poly_writer.keys(): - writer = poly_writer.get(file_extension)() - elif file_extension == "obj": - # Special case, since there is two obj format - find_keyword = file_name.lower().split(".") - if "mni" in find_keyword or "mnc" in find_keyword: - writer = MNIObjectWriter() - else: - raise IOError( - "Wavefront obj requires a scene \n" - " for MNI obj, use '.mni.obj' extension" - ) - else: - raise IOError("." + file_extension + " is not supported by FURY") - - writer.SetFileName(file_name) - writer = set_input(writer, polydata) - if color_array_name is not None and file_extension == "ply": - writer.SetArrayName(color_array_name) - - if binary: - writer.SetFileTypeToBinary() - writer.Update() - writer.Write() - - -@warn_on_args_to_kwargs() -def load_sprite_sheet(sheet_path, nb_rows, nb_cols, *, as_vtktype=False): - """Process and load sprites from a sprite sheet. - - Parameters - ---------- - sheet_path: str - Path to the sprite sheet - nb_rows: int - Number of rows in the sprite sheet - nb_cols: int - Number of columns in the sprite sheet - as_vtktype: bool, optional - If True, the output is a vtkImageData - - Returns - ------- - Dict containing the processed sprites. - - """ - sprite_dicts = {} - sprite_sheet = load_image(sheet_path) - width, height = sprite_sheet.shape[:2] - - sprite_size_x = int(np.ceil(width / nb_rows)) - sprite_size_y = int(np.ceil(height / nb_cols)) - - for row, col in np.ndindex((nb_rows, nb_cols)): - nxt_row = row + 1 - nxt_col = col + 1 - - box = ( - row * sprite_size_x, - col * sprite_size_y, - nxt_row * sprite_size_x, - nxt_col * sprite_size_y, - ) - - sprite_arr = sprite_sheet[ - box[0] : box[2], box[1] : box[3] # noqa: E203 - ] - if as_vtktype: - with InTemporaryDirectory() as tdir: - tmp_img_path = os.path.join(tdir, f"{row}{col}.png") - save_image(sprite_arr, tmp_img_path, compression_quality=100) - - sprite_dicts[(row, col)] = load_image( - tmp_img_path, - as_vtktype=True, - ) - else: - sprite_dicts[(row, col)] = sprite_arr - - return sprite_dicts +# import os +# from tempfile import TemporaryDirectory as InTemporaryDirectory +# from urllib.request import urlretrieve +# import warnings + +# from PIL import Image +# import numpy as np + +# from fury.decorators import warn_on_args_to_kwargs +# from fury.lib import ( +# BMPReader, +# BMPWriter, +# ImageData, +# ImageFlip, +# JPEGReader, +# JPEGWriter, +# MNIObjectReader, +# MNIObjectWriter, +# OBJReader, +# PLYReader, +# PLYWriter, +# PNGReader, +# PNGWriter, +# PolyDataReader, +# PolyDataWriter, +# STLReader, +# STLWriter, +# TIFFReader, +# TIFFWriter, +# Texture, +# XMLPolyDataReader, +# XMLPolyDataWriter, +# numpy_support, +# ) +# from fury.utils import set_input + + +# @warn_on_args_to_kwargs() +# def load_cubemap_texture(fnames, *, interpolate_on=True, mipmap_on=True): +# """Load a cube map texture from a list of 6 images. + +# Parameters +# ---------- +# fnames : list of strings +# List of 6 filenames with bmp, jpg, jpeg, png, tif or tiff extensions. +# interpolate_on : bool, optional +# mipmap_on : bool, optional + +# Returns +# ------- +# output : vtkTexture +# Cube map texture. + +# """ +# if len(fnames) != 6: +# raise IOError("Expected 6 filenames, got {}".format(len(fnames))) +# texture = Texture() +# texture.CubeMapOn() +# for idx, fn in enumerate(fnames): +# if not os.path.isfile(fn): +# raise FileNotFoundError(fn) +# else: +# # Read the images +# vtk_img = load_image(fn, as_vtktype=True) +# # Flip the image horizontally +# img_flip = ImageFlip() +# img_flip.SetInputData(vtk_img) +# img_flip.SetFilteredAxis(1) # flip y axis +# img_flip.Update() +# # Add the image to the cube map +# texture.SetInputDataObject(idx, img_flip.GetOutput()) +# if interpolate_on: +# texture.InterpolateOn() +# if mipmap_on: +# texture.MipmapOn() +# return texture + + +# @warn_on_args_to_kwargs() +# def load_image(filename, *, as_vtktype=False, use_pillow=True): +# """Load an image. + +# Parameters +# ---------- +# filename: str +# should be png, bmp, jpeg or jpg files +# as_vtktype: bool, optional +# if True, return vtk output otherwise an ndarray. Default False. +# use_pillow: bool, optional +# Use pillow python library to load the files. Default True + +# Returns +# ------- +# image: ndarray or vtk output +# desired image array + +# """ +# is_url = (filename.lower().startswith("http://")) or ( +# filename.lower().startswith("https://") +# ) + +# if is_url: +# image_name = os.path.basename(filename) + +# if len(image_name.split(".")) < 2: +# raise IOError(f"{filename} is not a valid image URL") + +# urlretrieve(filename, image_name) +# filename = image_name + +# if use_pillow: +# with Image.open(filename) as pil_image: +# if pil_image.mode in ["P"]: +# pil_image = pil_image.convert("RGB") + +# if pil_image.mode in ["RGBA", "RGB", "L"]: +# image = np.asarray(pil_image) +# elif pil_image.mode.startswith("I;16"): +# raw = pil_image.tobytes("raw", pil_image.mode) +# dtype = ">u2" if pil_image.mode.endswith("B") else " 3: +# raise IOError("Image Dimensions should be <=3") + +# if isinstance(dpi, (float, int)): +# dpi = (dpi, dpi) + +# d_writer = { +# ".png": PNGWriter, +# ".bmp": BMPWriter, +# ".jpeg": JPEGWriter, +# ".jpg": JPEGWriter, +# ".tiff": TIFFWriter, +# ".tif": TIFFWriter, +# } + +# extension = os.path.splitext(os.path.basename(filename).lower())[1] + +# if extension.lower() not in d_writer.keys(): +# raise IOError( +# "Impossible to save the file {0}: Unknown extension {1}".format( +# filename, extension +# ) +# ) + +# if use_pillow: +# im = Image.fromarray(arr) +# im.save(filename, quality=compression_quality, dpi=dpi) +# else: +# warnings.warn( +# UserWarning("DPI value is ignored while saving images via vtk."), +# stacklevel=2, +# ) +# if arr.ndim == 2: +# arr = arr[..., None] + +# shape = arr.shape +# arr = np.flipud(arr) +# if extension.lower() in [ +# ".png", +# ]: +# arr = arr.astype(np.uint8) +# arr = arr.reshape((shape[1] * shape[0], shape[2])) +# arr = np.ascontiguousarray(arr, dtype=arr.dtype) +# vtk_array_type = numpy_support.get_vtk_array_type(arr.dtype) +# vtk_array = numpy_support.numpy_to_vtk( +# num_array=arr, deep=True, array_type=vtk_array_type +# ) + +# # Todo, look the following link for managing png 16bit +# # https://stackoverflow.com/questions/15667947/vtkpngwriter-printing-out-black-images +# vtk_data = ImageData() +# vtk_data.SetDimensions(shape[1], shape[0], shape[2]) +# vtk_data.SetExtent(0, shape[1] - 1, 0, shape[0] - 1, 0, 0) +# vtk_data.SetSpacing(1.0, 1.0, 1.0) +# vtk_data.SetOrigin(0.0, 0.0, 0.0) +# vtk_data.GetPointData().SetScalars(vtk_array) + +# writer = d_writer.get(extension)() +# writer.SetFileName(filename) +# writer.SetInputData(vtk_data) +# if extension.lower() in [".jpg", ".jpeg"]: +# writer.ProgressiveOn() +# writer.SetQuality(compression_quality) +# if extension.lower() in [".tif", ".tiff"]: +# compression_type = compression_type or "nocompression" +# l_compression = [ +# "nocompression", +# "packbits", +# "jpeg", +# "deflate", +# "lzw", +# ] + +# if compression_type.lower() in l_compression: +# comp_id = l_compression.index(compression_type.lower()) +# writer.SetCompression(comp_id) +# else: +# writer.SetCompressionToDeflate() +# writer.Write() + + +# def load_polydata(file_name): +# """Load a vtk polydata to a supported format file. + +# Supported file formats are VTK, VTP, FIB, PLY, STL XML and OBJ + +# Parameters +# ---------- +# file_name : string + +# Returns +# ------- +# output : vtkPolyData + +# """ +# # Check if file actually exists +# if not os.path.isfile(file_name): +# raise FileNotFoundError(file_name) + +# file_extension = file_name.split(".")[-1].lower() + +# poly_reader = { +# "vtk": PolyDataReader, +# "vtp": XMLPolyDataReader, +# "fib": PolyDataReader, +# "ply": PLYReader, +# "stl": STLReader, +# "xml": XMLPolyDataReader, +# } + +# if file_extension in poly_reader.keys(): +# reader = poly_reader.get(file_extension)() +# elif file_extension == "obj": +# # Special case, since there is two obj format +# reader = OBJReader() +# reader.SetFileName(file_name) +# reader.Update() +# if reader.GetOutput().GetNumberOfCells() == 0: +# reader = MNIObjectReader() +# else: +# raise IOError("." + file_extension + " is not supported by FURY") + +# reader.SetFileName(file_name) +# reader.Update() +# return reader.GetOutput() + + +# @warn_on_args_to_kwargs() +# def save_polydata(polydata, file_name, *, binary=False, color_array_name=None): +# """Save a vtk polydata to a supported format file. + +# Save formats can be VTK, FIB, PLY, STL and XML. + +# Parameters +# ---------- +# polydata : vtkPolyData +# file_name : string +# binary : bool +# color_array_name: ndarray + +# """ +# # get file extension (type) +# file_extension = file_name.split(".")[-1].lower() +# poly_writer = { +# "vtk": PolyDataWriter, +# "vtp": XMLPolyDataWriter, +# "fib": PolyDataWriter, +# "ply": PLYWriter, +# "stl": STLWriter, +# "xml": XMLPolyDataWriter, +# } + +# if file_extension in poly_writer.keys(): +# writer = poly_writer.get(file_extension)() +# elif file_extension == "obj": +# # Special case, since there is two obj format +# find_keyword = file_name.lower().split(".") +# if "mni" in find_keyword or "mnc" in find_keyword: +# writer = MNIObjectWriter() +# else: +# raise IOError( +# "Wavefront obj requires a scene \n" +# " for MNI obj, use '.mni.obj' extension" +# ) +# else: +# raise IOError("." + file_extension + " is not supported by FURY") + +# writer.SetFileName(file_name) +# writer = set_input(writer, polydata) +# if color_array_name is not None and file_extension == "ply": +# writer.SetArrayName(color_array_name) + +# if binary: +# writer.SetFileTypeToBinary() +# writer.Update() +# writer.Write() + + +# @warn_on_args_to_kwargs() +# def load_sprite_sheet(sheet_path, nb_rows, nb_cols, *, as_vtktype=False): +# """Process and load sprites from a sprite sheet. + +# Parameters +# ---------- +# sheet_path: str +# Path to the sprite sheet +# nb_rows: int +# Number of rows in the sprite sheet +# nb_cols: int +# Number of columns in the sprite sheet +# as_vtktype: bool, optional +# If True, the output is a vtkImageData + +# Returns +# ------- +# Dict containing the processed sprites. + +# """ +# sprite_dicts = {} +# sprite_sheet = load_image(sheet_path) +# width, height = sprite_sheet.shape[:2] + +# sprite_size_x = int(np.ceil(width / nb_rows)) +# sprite_size_y = int(np.ceil(height / nb_cols)) + +# for row, col in np.ndindex((nb_rows, nb_cols)): +# nxt_row = row + 1 +# nxt_col = col + 1 + +# box = ( +# row * sprite_size_x, +# col * sprite_size_y, +# nxt_row * sprite_size_x, +# nxt_col * sprite_size_y, +# ) + +# sprite_arr = sprite_sheet[ +# box[0] : box[2], box[1] : box[3] # noqa: E203 +# ] +# if as_vtktype: +# with InTemporaryDirectory() as tdir: +# tmp_img_path = os.path.join(tdir, f"{row}{col}.png") +# save_image(sprite_arr, tmp_img_path, compression_quality=100) + +# sprite_dicts[(row, col)] = load_image( +# tmp_img_path, +# as_vtktype=True, +# ) +# else: +# sprite_dicts[(row, col)] = sprite_arr + +# return sprite_dicts diff --git a/fury/io/__init__.py b/fury/io/__init__.py index 62d86f776..adfb48395 100644 --- a/fury/io/__init__.py +++ b/fury/io/__init__.py @@ -1,3 +1,3 @@ -import lazy_loader as lazy +# import lazy_loader as lazy -__getattr__, __dir__, __all__ = lazy.attach_stub(__name__, __file__) +# __getattr__, __dir__, __all__ = lazy.attach_stub(__name__, __file__) diff --git a/fury/io/__init__.pyi b/fury/io/__init__.pyi index 3fa37cf58..fb277dc54 100644 --- a/fury/io/__init__.pyi +++ b/fury/io/__init__.pyi @@ -1,5 +1,3 @@ -__all__ = [ - "load_cube_map_texture" -] +# __all__ = [""] -from ._texture import load_cube_map_texture +# from ._texture import load_cube_map_texture diff --git a/fury/io/_texture.py b/fury/io/_texture.py index f20466b35..97f5e84bf 100644 --- a/fury/io/_texture.py +++ b/fury/io/_texture.py @@ -1,24 +1,24 @@ -from numpy import stack as np_stack -from pygfx import Texture +# from numpy import stack as np_stack +# from pygfx import Texture -from fury.decorators import warn_on_args_to_kwargs -from fury.io import load_image +# from fury.decorators import warn_on_args_to_kwargs +# # from fury.io import load_image -@warn_on_args_to_kwargs() -def load_cube_map_texture(fnames, *, size=None, generate_mipmaps=True): - images = [] - for fname in fnames: - images.append(load_image(fname)) +# @warn_on_args_to_kwargs() +# def load_cube_map_texture(fnames, *, size=None, generate_mipmaps=True): +# images = [] - if size is None: - min_side = min(*images[0].shape[:2]) - for image in images: - min_side = min(*image.shape[:2]) - size = (min_side, min_side, 6) +# for fname in fnames: +# # images.append(load_image(fname)) - data = np_stack(images, axis=0) +# if size is None: +# min_side = min(*images[0].shape[:2]) +# for image in images: +# min_side = min(*image.shape[:2]) +# size = (min_side, min_side, 6) +# data = np_stack(images, axis=0) - return Texture(data, dim=2, size=size, generate_mipmaps=generate_mipmaps) +# return Texture(data, dim=2, size=size, generate_mipmaps=generate_mipmaps) diff --git a/fury/lib.py b/fury/lib.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/fury/material.py b/fury/material.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/fury/ui/__init__.pyi b/fury/ui/__init__.pyi index 52bd7c956..8ac12a5c1 100644 --- a/fury/ui/__init__.pyi +++ b/fury/ui/__init__.pyi @@ -1,69 +1,69 @@ -__all__ = [ - "Panel2D", - "TabPanel2D", - "TabUI", - "ImageContainer2D", - "GridUI", - "Rectangle2D", - "Disk2D", - "TextBlock2D", - "Button2D", - "TextBox2D", - "LineSlider2D", - "LineDoubleSlider2D", - "RingSlider2D", - "RangeSlider", - "Checkbox", - "Option", - "RadioButton", - "ComboBox2D", - "ListBox2D", - "ListBoxItem2D", - "FileMenu2D", - "DrawShape", - "DrawPanel", - "PlaybackPanel", - "Card2D", - "SpinBox", - "UI", - "cal_bounding_box_2d", - "check_overflow", - "clip_overflow", - "rotate_2d", - "wrap_overflow", -] +# __all__ = [ +# "Panel2D", +# "TabPanel2D", +# "TabUI", +# "ImageContainer2D", +# "GridUI", +# "Rectangle2D", +# "Disk2D", +# "TextBlock2D", +# "Button2D", +# "TextBox2D", +# "LineSlider2D", +# "LineDoubleSlider2D", +# "RingSlider2D", +# "RangeSlider", +# "Checkbox", +# "Option", +# "RadioButton", +# "ComboBox2D", +# "ListBox2D", +# "ListBoxItem2D", +# "FileMenu2D", +# "DrawShape", +# "DrawPanel", +# "PlaybackPanel", +# "Card2D", +# "SpinBox", +# "UI", +# "cal_bounding_box_2d", +# "check_overflow", +# "clip_overflow", +# "rotate_2d", +# "wrap_overflow", +# ] -from . import ( - containers as containers, - core as core, - elements as elements, - helpers as helpers, -) -from .containers import GridUI, ImageContainer2D, Panel2D, TabPanel2D, TabUI -from .core import UI, Button2D, Disk2D, Rectangle2D, TextBlock2D -from .elements import ( - Card2D, - Checkbox, - ComboBox2D, - DrawPanel, - DrawShape, - FileMenu2D, - LineDoubleSlider2D, - LineSlider2D, - ListBox2D, - ListBoxItem2D, - Option, - PlaybackPanel, - RadioButton, - RangeSlider, - RingSlider2D, - SpinBox, - TextBox2D, -) -from .helpers import ( - cal_bounding_box_2d, - check_overflow, - clip_overflow, - rotate_2d, - wrap_overflow, -) +# from . import ( +# containers as containers, +# core as core, +# elements as elements, +# helpers as helpers, +# ) +# from .containers import GridUI, ImageContainer2D, Panel2D, TabPanel2D, TabUI +# from .core import UI, Button2D, Disk2D, Rectangle2D, TextBlock2D +# from .elements import ( +# Card2D, +# Checkbox, +# ComboBox2D, +# DrawPanel, +# DrawShape, +# FileMenu2D, +# LineDoubleSlider2D, +# LineSlider2D, +# ListBox2D, +# ListBoxItem2D, +# Option, +# PlaybackPanel, +# RadioButton, +# RangeSlider, +# RingSlider2D, +# SpinBox, +# TextBox2D, +# ) +# from .helpers import ( +# cal_bounding_box_2d, +# check_overflow, +# clip_overflow, +# rotate_2d, +# wrap_overflow, +# ) diff --git a/fury/ui/containers.py b/fury/ui/containers.py index 3f1d4f0fe..efa799f98 100644 --- a/fury/ui/containers.py +++ b/fury/ui/containers.py @@ -1,1234 +1,1239 @@ -"""UI container module.""" - -from warnings import warn - -import numpy as np - -from fury.actor import grid -from fury.decorators import warn_on_args_to_kwargs -from fury.io import load_image -from fury.lib import ( - CellArray, - FloatArray, - Points, - PolyData, - PolyDataMapper2D, - Property2D, - Texture, - TexturedActor2D, -) -from fury.ui.core import UI, Rectangle2D, TextBlock2D -from fury.utils import rotate, set_input - - -class Panel2D(UI): - """A 2D UI Panel. - - Can contain one or more UI elements. - - Attributes - ---------- - alignment : [left, right] - Alignment of the panel with respect to the overall screen. - - """ - - @warn_on_args_to_kwargs() - def __init__( - self, - size, - *, - position=(0, 0), - color=(0.1, 0.1, 0.1), - opacity=0.7, - align="left", - border_color=(1, 1, 1), - border_width=0, - has_border=False, - ): - """Init class instance. - - Parameters - ---------- - size : (int, int) - Size (width, height) in pixels of the panel. - position : (float, float) - Absolute coordinates (x, y) of the lower-left corner of the panel. - color : (float, float, float) - Must take values in [0, 1]. - opacity : float - Must take values in [0, 1]. - align : [left, right] - Alignment of the panel with respect to the overall screen. - border_color: (float, float, float), optional - Must take values in [0, 1]. - border_width: float, optional - width of the border - has_border: bool, optional - If the panel should have borders. - - """ - self.has_border = has_border - self._border_color = border_color - self._border_width = border_width - super(Panel2D, self).__init__(position=position) - self.resize(size) - self.alignment = align - self.color = color - self.opacity = opacity - self.position = position - self._drag_offset = None - - def _setup(self): - """Setup this UI component. - - Create the background (Rectangle2D) of the panel. - Create the borders (Rectangle2D) of the panel. - """ - self._elements = [] - self.element_offsets = [] - self.background = Rectangle2D() - - if self.has_border: - self.borders = { - "left": Rectangle2D(), - "right": Rectangle2D(), - "top": Rectangle2D(), - "bottom": Rectangle2D(), - } - - self.border_coords = { - "left": (0.0, 0.0), - "right": (1.0, 0.0), - "top": (0.0, 1.0), - "bottom": (0.0, 0.0), - } - - for key in self.borders.keys(): - self.borders[key].color = self._border_color - self.add_element(self.borders[key], self.border_coords[key]) - - for key in self.borders.keys(): - self.borders[ - key - ].on_left_mouse_button_pressed = self.left_button_pressed - - self.borders[ - key - ].on_left_mouse_button_dragged = self.left_button_dragged - - self.add_element(self.background, (0, 0)) - - # Add default events listener for this UI component. - self.background.on_left_mouse_button_pressed = self.left_button_pressed - self.background.on_left_mouse_button_dragged = self.left_button_dragged - - def _get_actors(self): - """Get the actors composing this UI component.""" - actors = [] - for element in self._elements: - actors += element.actors - - return actors - - def _add_to_scene(self, scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - scene : scene - - """ - for element in self._elements: - element.add_to_scene(scene) - - def _get_size(self): - return self.background.size - - def resize(self, size): - """Set the panel size. - - Parameters - ---------- - size : (float, float) - Panel size (width, height) in pixels. - - """ - self.background.resize(size) - - if self.has_border: - self.borders["left"].resize( - (self._border_width, size[1] + self._border_width) - ) - - self.borders["right"].resize( - (self._border_width, size[1] + self._border_width) - ) - - self.borders["top"].resize( - (self.size[0] + self._border_width, self._border_width) - ) - - self.borders["bottom"].resize( - (self.size[0] + self._border_width, self._border_width) - ) - - self.update_border_coords() - - def _set_position(self, coords): - """Set the lower-left corner position of this UI component. - - Parameters - ---------- - coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - coords = np.array(coords) - for element, offset in self.element_offsets: - element.position = coords + offset - - def set_visibility(self, visibility): - for element in self._elements: - element.set_visibility(visibility) - - @property - def color(self): - return self.background.color - - @color.setter - def color(self, color): - self.background.color = color - - @property - def opacity(self): - return self.background.opacity - - @opacity.setter - def opacity(self, opacity): - self.background.opacity = opacity - - @warn_on_args_to_kwargs() - def add_element(self, element, coords, *, anchor="position"): - """Add a UI component to the panel. - - The coordinates represent an offset from the lower left corner of the - panel. - - Parameters - ---------- - element : UI - The UI item to be added. - coords : (float, float) or (int, int) - If float, normalized coordinates are assumed and they must be - between [0,1]. - If int, pixels coordinates are assumed and it must fit within the - panel's size. - - """ - coords = np.array(coords) - - if np.issubdtype(coords.dtype, np.floating): - if np.any(coords < 0) or np.any(coords > 1): - raise ValueError("Normalized coordinates must be in [0,1].") - - coords = coords * self.size - - if anchor == "center": - element.center = self.position + coords - elif anchor == "position": - element.position = self.position + coords - else: - msg = "Unknown anchor {}. Supported anchors are 'position'" " and 'center'." - raise ValueError(msg) - - self._elements.append(element) - offset = element.position - self.position - self.element_offsets.append((element, offset)) - - def remove_element(self, element): - """Remove a UI component from the panel. - - Parameters - ---------- - element : UI - The UI item to be removed. - - """ - idx = self._elements.index(element) - del self._elements[idx] - del self.element_offsets[idx] - - @warn_on_args_to_kwargs() - def update_element(self, element, coords, *, anchor="position"): - """Update the position of a UI component in the panel. - - Parameters - ---------- - element : UI - The UI item to be updated. - coords : (float, float) or (int, int) - New coordinates. - If float, normalized coordinates are assumed and they must be - between [0,1]. - If int, pixels coordinates are assumed and it must fit within the - panel's size. - - """ - self.remove_element(element) - self.add_element(element, coords, anchor=anchor) - - def left_button_pressed(self, i_ren, _obj, panel2d_object): - click_pos = np.array(i_ren.event.position) - self._drag_offset = click_pos - self.position - i_ren.event.abort() # Stop propagating the event. - - def left_button_dragged(self, i_ren, _obj, _panel2d_object): - if self._drag_offset is not None: - click_position = np.array(i_ren.event.position) - new_position = click_position - self._drag_offset - self.position = new_position - i_ren.force_render() - - def re_align(self, window_size_change): - """Re-organise the elements in case the window size is changed. - - Parameters - ---------- - window_size_change : (int, int) - New window size (width, height) in pixels. - - """ - if self.alignment == "left": - pass - elif self.alignment == "right": - self.position += np.array(window_size_change) - else: - msg = "You can only left-align or right-align objects in a panel." - raise ValueError(msg) - - def update_border_coords(self): - """Update the coordinates of the borders""" - self.border_coords = { - "left": (0.0, 0.0), - "right": (1.0, 0.0), - "top": (0.0, 1.0), - "bottom": (0.0, 0.0), - } - - for key in self.borders.keys(): - self.update_element(self.borders[key], self.border_coords[key]) - - @property - def border_color(self): - sides = ["left", "right", "top", "bottom"] - return [self.borders[side].color for side in sides] - - @border_color.setter - def border_color(self, side_color): - """Set the color of a specific border - - Parameters - ---------- - side_color: Iterable - Iterable to pack side, color values - - """ - side, color = side_color - - if side.lower() not in ["left", "right", "top", "bottom"]: - raise ValueError(f"{side} not a valid border side") - - self.borders[side].color = color - - @property - def border_width(self): - sides = ["left", "right", "top", "bottom"] - widths = [] - - for side in sides: - if side in ["left", "right"]: - widths.append(self.borders[side].width) - elif side in ["top", "bottom"]: - widths.append(self.borders[side].height) - else: - raise ValueError(f"{side} not a valid border side") - return widths - - @border_width.setter - def border_width(self, side_width): - """Set the border width of a specific border - - Parameters - ---------- - side_width: Iterable - Iterable to pack side, width values - - """ - side, border_width = side_width - - if side.lower() in ["left", "right"]: - self.borders[side].width = border_width - elif side.lower() in ["top", "bottom"]: - self.borders[side].height = border_width - else: - raise ValueError(f"{side} not a valid border side") - - -class TabPanel2D(UI): - """Render content within a Tab. - - Attributes - ---------- - content_panel: :class: 'Panel2D' - Hold all the content UI components. - text_block: :class: 'TextBlock2D' - Renders the title of the tab. - - """ - - @warn_on_args_to_kwargs() - def __init__( - self, - *, - position=(0, 0), - size=(100, 100), - title="New Tab", - color=(0.5, 0.5, 0.5), - content_panel=None, - ): - """Init class instance. - - Parameters - ---------- - position : (float, float) - Absolute coordinates (x, y) of the lower-left corner of the - UI component - size : (int, int) - Width and height of the pixels of this UI component. - title : str - Renders the title for Tab panel. - color : list of 3 floats - Background color of tab panel. - content_panel : Panel2D - Panel consisting of the content UI elements. - - """ - self.content_panel = content_panel - self.panel_size = size - self._text_size = (int(1.0 * size[0]), size[1]) - - super(TabPanel2D, self).__init__() - self.title = title - self.panel.position = position - self.color = color - - def _setup(self): - """Setup this UI component. - - Create parent panel. - Create Text to hold tab information. - Create Button to close tab. - - """ - self.panel = Panel2D(size=self.panel_size) - self.text_block = TextBlock2D(size=self._text_size, color=(0, 0, 0)) - self.panel.add_element(self.text_block, (0, 0)) - - def _get_actors(self): - """Get the actors composing this UI component.""" - return self.panel.actors + self.content_panel.actors - - def _add_to_scene(self, _scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - scene : scene - - """ - self.panel.add_to_scene(_scene) - self.content_panel.add_to_scene(_scene) - - def _set_position(self, _coords): - """Set the lower-left corner position of this UI component. - - Parameters - ---------- - coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - self.panel.position = _coords - - def _get_size(self): - return self.panel.size - - def resize(self, size): - """Resize Tab panel. - - Parameters - ---------- - size : (int, int) - New width and height in pixels. - - """ - self._text_size = (int(0.7 * size[0]), size[1]) - self._button_size = (int(0.3 * size[0]), size[1]) - self.panel.resize(size) - self.text_block.resize(self._text_size) - - @property - def color(self): - """Return the background color of tab panel.""" - return self.panel.color - - @color.setter - def color(self, color): - """Set background color of tab panel. - - Parameters - ---------- - color : list of 3 floats. - - """ - self.panel.color = color - - @property - def title(self): - """Return the title of tab panel.""" - return self.text_block.message - - @title.setter - def title(self, text): - """Set the title of tab panel. - - Parameters - ---------- - text : str - New title for tab panel. - - """ - self.text_block.message = text - - @property - def title_bold(self): - """Is the title of a tab panel bold.""" - return self.text_block.bold - - @title_bold.setter - def title_bold(self, bold): - """Determine if the text title of a tab panel must be bold. - - Parameters - ---------- - bold : bool - Bold property for a text title in a tab panel. - - """ - self.text_block.bold = bold - - @property - def title_color(self): - """Return the title color of tab panel.""" - return self.text_block.color - - @title_color.setter - def title_color(self, color): - """Set the title color of tab panel. - - Parameters - ---------- - color : tuple - New title color for tab panel. - - """ - self.text_block.color = color - - @property - def title_font_size(self): - """Return the title font size of tab panel.""" - return self.text_block.font_size - - @title_font_size.setter - def title_font_size(self, font_size): - """Set the title font size of tab panel. - - Parameters - ---------- - font_size : int - New title font size for tab panel. - - """ - self.text_block.font_size = font_size - - @property - def title_italic(self): - """Is the title of a tab panel italic.""" - return self.text_block.italic - - @title_italic.setter - def title_italic(self, italic): - """Determine if the text title of a tab panel must be italic. - - Parameters - ---------- - italic : bool - Italic property for a text title in a tab panel. - - """ - self.text_block.italic = italic - - @warn_on_args_to_kwargs() - def add_element(self, element, coords, *, anchor="position"): - """Add a UI component to the content panel. - - The coordinates represent an offset from the lower left corner of the - panel. - - Parameters - ---------- - element : UI - The UI item to be added. - coords : (float, float) or (int, int) - If float, normalized coordinates are assumed and they must be - between [0,1]. - If int, pixels coordinates are assumed and it must fit within the - panel's size. - - """ - element.set_visibility(False) - self.content_panel.add_element(element, coords, anchor=anchor) - - def remove_element(self, element): - """Remove a UI component from the content panel. - - Parameters - ---------- - element : UI - The UI item to be removed. - - """ - self.content_panel.remove_element(element) - - @warn_on_args_to_kwargs() - def update_element(self, element, coords, *, anchor="position"): - """Update the position of a UI component in the content panel. - - Parameters - ---------- - element : UI - The UI item to be updated. - coords : (float, float) or (int, int) - New coordinates. - If float, normalized coordinates are assumed and they must be - between [0,1]. - If int, pixels coordinates are assumed and it must fit within the - panel's size. - - """ - self.content_panel.update_element(element, coords, anchor="position") - - -class TabUI(UI): - """UI element to add multiple panels within a single window. - - Attributes - ---------- - tabs: :class: List of 'TabPanel2D' - Stores all the instances of 'TabPanel2D' that renders the contents. - - """ - - @warn_on_args_to_kwargs() - def __init__( - self, - *, - position=(0, 0), - size=(100, 100), - nb_tabs=1, - active_color=(1, 1, 1), - inactive_color=(0.5, 0.5, 0.5), - draggable=False, - startup_tab_id=None, - tab_bar_pos="top", - ): - """Init class instance. - - Parameters - ---------- - position : (float, float) - Absolute coordinates (x, y) of the lower-left corner of this - UI component. - size : (int, int) - Width and height in pixels of this UI component. - nb_tabs : int - Number of tabs to be renders. - active_color : tuple of 3 floats. - Background color of active tab panel. - inactive_color : tuple of 3 floats. - Background color of inactive tab panels. - draggable : bool - Whether the UI element is draggable or not. - startup_tab_id : int, optional - Tab to be activated and uncollapsed on startup. - by default None is activated/ all collapsed. - tab_bar_pos : str, optional - Position of the Tab Bar in the panel - """ - self.tabs = [] - self.nb_tabs = nb_tabs - self.parent_size = size - self.content_size = (size[0], int(0.9 * size[1])) - self.draggable = draggable - self.active_color = active_color - self.inactive_color = inactive_color - self.active_tab_idx = startup_tab_id - self.collapsed = True - self.tab_bar_pos = tab_bar_pos - - super(TabUI, self).__init__() - self.position = position - - def _setup(self): - """Setup this UI component. - - Create parent panel. - Create tab panels. - """ - self.parent_panel = Panel2D(self.parent_size, opacity=0.0) - - # Offer some standard hooks to the user. - self.on_change = lambda ui: None - self.on_collapse = lambda ui: None - - for _ in range(self.nb_tabs): - content_panel = Panel2D(size=self.content_size) - content_panel.set_visibility(False) - tab_panel = TabPanel2D(content_panel=content_panel) - self.tabs.append(tab_panel) - self.update_tabs() - - if self.active_tab_idx is not None: - self.tabs[self.active_tab_idx].color = self.active_color - self.tabs[self.active_tab_idx].content_panel.set_visibility(True) - - def _get_actors(self): - """Get the actors composing this UI component.""" - actors = [] - actors += self.parent_panel.actors - for tab_panel in self.tabs: - actors += tab_panel.actors - - return actors - - def _add_to_scene(self, _scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - scene : scene - - """ - self.parent_panel.add_to_scene(_scene) - for tab_panel in self.tabs: - tab_panel.add_to_scene(_scene) - - def _set_position(self, _coords): - """Set the lower-left corner position of this UI component. - - Parameters - ---------- - coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - self.parent_panel.position = _coords - - def _get_size(self): - return self.parent_panel.size - - def update_tabs(self): - """Update position, size and callbacks for tab panels.""" - self.tab_panel_size = (self.size[0] // self.nb_tabs, int(0.1 * self.size[1])) - if self.tab_bar_pos.lower() not in ["top", "bottom"]: - warn("tab_bar_pos can only have value top/bottom", stacklevel=2) - self.tab_bar_pos = "top" - - if self.tab_bar_pos.lower() == "top": - tab_panel_pos = [0.0, 0.9] - elif self.tab_bar_pos.lower() == "bottom": - tab_panel_pos = [0.0, 0.0] - - for tab_panel in self.tabs: - tab_panel.resize(self.tab_panel_size) - tab_panel.content_panel.position = self.position - - content_panel = tab_panel.content_panel - if self.draggable: - tab_panel.panel.background.on_left_mouse_button_pressed = ( - self.left_button_pressed - ) - content_panel.background.on_left_mouse_button_pressed = ( - self.left_button_pressed - ) - tab_panel.text_block.on_left_mouse_button_pressed = ( - self.left_button_pressed - ) - - tab_panel.panel.background.on_left_mouse_button_dragged = ( - self.left_button_dragged - ) - content_panel.background.on_left_mouse_button_dragged = ( - self.left_button_dragged - ) - tab_panel.text_block.on_left_mouse_button_dragged = ( - self.left_button_dragged - ) - else: - tab_panel.panel.background.on_left_mouse_button_dragged = ( - lambda i_ren, _obj, _comp: i_ren.force_render - ) - content_panel.background.on_left_mouse_button_dragged = ( - lambda i_ren, _obj, _comp: i_ren.force_render - ) - - tab_panel.text_block.on_left_mouse_button_clicked = self.select_tab_callback - tab_panel.panel.background.on_left_mouse_button_clicked = ( - self.select_tab_callback - ) - - tab_panel.text_block.on_right_mouse_button_clicked = self.collapse_tab_ui - tab_panel.panel.background.on_right_mouse_button_clicked = ( - self.collapse_tab_ui - ) - - tab_panel.content_panel.resize(self.content_size) - self.parent_panel.add_element(tab_panel, tab_panel_pos) - if self.tab_bar_pos.lower() == "top": - self.parent_panel.add_element(tab_panel.content_panel, (0.0, 0.0)) - elif self.tab_bar_pos.lower() == "bottom": - self.parent_panel.add_element(tab_panel.content_panel, (0.0, 0.1)) - tab_panel_pos[0] += 1 / self.nb_tabs - - def select_tab_callback(self, iren, _obj, _tab_comp): - """Handle events when a tab is selected.""" - for idx, tab_panel in enumerate(self.tabs): - if ( - tab_panel.text_block is not _tab_comp - and tab_panel.panel.background is not _tab_comp - ): - tab_panel.color = self.inactive_color - tab_panel.content_panel.set_visibility(False) - else: - current_visibility = tab_panel.content_panel.actors[0].GetVisibility() - if not current_visibility: - tab_panel.color = self.active_color - else: - tab_panel.color = self.inactive_color - tab_panel.content_panel.set_visibility(not current_visibility) - self.active_tab_idx = idx - - self.collapsed = False - self.on_change(self) - iren.force_render() - iren.event.abort() - - def collapse_tab_ui(self, iren, _obj, _tab_comp): - """Handle events when Tab UI is collapsed.""" - if self.active_tab_idx is not None: - active_tab_panel = self.tabs[self.active_tab_idx] - active_tab_panel.color = self.inactive_color - active_tab_panel.content_panel.set_visibility(False) - self.active_tab_idx = None - self.collapsed = True - self.on_collapse(self) - iren.force_render() - iren.event.abort() - - @warn_on_args_to_kwargs() - def add_element(self, tab_idx, element, coords, *, anchor="position"): - """Add element to content panel after checking its existence.""" - if tab_idx < self.nb_tabs and tab_idx >= 0: - self.tabs[tab_idx].add_element(element, coords, anchor=anchor) - if tab_idx == self.active_tab_idx: - element.set_visibility(True) - else: - raise IndexError("Tab with index " "{} does not exist".format(tab_idx)) - - def remove_element(self, tab_idx, element): - """Remove element from content panel after checking its existence.""" - if tab_idx < self.nb_tabs and tab_idx >= 0: - self.tabs[tab_idx].remove_element(element) - else: - raise IndexError("Tab with index " "{} does not exist".format(tab_idx)) - - @warn_on_args_to_kwargs() - def update_element(self, tab_idx, element, coords, *, anchor="position"): - """Update element on content panel after checking its existence.""" - if tab_idx < self.nb_tabs and tab_idx >= 0: - self.tabs[tab_idx].update_element(element, coords, anchor=anchor) - else: - raise IndexError("Tab with index " "{} does not exist".format(tab_idx)) - - def left_button_pressed(self, i_ren, _obj, _sub_component): - click_pos = np.array(i_ren.event.position) - self._click_position = click_pos - i_ren.event.abort() # Stop propagating the event. - - def left_button_dragged(self, i_ren, _obj, _sub_component): - click_position = np.array(i_ren.event.position) - change = click_position - self._click_position - self.parent_panel.position += change - self._click_position = click_position - i_ren.force_render() - - -class ImageContainer2D(UI): - """A 2D container to hold an image. - - Currently Supports: - - png and jpg/jpeg images - - Attributes - ---------- - size: (float, float) - Image size (width, height) in pixels. - img : ImageData - The image loaded from the specified path. - - """ - - @warn_on_args_to_kwargs() - def __init__(self, img_path, *, position=(0, 0), size=(100, 100)): - """Init class instance. - - Parameters - ---------- - img_path : string - URL or local path of the image - position : (float, float), optional - Absolute coordinates (x, y) of the lower-left corner of the image. - size : (int, int), optional - Width and height in pixels of the image. - - """ - super(ImageContainer2D, self).__init__(position=position) - self.img = load_image(img_path, as_vtktype=True) - self.set_img(self.img) - self.resize(size) - - def _get_size(self): - lower_left_corner = self.texture_points.GetPoint(0) - upper_right_corner = self.texture_points.GetPoint(2) - size = np.array(upper_right_corner) - np.array(lower_left_corner) - return abs(size[:2]) - - def _setup(self): - """Setup this UI Component. - - Return an image as a 2D actor with a specific position. - - Returns - ------- - :class:`vtkTexturedActor2D` - - """ - self.texture_polydata = PolyData() - self.texture_points = Points() - self.texture_points.SetNumberOfPoints(4) - - polys = CellArray() - polys.InsertNextCell(4) - polys.InsertCellPoint(0) - polys.InsertCellPoint(1) - polys.InsertCellPoint(2) - polys.InsertCellPoint(3) - self.texture_polydata.SetPolys(polys) - - tc = FloatArray() - tc.SetNumberOfComponents(2) - tc.SetNumberOfTuples(4) - tc.InsertComponent(0, 0, 0.0) - tc.InsertComponent(0, 1, 0.0) - tc.InsertComponent(1, 0, 1.0) - tc.InsertComponent(1, 1, 0.0) - tc.InsertComponent(2, 0, 1.0) - tc.InsertComponent(2, 1, 1.0) - tc.InsertComponent(3, 0, 0.0) - tc.InsertComponent(3, 1, 1.0) - self.texture_polydata.GetPointData().SetTCoords(tc) - - texture_mapper = PolyDataMapper2D() - texture_mapper = set_input(texture_mapper, self.texture_polydata) - - image = TexturedActor2D() - image.SetMapper(texture_mapper) - - self.texture = Texture() - image.SetTexture(self.texture) - - image_property = Property2D() - image_property.SetOpacity(1.0) - image.SetProperty(image_property) - self.actor = image - - # Add default events listener to the VTK actor. - self.handle_events(self.actor) - - def _get_actors(self): - """Return the actors that compose this UI component.""" - return [self.actor] - - def _add_to_scene(self, scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - scene : scene - - """ - scene.add(self.actor) - - def resize(self, size): - """Resize the image. - - Parameters - ---------- - size : (float, float) - image size (width, height) in pixels. - - """ - # Update actor. - self.texture_points.SetPoint(0, 0, 0, 0.0) - self.texture_points.SetPoint(1, size[0], 0, 0.0) - self.texture_points.SetPoint(2, size[0], size[1], 0.0) - self.texture_points.SetPoint(3, 0, size[1], 0.0) - self.texture_polydata.SetPoints(self.texture_points) - - def _set_position(self, coords): - """Set the lower-left corner position of this UI component. - - Parameters - ---------- - coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - self.actor.SetPosition(*coords) - - def scale(self, factor): - """Scale the image. - - Parameters - ---------- - factor : (float, float) - Scaling factor (width, height) in pixels. - - """ - self.resize(self.size * factor) - - def set_img(self, img): - """Modify the image used by the vtkTexturedActor2D. - - Parameters - ---------- - img : imageData - - """ - self.texture = set_input(self.texture, img) - - -class GridUI(UI): - """Add actors in a grid and interact with them individually.""" - - @warn_on_args_to_kwargs() - def __init__( - self, - actors, - *, - captions=None, - caption_offset=(0, -100, 0), - cell_padding=0, - cell_shape="rect", - aspect_ratio=16 / 9.0, - dim=None, - rotation_speed=1, - rotation_axis=(0, 1, 0), - ): - # TODO: add rotation axis None by default - - self.container = grid( - actors, - captions=captions, - caption_offset=caption_offset, - cell_padding=cell_padding, - cell_shape=cell_shape, - aspect_ratio=aspect_ratio, - dim=dim, - ) - self._actors = [] - self._actors_dict = {} - self.rotation_speed = rotation_speed - self.rotation_axis = rotation_axis - - for item in self.container._items: - actor = item if captions is None else item._items[0] - self._actors.append(actor) - self._actors_dict[actor] = {"x": -np.inf, "y": -np.inf} - - super(GridUI, self).__init__(position=(0, 0, 0)) - - def _get_size(self): - return - - @staticmethod - def left_click_callback(istyle, _obj, _what): - istyle.trackball_actor.OnLeftButtonDown() - istyle.force_render() - istyle.event.abort() - - @staticmethod - def left_release_callback(istyle, _obj, _what): - istyle.trackball_actor.OnLeftButtonUp() - istyle.force_render() - istyle.event.abort() - - @staticmethod - def mouse_move_callback(istyle, _obj, _what): - istyle.trackball_actor.OnMouseMove() - istyle.force_render() - istyle.event.abort() - - @staticmethod - def left_click_callback2(istyle, obj, self): - rx, ry, rz = self.rotation_axis - clockwise_rotation = np.array([self.rotation_speed, rx, ry, rz]) - rotate(obj, clockwise_rotation) - - istyle.force_render() - istyle.event.abort() - - @staticmethod - def left_release_callback2(istyle, _obj, _what): - istyle.force_render() - istyle.event.abort() - - @staticmethod - def mouse_move_callback2(istyle, obj, self): - if self._actors_dict[obj]["y"] == -np.inf: - iren = istyle.GetInteractor() - event_pos = iren.GetEventPosition() - self._actors_dict[obj]["y"] = event_pos[1] - - else: - iren = istyle.GetInteractor() - event_pos = iren.GetEventPosition() - rx, ry, rz = self.rotation_axis - - if event_pos[1] >= self._actors_dict[obj]["y"]: - clockwise_rotation = np.array([-self.rotation_speed, rx, ry, rz]) - rotate(obj, clockwise_rotation) - else: - anti_clockwise_rotation = np.array([self.rotation_speed, rx, ry, rz]) - rotate(obj, anti_clockwise_rotation) - - self._actors_dict[obj]["y"] = event_pos[1] - - istyle.force_render() - istyle.event.abort() - - ANTICLOCKWISE_ROTATION_Y = np.array([-10, 0, 1, 0]) - CLOCKWISE_ROTATION_Y = np.array([10, 0, 1, 0]) - ANTICLOCKWISE_ROTATION_X = np.array([-10, 1, 0, 0]) - CLOCKWISE_ROTATION_X = np.array([10, 1, 0, 0]) - - def key_press_callback(self, istyle, obj, _what): - has_changed = False - if istyle.event.key == "Left": - has_changed = True - for a in self._actors: - rotate(a, self.ANTICLOCKWISE_ROTATION_Y) - elif istyle.event.key == "Right": - has_changed = True - for a in self._actors: - rotate(a, self.CLOCKWISE_ROTATION_Y) - elif istyle.event.key == "Up": - has_changed = True - for a in self._actors: - rotate(a, self.ANTICLOCKWISE_ROTATION_X) - elif istyle.event.key == "Down": - has_changed = True - for a in self._actors: - rotate(a, self.CLOCKWISE_ROTATION_X) - - if has_changed: - istyle.force_render() - - def _setup(self): - """Set up this UI component and the events of its actor.""" - # Add default events listener to the VTK actor. - for actor in self._actors: - # self.handle_events(actor) - - if self.rotation_axis is None: - self.add_callback( - actor, "LeftButtonPressEvent", self.left_click_callback - ) - self.add_callback( - actor, "LeftButtonReleaseEvent", self.left_release_callback - ) - self.add_callback(actor, "MouseMoveEvent", self.mouse_move_callback) - else: - self.add_callback( - actor, "LeftButtonPressEvent", self.left_click_callback2 - ) - # TODO: possibly add this too - self.add_callback( - actor, "LeftButtonReleaseEvent", self.left_release_callback2 - ) - self.add_callback(actor, "MouseMoveEvent", self.mouse_move_callback2) - - # TODO: this is currently not running - self.add_callback(actor, "KeyPressEvent", self.key_press_callback) - # self.on_key_press = self.key_press_callback2 - - def _get_actors(self): - """Get the actors composing this UI component.""" - return self._actors - - def _add_to_scene(self, scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - scene : scene - - """ - self.container.add_to_scene(scene) - - def resize(self, size): - """Resize the button. - - Parameters - ---------- - size : (float, float) - Button size (width, height) in pixels. - - """ - # Update actor. - pass - - def _set_position(self, coords): - """Set the lower-left corner position of this UI component. - - Parameters - ---------- - coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - # coords = (0, 0, 0) - pass - # self.actor.SetPosition(*coords) - # self.container.SetPosition(*coords) +# """UI container module.""" + +# from warnings import warn + +# import numpy as np + +# # from fury.actor import grid +# from fury.decorators import warn_on_args_to_kwargs + +# # from fury.lib import ( +# # CellArray, +# # FloatArray, +# # Points, +# # PolyData, +# # PolyDataMapper2D, +# # Property2D, +# # Texture, +# # TexturedActor2D, +# # ) +# # from fury.ui.core import UI, Rectangle2D, TextBlock2D + +# # from fury.utils import rotate, set_input + + +# class Panel2D(UI): +# """A 2D UI Panel. + +# Can contain one or more UI elements. + +# Attributes +# ---------- +# alignment : [left, right] +# Alignment of the panel with respect to the overall screen. + +# """ + +# @warn_on_args_to_kwargs() +# def __init__( +# self, +# size, +# *, +# position=(0, 0), +# color=(0.1, 0.1, 0.1), +# opacity=0.7, +# align="left", +# border_color=(1, 1, 1), +# border_width=0, +# has_border=False, +# ): +# """Init class instance. + +# Parameters +# ---------- +# size : (int, int) +# Size (width, height) in pixels of the panel. +# position : (float, float) +# Absolute coordinates (x, y) of the lower-left corner of the panel. +# color : (float, float, float) +# Must take values in [0, 1]. +# opacity : float +# Must take values in [0, 1]. +# align : [left, right] +# Alignment of the panel with respect to the overall screen. +# border_color: (float, float, float), optional +# Must take values in [0, 1]. +# border_width: float, optional +# width of the border +# has_border: bool, optional +# If the panel should have borders. + +# """ +# self.has_border = has_border +# self._border_color = border_color +# self._border_width = border_width +# super(Panel2D, self).__init__(position=position) +# self.resize(size) +# self.alignment = align +# self.color = color +# self.opacity = opacity +# self.position = position +# self._drag_offset = None + +# def _setup(self): +# """Setup this UI component. + +# Create the background (Rectangle2D) of the panel. +# Create the borders (Rectangle2D) of the panel. +# """ +# self._elements = [] +# self.element_offsets = [] +# self.background = Rectangle2D() + +# if self.has_border: +# self.borders = { +# "left": Rectangle2D(), +# "right": Rectangle2D(), +# "top": Rectangle2D(), +# "bottom": Rectangle2D(), +# } + +# self.border_coords = { +# "left": (0.0, 0.0), +# "right": (1.0, 0.0), +# "top": (0.0, 1.0), +# "bottom": (0.0, 0.0), +# } + +# for key in self.borders.keys(): +# self.borders[key].color = self._border_color +# self.add_element(self.borders[key], self.border_coords[key]) + +# for key in self.borders.keys(): +# self.borders[ +# key +# ].on_left_mouse_button_pressed = self.left_button_pressed + +# self.borders[ +# key +# ].on_left_mouse_button_dragged = self.left_button_dragged + +# self.add_element(self.background, (0, 0)) + +# # Add default events listener for this UI component. +# self.background.on_left_mouse_button_pressed = self.left_button_pressed +# self.background.on_left_mouse_button_dragged = self.left_button_dragged + +# def _get_actors(self): +# """Get the actors composing this UI component.""" +# actors = [] +# for element in self._elements: +# actors += element.actors + +# return actors + +# def _add_to_scene(self, scene): +# """Add all subcomponents or VTK props that compose this UI component. + +# Parameters +# ---------- +# scene : scene + +# """ +# for element in self._elements: +# element.add_to_scene(scene) + +# def _get_size(self): +# return self.background.size + +# def resize(self, size): +# """Set the panel size. + +# Parameters +# ---------- +# size : (float, float) +# Panel size (width, height) in pixels. + +# """ +# self.background.resize(size) + +# if self.has_border: +# self.borders["left"].resize( +# (self._border_width, size[1] + self._border_width) +# ) + +# self.borders["right"].resize( +# (self._border_width, size[1] + self._border_width) +# ) + +# self.borders["top"].resize( +# (self.size[0] + self._border_width, self._border_width) +# ) + +# self.borders["bottom"].resize( +# (self.size[0] + self._border_width, self._border_width) +# ) + +# self.update_border_coords() + +# def _set_position(self, coords): +# """Set the lower-left corner position of this UI component. + +# Parameters +# ---------- +# coords: (float, float) +# Absolute pixel coordinates (x, y). + +# """ +# coords = np.array(coords) +# for element, offset in self.element_offsets: +# element.position = coords + offset + +# def set_visibility(self, visibility): +# for element in self._elements: +# element.set_visibility(visibility) + +# @property +# def color(self): +# return self.background.color + +# @color.setter +# def color(self, color): +# self.background.color = color + +# @property +# def opacity(self): +# return self.background.opacity + +# @opacity.setter +# def opacity(self, opacity): +# self.background.opacity = opacity + +# @warn_on_args_to_kwargs() +# def add_element(self, element, coords, *, anchor="position"): +# """Add a UI component to the panel. + +# The coordinates represent an offset from the lower left corner of the +# panel. + +# Parameters +# ---------- +# element : UI +# The UI item to be added. +# coords : (float, float) or (int, int) +# If float, normalized coordinates are assumed and they must be +# between [0,1]. +# If int, pixels coordinates are assumed and it must fit within the +# panel's size. + +# """ +# coords = np.array(coords) + +# if np.issubdtype(coords.dtype, np.floating): +# if np.any(coords < 0) or np.any(coords > 1): +# raise ValueError("Normalized coordinates must be in [0,1].") + +# coords = coords * self.size + +# if anchor == "center": +# element.center = self.position + coords +# elif anchor == "position": +# element.position = self.position + coords +# else: +# msg = +# "Unknown anchor {}. Supported anchors are 'position'" " and 'center'." +# raise ValueError(msg) + +# self._elements.append(element) +# offset = element.position - self.position +# self.element_offsets.append((element, offset)) + +# def remove_element(self, element): +# """Remove a UI component from the panel. + +# Parameters +# ---------- +# element : UI +# The UI item to be removed. + +# """ +# idx = self._elements.index(element) +# del self._elements[idx] +# del self.element_offsets[idx] + +# @warn_on_args_to_kwargs() +# def update_element(self, element, coords, *, anchor="position"): +# """Update the position of a UI component in the panel. + +# Parameters +# ---------- +# element : UI +# The UI item to be updated. +# coords : (float, float) or (int, int) +# New coordinates. +# If float, normalized coordinates are assumed and they must be +# between [0,1]. +# If int, pixels coordinates are assumed and it must fit within the +# panel's size. + +# """ +# self.remove_element(element) +# self.add_element(element, coords, anchor=anchor) + +# def left_button_pressed(self, i_ren, _obj, panel2d_object): +# click_pos = np.array(i_ren.event.position) +# self._drag_offset = click_pos - self.position +# i_ren.event.abort() # Stop propagating the event. + +# def left_button_dragged(self, i_ren, _obj, _panel2d_object): +# if self._drag_offset is not None: +# click_position = np.array(i_ren.event.position) +# new_position = click_position - self._drag_offset +# self.position = new_position +# i_ren.force_render() + +# def re_align(self, window_size_change): +# """Re-organise the elements in case the window size is changed. + +# Parameters +# ---------- +# window_size_change : (int, int) +# New window size (width, height) in pixels. + +# """ +# if self.alignment == "left": +# pass +# elif self.alignment == "right": +# self.position += np.array(window_size_change) +# else: +# msg = "You can only left-align or right-align objects in a panel." +# raise ValueError(msg) + +# def update_border_coords(self): +# """Update the coordinates of the borders""" +# self.border_coords = { +# "left": (0.0, 0.0), +# "right": (1.0, 0.0), +# "top": (0.0, 1.0), +# "bottom": (0.0, 0.0), +# } + +# for key in self.borders.keys(): +# self.update_element(self.borders[key], self.border_coords[key]) + +# @property +# def border_color(self): +# sides = ["left", "right", "top", "bottom"] +# return [self.borders[side].color for side in sides] + +# @border_color.setter +# def border_color(self, side_color): +# """Set the color of a specific border + +# Parameters +# ---------- +# side_color: Iterable +# Iterable to pack side, color values + +# """ +# side, color = side_color + +# if side.lower() not in ["left", "right", "top", "bottom"]: +# raise ValueError(f"{side} not a valid border side") + +# self.borders[side].color = color + +# @property +# def border_width(self): +# sides = ["left", "right", "top", "bottom"] +# widths = [] + +# for side in sides: +# if side in ["left", "right"]: +# widths.append(self.borders[side].width) +# elif side in ["top", "bottom"]: +# widths.append(self.borders[side].height) +# else: +# raise ValueError(f"{side} not a valid border side") +# return widths + +# @border_width.setter +# def border_width(self, side_width): +# """Set the border width of a specific border + +# Parameters +# ---------- +# side_width: Iterable +# Iterable to pack side, width values + +# """ +# side, border_width = side_width + +# if side.lower() in ["left", "right"]: +# self.borders[side].width = border_width +# elif side.lower() in ["top", "bottom"]: +# self.borders[side].height = border_width +# else: +# raise ValueError(f"{side} not a valid border side") + + +# class TabPanel2D(UI): +# """Render content within a Tab. + +# Attributes +# ---------- +# content_panel: :class: 'Panel2D' +# Hold all the content UI components. +# text_block: :class: 'TextBlock2D' +# Renders the title of the tab. + +# """ + +# @warn_on_args_to_kwargs() +# def __init__( +# self, +# *, +# position=(0, 0), +# size=(100, 100), +# title="New Tab", +# color=(0.5, 0.5, 0.5), +# content_panel=None, +# ): +# """Init class instance. + +# Parameters +# ---------- +# position : (float, float) +# Absolute coordinates (x, y) of the lower-left corner of the +# UI component +# size : (int, int) +# Width and height of the pixels of this UI component. +# title : str +# Renders the title for Tab panel. +# color : list of 3 floats +# Background color of tab panel. +# content_panel : Panel2D +# Panel consisting of the content UI elements. + +# """ +# self.content_panel = content_panel +# self.panel_size = size +# self._text_size = (int(1.0 * size[0]), size[1]) + +# super(TabPanel2D, self).__init__() +# self.title = title +# self.panel.position = position +# self.color = color + +# def _setup(self): +# """Setup this UI component. + +# Create parent panel. +# Create Text to hold tab information. +# Create Button to close tab. + +# """ +# self.panel = Panel2D(size=self.panel_size) +# self.text_block = TextBlock2D(size=self._text_size, color=(0, 0, 0)) +# self.panel.add_element(self.text_block, (0, 0)) + +# def _get_actors(self): +# """Get the actors composing this UI component.""" +# return self.panel.actors + self.content_panel.actors + +# def _add_to_scene(self, _scene): +# """Add all subcomponents or VTK props that compose this UI component. + +# Parameters +# ---------- +# scene : scene + +# """ +# self.panel.add_to_scene(_scene) +# self.content_panel.add_to_scene(_scene) + +# def _set_position(self, _coords): +# """Set the lower-left corner position of this UI component. + +# Parameters +# ---------- +# coords: (float, float) +# Absolute pixel coordinates (x, y). + +# """ +# self.panel.position = _coords + +# def _get_size(self): +# return self.panel.size + +# def resize(self, size): +# """Resize Tab panel. + +# Parameters +# ---------- +# size : (int, int) +# New width and height in pixels. + +# """ +# self._text_size = (int(0.7 * size[0]), size[1]) +# self._button_size = (int(0.3 * size[0]), size[1]) +# self.panel.resize(size) +# self.text_block.resize(self._text_size) + +# @property +# def color(self): +# """Return the background color of tab panel.""" +# return self.panel.color + +# @color.setter +# def color(self, color): +# """Set background color of tab panel. + +# Parameters +# ---------- +# color : list of 3 floats. + +# """ +# self.panel.color = color + +# @property +# def title(self): +# """Return the title of tab panel.""" +# return self.text_block.message + +# @title.setter +# def title(self, text): +# """Set the title of tab panel. + +# Parameters +# ---------- +# text : str +# New title for tab panel. + +# """ +# self.text_block.message = text + +# @property +# def title_bold(self): +# """Is the title of a tab panel bold.""" +# return self.text_block.bold + +# @title_bold.setter +# def title_bold(self, bold): +# """Determine if the text title of a tab panel must be bold. + +# Parameters +# ---------- +# bold : bool +# Bold property for a text title in a tab panel. + +# """ +# self.text_block.bold = bold + +# @property +# def title_color(self): +# """Return the title color of tab panel.""" +# return self.text_block.color + +# @title_color.setter +# def title_color(self, color): +# """Set the title color of tab panel. + +# Parameters +# ---------- +# color : tuple +# New title color for tab panel. + +# """ +# self.text_block.color = color + +# @property +# def title_font_size(self): +# """Return the title font size of tab panel.""" +# return self.text_block.font_size + +# @title_font_size.setter +# def title_font_size(self, font_size): +# """Set the title font size of tab panel. + +# Parameters +# ---------- +# font_size : int +# New title font size for tab panel. + +# """ +# self.text_block.font_size = font_size + +# @property +# def title_italic(self): +# """Is the title of a tab panel italic.""" +# return self.text_block.italic + +# @title_italic.setter +# def title_italic(self, italic): +# """Determine if the text title of a tab panel must be italic. + +# Parameters +# ---------- +# italic : bool +# Italic property for a text title in a tab panel. + +# """ +# self.text_block.italic = italic + +# @warn_on_args_to_kwargs() +# def add_element(self, element, coords, *, anchor="position"): +# """Add a UI component to the content panel. + +# The coordinates represent an offset from the lower left corner of the +# panel. + +# Parameters +# ---------- +# element : UI +# The UI item to be added. +# coords : (float, float) or (int, int) +# If float, normalized coordinates are assumed and they must be +# between [0,1]. +# If int, pixels coordinates are assumed and it must fit within the +# panel's size. + +# """ +# element.set_visibility(False) +# self.content_panel.add_element(element, coords, anchor=anchor) + +# def remove_element(self, element): +# """Remove a UI component from the content panel. + +# Parameters +# ---------- +# element : UI +# The UI item to be removed. + +# """ +# self.content_panel.remove_element(element) + +# @warn_on_args_to_kwargs() +# def update_element(self, element, coords, *, anchor="position"): +# """Update the position of a UI component in the content panel. + +# Parameters +# ---------- +# element : UI +# The UI item to be updated. +# coords : (float, float) or (int, int) +# New coordinates. +# If float, normalized coordinates are assumed and they must be +# between [0,1]. +# If int, pixels coordinates are assumed and it must fit within the +# panel's size. + +# """ +# self.content_panel.update_element(element, coords, anchor="position") + + +# class TabUI(UI): +# """UI element to add multiple panels within a single window. + +# Attributes +# ---------- +# tabs: :class: List of 'TabPanel2D' +# Stores all the instances of 'TabPanel2D' that renders the contents. + +# """ + +# @warn_on_args_to_kwargs() +# def __init__( +# self, +# *, +# position=(0, 0), +# size=(100, 100), +# nb_tabs=1, +# active_color=(1, 1, 1), +# inactive_color=(0.5, 0.5, 0.5), +# draggable=False, +# startup_tab_id=None, +# tab_bar_pos="top", +# ): +# """Init class instance. + +# Parameters +# ---------- +# position : (float, float) +# Absolute coordinates (x, y) of the lower-left corner of this +# UI component. +# size : (int, int) +# Width and height in pixels of this UI component. +# nb_tabs : int +# Number of tabs to be renders. +# active_color : tuple of 3 floats. +# Background color of active tab panel. +# inactive_color : tuple of 3 floats. +# Background color of inactive tab panels. +# draggable : bool +# Whether the UI element is draggable or not. +# startup_tab_id : int, optional +# Tab to be activated and uncollapsed on startup. +# by default None is activated/ all collapsed. +# tab_bar_pos : str, optional +# Position of the Tab Bar in the panel +# """ +# self.tabs = [] +# self.nb_tabs = nb_tabs +# self.parent_size = size +# self.content_size = (size[0], int(0.9 * size[1])) +# self.draggable = draggable +# self.active_color = active_color +# self.inactive_color = inactive_color +# self.active_tab_idx = startup_tab_id +# self.collapsed = True +# self.tab_bar_pos = tab_bar_pos + +# super(TabUI, self).__init__() +# self.position = position + +# def _setup(self): +# """Setup this UI component. + +# Create parent panel. +# Create tab panels. +# """ +# self.parent_panel = Panel2D(self.parent_size, opacity=0.0) + +# # Offer some standard hooks to the user. +# self.on_change = lambda ui: None +# self.on_collapse = lambda ui: None + +# for _ in range(self.nb_tabs): +# content_panel = Panel2D(size=self.content_size) +# content_panel.set_visibility(False) +# tab_panel = TabPanel2D(content_panel=content_panel) +# self.tabs.append(tab_panel) +# self.update_tabs() + +# if self.active_tab_idx is not None: +# self.tabs[self.active_tab_idx].color = self.active_color +# self.tabs[self.active_tab_idx].content_panel.set_visibility(True) + +# def _get_actors(self): +# """Get the actors composing this UI component.""" +# actors = [] +# actors += self.parent_panel.actors +# for tab_panel in self.tabs: +# actors += tab_panel.actors + +# return actors + +# def _add_to_scene(self, _scene): +# """Add all subcomponents or VTK props that compose this UI component. + +# Parameters +# ---------- +# scene : scene + +# """ +# self.parent_panel.add_to_scene(_scene) +# for tab_panel in self.tabs: +# tab_panel.add_to_scene(_scene) + +# def _set_position(self, _coords): +# """Set the lower-left corner position of this UI component. + +# Parameters +# ---------- +# coords: (float, float) +# Absolute pixel coordinates (x, y). + +# """ +# self.parent_panel.position = _coords + +# def _get_size(self): +# return self.parent_panel.size + +# def update_tabs(self): +# """Update position, size and callbacks for tab panels.""" +# self.tab_panel_size = (self.size[0] // self.nb_tabs, int(0.1 * self.size[1])) +# if self.tab_bar_pos.lower() not in ["top", "bottom"]: +# warn("tab_bar_pos can only have value top/bottom", stacklevel=2) +# self.tab_bar_pos = "top" + +# if self.tab_bar_pos.lower() == "top": +# tab_panel_pos = [0.0, 0.9] +# elif self.tab_bar_pos.lower() == "bottom": +# tab_panel_pos = [0.0, 0.0] + +# for tab_panel in self.tabs: +# tab_panel.resize(self.tab_panel_size) +# tab_panel.content_panel.position = self.position + +# content_panel = tab_panel.content_panel +# if self.draggable: +# tab_panel.panel.background.on_left_mouse_button_pressed = ( +# self.left_button_pressed +# ) +# content_panel.background.on_left_mouse_button_pressed = ( +# self.left_button_pressed +# ) +# tab_panel.text_block.on_left_mouse_button_pressed = ( +# self.left_button_pressed +# ) + +# tab_panel.panel.background.on_left_mouse_button_dragged = ( +# self.left_button_dragged +# ) +# content_panel.background.on_left_mouse_button_dragged = ( +# self.left_button_dragged +# ) +# tab_panel.text_block.on_left_mouse_button_dragged = ( +# self.left_button_dragged +# ) +# else: +# tab_panel.panel.background.on_left_mouse_button_dragged = ( +# lambda i_ren, _obj, _comp: i_ren.force_render +# ) +# content_panel.background.on_left_mouse_button_dragged = ( +# lambda i_ren, _obj, _comp: i_ren.force_render +# ) + +# tab_panel.text_block.on_left_mouse_button_clicked = +# self.select_tab_callback +# tab_panel.panel.background.on_left_mouse_button_clicked = ( +# self.select_tab_callback +# ) + +# tab_panel.text_block.on_right_mouse_button_clicked = self.collapse_tab_ui +# tab_panel.panel.background.on_right_mouse_button_clicked = ( +# self.collapse_tab_ui +# ) + +# tab_panel.content_panel.resize(self.content_size) +# self.parent_panel.add_element(tab_panel, tab_panel_pos) +# if self.tab_bar_pos.lower() == "top": +# self.parent_panel.add_element(tab_panel.content_panel, (0.0, 0.0)) +# elif self.tab_bar_pos.lower() == "bottom": +# self.parent_panel.add_element(tab_panel.content_panel, (0.0, 0.1)) +# tab_panel_pos[0] += 1 / self.nb_tabs + +# def select_tab_callback(self, iren, _obj, _tab_comp): +# """Handle events when a tab is selected.""" +# for idx, tab_panel in enumerate(self.tabs): +# if ( +# tab_panel.text_block is not _tab_comp +# and tab_panel.panel.background is not _tab_comp +# ): +# tab_panel.color = self.inactive_color +# tab_panel.content_panel.set_visibility(False) +# else: +# current_visibility = tab_panel.content_panel.actors[0].GetVisibility() +# if not current_visibility: +# tab_panel.color = self.active_color +# else: +# tab_panel.color = self.inactive_color +# tab_panel.content_panel.set_visibility(not current_visibility) +# self.active_tab_idx = idx + +# self.collapsed = False +# self.on_change(self) +# iren.force_render() +# iren.event.abort() + +# def collapse_tab_ui(self, iren, _obj, _tab_comp): +# """Handle events when Tab UI is collapsed.""" +# if self.active_tab_idx is not None: +# active_tab_panel = self.tabs[self.active_tab_idx] +# active_tab_panel.color = self.inactive_color +# active_tab_panel.content_panel.set_visibility(False) +# self.active_tab_idx = None +# self.collapsed = True +# self.on_collapse(self) +# iren.force_render() +# iren.event.abort() + +# @warn_on_args_to_kwargs() +# def add_element(self, tab_idx, element, coords, *, anchor="position"): +# """Add element to content panel after checking its existence.""" +# if tab_idx < self.nb_tabs and tab_idx >= 0: +# self.tabs[tab_idx].add_element(element, coords, anchor=anchor) +# if tab_idx == self.active_tab_idx: +# element.set_visibility(True) +# else: +# raise IndexError("Tab with index " "{} does not exist".format(tab_idx)) + +# def remove_element(self, tab_idx, element): +# """Remove element from content panel after checking its existence.""" +# if tab_idx < self.nb_tabs and tab_idx >= 0: +# self.tabs[tab_idx].remove_element(element) +# else: +# raise IndexError("Tab with index " "{} does not exist".format(tab_idx)) + +# @warn_on_args_to_kwargs() +# def update_element(self, tab_idx, element, coords, *, anchor="position"): +# """Update element on content panel after checking its existence.""" +# if tab_idx < self.nb_tabs and tab_idx >= 0: +# self.tabs[tab_idx].update_element(element, coords, anchor=anchor) +# else: +# raise IndexError("Tab with index " "{} does not exist".format(tab_idx)) + +# def left_button_pressed(self, i_ren, _obj, _sub_component): +# click_pos = np.array(i_ren.event.position) +# self._click_position = click_pos +# i_ren.event.abort() # Stop propagating the event. + +# def left_button_dragged(self, i_ren, _obj, _sub_component): +# click_position = np.array(i_ren.event.position) +# change = click_position - self._click_position +# self.parent_panel.position += change +# self._click_position = click_position +# i_ren.force_render() + + +# # class ImageContainer2D(UI): +# # """A 2D container to hold an image. + +# # Currently Supports: +# # - png and jpg/jpeg images + +# # Attributes +# # ---------- +# # size: (float, float) +# # Image size (width, height) in pixels. +# # img : ImageData +# # The image loaded from the specified path. + +# # """ + +# # @warn_on_args_to_kwargs() +# # def __init__(self, img_path, *, position=(0, 0), size=(100, 100)): +# # """Init class instance. + +# # Parameters +# # ---------- +# # img_path : string +# # URL or local path of the image +# # position : (float, float), optional +# # Absolute coordinates (x, y) of the lower-left corner of the image. +# # size : (int, int), optional +# # Width and height in pixels of the image. + +# # """ +# # super(ImageContainer2D, self).__init__(position=position) +# # self.img = load_image(img_path, as_vtktype=True) +# # self.set_img(self.img) +# # self.resize(size) + +# # def _get_size(self): +# # lower_left_corner = self.texture_points.GetPoint(0) +# # upper_right_corner = self.texture_points.GetPoint(2) +# # size = np.array(upper_right_corner) - np.array(lower_left_corner) +# # return abs(size[:2]) + +# # def _setup(self): +# # """Setup this UI Component. + +# # Return an image as a 2D actor with a specific position. + +# # Returns +# # ------- +# # :class:`vtkTexturedActor2D` + +# # """ +# # self.texture_polydata = PolyData() +# # self.texture_points = Points() +# # self.texture_points.SetNumberOfPoints(4) + +# # polys = CellArray() +# # polys.InsertNextCell(4) +# # polys.InsertCellPoint(0) +# # polys.InsertCellPoint(1) +# # polys.InsertCellPoint(2) +# # polys.InsertCellPoint(3) +# # self.texture_polydata.SetPolys(polys) + +# # tc = FloatArray() +# # tc.SetNumberOfComponents(2) +# # tc.SetNumberOfTuples(4) +# # tc.InsertComponent(0, 0, 0.0) +# # tc.InsertComponent(0, 1, 0.0) +# # tc.InsertComponent(1, 0, 1.0) +# # tc.InsertComponent(1, 1, 0.0) +# # tc.InsertComponent(2, 0, 1.0) +# # tc.InsertComponent(2, 1, 1.0) +# # tc.InsertComponent(3, 0, 0.0) +# # tc.InsertComponent(3, 1, 1.0) +# # self.texture_polydata.GetPointData().SetTCoords(tc) + +# # texture_mapper = PolyDataMapper2D() +# # texture_mapper = set_input(texture_mapper, self.texture_polydata) + +# # image = TexturedActor2D() +# # image.SetMapper(texture_mapper) + +# # self.texture = Texture() +# # image.SetTexture(self.texture) + +# # image_property = Property2D() +# # image_property.SetOpacity(1.0) +# # image.SetProperty(image_property) +# # self.actor = image + +# # # Add default events listener to the VTK actor. +# # self.handle_events(self.actor) + +# # def _get_actors(self): +# # """Return the actors that compose this UI component.""" +# # return [self.actor] + +# # def _add_to_scene(self, scene): +# # """Add all subcomponents or VTK props that compose this UI component. + +# # Parameters +# # ---------- +# # scene : scene + +# # """ +# # scene.add(self.actor) + +# # def resize(self, size): +# # """Resize the image. + +# # Parameters +# # ---------- +# # size : (float, float) +# # image size (width, height) in pixels. + +# # """ +# # # Update actor. +# # self.texture_points.SetPoint(0, 0, 0, 0.0) +# # self.texture_points.SetPoint(1, size[0], 0, 0.0) +# # self.texture_points.SetPoint(2, size[0], size[1], 0.0) +# # self.texture_points.SetPoint(3, 0, size[1], 0.0) +# # self.texture_polydata.SetPoints(self.texture_points) + +# # def _set_position(self, coords): +# # """Set the lower-left corner position of this UI component. + +# # Parameters +# # ---------- +# # coords: (float, float) +# # Absolute pixel coordinates (x, y). + +# # """ +# # self.actor.SetPosition(*coords) + +# # def scale(self, factor): +# # """Scale the image. + +# # Parameters +# # ---------- +# # factor : (float, float) +# # Scaling factor (width, height) in pixels. + +# # """ +# # self.resize(self.size * factor) + +# # def set_img(self, img): +# # """Modify the image used by the vtkTexturedActor2D. + +# # Parameters +# # ---------- +# # img : imageData + +# # """ +# # self.texture = set_input(self.texture, img) + + +# # class GridUI(UI): +# # """Add actors in a grid and interact with them individually.""" + +# # @warn_on_args_to_kwargs() +# # def __init__( +# # self, +# # actors, +# # *, +# # captions=None, +# # caption_offset=(0, -100, 0), +# # cell_padding=0, +# # cell_shape="rect", +# # aspect_ratio=16 / 9.0, +# # dim=None, +# # rotation_speed=1, +# # rotation_axis=(0, 1, 0), +# # ): +# # # TODO: add rotation axis None by default + +# # self.container = grid( +# # actors, +# # captions=captions, +# # caption_offset=caption_offset, +# # cell_padding=cell_padding, +# # cell_shape=cell_shape, +# # aspect_ratio=aspect_ratio, +# # dim=dim, +# # ) +# # self._actors = [] +# # self._actors_dict = {} +# # self.rotation_speed = rotation_speed +# # self.rotation_axis = rotation_axis + +# # for item in self.container._items: +# # actor = item if captions is None else item._items[0] +# # self._actors.append(actor) +# # self._actors_dict[actor] = {"x": -np.inf, "y": -np.inf} + +# # super(GridUI, self).__init__(position=(0, 0, 0)) + +# # def _get_size(self): +# # return + +# # @staticmethod +# # def left_click_callback(istyle, _obj, _what): +# # istyle.trackball_actor.OnLeftButtonDown() +# # istyle.force_render() +# # istyle.event.abort() + +# # @staticmethod +# # def left_release_callback(istyle, _obj, _what): +# # istyle.trackball_actor.OnLeftButtonUp() +# # istyle.force_render() +# # istyle.event.abort() + +# # @staticmethod +# # def mouse_move_callback(istyle, _obj, _what): +# # istyle.trackball_actor.OnMouseMove() +# # istyle.force_render() +# # istyle.event.abort() + +# # @staticmethod +# # def left_click_callback2(istyle, obj, self): +# # rx, ry, rz = self.rotation_axis +# # clockwise_rotation = np.array([self.rotation_speed, rx, ry, rz]) +# # rotate(obj, clockwise_rotation) + +# # istyle.force_render() +# # istyle.event.abort() + +# # @staticmethod +# # def left_release_callback2(istyle, _obj, _what): +# # istyle.force_render() +# # istyle.event.abort() + +# # @staticmethod +# # def mouse_move_callback2(istyle, obj, self): +# # if self._actors_dict[obj]["y"] == -np.inf: +# # iren = istyle.GetInteractor() +# # event_pos = iren.GetEventPosition() +# # self._actors_dict[obj]["y"] = event_pos[1] + +# # else: +# # iren = istyle.GetInteractor() +# # event_pos = iren.GetEventPosition() +# # rx, ry, rz = self.rotation_axis + +# # if event_pos[1] >= self._actors_dict[obj]["y"]: +# # clockwise_rotation = np.array([-self.rotation_speed, rx, ry, rz]) +# # rotate(obj, clockwise_rotation) +# # else: +# # anti_clockwise_rotation = np.array( +# [self.rotation_speed, rx, ry, rz]) +# # rotate(obj, anti_clockwise_rotation) + +# # self._actors_dict[obj]["y"] = event_pos[1] + +# # istyle.force_render() +# # istyle.event.abort() + +# # ANTICLOCKWISE_ROTATION_Y = np.array([-10, 0, 1, 0]) +# # CLOCKWISE_ROTATION_Y = np.array([10, 0, 1, 0]) +# # ANTICLOCKWISE_ROTATION_X = np.array([-10, 1, 0, 0]) +# # CLOCKWISE_ROTATION_X = np.array([10, 1, 0, 0]) + +# # def key_press_callback(self, istyle, obj, _what): +# # has_changed = False +# # if istyle.event.key == "Left": +# # has_changed = True +# # for a in self._actors: +# # rotate(a, self.ANTICLOCKWISE_ROTATION_Y) +# # elif istyle.event.key == "Right": +# # has_changed = True +# # for a in self._actors: +# # rotate(a, self.CLOCKWISE_ROTATION_Y) +# # elif istyle.event.key == "Up": +# # has_changed = True +# # for a in self._actors: +# # rotate(a, self.ANTICLOCKWISE_ROTATION_X) +# # elif istyle.event.key == "Down": +# # has_changed = True +# # for a in self._actors: +# # rotate(a, self.CLOCKWISE_ROTATION_X) + +# # if has_changed: +# # istyle.force_render() + +# # def _setup(self): +# # """Set up this UI component and the events of its actor.""" +# # # Add default events listener to the VTK actor. +# # for actor in self._actors: +# # # self.handle_events(actor) + +# # if self.rotation_axis is None: +# # self.add_callback( +# # actor, "LeftButtonPressEvent", self.left_click_callback +# # ) +# # self.add_callback( +# # actor, "LeftButtonReleaseEvent", self.left_release_callback +# # ) +# # self.add_callback(actor, "MouseMoveEvent", self.mouse_move_callback) +# # else: +# # self.add_callback( +# # actor, "LeftButtonPressEvent", self.left_click_callback2 +# # ) +# # # TODO: possibly add this too +# # self.add_callback( +# # actor, "LeftButtonReleaseEvent", self.left_release_callback2 +# # ) +# # self.add_callback( +# actor, "MouseMoveEvent", self.mouse_move_callback2) + +# # # TODO: this is currently not running +# # self.add_callback(actor, "KeyPressEvent", self.key_press_callback) +# # # self.on_key_press = self.key_press_callback2 + +# # def _get_actors(self): +# # """Get the actors composing this UI component.""" +# # return self._actors + +# # def _add_to_scene(self, scene): +# # """Add all subcomponents or VTK props that compose this UI component. + +# # Parameters +# # ---------- +# # scene : scene + +# # """ +# # self.container.add_to_scene(scene) + +# # def resize(self, size): +# # """Resize the button. + +# # Parameters +# # ---------- +# # size : (float, float) +# # Button size (width, height) in pixels. + +# # """ +# # # Update actor. +# # pass + +# # def _set_position(self, coords): +# # """Set the lower-left corner position of this UI component. + +# # Parameters +# # ---------- +# # coords: (float, float) +# # Absolute pixel coordinates (x, y). + +# # """ +# # # coords = (0, 0, 0) +# # pass +# # # self.actor.SetPosition(*coords) +# # # self.container.SetPosition(*coords) diff --git a/fury/ui/core.py b/fury/ui/core.py index 1ff2b1720..8d7197af5 100644 --- a/fury/ui/core.py +++ b/fury/ui/core.py @@ -1,1451 +1,1453 @@ -"""UI core module that describe UI abstract class.""" - -import abc - -import numpy as np - -from fury.decorators import warn_on_args_to_kwargs -from fury.interactor import CustomInteractorStyle -from fury.io import load_image -from fury.lib import ( - Actor2D, - CellArray, - DiskSource, - FloatArray, - Points, - PolyData, - PolyDataMapper2D, - Polygon, - Property2D, - TextActor, - Texture, - TexturedActor2D, -) -from fury.utils import set_input - - -class UI(object, metaclass=abc.ABCMeta): - """An umbrella class for all UI elements. - - While adding UI elements to the scene, we go over all the sub-elements - that come with it and add those to the scene automatically. - - Attributes - ---------- - position : (float, float) - Absolute coordinates (x, y) of the lower-left corner of this - UI component. - center : (float, float) - Absolute coordinates (x, y) of the center of this UI component. - size : (int, int) - Width and height in pixels of this UI component. - on_left_mouse_button_pressed: function - Callback function for when the left mouse button is pressed. - on_left_mouse_button_released: function - Callback function for when the left mouse button is released. - on_left_mouse_button_clicked: function - Callback function for when clicked using the left mouse button - (i.e. pressed -> released). - on_left_mouse_double_clicked: function - Callback function for when left mouse button is double clicked - (i.e pressed -> released -> pressed -> released). - on_left_mouse_button_dragged: function - Callback function for when dragging using the left mouse button. - on_right_mouse_button_pressed: function - Callback function for when the right mouse button is pressed. - on_right_mouse_button_released: function - Callback function for when the right mouse button is released. - on_right_mouse_button_clicked: function - Callback function for when clicking using the right mouse button - (i.e. pressed -> released). - on_right_mouse_double_clicked: function - Callback function for when right mouse button is double clicked - (i.e pressed -> released -> pressed -> released). - on_right_mouse_button_dragged: function - Callback function for when dragging using the right mouse button. - on_middle_mouse_button_pressed: function - Callback function for when the middle mouse button is pressed. - on_middle_mouse_button_released: function - Callback function for when the middle mouse button is released. - on_middle_mouse_button_clicked: function - Callback function for when clicking using the middle mouse button - (i.e. pressed -> released). - on_middle_mouse_double_clicked: function - Callback function for when middle mouse button is double clicked - (i.e pressed -> released -> pressed -> released). - on_middle_mouse_button_dragged: function - Callback function for when dragging using the middle mouse button. - on_key_press: function - Callback function for when a keyboard key is pressed. - - """ - - @warn_on_args_to_kwargs() - def __init__(self, *, position=(0, 0)): - """Init scene. - - Parameters - ---------- - position : (float, float) - Absolute coordinates (x, y) of the lower-left corner of this - UI component. - - """ - self._scene = object() - self._position = np.array([0, 0]) - self._callbacks = [] - - self._setup() # Setup needed actors and sub UI components. - self.position = position - - self.left_button_state = "released" - self.right_button_state = "released" - self.middle_button_state = "released" - - self.on_left_mouse_button_pressed = lambda i_ren, obj, element: None - self.on_left_mouse_button_dragged = lambda i_ren, obj, element: None - self.on_left_mouse_button_released = lambda i_ren, obj, element: None - self.on_left_mouse_button_clicked = lambda i_ren, obj, element: None - self.on_left_mouse_double_clicked = lambda i_ren, obj, element: None - self.on_right_mouse_button_pressed = lambda i_ren, obj, element: None - self.on_right_mouse_button_released = lambda i_ren, obj, element: None - self.on_right_mouse_button_clicked = lambda i_ren, obj, element: None - self.on_right_mouse_double_clicked = lambda i_ren, obj, element: None - self.on_right_mouse_button_dragged = lambda i_ren, obj, element: None - self.on_middle_mouse_button_pressed = lambda i_ren, obj, element: None - self.on_middle_mouse_button_released = lambda i_ren, obj, element: None - self.on_middle_mouse_button_clicked = lambda i_ren, obj, element: None - self.on_middle_mouse_double_clicked = lambda i_ren, obj, element: None - self.on_middle_mouse_button_dragged = lambda i_ren, obj, element: None - self.on_key_press = lambda i_ren, obj, element: None - - @abc.abstractmethod - def _setup(self): - """Set up this UI component. - - This is where you should create all your needed actors and sub UI - components. - - """ - msg = "Subclasses of UI must implement `_setup(self)`." - raise NotImplementedError(msg) - - @abc.abstractmethod - def _get_actors(self): - """Get the actors composing this UI component.""" - msg = "Subclasses of UI must implement `_get_actors(self)`." - raise NotImplementedError(msg) - - @property - def actors(self): - """Actors composing this UI component.""" - return self._get_actors() - - @abc.abstractmethod - def _add_to_scene(self, _scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - _scene : Scene - - """ - msg = "Subclasses of UI must implement `_add_to_scene(self, scene)`." - raise NotImplementedError(msg) - - def add_to_scene(self, scene): - """Allow UI objects to add their own props to the scene. - - Parameters - ---------- - scene : scene - - """ - self._add_to_scene(scene) - - # Get a hold on the current interactor style. - iren = scene.GetRenderWindow().GetInteractor().GetInteractorStyle() - - for callback in self._callbacks: - if not isinstance(iren, CustomInteractorStyle): - msg = ( - "The ShowManager requires `CustomInteractorStyle` in" - " order to use callbacks." - ) - raise TypeError(msg) - - if callback[0] == self._scene: - iren.add_callback(iren, callback[1], callback[2], args=[self]) - else: - # iren.add_callback(*callback, args=[self]) - if len(callback) > 3: - iren.add_callback(*callback[:3], priority=callback[3], args=[self]) - - @warn_on_args_to_kwargs() - def add_callback(self, prop, event_type, callback, *, priority=0): - """Add a callback to a specific event for this UI component. - - Parameters - ---------- - prop : vtkProp - The prop on which is callback is to be added. - event_type : string - The event code. - callback : function - The callback function. - priority : int - Higher number is higher priority. - - """ - # Actually since we need an interactor style we will add the callback - # only when this UI component is added to the scene. - self._callbacks.append((prop, event_type, callback, priority)) - - @property - def position(self): - return self._position - - @position.setter - def position(self, coords): - coords = np.asarray(coords) - self._set_position(coords) - self._position = coords - - @abc.abstractmethod - def _set_position(self, _coords): - """Position the lower-left corner of this UI component. - - Parameters - ---------- - _coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - msg = "Subclasses of UI must implement `_set_position(self, coords)`." - raise NotImplementedError(msg) - - @property - def size(self): - return np.asarray(self._get_size(), dtype=int) - - @abc.abstractmethod - def _get_size(self): - msg = "Subclasses of UI must implement property `size`." - raise NotImplementedError(msg) - - @property - def center(self): - return self.position + self.size / 2.0 - - @center.setter - def center(self, coords): - """Position the center of this UI component. - - Parameters - ---------- - coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - if not hasattr(self, "size"): - msg = "Subclasses of UI must implement the `size` property." - raise NotImplementedError(msg) - - new_center = np.array(coords) - size = np.array(self.size) - new_lower_left_corner = new_center - size / 2.0 - self.position = new_lower_left_corner - - def set_visibility(self, visibility): - """Set visibility of this UI component.""" - for actor in self.actors: - actor.SetVisibility(visibility) - - def handle_events(self, actor): - self.add_callback( - actor, "LeftButtonPressEvent", self.left_button_click_callback - ) - self.add_callback( - actor, "LeftButtonReleaseEvent", self.left_button_release_callback - ) - self.add_callback( - actor, "RightButtonPressEvent", self.right_button_click_callback - ) - self.add_callback( - actor, "RightButtonReleaseEvent", self.right_button_release_callback - ) - self.add_callback( - actor, "MiddleButtonPressEvent", self.middle_button_click_callback - ) - self.add_callback( - actor, "MiddleButtonReleaseEvent", self.middle_button_release_callback - ) - self.add_callback(actor, "MouseMoveEvent", self.mouse_move_callback) - self.add_callback(actor, "KeyPressEvent", self.key_press_callback) - - @staticmethod - def left_button_click_callback(i_ren, obj, self): - self.left_button_state = "pressing" - self.on_left_mouse_button_pressed(i_ren, obj, self) - i_ren.event.abort() - - @staticmethod - def left_button_release_callback(i_ren, obj, self): - if self.left_button_state == "pressing": - self.on_left_mouse_button_clicked(i_ren, obj, self) - self.left_button_state = "released" - self.on_left_mouse_button_released(i_ren, obj, self) - - @staticmethod - def right_button_click_callback(i_ren, obj, self): - self.right_button_state = "pressing" - self.on_right_mouse_button_pressed(i_ren, obj, self) - i_ren.event.abort() - - @staticmethod - def right_button_release_callback(i_ren, obj, self): - if self.right_button_state == "pressing": - self.on_right_mouse_button_clicked(i_ren, obj, self) - self.right_button_state = "released" - self.on_right_mouse_button_released(i_ren, obj, self) - - @staticmethod - def middle_button_click_callback(i_ren, obj, self): - self.middle_button_state = "pressing" - self.on_middle_mouse_button_pressed(i_ren, obj, self) - i_ren.event.abort() - - @staticmethod - def middle_button_release_callback(i_ren, obj, self): - if self.middle_button_state == "pressing": - self.on_middle_mouse_button_clicked(i_ren, obj, self) - self.middle_button_state = "released" - self.on_middle_mouse_button_released(i_ren, obj, self) - - @staticmethod - def mouse_move_callback(i_ren, obj, self): - left_pressing_or_dragging = ( - self.left_button_state == "pressing" or self.left_button_state == "dragging" - ) - - right_pressing_or_dragging = ( - self.right_button_state == "pressing" - or self.right_button_state == "dragging" - ) - - middle_pressing_or_dragging = ( - self.middle_button_state == "pressing" - or self.middle_button_state == "dragging" - ) - - if left_pressing_or_dragging: - self.left_button_state = "dragging" - self.on_left_mouse_button_dragged(i_ren, obj, self) - elif right_pressing_or_dragging: - self.right_button_state = "dragging" - self.on_right_mouse_button_dragged(i_ren, obj, self) - elif middle_pressing_or_dragging: - self.middle_button_state = "dragging" - self.on_middle_mouse_button_dragged(i_ren, obj, self) - - @staticmethod - def key_press_callback(i_ren, obj, self): - self.on_key_press(i_ren, obj, self) - - -class Rectangle2D(UI): - """A 2D rectangle sub-classed from UI.""" - - @warn_on_args_to_kwargs() - def __init__(self, *, size=(0, 0), position=(0, 0), color=(1, 1, 1), opacity=1.0): - """Initialize a rectangle. - - Parameters - ---------- - size : (int, int) - The size of the rectangle (width, height) in pixels. - position : (float, float) - Coordinates (x, y) of the lower-left corner of the rectangle. - color : (float, float, float) - Must take values in [0, 1]. - opacity : float - Must take values in [0, 1]. - - """ - super(Rectangle2D, self).__init__(position=position) - self.color = color - self.opacity = opacity - self.resize(size) - - def _setup(self): - """Set up this UI component. - - Creating the polygon actor used internally. - """ - # Setup four points - size = (1, 1) - self._points = Points() - self._points.InsertNextPoint(0, 0, 0) - self._points.InsertNextPoint(size[0], 0, 0) - self._points.InsertNextPoint(size[0], size[1], 0) - self._points.InsertNextPoint(0, size[1], 0) - - # Create the polygon - polygon = Polygon() - polygon.GetPointIds().SetNumberOfIds(4) # make a quad - polygon.GetPointIds().SetId(0, 0) - polygon.GetPointIds().SetId(1, 1) - polygon.GetPointIds().SetId(2, 2) - polygon.GetPointIds().SetId(3, 3) - - # Add the polygon to a list of polygons - polygons = CellArray() - polygons.InsertNextCell(polygon) - - # Create a PolyData - self._polygonPolyData = PolyData() - self._polygonPolyData.SetPoints(self._points) - self._polygonPolyData.SetPolys(polygons) - - # Create a mapper and actor - mapper = PolyDataMapper2D() - mapper = set_input(mapper, self._polygonPolyData) - - self.actor = Actor2D() - self.actor.SetMapper(mapper) - - # Add default events listener to the VTK actor. - self.handle_events(self.actor) - - def _get_actors(self): - """Get the actors composing this UI component.""" - return [self.actor] - - def _add_to_scene(self, scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - scene : scene - - """ - scene.add(self.actor) - - def _get_size(self): - # Get 2D coordinates of two opposed corners of the rectangle. - lower_left_corner = np.array(self._points.GetPoint(0)[:2]) - upper_right_corner = np.array(self._points.GetPoint(2)[:2]) - size = abs(upper_right_corner - lower_left_corner) - return size - - @property - def width(self): - return self._points.GetPoint(2)[0] - - @width.setter - def width(self, width): - self.resize((width, self.height)) - - @property - def height(self): - return self._points.GetPoint(2)[1] - - @height.setter - def height(self, height): - self.resize((self.width, height)) - - def resize(self, size): - """Set the button size. - - Parameters - ---------- - size : (float, float) - Button size (width, height) in pixels. - - """ - self._points.SetPoint(0, 0, 0, 0.0) - self._points.SetPoint(1, size[0], 0, 0.0) - self._points.SetPoint(2, size[0], size[1], 0.0) - self._points.SetPoint(3, 0, size[1], 0.0) - self._polygonPolyData.SetPoints(self._points) - mapper = PolyDataMapper2D() - mapper = set_input(mapper, self._polygonPolyData) - - self.actor.SetMapper(mapper) - - def _set_position(self, coords): - """Set the lower-left corner position of this UI component. - - Parameters - ---------- - coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - self.actor.SetPosition(*coords) - - @property - def color(self): - """Get the rectangle's color.""" - color = self.actor.GetProperty().GetColor() - return np.asarray(color) - - @color.setter - def color(self, color): - """Set the rectangle's color. - - Parameters - ---------- - color : (float, float, float) - RGB. Must take values in [0, 1]. - - """ - self.actor.GetProperty().SetColor(*color) - - @property - def opacity(self): - """Get the rectangle's opacity.""" - return self.actor.GetProperty().GetOpacity() - - @opacity.setter - def opacity(self, opacity): - """Set the rectangle's opacity. - - Parameters - ---------- - opacity : float - Degree of transparency. Must be between [0, 1]. - - """ - self.actor.GetProperty().SetOpacity(opacity) - - -class Disk2D(UI): - """A 2D disk UI component.""" - - @warn_on_args_to_kwargs() - def __init__( - self, - outer_radius, - *, - inner_radius=0, - center=(0, 0), - color=(1, 1, 1), - opacity=1.0, - ): - """Initialize a 2D Disk. - - Parameters - ---------- - outer_radius : int - Outer radius of the disk. - inner_radius : int, optional - Inner radius of the disk. A value > 0, makes a ring. - center : (float, float), optional - Coordinates (x, y) of the center of the disk. - color : (float, float, float), optional - Must take values in [0, 1]. - opacity : float, optional - Must take values in [0, 1]. - - """ - super(Disk2D, self).__init__() - self.outer_radius = outer_radius - self.inner_radius = inner_radius - self.color = color - self.opacity = opacity - self.center = center - - def _setup(self): - """Setup this UI component. - - Creating the disk actor used internally. - - """ - # Setting up disk actor. - self._disk = DiskSource() - self._disk.SetRadialResolution(10) - self._disk.SetCircumferentialResolution(50) - self._disk.Update() - - # Mapper - mapper = PolyDataMapper2D() - mapper = set_input(mapper, self._disk.GetOutputPort()) - - # Actor - self.actor = Actor2D() - self.actor.SetMapper(mapper) - - # Add default events listener to the VTK actor. - self.handle_events(self.actor) - - def _get_actors(self): - """Get the actors composing this UI component.""" - return [self.actor] - - def _add_to_scene(self, scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - scene : scene - - """ - scene.add(self.actor) - - def _get_size(self): - diameter = 2 * self.outer_radius - size = (diameter, diameter) - return size - - def _set_position(self, coords): - """Set the lower-left corner position of this UI bounding box. - - Parameters - ---------- - coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - # Disk actor are positioned with respect to their center. - self.actor.SetPosition(*coords + self.outer_radius) - - @property - def color(self): - """Get the color of this UI component.""" - color = self.actor.GetProperty().GetColor() - return np.asarray(color) - - @color.setter - def color(self, color): - """Set the color of this UI component. - - Parameters - ---------- - color : (float, float, float) - RGB. Must take values in [0, 1]. - - """ - self.actor.GetProperty().SetColor(*color) - - @property - def opacity(self): - """Get the opacity of this UI component.""" - return self.actor.GetProperty().GetOpacity() - - @opacity.setter - def opacity(self, opacity): - """Set the opacity of this UI component. - - Parameters - ---------- - opacity : float - Degree of transparency. Must be between [0, 1]. - - """ - self.actor.GetProperty().SetOpacity(opacity) - - @property - def inner_radius(self): - return self._disk.GetInnerRadius() - - @inner_radius.setter - def inner_radius(self, radius): - self._disk.SetInnerRadius(radius) - self._disk.Update() - - @property - def outer_radius(self): - return self._disk.GetOuterRadius() - - @outer_radius.setter - def outer_radius(self, radius): - self._disk.SetOuterRadius(radius) - self._disk.Update() - - -class TextBlock2D(UI): - """Wrap over the default vtkTextActor and helps setting the text. - - Contains member functions for text formatting. - - Attributes - ---------- - actor : :class:`vtkTextActor` - The text actor. - message : str - The initial text while building the actor. - position : (float, float) - (x, y) in pixels. - color : (float, float, float) - RGB: Values must be between 0-1. - bg_color : (float, float, float) - RGB: Values must be between 0-1. - font_size : int - Size of the text font. - font_family : str - Currently only supports Arial. - justification : str - left, right or center. - vertical_justification : str - bottom, middle or top. - bold : bool - Makes text bold. - italic : bool - Makes text italicised. - shadow : bool - Adds text shadow. - size : (int, int) - Size (width, height) in pixels of the text bounding box. - auto_font_scale : bool - Automatically scale font according to the text bounding box. - dynamic_bbox : bool - Automatically resize the bounding box according to the content. - - """ - - @warn_on_args_to_kwargs() - def __init__( - self, - *, - text="Text Block", - font_size=18, - font_family="Arial", - justification="left", - vertical_justification="bottom", - bold=False, - italic=False, - shadow=False, - size=None, - color=(1, 1, 1), - bg_color=None, - position=(0, 0), - auto_font_scale=False, - dynamic_bbox=False, - ): - """Init class instance. - - Parameters - ---------- - text : str - The initial text while building the actor. - position : (float, float) - (x, y) in pixels. - color : (float, float, float) - RGB: Values must be between 0-1. - bg_color : (float, float, float) - RGB: Values must be between 0-1. - font_size : int - Size of the text font. - font_family : str - Currently only supports Arial. - justification : str - left, right or center. - vertical_justification : str - bottom, middle or top. - bold : bool - Makes text bold. - italic : bool - Makes text italicised. - shadow : bool - Adds text shadow. - size : (int, int) - Size (width, height) in pixels of the text bounding box. - auto_font_scale : bool, optional - Automatically scale font according to the text bounding box. - dynamic_bbox : bool, optional - Automatically resize the bounding box according to the content. - - """ - self.boundingbox = [0, 0, 0, 0] - super(TextBlock2D, self).__init__(position=position) - self.scene = None - self.have_bg = bool(bg_color) - self.color = color - self.background_color = bg_color - self.font_family = font_family - self._justification = justification - self.bold = bold - self.italic = italic - self.shadow = shadow - self._vertical_justification = vertical_justification - self._dynamic_bbox = dynamic_bbox - self.auto_font_scale = auto_font_scale - self.message = text - self.font_size = font_size - if size is not None: - self.resize(size) - elif not self.dynamic_bbox: - # raise ValueError("TextBlock size is required as it is not dynamic.") - self.resize((0, 0)) - - def _setup(self): - self.actor = TextActor() - self.actor.GetPosition2Coordinate().SetCoordinateSystemToViewport() - self.background = Rectangle2D() - self.handle_events(self.actor) - - def resize(self, size): - """Resize TextBlock2D. - - Parameters - ---------- - size : (int, int) - Text bounding box size(width, height) in pixels. - - """ - self.update_bounding_box(size=size) - - def _get_actors(self): - """Get the actors composing this UI component.""" - return [self.actor] + self.background.actors - - def _add_to_scene(self, scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - scene : scene - - """ - scene.add(self.background, self.actor) - - @property - def message(self): - """Get message from the text. - - Returns - ------- - str - The current text message. - - """ - return self.actor.GetInput() - - @message.setter - def message(self, text): - """Set the text message. - - Parameters - ---------- - text : str - The message to be set. - - """ - self.actor.SetInput(text) - if self.dynamic_bbox: - self.update_bounding_box() - - @property - def font_size(self): - """Get text font size. - - Returns - ------- - int - Text font size. - - """ - return self.actor.GetTextProperty().GetFontSize() - - @font_size.setter - def font_size(self, size): - """Set font size. - - Parameters - ---------- - size : int - Text font size. - - """ - if not self.auto_font_scale: - self.actor.SetTextScaleModeToNone() - self.actor.GetTextProperty().SetFontSize(size) - - if self.dynamic_bbox: - self.update_bounding_box() - - @property - def font_family(self): - """Get font family. - - Returns - ------- - str - Text font family. - - """ - return self.actor.GetTextProperty().GetFontFamilyAsString() - - @font_family.setter - def font_family(self, family="Arial"): - """Set font family. - - Currently Arial and Courier are supported. - - Parameters - ---------- - family : str - The font family. - - """ - if family == "Arial": - self.actor.GetTextProperty().SetFontFamilyToArial() - elif family == "Courier": - self.actor.GetTextProperty().SetFontFamilyToCourier() - else: - raise ValueError("Font not supported yet: {}.".format(family)) - - @property - def justification(self): - """Get text justification. - - Returns - ------- - str - Text justification. - - """ - return self._justification - - @justification.setter - def justification(self, justification): - """Justify text. - - Parameters - ---------- - justification : str - Possible values are left, right, center. - - """ - self._justification = justification - self.update_alignment() - - @property - def vertical_justification(self): - """Get text vertical justification. - - Returns - ------- - str - Text vertical justification. - - """ - return self._vertical_justification - - @vertical_justification.setter - def vertical_justification(self, vertical_justification): - """Justify text vertically. - - Parameters - ---------- - vertical_justification : str - Possible values are bottom, middle, top. - - """ - self._vertical_justification = vertical_justification - self.update_alignment() - - @property - def bold(self): - """Return whether the text is bold. - - Returns - ------- - bool - Text is bold if True. - - """ - return self.actor.GetTextProperty().GetBold() - - @bold.setter - def bold(self, flag): - """Bold/un-bold text. +# """UI core module that describe UI abstract class.""" + +# import abc + +# import numpy as np + +# from fury.decorators import warn_on_args_to_kwargs +# from fury.interactor import CustomInteractorStyle +# from fury.io import load_image +# from fury.lib import ( +# Actor2D, +# CellArray, +# DiskSource, +# FloatArray, +# Points, +# PolyData, +# PolyDataMapper2D, +# Polygon, +# Property2D, +# TextActor, +# Texture, +# TexturedActor2D, +# ) +# from fury.utils import set_input + + +# class UI(object, metaclass=abc.ABCMeta): +# """An umbrella class for all UI elements. + +# While adding UI elements to the scene, we go over all the sub-elements +# that come with it and add those to the scene automatically. + +# Attributes +# ---------- +# position : (float, float) +# Absolute coordinates (x, y) of the lower-left corner of this +# UI component. +# center : (float, float) +# Absolute coordinates (x, y) of the center of this UI component. +# size : (int, int) +# Width and height in pixels of this UI component. +# on_left_mouse_button_pressed: function +# Callback function for when the left mouse button is pressed. +# on_left_mouse_button_released: function +# Callback function for when the left mouse button is released. +# on_left_mouse_button_clicked: function +# Callback function for when clicked using the left mouse button +# (i.e. pressed -> released). +# on_left_mouse_double_clicked: function +# Callback function for when left mouse button is double clicked +# (i.e pressed -> released -> pressed -> released). +# on_left_mouse_button_dragged: function +# Callback function for when dragging using the left mouse button. +# on_right_mouse_button_pressed: function +# Callback function for when the right mouse button is pressed. +# on_right_mouse_button_released: function +# Callback function for when the right mouse button is released. +# on_right_mouse_button_clicked: function +# Callback function for when clicking using the right mouse button +# (i.e. pressed -> released). +# on_right_mouse_double_clicked: function +# Callback function for when right mouse button is double clicked +# (i.e pressed -> released -> pressed -> released). +# on_right_mouse_button_dragged: function +# Callback function for when dragging using the right mouse button. +# on_middle_mouse_button_pressed: function +# Callback function for when the middle mouse button is pressed. +# on_middle_mouse_button_released: function +# Callback function for when the middle mouse button is released. +# on_middle_mouse_button_clicked: function +# Callback function for when clicking using the middle mouse button +# (i.e. pressed -> released). +# on_middle_mouse_double_clicked: function +# Callback function for when middle mouse button is double clicked +# (i.e pressed -> released -> pressed -> released). +# on_middle_mouse_button_dragged: function +# Callback function for when dragging using the middle mouse button. +# on_key_press: function +# Callback function for when a keyboard key is pressed. + +# """ + +# @warn_on_args_to_kwargs() +# def __init__(self, *, position=(0, 0)): +# """Init scene. + +# Parameters +# ---------- +# position : (float, float) +# Absolute coordinates (x, y) of the lower-left corner of this +# UI component. + +# """ +# self._scene = object() +# self._position = np.array([0, 0]) +# self._callbacks = [] + +# self._setup() # Setup needed actors and sub UI components. +# self.position = position + +# self.left_button_state = "released" +# self.right_button_state = "released" +# self.middle_button_state = "released" + +# self.on_left_mouse_button_pressed = lambda i_ren, obj, element: None +# self.on_left_mouse_button_dragged = lambda i_ren, obj, element: None +# self.on_left_mouse_button_released = lambda i_ren, obj, element: None +# self.on_left_mouse_button_clicked = lambda i_ren, obj, element: None +# self.on_left_mouse_double_clicked = lambda i_ren, obj, element: None +# self.on_right_mouse_button_pressed = lambda i_ren, obj, element: None +# self.on_right_mouse_button_released = lambda i_ren, obj, element: None +# self.on_right_mouse_button_clicked = lambda i_ren, obj, element: None +# self.on_right_mouse_double_clicked = lambda i_ren, obj, element: None +# self.on_right_mouse_button_dragged = lambda i_ren, obj, element: None +# self.on_middle_mouse_button_pressed = lambda i_ren, obj, element: None +# self.on_middle_mouse_button_released = lambda i_ren, obj, element: None +# self.on_middle_mouse_button_clicked = lambda i_ren, obj, element: None +# self.on_middle_mouse_double_clicked = lambda i_ren, obj, element: None +# self.on_middle_mouse_button_dragged = lambda i_ren, obj, element: None +# self.on_key_press = lambda i_ren, obj, element: None + +# @abc.abstractmethod +# def _setup(self): +# """Set up this UI component. + +# This is where you should create all your needed actors and sub UI +# components. + +# """ +# msg = "Subclasses of UI must implement `_setup(self)`." +# raise NotImplementedError(msg) + +# @abc.abstractmethod +# def _get_actors(self): +# """Get the actors composing this UI component.""" +# msg = "Subclasses of UI must implement `_get_actors(self)`." +# raise NotImplementedError(msg) + +# @property +# def actors(self): +# """Actors composing this UI component.""" +# return self._get_actors() + +# @abc.abstractmethod +# def _add_to_scene(self, _scene): +# """Add all subcomponents or VTK props that compose this UI component. + +# Parameters +# ---------- +# _scene : Scene + +# """ +# msg = "Subclasses of UI must implement `_add_to_scene(self, scene)`." +# raise NotImplementedError(msg) + +# def add_to_scene(self, scene): +# """Allow UI objects to add their own props to the scene. + +# Parameters +# ---------- +# scene : scene + +# """ +# self._add_to_scene(scene) + +# # Get a hold on the current interactor style. +# iren = scene.GetRenderWindow().GetInteractor().GetInteractorStyle() + +# for callback in self._callbacks: +# if not isinstance(iren, CustomInteractorStyle): +# msg = ( +# "The ShowManager requires `CustomInteractorStyle` in" +# " order to use callbacks." +# ) +# raise TypeError(msg) + +# if callback[0] == self._scene: +# iren.add_callback(iren, callback[1], callback[2], args=[self]) +# else: +# # iren.add_callback(*callback, args=[self]) +# if len(callback) > 3: +# iren.add_callback( +# *callback[:3], priority=callback[3], args=[self]) + +# @warn_on_args_to_kwargs() +# def add_callback(self, prop, event_type, callback, *, priority=0): +# """Add a callback to a specific event for this UI component. + +# Parameters +# ---------- +# prop : vtkProp +# The prop on which is callback is to be added. +# event_type : string +# The event code. +# callback : function +# The callback function. +# priority : int +# Higher number is higher priority. + +# """ +# # Actually since we need an interactor style we will add the callback +# # only when this UI component is added to the scene. +# self._callbacks.append((prop, event_type, callback, priority)) + +# @property +# def position(self): +# return self._position + +# @position.setter +# def position(self, coords): +# coords = np.asarray(coords) +# self._set_position(coords) +# self._position = coords + +# @abc.abstractmethod +# def _set_position(self, _coords): +# """Position the lower-left corner of this UI component. + +# Parameters +# ---------- +# _coords: (float, float) +# Absolute pixel coordinates (x, y). + +# """ +# msg = "Subclasses of UI must implement `_set_position(self, coords)`." +# raise NotImplementedError(msg) + +# @property +# def size(self): +# return np.asarray(self._get_size(), dtype=int) + +# @abc.abstractmethod +# def _get_size(self): +# msg = "Subclasses of UI must implement property `size`." +# raise NotImplementedError(msg) + +# @property +# def center(self): +# return self.position + self.size / 2.0 + +# @center.setter +# def center(self, coords): +# """Position the center of this UI component. + +# Parameters +# ---------- +# coords: (float, float) +# Absolute pixel coordinates (x, y). + +# """ +# if not hasattr(self, "size"): +# msg = "Subclasses of UI must implement the `size` property." +# raise NotImplementedError(msg) + +# new_center = np.array(coords) +# size = np.array(self.size) +# new_lower_left_corner = new_center - size / 2.0 +# self.position = new_lower_left_corner + +# def set_visibility(self, visibility): +# """Set visibility of this UI component.""" +# for actor in self.actors: +# actor.SetVisibility(visibility) + +# def handle_events(self, actor): +# self.add_callback( +# actor, "LeftButtonPressEvent", self.left_button_click_callback +# ) +# self.add_callback( +# actor, "LeftButtonReleaseEvent", self.left_button_release_callback +# ) +# self.add_callback( +# actor, "RightButtonPressEvent", self.right_button_click_callback +# ) +# self.add_callback( +# actor, "RightButtonReleaseEvent", self.right_button_release_callback +# ) +# self.add_callback( +# actor, "MiddleButtonPressEvent", self.middle_button_click_callback +# ) +# self.add_callback( +# actor, "MiddleButtonReleaseEvent", self.middle_button_release_callback +# ) +# self.add_callback(actor, "MouseMoveEvent", self.mouse_move_callback) +# self.add_callback(actor, "KeyPressEvent", self.key_press_callback) + +# @staticmethod +# def left_button_click_callback(i_ren, obj, self): +# self.left_button_state = "pressing" +# self.on_left_mouse_button_pressed(i_ren, obj, self) +# i_ren.event.abort() + +# @staticmethod +# def left_button_release_callback(i_ren, obj, self): +# if self.left_button_state == "pressing": +# self.on_left_mouse_button_clicked(i_ren, obj, self) +# self.left_button_state = "released" +# self.on_left_mouse_button_released(i_ren, obj, self) + +# @staticmethod +# def right_button_click_callback(i_ren, obj, self): +# self.right_button_state = "pressing" +# self.on_right_mouse_button_pressed(i_ren, obj, self) +# i_ren.event.abort() + +# @staticmethod +# def right_button_release_callback(i_ren, obj, self): +# if self.right_button_state == "pressing": +# self.on_right_mouse_button_clicked(i_ren, obj, self) +# self.right_button_state = "released" +# self.on_right_mouse_button_released(i_ren, obj, self) + +# @staticmethod +# def middle_button_click_callback(i_ren, obj, self): +# self.middle_button_state = "pressing" +# self.on_middle_mouse_button_pressed(i_ren, obj, self) +# i_ren.event.abort() + +# @staticmethod +# def middle_button_release_callback(i_ren, obj, self): +# if self.middle_button_state == "pressing": +# self.on_middle_mouse_button_clicked(i_ren, obj, self) +# self.middle_button_state = "released" +# self.on_middle_mouse_button_released(i_ren, obj, self) + +# @staticmethod +# def mouse_move_callback(i_ren, obj, self): +# left_pressing_or_dragging = ( +# self.left_button_state == "pressing" or +# self.left_button_state == "dragging" +# ) + +# right_pressing_or_dragging = ( +# self.right_button_state == "pressing" +# or self.right_button_state == "dragging" +# ) + +# middle_pressing_or_dragging = ( +# self.middle_button_state == "pressing" +# or self.middle_button_state == "dragging" +# ) + +# if left_pressing_or_dragging: +# self.left_button_state = "dragging" +# self.on_left_mouse_button_dragged(i_ren, obj, self) +# elif right_pressing_or_dragging: +# self.right_button_state = "dragging" +# self.on_right_mouse_button_dragged(i_ren, obj, self) +# elif middle_pressing_or_dragging: +# self.middle_button_state = "dragging" +# self.on_middle_mouse_button_dragged(i_ren, obj, self) + +# @staticmethod +# def key_press_callback(i_ren, obj, self): +# self.on_key_press(i_ren, obj, self) + + +# class Rectangle2D(UI): +# """A 2D rectangle sub-classed from UI.""" + +# @warn_on_args_to_kwargs() +# def __init__(self, *, size=(0, 0), position=(0, 0), color=(1, 1, 1), opacity=1.0): +# """Initialize a rectangle. + +# Parameters +# ---------- +# size : (int, int) +# The size of the rectangle (width, height) in pixels. +# position : (float, float) +# Coordinates (x, y) of the lower-left corner of the rectangle. +# color : (float, float, float) +# Must take values in [0, 1]. +# opacity : float +# Must take values in [0, 1]. + +# """ +# super(Rectangle2D, self).__init__(position=position) +# self.color = color +# self.opacity = opacity +# self.resize(size) + +# def _setup(self): +# """Set up this UI component. + +# Creating the polygon actor used internally. +# """ +# # Setup four points +# size = (1, 1) +# self._points = Points() +# self._points.InsertNextPoint(0, 0, 0) +# self._points.InsertNextPoint(size[0], 0, 0) +# self._points.InsertNextPoint(size[0], size[1], 0) +# self._points.InsertNextPoint(0, size[1], 0) + +# # Create the polygon +# polygon = Polygon() +# polygon.GetPointIds().SetNumberOfIds(4) # make a quad +# polygon.GetPointIds().SetId(0, 0) +# polygon.GetPointIds().SetId(1, 1) +# polygon.GetPointIds().SetId(2, 2) +# polygon.GetPointIds().SetId(3, 3) + +# # Add the polygon to a list of polygons +# polygons = CellArray() +# polygons.InsertNextCell(polygon) + +# # Create a PolyData +# self._polygonPolyData = PolyData() +# self._polygonPolyData.SetPoints(self._points) +# self._polygonPolyData.SetPolys(polygons) + +# # Create a mapper and actor +# mapper = PolyDataMapper2D() +# mapper = set_input(mapper, self._polygonPolyData) + +# self.actor = Actor2D() +# self.actor.SetMapper(mapper) + +# # Add default events listener to the VTK actor. +# self.handle_events(self.actor) + +# def _get_actors(self): +# """Get the actors composing this UI component.""" +# return [self.actor] + +# def _add_to_scene(self, scene): +# """Add all subcomponents or VTK props that compose this UI component. + +# Parameters +# ---------- +# scene : scene + +# """ +# scene.add(self.actor) + +# def _get_size(self): +# # Get 2D coordinates of two opposed corners of the rectangle. +# lower_left_corner = np.array(self._points.GetPoint(0)[:2]) +# upper_right_corner = np.array(self._points.GetPoint(2)[:2]) +# size = abs(upper_right_corner - lower_left_corner) +# return size + +# @property +# def width(self): +# return self._points.GetPoint(2)[0] + +# @width.setter +# def width(self, width): +# self.resize((width, self.height)) + +# @property +# def height(self): +# return self._points.GetPoint(2)[1] + +# @height.setter +# def height(self, height): +# self.resize((self.width, height)) + +# def resize(self, size): +# """Set the button size. + +# Parameters +# ---------- +# size : (float, float) +# Button size (width, height) in pixels. + +# """ +# self._points.SetPoint(0, 0, 0, 0.0) +# self._points.SetPoint(1, size[0], 0, 0.0) +# self._points.SetPoint(2, size[0], size[1], 0.0) +# self._points.SetPoint(3, 0, size[1], 0.0) +# self._polygonPolyData.SetPoints(self._points) +# mapper = PolyDataMapper2D() +# mapper = set_input(mapper, self._polygonPolyData) + +# self.actor.SetMapper(mapper) + +# def _set_position(self, coords): +# """Set the lower-left corner position of this UI component. + +# Parameters +# ---------- +# coords: (float, float) +# Absolute pixel coordinates (x, y). + +# """ +# self.actor.SetPosition(*coords) + +# @property +# def color(self): +# """Get the rectangle's color.""" +# color = self.actor.GetProperty().GetColor() +# return np.asarray(color) + +# @color.setter +# def color(self, color): +# """Set the rectangle's color. + +# Parameters +# ---------- +# color : (float, float, float) +# RGB. Must take values in [0, 1]. + +# """ +# self.actor.GetProperty().SetColor(*color) + +# @property +# def opacity(self): +# """Get the rectangle's opacity.""" +# return self.actor.GetProperty().GetOpacity() + +# @opacity.setter +# def opacity(self, opacity): +# """Set the rectangle's opacity. + +# Parameters +# ---------- +# opacity : float +# Degree of transparency. Must be between [0, 1]. + +# """ +# self.actor.GetProperty().SetOpacity(opacity) + + +# class Disk2D(UI): +# """A 2D disk UI component.""" + +# @warn_on_args_to_kwargs() +# def __init__( +# self, +# outer_radius, +# *, +# inner_radius=0, +# center=(0, 0), +# color=(1, 1, 1), +# opacity=1.0, +# ): +# """Initialize a 2D Disk. + +# Parameters +# ---------- +# outer_radius : int +# Outer radius of the disk. +# inner_radius : int, optional +# Inner radius of the disk. A value > 0, makes a ring. +# center : (float, float), optional +# Coordinates (x, y) of the center of the disk. +# color : (float, float, float), optional +# Must take values in [0, 1]. +# opacity : float, optional +# Must take values in [0, 1]. + +# """ +# super(Disk2D, self).__init__() +# self.outer_radius = outer_radius +# self.inner_radius = inner_radius +# self.color = color +# self.opacity = opacity +# self.center = center + +# def _setup(self): +# """Setup this UI component. + +# Creating the disk actor used internally. + +# """ +# # Setting up disk actor. +# self._disk = DiskSource() +# self._disk.SetRadialResolution(10) +# self._disk.SetCircumferentialResolution(50) +# self._disk.Update() + +# # Mapper +# mapper = PolyDataMapper2D() +# mapper = set_input(mapper, self._disk.GetOutputPort()) + +# # Actor +# self.actor = Actor2D() +# self.actor.SetMapper(mapper) + +# # Add default events listener to the VTK actor. +# self.handle_events(self.actor) + +# def _get_actors(self): +# """Get the actors composing this UI component.""" +# return [self.actor] + +# def _add_to_scene(self, scene): +# """Add all subcomponents or VTK props that compose this UI component. + +# Parameters +# ---------- +# scene : scene + +# """ +# scene.add(self.actor) + +# def _get_size(self): +# diameter = 2 * self.outer_radius +# size = (diameter, diameter) +# return size + +# def _set_position(self, coords): +# """Set the lower-left corner position of this UI bounding box. + +# Parameters +# ---------- +# coords: (float, float) +# Absolute pixel coordinates (x, y). + +# """ +# # Disk actor are positioned with respect to their center. +# self.actor.SetPosition(*coords + self.outer_radius) + +# @property +# def color(self): +# """Get the color of this UI component.""" +# color = self.actor.GetProperty().GetColor() +# return np.asarray(color) + +# @color.setter +# def color(self, color): +# """Set the color of this UI component. + +# Parameters +# ---------- +# color : (float, float, float) +# RGB. Must take values in [0, 1]. + +# """ +# self.actor.GetProperty().SetColor(*color) + +# @property +# def opacity(self): +# """Get the opacity of this UI component.""" +# return self.actor.GetProperty().GetOpacity() + +# @opacity.setter +# def opacity(self, opacity): +# """Set the opacity of this UI component. + +# Parameters +# ---------- +# opacity : float +# Degree of transparency. Must be between [0, 1]. + +# """ +# self.actor.GetProperty().SetOpacity(opacity) + +# @property +# def inner_radius(self): +# return self._disk.GetInnerRadius() + +# @inner_radius.setter +# def inner_radius(self, radius): +# self._disk.SetInnerRadius(radius) +# self._disk.Update() + +# @property +# def outer_radius(self): +# return self._disk.GetOuterRadius() + +# @outer_radius.setter +# def outer_radius(self, radius): +# self._disk.SetOuterRadius(radius) +# self._disk.Update() + + +# class TextBlock2D(UI): +# """Wrap over the default vtkTextActor and helps setting the text. + +# Contains member functions for text formatting. + +# Attributes +# ---------- +# actor : :class:`vtkTextActor` +# The text actor. +# message : str +# The initial text while building the actor. +# position : (float, float) +# (x, y) in pixels. +# color : (float, float, float) +# RGB: Values must be between 0-1. +# bg_color : (float, float, float) +# RGB: Values must be between 0-1. +# font_size : int +# Size of the text font. +# font_family : str +# Currently only supports Arial. +# justification : str +# left, right or center. +# vertical_justification : str +# bottom, middle or top. +# bold : bool +# Makes text bold. +# italic : bool +# Makes text italicised. +# shadow : bool +# Adds text shadow. +# size : (int, int) +# Size (width, height) in pixels of the text bounding box. +# auto_font_scale : bool +# Automatically scale font according to the text bounding box. +# dynamic_bbox : bool +# Automatically resize the bounding box according to the content. + +# """ + +# @warn_on_args_to_kwargs() +# def __init__( +# self, +# *, +# text="Text Block", +# font_size=18, +# font_family="Arial", +# justification="left", +# vertical_justification="bottom", +# bold=False, +# italic=False, +# shadow=False, +# size=None, +# color=(1, 1, 1), +# bg_color=None, +# position=(0, 0), +# auto_font_scale=False, +# dynamic_bbox=False, +# ): +# """Init class instance. + +# Parameters +# ---------- +# text : str +# The initial text while building the actor. +# position : (float, float) +# (x, y) in pixels. +# color : (float, float, float) +# RGB: Values must be between 0-1. +# bg_color : (float, float, float) +# RGB: Values must be between 0-1. +# font_size : int +# Size of the text font. +# font_family : str +# Currently only supports Arial. +# justification : str +# left, right or center. +# vertical_justification : str +# bottom, middle or top. +# bold : bool +# Makes text bold. +# italic : bool +# Makes text italicised. +# shadow : bool +# Adds text shadow. +# size : (int, int) +# Size (width, height) in pixels of the text bounding box. +# auto_font_scale : bool, optional +# Automatically scale font according to the text bounding box. +# dynamic_bbox : bool, optional +# Automatically resize the bounding box according to the content. + +# """ +# self.boundingbox = [0, 0, 0, 0] +# super(TextBlock2D, self).__init__(position=position) +# self.scene = None +# self.have_bg = bool(bg_color) +# self.color = color +# self.background_color = bg_color +# self.font_family = font_family +# self._justification = justification +# self.bold = bold +# self.italic = italic +# self.shadow = shadow +# self._vertical_justification = vertical_justification +# self._dynamic_bbox = dynamic_bbox +# self.auto_font_scale = auto_font_scale +# self.message = text +# self.font_size = font_size +# if size is not None: +# self.resize(size) +# elif not self.dynamic_bbox: +# # raise ValueError("TextBlock size is required as it is not dynamic.") +# self.resize((0, 0)) + +# def _setup(self): +# self.actor = TextActor() +# self.actor.GetPosition2Coordinate().SetCoordinateSystemToViewport() +# self.background = Rectangle2D() +# self.handle_events(self.actor) + +# def resize(self, size): +# """Resize TextBlock2D. + +# Parameters +# ---------- +# size : (int, int) +# Text bounding box size(width, height) in pixels. + +# """ +# self.update_bounding_box(size=size) + +# def _get_actors(self): +# """Get the actors composing this UI component.""" +# return [self.actor] + self.background.actors + +# def _add_to_scene(self, scene): +# """Add all subcomponents or VTK props that compose this UI component. + +# Parameters +# ---------- +# scene : scene + +# """ +# scene.add(self.background, self.actor) + +# @property +# def message(self): +# """Get message from the text. + +# Returns +# ------- +# str +# The current text message. + +# """ +# return self.actor.GetInput() + +# @message.setter +# def message(self, text): +# """Set the text message. + +# Parameters +# ---------- +# text : str +# The message to be set. + +# """ +# self.actor.SetInput(text) +# if self.dynamic_bbox: +# self.update_bounding_box() + +# @property +# def font_size(self): +# """Get text font size. + +# Returns +# ------- +# int +# Text font size. + +# """ +# return self.actor.GetTextProperty().GetFontSize() + +# @font_size.setter +# def font_size(self, size): +# """Set font size. + +# Parameters +# ---------- +# size : int +# Text font size. + +# """ +# if not self.auto_font_scale: +# self.actor.SetTextScaleModeToNone() +# self.actor.GetTextProperty().SetFontSize(size) + +# if self.dynamic_bbox: +# self.update_bounding_box() + +# @property +# def font_family(self): +# """Get font family. + +# Returns +# ------- +# str +# Text font family. + +# """ +# return self.actor.GetTextProperty().GetFontFamilyAsString() + +# @font_family.setter +# def font_family(self, family="Arial"): +# """Set font family. + +# Currently Arial and Courier are supported. + +# Parameters +# ---------- +# family : str +# The font family. + +# """ +# if family == "Arial": +# self.actor.GetTextProperty().SetFontFamilyToArial() +# elif family == "Courier": +# self.actor.GetTextProperty().SetFontFamilyToCourier() +# else: +# raise ValueError("Font not supported yet: {}.".format(family)) + +# @property +# def justification(self): +# """Get text justification. + +# Returns +# ------- +# str +# Text justification. + +# """ +# return self._justification + +# @justification.setter +# def justification(self, justification): +# """Justify text. + +# Parameters +# ---------- +# justification : str +# Possible values are left, right, center. + +# """ +# self._justification = justification +# self.update_alignment() + +# @property +# def vertical_justification(self): +# """Get text vertical justification. + +# Returns +# ------- +# str +# Text vertical justification. + +# """ +# return self._vertical_justification + +# @vertical_justification.setter +# def vertical_justification(self, vertical_justification): +# """Justify text vertically. + +# Parameters +# ---------- +# vertical_justification : str +# Possible values are bottom, middle, top. + +# """ +# self._vertical_justification = vertical_justification +# self.update_alignment() + +# @property +# def bold(self): +# """Return whether the text is bold. + +# Returns +# ------- +# bool +# Text is bold if True. + +# """ +# return self.actor.GetTextProperty().GetBold() + +# @bold.setter +# def bold(self, flag): +# """Bold/un-bold text. - Parameters - ---------- - flag : bool - Sets text bold if True. - - """ - self.actor.GetTextProperty().SetBold(flag) - - @property - def italic(self): - """Return whether the text is italicised. - - Returns - ------- - bool - Text is italicised if True. - - """ - return self.actor.GetTextProperty().GetItalic() - - @italic.setter - def italic(self, flag): - """Italicise/un-italicise text. - - Parameters - ---------- - flag : bool - Italicises text if True. - - """ - self.actor.GetTextProperty().SetItalic(flag) - - @property - def shadow(self): - """Return whether the text has shadow. - - Returns - ------- - bool - Text is shadowed if True. - - """ - return self.actor.GetTextProperty().GetShadow() - - @shadow.setter - def shadow(self, flag): - """Add/remove text shadow. - - Parameters - ---------- - flag : bool - Shadows text if True. - - """ - self.actor.GetTextProperty().SetShadow(flag) - - @property - def color(self): - """Get text color. - - Returns - ------- - (float, float, float) - Returns text color in RGB. - - """ - return self.actor.GetTextProperty().GetColor() - - @color.setter - def color(self, color=(1, 0, 0)): - """Set text color. - - Parameters - ---------- - color : (float, float, float) - RGB: Values must be between 0-1. - - """ - self.actor.GetTextProperty().SetColor(*color) - - @property - def background_color(self): - """Get background color. - - Returns - ------- - (float, float, float) or None - If None, there no background color. - Otherwise, background color in RGB. - - """ - if not self.have_bg: - return None - - return self.background.color - - @background_color.setter - def background_color(self, color): - """Set text color. - - Parameters - ---------- - color : (float, float, float) or None - If None, remove background. - Otherwise, RGB values (must be between 0-1). - - """ - if color is None: - # Remove background. - self.have_bg = False - self.background.set_visibility(False) - - else: - self.have_bg = True - self.background.set_visibility(True) - self.background.color = color - - @property - def auto_font_scale(self): - """Return whether text font is automatically scaled. - - Returns - ------- - bool - Text is auto_font_scaled if True. - - """ - return self._auto_font_scale - - @auto_font_scale.setter - def auto_font_scale(self, flag): - """Add/remove text auto_font_scale. - - Parameters - ---------- - flag : bool - Automatically scales the text font if True. - - """ - self._auto_font_scale = flag - if flag: - self.actor.SetTextScaleModeToProp() - self._justification = "left" - self.update_bounding_box(size=self.size) - else: - self.actor.SetTextScaleModeToNone() - - @property - def dynamic_bbox(self): - """Automatically resize the bounding box according to the content. - - Returns - ------- - bool - Bounding box is dynamic if True. - - """ - return self._dynamic_bbox - - @dynamic_bbox.setter - def dynamic_bbox(self, flag): - """Add/remove dynamic_bbox. - - Parameters - ---------- - flag : bool - The text bounding box is dynamic if True. - - """ - self._dynamic_bbox = flag - if flag: - self.update_bounding_box() - - def update_alignment(self): - """Update Text Alignment.""" - text_property = self.actor.GetTextProperty() - updated_text_position = [0, 0] - - if self.justification.lower() == "left": - text_property.SetJustificationToLeft() - updated_text_position[0] = self.boundingbox[0] - elif self.justification.lower() == "center": - text_property.SetJustificationToCentered() - updated_text_position[0] = ( - self.boundingbox[0] + (self.boundingbox[2] - self.boundingbox[0]) // 2 - ) - elif self.justification.lower() == "right": - text_property.SetJustificationToRight() - updated_text_position[0] = self.boundingbox[2] - else: - msg = "Text can only be justified left, right and center." - raise ValueError(msg) - - if self.vertical_justification.lower() == "bottom": - text_property.SetVerticalJustificationToBottom() - updated_text_position[1] = self.boundingbox[1] - elif self.vertical_justification.lower() == "middle": - text_property.SetVerticalJustificationToCentered() - updated_text_position[1] = ( - self.boundingbox[1] + (self.boundingbox[3] - self.boundingbox[1]) // 2 - ) - elif self.vertical_justification.lower() == "top": - text_property.SetVerticalJustificationToTop() - updated_text_position[1] = self.boundingbox[3] - else: - msg = "Vertical justification must be: bottom, middle or top." - raise ValueError(msg) - - self.actor.SetPosition(updated_text_position) - - def cal_size_from_message(self): - """Calculate size of background according to the message it contains.""" - lines = self.message.split("\n") - max_length = max(len(line) for line in lines) - return [max_length * self.font_size, len(lines) * self.font_size] - - @warn_on_args_to_kwargs() - def update_bounding_box(self, *, size=None): - """Update Text Bounding Box. - - Parameters - ---------- - size : (int, int) or None - If None, calculates bounding box. - Otherwise, uses the given size. - - """ - if size is None: - size = self.cal_size_from_message() - - self.boundingbox = [ - self.position[0], - self.position[1], - self.position[0] + size[0], - self.position[1] + size[1], - ] - self.background.resize(size) - - if self.auto_font_scale: - self.actor.SetPosition2( - self.boundingbox[2] - self.boundingbox[0], - self.boundingbox[3] - self.boundingbox[1], - ) - else: - self.update_alignment() - - def _set_position(self, position): - """Set text actor position. - - Parameters - ---------- - position : (float, float) - The new position. (x, y) in pixels. - - """ - self.actor.SetPosition(*position) - self.background.position = position - - def _get_size(self): - bb_size = ( - self.boundingbox[2] - self.boundingbox[0], - self.boundingbox[3] - self.boundingbox[1], - ) - if self.dynamic_bbox or self.auto_font_scale or sum(bb_size): - return bb_size - return self.cal_size_from_message() - - -class Button2D(UI): - """A 2D overlay button and is of type vtkTexturedActor2D. - - Currently supports:: - - - Multiple icons. - - Switching between icons. - - """ - - @warn_on_args_to_kwargs() - def __init__(self, icon_fnames, *, position=(0, 0), size=(30, 30)): - """Init class instance. - - Parameters - ---------- - icon_fnames : List(string, string) - ((iconname, filename), (iconname, filename), ....) - position : (float, float), optional - Absolute coordinates (x, y) of the lower-left corner of the button. - size : (int, int), optional - Width and height in pixels of the button. - - """ - super(Button2D, self).__init__(position=position) - - self.icon_extents = {} - self.icons = self._build_icons(icon_fnames) - self.icon_names = [icon[0] for icon in self.icons] - self.current_icon_id = 0 - self.current_icon_name = self.icon_names[self.current_icon_id] - self.set_icon(self.icons[self.current_icon_id][1]) - self.resize(size) - - def _get_size(self): - lower_left_corner = self.texture_points.GetPoint(0) - upper_right_corner = self.texture_points.GetPoint(2) - size = np.array(upper_right_corner) - np.array(lower_left_corner) - return abs(size[:2]) - - def _build_icons(self, icon_fnames): - """Convert file names to ImageData. - - A pre-processing step to prevent re-read of file names during every - state change. - - Parameters - ---------- - icon_fnames : List(string, string) - ((iconname, filename), (iconname, filename), ....) - - Returns - ------- - icons : List - A list of corresponding ImageData. - - """ - icons = [] - for icon_name, icon_fname in icon_fnames: - icons.append((icon_name, load_image(icon_fname, as_vtktype=True))) - - return icons - - def _setup(self): - """Set up this UI component. - - Creating the button actor used internally. - - """ - # This is highly inspired by - # https://github.com/Kitware/VTK/blob/c3ec2495b183e3327820e927af7f8f90d34c3474/Interaction/Widgets/vtkBalloonRepresentation.cxx#L47 - - self.texture_polydata = PolyData() - self.texture_points = Points() - self.texture_points.SetNumberOfPoints(4) - - polys = CellArray() - polys.InsertNextCell(4) - polys.InsertCellPoint(0) - polys.InsertCellPoint(1) - polys.InsertCellPoint(2) - polys.InsertCellPoint(3) - self.texture_polydata.SetPolys(polys) - - tc = FloatArray() - tc.SetNumberOfComponents(2) - tc.SetNumberOfTuples(4) - tc.InsertComponent(0, 0, 0.0) - tc.InsertComponent(0, 1, 0.0) - tc.InsertComponent(1, 0, 1.0) - tc.InsertComponent(1, 1, 0.0) - tc.InsertComponent(2, 0, 1.0) - tc.InsertComponent(2, 1, 1.0) - tc.InsertComponent(3, 0, 0.0) - tc.InsertComponent(3, 1, 1.0) - self.texture_polydata.GetPointData().SetTCoords(tc) - - texture_mapper = PolyDataMapper2D() - texture_mapper = set_input(texture_mapper, self.texture_polydata) - - button = TexturedActor2D() - button.SetMapper(texture_mapper) - - self.texture = Texture() - button.SetTexture(self.texture) - - button_property = Property2D() - button_property.SetOpacity(1.0) - button.SetProperty(button_property) - self.actor = button - - # Add default events listener to the VTK actor. - self.handle_events(self.actor) - - def _get_actors(self): - """Get the actors composing this UI component.""" - return [self.actor] - - def _add_to_scene(self, scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - scene : scene - - """ - scene.add(self.actor) - - def resize(self, size): - """Resize the button. - - Parameters - ---------- - size : (float, float) - Button size (width, height) in pixels. - - """ - # Update actor. - self.texture_points.SetPoint(0, 0, 0, 0.0) - self.texture_points.SetPoint(1, size[0], 0, 0.0) - self.texture_points.SetPoint(2, size[0], size[1], 0.0) - self.texture_points.SetPoint(3, 0, size[1], 0.0) - self.texture_polydata.SetPoints(self.texture_points) - - def _set_position(self, coords): - """Set the lower-left corner position of this UI component. - - Parameters - ---------- - coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - self.actor.SetPosition(*coords) - - @property - def color(self): - """Get the button's color.""" - color = self.actor.GetProperty().GetColor() - return np.asarray(color) - - @color.setter - def color(self, color): - """Set the button's color. - - Parameters - ---------- - color : (float, float, float) - RGB. Must take values in [0, 1]. - - """ - self.actor.GetProperty().SetColor(*color) - - def scale(self, factor): - """Scale the button. - - Parameters - ---------- - factor : (float, float) - Scaling factor (width, height) in pixels. - - """ - self.resize(self.size * factor) - - def set_icon_by_name(self, icon_name): - """Set the button icon using its name. - - Parameters - ---------- - icon_name : str - - """ - icon_id = self.icon_names.index(icon_name) - self.set_icon(self.icons[icon_id][1]) +# Parameters +# ---------- +# flag : bool +# Sets text bold if True. + +# """ +# self.actor.GetTextProperty().SetBold(flag) + +# @property +# def italic(self): +# """Return whether the text is italicised. + +# Returns +# ------- +# bool +# Text is italicised if True. + +# """ +# return self.actor.GetTextProperty().GetItalic() + +# @italic.setter +# def italic(self, flag): +# """Italicise/un-italicise text. + +# Parameters +# ---------- +# flag : bool +# Italicises text if True. + +# """ +# self.actor.GetTextProperty().SetItalic(flag) + +# @property +# def shadow(self): +# """Return whether the text has shadow. + +# Returns +# ------- +# bool +# Text is shadowed if True. + +# """ +# return self.actor.GetTextProperty().GetShadow() + +# @shadow.setter +# def shadow(self, flag): +# """Add/remove text shadow. + +# Parameters +# ---------- +# flag : bool +# Shadows text if True. + +# """ +# self.actor.GetTextProperty().SetShadow(flag) + +# @property +# def color(self): +# """Get text color. + +# Returns +# ------- +# (float, float, float) +# Returns text color in RGB. + +# """ +# return self.actor.GetTextProperty().GetColor() + +# @color.setter +# def color(self, color=(1, 0, 0)): +# """Set text color. + +# Parameters +# ---------- +# color : (float, float, float) +# RGB: Values must be between 0-1. + +# """ +# self.actor.GetTextProperty().SetColor(*color) + +# @property +# def background_color(self): +# """Get background color. + +# Returns +# ------- +# (float, float, float) or None +# If None, there no background color. +# Otherwise, background color in RGB. + +# """ +# if not self.have_bg: +# return None + +# return self.background.color + +# @background_color.setter +# def background_color(self, color): +# """Set text color. + +# Parameters +# ---------- +# color : (float, float, float) or None +# If None, remove background. +# Otherwise, RGB values (must be between 0-1). + +# """ +# if color is None: +# # Remove background. +# self.have_bg = False +# self.background.set_visibility(False) + +# else: +# self.have_bg = True +# self.background.set_visibility(True) +# self.background.color = color + +# @property +# def auto_font_scale(self): +# """Return whether text font is automatically scaled. + +# Returns +# ------- +# bool +# Text is auto_font_scaled if True. + +# """ +# return self._auto_font_scale + +# @auto_font_scale.setter +# def auto_font_scale(self, flag): +# """Add/remove text auto_font_scale. + +# Parameters +# ---------- +# flag : bool +# Automatically scales the text font if True. + +# """ +# self._auto_font_scale = flag +# if flag: +# self.actor.SetTextScaleModeToProp() +# self._justification = "left" +# self.update_bounding_box(size=self.size) +# else: +# self.actor.SetTextScaleModeToNone() + +# @property +# def dynamic_bbox(self): +# """Automatically resize the bounding box according to the content. + +# Returns +# ------- +# bool +# Bounding box is dynamic if True. + +# """ +# return self._dynamic_bbox + +# @dynamic_bbox.setter +# def dynamic_bbox(self, flag): +# """Add/remove dynamic_bbox. + +# Parameters +# ---------- +# flag : bool +# The text bounding box is dynamic if True. + +# """ +# self._dynamic_bbox = flag +# if flag: +# self.update_bounding_box() + +# def update_alignment(self): +# """Update Text Alignment.""" +# text_property = self.actor.GetTextProperty() +# updated_text_position = [0, 0] + +# if self.justification.lower() == "left": +# text_property.SetJustificationToLeft() +# updated_text_position[0] = self.boundingbox[0] +# elif self.justification.lower() == "center": +# text_property.SetJustificationToCentered() +# updated_text_position[0] = ( +# self.boundingbox[0] + (self.boundingbox[2] - self.boundingbox[0]) // 2 +# ) +# elif self.justification.lower() == "right": +# text_property.SetJustificationToRight() +# updated_text_position[0] = self.boundingbox[2] +# else: +# msg = "Text can only be justified left, right and center." +# raise ValueError(msg) + +# if self.vertical_justification.lower() == "bottom": +# text_property.SetVerticalJustificationToBottom() +# updated_text_position[1] = self.boundingbox[1] +# elif self.vertical_justification.lower() == "middle": +# text_property.SetVerticalJustificationToCentered() +# updated_text_position[1] = ( +# self.boundingbox[1] + (self.boundingbox[3] - self.boundingbox[1]) // 2 +# ) +# elif self.vertical_justification.lower() == "top": +# text_property.SetVerticalJustificationToTop() +# updated_text_position[1] = self.boundingbox[3] +# else: +# msg = "Vertical justification must be: bottom, middle or top." +# raise ValueError(msg) + +# self.actor.SetPosition(updated_text_position) + +# def cal_size_from_message(self): +# """Calculate size of background according to the message it contains.""" +# lines = self.message.split("\n") +# max_length = max(len(line) for line in lines) +# return [max_length * self.font_size, len(lines) * self.font_size] + +# @warn_on_args_to_kwargs() +# def update_bounding_box(self, *, size=None): +# """Update Text Bounding Box. + +# Parameters +# ---------- +# size : (int, int) or None +# If None, calculates bounding box. +# Otherwise, uses the given size. + +# """ +# if size is None: +# size = self.cal_size_from_message() + +# self.boundingbox = [ +# self.position[0], +# self.position[1], +# self.position[0] + size[0], +# self.position[1] + size[1], +# ] +# self.background.resize(size) + +# if self.auto_font_scale: +# self.actor.SetPosition2( +# self.boundingbox[2] - self.boundingbox[0], +# self.boundingbox[3] - self.boundingbox[1], +# ) +# else: +# self.update_alignment() + +# def _set_position(self, position): +# """Set text actor position. + +# Parameters +# ---------- +# position : (float, float) +# The new position. (x, y) in pixels. + +# """ +# self.actor.SetPosition(*position) +# self.background.position = position + +# def _get_size(self): +# bb_size = ( +# self.boundingbox[2] - self.boundingbox[0], +# self.boundingbox[3] - self.boundingbox[1], +# ) +# if self.dynamic_bbox or self.auto_font_scale or sum(bb_size): +# return bb_size +# return self.cal_size_from_message() + + +# class Button2D(UI): +# """A 2D overlay button and is of type vtkTexturedActor2D. + +# Currently supports:: + +# - Multiple icons. +# - Switching between icons. + +# """ + +# @warn_on_args_to_kwargs() +# def __init__(self, icon_fnames, *, position=(0, 0), size=(30, 30)): +# """Init class instance. + +# Parameters +# ---------- +# icon_fnames : List(string, string) +# ((iconname, filename), (iconname, filename), ....) +# position : (float, float), optional +# Absolute coordinates (x, y) of the lower-left corner of the button. +# size : (int, int), optional +# Width and height in pixels of the button. + +# """ +# super(Button2D, self).__init__(position=position) + +# self.icon_extents = {} +# self.icons = self._build_icons(icon_fnames) +# self.icon_names = [icon[0] for icon in self.icons] +# self.current_icon_id = 0 +# self.current_icon_name = self.icon_names[self.current_icon_id] +# self.set_icon(self.icons[self.current_icon_id][1]) +# self.resize(size) + +# def _get_size(self): +# lower_left_corner = self.texture_points.GetPoint(0) +# upper_right_corner = self.texture_points.GetPoint(2) +# size = np.array(upper_right_corner) - np.array(lower_left_corner) +# return abs(size[:2]) + +# def _build_icons(self, icon_fnames): +# """Convert file names to ImageData. + +# A pre-processing step to prevent re-read of file names during every +# state change. + +# Parameters +# ---------- +# icon_fnames : List(string, string) +# ((iconname, filename), (iconname, filename), ....) + +# Returns +# ------- +# icons : List +# A list of corresponding ImageData. + +# """ +# icons = [] +# for icon_name, icon_fname in icon_fnames: +# icons.append((icon_name, load_image(icon_fname, as_vtktype=True))) + +# return icons + +# def _setup(self): +# """Set up this UI component. + +# Creating the button actor used internally. + +# """ +# # This is highly inspired by +# # https://github.com/Kitware/VTK/blob/c3ec2495b183e3327820e927af7f8f90d34c3474/Interaction/Widgets/vtkBalloonRepresentation.cxx#L47 + +# self.texture_polydata = PolyData() +# self.texture_points = Points() +# self.texture_points.SetNumberOfPoints(4) + +# polys = CellArray() +# polys.InsertNextCell(4) +# polys.InsertCellPoint(0) +# polys.InsertCellPoint(1) +# polys.InsertCellPoint(2) +# polys.InsertCellPoint(3) +# self.texture_polydata.SetPolys(polys) + +# tc = FloatArray() +# tc.SetNumberOfComponents(2) +# tc.SetNumberOfTuples(4) +# tc.InsertComponent(0, 0, 0.0) +# tc.InsertComponent(0, 1, 0.0) +# tc.InsertComponent(1, 0, 1.0) +# tc.InsertComponent(1, 1, 0.0) +# tc.InsertComponent(2, 0, 1.0) +# tc.InsertComponent(2, 1, 1.0) +# tc.InsertComponent(3, 0, 0.0) +# tc.InsertComponent(3, 1, 1.0) +# self.texture_polydata.GetPointData().SetTCoords(tc) + +# texture_mapper = PolyDataMapper2D() +# texture_mapper = set_input(texture_mapper, self.texture_polydata) + +# button = TexturedActor2D() +# button.SetMapper(texture_mapper) + +# self.texture = Texture() +# button.SetTexture(self.texture) + +# button_property = Property2D() +# button_property.SetOpacity(1.0) +# button.SetProperty(button_property) +# self.actor = button + +# # Add default events listener to the VTK actor. +# self.handle_events(self.actor) + +# def _get_actors(self): +# """Get the actors composing this UI component.""" +# return [self.actor] + +# def _add_to_scene(self, scene): +# """Add all subcomponents or VTK props that compose this UI component. + +# Parameters +# ---------- +# scene : scene + +# """ +# scene.add(self.actor) + +# def resize(self, size): +# """Resize the button. + +# Parameters +# ---------- +# size : (float, float) +# Button size (width, height) in pixels. + +# """ +# # Update actor. +# self.texture_points.SetPoint(0, 0, 0, 0.0) +# self.texture_points.SetPoint(1, size[0], 0, 0.0) +# self.texture_points.SetPoint(2, size[0], size[1], 0.0) +# self.texture_points.SetPoint(3, 0, size[1], 0.0) +# self.texture_polydata.SetPoints(self.texture_points) + +# def _set_position(self, coords): +# """Set the lower-left corner position of this UI component. + +# Parameters +# ---------- +# coords: (float, float) +# Absolute pixel coordinates (x, y). + +# """ +# self.actor.SetPosition(*coords) + +# @property +# def color(self): +# """Get the button's color.""" +# color = self.actor.GetProperty().GetColor() +# return np.asarray(color) + +# @color.setter +# def color(self, color): +# """Set the button's color. + +# Parameters +# ---------- +# color : (float, float, float) +# RGB. Must take values in [0, 1]. + +# """ +# self.actor.GetProperty().SetColor(*color) + +# def scale(self, factor): +# """Scale the button. + +# Parameters +# ---------- +# factor : (float, float) +# Scaling factor (width, height) in pixels. + +# """ +# self.resize(self.size * factor) + +# def set_icon_by_name(self, icon_name): +# """Set the button icon using its name. + +# Parameters +# ---------- +# icon_name : str + +# """ +# icon_id = self.icon_names.index(icon_name) +# self.set_icon(self.icons[icon_id][1]) - def set_icon(self, icon): - """Modify the icon used by the vtkTexturedActor2D. +# def set_icon(self, icon): +# """Modify the icon used by the vtkTexturedActor2D. - Parameters - ---------- - icon : imageData +# Parameters +# ---------- +# icon : imageData - """ - self.texture = set_input(self.texture, icon) +# """ +# self.texture = set_input(self.texture, icon) - def next_icon_id(self): - """Set the next icon ID while cycling through icons.""" - self.current_icon_id += 1 - if self.current_icon_id == len(self.icons): - self.current_icon_id = 0 - self.current_icon_name = self.icon_names[self.current_icon_id] - - def next_icon(self): - """Increment the state of the Button. - - Also changes the icon. - """ - self.next_icon_id() - self.set_icon(self.icons[self.current_icon_id][1]) +# def next_icon_id(self): +# """Set the next icon ID while cycling through icons.""" +# self.current_icon_id += 1 +# if self.current_icon_id == len(self.icons): +# self.current_icon_id = 0 +# self.current_icon_name = self.icon_names[self.current_icon_id] + +# def next_icon(self): +# """Increment the state of the Button. + +# Also changes the icon. +# """ +# self.next_icon_id() +# self.set_icon(self.icons[self.current_icon_id][1]) diff --git a/fury/ui/elements.py b/fury/ui/elements.py index 596c34654..f91bd588d 100644 --- a/fury/ui/elements.py +++ b/fury/ui/elements.py @@ -1,4692 +1,4702 @@ -"""UI components module.""" - -__all__ = [ - "TextBox2D", - "LineSlider2D", - "LineDoubleSlider2D", - "RingSlider2D", - "RangeSlider", - "Checkbox", - "Option", - "RadioButton", - "ComboBox2D", - "ListBox2D", - "ListBoxItem2D", - "FileMenu2D", - "DrawShape", - "DrawPanel", - "PlaybackPanel", - "Card2D", - "SpinBox", -] - -from collections import OrderedDict -from numbers import Number -import os -from string import printable -from urllib.request import urlopen - -from PIL import Image, UnidentifiedImageError -import numpy as np - -from fury.data import read_viz_icons -from fury.decorators import warn_on_args_to_kwargs -from fury.lib import Command -from fury.ui.containers import ImageContainer2D, Panel2D -from fury.ui.core import UI, Button2D, Disk2D, Rectangle2D, TextBlock2D -from fury.ui.helpers import ( - TWO_PI, - cal_bounding_box_2d, - clip_overflow, - rotate_2d, - wrap_overflow, -) -from fury.utils import set_polydata_vertices, update_actor, vertices_from_actor - - -class TextBox2D(UI): - """An editable 2D text box that behaves as a UI component. - - Currently supports: - - Basic text editing. - - Cursor movements. - - Single and multi-line text boxes. - - Pre text formatting (text needs to be formatted beforehand). - - Attributes - ---------- - text : str - The current text state. - actor : :class:`vtkActor2d` - The text actor. - width : int - The number of characters in a single line of text. - height : int - The number of lines in the textbox. - window_left : int - Left limit of visible text in the textbox. - window_right : int - Right limit of visible text in the textbox. - caret_pos : int - Position of the caret in the text. - init : bool - Flag which says whether the textbox has just been initialized. - - """ - - @warn_on_args_to_kwargs() - def __init__( - self, - width, - height, - *, - text="Enter Text", - position=(100, 10), - color=(0, 0, 0), - font_size=18, - font_family="Arial", - justification="left", - bold=False, - italic=False, - shadow=False, - ): - """Init this UI element. - - Parameters - ---------- - width : int - The number of characters in a single line of text. - height : int - The number of lines in the textbox. - text : str - The initial text while building the actor. - position : (float, float) - (x, y) in pixels. - color : (float, float, float) - RGB: Values must be between 0-1. - font_size : int - Size of the text font. - font_family : str - Currently only supports Arial. - justification : str - left, right or center. - bold : bool - Makes text bold. - italic : bool - Makes text italicised. - shadow : bool - Adds text shadow. - - """ - super(TextBox2D, self).__init__(position=position) - - self.message = text - self.text.message = text - self.text.font_size = font_size - self.text.font_family = font_family - self.text.justification = justification - self.text.bold = bold - self.text.italic = italic - self.text.shadow = shadow - self.text.color = color - self.text.background_color = (1, 1, 1) - - self.width = width - self.height = height - self.window_left = 0 - self.window_right = 0 - self.caret_pos = 0 - self.init = True - - self.off_focus = lambda ui: None - - def _setup(self): - """Setup this UI component. - - Create the TextBlock2D component used for the textbox. - """ - self.text = TextBlock2D(dynamic_bbox=True) - - # Add default events listener for this UI component. - self.text.on_left_mouse_button_pressed = self.left_button_press - self.text.on_key_press = self.key_press - - def _get_actors(self): - """Get the actors composing this UI component.""" - return self.text.actors - - def _add_to_scene(self, scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - scene : scene - - """ - self.text.add_to_scene(scene) - - def _set_position(self, coords): - """Set the lower-left corner position of this UI component. - - Parameters - ---------- - coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - self.text.position = coords - - def _get_size(self): - return self.text.size - - def set_message(self, message): - """Set custom text to textbox. - - Parameters - ---------- - message: str - The custom message to be set. - - """ - self.message = message - self.text.message = message - self.init = False - self.window_right = len(self.message) - self.window_left = 0 - self.caret_pos = self.window_right - - def width_set_text(self, text): - """Add newlines to text where necessary. - - This is needed for multi-line text boxes. - - Parameters - ---------- - text : str - The final text to be formatted. - - Returns - ------- - str - A multi line formatted text. - - """ - multi_line_text = "" - for i, t in enumerate(text): - multi_line_text += t - if (i + 1) % self.width == 0: - multi_line_text += "\n" - return multi_line_text.rstrip("\n") - - def handle_character(self, key, key_char): - """Handle button events. - - # TODO: Need to handle all kinds of characters like !, +, etc. - - Parameters - ---------- - character : str - - """ - if key.lower() == "return": - self.render_text(show_caret=False) - self.off_focus(self) - return True - elif key_char != "" and key_char in printable: - self.add_character(key_char) - if key.lower() == "backspace": - self.remove_character() - elif key.lower() == "left": - self.move_left() - elif key.lower() == "right": - self.move_right() - - self.render_text() - return False - - def move_caret_right(self): - """Move the caret towards right.""" - self.caret_pos = min(self.caret_pos + 1, len(self.message)) - - def move_caret_left(self): - """Move the caret towards left.""" - self.caret_pos = max(self.caret_pos - 1, 0) - - def right_move_right(self): - """Move right boundary of the text window right-wards.""" - if self.window_right <= len(self.message): - self.window_right += 1 - - def right_move_left(self): - """Move right boundary of the text window left-wards.""" - if self.window_right > 0: - self.window_right -= 1 - - def left_move_right(self): - """Move left boundary of the text window right-wards.""" - if self.window_left <= len(self.message): - self.window_left += 1 - - def left_move_left(self): - """Move left boundary of the text window left-wards.""" - if self.window_left > 0: - self.window_left -= 1 - - def add_character(self, character): - """Insert a character into the text and moves window and caret. - - Parameters - ---------- - character : str - - """ - if len(character) > 1 and character.lower() != "space": - return - if character.lower() == "space": - character = " " - self.message = ( - self.message[: self.caret_pos] + character + self.message[self.caret_pos :] - ) - self.move_caret_right() - if self.window_right - self.window_left == self.height * self.width - 1: - self.left_move_right() - self.right_move_right() - - def remove_character(self): - """Remove a character and moves window and caret accordingly.""" - if self.caret_pos == 0: - return - self.message = ( - self.message[: self.caret_pos - 1] + self.message[self.caret_pos :] - ) - self.move_caret_left() - if len(self.message) < self.height * self.width - 1: - self.right_move_left() - if self.window_right - self.window_left == self.height * self.width - 1: - if self.window_left > 0: - self.left_move_left() - self.right_move_left() - - def move_left(self): - """Handle left button press.""" - self.move_caret_left() - if self.caret_pos == self.window_left - 1: - if self.window_right - self.window_left == self.height * self.width - 1: - self.left_move_left() - self.right_move_left() - - def move_right(self): - """Handle right button press.""" - self.move_caret_right() - if self.caret_pos == self.window_right + 1: - if self.window_right - self.window_left == self.height * self.width - 1: - self.left_move_right() - self.right_move_right() - - def showable_text(self, show_caret): - """Chop out text to be shown on the screen. - - Parameters - ---------- - show_caret : bool - Whether or not to show the caret. - - """ - if show_caret: - ret_text = ( - self.message[: self.caret_pos] + "_" + self.message[self.caret_pos :] - ) - else: - ret_text = self.message - ret_text = ret_text[self.window_left : self.window_right + 1] - return ret_text - - @warn_on_args_to_kwargs() - def render_text(self, *, show_caret=True): - """Render text after processing. - - Parameters - ---------- - show_caret : bool - Whether or not to show the caret. - - """ - text = self.showable_text(show_caret) - if text == "": - text = "Enter Text" - self.text.message = self.width_set_text(text) - - def edit_mode(self): - """Turn on edit mode.""" - if self.init: - self.message = "" - self.init = False - self.caret_pos = 0 - self.render_text() - - def left_button_press(self, i_ren, _obj, _textbox_object): - """Handle left button press for textbox. - - Parameters - ---------- - i_ren: :class:`CustomInteractorStyle` - obj: :class:`vtkActor` - The picked actor - _textbox_object: :class:`TextBox2D` - - """ - i_ren.add_active_prop(self.text.actor) - self.edit_mode() - i_ren.force_render() - - def key_press(self, i_ren, _obj, _textbox_object): - """Handle Key press for textboxself. - - Parameters - ---------- - i_ren: :class:`CustomInteractorStyle` - obj: :class:`vtkActor` - The picked actor - _textbox_object: :class:`TextBox2D` - - """ - key = i_ren.event.key - key_char = i_ren.event.key_char - is_done = self.handle_character(key, key_char) - if is_done: - i_ren.remove_active_prop(self.text.actor) - - i_ren.force_render() - - -class LineSlider2D(UI): - """A 2D Line Slider. - - A sliding handle on a line with a percentage indicator. - - Attributes - ---------- - line_width : int - Width of the line on which the disk will slide. - length : int - Length of the slider. - track : :class:`Rectangle2D` - The line on which the slider's handle moves. - handle : :class:`Disk2D` - The moving part of the slider. - text : :class:`TextBlock2D` - The text that shows percentage. - shape : string - Describes the shape of the handle. - Currently supports 'disk' and 'square'. - default_color : (float, float, float) - Color of the handle when in unpressed state. - active_color : (float, float, float) - Color of the handle when it is pressed. - - """ - - @warn_on_args_to_kwargs() - def __init__( - self, - *, - center=(0, 0), - initial_value=50, - min_value=0, - max_value=100, - length=200, - line_width=5, - inner_radius=0, - outer_radius=10, - handle_side=20, - font_size=16, - orientation="horizontal", - text_alignment="", - text_template="{value:.1f} ({ratio:.0%})", - shape="disk", - ): - """Init this UI element. - - Parameters - ---------- - center : (float, float) - Center of the slider's center. - initial_value : float - Initial value of the slider. - min_value : float - Minimum value of the slider. - max_value : float - Maximum value of the slider. - length : int - Length of the slider. - line_width : int - Width of the line on which the disk will slide. - inner_radius : int - Inner radius of the handles (if disk). - outer_radius : int - Outer radius of the handles (if disk). - handle_side : int - Side length of the handles (if square). - font_size : int - Size of the text to display alongside the slider (pt). - orientation : str - horizontal or vertical - text_alignment : str - define text alignment on a slider. Left (default)/ right for the - vertical slider or top/bottom (default) for an horizontal slider. - text_template : str, callable - If str, text template can contain one or multiple of the - replacement fields: `{value:}`, `{ratio:}`. - If callable, this instance of `:class:LineSlider2D` will be - passed as argument to the text template function. - shape : string - Describes the shape of the handle. - Currently supports 'disk' and 'square'. - - """ - self.shape = shape - self.orientation = orientation.lower().strip() - self.align_dict = { - "horizontal": ["top", "bottom"], - "vertical": ["left", "right"], - } - self.default_color = (1, 1, 1) - self.active_color = (0, 0, 1) - self.alignment = text_alignment.lower() - super(LineSlider2D, self).__init__() - - if self.orientation == "horizontal": - self.alignment = "bottom" if not self.alignment else self.alignment - self.track.width = length - self.track.height = line_width - elif self.orientation == "vertical": - self.alignment = "left" if not self.alignment else self.alignment - self.track.width = line_width - self.track.height = length - else: - raise ValueError("Unknown orientation") - - if self.alignment not in self.align_dict[self.orientation]: - raise ValueError( - "Unknown alignment: choose from '{}' or '{}'".format( - *self.align_dict[self.orientation] - ) - ) - - if shape == "disk": - self.handle.inner_radius = inner_radius - self.handle.outer_radius = outer_radius - elif shape == "square": - self.handle.width = handle_side - self.handle.height = handle_side - self.center = center - - self.min_value = min_value - self.max_value = max_value - self.text.font_size = font_size - self.text_template = text_template - - # Offer some standard hooks to the user. - self.on_change = lambda ui: None - self.on_value_changed = lambda ui: None - self.on_moving_slider = lambda ui: None - - self.value = initial_value - self.update() - - def _setup(self): - """Setup this UI component. - - Create the slider's track (Rectangle2D), the handle (Disk2D) and - the text (TextBlock2D). - """ - # Slider's track - self.track = Rectangle2D() - self.track.color = (1, 0, 0) - - # Slider's handle - if self.shape == "disk": - self.handle = Disk2D(outer_radius=1) - elif self.shape == "square": - self.handle = Rectangle2D(size=(1, 1)) - self.handle.color = self.default_color - - # Slider Text - self.text = TextBlock2D(justification="center", vertical_justification="top") - - # Add default events listener for this UI component. - self.track.on_left_mouse_button_pressed = self.track_click_callback - self.track.on_left_mouse_button_dragged = self.handle_move_callback - self.track.on_left_mouse_button_released = self.handle_release_callback - self.handle.on_left_mouse_button_dragged = self.handle_move_callback - self.handle.on_left_mouse_button_released = self.handle_release_callback - - def _get_actors(self): - """Get the actors composing this UI component.""" - return self.track.actors + self.handle.actors + self.text.actors - - def _add_to_scene(self, scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - scene : scene - - """ - self.track.add_to_scene(scene) - self.handle.add_to_scene(scene) - self.text.add_to_scene(scene) - - def _get_size(self): - # Consider the handle's size when computing the slider's size. - width = None - height = None - if self.orientation == "horizontal": - width = self.track.width + self.handle.size[0] - height = max(self.track.height, self.handle.size[1]) - else: - width = max(self.track.width, self.handle.size[0]) - height = self.track.height + self.handle.size[1] - - return np.array([width, height]) - - def _set_position(self, coords): - """Set the lower-left corner position of this UI component. - - Parameters - ---------- - coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - # Offset the slider line by the handle's radius. - track_position = coords + self.handle.size / 2.0 - if self.orientation == "horizontal": - # Offset the slider line height by half the slider line width. - track_position[1] -= self.track.size[1] / 2.0 - else: - # Offset the slider line width by half the slider line height. - track_position[0] += self.track.size[0] / 2.0 - - self.track.position = track_position - self.handle.position = self.handle.position.astype(float) - self.handle.position += coords - self.position - # Position the text below the handle. - if self.orientation == "horizontal": - align = 35 if self.alignment == "top" else -10 - self.text.position = ( - self.handle.center[0], - self.handle.position[1] + align, - ) - else: - align = 70 if self.alignment == "right" else -35 - self.text.position = ( - self.handle.position[0] + align, - self.handle.center[1] + 2, - ) - - @property - def bottom_y_position(self): - return self.track.position[1] - - @property - def top_y_position(self): - return self.track.position[1] + self.track.size[1] - - @property - def left_x_position(self): - return self.track.position[0] - - @property - def right_x_position(self): - return self.track.position[0] + self.track.size[0] - - def set_position(self, position): - """Set the disk's position. - - Parameters - ---------- - position : (float, float) - The absolute position of the disk (x, y). - - """ - # Move slider disk. - if self.orientation == "horizontal": - x_position = position[0] - x_position = max(x_position, self.left_x_position) - x_position = min(x_position, self.right_x_position) - self.handle.center = (x_position, self.track.center[1]) - else: - y_position = position[1] - y_position = max(y_position, self.bottom_y_position) - y_position = min(y_position, self.top_y_position) - self.handle.center = (self.track.center[0], y_position) - self.update() # Update information. - - @property - def value(self): - return self._value - - @value.setter - def value(self, value): - value_range = self.max_value - self.min_value - self.ratio = (value - self.min_value) / value_range if value_range else 0 - self.on_value_changed(self) - - @property - def ratio(self): - return self._ratio - - @ratio.setter - def ratio(self, ratio): - position_x = self.left_x_position + ratio * self.track.width - position_y = self.bottom_y_position + ratio * self.track.height - self.set_position((position_x, position_y)) - - def format_text(self): - """Return formatted text to display along the slider.""" - if callable(self.text_template): - return self.text_template(self) - return self.text_template.format(ratio=self.ratio, value=self.value) - - def update(self): - """Update the slider.""" - # Compute the ratio determined by the position of the slider disk. - disk_position_x = None - disk_position_y = None - - if self.orientation == "horizontal": - length = float(self.right_x_position - self.left_x_position) - length = np.round(length, decimals=6) - if length != self.track.width: - raise ValueError("Disk position outside the slider line") - disk_position_x = self.handle.center[0] - self._ratio = (disk_position_x - self.left_x_position) / length - else: - length = float(self.top_y_position - self.bottom_y_position) - if length != self.track.height: - raise ValueError("Disk position outside the slider line") - disk_position_y = self.handle.center[1] - self._ratio = (disk_position_y - self.bottom_y_position) / length - - # Compute the selected value considering min_value and max_value. - value_range = self.max_value - self.min_value - self._value = self.min_value + self.ratio * value_range - - # Update text. - text = self.format_text() - self.text.message = text - - # Move the text below the slider's handle. - if self.orientation == "horizontal": - self.text.position = (disk_position_x, self.text.position[1]) - else: - self.text.position = (self.text.position[0], disk_position_y) - - self.on_change(self) - - def track_click_callback(self, i_ren, _vtkactor, _slider): - """Update disk position and grab the focus. - - Parameters - ---------- - i_ren : :class:`CustomInteractorStyle` - vtkactor : :class:`vtkActor` - The picked actor - _slider : :class:`LineSlider2D` - - """ - position = i_ren.event.position - self.set_position(position) - self.on_moving_slider(self) - i_ren.force_render() - i_ren.event.abort() # Stop propagating the event. - - def handle_move_callback(self, i_ren, _vtkactor, _slider): - """Handle movement. - - Parameters - ---------- - i_ren : :class:`CustomInteractorStyle` - vtkactor : :class:`vtkActor` - The picked actor - slider : :class:`LineSlider2D` - - """ - self.handle.color = self.active_color - position = i_ren.event.position - self.set_position(position) - self.on_moving_slider(self) - i_ren.force_render() - i_ren.event.abort() # Stop propagating the event. - - def handle_release_callback(self, i_ren, _vtkactor, _slider): - """Change color when handle is released. - - Parameters - ---------- - i_ren : :class:`CustomInteractorStyle` - vtkactor : :class:`vtkActor` - The picked actor - slider : :class:`LineSlider2D` - - """ - self.handle.color = self.default_color - i_ren.force_render() - - -class LineDoubleSlider2D(UI): - """A 2D Line Slider with two sliding rings. - - Useful for setting min and max values for something. - - Currently supports: - - Setting positions of both disks. - - Attributes - ---------- - line_width : int - Width of the line on which the disk will slide. - length : int - Length of the slider. - track : :class:`vtkActor` - The line on which the handles move. - handles : [:class:`vtkActor`, :class:`vtkActor`] - The moving slider disks. - text : [:class:`TextBlock2D`, :class:`TextBlock2D`] - The texts that show the values of the disks. - shape : string - Describes the shape of the handle. - Currently supports 'disk' and 'square'. - default_color : (float, float, float) - Color of the handles when in unpressed state. - active_color : (float, float, float) - Color of the handles when they are pressed. - - """ - - @warn_on_args_to_kwargs() - def __init__( - self, - *, - line_width=5, - inner_radius=0, - outer_radius=10, - handle_side=20, - center=(450, 300), - length=200, - initial_values=(0, 100), - min_value=0, - max_value=100, - font_size=16, - text_template="{value:.1f}", - orientation="horizontal", - shape="disk", - ): - """Init this UI element. - - Parameters - ---------- - line_width : int - Width of the line on which the disk will slide. - inner_radius : int - Inner radius of the handles (if disk). - outer_radius : int - Outer radius of the handles (if disk). - handle_side : int - Side length of the handles (if square). - center : (float, float) - Center of the slider. - length : int - Length of the slider. - initial_values : (float, float) - Initial values of the two handles. - min_value : float - Minimum value of the slider. - max_value : float - Maximum value of the slider. - font_size : int - Size of the text to display alongside the slider (pt). - text_template : str, callable - If str, text template can contain one or multiple of the - replacement fields: `{value:}`, `{ratio:}`. - If callable, this instance of `:class:LineDoubleSlider2D` will be - passed as argument to the text template function. - orientation : str - horizontal or vertical - shape : string - Describes the shape of the handle. - Currently supports 'disk' and 'square'. - - """ - self.shape = shape - self.default_color = (1, 1, 1) - self.active_color = (0, 0, 1) - self.orientation = orientation.lower() - super(LineDoubleSlider2D, self).__init__() - - if self.orientation == "horizontal": - self.track.width = length - self.track.height = line_width - elif self.orientation == "vertical": - self.track.width = line_width - self.track.height = length - else: - raise ValueError("Unknown orientation") - - self.center = center - if shape == "disk": - self.handles[0].inner_radius = inner_radius - self.handles[0].outer_radius = outer_radius - self.handles[1].inner_radius = inner_radius - self.handles[1].outer_radius = outer_radius - elif shape == "square": - self.handles[0].width = handle_side - self.handles[0].height = handle_side - self.handles[1].width = handle_side - self.handles[1].height = handle_side - - self.min_value = min_value - self.max_value = max_value - self.text[0].font_size = font_size - self.text[1].font_size = font_size - self.text_template = text_template - - # Offer some standard hooks to the user. - self.on_change = lambda ui: None - self.on_value_changed = lambda ui: None - self.on_moving_slider = lambda ui: None - - # Setting the handle positions will also update everything. - self._values = [initial_values[0], initial_values[1]] - self._ratio = [None, None] - self.left_disk_value = initial_values[0] - self.right_disk_value = initial_values[1] - self.bottom_disk_value = initial_values[0] - self.top_disk_value = initial_values[1] - - def _setup(self): - """Setup this UI component. - - Create the slider's track (Rectangle2D), the handles (Disk2D) and - the text (TextBlock2D). - - """ - # Slider's track - self.track = Rectangle2D() - self.track.color = (1, 0, 0) - - # Handles - self.handles = [] - if self.shape == "disk": - self.handles.append(Disk2D(outer_radius=1)) - self.handles.append(Disk2D(outer_radius=1)) - elif self.shape == "square": - self.handles.append(Rectangle2D(size=(1, 1))) - self.handles.append(Rectangle2D(size=(1, 1))) - self.handles[0].color = self.default_color - self.handles[1].color = self.default_color - - # Slider Text - self.text = [ - TextBlock2D(justification="center", vertical_justification="top"), - TextBlock2D(justification="center", vertical_justification="top"), - ] - - # Add default events listener for this UI component. - self.track.on_left_mouse_button_dragged = self.handle_move_callback - self.handles[0].on_left_mouse_button_dragged = self.handle_move_callback - self.handles[1].on_left_mouse_button_dragged = self.handle_move_callback - self.handles[0].on_left_mouse_button_released = self.handle_release_callback - self.handles[1].on_left_mouse_button_released = self.handle_release_callback - - def _get_actors(self): - """Get the actors composing this UI component.""" - return ( - self.track.actors - + self.handles[0].actors - + self.handles[1].actors - + self.text[0].actors - + self.text[1].actors - ) - - def _add_to_scene(self, scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - scene : scene - - """ - self.track.add_to_scene(scene) - self.handles[0].add_to_scene(scene) - self.handles[1].add_to_scene(scene) - self.text[0].add_to_scene(scene) - self.text[1].add_to_scene(scene) - - def _get_size(self): - # Consider the handle's size when computing the slider's size. - width = None - height = None - if self.orientation == "horizontal": - width = self.track.width + self.handles[0].size[0] - height = max(self.track.height, self.handles[0].size[1]) - else: - width = max(self.track.width, self.handles[0].size[0]) - height = self.track.height + self.handles[0].size[1] - - return np.array([width, height]) - - def _set_position(self, coords): - """Set the lower-left corner position of this UI component. - - Parameters - ---------- - coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - # Offset the slider line by the handle's radius. - track_position = coords - if self.orientation == "horizontal": - # Offset the slider line height by half the slider line width. - track_position[1] -= self.track.size[1] / 2.0 - else: - # Offset the slider line width by half the slider line height. - track_position[0] -= self.track.size[0] / 2.0 - - self.track.position = track_position - - self.handles[0].position = self.handles[0].position.astype(float) - self.handles[1].position = self.handles[1].position.astype(float) - - self.handles[0].position += coords - self.position - self.handles[1].position += coords - self.position - - if self.orientation == "horizontal": - # Position the text below the handles. - self.text[0].position = ( - self.handles[0].center[0], - self.handles[0].position[1] - 10, - ) - self.text[1].position = ( - self.handles[1].center[0], - self.handles[1].position[1] - 10, - ) - else: - # Position the text to the left of the handles. - self.text[0].position = ( - self.handles[0].center[0] - 35, - self.handles[0].position[1], - ) - self.text[1].position = ( - self.handles[1].center[0] - 35, - self.handles[1].position[1], - ) - - @property - def bottom_y_position(self): - return self.track.position[1] - - @property - def top_y_position(self): - return self.track.position[1] + self.track.size[1] - - @property - def left_x_position(self): - return self.track.position[0] - - @property - def right_x_position(self): - return self.track.position[0] + self.track.size[0] - - def value_to_ratio(self, value): - """Convert the value of a disk to the ratio. - - Parameters - ---------- - value : float - - """ - value_range = self.max_value - self.min_value - return (value - self.min_value) / value_range if value_range else 0 - - def ratio_to_coord(self, ratio): - """Convert the ratio to the absolute coordinate. - - Parameters - ---------- - ratio : float - - """ - if self.orientation == "horizontal": - return self.left_x_position + ratio * self.track.width - return self.bottom_y_position + ratio * self.track.height - - def coord_to_ratio(self, coord): - """Convert the x coordinate of a disk to the ratio. - - Parameters - ---------- - coord : float - - """ - if self.orientation == "horizontal": - return (coord - self.left_x_position) / float(self.track.width) - return (coord - self.bottom_y_position) / float(self.track.height) - - def ratio_to_value(self, ratio): - """Convert the ratio to the value of the disk. - - Parameters - ---------- - ratio : float - - """ - value_range = self.max_value - self.min_value - return self.min_value + ratio * value_range - - def set_position(self, position, disk_number): - """Set the disk's position. - - Parameters - ---------- - position : (float, float) - The absolute position of the disk (x, y). - disk_number : int - The index of disk being moved. - - """ - if self.orientation == "horizontal": - x_position = position[0] - - if disk_number == 0 and x_position >= self.handles[1].center[0]: - x_position = self.ratio_to_coord( - self.value_to_ratio(self._values[1] - 1) - ) - - if disk_number == 1 and x_position <= self.handles[0].center[0]: - x_position = self.ratio_to_coord( - self.value_to_ratio(self._values[0] + 1) - ) - - x_position = max(x_position, self.left_x_position) - x_position = min(x_position, self.right_x_position) - - self.handles[disk_number].center = (x_position, self.track.center[1]) - else: - y_position = position[1] - - if disk_number == 0 and y_position >= self.handles[1].center[1]: - y_position = self.ratio_to_coord( - self.value_to_ratio(self._values[1] - 1) - ) - - if disk_number == 1 and y_position <= self.handles[0].center[1]: - y_position = self.ratio_to_coord( - self.value_to_ratio(self._values[0] + 1) - ) - - y_position = max(y_position, self.bottom_y_position) - y_position = min(y_position, self.top_y_position) - - self.handles[disk_number].center = (self.track.center[0], y_position) - self.update(disk_number) - - @property - def bottom_disk_value(self): - """Return the value of the bottom disk.""" - return self._values[0] - - @bottom_disk_value.setter - def bottom_disk_value(self, bottom_disk_value): - """Set the value of the bottom disk. - - Parameters - ---------- - bottom_disk_value : float - New value for the bottom disk. - - """ - self.bottom_disk_ratio = self.value_to_ratio(bottom_disk_value) - - @property - def top_disk_value(self): - """Return the value of the top disk.""" - return self._values[1] - - @top_disk_value.setter - def top_disk_value(self, top_disk_value): - """Set the value of the top disk. - - Parameters - ---------- - top_disk_value : float - New value for the top disk. - - """ - self.top_disk_ratio = self.value_to_ratio(top_disk_value) - - @property - def left_disk_value(self): - """Return the value of the left disk.""" - return self._values[0] - - @left_disk_value.setter - def left_disk_value(self, left_disk_value): - """Set the value of the left disk. - - Parameters - ---------- - left_disk_value : float - New value for the left disk. - - """ - self.left_disk_ratio = self.value_to_ratio(left_disk_value) - self.on_value_changed(self) - - @property - def right_disk_value(self): - """Return the value of the right disk.""" - return self._values[1] - - @right_disk_value.setter - def right_disk_value(self, right_disk_value): - """Set the value of the right disk. - - Parameters - ---------- - right_disk_value : float - New value for the right disk. - - """ - self.right_disk_ratio = self.value_to_ratio(right_disk_value) - self.on_value_changed(self) - - @property - def bottom_disk_ratio(self): - """Return the ratio of the bottom disk.""" - return self._ratio[0] - - @bottom_disk_ratio.setter - def bottom_disk_ratio(self, bottom_disk_ratio): - """Set the ratio of the bottom disk. - - Parameters - ---------- - bottom_disk_ratio : float - New ratio for the bottom disk. - - """ - position_x = self.ratio_to_coord(bottom_disk_ratio) - position_y = self.ratio_to_coord(bottom_disk_ratio) - self.set_position((position_x, position_y), 0) - - @property - def top_disk_ratio(self): - """Return the ratio of the top disk.""" - return self._ratio[1] - - @top_disk_ratio.setter - def top_disk_ratio(self, top_disk_ratio): - """Set the ratio of the top disk. - - Parameters - ---------- - top_disk_ratio : float - New ratio for the top disk. - - """ - position_x = self.ratio_to_coord(top_disk_ratio) - position_y = self.ratio_to_coord(top_disk_ratio) - self.set_position((position_x, position_y), 1) - - @property - def left_disk_ratio(self): - """Return the ratio of the left disk.""" - return self._ratio[0] - - @left_disk_ratio.setter - def left_disk_ratio(self, left_disk_ratio): - """Set the ratio of the left disk. - - Parameters - ---------- - left_disk_ratio : float - New ratio for the left disk. - - """ - position_x = self.ratio_to_coord(left_disk_ratio) - position_y = self.ratio_to_coord(left_disk_ratio) - self.set_position((position_x, position_y), 0) - - @property - def right_disk_ratio(self): - """Return the ratio of the right disk.""" - return self._ratio[1] - - @right_disk_ratio.setter - def right_disk_ratio(self, right_disk_ratio): - """Set the ratio of the right disk. - - Parameters - ---------- - right_disk_ratio : float - New ratio for the right disk. - - """ - position_x = self.ratio_to_coord(right_disk_ratio) - position_y = self.ratio_to_coord(right_disk_ratio) - self.set_position((position_x, position_y), 1) - - def format_text(self, disk_number): - """Return formatted text to display along the slider. - - Parameters - ---------- - disk_number : int - Index of the disk. - - """ - if callable(self.text_template): - return self.text_template(self) - - return self.text_template.format(value=self._values[disk_number]) - - def update(self, disk_number): - """Update the slider. - - Parameters - ---------- - disk_number : int - Index of the disk to be updated. - - """ - # Compute the ratio determined by the position of the slider disk. - if self.orientation == "horizontal": - self._ratio[disk_number] = self.coord_to_ratio( - self.handles[disk_number].center[0] - ) - else: - self._ratio[disk_number] = self.coord_to_ratio( - self.handles[disk_number].center[1] - ) - - # Compute the selected value considering min_value and max_value. - self._values[disk_number] = self.ratio_to_value(self._ratio[disk_number]) - - # Update text. - text = self.format_text(disk_number) - self.text[disk_number].message = text - - if self.orientation == "horizontal": - self.text[disk_number].position = ( - self.handles[disk_number].center[0], - self.text[disk_number].position[1], - ) - else: - self.text[disk_number].position = ( - self.text[disk_number].position[0], - self.handles[disk_number].center[1], - ) - self.on_change(self) - - def handle_move_callback(self, i_ren, vtkactor, _slider): - """Handle movement. - - Parameters - ---------- - i_ren : :class:`CustomInteractorStyle` - vtkactor : :class:`vtkActor` - The picked actor - _slider : :class:`LineDoubleSlider2D` - - """ - position = i_ren.event.position - if vtkactor == self.handles[0].actors[0]: - self.set_position(position, 0) - self.handles[0].color = self.active_color - elif vtkactor == self.handles[1].actors[0]: - self.set_position(position, 1) - self.handles[1].color = self.active_color - self.on_moving_slider(self) - i_ren.force_render() - i_ren.event.abort() # Stop propagating the event. - - def handle_release_callback(self, i_ren, vtkactor, _slider): - """Change color when handle is released. - - Parameters - ---------- - i_ren : :class:`CustomInteractorStyle` - vtkactor : :class:`vtkActor` - The picked actor - _slider : :class:`LineDoubleSlider2D` - - """ - if vtkactor == self.handles[0].actors[0]: - self.handles[0].color = self.default_color - elif vtkactor == self.handles[1].actors[0]: - self.handles[1].color = self.default_color - i_ren.force_render() - - -class RingSlider2D(UI): - """A disk slider. - - A disk moves along the boundary of a ring. - Goes from 0-360 degrees. - - Attributes - ---------- - mid_track_radius: float - Distance from the center of the slider to the middle of the track. - previous_value: float - Value of Rotation of the actor before the current value. - track : :class:`Disk2D` - The circle on which the slider's handle moves. - handle : :class:`Disk2D` - The moving part of the slider. - text : :class:`TextBlock2D` - The text that shows percentage. - default_color : (float, float, float) - Color of the handle when in unpressed state. - active_color : (float, float, float) - Color of the handle when it is pressed. - - """ - - @warn_on_args_to_kwargs() - def __init__( - self, - *, - center=(0, 0), - initial_value=180, - min_value=0, - max_value=360, - slider_inner_radius=40, - slider_outer_radius=44, - handle_inner_radius=0, - handle_outer_radius=10, - font_size=16, - text_template="{ratio:.0%}", - ): - """Init this UI element. - - Parameters - ---------- - center : (float, float) - Position (x, y) of the slider's center. - initial_value : float - Initial value of the slider. - min_value : float - Minimum value of the slider. - max_value : float - Maximum value of the slider. - slider_inner_radius : int - Inner radius of the base disk. - slider_outer_radius : int - Outer radius of the base disk. - handle_outer_radius : int - Outer radius of the slider's handle. - handle_inner_radius : int - Inner radius of the slider's handle. - font_size : int - Size of the text to display alongside the slider (pt). - text_template : str, callable - If str, text template can contain one or multiple of the - replacement fields: `{value:}`, `{ratio:}`, `{angle:}`. - If callable, this instance of `:class:RingSlider2D` will be - passed as argument to the text template function. - - """ - self.default_color = (1, 1, 1) - self.active_color = (0, 0, 1) - super(RingSlider2D, self).__init__() - - self.track.inner_radius = slider_inner_radius - self.track.outer_radius = slider_outer_radius - self.handle.inner_radius = handle_inner_radius - self.handle.outer_radius = handle_outer_radius - self.center = center - - self.min_value = min_value - self.max_value = max_value - self.text.font_size = font_size - self.text_template = text_template - - # Offer some standard hooks to the user. - self.on_change = lambda ui: None - self.on_value_changed = lambda ui: None - self.on_moving_slider = lambda ui: None - - self._value = initial_value - self.value = initial_value - self._previous_value = initial_value - self._angle = 0 - self._ratio = self.angle / TWO_PI - - def _setup(self): - """Setup this UI component. - - Create the slider's circle (Disk2D), the handle (Disk2D) and - the text (TextBlock2D). - - """ - # Slider's track. - self.track = Disk2D(outer_radius=1) - self.track.color = (1, 0, 0) - - # Slider's handle. - self.handle = Disk2D(outer_radius=1) - self.handle.color = self.default_color - - # Slider Text - self.text = TextBlock2D(justification="center", vertical_justification="middle") - - # Add default events listener for this UI component. - self.track.on_left_mouse_button_pressed = self.track_click_callback - self.track.on_left_mouse_button_dragged = self.handle_move_callback - self.track.on_left_mouse_button_released = self.handle_release_callback - self.handle.on_left_mouse_button_dragged = self.handle_move_callback - self.handle.on_left_mouse_button_released = self.handle_release_callback - - def _get_actors(self): - """Get the actors composing this UI component.""" - return self.track.actors + self.handle.actors + self.text.actors - - def _add_to_scene(self, scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - scene : scene - - """ - self.track.add_to_scene(scene) - self.handle.add_to_scene(scene) - self.text.add_to_scene(scene) - - def _get_size(self): - return self.track.size + self.handle.size - - def _set_position(self, coords): - """Set the lower-left corner position of this UI component. - - Parameters - ---------- - coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - self.track.position = coords + self.handle.size / 2.0 - self.handle.position += coords - self.position - # Position the text in the center of the slider's track. - self.text.position = coords + self.size / 2.0 - - @property - def mid_track_radius(self): - return (self.track.inner_radius + self.track.outer_radius) / 2.0 - - @property - def value(self): - return self._value - - @value.setter - def value(self, value): - value_range = self.max_value - self.min_value - self.ratio = (value - self.min_value) / value_range if value_range else 0 - self.on_value_changed(self) - - @property - def previous_value(self): - return self._previous_value - - @property - def ratio(self): - return self._ratio - - @ratio.setter - def ratio(self, ratio): - self.angle = ratio * TWO_PI - - @property - def angle(self): - """Return Angle (in rad) the handle makes with x-axis.""" - return self._angle - - @angle.setter - def angle(self, angle): - self._angle = angle % TWO_PI # Wraparound - self.update() - - def format_text(self): - """Return formatted text to display along the slider.""" - if callable(self.text_template): - return self.text_template(self) - - return self.text_template.format( - ratio=self.ratio, value=self.value, angle=np.rad2deg(self.angle) - ) - - def update(self): - """Update the slider.""" - # Compute the ratio determined by the position of the slider disk. - self._ratio = self.angle / TWO_PI - - # Compute the selected value considering min_value and max_value. - value_range = self.max_value - self.min_value - self._previous_value = self.value - self._value = self.min_value + self.ratio * value_range - - # Update text disk actor. - x = self.mid_track_radius * np.cos(self.angle) + self.center[0] - y = self.mid_track_radius * np.sin(self.angle) + self.center[1] - self.handle.center = (x, y) - - # Update text. - text = self.format_text() - self.text.message = text - - self.on_change(self) # Call hook. - - def move_handle(self, click_position): - """Move the slider's handle. - - Parameters - ---------- - click_position: (float, float) - Position of the mouse click. - - """ - x, y = np.array(click_position) - self.center - angle = np.arctan2(y, x) - if angle < 0: - angle += TWO_PI - - self.angle = angle - - def track_click_callback(self, i_ren, _obj, _slider): - """Update disk position and grab the focus. - - Parameters - ---------- - i_ren : :class:`CustomInteractorStyle` - obj : :class:`vtkActor` - The picked actor - _slider : :class:`RingSlider2D` - - """ - click_position = i_ren.event.position - self.move_handle(click_position=click_position) - self.on_moving_slider(self) - i_ren.force_render() - i_ren.event.abort() # Stop propagating the event. - - def handle_move_callback(self, i_ren, _obj, _slider): - """Move the slider's handle. - - Parameters - ---------- - i_ren : :class:`CustomInteractorStyle` - obj : :class:`vtkActor` - The picked actor - _slider : :class:`RingSlider2D` - - """ - click_position = i_ren.event.position - self.handle.color = self.active_color - self.move_handle(click_position=click_position) - self.on_moving_slider(self) - i_ren.force_render() - i_ren.event.abort() # Stop propagating the event. - - def handle_release_callback(self, i_ren, _obj, _slider): - """Change color when handle is released. - - Parameters - ---------- - i_ren : :class:`CustomInteractorStyle` - vtkactor : :class:`vtkActor` - The picked actor - _slider : :class:`RingSlider2D` - - """ - self.handle.color = self.default_color - i_ren.force_render() - - -class RangeSlider(UI): - """A set of a LineSlider2D and a LineDoubleSlider2D. - The double slider is used to set the min and max value - for the LineSlider2D - - Attributes - ---------- - range_slider_center : (float, float) - Center of the LineDoubleSlider2D object. - value_slider_center : (float, float) - Center of the LineSlider2D object. - range_slider : :class:`LineDoubleSlider2D` - The line slider which sets the min and max values - value_slider : :class:`LineSlider2D` - The line slider which sets the value - - """ - - @warn_on_args_to_kwargs() - def __init__( - self, - *, - line_width=5, - inner_radius=0, - outer_radius=10, - handle_side=20, - range_slider_center=(450, 400), - value_slider_center=(450, 300), - length=200, - min_value=0, - max_value=100, - font_size=16, - range_precision=1, - orientation="horizontal", - value_precision=2, - shape="disk", - ): - """Init this class instance. - - Parameters - ---------- - line_width : int - Width of the slider tracks - inner_radius : int - Inner radius of the handles. - outer_radius : int - Outer radius of the handles. - handle_side : int - Side length of the handles (if square). - range_slider_center : (float, float) - Center of the LineDoubleSlider2D object. - value_slider_center : (float, float) - Center of the LineSlider2D object. - length : int - Length of the sliders. - min_value : float - Minimum value of the double slider. - max_value : float - Maximum value of the double slider. - font_size : int - Size of the text to display alongside the sliders (pt). - range_precision : int - Number of decimal places to show the min and max values set. - orientation : str - horizontal or vertical - value_precision : int - Number of decimal places to show the value set on slider. - shape : string - Describes the shape of the handle. - Currently supports 'disk' and 'square'. - - """ - self.min_value = min_value - self.max_value = max_value - self.inner_radius = inner_radius - self.outer_radius = outer_radius - self.handle_side = handle_side - self.length = length - self.line_width = line_width - self.font_size = font_size - self.shape = shape - self.orientation = orientation.lower() - - self.range_slider_text_template = "{value:." + str(range_precision) + "f}" - self.value_slider_text_template = "{value:." + str(value_precision) + "f}" - - self.range_slider_center = range_slider_center - self.value_slider_center = value_slider_center - super(RangeSlider, self).__init__() - - def _setup(self): - """Setup this UI component.""" - self.range_slider = LineDoubleSlider2D( - line_width=self.line_width, - inner_radius=self.inner_radius, - outer_radius=self.outer_radius, - handle_side=self.handle_side, - center=self.range_slider_center, - length=self.length, - min_value=self.min_value, - max_value=self.max_value, - initial_values=(self.min_value, self.max_value), - font_size=self.font_size, - shape=self.shape, - orientation=self.orientation, - text_template=self.range_slider_text_template, - ) - - self.value_slider = LineSlider2D( - line_width=self.line_width, - length=self.length, - inner_radius=self.inner_radius, - outer_radius=self.outer_radius, - handle_side=self.handle_side, - center=self.value_slider_center, - min_value=self.min_value, - max_value=self.max_value, - initial_value=(self.min_value + self.max_value) / 2, - font_size=self.font_size, - shape=self.shape, - orientation=self.orientation, - text_template=self.value_slider_text_template, - ) - - # Add default events listener for this UI component. - self.range_slider.handles[ - 0 - ].on_left_mouse_button_dragged = self.range_slider_handle_move_callback - self.range_slider.handles[ - 1 - ].on_left_mouse_button_dragged = self.range_slider_handle_move_callback - - def _get_actors(self): - """Get the actors composing this UI component.""" - return self.range_slider.actors + self.value_slider.actors - - def _add_to_scene(self, scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - scene : scene - - """ - self.range_slider.add_to_scene(scene) - self.value_slider.add_to_scene(scene) - - def _get_size(self): - return self.range_slider.size + self.value_slider.size - - def _set_position(self, coords): - pass - - def range_slider_handle_move_callback(self, i_ren, obj, _slider): - """Update range_slider's handles. - - Parameters - ---------- - i_ren : :class:`CustomInteractorStyle` - obj : :class:`vtkActor` - The picked actor - _slider : :class:`RangeSlider` - - """ - position = i_ren.event.position - if obj == self.range_slider.handles[0].actors[0]: - self.range_slider.handles[0].color = self.range_slider.active_color - self.range_slider.set_position(position, 0) - self.value_slider.min_value = self.range_slider.left_disk_value - self.value_slider.update() - elif obj == self.range_slider.handles[1].actors[0]: - self.range_slider.handles[1].color = self.range_slider.active_color - self.range_slider.set_position(position, 1) - self.value_slider.max_value = self.range_slider.right_disk_value - self.value_slider.update() - i_ren.force_render() - i_ren.event.abort() # Stop propagating the event. - - -class Option(UI): - """A set of a Button2D and a TextBlock2D to act as a single option - for checkboxes and radio buttons. - Clicking the button toggles its checked/unchecked status. - - Attributes - ---------- - label : str - The label for the option. - font_size : int - Font Size of the label. - - """ - - @warn_on_args_to_kwargs() - def __init__(self, label, *, position=(0, 0), font_size=18, checked=False): - """Init this class instance. - - Parameters - ---------- - label : str - Text to be displayed next to the option's button. - position : (float, float) - Absolute coordinates (x, y) of the lower-left corner of - the button of the option. - font_size : int - Font size of the label. - checked : bool, optional - Boolean value indicates the initial state of the option - - """ - self.label = label - self.font_size = font_size - self.checked = checked - self.button_size = (font_size * 1.2, font_size * 1.2) - self.button_label_gap = 10 - super(Option, self).__init__(position=position) - - # Offer some standard hooks to the user. - self.on_change = lambda obj: None - - def _setup(self): - """Setup this UI component.""" - # Option's button - self.button_icons = [] - self.button_icons.append(("unchecked", read_viz_icons(fname="stop2.png"))) - self.button_icons.append(("checked", read_viz_icons(fname="checkmark.png"))) - self.button = Button2D(icon_fnames=self.button_icons, size=self.button_size) - - self.text = TextBlock2D(text=self.label, font_size=self.font_size) - - # Display initial state - if self.checked: - self.button.set_icon_by_name("checked") - - # Add callbacks - self.button.on_left_mouse_button_clicked = self.toggle - self.text.on_left_mouse_button_clicked = self.toggle - - def _get_actors(self): - """Get the actors composing this UI component.""" - return self.button.actors + self.text.actors - - def _add_to_scene(self, scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - scene : scene - - """ - self.button.add_to_scene(scene) - self.text.add_to_scene(scene) - - def _get_size(self): - width = self.button.size[0] + self.button_label_gap + self.text.size[0] - height = max(self.button.size[1], self.text.size[1]) - return np.array([width, height]) - - def _set_position(self, coords): - """Set the lower-left corner position of this UI component. - - Parameters - ---------- - coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - num_newlines = self.label.count("\n") - self.button.position = coords + (0, num_newlines * self.font_size * 0.5) - offset = (self.button.size[0] + self.button_label_gap, 0) - self.text.position = coords + offset - - def toggle(self, i_ren, _obj, _element): - if self.checked: - self.deselect() - else: - self.select() - - self.on_change(self) - i_ren.force_render() - - def select(self): - self.checked = True - self.button.set_icon_by_name("checked") - - def deselect(self): - self.checked = False - self.button.set_icon_by_name("unchecked") - - -class Checkbox(UI): - """A 2D set of :class:'Option' objects. - Multiple options can be selected. - - Attributes - ---------- - labels : list(string) - List of labels of each option. - options : dict(Option) - Dictionary of all the options in the checkbox set. - padding : float - Distance between two adjacent options - - """ - - @warn_on_args_to_kwargs() - def __init__( - self, - labels, - *, - checked_labels=(), - padding=1, - font_size=18, - font_family="Arial", - position=(0, 0), - ): - """Init this class instance. - - Parameters - ---------- - labels : list(str) - List of labels of each option. - checked_labels: list(str), optional - List of labels that are checked on setting up. - padding : float, optional - The distance between two adjacent options - font_size : int, optional - Size of the text font. - font_family : str, optional - Currently only supports Arial. - position : (float, float), optional - Absolute coordinates (x, y) of the lower-left corner of - the button of the first option. - - """ - self.labels = list(reversed(list(labels))) - self._padding = padding - self._font_size = font_size - self.font_family = font_family - self.checked_labels = list(checked_labels) - super(Checkbox, self).__init__(position=position) - self.on_change = lambda checkbox: None - - def _setup(self): - """Setup this UI component.""" - self.options = OrderedDict() - button_y = self.position[1] - for label in self.labels: - option = Option( - label=label, - font_size=self.font_size, - position=(self.position[0], button_y), - checked=(label in self.checked_labels), - ) - - line_spacing = option.text.actor.GetTextProperty().GetLineSpacing() - button_y = ( - button_y - + self.font_size * (label.count("\n") + 1) * (line_spacing + 0.1) - + self.padding - ) - self.options[label] = option - - # Set callback - option.on_change = self._handle_option_change - - def _get_actors(self): - """Get the actors composing this UI component.""" - actors = [] - for option in self.options.values(): - actors = actors + option.actors - return actors - - def _add_to_scene(self, scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - scene : scene - - """ - for option in self.options.values(): - option.add_to_scene(scene) - - def _get_size(self): - option_width, option_height = self.options.values()[0].get_size() - height = len(self.labels) * (option_height + self.padding) - self.padding - return np.asarray([option_width, height]) - - def _handle_option_change(self, option): - """Update whenever an option changes. - - Parameters - ---------- - option : :class:`Option` - - """ - if option.checked: - self.checked_labels.append(option.label) - else: - self.checked_labels.remove(option.label) - - self.on_change(self) - - def _set_position(self, coords): - """Set the lower-left corner position of this UI component. - - Parameters - ---------- - coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - button_y = coords[1] - for option_no, option in enumerate(self.options.values()): - option.position = (coords[0], button_y) - line_spacing = option.text.actor.GetTextProperty().GetLineSpacing() - button_y = ( - button_y - + self.font_size - * (self.labels[option_no].count("\n") + 1) - * (line_spacing + 0.1) - + self.padding - ) - - @property - def font_size(self): - """Gets the font size of text.""" - return self._font_size - - @property - def padding(self): - """Get the padding between options.""" - return self._padding - - -class RadioButton(Checkbox): - """A 2D set of :class:'Option' objects. - Only one option can be selected. - - Attributes - ---------- - labels : list(string) - List of labels of each option. - options : dict(Option) - Dictionary of all the options in the checkbox set. - padding : float - Distance between two adjacent options - - """ - - @warn_on_args_to_kwargs() - def __init__( - self, - labels, - checked_labels, - *, - padding=1, - font_size=18, - font_family="Arial", - position=(0, 0), - ): - """Init class instance. - - Parameters - ---------- - labels : list(str) - List of labels of each option. - checked_labels: list(str), optional - List of labels that are checked on setting up. - padding : float, optional - The distance between two adjacent options - font_size : int, optional - Size of the text font. - font_family : str, optional - Currently only supports Arial. - position : (float, float), optional - Absolute coordinates (x, y) of the lower-left corner of - the button of the first option. - - """ - if len(checked_labels) > 1: - err_msg = "Only one option can be pre-selected for radio buttons." - raise ValueError(err_msg) - - super(RadioButton, self).__init__( - labels=labels, - position=position, - padding=padding, - font_size=font_size, - font_family=font_family, - checked_labels=checked_labels, - ) - - def _handle_option_change(self, option): - for option_ in self.options.values(): - option_.deselect() - - option.select() - self.checked_labels = [option.label] - self.on_change(self) - - -class ComboBox2D(UI): - """UI element to create drop-down menus. - - Attributes - ---------- - selection_box: :class: 'TextBox2D' - Display selection and placeholder text. - drop_down_button: :class: 'Button2D' - Button to show or hide menu. - drop_down_menu: :class: 'ListBox2D' - Container for item list. - - """ - - @warn_on_args_to_kwargs() - def __init__( - self, - *, - items=None, - position=(0, 0), - size=(300, 200), - placeholder="Choose selection...", - draggable=True, - selection_text_color=(0, 0, 0), - selection_bg_color=(1, 1, 1), - menu_text_color=(0.2, 0.2, 0.2), - selected_color=(0.9, 0.6, 0.6), - unselected_color=(0.6, 0.6, 0.6), - scroll_bar_active_color=(0.6, 0.2, 0.2), - scroll_bar_inactive_color=(0.9, 0.0, 0.0), - menu_opacity=1.0, - reverse_scrolling=False, - font_size=20, - line_spacing=1.4, - ): - """Init class Instance. - - Parameters - ---------- - items: list(string) - List of items to be displayed as choices. - position : (float, float) - Absolute coordinates (x, y) of the lower-left corner of this - UI component. - size : (int, int) - Width and height in pixels of this UI component. - placeholder : str - Holds the default text to be displayed. - draggable: {True, False} - Whether the UI element is draggable or not. - selection_text_color : tuple of 3 floats - Color of the selected text to be displayed. - selection_bg_color : tuple of 3 floats - Background color of the selection text. - menu_text_color : tuple of 3 floats. - Color of the options displayed in drop down menu. - selected_color : tuple of 3 floats. - Background color of the selected option in drop down menu. - unselected_color : tuple of 3 floats. - Background color of the unselected option in drop down menu. - scroll_bar_active_color : tuple of 3 floats. - Color of the scrollbar when in active use. - scroll_bar_inactive_color : tuple of 3 floats. - Color of the scrollbar when inactive. - reverse_scrolling: {True, False} - If True, scrolling up will move the list of files down. - font_size: int - The font size of selected text in pixels. - line_spacing: float - Distance between drop down menu's items in pixels. - - """ - if items is None: - items = [] - - self.items = items.copy() - self.font_size = font_size - self.reverse_scrolling = reverse_scrolling - self.line_spacing = line_spacing - self.panel_size = size - self._selection = placeholder - self._menu_visibility = False - self._selection_ID = None - self.draggable = draggable - self.sel_text_color = selection_text_color - self.sel_bg_color = selection_bg_color - self.menu_txt_color = menu_text_color - self.selected_color = selected_color - self.unselected_color = unselected_color - self.scroll_active_color = scroll_bar_active_color - self.scroll_inactive_color = scroll_bar_inactive_color - self.menu_opacity = menu_opacity - - # Define subcomponent sizes. - self.text_block_size = (int(0.9 * size[0]), int(0.1 * size[1])) - self.drop_menu_size = (int(0.9 * size[0]), int(0.7 * size[1])) - self.drop_button_size = (int(0.1 * size[0]), int(0.1 * size[1])) - - self._icon_files = [ - ("left", read_viz_icons(fname="circle-left.png")), - ("down", read_viz_icons(fname="circle-down.png")), - ] - - super(ComboBox2D, self).__init__() - self.position = position - - def _setup(self): - """Setup this UI component. - - Create the ListBox filled with empty slots (ListBoxItem2D). - Create TextBox with placeholder text. - Create Button for toggling drop down menu. - """ - self.selection_box = TextBlock2D( - size=self.text_block_size, - color=self.sel_text_color, - bg_color=self.sel_bg_color, - text=self._selection, - ) - - self.drop_down_button = Button2D( - icon_fnames=self._icon_files, size=self.drop_button_size - ) - - self.drop_down_menu = ListBox2D( - values=self.items, - multiselection=False, - font_size=self.font_size, - line_spacing=self.line_spacing, - text_color=self.menu_txt_color, - selected_color=self.selected_color, - unselected_color=self.unselected_color, - scroll_bar_active_color=self.scroll_active_color, - scroll_bar_inactive_color=self.scroll_inactive_color, - background_opacity=self.menu_opacity, - reverse_scrolling=self.reverse_scrolling, - size=self.drop_menu_size, - ) - - self.drop_down_menu.set_visibility(False) - - self.panel = Panel2D(self.panel_size, opacity=0.0) - self.panel.add_element(self.selection_box, (0.001, 0.7)) - self.panel.add_element(self.drop_down_button, (0.8, 0.7)) - self.panel.add_element(self.drop_down_menu, (0, 0)) - - if self.draggable: - self.drop_down_button.on_left_mouse_button_dragged = ( - self.left_button_dragged - ) - self.drop_down_menu.panel.background.on_left_mouse_button_dragged = ( - self.left_button_dragged - ) - self.selection_box.on_left_mouse_button_dragged = self.left_button_dragged - self.selection_box.background.on_left_mouse_button_dragged = ( - self.left_button_dragged - ) - - self.drop_down_button.on_left_mouse_button_pressed = ( - self.left_button_pressed - ) - self.drop_down_menu.panel.background.on_left_mouse_button_pressed = ( - self.left_button_pressed - ) - self.selection_box.on_left_mouse_button_pressed = self.left_button_pressed - self.selection_box.background.on_left_mouse_button_pressed = ( - self.left_button_pressed - ) - else: - self.panel.background.on_left_mouse_button_dragged = ( - lambda i_ren, _obj, _comp: i_ren.force_render - ) - self.drop_down_menu.panel.background.on_left_mouse_button_dragged = ( - lambda i_ren, _obj, _comp: i_ren.force_render - ) - - # Handle mouse wheel events on the slots. - for slot in self.drop_down_menu.slots: - slot.add_callback( - slot.textblock.actor, - "LeftButtonPressEvent", - self.select_option_callback, - ) - - slot.add_callback( - slot.background.actor, - "LeftButtonPressEvent", - self.select_option_callback, - ) - - self.drop_down_button.on_left_mouse_button_clicked = self.menu_toggle_callback - - # Offer some standard hooks to the user. - self.on_change = lambda ui: None - - def _get_actors(self): - """Get the actors composing this UI component.""" - return self.panel.actors - - def resize(self, size): - """Resize ComboBox2D. - - Parameters - ---------- - size : (int, int) - ComboBox size(width, height) in pixels. - - """ - self.panel.resize(size) - - self.text_block_size = (int(0.9 * size[0]), int(0.1 * size[1])) - self.drop_menu_size = (int(0.9 * size[0]), int(0.7 * size[1])) - self.drop_button_size = (int(0.1 * size[0]), int(0.1 * size[1])) - - self.panel.update_element(self.selection_box, (0.001, 0.7)) - self.panel.update_element(self.drop_down_button, (0.8, 0.7)) - self.panel.update_element(self.drop_down_menu, (0, 0)) - - self.drop_down_button.resize(self.drop_button_size) - self.drop_down_menu.resize(self.drop_menu_size) - self.selection_box.resize(self.text_block_size) - - def _set_position(self, coords): - """Set the lower-left corner position of this UI component. - - Parameters - ---------- - coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - self.panel.position = coords - self.panel.position = ( - self.panel.position[0], - self.panel.position[1] - self.drop_menu_size[1], - ) - - def _add_to_scene(self, scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - scene : scene - - """ - self.panel.add_to_scene(scene) - self.selection_box.font_size = self.font_size - - def _get_size(self): - return self.panel.size - - @property - def selected_text(self): - return self._selection - - @property - def selected_text_index(self): - return self._selection_ID - - def set_visibility(self, visibility): - super().set_visibility(visibility) - if not self._menu_visibility: - self.drop_down_menu.set_visibility(False) - - def append_item(self, *items): - """Append additional options to the menu. - - Parameters - ---------- - items : n-d list, n-d tuple, Number or str - Additional options. - - """ - for item in items: - if isinstance(item, (list, tuple)): - # Useful when n-d lists/tuples are used. - self.append_item(*item) - elif isinstance(item, (str, Number)): - self.items.append(str(item)) - else: - raise TypeError("Invalid item instance {}".format(type(item))) - - self.drop_down_menu.update_scrollbar() - if not self._menu_visibility: - self.drop_down_menu.scroll_bar.set_visibility(False) - - def select_option_callback(self, i_ren, _obj, listboxitem): - """Select the appropriate option - - Parameters - ---------- - i_ren: :class:`CustomInteractorStyle` - obj: :class:`vtkActor` - The picked actor - listboxitem: :class:`ListBoxItem2D` - - """ - # Set the Text of TextBlock2D to the text of listboxitem - self._selection = listboxitem.element - self._selection_ID = self.items.index(self._selection) - - self.selection_box.message = self._selection - clip_overflow(self.selection_box, self.selection_box.background.size[0]) - self.drop_down_menu.set_visibility(False) - self._menu_visibility = False - - self.drop_down_button.next_icon() - - self.on_change(self) - - i_ren.force_render() - i_ren.event.abort() - - def menu_toggle_callback(self, i_ren, _vtkactor, _combobox): - """Toggle visibility of drop down menu list. - - Parameters - ---------- - i_ren : :class:`CustomInteractorStyle` - vtkactor : :class:`vtkActor` - The picked actor - combobox : :class:`ComboBox2D` - - """ - self._menu_visibility = not self._menu_visibility - self.drop_down_menu.set_visibility(self._menu_visibility) - - self.drop_down_button.next_icon() - - i_ren.force_render() - i_ren.event.abort() # Stop propagating the event. - - def left_button_pressed(self, i_ren, _obj, _sub_component): - click_pos = np.array(i_ren.event.position) - self._click_position = click_pos - i_ren.event.abort() # Stop propagating the event. - - def left_button_dragged(self, i_ren, _obj, _sub_component): - click_position = np.array(i_ren.event.position) - change = click_position - self._click_position - self.panel.position += change - self._click_position = click_position - i_ren.force_render() - - -class ListBox2D(UI): - """UI component that allows the user to select items from a list. - - Attributes - ---------- - on_change: function - Callback function for when the selected items have changed. - - """ - - @warn_on_args_to_kwargs() - def __init__( - self, - values, - *, - position=(0, 0), - size=(100, 300), - multiselection=True, - reverse_scrolling=False, - font_size=20, - line_spacing=1.4, - text_color=(0.2, 0.2, 0.2), - selected_color=(0.9, 0.6, 0.6), - unselected_color=(0.6, 0.6, 0.6), - scroll_bar_active_color=(0.6, 0.2, 0.2), - scroll_bar_inactive_color=(0.9, 0.0, 0.0), - background_opacity=1.0, - ): - """Init class instance. - - Parameters - ---------- - values: list of objects - Values used to populate this listbox. Objects must be castable - to string. - position : (float, float) - Absolute coordinates (x, y) of the lower-left corner of this - UI component. - size : (int, int) - Width and height in pixels of this UI component. - multiselection: {True, False} - Whether multiple values can be selected at once. - reverse_scrolling: {True, False} - If True, scrolling up will move the list of files down. - font_size: int - The font size in pixels. - line_spacing: float - Distance between listbox's items in pixels. - text_color : tuple of 3 floats - selected_color : tuple of 3 floats - unselected_color : tuple of 3 floats - scroll_bar_active_color : tuple of 3 floats - scroll_bar_inactive_color : tuple of 3 floats - background_opacity : float - - """ - self.view_offset = 0 - self.slots = [] - self.selected = [] - - self.panel_size = size - self.font_size = font_size - self.line_spacing = line_spacing - self.slot_height = int(self.font_size * self.line_spacing) - - self.text_color = text_color - self.selected_color = selected_color - self.unselected_color = unselected_color - self.background_opacity = background_opacity - - # self.panel.resize(size) - self.values = values - self.multiselection = multiselection - self.last_selection_idx = 0 - self.reverse_scrolling = reverse_scrolling - super(ListBox2D, self).__init__() - - denom = len(self.values) - self.nb_slots - if not denom: - denom += 1 - self.scroll_step_size = ( - self.slot_height * self.nb_slots - self.scroll_bar.height - ) / denom - - self.scroll_bar_active_color = scroll_bar_active_color - self.scroll_bar_inactive_color = scroll_bar_inactive_color - self.scroll_bar.color = self.scroll_bar_inactive_color - self.scroll_bar.opacity = self.background_opacity - - self.position = position - self.scroll_init_position = 0 - self.update() - - # Offer some standard hooks to the user. - self.on_change = lambda: None - - def _setup(self): - """Setup this UI component. - - Create the ListBox (Panel2D) filled with empty slots (ListBoxItem2D). - """ - self.margin = 10 - size = self.panel_size - font_size = self.font_size - # Calculating the number of slots. - self.nb_slots = int((size[1] - 2 * self.margin) // self.slot_height) - - # This panel facilitates adding slots at the right position. - self.panel = Panel2D(size=size, color=(1, 1, 1)) - - # Add a scroll bar - scroll_bar_height = ( - self.nb_slots * (size[1] - 2 * self.margin) / len(self.values) - ) - self.scroll_bar = Rectangle2D(size=(int(size[0] / 20), scroll_bar_height)) - if len(self.values) <= self.nb_slots: - self.scroll_bar.set_visibility(False) - self.scroll_bar.height = 0 - self.panel.add_element( - self.scroll_bar, size - self.scroll_bar.size - self.margin - ) - - # Initialisation of empty text actors - self.slot_width = ( - size[0] - self.scroll_bar.size[0] - 2 * self.margin - self.margin - ) - x = self.margin - y = size[1] - self.margin - for _ in range(self.nb_slots): - y -= self.slot_height - item = ListBoxItem2D( - list_box=self, - size=(self.slot_width, self.slot_height), - text_color=self.text_color, - selected_color=self.selected_color, - unselected_color=self.unselected_color, - background_opacity=self.background_opacity, - ) - item.textblock.font_size = font_size - self.slots.append(item) - self.panel.add_element(item, (x, y + self.margin)) - - # Add default events listener for this UI component. - self.scroll_bar.on_left_mouse_button_pressed = self.scroll_click_callback - self.scroll_bar.on_left_mouse_button_released = self.scroll_release_callback - self.scroll_bar.on_left_mouse_button_dragged = self.scroll_drag_callback - - # Handle mouse wheel events on the panel. - up_event = "MouseWheelForwardEvent" - down_event = "MouseWheelBackwardEvent" - if self.reverse_scrolling: - up_event, down_event = down_event, up_event # Swap events - - self.add_callback( - self.panel.background.actor, up_event, self.up_button_callback - ) - self.add_callback( - self.panel.background.actor, down_event, self.down_button_callback - ) - - # Handle mouse wheel events on the slots. - for slot in self.slots: - self.add_callback(slot.background.actor, up_event, self.up_button_callback) - self.add_callback( - slot.background.actor, down_event, self.down_button_callback - ) - self.add_callback(slot.textblock.actor, up_event, self.up_button_callback) - self.add_callback( - slot.textblock.actor, down_event, self.down_button_callback - ) - - def resize(self, size): - pass - - def _get_actors(self): - """Get the actors composing this UI component.""" - return self.panel.actors - - def _add_to_scene(self, scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - scene : scene - - """ - self.panel.add_to_scene(scene) - for slot in self.slots: - clip_overflow(slot.textblock, self.slot_width) - - def _get_size(self): - return self.panel.size - - def _set_position(self, coords): - """Position the lower-left corner of this UI component. - - Parameters - ---------- - coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - self.panel.position = coords - - def up_button_callback(self, i_ren, _obj, _list_box): - """Pressing up button scrolls up in the combo box. - - Parameters - ---------- - i_ren: :class:`CustomInteractorStyle` - obj: :class:`vtkActor` - The picked actor - _list_box: :class:`ListBox2D` - - """ - if self.view_offset > 0: - self.view_offset -= 1 - self.update() - scroll_bar_idx = self.panel._elements.index(self.scroll_bar) - self.scroll_bar.center = ( - self.scroll_bar.center[0], - self.scroll_bar.center[1] + self.scroll_step_size, - ) - self.panel.element_offsets[scroll_bar_idx] = ( - self.scroll_bar, - (self.scroll_bar.position - self.panel.position), - ) - - i_ren.force_render() - i_ren.event.abort() # Stop propagating the event. - - def down_button_callback(self, i_ren, _obj, _list_box): - """Pressing down button scrolls down in the combo box. - - Parameters - ---------- - i_ren: :class:`CustomInteractorStyle` - obj: :class:`vtkActor` - The picked actor - _list_box: :class:`ListBox2D` - - """ - view_end = self.view_offset + self.nb_slots - if view_end < len(self.values): - self.view_offset += 1 - self.update() - scroll_bar_idx = self.panel._elements.index(self.scroll_bar) - self.scroll_bar.center = ( - self.scroll_bar.center[0], - self.scroll_bar.center[1] - self.scroll_step_size, - ) - self.panel.element_offsets[scroll_bar_idx] = ( - self.scroll_bar, - (self.scroll_bar.position - self.panel.position), - ) - - i_ren.force_render() - i_ren.event.abort() # Stop propagating the event. - - def scroll_click_callback(self, i_ren, _obj, _rect_obj): - """Callback to change the color of the bar when it is clicked. - - Parameters - ---------- - i_ren: :class:`CustomInteractorStyle` - obj: :class:`vtkActor` - The picked actor - _rect_obj: :class:`Rectangle2D` - - """ - self.scroll_bar.color = self.scroll_bar_active_color - self.scroll_init_position = i_ren.event.position[1] - i_ren.force_render() - i_ren.event.abort() - - def scroll_release_callback(self, i_ren, _obj, _rect_obj): - """Callback to change the color of the bar when it is released. - - Parameters - ---------- - i_ren: :class:`CustomInteractorStyle` - obj: :class:`vtkActor` - The picked actor - rect_obj: :class:`Rectangle2D` - - """ - self.scroll_bar.color = self.scroll_bar_inactive_color - i_ren.force_render() - - def scroll_drag_callback(self, i_ren, _obj, _rect_obj): - """Drag scroll bar in the combo box. - - Parameters - ---------- - i_ren: :class:`CustomInteractorStyle` - obj: :class:`vtkActor` - The picked actor - rect_obj: :class:`Rectangle2D` - - """ - position = i_ren.event.position - offset = int((position[1] - self.scroll_init_position) / self.scroll_step_size) - if offset > 0 and self.view_offset > 0: - offset = min(offset, self.view_offset) - - elif offset < 0 and (self.view_offset + self.nb_slots < len(self.values)): - offset = min(-offset, len(self.values) - self.nb_slots - self.view_offset) - offset = -offset - else: - return - - self.view_offset -= offset - self.update() - scroll_bar_idx = self.panel._elements.index(self.scroll_bar) - self.scroll_bar.center = ( - self.scroll_bar.center[0], - self.scroll_bar.center[1] + offset * self.scroll_step_size, - ) - - self.scroll_init_position += offset * self.scroll_step_size - - self.panel.element_offsets[scroll_bar_idx] = ( - self.scroll_bar, - (self.scroll_bar.position - self.panel.position), - ) - i_ren.force_render() - i_ren.event.abort() - - def update(self): - """Refresh listbox's content.""" - view_start = self.view_offset - view_end = view_start + self.nb_slots - values_to_show = self.values[view_start:view_end] - - # Populate slots according to the view. - for i, choice in enumerate(values_to_show): - slot = self.slots[i] - slot.element = choice - if slot.textblock.scene is not None: - clip_overflow(slot.textblock, self.slot_width) - slot.set_visibility(True) - if slot.size[1] != self.slot_height: - slot.resize((self.slot_width, self.slot_height)) - if slot.element in self.selected: - slot.select() - else: - slot.deselect() - - # Flush remaining slots. - for slot in self.slots[len(values_to_show) :]: - slot.element = None - slot.set_visibility(False) - slot.resize((self.slot_width, 0)) - slot.deselect() - - def update_scrollbar(self): - """Change the scroll-bar height when the values - in the listbox change - """ - self.scroll_bar.set_visibility(True) - - self.scroll_bar.height = ( - self.nb_slots * (self.panel_size[1] - 2 * self.margin) / len(self.values) - ) - - self.scroll_step_size = ( - self.slot_height * self.nb_slots - self.scroll_bar.height - ) / (len(self.values) - self.nb_slots) - - self.panel.update_element( - self.scroll_bar, self.panel_size - self.scroll_bar.size - self.margin - ) - - if len(self.values) <= self.nb_slots: - self.scroll_bar.set_visibility(False) - self.scroll_bar.height = 0 - - def clear_selection(self): - del self.selected[:] - - @warn_on_args_to_kwargs() - def select(self, item, *, multiselect=False, range_select=False): - """Select the item. - - Parameters - ---------- - item: ListBoxItem2D's object - Item to select. - multiselect: {True, False} - If True and multiselection is allowed, the item is added to the - selection. - Otherwise, the selection will only contain the provided item unless - range_select is True. - range_select: {True, False} - If True and multiselection is allowed, all items between the last - selected item and the current one will be added to the selection. - Otherwise, the selection will only contain the provided item unless - multi_select is True. - - """ - selection_idx = self.values.index(item.element) - if self.multiselection and range_select: - self.clear_selection() - step = 1 if selection_idx >= self.last_selection_idx else -1 - for i in range(self.last_selection_idx, selection_idx + step, step): - self.selected.append(self.values[i]) - - elif self.multiselection and multiselect: - if item.element in self.selected: - self.selected.remove(item.element) - else: - self.selected.append(item.element) - self.last_selection_idx = selection_idx - - else: - self.clear_selection() - self.selected.append(item.element) - self.last_selection_idx = selection_idx - - self.on_change() # Call hook. - self.update() - - -class ListBoxItem2D(UI): - """The text displayed in a listbox.""" - - @warn_on_args_to_kwargs() - def __init__( - self, - list_box, - size, - *, - text_color=(1.0, 0.0, 0.0), - selected_color=(0.4, 0.4, 0.4), - unselected_color=(0.9, 0.9, 0.9), - background_opacity=1.0, - ): - """Init ListBox Item instance. - - Parameters - ---------- - list_box : :class:`ListBox` - The ListBox reference this text belongs to. - size : tuple of 2 ints - The size of the listbox item. - text_color : tuple of 3 floats - unselected_color : tuple of 3 floats - selected_color : tuple of 3 floats - background_opacity : float - - """ - super(ListBoxItem2D, self).__init__() - self._element = None - self.list_box = list_box - self.background.resize(size) - self.background_opacity = background_opacity - self.selected = False - self.text_color = text_color - self.textblock.color = self.text_color - self.selected_color = selected_color - self.unselected_color = unselected_color - self.background.opacity = self.background_opacity - self.deselect() - - def _setup(self): - """Setup this UI component. - - Create the ListBoxItem2D with its background (Rectangle2D) and its - label (TextBlock2D). - """ - self.background = Rectangle2D() - self.textblock = TextBlock2D( - justification="left", vertical_justification="middle" - ) - - # Add default events listener for this UI component. - self.add_callback( - self.textblock.actor, "LeftButtonPressEvent", self.left_button_clicked - ) - self.add_callback( - self.background.actor, "LeftButtonPressEvent", self.left_button_clicked - ) - - def _get_actors(self): - """Get the actors composing this UI component.""" - return self.background.actors + self.textblock.actors - - def _add_to_scene(self, scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - scene : scene - - """ - self.background.add_to_scene(scene) - self.textblock.add_to_scene(scene) - - def _get_size(self): - return self.background.size - - def _set_position(self, coords): - """Set the lower-left corner position of this UI component. - - Parameters - ---------- - coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - self.textblock.position = coords - # Center background underneath the text. - position = coords - self.background.position = ( - position[0], - position[1] - self.background.size[1] / 2.0, - ) - - def deselect(self): - self.background.color = self.unselected_color - self.textblock.bold = False - self.selected = False - - def select(self): - self.textblock.bold = True - self.background.color = self.selected_color - self.selected = True - - @property - def element(self): - return self._element - - @element.setter - def element(self, element): - self._element = element - self.textblock.message = "" if self._element is None else str(element) - - def left_button_clicked(self, i_ren, _obj, _list_box_item): - """Handle left click for this UI element. - - Parameters - ---------- - i_ren: :class:`CustomInteractorStyle` - obj: :class:`vtkActor` - The picked actor - _list_box_item: :class:`ListBoxItem2D` - - """ - multiselect = i_ren.event.ctrl_key - range_select = i_ren.event.shift_key - self.list_box.select( - item=self, multiselect=multiselect, range_select=range_select - ) - i_ren.force_render() - - def resize(self, size): - self.background.resize(size) - - -class FileMenu2D(UI): - """A menu to select files in the current folder. - - Can go to new folder, previous folder and select multiple files. - - Attributes - ---------- - extensions: ['extension1', 'extension2', ....] - To show all files, extensions=["*"] or [""] - List of extensions to be shown as files. - listbox : :class: 'ListBox2D' - Container for the menu. - - """ - - @warn_on_args_to_kwargs() - def __init__( - self, - directory_path, - *, - extensions=None, - position=(0, 0), - size=(100, 300), - multiselection=True, - reverse_scrolling=False, - font_size=20, - line_spacing=1.4, - ): - """Init class instance. - - Parameters - ---------- - extensions: list(string) - List of extensions to be shown as files. - directory_path: string - Path of the directory where this dialog should open. - position : (float, float) - Absolute coordinates (x, y) of the lower-left corner of this - UI component. - size : (int, int) - Width and height in pixels of this UI component. - multiselection: {True, False} - Whether multiple values can be selected at once. - reverse_scrolling: {True, False} - If True, scrolling up will move the list of files down. - font_size: int - The font size in pixels. - line_spacing: float - Distance between listbox's items in pixels. - - """ - self.font_size = font_size - self.multiselection = multiselection - self.reverse_scrolling = reverse_scrolling - self.line_spacing = line_spacing - self.extensions = extensions or ["*"] - self.current_directory = directory_path - self.menu_size = size - self.directory_contents = [] - - super(FileMenu2D, self).__init__() - self.position = position - self.set_slot_colors() - - def _setup(self): - """Setup this UI component. - - Create the ListBox (Panel2D) filled with empty slots (ListBoxItem2D). - - """ - self.directory_contents = self.get_all_file_names() - content_names = [x[0] for x in self.directory_contents] - self.listbox = ListBox2D( - values=content_names, - multiselection=self.multiselection, - font_size=self.font_size, - line_spacing=self.line_spacing, - reverse_scrolling=self.reverse_scrolling, - size=self.menu_size, - ) - - self.add_callback( - self.listbox.scroll_bar.actor, "MouseMoveEvent", self.scroll_callback - ) - - # Handle mouse wheel events on the panel. - up_event = "MouseWheelForwardEvent" - down_event = "MouseWheelBackwardEvent" - if self.reverse_scrolling: - up_event, down_event = down_event, up_event # Swap events - - self.add_callback( - self.listbox.panel.background.actor, up_event, self.scroll_callback - ) - self.add_callback( - self.listbox.panel.background.actor, down_event, self.scroll_callback - ) - - # Handle mouse wheel events on the slots. - for slot in self.listbox.slots: - self.add_callback(slot.background.actor, up_event, self.scroll_callback) - self.add_callback(slot.background.actor, down_event, self.scroll_callback) - self.add_callback(slot.textblock.actor, up_event, self.scroll_callback) - self.add_callback(slot.textblock.actor, down_event, self.scroll_callback) - slot.add_callback( - slot.textblock.actor, - "LeftButtonPressEvent", - self.directory_click_callback, - ) - slot.add_callback( - slot.background.actor, - "LeftButtonPressEvent", - self.directory_click_callback, - ) - - def _get_actors(self): - """Get the actors composing this UI component.""" - return self.listbox.actors - - def resize(self, size): - pass - - def _set_position(self, coords): - """Set the lower-left corner position of this UI component. - - Parameters - ---------- - coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - self.listbox.position = coords - - def _add_to_scene(self, scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - scene : scene - - """ - self.listbox.add_to_scene(scene) - - def _get_size(self): - return self.listbox.size - - def get_all_file_names(self): - """Get file and directory names. - - Returns - ------- - all_file_names: list((string, {"directory", "file"})) - List of all file and directory names as string. - - """ - all_file_names = [] - - directory_names = self.get_directory_names() - for directory_name in directory_names: - all_file_names.append((directory_name, "directory")) - - file_names = self.get_file_names() - for file_name in file_names: - all_file_names.append((file_name, "file")) - - return all_file_names - - def get_directory_names(self): - """Find names of all directories in the current_directory - - Returns - ------- - directory_names: list(string) - List of all directory names as string. - - """ - # A list of directory names in the current directory - directory_names = [] - for _, dirnames, _ in os.walk(self.current_directory): - directory_names += dirnames - break - directory_names.sort(key=lambda s: s.lower()) - directory_names.insert(0, "../") - return directory_names - - def get_file_names(self): - """Find names of all files in the current_directory - - Returns - ------- - file_names: list(string) - List of all file names as string. - - """ - # A list of file names with extension in the current directory - files = [] - for _, _, f in os.walk(self.current_directory): - files += f - break - - file_names = [] - if "*" in self.extensions or "" in self.extensions: - file_names = files - else: - for ext in self.extensions: - for file in files: - if file.endswith("." + ext): - file_names.append(file) - file_names.sort(key=lambda s: s.lower()) - return file_names - - def set_slot_colors(self): - """Set the text color of the slots based on the type of element - they show. Blue for directories and green for files. - """ - for idx, slot in enumerate(self.listbox.slots): - list_idx = min( - self.listbox.view_offset + idx, len(self.directory_contents) - 1 - ) - if self.directory_contents[list_idx][1] == "directory": - slot.textblock.color = (0, 0.6, 0) - elif self.directory_contents[list_idx][1] == "file": - slot.textblock.color = (0, 0, 0.7) - - def scroll_callback(self, i_ren, _obj, _filemenu_item): - """Handle scroll and change the slot text colors. - - Parameters - ---------- - i_ren: :class:`CustomInteractorStyle` - obj: :class:`vtkActor` - The picked actor - _filemenu_item: :class:`FileMenu2D` - - """ - self.set_slot_colors() - i_ren.force_render() - i_ren.event.abort() - - def directory_click_callback(self, i_ren, _obj, listboxitem): - """Handle the move into a directory if it has been clicked. - - Parameters - ---------- - i_ren: :class:`CustomInteractorStyle` - obj: :class:`vtkActor` - The picked actor - listboxitem: :class:`ListBoxItem2D` - - """ - if (listboxitem.element, "directory") in self.directory_contents: - new_directory_path = os.path.join( - self.current_directory, listboxitem.element - ) - if os.access(new_directory_path, os.R_OK): - self.current_directory = new_directory_path - self.directory_contents = self.get_all_file_names() - content_names = [x[0] for x in self.directory_contents] - self.listbox.clear_selection() - self.listbox.values = content_names - self.listbox.view_offset = 0 - self.listbox.update() - self.listbox.update_scrollbar() - self.set_slot_colors() - i_ren.force_render() - i_ren.event.abort() - - -class DrawShape(UI): - """Create and Manage 2D Shapes.""" - - @warn_on_args_to_kwargs() - def __init__(self, shape_type, *, drawpanel=None, position=(0, 0)): - """Init this UI element. - - Parameters - ---------- - shape_type : string - Type of shape to be created. - drawpanel : DrawPanel, optional - Reference to the main canvas on which it is drawn. - position : (float, float), optional - (x, y) in pixels. - - """ - self.shape = None - self.shape_type = shape_type.lower() - self.drawpanel = drawpanel - self.max_size = None - self.rotation = 0 - super(DrawShape, self).__init__(position=position) - self.shape.color = np.random.random(3) - - def _setup(self): - """Setup this UI component. - - Create a Shape. - """ - if self.shape_type == "line": - self.shape = Rectangle2D(size=(3, 3)) - elif self.shape_type == "quad": - self.shape = Rectangle2D(size=(3, 3)) - elif self.shape_type == "circle": - self.shape = Disk2D(outer_radius=2) - else: - raise IOError("Unknown shape type: {}.".format(self.shape_type)) - - self.shape.on_left_mouse_button_pressed = self.left_button_pressed - self.shape.on_left_mouse_button_dragged = self.left_button_dragged - self.shape.on_left_mouse_button_released = self.left_button_released - - def _get_actors(self): - """Get the actors composing this UI component.""" - return self.shape - - def _add_to_scene(self, scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - scene : scene - - """ - self._scene = scene - self.shape.add_to_scene(scene) - - def _get_size(self): - return self.shape.size - - def _set_position(self, coords): - """Set the lower-left corner position of this UI component. - - Parameters - ---------- - coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - if self.shape_type == "circle": - self.shape.center = coords - else: - self.shape.position = coords - - def update_shape_position(self, center_position): - """Update the center position on the canvas. - - Parameters - ---------- - center_position: (float, float) - Absolute pixel coordinates (x, y). - - """ - new_center = self.clamp_position(center=center_position) - self.drawpanel.canvas.update_element(self, new_center, anchor="center") - self.cal_bounding_box() - - @property - def center(self): - return self._bounding_box_min + self._bounding_box_size // 2 - - @center.setter - def center(self, coords): - """Position the center of this UI component. - - Parameters - ---------- - coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - new_center = np.array(coords) - new_lower_left_corner = new_center - self._bounding_box_size // 2 - self.position = new_lower_left_corner + self._bounding_box_offset - self.cal_bounding_box() - - @property - def is_selected(self): - return self._is_selected - - @is_selected.setter - def is_selected(self, value): - if self.drawpanel and value: - self.drawpanel.current_shape = self - self._is_selected = value - self.selection_change() - - def selection_change(self): - if self.is_selected: - self.drawpanel.rotation_slider.value = self.rotation - else: - self.drawpanel.rotation_slider.set_visibility(False) - - def rotate(self, angle): - """Rotate the vertices of the UI component using specific angle. - - Parameters - ---------- - angle: float - Value by which the vertices are rotated in radian. - - """ - if self.shape_type == "circle": - return - points_arr = vertices_from_actor(self.shape.actor) - new_points_arr = rotate_2d(points_arr, angle) - set_polydata_vertices(self.shape._polygonPolyData, new_points_arr) - update_actor(self.shape.actor) - - self.cal_bounding_box() - - def cal_bounding_box(self): - """Calculate the min, max position and the size of the bounding box.""" - vertices = self.position + vertices_from_actor(self.shape.actor)[:, :-1] - - ( - self._bounding_box_min, - self._bounding_box_max, - self._bounding_box_size, - ) = cal_bounding_box_2d(vertices) - - self._bounding_box_offset = self.position - self._bounding_box_min - - @warn_on_args_to_kwargs() - def clamp_position(self, *, center=None): - """Clamp the given center according to the DrawPanel canvas. - - Parameters - ---------- - center : (float, float) - (x, y) in pixels. - - Returns - ------- - new_center: ndarray(int) - New center for the shape. - - """ - center = self.center if center is None else center - new_center = np.clip( - center, - self._bounding_box_size // 2, - self.drawpanel.canvas.size - self._bounding_box_size // 2, - ) - return new_center.astype(int) - - def resize(self, size): - """Resize the UI.""" - if self.shape_type == "line": - hyp = np.hypot(size[0], size[1]) - self.shape.resize((hyp, 3)) - self.rotate(angle=np.arctan2(size[1], size[0])) - - elif self.shape_type == "quad": - self.shape.resize(size) - - elif self.shape_type == "circle": - hyp = np.hypot(size[0], size[1]) - if self.max_size and hyp > self.max_size: - hyp = self.max_size - self.shape.outer_radius = hyp - - self.cal_bounding_box() - - def remove(self): - """Remove the Shape and all related actors.""" - self._scene.rm(self.shape.actor) - self.drawpanel.rotation_slider.set_visibility(False) - - def left_button_pressed(self, i_ren, _obj, shape): - mode = self.drawpanel.current_mode - if mode == "selection": - self.drawpanel.update_shape_selection(self) - - click_pos = np.array(i_ren.event.position) - self._drag_offset = click_pos - self.center - self.drawpanel.show_rotation_slider() - i_ren.event.abort() - elif mode == "delete": - self.remove() - else: - self.drawpanel.left_button_pressed(i_ren, _obj, self.drawpanel) - i_ren.force_render() - - def left_button_dragged(self, i_ren, _obj, shape): - if self.drawpanel.current_mode == "selection": - self.drawpanel.rotation_slider.set_visibility(False) - if self._drag_offset is not None: - click_position = i_ren.event.position - relative_center_position = ( - click_position - self._drag_offset - self.drawpanel.canvas.position - ) - self.update_shape_position(relative_center_position) - i_ren.force_render() - else: - self.drawpanel.left_button_dragged(i_ren, _obj, self.drawpanel) - - def left_button_released(self, i_ren, _obj, shape): - if self.drawpanel.current_mode == "selection": - self.drawpanel.show_rotation_slider() - i_ren.force_render() - - -class DrawPanel(UI): - """The main Canvas(Panel2D) on which everything would be drawn.""" - - @warn_on_args_to_kwargs() - def __init__(self, *, size=(400, 400), position=(0, 0), is_draggable=False): - """Init this UI element. - - Parameters - ---------- - size : (int, int), optional - Width and height in pixels of this UI component. - position : (float, float), optional - (x, y) in pixels. - is_draggable : bool, optional - Whether the background canvas will be draggble or not. - - """ - self.panel_size = size - super(DrawPanel, self).__init__(position=position) - self.is_draggable = is_draggable - self.current_mode = None - - if is_draggable: - self.current_mode = "selection" - - self.shape_list = [] - self.current_shape = None - - def _setup(self): - """Setup this UI component. - - Create a Canvas(Panel2D). - """ - self.canvas = Panel2D(size=self.panel_size) - self.canvas.background.on_left_mouse_button_pressed = self.left_button_pressed - self.canvas.background.on_left_mouse_button_dragged = self.left_button_dragged - - # Todo - # Convert mode_data into a private variable and make it read-only - # Then add the ability to insert user-defined mode - mode_data = { - "selection": ["selection.png", "selection-pressed.png"], - "line": ["line.png", "line-pressed.png"], - "quad": ["quad.png", "quad-pressed.png"], - "circle": ["circle.png", "circle-pressed.png"], - "delete": ["delete.png", "delete-pressed.png"], - } - - padding = 5 - # Todo - # Add this size to __init__ - mode_panel_size = (len(mode_data) * 35 + 2 * padding, 40) - self.mode_panel = Panel2D(size=mode_panel_size, color=(0.5, 0.5, 0.5)) - btn_pos = np.array([0, 0]) - - for mode, fname in mode_data.items(): - icon_files = [] - icon_files.append((mode, read_viz_icons(style="new_icons", fname=fname[0]))) - icon_files.append( - (mode + "-pressed", read_viz_icons(style="new_icons", fname=fname[1])) - ) - btn = Button2D(icon_fnames=icon_files) - - def mode_selector(i_ren, _obj, btn): - self.current_mode = btn.icon_names[0] - i_ren.force_render() - - btn.on_left_mouse_button_pressed = mode_selector - - self.mode_panel.add_element(btn, btn_pos + padding) - btn_pos[0] += btn.size[0] + padding - - self.canvas.add_element(self.mode_panel, (0, -mode_panel_size[1])) - - self.mode_text = TextBlock2D( - text="Select appropriate drawing mode using below icon" - ) - self.canvas.add_element(self.mode_text, (0.0, 1.0)) - - self.rotation_slider = RingSlider2D( - initial_value=0, text_template="{angle:5.1f}°" - ) - self.rotation_slider.set_visibility(False) - - def rotate_shape(slider): - angle = slider.value - previous_angle = slider.previous_value - rotation_angle = angle - previous_angle - - current_center = self.current_shape.center - self.current_shape.rotate(np.deg2rad(rotation_angle)) - self.current_shape.rotation = slider.value - self.current_shape.update_shape_position( - current_center - self.canvas.position - ) - - self.rotation_slider.on_moving_slider = rotate_shape - - def _get_actors(self): - """Get the actors composing this UI component.""" - return self.canvas.actors - - def _add_to_scene(self, scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - scene : scene - - """ - self._scene = scene - self.canvas.add_to_scene(scene) - - def _get_size(self): - return self.canvas.size - - def _set_position(self, coords): - """Set the lower-left corner position of this UI component. - - Parameters - ---------- - coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - self.canvas.position = coords + [0, self.mode_panel.size[1]] - slider_position = self.canvas.position + [ - self.canvas.size[0] - self.rotation_slider.size[0] / 2, - self.rotation_slider.size[1] / 2, - ] - self.rotation_slider.center = slider_position - - def resize(self, size): - """Resize the UI.""" - pass - - @property - def current_mode(self): - return self._current_mode - - @current_mode.setter - def current_mode(self, mode): - self.update_button_icons(mode) - self._current_mode = mode - if mode is not None: - self.mode_text.message = f"Mode: {mode}" - - def cal_min_boundary_distance(self, position): - """Calculate minimum distance between the current position and canvas boundary. - - Parameters - ---------- - position: (float,float) - current position of the shape. - - Returns - ------- - float - Minimum distance from the boundary. - - """ - distance_list = [] - # calculate distance from element to left and lower boundary - distance_list.extend(position - self.canvas.position) - # calculate distance from element to upper and right boundary - distance_list.extend(self.canvas.position + self.canvas.size - position) - - return min(distance_list) - - def draw_shape(self, shape_type, current_position): - """Draw the required shape at the given position. - - Parameters - ---------- - shape_type: string - Type of shape - line, quad, circle. - current_position: (float,float) - Lower left corner position for the shape. - - """ - shape = DrawShape( - shape_type=shape_type, drawpanel=self, position=current_position - ) - if shape_type == "circle": - shape.max_size = self.cal_min_boundary_distance(current_position) - self.shape_list.append(shape) - self._scene.add(shape) - self.canvas.add_element(shape, current_position - self.canvas.position) - self.update_shape_selection(shape) - - def resize_shape(self, current_position): - """Resize the shape. - - Parameters - ---------- - current_position: (float,float) - Lower left corner position for the shape. - - """ - self.current_shape = self.shape_list[-1] - size = current_position - self.current_shape.position - self.current_shape.resize(size) - - def update_shape_selection(self, selected_shape): - for shape in self.shape_list: - if selected_shape == shape: - shape.is_selected = True - else: - shape.is_selected = False - - def show_rotation_slider(self): - """Display the RingSlider2D to allow rotation of shape from the center.""" - self._scene.rm(*self.rotation_slider.actors) - self.rotation_slider.add_to_scene(self._scene) - self.rotation_slider.set_visibility(True) - - def update_button_icons(self, current_mode): - """Update the button icon. - - Parameters - ---------- - current_mode: string - Current mode of the UI. - - """ - for btn in self.mode_panel._elements[1:]: - if btn.icon_names[0] == current_mode: - btn.next_icon() - elif btn.current_icon_id == 1: - btn.next_icon() - - def clamp_mouse_position(self, mouse_position): - """Restrict the mouse position to the canvas boundary. - - Parameters - ---------- - mouse_position: (float,float) - Current mouse position. - - Returns - ------- - list(float) - New clipped position. - - """ - return np.clip( - mouse_position, - self.canvas.position, - self.canvas.position + self.canvas.size, - ) - - def handle_mouse_click(self, position): - if self.current_mode == "selection": - if self.is_draggable: - self._drag_offset = position - self.position - self.current_shape.is_selected = False - if self.current_mode in ["line", "quad", "circle"]: - self.draw_shape(self.current_mode, position) - - def left_button_pressed(self, i_ren, _obj, element): - self.handle_mouse_click(i_ren.event.position) - i_ren.force_render() - - def handle_mouse_drag(self, position): - if self.is_draggable and self.current_mode == "selection": - if self._drag_offset is not None: - new_position = position - self._drag_offset - self.position = new_position - if self.current_mode in ["line", "quad", "circle"]: - self.resize_shape(position) - - def left_button_dragged(self, i_ren, _obj, element): - mouse_position = self.clamp_mouse_position(i_ren.event.position) - self.handle_mouse_drag(mouse_position) - i_ren.force_render() - - -class PlaybackPanel(UI): - """A playback controller that can do essential functionalities. - such as play, pause, stop, and seek. - """ - - @warn_on_args_to_kwargs() - def __init__(self, *, loop=False, position=(0, 0), width=None): - self._width = width if width is not None else 900 - self._auto_width = width is None - self._position = position - super(PlaybackPanel, self).__init__(position=position) - self._playing = False - self._loop = None - self.loop() if loop else self.play_once() - self._speed = 1 - # callback functions - self.on_play_pause_toggle = lambda state: None - self.on_play = lambda: None - self.on_pause = lambda: None - self.on_stop = lambda: None - self.on_loop_toggle = lambda is_looping: None - self.on_progress_bar_changed = lambda x: None - self.on_speed_up = lambda x: None - self.on_slow_down = lambda x: None - self.on_speed_changed = lambda x: None - self._set_position(position) - - def _setup(self): - """Setup this Panel component.""" - self.time_text = TextBlock2D() - self.speed_text = TextBlock2D( - text="1", - font_size=21, - color=(0.2, 0.2, 0.2), - bold=True, - justification="center", - vertical_justification="middle", - ) - - self.panel = Panel2D( - size=(190, 30), - color=(1, 1, 1), - align="right", - has_border=True, - border_color=(0, 0.3, 0), - border_width=2, - ) - - play_pause_icons = [ - ("play", read_viz_icons(fname="play3.png")), - ("pause", read_viz_icons(fname="pause2.png")), - ] - - loop_icons = [ - ("once", read_viz_icons(fname="checkmark.png")), - ("loop", read_viz_icons(fname="infinite.png")), - ] - - self._play_pause_btn = Button2D(icon_fnames=play_pause_icons) - - self._loop_btn = Button2D(icon_fnames=loop_icons) - - self._stop_btn = Button2D( - icon_fnames=[("stop", read_viz_icons(fname="stop2.png"))] - ) - - self._speed_up_btn = Button2D( - icon_fnames=[("plus", read_viz_icons(fname="plus.png"))], size=(15, 15) - ) - - self._slow_down_btn = Button2D( - icon_fnames=[("minus", read_viz_icons(fname="minus.png"))], size=(15, 15) - ) - - self._progress_bar = LineSlider2D( - initial_value=0, - orientation="horizontal", - min_value=0, - max_value=100, - text_alignment="top", - length=590, - text_template="", - line_width=9, - ) - - start = 0.04 - w = 0.2 - self.panel.add_element(self._play_pause_btn, (start, 0.04)) - self.panel.add_element(self._stop_btn, (start + w, 0.04)) - self.panel.add_element(self._loop_btn, (start + 2 * w, 0.04)) - self.panel.add_element(self._slow_down_btn, (start + 0.63, 0.3)) - self.panel.add_element(self.speed_text, (start + 0.78, 0.45)) - self.panel.add_element(self._speed_up_btn, (start + 0.86, 0.3)) - - def play_pause_toggle(i_ren, _obj, _button): - self._playing = not self._playing - if self._playing: - self.play() - else: - self.pause() - self.on_play_pause_toggle(self._playing) - i_ren.force_render() - - def stop(i_ren, _obj, _button): - self.stop() - i_ren.force_render() - - def speed_up(i_ren, _obj, _button): - inc = 10 ** np.floor(np.log10(self.speed)) - self.speed = round(self.speed + inc, 13) - self.on_speed_up(self._speed) - self.on_speed_changed(self._speed) - i_ren.force_render() - - def slow_down(i_ren, _obj, _button): - dec = 10 ** np.floor(np.log10(self.speed - self.speed / 10)) - self.speed = round(self.speed - dec, 13) - self.on_slow_down(self._speed) - self.on_speed_changed(self._speed) - i_ren.force_render() - - def loop_toggle(i_ren, _obj, _button): - self._loop = not self._loop - if self._loop: - self.loop() - else: - self.play_once() - self.on_loop_toggle(self._loop) - i_ren.force_render() - - # using the adapters created above - self._play_pause_btn.on_left_mouse_button_pressed = play_pause_toggle - self._stop_btn.on_left_mouse_button_pressed = stop - self._loop_btn.on_left_mouse_button_pressed = loop_toggle - self._speed_up_btn.on_left_mouse_button_pressed = speed_up - self._slow_down_btn.on_left_mouse_button_pressed = slow_down - - def on_progress_change(slider): - t = slider.value - self.on_progress_bar_changed(t) - self.current_time = t - - self._progress_bar.on_moving_slider = on_progress_change - self.current_time = 0 - - def play(self): - """Play the playback""" - self._playing = True - self._play_pause_btn.set_icon_by_name("pause") - self.on_play() - - def stop(self): - """Stop the playback""" - self._playing = False - self._play_pause_btn.set_icon_by_name("play") - self.on_stop() - - def pause(self): - """Pause the playback""" - self._playing = False - self._play_pause_btn.set_icon_by_name("play") - self.on_pause() - - def loop(self): - """Set repeating mode to loop.""" - self._loop = True - self._loop_btn.set_icon_by_name("loop") - - def play_once(self): - """Set repeating mode to repeat once.""" - self._loop = False - self._loop_btn.set_icon_by_name("once") - - @property - def final_time(self): - """Set final progress slider time value. - - Returns - ------- - float - Final time for the progress slider. - - """ - return self._progress_bar.max_value - - @final_time.setter - def final_time(self, t): - """Set final progress slider time value. - - Parameters - ---------- - t: float - Final time for the progress slider. - - """ - self._progress_bar.max_value = t - - @property - def current_time(self): - """Get current time of the progress slider. - - Returns - ------- - float - Progress slider current value. - - """ - return self._progress_bar.value - - @current_time.setter - def current_time(self, t): - """Set progress slider value. - - Parameters - ---------- - t: float - Current time to be set. - - """ - self._progress_bar.value = t - self.current_time_str = t - - @property - def current_time_str(self): - """Returns current time as a string. - - Returns - ------- - str - Current time formatted as a string in the form:`HH:MM:SS`. - - """ - return self.time_text.message - - @current_time_str.setter - def current_time_str(self, t): - """Set time counter. - - Parameters - ---------- - t: float - Time to be set in the time_text counter. - - Notes - ----- - This should only be used when the `current_value` is not being set - since setting`current_value` automatically sets this property as well. - - """ - t = np.clip(t, 0, self.final_time) - if self.final_time < 3600: - m, s = divmod(t, 60) - t_str = r"%02d:%05.2f" % (m, s) - else: - m, s = divmod(t, 60) - h, m = divmod(m, 60) - t_str = r"%02d:%02d:%02d" % (h, m, s) - self.time_text.message = t_str - - @property - def speed(self): - """Returns current speed. - - Returns - ------- - str - Current time formatted as a string in the form:`HH:MM:SS`. - - """ - return self._speed - - @speed.setter - def speed(self, speed): - """Set time counter. - - Parameters - ---------- - speed: float - Speed value to be set in the speed_text counter. - - """ - if speed <= 0: - speed = 0.01 - self._speed = speed - speed_str = f"{speed}".strip("0").rstrip(".") - self.speed_text.font_size = 21 if 0.01 <= speed < 100 else 14 - self.speed_text.message = speed_str - - def show(self): - [act.SetVisibility(1) for act in self._get_actors()] - - def hide(self): - [act.SetVisibility(0) for act in self._get_actors()] - - def _get_actors(self): - """Get the actors composing this UI component.""" - return self.panel.actors + self._progress_bar.actors + self.time_text.actors - - def _add_to_scene(self, _scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - _scene : scene - - """ - - def resize_cbk(caller, ev): - if self._auto_width: - width = _scene.GetSize()[0] - if width == self.width: - return - self._width = width - self._set_position(self.position) - self._progress_bar.value = self._progress_bar.value - - _scene.AddObserver(Command.StartEvent, resize_cbk) - self.panel.add_to_scene(_scene) - self._progress_bar.add_to_scene(_scene) - self.time_text.add_to_scene(_scene) - - @property - def width(self): - """Return the width of the PlaybackPanel - - Returns - ------- - float - The width of the PlaybackPanel. - - """ - return self._width - - @width.setter - def width(self, width): - """Set width of the PlaybackPanel. - - Parameters - ---------- - width: float - The width of the whole panel. - If set to None, The width will be the same as the window's width. - - """ - self._width = width if width is not None else 900 - self._auto_width = width is None - self._set_position(self.position) - - def _set_position(self, _coords): - x, y = self.position - width = self.width - self.panel.position = (x + 5, y + 5) - progress_length = max(width - 310 - x, 1.0) - self._progress_bar.track.width = progress_length - self._progress_bar.center = (x + 215 + progress_length / 2, y + 20) - self.time_text.position = (x + 225 + progress_length, y + 10) - - def _get_size(self): - return self.panel.size + self._progress_bar.size + self.time_text.size - - -class Card2D(UI): - """Card element to show image and related text - - Attributes - ---------- - image: :class: 'ImageContainer2D' - Renders the image on the card. - title_box: :class: 'TextBlock2D' - Displays the title on card. - body_box: :class: 'TextBLock2D' - Displays the body text. - - """ - - @warn_on_args_to_kwargs() - def __init__( - self, - image_path, - *, - body_text="", - draggable=True, - title_text="", - padding=10, - position=(0, 0), - size=(400, 400), - image_scale=0.5, - bg_color=(0.5, 0.5, 0.5), - bg_opacity=1, - title_color=(0.0, 0.0, 0.0), - body_color=(0.0, 0.0, 0.0), - border_color=(1.0, 1.0, 1.0), - border_width=0, - maintain_aspect=False, - ): - """Parameters - ---------- - image_path: str - Path of the image, supports png and jpg/jpeg images - body_text: str, optional - Card body text - draggable: Bool, optional - If the card should be draggable - title_text: str, optional - Card title text - padding: int, optional - Padding between image, title, body - position : (float, float), optional - Absolute coordinates (x, y) of the lower-left corner of the - UI component - size : (int, int), optional - Width and height of the pixels of this UI component. - image_scale: float, optional - fraction of size taken by the image (between 0 , 1) - bg_color: (float, float, float), optional - Background color of card - bg_opacity: float, optional - Background opacity - title_color: (float, float, float), optional - Title text color - body_color: (float, float, float), optional - Body text color - border_color: (float, float, float), optional - Border color - border_width: int, optional - Width of the border - maintain_aspect: bool, optional - If the image should be scaled to maintain aspect ratio - - """ - self.image_path = image_path - self._basename = os.path.basename(self.image_path) - self._extension = self._basename.split(".")[-1] - if self._extension not in ["jpg", "jpeg", "png"]: - raise UnidentifiedImageError( - f"Image extension {self._extension} not supported" - ) - - self.body_text = body_text - self.title_text = title_text - self.draggable = draggable - self.card_size = size - self.padding = padding - - self.title_color = [np.clip(value, 0, 1) for value in title_color] - self.body_color = [np.clip(value, 0, 1) for value in body_color] - self.bg_color = [np.clip(value, 0, 1) for value in bg_color] - self.border_color = [np.clip(value, 0, 1) for value in border_color] - self.bg_opacity = bg_opacity - - self.text_scale = np.clip(1 - image_scale, 0, 1) - self.image_scale = np.clip(image_scale, 0, 1) - - self.maintain_aspect = maintain_aspect - if self.maintain_aspect: - self._true_image_size = Image.open(urlopen(self.image_path)).size - - self._image_size = (self.card_size[0], self.card_size[1] * self.image_scale) - - self.border_width = border_width - self.has_border = bool(border_width) - - super(Card2D, self).__init__() - self.position = position - - if self.maintain_aspect: - self._new_size = ( - self._true_image_size[0], - self._true_image_size[1] // self.image_scale, - ) - self.resize(self._new_size) - else: - self.resize(size) - - def _setup(self): - """Setup this UI component - Create the image. - Create the title and body. - Create a Panel2D widget to hold image, title, body. - """ - self.image = ImageContainer2D(img_path=self.image_path, size=self._image_size) - - self.body_box = TextBlock2D(text=self.body_text, color=self.body_color) - - self.title_box = TextBlock2D( - text=self.title_text, bold=True, color=self.title_color - ) - - self.panel = Panel2D( - self.card_size, - color=self.bg_color, - opacity=self.bg_opacity, - border_color=self.border_color, - border_width=self.border_width, - has_border=self.has_border, - ) - - self.panel.add_element(self.image, (0.0, 0.0)) - self.panel.add_element(self.title_box, (0.0, 0.0)) - self.panel.add_element(self.body_box, (0.0, 0.0)) - - if self.draggable: - self.panel.background.on_left_mouse_button_dragged = ( - self.left_button_dragged - ) - self.panel.background.on_left_mouse_button_pressed = ( - self.left_button_pressed - ) - self.image.on_left_mouse_button_dragged = self.left_button_dragged - self.image.on_left_mouse_button_pressed = self.left_button_pressed - else: - self.panel.background.on_left_mouse_button_dragged = ( - lambda i_ren, _obj, _comp: i_ren.force_render - ) - - def _get_actors(self): - """Get the actors composing this UI component.""" - return self.panel.actors - - def _add_to_scene(self, _scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - scene : scene - - """ - self.panel.add_to_scene(_scene) - if self.size[0] <= 200: - clip_overflow(self.body_box, self.size[0] - 2 * self.padding) - else: - wrap_overflow(self.body_box, self.size[0] - 2 * self.padding) - - wrap_overflow(self.title_box, self.size[0] - 2 * self.padding) - - def _get_size(self): - return self.panel.size - - def resize(self, size): - """Resize Card2D. - - Parameters - ---------- - size : (int, int) - Card2D size(width, height) in pixels. - - """ - _width, _height = size - self.panel.resize(size) - - self._image_size = ( - size[0] - int(self.border_width), - int(self.image_scale * size[1]), - ) - - _title_box_size = ( - _width - 2 * self.padding, - _height * 0.34 * self.text_scale / 2, - ) - - _body_box_size = (_width - 2 * self.padding, _height * self.text_scale / 2) - - _img_coords = (int(self.border_width), int(size[1] - self._image_size[1])) - - _title_coords = ( - self.padding, - int(_img_coords[1] - _title_box_size[1] - self.padding + self.border_width), - ) - - _text_coords = ( - self.padding, - int( - _title_coords[1] - _body_box_size[1] - self.padding + self.border_width - ), - ) - - self.panel.update_element(self.image, _img_coords) - self.panel.update_element(self.body_box, _text_coords) - self.panel.update_element(self.title_box, _title_coords) - - self.image.resize(self._image_size) - self.title_box.resize(_title_box_size) - - def _set_position(self, _coords): - """Position the lower-left corner of this UI component. - - Parameters - ---------- - coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - self.panel.position = _coords - - @property - def color(self): - """Returns the background color of card.""" - return self.panel.color - - @color.setter - def color(self, color): - """Sets background color of card. - - Parameters - ---------- - color : list of 3 floats. - - """ - self.panel.color = color - - @property - def body(self): - """Returns the body text of the card.""" - return self.body_box.message - - @body.setter - def body(self, text): - self.body_box.message = text - - @property - def title(self): - """Returns the title text of the card""" - return self.title_box.message - - @title.setter - def title(self, text): - self.title_box.message = text - - def left_button_pressed(self, i_ren, _obj, _sub_component): - click_pos = np.array(i_ren.event.position) - self._click_position = click_pos - i_ren.event.abort() - - def left_button_dragged(self, i_ren, _obj, _sub_component): - click_position = np.array(i_ren.event.position) - change = click_position - self._click_position - self.panel.position += change - self._click_position = click_position - i_ren.force_render() - - -class SpinBox(UI): - """SpinBox UI.""" - - @warn_on_args_to_kwargs() - def __init__( - self, - *, - position=(350, 400), - size=(300, 100), - padding=10, - panel_color=(1, 1, 1), - min_val=0, - max_val=100, - initial_val=50, - step=1, - max_column=10, - max_line=2, - ): - """Init this UI element. - - Parameters - ---------- - position : (int, int), optional - Absolute coordinates (x, y) of the lower-left corner of this - UI component. - size : (int, int), optional - Width and height in pixels of this UI component. - padding : int, optional - Distance between TextBox and Buttons. - panel_color : (float, float, float), optional - Panel color of SpinBoxUI. - min_val: int, optional - Minimum value of SpinBoxUI. - max_val: int, optional - Maximum value of SpinBoxUI. - initial_val: int, optional - Initial value of SpinBoxUI. - step: int, optional - Step value of SpinBoxUI. - max_column: int, optional - Max number of characters in a line. - max_line: int, optional - Max number of lines in the textbox. - - """ - self.panel_size = size - self.padding = padding - self.panel_color = panel_color - self.min_val = min_val - self.max_val = max_val - self.step = step - self.max_column = max_column - self.max_line = max_line - - super(SpinBox, self).__init__(position=position) - self.value = initial_val - self.resize(size) - - self.on_change = lambda ui: None - - def _setup(self): - """Setup this UI component. - - Create the SpinBoxUI with Background (Panel2D) and InputBox (TextBox2D) - and Increment,Decrement Button (Button2D). - """ - self.panel = Panel2D(size=self.panel_size, color=self.panel_color) - - self.textbox = TextBox2D(width=self.max_column, height=self.max_line) - self.textbox.text.dynamic_bbox = False - self.textbox.text.auto_font_scale = True - self.increment_button = Button2D( - icon_fnames=[("up", read_viz_icons(fname="circle-up.png"))] - ) - self.decrement_button = Button2D( - icon_fnames=[("down", read_viz_icons(fname="circle-down.png"))] - ) - - self.panel.add_element(self.textbox, (0, 0)) - self.panel.add_element(self.increment_button, (0, 0)) - self.panel.add_element(self.decrement_button, (0, 0)) - - # Adding button click callbacks - self.increment_button.on_left_mouse_button_pressed = self.increment_callback - self.decrement_button.on_left_mouse_button_pressed = self.decrement_callback - self.textbox.off_focus = self.textbox_update_value - - def resize(self, size): - """Resize SpinBox. - - Parameters - ---------- - size : (float, float) - SpinBox size(width, height) in pixels. - - """ - self.panel_size = size - self.textbox_size = (int(0.7 * size[0]), int(0.8 * size[1])) - self.button_size = (int(0.2 * size[0]), int(0.3 * size[1])) - self.padding = int(0.03 * self.panel_size[0]) - - self.panel.resize(size) - self.textbox.text.resize(self.textbox_size) - self.increment_button.resize(self.button_size) - self.decrement_button.resize(self.button_size) - - textbox_pos = (self.padding, int((size[1] - self.textbox_size[1]) / 2)) - inc_btn_pos = ( - size[0] - self.padding - self.button_size[0], - int((1.5 * size[1] - self.button_size[1]) / 2), - ) - dec_btn_pos = ( - size[0] - self.padding - self.button_size[0], - int((0.5 * size[1] - self.button_size[1]) / 2), - ) - - self.panel.update_element(self.textbox, textbox_pos) - self.panel.update_element(self.increment_button, inc_btn_pos) - self.panel.update_element(self.decrement_button, dec_btn_pos) - - def _get_actors(self): - """Get the actors composing this UI component.""" - return self.panel.actors - - def _add_to_scene(self, scene): - """Add all subcomponents or VTK props that compose this UI component. - - Parameters - ---------- - scene : Scene - - """ - self.panel.add_to_scene(scene) - - def _get_size(self): - return self.panel.size - - def _set_position(self, coords): - """Set the lower-left corner position of this UI component. - - Parameters - ---------- - coords: (float, float) - Absolute pixel coordinates (x, y). - - """ - self.panel.center = coords - - def increment_callback(self, i_ren, _obj, _button): - self.increment() - i_ren.force_render() - i_ren.event.abort() - - def decrement_callback(self, i_ren, _obj, _button): - self.decrement() - i_ren.force_render() - i_ren.event.abort() - - @property - def value(self): - return self._value - - @value.setter - def value(self, value): - if value >= self.max_val: - self._value = self.max_val - elif value <= self.min_val: - self._value = self.min_val - else: - self._value = value - - self.textbox.set_message(str(self._value)) - - def validate_value(self, value): - """Validate and convert the given value into integer. - - Parameters - ---------- - value : str - Input value received from the textbox. - - Returns - ------- - int - If valid return converted integer else the previous value. - - """ - if value.isnumeric(): - return int(value) - - return self.value - - def increment(self): - """Increment the current value by the step.""" - current_val = self.validate_value(self.textbox.message) - self.value = current_val + self.step - self.on_change(self) - - def decrement(self): - """Decrement the current value by the step.""" - current_val = self.validate_value(self.textbox.message) - self.value = current_val - self.step - self.on_change(self) - - def textbox_update_value(self, textbox): - self.value = self.validate_value(textbox.message) - self.on_change(self) +# """UI components module.""" + +# __all__ = [ +# "TextBox2D", +# "LineSlider2D", +# "LineDoubleSlider2D", +# "RingSlider2D", +# "RangeSlider", +# "Checkbox", +# "Option", +# "RadioButton", +# "ComboBox2D", +# "ListBox2D", +# "ListBoxItem2D", +# "FileMenu2D", +# "DrawShape", +# "DrawPanel", +# "PlaybackPanel", +# "Card2D", +# "SpinBox", +# ] + +# from collections import OrderedDict +# from numbers import Number +# import os +# from string import printable +# from urllib.request import urlopen + +# from PIL import Image, UnidentifiedImageError +# import numpy as np + +# from fury.data import read_viz_icons +# from fury.decorators import warn_on_args_to_kwargs +# from fury.lib import Command +# from fury.ui.containers import ImageContainer2D, Panel2D +# from fury.ui.core import UI, Button2D, Disk2D, Rectangle2D, TextBlock2D +# from fury.ui.helpers import ( +# TWO_PI, +# cal_bounding_box_2d, +# clip_overflow, +# rotate_2d, +# wrap_overflow, +# ) +# from fury.utils import set_polydata_vertices, update_actor, vertices_from_actor + + +# class TextBox2D(UI): +# """An editable 2D text box that behaves as a UI component. + +# Currently supports: +# - Basic text editing. +# - Cursor movements. +# - Single and multi-line text boxes. +# - Pre text formatting (text needs to be formatted beforehand). + +# Attributes +# ---------- +# text : str +# The current text state. +# actor : :class:`vtkActor2d` +# The text actor. +# width : int +# The number of characters in a single line of text. +# height : int +# The number of lines in the textbox. +# window_left : int +# Left limit of visible text in the textbox. +# window_right : int +# Right limit of visible text in the textbox. +# caret_pos : int +# Position of the caret in the text. +# init : bool +# Flag which says whether the textbox has just been initialized. + +# """ + +# @warn_on_args_to_kwargs() +# def __init__( +# self, +# width, +# height, +# *, +# text="Enter Text", +# position=(100, 10), +# color=(0, 0, 0), +# font_size=18, +# font_family="Arial", +# justification="left", +# bold=False, +# italic=False, +# shadow=False, +# ): +# """Init this UI element. + +# Parameters +# ---------- +# width : int +# The number of characters in a single line of text. +# height : int +# The number of lines in the textbox. +# text : str +# The initial text while building the actor. +# position : (float, float) +# (x, y) in pixels. +# color : (float, float, float) +# RGB: Values must be between 0-1. +# font_size : int +# Size of the text font. +# font_family : str +# Currently only supports Arial. +# justification : str +# left, right or center. +# bold : bool +# Makes text bold. +# italic : bool +# Makes text italicised. +# shadow : bool +# Adds text shadow. + +# """ +# super(TextBox2D, self).__init__(position=position) + +# self.message = text +# self.text.message = text +# self.text.font_size = font_size +# self.text.font_family = font_family +# self.text.justification = justification +# self.text.bold = bold +# self.text.italic = italic +# self.text.shadow = shadow +# self.text.color = color +# self.text.background_color = (1, 1, 1) + +# self.width = width +# self.height = height +# self.window_left = 0 +# self.window_right = 0 +# self.caret_pos = 0 +# self.init = True + +# self.off_focus = lambda ui: None + +# def _setup(self): +# """Setup this UI component. + +# Create the TextBlock2D component used for the textbox. +# """ +# self.text = TextBlock2D(dynamic_bbox=True) + +# # Add default events listener for this UI component. +# self.text.on_left_mouse_button_pressed = self.left_button_press +# self.text.on_key_press = self.key_press + +# def _get_actors(self): +# """Get the actors composing this UI component.""" +# return self.text.actors + +# def _add_to_scene(self, scene): +# """Add all subcomponents or VTK props that compose this UI component. + +# Parameters +# ---------- +# scene : scene + +# """ +# self.text.add_to_scene(scene) + +# def _set_position(self, coords): +# """Set the lower-left corner position of this UI component. + +# Parameters +# ---------- +# coords: (float, float) +# Absolute pixel coordinates (x, y). + +# """ +# self.text.position = coords + +# def _get_size(self): +# return self.text.size + +# def set_message(self, message): +# """Set custom text to textbox. + +# Parameters +# ---------- +# message: str +# The custom message to be set. + +# """ +# self.message = message +# self.text.message = message +# self.init = False +# self.window_right = len(self.message) +# self.window_left = 0 +# self.caret_pos = self.window_right + +# def width_set_text(self, text): +# """Add newlines to text where necessary. + +# This is needed for multi-line text boxes. + +# Parameters +# ---------- +# text : str +# The final text to be formatted. + +# Returns +# ------- +# str +# A multi line formatted text. + +# """ +# multi_line_text = "" +# for i, t in enumerate(text): +# multi_line_text += t +# if (i + 1) % self.width == 0: +# multi_line_text += "\n" +# return multi_line_text.rstrip("\n") + +# def handle_character(self, key, key_char): +# """Handle button events. + +# # TODO: Need to handle all kinds of characters like !, +, etc. + +# Parameters +# ---------- +# character : str + +# """ +# if key.lower() == "return": +# self.render_text(show_caret=False) +# self.off_focus(self) +# return True +# elif key_char != "" and key_char in printable: +# self.add_character(key_char) +# if key.lower() == "backspace": +# self.remove_character() +# elif key.lower() == "left": +# self.move_left() +# elif key.lower() == "right": +# self.move_right() + +# self.render_text() +# return False + +# def move_caret_right(self): +# """Move the caret towards right.""" +# self.caret_pos = min(self.caret_pos + 1, len(self.message)) + +# def move_caret_left(self): +# """Move the caret towards left.""" +# self.caret_pos = max(self.caret_pos - 1, 0) + +# def right_move_right(self): +# """Move right boundary of the text window right-wards.""" +# if self.window_right <= len(self.message): +# self.window_right += 1 + +# def right_move_left(self): +# """Move right boundary of the text window left-wards.""" +# if self.window_right > 0: +# self.window_right -= 1 + +# def left_move_right(self): +# """Move left boundary of the text window right-wards.""" +# if self.window_left <= len(self.message): +# self.window_left += 1 + +# def left_move_left(self): +# """Move left boundary of the text window left-wards.""" +# if self.window_left > 0: +# self.window_left -= 1 + +# def add_character(self, character): +# """Insert a character into the text and moves window and caret. + +# Parameters +# ---------- +# character : str + +# """ +# if len(character) > 1 and character.lower() != "space": +# return +# if character.lower() == "space": +# character = " " +# self.message = ( +# +# self.message[: self.caret_pos] + character + self.message[self.caret_pos :] +# ) +# self.move_caret_right() +# if self.window_right - self.window_left == self.height * self.width - 1: +# self.left_move_right() +# self.right_move_right() + +# def remove_character(self): +# """Remove a character and moves window and caret accordingly.""" +# if self.caret_pos == 0: +# return +# self.message = ( +# self.message[: self.caret_pos - 1] + self.message[self.caret_pos :] +# ) +# self.move_caret_left() +# if len(self.message) < self.height * self.width - 1: +# self.right_move_left() +# if self.window_right - self.window_left == self.height * self.width - 1: +# if self.window_left > 0: +# self.left_move_left() +# self.right_move_left() + +# def move_left(self): +# """Handle left button press.""" +# self.move_caret_left() +# if self.caret_pos == self.window_left - 1: +# if self.window_right - self.window_left == self.height * self.width - 1: +# self.left_move_left() +# self.right_move_left() + +# def move_right(self): +# """Handle right button press.""" +# self.move_caret_right() +# if self.caret_pos == self.window_right + 1: +# if self.window_right - self.window_left == self.height * self.width - 1: +# self.left_move_right() +# self.right_move_right() + +# def showable_text(self, show_caret): +# """Chop out text to be shown on the screen. + +# Parameters +# ---------- +# show_caret : bool +# Whether or not to show the caret. + +# """ +# if show_caret: +# ret_text = ( +# self.message[: self.caret_pos] + "_" + self.message[self.caret_pos :] +# ) +# else: +# ret_text = self.message +# ret_text = ret_text[self.window_left : self.window_right + 1] +# return ret_text + +# @warn_on_args_to_kwargs() +# def render_text(self, *, show_caret=True): +# """Render text after processing. + +# Parameters +# ---------- +# show_caret : bool +# Whether or not to show the caret. + +# """ +# text = self.showable_text(show_caret) +# if text == "": +# text = "Enter Text" +# self.text.message = self.width_set_text(text) + +# def edit_mode(self): +# """Turn on edit mode.""" +# if self.init: +# self.message = "" +# self.init = False +# self.caret_pos = 0 +# self.render_text() + +# def left_button_press(self, i_ren, _obj, _textbox_object): +# """Handle left button press for textbox. + +# Parameters +# ---------- +# i_ren: :class:`CustomInteractorStyle` +# obj: :class:`vtkActor` +# The picked actor +# _textbox_object: :class:`TextBox2D` + +# """ +# i_ren.add_active_prop(self.text.actor) +# self.edit_mode() +# i_ren.force_render() + +# def key_press(self, i_ren, _obj, _textbox_object): +# """Handle Key press for textboxself. + +# Parameters +# ---------- +# i_ren: :class:`CustomInteractorStyle` +# obj: :class:`vtkActor` +# The picked actor +# _textbox_object: :class:`TextBox2D` + +# """ +# key = i_ren.event.key +# key_char = i_ren.event.key_char +# is_done = self.handle_character(key, key_char) +# if is_done: +# i_ren.remove_active_prop(self.text.actor) + +# i_ren.force_render() + + +# class LineSlider2D(UI): +# """A 2D Line Slider. + +# A sliding handle on a line with a percentage indicator. + +# Attributes +# ---------- +# line_width : int +# Width of the line on which the disk will slide. +# length : int +# Length of the slider. +# track : :class:`Rectangle2D` +# The line on which the slider's handle moves. +# handle : :class:`Disk2D` +# The moving part of the slider. +# text : :class:`TextBlock2D` +# The text that shows percentage. +# shape : string +# Describes the shape of the handle. +# Currently supports 'disk' and 'square'. +# default_color : (float, float, float) +# Color of the handle when in unpressed state. +# active_color : (float, float, float) +# Color of the handle when it is pressed. + +# """ + +# @warn_on_args_to_kwargs() +# def __init__( +# self, +# *, +# center=(0, 0), +# initial_value=50, +# min_value=0, +# max_value=100, +# length=200, +# line_width=5, +# inner_radius=0, +# outer_radius=10, +# handle_side=20, +# font_size=16, +# orientation="horizontal", +# text_alignment="", +# text_template="{value:.1f} ({ratio:.0%})", +# shape="disk", +# ): +# """Init this UI element. + +# Parameters +# ---------- +# center : (float, float) +# Center of the slider's center. +# initial_value : float +# Initial value of the slider. +# min_value : float +# Minimum value of the slider. +# max_value : float +# Maximum value of the slider. +# length : int +# Length of the slider. +# line_width : int +# Width of the line on which the disk will slide. +# inner_radius : int +# Inner radius of the handles (if disk). +# outer_radius : int +# Outer radius of the handles (if disk). +# handle_side : int +# Side length of the handles (if square). +# font_size : int +# Size of the text to display alongside the slider (pt). +# orientation : str +# horizontal or vertical +# text_alignment : str +# define text alignment on a slider. Left (default)/ right for the +# vertical slider or top/bottom (default) for an horizontal slider. +# text_template : str, callable +# If str, text template can contain one or multiple of the +# replacement fields: `{value:}`, `{ratio:}`. +# If callable, this instance of `:class:LineSlider2D` will be +# passed as argument to the text template function. +# shape : string +# Describes the shape of the handle. +# Currently supports 'disk' and 'square'. + +# """ +# self.shape = shape +# self.orientation = orientation.lower().strip() +# self.align_dict = { +# "horizontal": ["top", "bottom"], +# "vertical": ["left", "right"], +# } +# self.default_color = (1, 1, 1) +# self.active_color = (0, 0, 1) +# self.alignment = text_alignment.lower() +# super(LineSlider2D, self).__init__() + +# if self.orientation == "horizontal": +# self.alignment = "bottom" if not self.alignment else self.alignment +# self.track.width = length +# self.track.height = line_width +# elif self.orientation == "vertical": +# self.alignment = "left" if not self.alignment else self.alignment +# self.track.width = line_width +# self.track.height = length +# else: +# raise ValueError("Unknown orientation") + +# if self.alignment not in self.align_dict[self.orientation]: +# raise ValueError( +# "Unknown alignment: choose from '{}' or '{}'".format( +# *self.align_dict[self.orientation] +# ) +# ) + +# if shape == "disk": +# self.handle.inner_radius = inner_radius +# self.handle.outer_radius = outer_radius +# elif shape == "square": +# self.handle.width = handle_side +# self.handle.height = handle_side +# self.center = center + +# self.min_value = min_value +# self.max_value = max_value +# self.text.font_size = font_size +# self.text_template = text_template + +# # Offer some standard hooks to the user. +# self.on_change = lambda ui: None +# self.on_value_changed = lambda ui: None +# self.on_moving_slider = lambda ui: None + +# self.value = initial_value +# self.update() + +# def _setup(self): +# """Setup this UI component. + +# Create the slider's track (Rectangle2D), the handle (Disk2D) and +# the text (TextBlock2D). +# """ +# # Slider's track +# self.track = Rectangle2D() +# self.track.color = (1, 0, 0) + +# # Slider's handle +# if self.shape == "disk": +# self.handle = Disk2D(outer_radius=1) +# elif self.shape == "square": +# self.handle = Rectangle2D(size=(1, 1)) +# self.handle.color = self.default_color + +# # Slider Text +# self.text = TextBlock2D(justification="center", vertical_justification="top") + +# # Add default events listener for this UI component. +# self.track.on_left_mouse_button_pressed = self.track_click_callback +# self.track.on_left_mouse_button_dragged = self.handle_move_callback +# self.track.on_left_mouse_button_released = self.handle_release_callback +# self.handle.on_left_mouse_button_dragged = self.handle_move_callback +# self.handle.on_left_mouse_button_released = self.handle_release_callback + +# def _get_actors(self): +# """Get the actors composing this UI component.""" +# return self.track.actors + self.handle.actors + self.text.actors + +# def _add_to_scene(self, scene): +# """Add all subcomponents or VTK props that compose this UI component. + +# Parameters +# ---------- +# scene : scene + +# """ +# self.track.add_to_scene(scene) +# self.handle.add_to_scene(scene) +# self.text.add_to_scene(scene) + +# def _get_size(self): +# # Consider the handle's size when computing the slider's size. +# width = None +# height = None +# if self.orientation == "horizontal": +# width = self.track.width + self.handle.size[0] +# height = max(self.track.height, self.handle.size[1]) +# else: +# width = max(self.track.width, self.handle.size[0]) +# height = self.track.height + self.handle.size[1] + +# return np.array([width, height]) + +# def _set_position(self, coords): +# """Set the lower-left corner position of this UI component. + +# Parameters +# ---------- +# coords: (float, float) +# Absolute pixel coordinates (x, y). + +# """ +# # Offset the slider line by the handle's radius. +# track_position = coords + self.handle.size / 2.0 +# if self.orientation == "horizontal": +# # Offset the slider line height by half the slider line width. +# track_position[1] -= self.track.size[1] / 2.0 +# else: +# # Offset the slider line width by half the slider line height. +# track_position[0] += self.track.size[0] / 2.0 + +# self.track.position = track_position +# self.handle.position = self.handle.position.astype(float) +# self.handle.position += coords - self.position +# # Position the text below the handle. +# if self.orientation == "horizontal": +# align = 35 if self.alignment == "top" else -10 +# self.text.position = ( +# self.handle.center[0], +# self.handle.position[1] + align, +# ) +# else: +# align = 70 if self.alignment == "right" else -35 +# self.text.position = ( +# self.handle.position[0] + align, +# self.handle.center[1] + 2, +# ) + +# @property +# def bottom_y_position(self): +# return self.track.position[1] + +# @property +# def top_y_position(self): +# return self.track.position[1] + self.track.size[1] + +# @property +# def left_x_position(self): +# return self.track.position[0] + +# @property +# def right_x_position(self): +# return self.track.position[0] + self.track.size[0] + +# def set_position(self, position): +# """Set the disk's position. + +# Parameters +# ---------- +# position : (float, float) +# The absolute position of the disk (x, y). + +# """ +# # Move slider disk. +# if self.orientation == "horizontal": +# x_position = position[0] +# x_position = max(x_position, self.left_x_position) +# x_position = min(x_position, self.right_x_position) +# self.handle.center = (x_position, self.track.center[1]) +# else: +# y_position = position[1] +# y_position = max(y_position, self.bottom_y_position) +# y_position = min(y_position, self.top_y_position) +# self.handle.center = (self.track.center[0], y_position) +# self.update() # Update information. + +# @property +# def value(self): +# return self._value + +# @value.setter +# def value(self, value): +# value_range = self.max_value - self.min_value +# self.ratio = (value - self.min_value) / value_range if value_range else 0 +# self.on_value_changed(self) + +# @property +# def ratio(self): +# return self._ratio + +# @ratio.setter +# def ratio(self, ratio): +# position_x = self.left_x_position + ratio * self.track.width +# position_y = self.bottom_y_position + ratio * self.track.height +# self.set_position((position_x, position_y)) + +# def format_text(self): +# """Return formatted text to display along the slider.""" +# if callable(self.text_template): +# return self.text_template(self) +# return self.text_template.format(ratio=self.ratio, value=self.value) + +# def update(self): +# """Update the slider.""" +# # Compute the ratio determined by the position of the slider disk. +# disk_position_x = None +# disk_position_y = None + +# if self.orientation == "horizontal": +# length = float(self.right_x_position - self.left_x_position) +# length = np.round(length, decimals=6) +# if length != self.track.width: +# raise ValueError("Disk position outside the slider line") +# disk_position_x = self.handle.center[0] +# self._ratio = (disk_position_x - self.left_x_position) / length +# else: +# length = float(self.top_y_position - self.bottom_y_position) +# if length != self.track.height: +# raise ValueError("Disk position outside the slider line") +# disk_position_y = self.handle.center[1] +# self._ratio = (disk_position_y - self.bottom_y_position) / length + +# # Compute the selected value considering min_value and max_value. +# value_range = self.max_value - self.min_value +# self._value = self.min_value + self.ratio * value_range + +# # Update text. +# text = self.format_text() +# self.text.message = text + +# # Move the text below the slider's handle. +# if self.orientation == "horizontal": +# self.text.position = (disk_position_x, self.text.position[1]) +# else: +# self.text.position = (self.text.position[0], disk_position_y) + +# self.on_change(self) + +# def track_click_callback(self, i_ren, _vtkactor, _slider): +# """Update disk position and grab the focus. + +# Parameters +# ---------- +# i_ren : :class:`CustomInteractorStyle` +# vtkactor : :class:`vtkActor` +# The picked actor +# _slider : :class:`LineSlider2D` + +# """ +# position = i_ren.event.position +# self.set_position(position) +# self.on_moving_slider(self) +# i_ren.force_render() +# i_ren.event.abort() # Stop propagating the event. + +# def handle_move_callback(self, i_ren, _vtkactor, _slider): +# """Handle movement. + +# Parameters +# ---------- +# i_ren : :class:`CustomInteractorStyle` +# vtkactor : :class:`vtkActor` +# The picked actor +# slider : :class:`LineSlider2D` + +# """ +# self.handle.color = self.active_color +# position = i_ren.event.position +# self.set_position(position) +# self.on_moving_slider(self) +# i_ren.force_render() +# i_ren.event.abort() # Stop propagating the event. + +# def handle_release_callback(self, i_ren, _vtkactor, _slider): +# """Change color when handle is released. + +# Parameters +# ---------- +# i_ren : :class:`CustomInteractorStyle` +# vtkactor : :class:`vtkActor` +# The picked actor +# slider : :class:`LineSlider2D` + +# """ +# self.handle.color = self.default_color +# i_ren.force_render() + + +# class LineDoubleSlider2D(UI): +# """A 2D Line Slider with two sliding rings. + +# Useful for setting min and max values for something. + +# Currently supports: +# - Setting positions of both disks. + +# Attributes +# ---------- +# line_width : int +# Width of the line on which the disk will slide. +# length : int +# Length of the slider. +# track : :class:`vtkActor` +# The line on which the handles move. +# handles : [:class:`vtkActor`, :class:`vtkActor`] +# The moving slider disks. +# text : [:class:`TextBlock2D`, :class:`TextBlock2D`] +# The texts that show the values of the disks. +# shape : string +# Describes the shape of the handle. +# Currently supports 'disk' and 'square'. +# default_color : (float, float, float) +# Color of the handles when in unpressed state. +# active_color : (float, float, float) +# Color of the handles when they are pressed. + +# """ + +# @warn_on_args_to_kwargs() +# def __init__( +# self, +# *, +# line_width=5, +# inner_radius=0, +# outer_radius=10, +# handle_side=20, +# center=(450, 300), +# length=200, +# initial_values=(0, 100), +# min_value=0, +# max_value=100, +# font_size=16, +# text_template="{value:.1f}", +# orientation="horizontal", +# shape="disk", +# ): +# """Init this UI element. + +# Parameters +# ---------- +# line_width : int +# Width of the line on which the disk will slide. +# inner_radius : int +# Inner radius of the handles (if disk). +# outer_radius : int +# Outer radius of the handles (if disk). +# handle_side : int +# Side length of the handles (if square). +# center : (float, float) +# Center of the slider. +# length : int +# Length of the slider. +# initial_values : (float, float) +# Initial values of the two handles. +# min_value : float +# Minimum value of the slider. +# max_value : float +# Maximum value of the slider. +# font_size : int +# Size of the text to display alongside the slider (pt). +# text_template : str, callable +# If str, text template can contain one or multiple of the +# replacement fields: `{value:}`, `{ratio:}`. +# If callable, this instance of `:class:LineDoubleSlider2D` will be +# passed as argument to the text template function. +# orientation : str +# horizontal or vertical +# shape : string +# Describes the shape of the handle. +# Currently supports 'disk' and 'square'. + +# """ +# self.shape = shape +# self.default_color = (1, 1, 1) +# self.active_color = (0, 0, 1) +# self.orientation = orientation.lower() +# super(LineDoubleSlider2D, self).__init__() + +# if self.orientation == "horizontal": +# self.track.width = length +# self.track.height = line_width +# elif self.orientation == "vertical": +# self.track.width = line_width +# self.track.height = length +# else: +# raise ValueError("Unknown orientation") + +# self.center = center +# if shape == "disk": +# self.handles[0].inner_radius = inner_radius +# self.handles[0].outer_radius = outer_radius +# self.handles[1].inner_radius = inner_radius +# self.handles[1].outer_radius = outer_radius +# elif shape == "square": +# self.handles[0].width = handle_side +# self.handles[0].height = handle_side +# self.handles[1].width = handle_side +# self.handles[1].height = handle_side + +# self.min_value = min_value +# self.max_value = max_value +# self.text[0].font_size = font_size +# self.text[1].font_size = font_size +# self.text_template = text_template + +# # Offer some standard hooks to the user. +# self.on_change = lambda ui: None +# self.on_value_changed = lambda ui: None +# self.on_moving_slider = lambda ui: None + +# # Setting the handle positions will also update everything. +# self._values = [initial_values[0], initial_values[1]] +# self._ratio = [None, None] +# self.left_disk_value = initial_values[0] +# self.right_disk_value = initial_values[1] +# self.bottom_disk_value = initial_values[0] +# self.top_disk_value = initial_values[1] + +# def _setup(self): +# """Setup this UI component. + +# Create the slider's track (Rectangle2D), the handles (Disk2D) and +# the text (TextBlock2D). + +# """ +# # Slider's track +# self.track = Rectangle2D() +# self.track.color = (1, 0, 0) + +# # Handles +# self.handles = [] +# if self.shape == "disk": +# self.handles.append(Disk2D(outer_radius=1)) +# self.handles.append(Disk2D(outer_radius=1)) +# elif self.shape == "square": +# self.handles.append(Rectangle2D(size=(1, 1))) +# self.handles.append(Rectangle2D(size=(1, 1))) +# self.handles[0].color = self.default_color +# self.handles[1].color = self.default_color + +# # Slider Text +# self.text = [ +# TextBlock2D(justification="center", vertical_justification="top"), +# TextBlock2D(justification="center", vertical_justification="top"), +# ] + +# # Add default events listener for this UI component. +# self.track.on_left_mouse_button_dragged = self.handle_move_callback +# self.handles[0].on_left_mouse_button_dragged = self.handle_move_callback +# self.handles[1].on_left_mouse_button_dragged = self.handle_move_callback +# self.handles[0].on_left_mouse_button_released = self.handle_release_callback +# self.handles[1].on_left_mouse_button_released = self.handle_release_callback + +# def _get_actors(self): +# """Get the actors composing this UI component.""" +# return ( +# self.track.actors +# + self.handles[0].actors +# + self.handles[1].actors +# + self.text[0].actors +# + self.text[1].actors +# ) + +# def _add_to_scene(self, scene): +# """Add all subcomponents or VTK props that compose this UI component. + +# Parameters +# ---------- +# scene : scene + +# """ +# self.track.add_to_scene(scene) +# self.handles[0].add_to_scene(scene) +# self.handles[1].add_to_scene(scene) +# self.text[0].add_to_scene(scene) +# self.text[1].add_to_scene(scene) + +# def _get_size(self): +# # Consider the handle's size when computing the slider's size. +# width = None +# height = None +# if self.orientation == "horizontal": +# width = self.track.width + self.handles[0].size[0] +# height = max(self.track.height, self.handles[0].size[1]) +# else: +# width = max(self.track.width, self.handles[0].size[0]) +# height = self.track.height + self.handles[0].size[1] + +# return np.array([width, height]) + +# def _set_position(self, coords): +# """Set the lower-left corner position of this UI component. + +# Parameters +# ---------- +# coords: (float, float) +# Absolute pixel coordinates (x, y). + +# """ +# # Offset the slider line by the handle's radius. +# track_position = coords +# if self.orientation == "horizontal": +# # Offset the slider line height by half the slider line width. +# track_position[1] -= self.track.size[1] / 2.0 +# else: +# # Offset the slider line width by half the slider line height. +# track_position[0] -= self.track.size[0] / 2.0 + +# self.track.position = track_position + +# self.handles[0].position = self.handles[0].position.astype(float) +# self.handles[1].position = self.handles[1].position.astype(float) + +# self.handles[0].position += coords - self.position +# self.handles[1].position += coords - self.position + +# if self.orientation == "horizontal": +# # Position the text below the handles. +# self.text[0].position = ( +# self.handles[0].center[0], +# self.handles[0].position[1] - 10, +# ) +# self.text[1].position = ( +# self.handles[1].center[0], +# self.handles[1].position[1] - 10, +# ) +# else: +# # Position the text to the left of the handles. +# self.text[0].position = ( +# self.handles[0].center[0] - 35, +# self.handles[0].position[1], +# ) +# self.text[1].position = ( +# self.handles[1].center[0] - 35, +# self.handles[1].position[1], +# ) + +# @property +# def bottom_y_position(self): +# return self.track.position[1] + +# @property +# def top_y_position(self): +# return self.track.position[1] + self.track.size[1] + +# @property +# def left_x_position(self): +# return self.track.position[0] + +# @property +# def right_x_position(self): +# return self.track.position[0] + self.track.size[0] + +# def value_to_ratio(self, value): +# """Convert the value of a disk to the ratio. + +# Parameters +# ---------- +# value : float + +# """ +# value_range = self.max_value - self.min_value +# return (value - self.min_value) / value_range if value_range else 0 + +# def ratio_to_coord(self, ratio): +# """Convert the ratio to the absolute coordinate. + +# Parameters +# ---------- +# ratio : float + +# """ +# if self.orientation == "horizontal": +# return self.left_x_position + ratio * self.track.width +# return self.bottom_y_position + ratio * self.track.height + +# def coord_to_ratio(self, coord): +# """Convert the x coordinate of a disk to the ratio. + +# Parameters +# ---------- +# coord : float + +# """ +# if self.orientation == "horizontal": +# return (coord - self.left_x_position) / float(self.track.width) +# return (coord - self.bottom_y_position) / float(self.track.height) + +# def ratio_to_value(self, ratio): +# """Convert the ratio to the value of the disk. + +# Parameters +# ---------- +# ratio : float + +# """ +# value_range = self.max_value - self.min_value +# return self.min_value + ratio * value_range + +# def set_position(self, position, disk_number): +# """Set the disk's position. + +# Parameters +# ---------- +# position : (float, float) +# The absolute position of the disk (x, y). +# disk_number : int +# The index of disk being moved. + +# """ +# if self.orientation == "horizontal": +# x_position = position[0] + +# if disk_number == 0 and x_position >= self.handles[1].center[0]: +# x_position = self.ratio_to_coord( +# self.value_to_ratio(self._values[1] - 1) +# ) + +# if disk_number == 1 and x_position <= self.handles[0].center[0]: +# x_position = self.ratio_to_coord( +# self.value_to_ratio(self._values[0] + 1) +# ) + +# x_position = max(x_position, self.left_x_position) +# x_position = min(x_position, self.right_x_position) + +# self.handles[disk_number].center = (x_position, self.track.center[1]) +# else: +# y_position = position[1] + +# if disk_number == 0 and y_position >= self.handles[1].center[1]: +# y_position = self.ratio_to_coord( +# self.value_to_ratio(self._values[1] - 1) +# ) + +# if disk_number == 1 and y_position <= self.handles[0].center[1]: +# y_position = self.ratio_to_coord( +# self.value_to_ratio(self._values[0] + 1) +# ) + +# y_position = max(y_position, self.bottom_y_position) +# y_position = min(y_position, self.top_y_position) + +# self.handles[disk_number].center = (self.track.center[0], y_position) +# self.update(disk_number) + +# @property +# def bottom_disk_value(self): +# """Return the value of the bottom disk.""" +# return self._values[0] + +# @bottom_disk_value.setter +# def bottom_disk_value(self, bottom_disk_value): +# """Set the value of the bottom disk. + +# Parameters +# ---------- +# bottom_disk_value : float +# New value for the bottom disk. + +# """ +# self.bottom_disk_ratio = self.value_to_ratio(bottom_disk_value) + +# @property +# def top_disk_value(self): +# """Return the value of the top disk.""" +# return self._values[1] + +# @top_disk_value.setter +# def top_disk_value(self, top_disk_value): +# """Set the value of the top disk. + +# Parameters +# ---------- +# top_disk_value : float +# New value for the top disk. + +# """ +# self.top_disk_ratio = self.value_to_ratio(top_disk_value) + +# @property +# def left_disk_value(self): +# """Return the value of the left disk.""" +# return self._values[0] + +# @left_disk_value.setter +# def left_disk_value(self, left_disk_value): +# """Set the value of the left disk. + +# Parameters +# ---------- +# left_disk_value : float +# New value for the left disk. + +# """ +# self.left_disk_ratio = self.value_to_ratio(left_disk_value) +# self.on_value_changed(self) + +# @property +# def right_disk_value(self): +# """Return the value of the right disk.""" +# return self._values[1] + +# @right_disk_value.setter +# def right_disk_value(self, right_disk_value): +# """Set the value of the right disk. + +# Parameters +# ---------- +# right_disk_value : float +# New value for the right disk. + +# """ +# self.right_disk_ratio = self.value_to_ratio(right_disk_value) +# self.on_value_changed(self) + +# @property +# def bottom_disk_ratio(self): +# """Return the ratio of the bottom disk.""" +# return self._ratio[0] + +# @bottom_disk_ratio.setter +# def bottom_disk_ratio(self, bottom_disk_ratio): +# """Set the ratio of the bottom disk. + +# Parameters +# ---------- +# bottom_disk_ratio : float +# New ratio for the bottom disk. + +# """ +# position_x = self.ratio_to_coord(bottom_disk_ratio) +# position_y = self.ratio_to_coord(bottom_disk_ratio) +# self.set_position((position_x, position_y), 0) + +# @property +# def top_disk_ratio(self): +# """Return the ratio of the top disk.""" +# return self._ratio[1] + +# @top_disk_ratio.setter +# def top_disk_ratio(self, top_disk_ratio): +# """Set the ratio of the top disk. + +# Parameters +# ---------- +# top_disk_ratio : float +# New ratio for the top disk. + +# """ +# position_x = self.ratio_to_coord(top_disk_ratio) +# position_y = self.ratio_to_coord(top_disk_ratio) +# self.set_position((position_x, position_y), 1) + +# @property +# def left_disk_ratio(self): +# """Return the ratio of the left disk.""" +# return self._ratio[0] + +# @left_disk_ratio.setter +# def left_disk_ratio(self, left_disk_ratio): +# """Set the ratio of the left disk. + +# Parameters +# ---------- +# left_disk_ratio : float +# New ratio for the left disk. + +# """ +# position_x = self.ratio_to_coord(left_disk_ratio) +# position_y = self.ratio_to_coord(left_disk_ratio) +# self.set_position((position_x, position_y), 0) + +# @property +# def right_disk_ratio(self): +# """Return the ratio of the right disk.""" +# return self._ratio[1] + +# @right_disk_ratio.setter +# def right_disk_ratio(self, right_disk_ratio): +# """Set the ratio of the right disk. + +# Parameters +# ---------- +# right_disk_ratio : float +# New ratio for the right disk. + +# """ +# position_x = self.ratio_to_coord(right_disk_ratio) +# position_y = self.ratio_to_coord(right_disk_ratio) +# self.set_position((position_x, position_y), 1) + +# def format_text(self, disk_number): +# """Return formatted text to display along the slider. + +# Parameters +# ---------- +# disk_number : int +# Index of the disk. + +# """ +# if callable(self.text_template): +# return self.text_template(self) + +# return self.text_template.format(value=self._values[disk_number]) + +# def update(self, disk_number): +# """Update the slider. + +# Parameters +# ---------- +# disk_number : int +# Index of the disk to be updated. + +# """ +# # Compute the ratio determined by the position of the slider disk. +# if self.orientation == "horizontal": +# self._ratio[disk_number] = self.coord_to_ratio( +# self.handles[disk_number].center[0] +# ) +# else: +# self._ratio[disk_number] = self.coord_to_ratio( +# self.handles[disk_number].center[1] +# ) + +# # Compute the selected value considering min_value and max_value. +# self._values[disk_number] = self.ratio_to_value(self._ratio[disk_number]) + +# # Update text. +# text = self.format_text(disk_number) +# self.text[disk_number].message = text + +# if self.orientation == "horizontal": +# self.text[disk_number].position = ( +# self.handles[disk_number].center[0], +# self.text[disk_number].position[1], +# ) +# else: +# self.text[disk_number].position = ( +# self.text[disk_number].position[0], +# self.handles[disk_number].center[1], +# ) +# self.on_change(self) + +# def handle_move_callback(self, i_ren, vtkactor, _slider): +# """Handle movement. + +# Parameters +# ---------- +# i_ren : :class:`CustomInteractorStyle` +# vtkactor : :class:`vtkActor` +# The picked actor +# _slider : :class:`LineDoubleSlider2D` + +# """ +# position = i_ren.event.position +# if vtkactor == self.handles[0].actors[0]: +# self.set_position(position, 0) +# self.handles[0].color = self.active_color +# elif vtkactor == self.handles[1].actors[0]: +# self.set_position(position, 1) +# self.handles[1].color = self.active_color +# self.on_moving_slider(self) +# i_ren.force_render() +# i_ren.event.abort() # Stop propagating the event. + +# def handle_release_callback(self, i_ren, vtkactor, _slider): +# """Change color when handle is released. + +# Parameters +# ---------- +# i_ren : :class:`CustomInteractorStyle` +# vtkactor : :class:`vtkActor` +# The picked actor +# _slider : :class:`LineDoubleSlider2D` + +# """ +# if vtkactor == self.handles[0].actors[0]: +# self.handles[0].color = self.default_color +# elif vtkactor == self.handles[1].actors[0]: +# self.handles[1].color = self.default_color +# i_ren.force_render() + + +# class RingSlider2D(UI): +# """A disk slider. + +# A disk moves along the boundary of a ring. +# Goes from 0-360 degrees. + +# Attributes +# ---------- +# mid_track_radius: float +# Distance from the center of the slider to the middle of the track. +# previous_value: float +# Value of Rotation of the actor before the current value. +# track : :class:`Disk2D` +# The circle on which the slider's handle moves. +# handle : :class:`Disk2D` +# The moving part of the slider. +# text : :class:`TextBlock2D` +# The text that shows percentage. +# default_color : (float, float, float) +# Color of the handle when in unpressed state. +# active_color : (float, float, float) +# Color of the handle when it is pressed. + +# """ + +# @warn_on_args_to_kwargs() +# def __init__( +# self, +# *, +# center=(0, 0), +# initial_value=180, +# min_value=0, +# max_value=360, +# slider_inner_radius=40, +# slider_outer_radius=44, +# handle_inner_radius=0, +# handle_outer_radius=10, +# font_size=16, +# text_template="{ratio:.0%}", +# ): +# """Init this UI element. + +# Parameters +# ---------- +# center : (float, float) +# Position (x, y) of the slider's center. +# initial_value : float +# Initial value of the slider. +# min_value : float +# Minimum value of the slider. +# max_value : float +# Maximum value of the slider. +# slider_inner_radius : int +# Inner radius of the base disk. +# slider_outer_radius : int +# Outer radius of the base disk. +# handle_outer_radius : int +# Outer radius of the slider's handle. +# handle_inner_radius : int +# Inner radius of the slider's handle. +# font_size : int +# Size of the text to display alongside the slider (pt). +# text_template : str, callable +# If str, text template can contain one or multiple of the +# replacement fields: `{value:}`, `{ratio:}`, `{angle:}`. +# If callable, this instance of `:class:RingSlider2D` will be +# passed as argument to the text template function. + +# """ +# self.default_color = (1, 1, 1) +# self.active_color = (0, 0, 1) +# super(RingSlider2D, self).__init__() + +# self.track.inner_radius = slider_inner_radius +# self.track.outer_radius = slider_outer_radius +# self.handle.inner_radius = handle_inner_radius +# self.handle.outer_radius = handle_outer_radius +# self.center = center + +# self.min_value = min_value +# self.max_value = max_value +# self.text.font_size = font_size +# self.text_template = text_template + +# # Offer some standard hooks to the user. +# self.on_change = lambda ui: None +# self.on_value_changed = lambda ui: None +# self.on_moving_slider = lambda ui: None + +# self._value = initial_value +# self.value = initial_value +# self._previous_value = initial_value +# self._angle = 0 +# self._ratio = self.angle / TWO_PI + +# def _setup(self): +# """Setup this UI component. + +# Create the slider's circle (Disk2D), the handle (Disk2D) and +# the text (TextBlock2D). + +# """ +# # Slider's track. +# self.track = Disk2D(outer_radius=1) +# self.track.color = (1, 0, 0) + +# # Slider's handle. +# self.handle = Disk2D(outer_radius=1) +# self.handle.color = self.default_color + +# # Slider Text +# self.text = TextBlock2D( +# justification="center", vertical_justification="middle") + +# # Add default events listener for this UI component. +# self.track.on_left_mouse_button_pressed = self.track_click_callback +# self.track.on_left_mouse_button_dragged = self.handle_move_callback +# self.track.on_left_mouse_button_released = self.handle_release_callback +# self.handle.on_left_mouse_button_dragged = self.handle_move_callback +# self.handle.on_left_mouse_button_released = self.handle_release_callback + +# def _get_actors(self): +# """Get the actors composing this UI component.""" +# return self.track.actors + self.handle.actors + self.text.actors + +# def _add_to_scene(self, scene): +# """Add all subcomponents or VTK props that compose this UI component. + +# Parameters +# ---------- +# scene : scene + +# """ +# self.track.add_to_scene(scene) +# self.handle.add_to_scene(scene) +# self.text.add_to_scene(scene) + +# def _get_size(self): +# return self.track.size + self.handle.size + +# def _set_position(self, coords): +# """Set the lower-left corner position of this UI component. + +# Parameters +# ---------- +# coords: (float, float) +# Absolute pixel coordinates (x, y). + +# """ +# self.track.position = coords + self.handle.size / 2.0 +# self.handle.position += coords - self.position +# # Position the text in the center of the slider's track. +# self.text.position = coords + self.size / 2.0 + +# @property +# def mid_track_radius(self): +# return (self.track.inner_radius + self.track.outer_radius) / 2.0 + +# @property +# def value(self): +# return self._value + +# @value.setter +# def value(self, value): +# value_range = self.max_value - self.min_value +# self.ratio = (value - self.min_value) / value_range if value_range else 0 +# self.on_value_changed(self) + +# @property +# def previous_value(self): +# return self._previous_value + +# @property +# def ratio(self): +# return self._ratio + +# @ratio.setter +# def ratio(self, ratio): +# self.angle = ratio * TWO_PI + +# @property +# def angle(self): +# """Return Angle (in rad) the handle makes with x-axis.""" +# return self._angle + +# @angle.setter +# def angle(self, angle): +# self._angle = angle % TWO_PI # Wraparound +# self.update() + +# def format_text(self): +# """Return formatted text to display along the slider.""" +# if callable(self.text_template): +# return self.text_template(self) + +# return self.text_template.format( +# ratio=self.ratio, value=self.value, angle=np.rad2deg(self.angle) +# ) + +# def update(self): +# """Update the slider.""" +# # Compute the ratio determined by the position of the slider disk. +# self._ratio = self.angle / TWO_PI + +# # Compute the selected value considering min_value and max_value. +# value_range = self.max_value - self.min_value +# self._previous_value = self.value +# self._value = self.min_value + self.ratio * value_range + +# # Update text disk actor. +# x = self.mid_track_radius * np.cos(self.angle) + self.center[0] +# y = self.mid_track_radius * np.sin(self.angle) + self.center[1] +# self.handle.center = (x, y) + +# # Update text. +# text = self.format_text() +# self.text.message = text + +# self.on_change(self) # Call hook. + +# def move_handle(self, click_position): +# """Move the slider's handle. + +# Parameters +# ---------- +# click_position: (float, float) +# Position of the mouse click. + +# """ +# x, y = np.array(click_position) - self.center +# angle = np.arctan2(y, x) +# if angle < 0: +# angle += TWO_PI + +# self.angle = angle + +# def track_click_callback(self, i_ren, _obj, _slider): +# """Update disk position and grab the focus. + +# Parameters +# ---------- +# i_ren : :class:`CustomInteractorStyle` +# obj : :class:`vtkActor` +# The picked actor +# _slider : :class:`RingSlider2D` + +# """ +# click_position = i_ren.event.position +# self.move_handle(click_position=click_position) +# self.on_moving_slider(self) +# i_ren.force_render() +# i_ren.event.abort() # Stop propagating the event. + +# def handle_move_callback(self, i_ren, _obj, _slider): +# """Move the slider's handle. + +# Parameters +# ---------- +# i_ren : :class:`CustomInteractorStyle` +# obj : :class:`vtkActor` +# The picked actor +# _slider : :class:`RingSlider2D` + +# """ +# click_position = i_ren.event.position +# self.handle.color = self.active_color +# self.move_handle(click_position=click_position) +# self.on_moving_slider(self) +# i_ren.force_render() +# i_ren.event.abort() # Stop propagating the event. + +# def handle_release_callback(self, i_ren, _obj, _slider): +# """Change color when handle is released. + +# Parameters +# ---------- +# i_ren : :class:`CustomInteractorStyle` +# vtkactor : :class:`vtkActor` +# The picked actor +# _slider : :class:`RingSlider2D` + +# """ +# self.handle.color = self.default_color +# i_ren.force_render() + + +# class RangeSlider(UI): +# """A set of a LineSlider2D and a LineDoubleSlider2D. +# The double slider is used to set the min and max value +# for the LineSlider2D + +# Attributes +# ---------- +# range_slider_center : (float, float) +# Center of the LineDoubleSlider2D object. +# value_slider_center : (float, float) +# Center of the LineSlider2D object. +# range_slider : :class:`LineDoubleSlider2D` +# The line slider which sets the min and max values +# value_slider : :class:`LineSlider2D` +# The line slider which sets the value + +# """ + +# @warn_on_args_to_kwargs() +# def __init__( +# self, +# *, +# line_width=5, +# inner_radius=0, +# outer_radius=10, +# handle_side=20, +# range_slider_center=(450, 400), +# value_slider_center=(450, 300), +# length=200, +# min_value=0, +# max_value=100, +# font_size=16, +# range_precision=1, +# orientation="horizontal", +# value_precision=2, +# shape="disk", +# ): +# """Init this class instance. + +# Parameters +# ---------- +# line_width : int +# Width of the slider tracks +# inner_radius : int +# Inner radius of the handles. +# outer_radius : int +# Outer radius of the handles. +# handle_side : int +# Side length of the handles (if square). +# range_slider_center : (float, float) +# Center of the LineDoubleSlider2D object. +# value_slider_center : (float, float) +# Center of the LineSlider2D object. +# length : int +# Length of the sliders. +# min_value : float +# Minimum value of the double slider. +# max_value : float +# Maximum value of the double slider. +# font_size : int +# Size of the text to display alongside the sliders (pt). +# range_precision : int +# Number of decimal places to show the min and max values set. +# orientation : str +# horizontal or vertical +# value_precision : int +# Number of decimal places to show the value set on slider. +# shape : string +# Describes the shape of the handle. +# Currently supports 'disk' and 'square'. + +# """ +# self.min_value = min_value +# self.max_value = max_value +# self.inner_radius = inner_radius +# self.outer_radius = outer_radius +# self.handle_side = handle_side +# self.length = length +# self.line_width = line_width +# self.font_size = font_size +# self.shape = shape +# self.orientation = orientation.lower() + +# self.range_slider_text_template = "{value:." + str(range_precision) + "f}" +# self.value_slider_text_template = "{value:." + str(value_precision) + "f}" + +# self.range_slider_center = range_slider_center +# self.value_slider_center = value_slider_center +# super(RangeSlider, self).__init__() + +# def _setup(self): +# """Setup this UI component.""" +# self.range_slider = LineDoubleSlider2D( +# line_width=self.line_width, +# inner_radius=self.inner_radius, +# outer_radius=self.outer_radius, +# handle_side=self.handle_side, +# center=self.range_slider_center, +# length=self.length, +# min_value=self.min_value, +# max_value=self.max_value, +# initial_values=(self.min_value, self.max_value), +# font_size=self.font_size, +# shape=self.shape, +# orientation=self.orientation, +# text_template=self.range_slider_text_template, +# ) + +# self.value_slider = LineSlider2D( +# line_width=self.line_width, +# length=self.length, +# inner_radius=self.inner_radius, +# outer_radius=self.outer_radius, +# handle_side=self.handle_side, +# center=self.value_slider_center, +# min_value=self.min_value, +# max_value=self.max_value, +# initial_value=(self.min_value + self.max_value) / 2, +# font_size=self.font_size, +# shape=self.shape, +# orientation=self.orientation, +# text_template=self.value_slider_text_template, +# ) + +# # Add default events listener for this UI component. +# self.range_slider.handles[ +# 0 +# ].on_left_mouse_button_dragged = self.range_slider_handle_move_callback +# self.range_slider.handles[ +# 1 +# ].on_left_mouse_button_dragged = self.range_slider_handle_move_callback + +# def _get_actors(self): +# """Get the actors composing this UI component.""" +# return self.range_slider.actors + self.value_slider.actors + +# def _add_to_scene(self, scene): +# """Add all subcomponents or VTK props that compose this UI component. + +# Parameters +# ---------- +# scene : scene + +# """ +# self.range_slider.add_to_scene(scene) +# self.value_slider.add_to_scene(scene) + +# def _get_size(self): +# return self.range_slider.size + self.value_slider.size + +# def _set_position(self, coords): +# pass + +# def range_slider_handle_move_callback(self, i_ren, obj, _slider): +# """Update range_slider's handles. + +# Parameters +# ---------- +# i_ren : :class:`CustomInteractorStyle` +# obj : :class:`vtkActor` +# The picked actor +# _slider : :class:`RangeSlider` + +# """ +# position = i_ren.event.position +# if obj == self.range_slider.handles[0].actors[0]: +# self.range_slider.handles[0].color = self.range_slider.active_color +# self.range_slider.set_position(position, 0) +# self.value_slider.min_value = self.range_slider.left_disk_value +# self.value_slider.update() +# elif obj == self.range_slider.handles[1].actors[0]: +# self.range_slider.handles[1].color = self.range_slider.active_color +# self.range_slider.set_position(position, 1) +# self.value_slider.max_value = self.range_slider.right_disk_value +# self.value_slider.update() +# i_ren.force_render() +# i_ren.event.abort() # Stop propagating the event. + + +# class Option(UI): +# """A set of a Button2D and a TextBlock2D to act as a single option +# for checkboxes and radio buttons. +# Clicking the button toggles its checked/unchecked status. + +# Attributes +# ---------- +# label : str +# The label for the option. +# font_size : int +# Font Size of the label. + +# """ + +# @warn_on_args_to_kwargs() +# def __init__(self, label, *, position=(0, 0), font_size=18, checked=False): +# """Init this class instance. + +# Parameters +# ---------- +# label : str +# Text to be displayed next to the option's button. +# position : (float, float) +# Absolute coordinates (x, y) of the lower-left corner of +# the button of the option. +# font_size : int +# Font size of the label. +# checked : bool, optional +# Boolean value indicates the initial state of the option + +# """ +# self.label = label +# self.font_size = font_size +# self.checked = checked +# self.button_size = (font_size * 1.2, font_size * 1.2) +# self.button_label_gap = 10 +# super(Option, self).__init__(position=position) + +# # Offer some standard hooks to the user. +# self.on_change = lambda obj: None + +# def _setup(self): +# """Setup this UI component.""" +# # Option's button +# self.button_icons = [] +# self.button_icons.append(("unchecked", read_viz_icons(fname="stop2.png"))) +# self.button_icons.append(("checked", read_viz_icons(fname="checkmark.png"))) +# self.button = Button2D(icon_fnames=self.button_icons, size=self.button_size) + +# self.text = TextBlock2D(text=self.label, font_size=self.font_size) + +# # Display initial state +# if self.checked: +# self.button.set_icon_by_name("checked") + +# # Add callbacks +# self.button.on_left_mouse_button_clicked = self.toggle +# self.text.on_left_mouse_button_clicked = self.toggle + +# def _get_actors(self): +# """Get the actors composing this UI component.""" +# return self.button.actors + self.text.actors + +# def _add_to_scene(self, scene): +# """Add all subcomponents or VTK props that compose this UI component. + +# Parameters +# ---------- +# scene : scene + +# """ +# self.button.add_to_scene(scene) +# self.text.add_to_scene(scene) + +# def _get_size(self): +# width = self.button.size[0] + self.button_label_gap + self.text.size[0] +# height = max(self.button.size[1], self.text.size[1]) +# return np.array([width, height]) + +# def _set_position(self, coords): +# """Set the lower-left corner position of this UI component. + +# Parameters +# ---------- +# coords: (float, float) +# Absolute pixel coordinates (x, y). + +# """ +# num_newlines = self.label.count("\n") +# self.button.position = coords + (0, num_newlines * self.font_size * 0.5) +# offset = (self.button.size[0] + self.button_label_gap, 0) +# self.text.position = coords + offset + +# def toggle(self, i_ren, _obj, _element): +# if self.checked: +# self.deselect() +# else: +# self.select() + +# self.on_change(self) +# i_ren.force_render() + +# def select(self): +# self.checked = True +# self.button.set_icon_by_name("checked") + +# def deselect(self): +# self.checked = False +# self.button.set_icon_by_name("unchecked") + + +# class Checkbox(UI): +# """A 2D set of :class:'Option' objects. +# Multiple options can be selected. + +# Attributes +# ---------- +# labels : list(string) +# List of labels of each option. +# options : dict(Option) +# Dictionary of all the options in the checkbox set. +# padding : float +# Distance between two adjacent options + +# """ + +# @warn_on_args_to_kwargs() +# def __init__( +# self, +# labels, +# *, +# checked_labels=(), +# padding=1, +# font_size=18, +# font_family="Arial", +# position=(0, 0), +# ): +# """Init this class instance. + +# Parameters +# ---------- +# labels : list(str) +# List of labels of each option. +# checked_labels: list(str), optional +# List of labels that are checked on setting up. +# padding : float, optional +# The distance between two adjacent options +# font_size : int, optional +# Size of the text font. +# font_family : str, optional +# Currently only supports Arial. +# position : (float, float), optional +# Absolute coordinates (x, y) of the lower-left corner of +# the button of the first option. + +# """ +# self.labels = list(reversed(list(labels))) +# self._padding = padding +# self._font_size = font_size +# self.font_family = font_family +# self.checked_labels = list(checked_labels) +# super(Checkbox, self).__init__(position=position) +# self.on_change = lambda checkbox: None + +# def _setup(self): +# """Setup this UI component.""" +# self.options = OrderedDict() +# button_y = self.position[1] +# for label in self.labels: +# option = Option( +# label=label, +# font_size=self.font_size, +# position=(self.position[0], button_y), +# checked=(label in self.checked_labels), +# ) + +# line_spacing = option.text.actor.GetTextProperty().GetLineSpacing() +# button_y = ( +# button_y +# + self.font_size * (label.count("\n") + 1) * (line_spacing + 0.1) +# + self.padding +# ) +# self.options[label] = option + +# # Set callback +# option.on_change = self._handle_option_change + +# def _get_actors(self): +# """Get the actors composing this UI component.""" +# actors = [] +# for option in self.options.values(): +# actors = actors + option.actors +# return actors + +# def _add_to_scene(self, scene): +# """Add all subcomponents or VTK props that compose this UI component. + +# Parameters +# ---------- +# scene : scene + +# """ +# for option in self.options.values(): +# option.add_to_scene(scene) + +# def _get_size(self): +# option_width, option_height = self.options.values()[0].get_size() +# height = len(self.labels) * (option_height + self.padding) - self.padding +# return np.asarray([option_width, height]) + +# def _handle_option_change(self, option): +# """Update whenever an option changes. + +# Parameters +# ---------- +# option : :class:`Option` + +# """ +# if option.checked: +# self.checked_labels.append(option.label) +# else: +# self.checked_labels.remove(option.label) + +# self.on_change(self) + +# def _set_position(self, coords): +# """Set the lower-left corner position of this UI component. + +# Parameters +# ---------- +# coords: (float, float) +# Absolute pixel coordinates (x, y). + +# """ +# button_y = coords[1] +# for option_no, option in enumerate(self.options.values()): +# option.position = (coords[0], button_y) +# line_spacing = option.text.actor.GetTextProperty().GetLineSpacing() +# button_y = ( +# button_y +# + self.font_size +# * (self.labels[option_no].count("\n") + 1) +# * (line_spacing + 0.1) +# + self.padding +# ) + +# @property +# def font_size(self): +# """Gets the font size of text.""" +# return self._font_size + +# @property +# def padding(self): +# """Get the padding between options.""" +# return self._padding + + +# class RadioButton(Checkbox): +# """A 2D set of :class:'Option' objects. +# Only one option can be selected. + +# Attributes +# ---------- +# labels : list(string) +# List of labels of each option. +# options : dict(Option) +# Dictionary of all the options in the checkbox set. +# padding : float +# Distance between two adjacent options + +# """ + +# @warn_on_args_to_kwargs() +# def __init__( +# self, +# labels, +# checked_labels, +# *, +# padding=1, +# font_size=18, +# font_family="Arial", +# position=(0, 0), +# ): +# """Init class instance. + +# Parameters +# ---------- +# labels : list(str) +# List of labels of each option. +# checked_labels: list(str), optional +# List of labels that are checked on setting up. +# padding : float, optional +# The distance between two adjacent options +# font_size : int, optional +# Size of the text font. +# font_family : str, optional +# Currently only supports Arial. +# position : (float, float), optional +# Absolute coordinates (x, y) of the lower-left corner of +# the button of the first option. + +# """ +# if len(checked_labels) > 1: +# err_msg = "Only one option can be pre-selected for radio buttons." +# raise ValueError(err_msg) + +# super(RadioButton, self).__init__( +# labels=labels, +# position=position, +# padding=padding, +# font_size=font_size, +# font_family=font_family, +# checked_labels=checked_labels, +# ) + +# def _handle_option_change(self, option): +# for option_ in self.options.values(): +# option_.deselect() + +# option.select() +# self.checked_labels = [option.label] +# self.on_change(self) + + +# class ComboBox2D(UI): +# """UI element to create drop-down menus. + +# Attributes +# ---------- +# selection_box: :class: 'TextBox2D' +# Display selection and placeholder text. +# drop_down_button: :class: 'Button2D' +# Button to show or hide menu. +# drop_down_menu: :class: 'ListBox2D' +# Container for item list. + +# """ + +# @warn_on_args_to_kwargs() +# def __init__( +# self, +# *, +# items=None, +# position=(0, 0), +# size=(300, 200), +# placeholder="Choose selection...", +# draggable=True, +# selection_text_color=(0, 0, 0), +# selection_bg_color=(1, 1, 1), +# menu_text_color=(0.2, 0.2, 0.2), +# selected_color=(0.9, 0.6, 0.6), +# unselected_color=(0.6, 0.6, 0.6), +# scroll_bar_active_color=(0.6, 0.2, 0.2), +# scroll_bar_inactive_color=(0.9, 0.0, 0.0), +# menu_opacity=1.0, +# reverse_scrolling=False, +# font_size=20, +# line_spacing=1.4, +# ): +# """Init class Instance. + +# Parameters +# ---------- +# items: list(string) +# List of items to be displayed as choices. +# position : (float, float) +# Absolute coordinates (x, y) of the lower-left corner of this +# UI component. +# size : (int, int) +# Width and height in pixels of this UI component. +# placeholder : str +# Holds the default text to be displayed. +# draggable: {True, False} +# Whether the UI element is draggable or not. +# selection_text_color : tuple of 3 floats +# Color of the selected text to be displayed. +# selection_bg_color : tuple of 3 floats +# Background color of the selection text. +# menu_text_color : tuple of 3 floats. +# Color of the options displayed in drop down menu. +# selected_color : tuple of 3 floats. +# Background color of the selected option in drop down menu. +# unselected_color : tuple of 3 floats. +# Background color of the unselected option in drop down menu. +# scroll_bar_active_color : tuple of 3 floats. +# Color of the scrollbar when in active use. +# scroll_bar_inactive_color : tuple of 3 floats. +# Color of the scrollbar when inactive. +# reverse_scrolling: {True, False} +# If True, scrolling up will move the list of files down. +# font_size: int +# The font size of selected text in pixels. +# line_spacing: float +# Distance between drop down menu's items in pixels. + +# """ +# if items is None: +# items = [] + +# self.items = items.copy() +# self.font_size = font_size +# self.reverse_scrolling = reverse_scrolling +# self.line_spacing = line_spacing +# self.panel_size = size +# self._selection = placeholder +# self._menu_visibility = False +# self._selection_ID = None +# self.draggable = draggable +# self.sel_text_color = selection_text_color +# self.sel_bg_color = selection_bg_color +# self.menu_txt_color = menu_text_color +# self.selected_color = selected_color +# self.unselected_color = unselected_color +# self.scroll_active_color = scroll_bar_active_color +# self.scroll_inactive_color = scroll_bar_inactive_color +# self.menu_opacity = menu_opacity + +# # Define subcomponent sizes. +# self.text_block_size = (int(0.9 * size[0]), int(0.1 * size[1])) +# self.drop_menu_size = (int(0.9 * size[0]), int(0.7 * size[1])) +# self.drop_button_size = (int(0.1 * size[0]), int(0.1 * size[1])) + +# self._icon_files = [ +# ("left", read_viz_icons(fname="circle-left.png")), +# ("down", read_viz_icons(fname="circle-down.png")), +# ] + +# super(ComboBox2D, self).__init__() +# self.position = position + +# def _setup(self): +# """Setup this UI component. + +# Create the ListBox filled with empty slots (ListBoxItem2D). +# Create TextBox with placeholder text. +# Create Button for toggling drop down menu. +# """ +# self.selection_box = TextBlock2D( +# size=self.text_block_size, +# color=self.sel_text_color, +# bg_color=self.sel_bg_color, +# text=self._selection, +# ) + +# self.drop_down_button = Button2D( +# icon_fnames=self._icon_files, size=self.drop_button_size +# ) + +# self.drop_down_menu = ListBox2D( +# values=self.items, +# multiselection=False, +# font_size=self.font_size, +# line_spacing=self.line_spacing, +# text_color=self.menu_txt_color, +# selected_color=self.selected_color, +# unselected_color=self.unselected_color, +# scroll_bar_active_color=self.scroll_active_color, +# scroll_bar_inactive_color=self.scroll_inactive_color, +# background_opacity=self.menu_opacity, +# reverse_scrolling=self.reverse_scrolling, +# size=self.drop_menu_size, +# ) + +# self.drop_down_menu.set_visibility(False) + +# self.panel = Panel2D(self.panel_size, opacity=0.0) +# self.panel.add_element(self.selection_box, (0.001, 0.7)) +# self.panel.add_element(self.drop_down_button, (0.8, 0.7)) +# self.panel.add_element(self.drop_down_menu, (0, 0)) + +# if self.draggable: +# self.drop_down_button.on_left_mouse_button_dragged = ( +# self.left_button_dragged +# ) +# self.drop_down_menu.panel.background.on_left_mouse_button_dragged = ( +# self.left_button_dragged +# ) +# self.selection_box.on_left_mouse_button_dragged = self.left_button_dragged +# self.selection_box.background.on_left_mouse_button_dragged = ( +# self.left_button_dragged +# ) + +# self.drop_down_button.on_left_mouse_button_pressed = ( +# self.left_button_pressed +# ) +# self.drop_down_menu.panel.background.on_left_mouse_button_pressed = ( +# self.left_button_pressed +# ) +# self.selection_box.on_left_mouse_button_pressed = self.left_button_pressed +# self.selection_box.background.on_left_mouse_button_pressed = ( +# self.left_button_pressed +# ) +# else: +# self.panel.background.on_left_mouse_button_dragged = ( +# lambda i_ren, _obj, _comp: i_ren.force_render +# ) +# self.drop_down_menu.panel.background.on_left_mouse_button_dragged = ( +# lambda i_ren, _obj, _comp: i_ren.force_render +# ) + +# # Handle mouse wheel events on the slots. +# for slot in self.drop_down_menu.slots: +# slot.add_callback( +# slot.textblock.actor, +# "LeftButtonPressEvent", +# self.select_option_callback, +# ) + +# slot.add_callback( +# slot.background.actor, +# "LeftButtonPressEvent", +# self.select_option_callback, +# ) + +# self.drop_down_button.on_left_mouse_button_clicked = self.menu_toggle_callback + +# # Offer some standard hooks to the user. +# self.on_change = lambda ui: None + +# def _get_actors(self): +# """Get the actors composing this UI component.""" +# return self.panel.actors + +# def resize(self, size): +# """Resize ComboBox2D. + +# Parameters +# ---------- +# size : (int, int) +# ComboBox size(width, height) in pixels. + +# """ +# self.panel.resize(size) + +# self.text_block_size = (int(0.9 * size[0]), int(0.1 * size[1])) +# self.drop_menu_size = (int(0.9 * size[0]), int(0.7 * size[1])) +# self.drop_button_size = (int(0.1 * size[0]), int(0.1 * size[1])) + +# self.panel.update_element(self.selection_box, (0.001, 0.7)) +# self.panel.update_element(self.drop_down_button, (0.8, 0.7)) +# self.panel.update_element(self.drop_down_menu, (0, 0)) + +# self.drop_down_button.resize(self.drop_button_size) +# self.drop_down_menu.resize(self.drop_menu_size) +# self.selection_box.resize(self.text_block_size) + +# def _set_position(self, coords): +# """Set the lower-left corner position of this UI component. + +# Parameters +# ---------- +# coords: (float, float) +# Absolute pixel coordinates (x, y). + +# """ +# self.panel.position = coords +# self.panel.position = ( +# self.panel.position[0], +# self.panel.position[1] - self.drop_menu_size[1], +# ) + +# def _add_to_scene(self, scene): +# """Add all subcomponents or VTK props that compose this UI component. + +# Parameters +# ---------- +# scene : scene + +# """ +# self.panel.add_to_scene(scene) +# self.selection_box.font_size = self.font_size + +# def _get_size(self): +# return self.panel.size + +# @property +# def selected_text(self): +# return self._selection + +# @property +# def selected_text_index(self): +# return self._selection_ID + +# def set_visibility(self, visibility): +# super().set_visibility(visibility) +# if not self._menu_visibility: +# self.drop_down_menu.set_visibility(False) + +# def append_item(self, *items): +# """Append additional options to the menu. + +# Parameters +# ---------- +# items : n-d list, n-d tuple, Number or str +# Additional options. + +# """ +# for item in items: +# if isinstance(item, (list, tuple)): +# # Useful when n-d lists/tuples are used. +# self.append_item(*item) +# elif isinstance(item, (str, Number)): +# self.items.append(str(item)) +# else: +# raise TypeError("Invalid item instance {}".format(type(item))) + +# self.drop_down_menu.update_scrollbar() +# if not self._menu_visibility: +# self.drop_down_menu.scroll_bar.set_visibility(False) + +# def select_option_callback(self, i_ren, _obj, listboxitem): +# """Select the appropriate option + +# Parameters +# ---------- +# i_ren: :class:`CustomInteractorStyle` +# obj: :class:`vtkActor` +# The picked actor +# listboxitem: :class:`ListBoxItem2D` + +# """ +# # Set the Text of TextBlock2D to the text of listboxitem +# self._selection = listboxitem.element +# self._selection_ID = self.items.index(self._selection) + +# self.selection_box.message = self._selection +# clip_overflow(self.selection_box, self.selection_box.background.size[0]) +# self.drop_down_menu.set_visibility(False) +# self._menu_visibility = False + +# self.drop_down_button.next_icon() + +# self.on_change(self) + +# i_ren.force_render() +# i_ren.event.abort() + +# def menu_toggle_callback(self, i_ren, _vtkactor, _combobox): +# """Toggle visibility of drop down menu list. + +# Parameters +# ---------- +# i_ren : :class:`CustomInteractorStyle` +# vtkactor : :class:`vtkActor` +# The picked actor +# combobox : :class:`ComboBox2D` + +# """ +# self._menu_visibility = not self._menu_visibility +# self.drop_down_menu.set_visibility(self._menu_visibility) + +# self.drop_down_button.next_icon() + +# i_ren.force_render() +# i_ren.event.abort() # Stop propagating the event. + +# def left_button_pressed(self, i_ren, _obj, _sub_component): +# click_pos = np.array(i_ren.event.position) +# self._click_position = click_pos +# i_ren.event.abort() # Stop propagating the event. + +# def left_button_dragged(self, i_ren, _obj, _sub_component): +# click_position = np.array(i_ren.event.position) +# change = click_position - self._click_position +# self.panel.position += change +# self._click_position = click_position +# i_ren.force_render() + + +# class ListBox2D(UI): +# """UI component that allows the user to select items from a list. + +# Attributes +# ---------- +# on_change: function +# Callback function for when the selected items have changed. + +# """ + +# @warn_on_args_to_kwargs() +# def __init__( +# self, +# values, +# *, +# position=(0, 0), +# size=(100, 300), +# multiselection=True, +# reverse_scrolling=False, +# font_size=20, +# line_spacing=1.4, +# text_color=(0.2, 0.2, 0.2), +# selected_color=(0.9, 0.6, 0.6), +# unselected_color=(0.6, 0.6, 0.6), +# scroll_bar_active_color=(0.6, 0.2, 0.2), +# scroll_bar_inactive_color=(0.9, 0.0, 0.0), +# background_opacity=1.0, +# ): +# """Init class instance. + +# Parameters +# ---------- +# values: list of objects +# Values used to populate this listbox. Objects must be castable +# to string. +# position : (float, float) +# Absolute coordinates (x, y) of the lower-left corner of this +# UI component. +# size : (int, int) +# Width and height in pixels of this UI component. +# multiselection: {True, False} +# Whether multiple values can be selected at once. +# reverse_scrolling: {True, False} +# If True, scrolling up will move the list of files down. +# font_size: int +# The font size in pixels. +# line_spacing: float +# Distance between listbox's items in pixels. +# text_color : tuple of 3 floats +# selected_color : tuple of 3 floats +# unselected_color : tuple of 3 floats +# scroll_bar_active_color : tuple of 3 floats +# scroll_bar_inactive_color : tuple of 3 floats +# background_opacity : float + +# """ +# self.view_offset = 0 +# self.slots = [] +# self.selected = [] + +# self.panel_size = size +# self.font_size = font_size +# self.line_spacing = line_spacing +# self.slot_height = int(self.font_size * self.line_spacing) + +# self.text_color = text_color +# self.selected_color = selected_color +# self.unselected_color = unselected_color +# self.background_opacity = background_opacity + +# # self.panel.resize(size) +# self.values = values +# self.multiselection = multiselection +# self.last_selection_idx = 0 +# self.reverse_scrolling = reverse_scrolling +# super(ListBox2D, self).__init__() + +# denom = len(self.values) - self.nb_slots +# if not denom: +# denom += 1 +# self.scroll_step_size = ( +# self.slot_height * self.nb_slots - self.scroll_bar.height +# ) / denom + +# self.scroll_bar_active_color = scroll_bar_active_color +# self.scroll_bar_inactive_color = scroll_bar_inactive_color +# self.scroll_bar.color = self.scroll_bar_inactive_color +# self.scroll_bar.opacity = self.background_opacity + +# self.position = position +# self.scroll_init_position = 0 +# self.update() + +# # Offer some standard hooks to the user. +# self.on_change = lambda: None + +# def _setup(self): +# """Setup this UI component. + +# Create the ListBox (Panel2D) filled with empty slots (ListBoxItem2D). +# """ +# self.margin = 10 +# size = self.panel_size +# font_size = self.font_size +# # Calculating the number of slots. +# self.nb_slots = int((size[1] - 2 * self.margin) // self.slot_height) + +# # This panel facilitates adding slots at the right position. +# self.panel = Panel2D(size=size, color=(1, 1, 1)) + +# # Add a scroll bar +# scroll_bar_height = ( +# self.nb_slots * (size[1] - 2 * self.margin) / len(self.values) +# ) +# self.scroll_bar = Rectangle2D(size=(int(size[0] / 20), scroll_bar_height)) +# if len(self.values) <= self.nb_slots: +# self.scroll_bar.set_visibility(False) +# self.scroll_bar.height = 0 +# self.panel.add_element( +# self.scroll_bar, size - self.scroll_bar.size - self.margin +# ) + +# # Initialisation of empty text actors +# self.slot_width = ( +# size[0] - self.scroll_bar.size[0] - 2 * self.margin - self.margin +# ) +# x = self.margin +# y = size[1] - self.margin +# for _ in range(self.nb_slots): +# y -= self.slot_height +# item = ListBoxItem2D( +# list_box=self, +# size=(self.slot_width, self.slot_height), +# text_color=self.text_color, +# selected_color=self.selected_color, +# unselected_color=self.unselected_color, +# background_opacity=self.background_opacity, +# ) +# item.textblock.font_size = font_size +# self.slots.append(item) +# self.panel.add_element(item, (x, y + self.margin)) + +# # Add default events listener for this UI component. +# self.scroll_bar.on_left_mouse_button_pressed = self.scroll_click_callback +# self.scroll_bar.on_left_mouse_button_released = self.scroll_release_callback +# self.scroll_bar.on_left_mouse_button_dragged = self.scroll_drag_callback + +# # Handle mouse wheel events on the panel. +# up_event = "MouseWheelForwardEvent" +# down_event = "MouseWheelBackwardEvent" +# if self.reverse_scrolling: +# up_event, down_event = down_event, up_event # Swap events + +# self.add_callback( +# self.panel.background.actor, up_event, self.up_button_callback +# ) +# self.add_callback( +# self.panel.background.actor, down_event, self.down_button_callback +# ) + +# # Handle mouse wheel events on the slots. +# for slot in self.slots: +# self.add_callback( +# slot.background.actor, up_event, self.up_button_callback +# ) +# self.add_callback( +# slot.background.actor, down_event, self.down_button_callback +# ) +# self.add_callback(slot.textblock.actor, up_event, self.up_button_callback) +# self.add_callback( +# slot.textblock.actor, down_event, self.down_button_callback +# ) + +# def resize(self, size): +# pass + +# def _get_actors(self): +# """Get the actors composing this UI component.""" +# return self.panel.actors + +# def _add_to_scene(self, scene): +# """Add all subcomponents or VTK props that compose this UI component. + +# Parameters +# ---------- +# scene : scene + +# """ +# self.panel.add_to_scene(scene) +# for slot in self.slots: +# clip_overflow(slot.textblock, self.slot_width) + +# def _get_size(self): +# return self.panel.size + +# def _set_position(self, coords): +# """Position the lower-left corner of this UI component. + +# Parameters +# ---------- +# coords: (float, float) +# Absolute pixel coordinates (x, y). + +# """ +# self.panel.position = coords + +# def up_button_callback(self, i_ren, _obj, _list_box): +# """Pressing up button scrolls up in the combo box. + +# Parameters +# ---------- +# i_ren: :class:`CustomInteractorStyle` +# obj: :class:`vtkActor` +# The picked actor +# _list_box: :class:`ListBox2D` + +# """ +# if self.view_offset > 0: +# self.view_offset -= 1 +# self.update() +# scroll_bar_idx = self.panel._elements.index(self.scroll_bar) +# self.scroll_bar.center = ( +# self.scroll_bar.center[0], +# self.scroll_bar.center[1] + self.scroll_step_size, +# ) +# self.panel.element_offsets[scroll_bar_idx] = ( +# self.scroll_bar, +# (self.scroll_bar.position - self.panel.position), +# ) + +# i_ren.force_render() +# i_ren.event.abort() # Stop propagating the event. + +# def down_button_callback(self, i_ren, _obj, _list_box): +# """Pressing down button scrolls down in the combo box. + +# Parameters +# ---------- +# i_ren: :class:`CustomInteractorStyle` +# obj: :class:`vtkActor` +# The picked actor +# _list_box: :class:`ListBox2D` + +# """ +# view_end = self.view_offset + self.nb_slots +# if view_end < len(self.values): +# self.view_offset += 1 +# self.update() +# scroll_bar_idx = self.panel._elements.index(self.scroll_bar) +# self.scroll_bar.center = ( +# self.scroll_bar.center[0], +# self.scroll_bar.center[1] - self.scroll_step_size, +# ) +# self.panel.element_offsets[scroll_bar_idx] = ( +# self.scroll_bar, +# (self.scroll_bar.position - self.panel.position), +# ) + +# i_ren.force_render() +# i_ren.event.abort() # Stop propagating the event. + +# def scroll_click_callback(self, i_ren, _obj, _rect_obj): +# """Callback to change the color of the bar when it is clicked. + +# Parameters +# ---------- +# i_ren: :class:`CustomInteractorStyle` +# obj: :class:`vtkActor` +# The picked actor +# _rect_obj: :class:`Rectangle2D` + +# """ +# self.scroll_bar.color = self.scroll_bar_active_color +# self.scroll_init_position = i_ren.event.position[1] +# i_ren.force_render() +# i_ren.event.abort() + +# def scroll_release_callback(self, i_ren, _obj, _rect_obj): +# """Callback to change the color of the bar when it is released. + +# Parameters +# ---------- +# i_ren: :class:`CustomInteractorStyle` +# obj: :class:`vtkActor` +# The picked actor +# rect_obj: :class:`Rectangle2D` + +# """ +# self.scroll_bar.color = self.scroll_bar_inactive_color +# i_ren.force_render() + +# def scroll_drag_callback(self, i_ren, _obj, _rect_obj): +# """Drag scroll bar in the combo box. + +# Parameters +# ---------- +# i_ren: :class:`CustomInteractorStyle` +# obj: :class:`vtkActor` +# The picked actor +# rect_obj: :class:`Rectangle2D` + +# """ +# position = i_ren.event.position +# offset = int( +# (position[1] - self.scroll_init_position) / self.scroll_step_size) +# if offset > 0 and self.view_offset > 0: +# offset = min(offset, self.view_offset) + +# elif offset < 0 and (self.view_offset + self.nb_slots < len(self.values)): +# offset = min(-offset, len(self.values) - self.nb_slots - self.view_offset) +# offset = -offset +# else: +# return + +# self.view_offset -= offset +# self.update() +# scroll_bar_idx = self.panel._elements.index(self.scroll_bar) +# self.scroll_bar.center = ( +# self.scroll_bar.center[0], +# self.scroll_bar.center[1] + offset * self.scroll_step_size, +# ) + +# self.scroll_init_position += offset * self.scroll_step_size + +# self.panel.element_offsets[scroll_bar_idx] = ( +# self.scroll_bar, +# (self.scroll_bar.position - self.panel.position), +# ) +# i_ren.force_render() +# i_ren.event.abort() + +# def update(self): +# """Refresh listbox's content.""" +# view_start = self.view_offset +# view_end = view_start + self.nb_slots +# values_to_show = self.values[view_start:view_end] + +# # Populate slots according to the view. +# for i, choice in enumerate(values_to_show): +# slot = self.slots[i] +# slot.element = choice +# if slot.textblock.scene is not None: +# clip_overflow(slot.textblock, self.slot_width) +# slot.set_visibility(True) +# if slot.size[1] != self.slot_height: +# slot.resize((self.slot_width, self.slot_height)) +# if slot.element in self.selected: +# slot.select() +# else: +# slot.deselect() + +# # Flush remaining slots. +# for slot in self.slots[len(values_to_show) :]: +# slot.element = None +# slot.set_visibility(False) +# slot.resize((self.slot_width, 0)) +# slot.deselect() + +# def update_scrollbar(self): +# """Change the scroll-bar height when the values +# in the listbox change +# """ +# self.scroll_bar.set_visibility(True) + +# self.scroll_bar.height = ( +# self.nb_slots * (self.panel_size[1] - 2 * self.margin) / len(self.values) +# ) + +# self.scroll_step_size = ( +# self.slot_height * self.nb_slots - self.scroll_bar.height +# ) / (len(self.values) - self.nb_slots) + +# self.panel.update_element( +# self.scroll_bar, self.panel_size - self.scroll_bar.size - self.margin +# ) + +# if len(self.values) <= self.nb_slots: +# self.scroll_bar.set_visibility(False) +# self.scroll_bar.height = 0 + +# def clear_selection(self): +# del self.selected[:] + +# @warn_on_args_to_kwargs() +# def select(self, item, *, multiselect=False, range_select=False): +# """Select the item. + +# Parameters +# ---------- +# item: ListBoxItem2D's object +# Item to select. +# multiselect: {True, False} +# If True and multiselection is allowed, the item is added to the +# selection. +# Otherwise, the selection will only contain the provided item unless +# range_select is True. +# range_select: {True, False} +# If True and multiselection is allowed, all items between the last +# selected item and the current one will be added to the selection. +# Otherwise, the selection will only contain the provided item unless +# multi_select is True. + +# """ +# selection_idx = self.values.index(item.element) +# if self.multiselection and range_select: +# self.clear_selection() +# step = 1 if selection_idx >= self.last_selection_idx else -1 +# for i in range(self.last_selection_idx, selection_idx + step, step): +# self.selected.append(self.values[i]) + +# elif self.multiselection and multiselect: +# if item.element in self.selected: +# self.selected.remove(item.element) +# else: +# self.selected.append(item.element) +# self.last_selection_idx = selection_idx + +# else: +# self.clear_selection() +# self.selected.append(item.element) +# self.last_selection_idx = selection_idx + +# self.on_change() # Call hook. +# self.update() + + +# class ListBoxItem2D(UI): +# """The text displayed in a listbox.""" + +# @warn_on_args_to_kwargs() +# def __init__( +# self, +# list_box, +# size, +# *, +# text_color=(1.0, 0.0, 0.0), +# selected_color=(0.4, 0.4, 0.4), +# unselected_color=(0.9, 0.9, 0.9), +# background_opacity=1.0, +# ): +# """Init ListBox Item instance. + +# Parameters +# ---------- +# list_box : :class:`ListBox` +# The ListBox reference this text belongs to. +# size : tuple of 2 ints +# The size of the listbox item. +# text_color : tuple of 3 floats +# unselected_color : tuple of 3 floats +# selected_color : tuple of 3 floats +# background_opacity : float + +# """ +# super(ListBoxItem2D, self).__init__() +# self._element = None +# self.list_box = list_box +# self.background.resize(size) +# self.background_opacity = background_opacity +# self.selected = False +# self.text_color = text_color +# self.textblock.color = self.text_color +# self.selected_color = selected_color +# self.unselected_color = unselected_color +# self.background.opacity = self.background_opacity +# self.deselect() + +# def _setup(self): +# """Setup this UI component. + +# Create the ListBoxItem2D with its background (Rectangle2D) and its +# label (TextBlock2D). +# """ +# self.background = Rectangle2D() +# self.textblock = TextBlock2D( +# justification="left", vertical_justification="middle" +# ) + +# # Add default events listener for this UI component. +# self.add_callback( +# self.textblock.actor, "LeftButtonPressEvent", self.left_button_clicked +# ) +# self.add_callback( +# self.background.actor, "LeftButtonPressEvent", self.left_button_clicked +# ) + +# def _get_actors(self): +# """Get the actors composing this UI component.""" +# return self.background.actors + self.textblock.actors + +# def _add_to_scene(self, scene): +# """Add all subcomponents or VTK props that compose this UI component. + +# Parameters +# ---------- +# scene : scene + +# """ +# self.background.add_to_scene(scene) +# self.textblock.add_to_scene(scene) + +# def _get_size(self): +# return self.background.size + +# def _set_position(self, coords): +# """Set the lower-left corner position of this UI component. + +# Parameters +# ---------- +# coords: (float, float) +# Absolute pixel coordinates (x, y). + +# """ +# self.textblock.position = coords +# # Center background underneath the text. +# position = coords +# self.background.position = ( +# position[0], +# position[1] - self.background.size[1] / 2.0, +# ) + +# def deselect(self): +# self.background.color = self.unselected_color +# self.textblock.bold = False +# self.selected = False + +# def select(self): +# self.textblock.bold = True +# self.background.color = self.selected_color +# self.selected = True + +# @property +# def element(self): +# return self._element + +# @element.setter +# def element(self, element): +# self._element = element +# self.textblock.message = "" if self._element is None else str(element) + +# def left_button_clicked(self, i_ren, _obj, _list_box_item): +# """Handle left click for this UI element. + +# Parameters +# ---------- +# i_ren: :class:`CustomInteractorStyle` +# obj: :class:`vtkActor` +# The picked actor +# _list_box_item: :class:`ListBoxItem2D` + +# """ +# multiselect = i_ren.event.ctrl_key +# range_select = i_ren.event.shift_key +# self.list_box.select( +# item=self, multiselect=multiselect, range_select=range_select +# ) +# i_ren.force_render() + +# def resize(self, size): +# self.background.resize(size) + + +# class FileMenu2D(UI): +# """A menu to select files in the current folder. + +# Can go to new folder, previous folder and select multiple files. + +# Attributes +# ---------- +# extensions: ['extension1', 'extension2', ....] +# To show all files, extensions=["*"] or [""] +# List of extensions to be shown as files. +# listbox : :class: 'ListBox2D' +# Container for the menu. + +# """ + +# @warn_on_args_to_kwargs() +# def __init__( +# self, +# directory_path, +# *, +# extensions=None, +# position=(0, 0), +# size=(100, 300), +# multiselection=True, +# reverse_scrolling=False, +# font_size=20, +# line_spacing=1.4, +# ): +# """Init class instance. + +# Parameters +# ---------- +# extensions: list(string) +# List of extensions to be shown as files. +# directory_path: string +# Path of the directory where this dialog should open. +# position : (float, float) +# Absolute coordinates (x, y) of the lower-left corner of this +# UI component. +# size : (int, int) +# Width and height in pixels of this UI component. +# multiselection: {True, False} +# Whether multiple values can be selected at once. +# reverse_scrolling: {True, False} +# If True, scrolling up will move the list of files down. +# font_size: int +# The font size in pixels. +# line_spacing: float +# Distance between listbox's items in pixels. + +# """ +# self.font_size = font_size +# self.multiselection = multiselection +# self.reverse_scrolling = reverse_scrolling +# self.line_spacing = line_spacing +# self.extensions = extensions or ["*"] +# self.current_directory = directory_path +# self.menu_size = size +# self.directory_contents = [] + +# super(FileMenu2D, self).__init__() +# self.position = position +# self.set_slot_colors() + +# def _setup(self): +# """Setup this UI component. + +# Create the ListBox (Panel2D) filled with empty slots (ListBoxItem2D). + +# """ +# self.directory_contents = self.get_all_file_names() +# content_names = [x[0] for x in self.directory_contents] +# self.listbox = ListBox2D( +# values=content_names, +# multiselection=self.multiselection, +# font_size=self.font_size, +# line_spacing=self.line_spacing, +# reverse_scrolling=self.reverse_scrolling, +# size=self.menu_size, +# ) + +# self.add_callback( +# self.listbox.scroll_bar.actor, "MouseMoveEvent", self.scroll_callback +# ) + +# # Handle mouse wheel events on the panel. +# up_event = "MouseWheelForwardEvent" +# down_event = "MouseWheelBackwardEvent" +# if self.reverse_scrolling: +# up_event, down_event = down_event, up_event # Swap events + +# self.add_callback( +# self.listbox.panel.background.actor, up_event, self.scroll_callback +# ) +# self.add_callback( +# self.listbox.panel.background.actor, down_event, self.scroll_callback +# ) + +# # Handle mouse wheel events on the slots. +# for slot in self.listbox.slots: +# self.add_callback(slot.background.actor, up_event, self.scroll_callback) +# self.add_callback(slot.background.actor, down_event, self.scroll_callback) +# self.add_callback(slot.textblock.actor, up_event, self.scroll_callback) +# self.add_callback(slot.textblock.actor, down_event, self.scroll_callback) +# slot.add_callback( +# slot.textblock.actor, +# "LeftButtonPressEvent", +# self.directory_click_callback, +# ) +# slot.add_callback( +# slot.background.actor, +# "LeftButtonPressEvent", +# self.directory_click_callback, +# ) + +# def _get_actors(self): +# """Get the actors composing this UI component.""" +# return self.listbox.actors + +# def resize(self, size): +# pass + +# def _set_position(self, coords): +# """Set the lower-left corner position of this UI component. + +# Parameters +# ---------- +# coords: (float, float) +# Absolute pixel coordinates (x, y). + +# """ +# self.listbox.position = coords + +# def _add_to_scene(self, scene): +# """Add all subcomponents or VTK props that compose this UI component. + +# Parameters +# ---------- +# scene : scene + +# """ +# self.listbox.add_to_scene(scene) + +# def _get_size(self): +# return self.listbox.size + +# def get_all_file_names(self): +# """Get file and directory names. + +# Returns +# ------- +# all_file_names: list((string, {"directory", "file"})) +# List of all file and directory names as string. + +# """ +# all_file_names = [] + +# directory_names = self.get_directory_names() +# for directory_name in directory_names: +# all_file_names.append((directory_name, "directory")) + +# file_names = self.get_file_names() +# for file_name in file_names: +# all_file_names.append((file_name, "file")) + +# return all_file_names + +# def get_directory_names(self): +# """Find names of all directories in the current_directory + +# Returns +# ------- +# directory_names: list(string) +# List of all directory names as string. + +# """ +# # A list of directory names in the current directory +# directory_names = [] +# for _, dirnames, _ in os.walk(self.current_directory): +# directory_names += dirnames +# break +# directory_names.sort(key=lambda s: s.lower()) +# directory_names.insert(0, "../") +# return directory_names + +# def get_file_names(self): +# """Find names of all files in the current_directory + +# Returns +# ------- +# file_names: list(string) +# List of all file names as string. + +# """ +# # A list of file names with extension in the current directory +# files = [] +# for _, _, f in os.walk(self.current_directory): +# files += f +# break + +# file_names = [] +# if "*" in self.extensions or "" in self.extensions: +# file_names = files +# else: +# for ext in self.extensions: +# for file in files: +# if file.endswith("." + ext): +# file_names.append(file) +# file_names.sort(key=lambda s: s.lower()) +# return file_names + +# def set_slot_colors(self): +# """Set the text color of the slots based on the type of element +# they show. Blue for directories and green for files. +# """ +# for idx, slot in enumerate(self.listbox.slots): +# list_idx = min( +# self.listbox.view_offset + idx, len(self.directory_contents) - 1 +# ) +# if self.directory_contents[list_idx][1] == "directory": +# slot.textblock.color = (0, 0.6, 0) +# elif self.directory_contents[list_idx][1] == "file": +# slot.textblock.color = (0, 0, 0.7) + +# def scroll_callback(self, i_ren, _obj, _filemenu_item): +# """Handle scroll and change the slot text colors. + +# Parameters +# ---------- +# i_ren: :class:`CustomInteractorStyle` +# obj: :class:`vtkActor` +# The picked actor +# _filemenu_item: :class:`FileMenu2D` + +# """ +# self.set_slot_colors() +# i_ren.force_render() +# i_ren.event.abort() + +# def directory_click_callback(self, i_ren, _obj, listboxitem): +# """Handle the move into a directory if it has been clicked. + +# Parameters +# ---------- +# i_ren: :class:`CustomInteractorStyle` +# obj: :class:`vtkActor` +# The picked actor +# listboxitem: :class:`ListBoxItem2D` + +# """ +# if (listboxitem.element, "directory") in self.directory_contents: +# new_directory_path = os.path.join( +# self.current_directory, listboxitem.element +# ) +# if os.access(new_directory_path, os.R_OK): +# self.current_directory = new_directory_path +# self.directory_contents = self.get_all_file_names() +# content_names = [x[0] for x in self.directory_contents] +# self.listbox.clear_selection() +# self.listbox.values = content_names +# self.listbox.view_offset = 0 +# self.listbox.update() +# self.listbox.update_scrollbar() +# self.set_slot_colors() +# i_ren.force_render() +# i_ren.event.abort() + + +# class DrawShape(UI): +# """Create and Manage 2D Shapes.""" + +# @warn_on_args_to_kwargs() +# def __init__(self, shape_type, *, drawpanel=None, position=(0, 0)): +# """Init this UI element. + +# Parameters +# ---------- +# shape_type : string +# Type of shape to be created. +# drawpanel : DrawPanel, optional +# Reference to the main canvas on which it is drawn. +# position : (float, float), optional +# (x, y) in pixels. + +# """ +# self.shape = None +# self.shape_type = shape_type.lower() +# self.drawpanel = drawpanel +# self.max_size = None +# self.rotation = 0 +# super(DrawShape, self).__init__(position=position) +# self.shape.color = np.random.random(3) + +# def _setup(self): +# """Setup this UI component. + +# Create a Shape. +# """ +# if self.shape_type == "line": +# self.shape = Rectangle2D(size=(3, 3)) +# elif self.shape_type == "quad": +# self.shape = Rectangle2D(size=(3, 3)) +# elif self.shape_type == "circle": +# self.shape = Disk2D(outer_radius=2) +# else: +# raise IOError("Unknown shape type: {}.".format(self.shape_type)) + +# self.shape.on_left_mouse_button_pressed = self.left_button_pressed +# self.shape.on_left_mouse_button_dragged = self.left_button_dragged +# self.shape.on_left_mouse_button_released = self.left_button_released + +# def _get_actors(self): +# """Get the actors composing this UI component.""" +# return self.shape + +# def _add_to_scene(self, scene): +# """Add all subcomponents or VTK props that compose this UI component. + +# Parameters +# ---------- +# scene : scene + +# """ +# self._scene = scene +# self.shape.add_to_scene(scene) + +# def _get_size(self): +# return self.shape.size + +# def _set_position(self, coords): +# """Set the lower-left corner position of this UI component. + +# Parameters +# ---------- +# coords: (float, float) +# Absolute pixel coordinates (x, y). + +# """ +# if self.shape_type == "circle": +# self.shape.center = coords +# else: +# self.shape.position = coords + +# def update_shape_position(self, center_position): +# """Update the center position on the canvas. + +# Parameters +# ---------- +# center_position: (float, float) +# Absolute pixel coordinates (x, y). + +# """ +# new_center = self.clamp_position(center=center_position) +# self.drawpanel.canvas.update_element(self, new_center, anchor="center") +# self.cal_bounding_box() + +# @property +# def center(self): +# return self._bounding_box_min + self._bounding_box_size // 2 + +# @center.setter +# def center(self, coords): +# """Position the center of this UI component. + +# Parameters +# ---------- +# coords: (float, float) +# Absolute pixel coordinates (x, y). + +# """ +# new_center = np.array(coords) +# new_lower_left_corner = new_center - self._bounding_box_size // 2 +# self.position = new_lower_left_corner + self._bounding_box_offset +# self.cal_bounding_box() + +# @property +# def is_selected(self): +# return self._is_selected + +# @is_selected.setter +# def is_selected(self, value): +# if self.drawpanel and value: +# self.drawpanel.current_shape = self +# self._is_selected = value +# self.selection_change() + +# def selection_change(self): +# if self.is_selected: +# self.drawpanel.rotation_slider.value = self.rotation +# else: +# self.drawpanel.rotation_slider.set_visibility(False) + +# def rotate(self, angle): +# """Rotate the vertices of the UI component using specific angle. + +# Parameters +# ---------- +# angle: float +# Value by which the vertices are rotated in radian. + +# """ +# if self.shape_type == "circle": +# return +# points_arr = vertices_from_actor(self.shape.actor) +# new_points_arr = rotate_2d(points_arr, angle) +# set_polydata_vertices(self.shape._polygonPolyData, new_points_arr) +# update_actor(self.shape.actor) + +# self.cal_bounding_box() + +# def cal_bounding_box(self): +# """Calculate the min, max position and the size of the bounding box.""" +# vertices = self.position + vertices_from_actor(self.shape.actor)[:, :-1] + +# ( +# self._bounding_box_min, +# self._bounding_box_max, +# self._bounding_box_size, +# ) = cal_bounding_box_2d(vertices) + +# self._bounding_box_offset = self.position - self._bounding_box_min + +# @warn_on_args_to_kwargs() +# def clamp_position(self, *, center=None): +# """Clamp the given center according to the DrawPanel canvas. + +# Parameters +# ---------- +# center : (float, float) +# (x, y) in pixels. + +# Returns +# ------- +# new_center: ndarray(int) +# New center for the shape. + +# """ +# center = self.center if center is None else center +# new_center = np.clip( +# center, +# self._bounding_box_size // 2, +# self.drawpanel.canvas.size - self._bounding_box_size // 2, +# ) +# return new_center.astype(int) + +# def resize(self, size): +# """Resize the UI.""" +# if self.shape_type == "line": +# hyp = np.hypot(size[0], size[1]) +# self.shape.resize((hyp, 3)) +# self.rotate(angle=np.arctan2(size[1], size[0])) + +# elif self.shape_type == "quad": +# self.shape.resize(size) + +# elif self.shape_type == "circle": +# hyp = np.hypot(size[0], size[1]) +# if self.max_size and hyp > self.max_size: +# hyp = self.max_size +# self.shape.outer_radius = hyp + +# self.cal_bounding_box() + +# def remove(self): +# """Remove the Shape and all related actors.""" +# self._scene.rm(self.shape.actor) +# self.drawpanel.rotation_slider.set_visibility(False) + +# def left_button_pressed(self, i_ren, _obj, shape): +# mode = self.drawpanel.current_mode +# if mode == "selection": +# self.drawpanel.update_shape_selection(self) + +# click_pos = np.array(i_ren.event.position) +# self._drag_offset = click_pos - self.center +# self.drawpanel.show_rotation_slider() +# i_ren.event.abort() +# elif mode == "delete": +# self.remove() +# else: +# self.drawpanel.left_button_pressed(i_ren, _obj, self.drawpanel) +# i_ren.force_render() + +# def left_button_dragged(self, i_ren, _obj, shape): +# if self.drawpanel.current_mode == "selection": +# self.drawpanel.rotation_slider.set_visibility(False) +# if self._drag_offset is not None: +# click_position = i_ren.event.position +# relative_center_position = ( +# +# click_position - self._drag_offset - self.drawpanel.canvas.position +# ) +# self.update_shape_position(relative_center_position) +# i_ren.force_render() +# else: +# self.drawpanel.left_button_dragged(i_ren, _obj, self.drawpanel) + +# def left_button_released(self, i_ren, _obj, shape): +# if self.drawpanel.current_mode == "selection": +# self.drawpanel.show_rotation_slider() +# i_ren.force_render() + + +# class DrawPanel(UI): +# """The main Canvas(Panel2D) on which everything would be drawn.""" + +# @warn_on_args_to_kwargs() +# def __init__(self, *, size=(400, 400), position=(0, 0), is_draggable=False): +# """Init this UI element. + +# Parameters +# ---------- +# size : (int, int), optional +# Width and height in pixels of this UI component. +# position : (float, float), optional +# (x, y) in pixels. +# is_draggable : bool, optional +# Whether the background canvas will be draggble or not. + +# """ +# self.panel_size = size +# super(DrawPanel, self).__init__(position=position) +# self.is_draggable = is_draggable +# self.current_mode = None + +# if is_draggable: +# self.current_mode = "selection" + +# self.shape_list = [] +# self.current_shape = None + +# def _setup(self): +# """Setup this UI component. + +# Create a Canvas(Panel2D). +# """ +# self.canvas = Panel2D(size=self.panel_size) +# self.canvas.background.on_left_mouse_button_pressed = self.left_button_pressed +# self.canvas.background.on_left_mouse_button_dragged = self.left_button_dragged + +# # Todo +# # Convert mode_data into a private variable and make it read-only +# # Then add the ability to insert user-defined mode +# mode_data = { +# "selection": ["selection.png", "selection-pressed.png"], +# "line": ["line.png", "line-pressed.png"], +# "quad": ["quad.png", "quad-pressed.png"], +# "circle": ["circle.png", "circle-pressed.png"], +# "delete": ["delete.png", "delete-pressed.png"], +# } + +# padding = 5 +# # Todo +# # Add this size to __init__ +# mode_panel_size = (len(mode_data) * 35 + 2 * padding, 40) +# self.mode_panel = Panel2D(size=mode_panel_size, color=(0.5, 0.5, 0.5)) +# btn_pos = np.array([0, 0]) + +# for mode, fname in mode_data.items(): +# icon_files = [] +# icon_files.append( +# (mode, read_viz_icons(style="new_icons", fname=fname[0]))) +# icon_files.append( +# (mode + "-pressed", read_viz_icons(style="new_icons", fname=fname[1])) +# ) +# btn = Button2D(icon_fnames=icon_files) + +# def mode_selector(i_ren, _obj, btn): +# self.current_mode = btn.icon_names[0] +# i_ren.force_render() + +# btn.on_left_mouse_button_pressed = mode_selector + +# self.mode_panel.add_element(btn, btn_pos + padding) +# btn_pos[0] += btn.size[0] + padding + +# self.canvas.add_element(self.mode_panel, (0, -mode_panel_size[1])) + +# self.mode_text = TextBlock2D( +# text="Select appropriate drawing mode using below icon" +# ) +# self.canvas.add_element(self.mode_text, (0.0, 1.0)) + +# self.rotation_slider = RingSlider2D( +# initial_value=0, text_template="{angle:5.1f}°" +# ) +# self.rotation_slider.set_visibility(False) + +# def rotate_shape(slider): +# angle = slider.value +# previous_angle = slider.previous_value +# rotation_angle = angle - previous_angle + +# current_center = self.current_shape.center +# self.current_shape.rotate(np.deg2rad(rotation_angle)) +# self.current_shape.rotation = slider.value +# self.current_shape.update_shape_position( +# current_center - self.canvas.position +# ) + +# self.rotation_slider.on_moving_slider = rotate_shape + +# def _get_actors(self): +# """Get the actors composing this UI component.""" +# return self.canvas.actors + +# def _add_to_scene(self, scene): +# """Add all subcomponents or VTK props that compose this UI component. + +# Parameters +# ---------- +# scene : scene + +# """ +# self._scene = scene +# self.canvas.add_to_scene(scene) + +# def _get_size(self): +# return self.canvas.size + +# def _set_position(self, coords): +# """Set the lower-left corner position of this UI component. + +# Parameters +# ---------- +# coords: (float, float) +# Absolute pixel coordinates (x, y). + +# """ +# self.canvas.position = coords + [0, self.mode_panel.size[1]] +# slider_position = self.canvas.position + [ +# self.canvas.size[0] - self.rotation_slider.size[0] / 2, +# self.rotation_slider.size[1] / 2, +# ] +# self.rotation_slider.center = slider_position + +# def resize(self, size): +# """Resize the UI.""" +# pass + +# @property +# def current_mode(self): +# return self._current_mode + +# @current_mode.setter +# def current_mode(self, mode): +# self.update_button_icons(mode) +# self._current_mode = mode +# if mode is not None: +# self.mode_text.message = f"Mode: {mode}" + +# def cal_min_boundary_distance(self, position): +# """Calculate minimum distance between the current +# position and canvas boundary. + +# Parameters +# ---------- +# position: (float,float) +# current position of the shape. + +# Returns +# ------- +# float +# Minimum distance from the boundary. + +# """ +# distance_list = [] +# # calculate distance from element to left and lower boundary +# distance_list.extend(position - self.canvas.position) +# # calculate distance from element to upper and right boundary +# distance_list.extend(self.canvas.position + self.canvas.size - position) + +# return min(distance_list) + +# def draw_shape(self, shape_type, current_position): +# """Draw the required shape at the given position. + +# Parameters +# ---------- +# shape_type: string +# Type of shape - line, quad, circle. +# current_position: (float,float) +# Lower left corner position for the shape. + +# """ +# shape = DrawShape( +# shape_type=shape_type, drawpanel=self, position=current_position +# ) +# if shape_type == "circle": +# shape.max_size = self.cal_min_boundary_distance(current_position) +# self.shape_list.append(shape) +# self._scene.add(shape) +# self.canvas.add_element(shape, current_position - self.canvas.position) +# self.update_shape_selection(shape) + +# def resize_shape(self, current_position): +# """Resize the shape. + +# Parameters +# ---------- +# current_position: (float,float) +# Lower left corner position for the shape. + +# """ +# self.current_shape = self.shape_list[-1] +# size = current_position - self.current_shape.position +# self.current_shape.resize(size) + +# def update_shape_selection(self, selected_shape): +# for shape in self.shape_list: +# if selected_shape == shape: +# shape.is_selected = True +# else: +# shape.is_selected = False + +# def show_rotation_slider(self): +# """Display the RingSlider2D to allow rotation of shape from the center.""" +# self._scene.rm(*self.rotation_slider.actors) +# self.rotation_slider.add_to_scene(self._scene) +# self.rotation_slider.set_visibility(True) + +# def update_button_icons(self, current_mode): +# """Update the button icon. + +# Parameters +# ---------- +# current_mode: string +# Current mode of the UI. + +# """ +# for btn in self.mode_panel._elements[1:]: +# if btn.icon_names[0] == current_mode: +# btn.next_icon() +# elif btn.current_icon_id == 1: +# btn.next_icon() + +# def clamp_mouse_position(self, mouse_position): +# """Restrict the mouse position to the canvas boundary. + +# Parameters +# ---------- +# mouse_position: (float,float) +# Current mouse position. + +# Returns +# ------- +# list(float) +# New clipped position. + +# """ +# return np.clip( +# mouse_position, +# self.canvas.position, +# self.canvas.position + self.canvas.size, +# ) + +# def handle_mouse_click(self, position): +# if self.current_mode == "selection": +# if self.is_draggable: +# self._drag_offset = position - self.position +# self.current_shape.is_selected = False +# if self.current_mode in ["line", "quad", "circle"]: +# self.draw_shape(self.current_mode, position) + +# def left_button_pressed(self, i_ren, _obj, element): +# self.handle_mouse_click(i_ren.event.position) +# i_ren.force_render() + +# def handle_mouse_drag(self, position): +# if self.is_draggable and self.current_mode == "selection": +# if self._drag_offset is not None: +# new_position = position - self._drag_offset +# self.position = new_position +# if self.current_mode in ["line", "quad", "circle"]: +# self.resize_shape(position) + +# def left_button_dragged(self, i_ren, _obj, element): +# mouse_position = self.clamp_mouse_position(i_ren.event.position) +# self.handle_mouse_drag(mouse_position) +# i_ren.force_render() + + +# class PlaybackPanel(UI): +# """A playback controller that can do essential functionalities. +# such as play, pause, stop, and seek. +# """ + +# @warn_on_args_to_kwargs() +# def __init__(self, *, loop=False, position=(0, 0), width=None): +# self._width = width if width is not None else 900 +# self._auto_width = width is None +# self._position = position +# super(PlaybackPanel, self).__init__(position=position) +# self._playing = False +# self._loop = None +# self.loop() if loop else self.play_once() +# self._speed = 1 +# # callback functions +# self.on_play_pause_toggle = lambda state: None +# self.on_play = lambda: None +# self.on_pause = lambda: None +# self.on_stop = lambda: None +# self.on_loop_toggle = lambda is_looping: None +# self.on_progress_bar_changed = lambda x: None +# self.on_speed_up = lambda x: None +# self.on_slow_down = lambda x: None +# self.on_speed_changed = lambda x: None +# self._set_position(position) + +# def _setup(self): +# """Setup this Panel component.""" +# self.time_text = TextBlock2D() +# self.speed_text = TextBlock2D( +# text="1", +# font_size=21, +# color=(0.2, 0.2, 0.2), +# bold=True, +# justification="center", +# vertical_justification="middle", +# ) + +# self.panel = Panel2D( +# size=(190, 30), +# color=(1, 1, 1), +# align="right", +# has_border=True, +# border_color=(0, 0.3, 0), +# border_width=2, +# ) + +# play_pause_icons = [ +# ("play", read_viz_icons(fname="play3.png")), +# ("pause", read_viz_icons(fname="pause2.png")), +# ] + +# loop_icons = [ +# ("once", read_viz_icons(fname="checkmark.png")), +# ("loop", read_viz_icons(fname="infinite.png")), +# ] + +# self._play_pause_btn = Button2D(icon_fnames=play_pause_icons) + +# self._loop_btn = Button2D(icon_fnames=loop_icons) + +# self._stop_btn = Button2D( +# icon_fnames=[("stop", read_viz_icons(fname="stop2.png"))] +# ) + +# self._speed_up_btn = Button2D( +# icon_fnames=[("plus", read_viz_icons(fname="plus.png"))], size=(15, 15) +# ) + +# self._slow_down_btn = Button2D( +# icon_fnames=[("minus", read_viz_icons(fname="minus.png"))], size=(15, 15) +# ) + +# self._progress_bar = LineSlider2D( +# initial_value=0, +# orientation="horizontal", +# min_value=0, +# max_value=100, +# text_alignment="top", +# length=590, +# text_template="", +# line_width=9, +# ) + +# start = 0.04 +# w = 0.2 +# self.panel.add_element(self._play_pause_btn, (start, 0.04)) +# self.panel.add_element(self._stop_btn, (start + w, 0.04)) +# self.panel.add_element(self._loop_btn, (start + 2 * w, 0.04)) +# self.panel.add_element(self._slow_down_btn, (start + 0.63, 0.3)) +# self.panel.add_element(self.speed_text, (start + 0.78, 0.45)) +# self.panel.add_element(self._speed_up_btn, (start + 0.86, 0.3)) + +# def play_pause_toggle(i_ren, _obj, _button): +# self._playing = not self._playing +# if self._playing: +# self.play() +# else: +# self.pause() +# self.on_play_pause_toggle(self._playing) +# i_ren.force_render() + +# def stop(i_ren, _obj, _button): +# self.stop() +# i_ren.force_render() + +# def speed_up(i_ren, _obj, _button): +# inc = 10 ** np.floor(np.log10(self.speed)) +# self.speed = round(self.speed + inc, 13) +# self.on_speed_up(self._speed) +# self.on_speed_changed(self._speed) +# i_ren.force_render() + +# def slow_down(i_ren, _obj, _button): +# dec = 10 ** np.floor(np.log10(self.speed - self.speed / 10)) +# self.speed = round(self.speed - dec, 13) +# self.on_slow_down(self._speed) +# self.on_speed_changed(self._speed) +# i_ren.force_render() + +# def loop_toggle(i_ren, _obj, _button): +# self._loop = not self._loop +# if self._loop: +# self.loop() +# else: +# self.play_once() +# self.on_loop_toggle(self._loop) +# i_ren.force_render() + +# # using the adapters created above +# self._play_pause_btn.on_left_mouse_button_pressed = play_pause_toggle +# self._stop_btn.on_left_mouse_button_pressed = stop +# self._loop_btn.on_left_mouse_button_pressed = loop_toggle +# self._speed_up_btn.on_left_mouse_button_pressed = speed_up +# self._slow_down_btn.on_left_mouse_button_pressed = slow_down + +# def on_progress_change(slider): +# t = slider.value +# self.on_progress_bar_changed(t) +# self.current_time = t + +# self._progress_bar.on_moving_slider = on_progress_change +# self.current_time = 0 + +# def play(self): +# """Play the playback""" +# self._playing = True +# self._play_pause_btn.set_icon_by_name("pause") +# self.on_play() + +# def stop(self): +# """Stop the playback""" +# self._playing = False +# self._play_pause_btn.set_icon_by_name("play") +# self.on_stop() + +# def pause(self): +# """Pause the playback""" +# self._playing = False +# self._play_pause_btn.set_icon_by_name("play") +# self.on_pause() + +# def loop(self): +# """Set repeating mode to loop.""" +# self._loop = True +# self._loop_btn.set_icon_by_name("loop") + +# def play_once(self): +# """Set repeating mode to repeat once.""" +# self._loop = False +# self._loop_btn.set_icon_by_name("once") + +# @property +# def final_time(self): +# """Set final progress slider time value. + +# Returns +# ------- +# float +# Final time for the progress slider. + +# """ +# return self._progress_bar.max_value + +# @final_time.setter +# def final_time(self, t): +# """Set final progress slider time value. + +# Parameters +# ---------- +# t: float +# Final time for the progress slider. + +# """ +# self._progress_bar.max_value = t + +# @property +# def current_time(self): +# """Get current time of the progress slider. + +# Returns +# ------- +# float +# Progress slider current value. + +# """ +# return self._progress_bar.value + +# @current_time.setter +# def current_time(self, t): +# """Set progress slider value. + +# Parameters +# ---------- +# t: float +# Current time to be set. + +# """ +# self._progress_bar.value = t +# self.current_time_str = t + +# @property +# def current_time_str(self): +# """Returns current time as a string. + +# Returns +# ------- +# str +# Current time formatted as a string in the form:`HH:MM:SS`. + +# """ +# return self.time_text.message + +# @current_time_str.setter +# def current_time_str(self, t): +# """Set time counter. + +# Parameters +# ---------- +# t: float +# Time to be set in the time_text counter. + +# Notes +# ----- +# This should only be used when the `current_value` is not being set +# since setting`current_value` automatically sets this property as well. + +# """ +# t = np.clip(t, 0, self.final_time) +# if self.final_time < 3600: +# m, s = divmod(t, 60) +# t_str = r"%02d:%05.2f" % (m, s) +# else: +# m, s = divmod(t, 60) +# h, m = divmod(m, 60) +# t_str = r"%02d:%02d:%02d" % (h, m, s) +# self.time_text.message = t_str + +# @property +# def speed(self): +# """Returns current speed. + +# Returns +# ------- +# str +# Current time formatted as a string in the form:`HH:MM:SS`. + +# """ +# return self._speed + +# @speed.setter +# def speed(self, speed): +# """Set time counter. + +# Parameters +# ---------- +# speed: float +# Speed value to be set in the speed_text counter. + +# """ +# if speed <= 0: +# speed = 0.01 +# self._speed = speed +# speed_str = f"{speed}".strip("0").rstrip(".") +# self.speed_text.font_size = 21 if 0.01 <= speed < 100 else 14 +# self.speed_text.message = speed_str + +# def show(self): +# [act.SetVisibility(1) for act in self._get_actors()] + +# def hide(self): +# [act.SetVisibility(0) for act in self._get_actors()] + +# def _get_actors(self): +# """Get the actors composing this UI component.""" +# return self.panel.actors + self._progress_bar.actors + self.time_text.actors + +# def _add_to_scene(self, _scene): +# """Add all subcomponents or VTK props that compose this UI component. + +# Parameters +# ---------- +# _scene : scene + +# """ + +# def resize_cbk(caller, ev): +# if self._auto_width: +# width = _scene.GetSize()[0] +# if width == self.width: +# return +# self._width = width +# self._set_position(self.position) +# self._progress_bar.value = self._progress_bar.value + +# _scene.AddObserver(Command.StartEvent, resize_cbk) +# self.panel.add_to_scene(_scene) +# self._progress_bar.add_to_scene(_scene) +# self.time_text.add_to_scene(_scene) + +# @property +# def width(self): +# """Return the width of the PlaybackPanel + +# Returns +# ------- +# float +# The width of the PlaybackPanel. + +# """ +# return self._width + +# @width.setter +# def width(self, width): +# """Set width of the PlaybackPanel. + +# Parameters +# ---------- +# width: float +# The width of the whole panel. +# If set to None, The width will be the same as the window's width. + +# """ +# self._width = width if width is not None else 900 +# self._auto_width = width is None +# self._set_position(self.position) + +# def _set_position(self, _coords): +# x, y = self.position +# width = self.width +# self.panel.position = (x + 5, y + 5) +# progress_length = max(width - 310 - x, 1.0) +# self._progress_bar.track.width = progress_length +# self._progress_bar.center = (x + 215 + progress_length / 2, y + 20) +# self.time_text.position = (x + 225 + progress_length, y + 10) + +# def _get_size(self): +# return self.panel.size + self._progress_bar.size + self.time_text.size + + +# class Card2D(UI): +# """Card element to show image and related text + +# Attributes +# ---------- +# image: :class: 'ImageContainer2D' +# Renders the image on the card. +# title_box: :class: 'TextBlock2D' +# Displays the title on card. +# body_box: :class: 'TextBLock2D' +# Displays the body text. + +# """ + +# @warn_on_args_to_kwargs() +# def __init__( +# self, +# image_path, +# *, +# body_text="", +# draggable=True, +# title_text="", +# padding=10, +# position=(0, 0), +# size=(400, 400), +# image_scale=0.5, +# bg_color=(0.5, 0.5, 0.5), +# bg_opacity=1, +# title_color=(0.0, 0.0, 0.0), +# body_color=(0.0, 0.0, 0.0), +# border_color=(1.0, 1.0, 1.0), +# border_width=0, +# maintain_aspect=False, +# ): +# """Parameters +# ---------- +# image_path: str +# Path of the image, supports png and jpg/jpeg images +# body_text: str, optional +# Card body text +# draggable: Bool, optional +# If the card should be draggable +# title_text: str, optional +# Card title text +# padding: int, optional +# Padding between image, title, body +# position : (float, float), optional +# Absolute coordinates (x, y) of the lower-left corner of the +# UI component +# size : (int, int), optional +# Width and height of the pixels of this UI component. +# image_scale: float, optional +# fraction of size taken by the image (between 0 , 1) +# bg_color: (float, float, float), optional +# Background color of card +# bg_opacity: float, optional +# Background opacity +# title_color: (float, float, float), optional +# Title text color +# body_color: (float, float, float), optional +# Body text color +# border_color: (float, float, float), optional +# Border color +# border_width: int, optional +# Width of the border +# maintain_aspect: bool, optional +# If the image should be scaled to maintain aspect ratio + +# """ +# self.image_path = image_path +# self._basename = os.path.basename(self.image_path) +# self._extension = self._basename.split(".")[-1] +# if self._extension not in ["jpg", "jpeg", "png"]: +# raise UnidentifiedImageError( +# f"Image extension {self._extension} not supported" +# ) + +# self.body_text = body_text +# self.title_text = title_text +# self.draggable = draggable +# self.card_size = size +# self.padding = padding + +# self.title_color = [np.clip(value, 0, 1) for value in title_color] +# self.body_color = [np.clip(value, 0, 1) for value in body_color] +# self.bg_color = [np.clip(value, 0, 1) for value in bg_color] +# self.border_color = [np.clip(value, 0, 1) for value in border_color] +# self.bg_opacity = bg_opacity + +# self.text_scale = np.clip(1 - image_scale, 0, 1) +# self.image_scale = np.clip(image_scale, 0, 1) + +# self.maintain_aspect = maintain_aspect +# if self.maintain_aspect: +# self._true_image_size = Image.open(urlopen(self.image_path)).size + +# self._image_size = (self.card_size[0], self.card_size[1] * self.image_scale) + +# self.border_width = border_width +# self.has_border = bool(border_width) + +# super(Card2D, self).__init__() +# self.position = position + +# if self.maintain_aspect: +# self._new_size = ( +# self._true_image_size[0], +# self._true_image_size[1] // self.image_scale, +# ) +# self.resize(self._new_size) +# else: +# self.resize(size) + +# def _setup(self): +# """Setup this UI component +# Create the image. +# Create the title and body. +# Create a Panel2D widget to hold image, title, body. +# """ +# self.image = ImageContainer2D(img_path=self.image_path, size=self._image_size) + +# self.body_box = TextBlock2D(text=self.body_text, color=self.body_color) + +# self.title_box = TextBlock2D( +# text=self.title_text, bold=True, color=self.title_color +# ) + +# self.panel = Panel2D( +# self.card_size, +# color=self.bg_color, +# opacity=self.bg_opacity, +# border_color=self.border_color, +# border_width=self.border_width, +# has_border=self.has_border, +# ) + +# self.panel.add_element(self.image, (0.0, 0.0)) +# self.panel.add_element(self.title_box, (0.0, 0.0)) +# self.panel.add_element(self.body_box, (0.0, 0.0)) + +# if self.draggable: +# self.panel.background.on_left_mouse_button_dragged = ( +# self.left_button_dragged +# ) +# self.panel.background.on_left_mouse_button_pressed = ( +# self.left_button_pressed +# ) +# self.image.on_left_mouse_button_dragged = self.left_button_dragged +# self.image.on_left_mouse_button_pressed = self.left_button_pressed +# else: +# self.panel.background.on_left_mouse_button_dragged = ( +# lambda i_ren, _obj, _comp: i_ren.force_render +# ) + +# def _get_actors(self): +# """Get the actors composing this UI component.""" +# return self.panel.actors + +# def _add_to_scene(self, _scene): +# """Add all subcomponents or VTK props that compose this UI component. + +# Parameters +# ---------- +# scene : scene + +# """ +# self.panel.add_to_scene(_scene) +# if self.size[0] <= 200: +# clip_overflow(self.body_box, self.size[0] - 2 * self.padding) +# else: +# wrap_overflow(self.body_box, self.size[0] - 2 * self.padding) + +# wrap_overflow(self.title_box, self.size[0] - 2 * self.padding) + +# def _get_size(self): +# return self.panel.size + +# def resize(self, size): +# """Resize Card2D. + +# Parameters +# ---------- +# size : (int, int) +# Card2D size(width, height) in pixels. + +# """ +# _width, _height = size +# self.panel.resize(size) + +# self._image_size = ( +# size[0] - int(self.border_width), +# int(self.image_scale * size[1]), +# ) + +# _title_box_size = ( +# _width - 2 * self.padding, +# _height * 0.34 * self.text_scale / 2, +# ) + +# _body_box_size = (_width - 2 * self.padding, _height * self.text_scale / 2) + +# _img_coords = (int(self.border_width), int(size[1] - self._image_size[1])) + +# _title_coords = ( +# self.padding, +# int(_img_coords[1] - _title_box_size[1] - \ +# self.padding + self.border_width), +# ) + +# _text_coords = ( +# self.padding, +# int( +# _title_coords[1] - _body_box_size[1] - \ +# self.padding + self.border_width +# ), +# ) + +# self.panel.update_element(self.image, _img_coords) +# self.panel.update_element(self.body_box, _text_coords) +# self.panel.update_element(self.title_box, _title_coords) + +# self.image.resize(self._image_size) +# self.title_box.resize(_title_box_size) + +# def _set_position(self, _coords): +# """Position the lower-left corner of this UI component. + +# Parameters +# ---------- +# coords: (float, float) +# Absolute pixel coordinates (x, y). + +# """ +# self.panel.position = _coords + +# @property +# def color(self): +# """Returns the background color of card.""" +# return self.panel.color + +# @color.setter +# def color(self, color): +# """Sets background color of card. + +# Parameters +# ---------- +# color : list of 3 floats. + +# """ +# self.panel.color = color + +# @property +# def body(self): +# """Returns the body text of the card.""" +# return self.body_box.message + +# @body.setter +# def body(self, text): +# self.body_box.message = text + +# @property +# def title(self): +# """Returns the title text of the card""" +# return self.title_box.message + +# @title.setter +# def title(self, text): +# self.title_box.message = text + +# def left_button_pressed(self, i_ren, _obj, _sub_component): +# click_pos = np.array(i_ren.event.position) +# self._click_position = click_pos +# i_ren.event.abort() + +# def left_button_dragged(self, i_ren, _obj, _sub_component): +# click_position = np.array(i_ren.event.position) +# change = click_position - self._click_position +# self.panel.position += change +# self._click_position = click_position +# i_ren.force_render() + + +# class SpinBox(UI): +# """SpinBox UI.""" + +# @warn_on_args_to_kwargs() +# def __init__( +# self, +# *, +# position=(350, 400), +# size=(300, 100), +# padding=10, +# panel_color=(1, 1, 1), +# min_val=0, +# max_val=100, +# initial_val=50, +# step=1, +# max_column=10, +# max_line=2, +# ): +# """Init this UI element. + +# Parameters +# ---------- +# position : (int, int), optional +# Absolute coordinates (x, y) of the lower-left corner of this +# UI component. +# size : (int, int), optional +# Width and height in pixels of this UI component. +# padding : int, optional +# Distance between TextBox and Buttons. +# panel_color : (float, float, float), optional +# Panel color of SpinBoxUI. +# min_val: int, optional +# Minimum value of SpinBoxUI. +# max_val: int, optional +# Maximum value of SpinBoxUI. +# initial_val: int, optional +# Initial value of SpinBoxUI. +# step: int, optional +# Step value of SpinBoxUI. +# max_column: int, optional +# Max number of characters in a line. +# max_line: int, optional +# Max number of lines in the textbox. + +# """ +# self.panel_size = size +# self.padding = padding +# self.panel_color = panel_color +# self.min_val = min_val +# self.max_val = max_val +# self.step = step +# self.max_column = max_column +# self.max_line = max_line + +# super(SpinBox, self).__init__(position=position) +# self.value = initial_val +# self.resize(size) + +# self.on_change = lambda ui: None + +# def _setup(self): +# """Setup this UI component. + +# Create the SpinBoxUI with Background (Panel2D) and InputBox (TextBox2D) +# and Increment,Decrement Button (Button2D). +# """ +# self.panel = Panel2D(size=self.panel_size, color=self.panel_color) + +# self.textbox = TextBox2D(width=self.max_column, height=self.max_line) +# self.textbox.text.dynamic_bbox = False +# self.textbox.text.auto_font_scale = True +# self.increment_button = Button2D( +# icon_fnames=[("up", read_viz_icons(fname="circle-up.png"))] +# ) +# self.decrement_button = Button2D( +# icon_fnames=[("down", read_viz_icons(fname="circle-down.png"))] +# ) + +# self.panel.add_element(self.textbox, (0, 0)) +# self.panel.add_element(self.increment_button, (0, 0)) +# self.panel.add_element(self.decrement_button, (0, 0)) + +# # Adding button click callbacks +# self.increment_button.on_left_mouse_button_pressed = self.increment_callback +# self.decrement_button.on_left_mouse_button_pressed = self.decrement_callback +# self.textbox.off_focus = self.textbox_update_value + +# def resize(self, size): +# """Resize SpinBox. + +# Parameters +# ---------- +# size : (float, float) +# SpinBox size(width, height) in pixels. + +# """ +# self.panel_size = size +# self.textbox_size = (int(0.7 * size[0]), int(0.8 * size[1])) +# self.button_size = (int(0.2 * size[0]), int(0.3 * size[1])) +# self.padding = int(0.03 * self.panel_size[0]) + +# self.panel.resize(size) +# self.textbox.text.resize(self.textbox_size) +# self.increment_button.resize(self.button_size) +# self.decrement_button.resize(self.button_size) + +# textbox_pos = (self.padding, int((size[1] - self.textbox_size[1]) / 2)) +# inc_btn_pos = ( +# size[0] - self.padding - self.button_size[0], +# int((1.5 * size[1] - self.button_size[1]) / 2), +# ) +# dec_btn_pos = ( +# size[0] - self.padding - self.button_size[0], +# int((0.5 * size[1] - self.button_size[1]) / 2), +# ) + +# self.panel.update_element(self.textbox, textbox_pos) +# self.panel.update_element(self.increment_button, inc_btn_pos) +# self.panel.update_element(self.decrement_button, dec_btn_pos) + +# def _get_actors(self): +# """Get the actors composing this UI component.""" +# return self.panel.actors + +# def _add_to_scene(self, scene): +# """Add all subcomponents or VTK props that compose this UI component. + +# Parameters +# ---------- +# scene : Scene + +# """ +# self.panel.add_to_scene(scene) + +# def _get_size(self): +# return self.panel.size + +# def _set_position(self, coords): +# """Set the lower-left corner position of this UI component. + +# Parameters +# ---------- +# coords: (float, float) +# Absolute pixel coordinates (x, y). + +# """ +# self.panel.center = coords + +# def increment_callback(self, i_ren, _obj, _button): +# self.increment() +# i_ren.force_render() +# i_ren.event.abort() + +# def decrement_callback(self, i_ren, _obj, _button): +# self.decrement() +# i_ren.force_render() +# i_ren.event.abort() + +# @property +# def value(self): +# return self._value + +# @value.setter +# def value(self, value): +# if value >= self.max_val: +# self._value = self.max_val +# elif value <= self.min_val: +# self._value = self.min_val +# else: +# self._value = value + +# self.textbox.set_message(str(self._value)) + +# def validate_value(self, value): +# """Validate and convert the given value into integer. + +# Parameters +# ---------- +# value : str +# Input value received from the textbox. + +# Returns +# ------- +# int +# If valid return converted integer else the previous value. + +# """ +# if value.isnumeric(): +# return int(value) + +# return self.value + +# def increment(self): +# """Increment the current value by the step.""" +# current_val = self.validate_value(self.textbox.message) +# self.value = current_val + self.step +# self.on_change(self) + +# def decrement(self): +# """Decrement the current value by the step.""" +# current_val = self.validate_value(self.textbox.message) +# self.value = current_val - self.step +# self.on_change(self) + +# def textbox_update_value(self, textbox): +# self.value = self.validate_value(textbox.message) +# self.on_change(self) diff --git a/fury/window/__init__.pyi b/fury/window/__init__.pyi index d229f967c..c7588f690 100644 --- a/fury/window/__init__.pyi +++ b/fury/window/__init__.pyi @@ -2,7 +2,6 @@ __all__ = [ "ShowManager", "Scene", "record", - "reset_camera", "update_camera", "update_viewports", "render_screens", @@ -16,7 +15,6 @@ from .screen import ( calculate_screen_sizes, create_screen, render_screens, - reset_camera, update_camera, update_viewports, ) diff --git a/fury/window/show_manager.py b/fury/window/show_manager.py index d5415ad12..35505bdff 100644 --- a/fury/window/show_manager.py +++ b/fury/window/show_manager.py @@ -131,8 +131,8 @@ def title(self, value): def pixel_ratio(self): return self.renderer.pixel_ratio - @title.setter - def title(self, value): + @pixel_ratio.setter + def pixel_ratio(self, value): self.renderer.pixel_ratio = value @property diff --git a/requirements/default.txt b/requirements/default.txt index fbf8c30e6..a08c478c8 100644 --- a/requirements/default.txt +++ b/requirements/default.txt @@ -7,4 +7,4 @@ aiohttp>=3.8.4 pygltflib>=1.15.1 lazy_loader>=0.4 pygfx>=0.3.0 -glfw>=2.7.0 \ No newline at end of file +glfw>=2.7.0