Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci_cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:

env:
MAIN_PYTHON_VERSION: '3.13'
RESET_IMAGE_CACHE: 0
RESET_IMAGE_CACHE: 1
PACKAGE_NAME: ansys-tools-visualization-interface
DOCUMENTATION_CNAME: visualization-interface.tools.docs.pyansys.com
IN_GITHUB_ACTIONS: true
Expand Down
1 change: 1 addition & 0 deletions doc/changelog.d/359.maintenance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Feat: Add night mode and reorganize buttons
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import ansys.tools.visualization_interface
from ansys.tools.visualization_interface.backends._base import BaseBackend
from ansys.tools.visualization_interface.backends.pyvista.pyvista_interface import PyVistaInterface
from ansys.tools.visualization_interface.backends.pyvista.widgets.dark_mode import DarkModeButton
from ansys.tools.visualization_interface.backends.pyvista.widgets.displace_arrows import (
CameraPanDirection,
DisplacementArrow,
Expand Down Expand Up @@ -209,6 +210,7 @@ def enable_widgets(self, dark_mode: bool = False) -> None:
self._widgets.append(MeshSliderWidget(self, dark_mode))
self._widgets.append(HideButton(self, dark_mode))
self._widgets.append(PickRotCenterButton(self, dark_mode))
self._widgets.append(DarkModeButton(self, dark_mode))

def add_widget(self, widget: Union[PlotterWidget, List[PlotterWidget]]):
"""Add one or more custom widgets to the plotter.
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: MIT
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""Provides the dark mode button widget for the PyAnsys plotter."""
from pathlib import Path
from typing import TYPE_CHECKING

from vtk import vtkActor, vtkButtonWidget, vtkPNGReader

from ansys.tools.visualization_interface.backends.pyvista.widgets.widget import PlotterWidget

if TYPE_CHECKING:
from ansys.tools.visualization_interface.backends.pyvista.pyvista import Plotter


class DarkModeButton(PlotterWidget):
"""Provides the dark mode widget for the Visualization Interface Tool ``Plotter`` class.

Parameters
----------
plotter_helper : PlotterHelper
Plotter to add the dark mode widget to.
dark_mode : bool, optional
Whether to activate the dark mode or not.

"""

def __init__(self, plotter: "Plotter", dark_mode: bool = False) -> None:
"""Initialize the ``DarkModeButton`` class."""
# Call PlotterWidget ctor
super().__init__(plotter._pl.scene)
self._dark_mode = dark_mode
# Initialize variables
self._actor: vtkActor = None
self._plotter = plotter
self._button: vtkButtonWidget = self._plotter._pl.scene.add_checkbox_button_widget(
self.callback, position=(43, 10), size=30, border_size=3
)
self.update()

def callback(self, state: bool) -> None:
"""Remove or add the dark mode widget actor upon click.

Parameters
----------
state : bool
Whether the state of the button, which is inherited from PyVista, is active.

"""
if not self._dark_mode:
self._plotter.scene.set_background("black")
for widget in self._plotter._widgets:
widget._dark_mode = True
widget.update()

# Using internal method to force the render to update
self._plotter.scene.iren._mouse_right_button_click()
else:
self._plotter.scene.set_background("white")
for widget in self._plotter._widgets:
widget._dark_mode = False
widget.update()

# Using internal method to force the render to update
self._plotter.scene.iren._mouse_right_button_click()

def update(self) -> None:
"""Define the dark mode widget button parameters."""
if self._dark_mode:
is_inv = "_inv"
else:
is_inv = ""

show_vr = self._button.GetRepresentation()
show_vison_icon_file = Path(
Path(__file__).parent / "_images" / f"dark_mode{is_inv}.png"
)
show_visoff_icon_file = Path(
Path(__file__).parent / "_images" / f"dark_mode{is_inv}.png"
)
show_r_on = vtkPNGReader()
show_r_on.SetFileName(show_vison_icon_file)
show_r_on.Update()
image_on = show_r_on.GetOutput()

show_r_off = vtkPNGReader()
show_r_off.SetFileName(show_visoff_icon_file)
show_r_off.Update()
image_off = show_r_off.GetOutput()


show_vr.SetButtonTexture(0, image_off)
show_vr.SetButtonTexture(1, image_on)
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@
class CameraPanDirection(Enum):
"""Provides an enum with the available movement directions of the camera."""

XUP = 0, "upxarrow", (5, 170)
XDOWN = 1, "downarrow", (5, 130)
YUP = 2, "upyarrow", (35, 170)
YDOWN = 3, "downarrow", (35, 130)
ZUP = 4, "upzarrow", (65, 170)
ZDOWN = 5, "downarrow", (65, 130)
XUP = 0, "upxarrow", (5, 230)
XDOWN = 1, "downarrow", (5, 190)
YUP = 2, "upyarrow", (35, 230)
YDOWN = 3, "downarrow", (35, 190)
ZUP = 4, "upzarrow", (65, 230)
ZDOWN = 5, "downarrow", (65, 190)


class DisplacementArrow(Button):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def __init__(self, plotter_helper: "Plotter", dark_mode: bool = False) -> None:
self._actor: vtkActor = None
self.plotter_helper = plotter_helper
self._button: vtkButtonWidget = self.plotter_helper._pl.scene.add_checkbox_button_widget(
self.callback, position=(10, 60), size=30, border_size=3
self.callback, position=(5, 160), size=30, border_size=3
)
self.update()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def __init__(self, plotter_helper: "Plotter", dark_mode: bool = False) -> None:
self._widget_actor: vtkActor = None
self.plotter_helper = plotter_helper
self._button: vtkButtonWidget = self.plotter_helper._pl.scene.add_checkbox_button_widget(
self.callback, position=(45, 60), size=30, border_size=3
self.callback, position=(37, 160), size=30, border_size=3
)
self._mb = None
self._mesh_actor_list = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def __init__(self, plotter_helper: "Plotter", dark_mode: bool = False) -> None:
self._actor: vtkActor = None
self.plotter_helper = plotter_helper
self._button: vtkButtonWidget = self.plotter_helper._pl.scene.add_checkbox_button_widget(
self.callback, position=(45, 10), size=30, border_size=3
self.callback, position=(37, 128), size=30, border_size=3
)
self.update()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def __init__(self, plotter: Plotter, dark_mode: bool = False) -> None:
# Initialize variables
self._actor: vtkActor = None
self._button: vtkButtonWidget = self.plotter.add_checkbox_button_widget(
self.callback, position=(10, 100), size=30, border_size=3
self.callback, position=(3, 128), size=30, border_size=3
)
self.update()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def __init__(self, plotter: Plotter, dark_mode: bool = False) -> None:
# Initialize variables
self._actor: vtkActor = None
self._button: vtkButtonWidget = self.plotter._pl.scene.add_checkbox_button_widget(
self.callback, position=(45, 100), size=30, border_size=3
self.callback, position=(69, 160), size=30, border_size=3
)
self.update()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@
class ViewDirection(Enum):
"""Provides an enum with the available views."""

XYPLUS = 0, "+xy", (5, 220)
XYMINUS = 1, "-xy", (5, 251)
XZPLUS = 2, "+xz", (5, 282)
XZMINUS = 3, "-xz", (5, 313)
YZPLUS = 4, "+yz", (5, 344)
YZMINUS = 5, "-yz", (5, 375)
ISOMETRIC = 6, "isometric", (5, 406)
XYPLUS = 0, "+xy", (5, 280)
XYMINUS = 1, "-xy", (5, 311)
XZPLUS = 2, "+xz", (5, 342)
XZMINUS = 3, "-xz", (5, 373)
YZPLUS = 4, "+yz", (5, 404)
YZMINUS = 5, "-yz", (5, 435)
ISOMETRIC = 6, "isometric", (5, 466)


class ViewButton(Button):
Expand Down
63 changes: 43 additions & 20 deletions tests/test_interactables.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,13 @@ def test_picking():

# TODO: View and displace arrows tests do not give expected results, PyVista issue?
view = [
(5, 220), # XYPLUS
(5, 251), # XYMINUS
(5, 282), # XZPLUS
(5, 313), # XZMINUS
(5, 344), # YZPLUS
(5, 375), # YZMINUS
(5, 406) # ISOMETRIC
(5, 280), # XYPLUS
(5, 311), # XYMINUS
(5, 342), # XZPLUS
(5, 373), # XZMINUS
(5, 404), # YZPLUS
(5, 435), # YZMINUS
(5, 466) # ISOMETRIC

]

Expand Down Expand Up @@ -107,12 +107,12 @@ def test_view_buttons(view):
raw_plotter.close()

displace_arrow = [
(5, 170), # XUP
(5, 130), # XDOWN
(35, 170), # YUP
(35, 130), # YDOWN
(65, 170), # ZUP
(65, 130) # ZDOWN
(5, 230), # XUP
(5, 190), # XDOWN
(35, 230), # YUP
(35, 190), # YDOWN
(65, 230), # ZUP
(65, 190) # ZDOWN
]

@pytest.mark.parametrize("displace_arrow", displace_arrow)
Expand Down Expand Up @@ -160,7 +160,7 @@ def test_ruler_button():
raw_plotter = pl.backend.scene
width, height = raw_plotter.window_size

raw_plotter.iren._mouse_left_button_click(10, 100)
raw_plotter.iren._mouse_left_button_click(3, 128)
raw_plotter.close()


Expand All @@ -184,7 +184,7 @@ def test_measure_tool():
raw_plotter = pl.backend.scene
width, height = raw_plotter.window_size

raw_plotter.iren._mouse_left_button_click(10, 60)
raw_plotter.iren._mouse_left_button_click(5, 160)
raw_plotter.iren._mouse_left_button_click(200, 200)
raw_plotter.iren._mouse_left_button_click(300, 300)
raw_plotter.close()
Expand All @@ -210,10 +210,10 @@ def test_measure_tool_close():
raw_plotter = pl.backend.scene
width, height = raw_plotter.window_size

raw_plotter.iren._mouse_left_button_click(10, 60)
raw_plotter.iren._mouse_left_button_click(5, 160)
raw_plotter.iren._mouse_left_button_click(200, 200)
raw_plotter.iren._mouse_left_button_click(300, 300)
raw_plotter.iren._mouse_left_button_click(10, 60)
raw_plotter.iren._mouse_left_button_click(5, 160)

raw_plotter.close()

Expand All @@ -238,7 +238,7 @@ def test_screenshot_button():
raw_plotter = pl.backend.scene
width, height = raw_plotter.window_size

raw_plotter.iren._mouse_left_button_click(45, 100)
raw_plotter.iren._mouse_left_button_click(69, 160)

raw_plotter.close()
assert Path("screenshot.png").exists()
Expand Down Expand Up @@ -317,7 +317,7 @@ def test_slicing_tool():
raw_plotter = pl.backend.scene
width, height = raw_plotter.window_size

raw_plotter.iren._mouse_left_button_click(45, 60)
raw_plotter.iren._mouse_left_button_click(37, 160)
raw_plotter.close()

def test_pick_rotation_center():
Expand All @@ -340,7 +340,30 @@ def test_pick_rotation_center():
raw_plotter = pl.backend.scene
width, height = raw_plotter.window_size

raw_plotter.iren._mouse_left_button_click(45, 10)
raw_plotter.iren._mouse_left_button_click(37, 128)
raw_plotter.iren._mouse_right_button_press(width//2, height//2)
raw_plotter.iren._mouse_right_button_release(width//2, height//2)
raw_plotter.close()

def test_dark_mode_button():
"""Test dark mode button interaction."""
pv_backend = PyVistaBackend()

pl = Plotter(backend=pv_backend)

# Create custom sphere
custom_sphere = CustomObject()
custom_sphere.mesh = pv.Sphere(center=(0, 0, 5))
custom_sphere.name = "CustomSphere"
mesh_object_sphere = MeshObjectPlot(custom_sphere, custom_sphere.get_mesh())

pl.plot(mesh_object_sphere)

# Run the plotter and simulate a click
pl.show(auto_close=False)

raw_plotter = pl.backend.scene
width, height = raw_plotter.window_size

raw_plotter.iren._mouse_left_button_click(43, 10)
raw_plotter.close()
Loading