3D transformations for Python with comprehensive rotation representations, coordinate conversions, and visualization tools
—
Interactive GUI tools for visual transformation editing using PyQt with real-time 3D visualization and parameter adjustment capabilities.
Complete GUI application for interactive transformation editing with 3D visualization.
class TransformEditor(QMainWindow):
def __init__(self, transform_manager, base_frame, xlim=(-1.0, 1.0), ylim=(-1.0, 1.0), zlim=(-1.0, 1.0), s=1.0, figsize=(10, 10), dpi=100, window_size=(500, 600), parent=None):
"""
Interactive GUI for editing transformations with 3D visualization.
Parameters:
- transform_manager: TransformManager - Transform manager instance
- base_frame: str - Base reference frame name
- xlim: tuple - X-axis visualization limits
- ylim: tuple - Y-axis visualization limits
- zlim: tuple - Z-axis visualization limits
- s: float - Frame visualization scaling
- figsize: tuple - Matplotlib figure size
- dpi: int - Figure DPI
- window_size: tuple - GUI window size (width, height)
- parent: QWidget, optional - Parent widget
"""
def show(self):
"""Start the GUI application and display editor window."""
# Attributes after completion:
# transform_manager: TransformManager - Updated with all edited transformationsFrame editor widget using Euler angle representation for orientation control.
class PositionEulerEditor(QWidget):
# Signal emitted when frame changes
frameChanged = QtCore.pyqtSignal()
def __init__(self, base_frame, xlim, ylim, zlim, parent=None):
"""
Frame editor using position and Euler angles.
Parameters:
- base_frame: str - Base frame name
- xlim: tuple - X-axis limits for visualization
- ylim: tuple - Y-axis limits for visualization
- zlim: tuple - Z-axis limits for visualization
- parent: QWidget, optional - Parent widget
"""
def set_frame(self, A2B):
"""
Set pose of frame using transformation matrix.
Parameters:
- A2B: array, shape (4, 4) - Transformation matrix
"""Constants indicating PyQt availability and version information.
qt_available: bool # Whether PyQt is available for GUI functionality
qt_version: int | None # PyQt version (4 or 5) if available, None otherwisefrom pytransform3d.editor import TransformEditor, qt_available
from pytransform3d.transform_manager import TransformManager
import pytransform3d.transformations as pt
if qt_available:
# Create transform manager with some initial transformations
tm = TransformManager()
tm.add_transform("base", "link1", pt.transform_from(p=[1, 0, 0]))
tm.add_transform("link1", "link2", pt.transform_from(p=[0, 1, 0]))
tm.add_transform("link2", "end_effector", pt.transform_from(p=[0, 0, 0.5]))
# Create and show editor
app = QApplication.instance()
if app is None:
app = QApplication([])
editor = TransformEditor(tm, "base", xlim=(-2, 2), ylim=(-2, 2), zlim=(-1, 2))
editor.show()
# Run application
app.exec_()
# Access modified transformations
print("Final transformations:")
for from_frame, to_frame in [("base", "link1"), ("link1", "link2"), ("link2", "end_effector")]:
T = tm.get_transform(from_frame, to_frame)
print(f"{from_frame} -> {to_frame}:")
print(f" Position: {T[:3, 3]}")
else:
print("PyQt not available - GUI editor cannot be used")from pytransform3d.editor import TransformEditor, qt_available
from pytransform3d.transform_manager import TransformManager
import pytransform3d.transformations as pt
import pytransform3d.rotations as pr
import numpy as np
if qt_available:
# Create robot arm
tm = TransformManager()
# Base
tm.add_transform("world", "base", pt.transform_from(p=[0, 0, 0.1]))
# Shoulder joint
tm.add_transform("base", "shoulder", pt.transform_from(p=[0, 0, 0.2]))
# Upper arm
tm.add_transform("shoulder", "upper_arm", pt.transform_from(p=[0.3, 0, 0]))
# Elbow joint
R_elbow = pr.matrix_from_euler([0, -np.pi/4, 0], "xyz", extrinsic=True)
tm.add_transform("upper_arm", "elbow", pt.transform_from(R=R_elbow))
# Forearm
tm.add_transform("elbow", "forearm", pt.transform_from(p=[0.25, 0, 0]))
# Wrist
tm.add_transform("forearm", "wrist", pt.transform_from(p=[0.1, 0, 0]))
# Hand
tm.add_transform("wrist", "hand", pt.transform_from(p=[0.05, 0, 0]))
# Launch editor
app = QApplication.instance()
if app is None:
app = QApplication([])
# Configure editor for robot workspace
editor = TransformEditor(
transform_manager=tm,
base_frame="world",
xlim=(-0.8, 0.8),
ylim=(-0.8, 0.8),
zlim=(0, 1.0),
s=0.1, # Smaller frame visualization
figsize=(12, 8),
window_size=(800, 600)
)
editor.show()
app.exec_()
# Print final robot configuration
print("\nFinal robot configuration:")
print(f"Hand position: {tm.get_transform('world', 'hand')[:3, 3]}")
else:
print("PyQt not available - install PyQt5 or PyQt6 to use GUI editor")from pytransform3d.editor import PositionEulerEditor, qt_available
import pytransform3d.transformations as pt
import pytransform3d.rotations as pr
import numpy as np
if qt_available:
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
from PyQt5.QtCore import pyqtSlot
class CustomFrameEditor(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Custom Frame Editor")
# Create central widget
central_widget = QWidget()
self.setCentralWidget(central_widget)
layout = QVBoxLayout(central_widget)
# Create frame editor
self.frame_editor = PositionEulerEditor(
base_frame="world",
xlim=(-2, 2),
ylim=(-2, 2),
zlim=(-2, 2)
)
# Connect signal
self.frame_editor.frameChanged.connect(self.on_frame_changed)
layout.addWidget(self.frame_editor)
# Set initial frame
initial_transform = pt.transform_from(p=[1, 0, 0.5])
self.frame_editor.set_frame(initial_transform)
@pyqtSlot()
def on_frame_changed(self):
"""Handle frame change event."""
print("Frame was modified by user!")
# Access current frame state from editor
# (Implementation depends on specific editor internals)
# Run custom editor
app = QApplication.instance()
if app is None:
app = QApplication([])
editor = CustomFrameEditor()
editor.show()
app.exec_()
else:
print("PyQt not available")from pytransform3d.editor import PositionEulerEditor, qt_available
import pytransform3d.transformations as pt
import pytransform3d.rotations as pr
import numpy as np
if qt_available:
from PyQt5.QtWidgets import QApplication
# Create application
app = QApplication.instance()
if app is None:
app = QApplication([])
# Create editor widget
editor = PositionEulerEditor(
base_frame="base",
xlim=(-3, 3),
ylim=(-3, 3),
zlim=(-1, 3)
)
# Set various frame poses programmatically
poses = [
pt.transform_from(p=[1, 0, 0]),
pt.transform_from(p=[0, 1, 0]),
pt.transform_from(p=[0, 0, 1]),
pt.transform_from(
R=pr.matrix_from_euler([0, 0, np.pi/4], "xyz", extrinsic=True),
p=[1, 1, 0]
)
]
# Demonstrate setting different frames
for i, pose in enumerate(poses):
print(f"Setting pose {i+1}: position = {pose[:3, 3]}")
editor.set_frame(pose)
# Show editor briefly (in real app, user would interact)
editor.show()
app.processEvents() # Process GUI events
# In practice, you would wait for user interaction
# or use signals/slots to handle frame changes
print("Frame editing demonstration complete")
else:
print("PyQt not available - cannot demonstrate frame editor")from pytransform3d.editor import qt_available, qt_version
print(f"PyQt available: {qt_available}")
if qt_available:
print(f"PyQt version: {qt_version}")
print("GUI editor functionality is available")
# Import GUI classes safely
from pytransform3d.editor import TransformEditor, PositionEulerEditor
print("Successfully imported GUI classes")
else:
print("PyQt not available")
print("Install PyQt5 or PyQt6 to enable GUI functionality:")
print(" pip install PyQt5")
print(" or")
print(" pip install PyQt6")
# Fallback to non-GUI alternatives
print("Using matplotlib-based visualization instead...")
# Use standard matplotlib plotting functionsThe interactive editor provides:
The editor is particularly useful for:
Install with Tessl CLI
npx tessl i tessl/pypi-pytransform3d