0
# Editor
1
2
Interactive GUI tools for visual transformation editing using PyQt with real-time 3D visualization and parameter adjustment capabilities.
3
4
## Capabilities
5
6
### TransformEditor Class
7
8
Complete GUI application for interactive transformation editing with 3D visualization.
9
10
```python { .api }
11
class TransformEditor(QMainWindow):
12
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):
13
"""
14
Interactive GUI for editing transformations with 3D visualization.
15
16
Parameters:
17
- transform_manager: TransformManager - Transform manager instance
18
- base_frame: str - Base reference frame name
19
- xlim: tuple - X-axis visualization limits
20
- ylim: tuple - Y-axis visualization limits
21
- zlim: tuple - Z-axis visualization limits
22
- s: float - Frame visualization scaling
23
- figsize: tuple - Matplotlib figure size
24
- dpi: int - Figure DPI
25
- window_size: tuple - GUI window size (width, height)
26
- parent: QWidget, optional - Parent widget
27
"""
28
29
def show(self):
30
"""Start the GUI application and display editor window."""
31
32
# Attributes after completion:
33
# transform_manager: TransformManager - Updated with all edited transformations
34
```
35
36
### PositionEulerEditor Class
37
38
Frame editor widget using Euler angle representation for orientation control.
39
40
```python { .api }
41
class PositionEulerEditor(QWidget):
42
# Signal emitted when frame changes
43
frameChanged = QtCore.pyqtSignal()
44
45
def __init__(self, base_frame, xlim, ylim, zlim, parent=None):
46
"""
47
Frame editor using position and Euler angles.
48
49
Parameters:
50
- base_frame: str - Base frame name
51
- xlim: tuple - X-axis limits for visualization
52
- ylim: tuple - Y-axis limits for visualization
53
- zlim: tuple - Z-axis limits for visualization
54
- parent: QWidget, optional - Parent widget
55
"""
56
57
def set_frame(self, A2B):
58
"""
59
Set pose of frame using transformation matrix.
60
61
Parameters:
62
- A2B: array, shape (4, 4) - Transformation matrix
63
"""
64
```
65
66
### GUI Availability
67
68
Constants indicating PyQt availability and version information.
69
70
```python { .api }
71
qt_available: bool # Whether PyQt is available for GUI functionality
72
qt_version: int | None # PyQt version (4 or 5) if available, None otherwise
73
```
74
75
## Usage Examples
76
77
### Basic Transform Editor
78
79
```python
80
from pytransform3d.editor import TransformEditor, qt_available
81
from pytransform3d.transform_manager import TransformManager
82
import pytransform3d.transformations as pt
83
84
if qt_available:
85
# Create transform manager with some initial transformations
86
tm = TransformManager()
87
tm.add_transform("base", "link1", pt.transform_from(p=[1, 0, 0]))
88
tm.add_transform("link1", "link2", pt.transform_from(p=[0, 1, 0]))
89
tm.add_transform("link2", "end_effector", pt.transform_from(p=[0, 0, 0.5]))
90
91
# Create and show editor
92
app = QApplication.instance()
93
if app is None:
94
app = QApplication([])
95
96
editor = TransformEditor(tm, "base", xlim=(-2, 2), ylim=(-2, 2), zlim=(-1, 2))
97
editor.show()
98
99
# Run application
100
app.exec_()
101
102
# Access modified transformations
103
print("Final transformations:")
104
for from_frame, to_frame in [("base", "link1"), ("link1", "link2"), ("link2", "end_effector")]:
105
T = tm.get_transform(from_frame, to_frame)
106
print(f"{from_frame} -> {to_frame}:")
107
print(f" Position: {T[:3, 3]}")
108
109
else:
110
print("PyQt not available - GUI editor cannot be used")
111
```
112
113
### Robot Joint Editor
114
115
```python
116
from pytransform3d.editor import TransformEditor, qt_available
117
from pytransform3d.transform_manager import TransformManager
118
import pytransform3d.transformations as pt
119
import pytransform3d.rotations as pr
120
import numpy as np
121
122
if qt_available:
123
# Create robot arm
124
tm = TransformManager()
125
126
# Base
127
tm.add_transform("world", "base", pt.transform_from(p=[0, 0, 0.1]))
128
129
# Shoulder joint
130
tm.add_transform("base", "shoulder", pt.transform_from(p=[0, 0, 0.2]))
131
132
# Upper arm
133
tm.add_transform("shoulder", "upper_arm", pt.transform_from(p=[0.3, 0, 0]))
134
135
# Elbow joint
136
R_elbow = pr.matrix_from_euler([0, -np.pi/4, 0], "xyz", extrinsic=True)
137
tm.add_transform("upper_arm", "elbow", pt.transform_from(R=R_elbow))
138
139
# Forearm
140
tm.add_transform("elbow", "forearm", pt.transform_from(p=[0.25, 0, 0]))
141
142
# Wrist
143
tm.add_transform("forearm", "wrist", pt.transform_from(p=[0.1, 0, 0]))
144
145
# Hand
146
tm.add_transform("wrist", "hand", pt.transform_from(p=[0.05, 0, 0]))
147
148
# Launch editor
149
app = QApplication.instance()
150
if app is None:
151
app = QApplication([])
152
153
# Configure editor for robot workspace
154
editor = TransformEditor(
155
transform_manager=tm,
156
base_frame="world",
157
xlim=(-0.8, 0.8),
158
ylim=(-0.8, 0.8),
159
zlim=(0, 1.0),
160
s=0.1, # Smaller frame visualization
161
figsize=(12, 8),
162
window_size=(800, 600)
163
)
164
165
editor.show()
166
app.exec_()
167
168
# Print final robot configuration
169
print("\nFinal robot configuration:")
170
print(f"Hand position: {tm.get_transform('world', 'hand')[:3, 3]}")
171
172
else:
173
print("PyQt not available - install PyQt5 or PyQt6 to use GUI editor")
174
```
175
176
### Custom Frame Editor
177
178
```python
179
from pytransform3d.editor import PositionEulerEditor, qt_available
180
import pytransform3d.transformations as pt
181
import pytransform3d.rotations as pr
182
import numpy as np
183
184
if qt_available:
185
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
186
from PyQt5.QtCore import pyqtSlot
187
188
class CustomFrameEditor(QMainWindow):
189
def __init__(self):
190
super().__init__()
191
self.setWindowTitle("Custom Frame Editor")
192
193
# Create central widget
194
central_widget = QWidget()
195
self.setCentralWidget(central_widget)
196
layout = QVBoxLayout(central_widget)
197
198
# Create frame editor
199
self.frame_editor = PositionEulerEditor(
200
base_frame="world",
201
xlim=(-2, 2),
202
ylim=(-2, 2),
203
zlim=(-2, 2)
204
)
205
206
# Connect signal
207
self.frame_editor.frameChanged.connect(self.on_frame_changed)
208
209
layout.addWidget(self.frame_editor)
210
211
# Set initial frame
212
initial_transform = pt.transform_from(p=[1, 0, 0.5])
213
self.frame_editor.set_frame(initial_transform)
214
215
@pyqtSlot()
216
def on_frame_changed(self):
217
"""Handle frame change event."""
218
print("Frame was modified by user!")
219
# Access current frame state from editor
220
# (Implementation depends on specific editor internals)
221
222
# Run custom editor
223
app = QApplication.instance()
224
if app is None:
225
app = QApplication([])
226
227
editor = CustomFrameEditor()
228
editor.show()
229
app.exec_()
230
231
else:
232
print("PyQt not available")
233
```
234
235
### Programmatic Frame Editing
236
237
```python
238
from pytransform3d.editor import PositionEulerEditor, qt_available
239
import pytransform3d.transformations as pt
240
import pytransform3d.rotations as pr
241
import numpy as np
242
243
if qt_available:
244
from PyQt5.QtWidgets import QApplication
245
246
# Create application
247
app = QApplication.instance()
248
if app is None:
249
app = QApplication([])
250
251
# Create editor widget
252
editor = PositionEulerEditor(
253
base_frame="base",
254
xlim=(-3, 3),
255
ylim=(-3, 3),
256
zlim=(-1, 3)
257
)
258
259
# Set various frame poses programmatically
260
poses = [
261
pt.transform_from(p=[1, 0, 0]),
262
pt.transform_from(p=[0, 1, 0]),
263
pt.transform_from(p=[0, 0, 1]),
264
pt.transform_from(
265
R=pr.matrix_from_euler([0, 0, np.pi/4], "xyz", extrinsic=True),
266
p=[1, 1, 0]
267
)
268
]
269
270
# Demonstrate setting different frames
271
for i, pose in enumerate(poses):
272
print(f"Setting pose {i+1}: position = {pose[:3, 3]}")
273
editor.set_frame(pose)
274
275
# Show editor briefly (in real app, user would interact)
276
editor.show()
277
app.processEvents() # Process GUI events
278
279
# In practice, you would wait for user interaction
280
# or use signals/slots to handle frame changes
281
282
print("Frame editing demonstration complete")
283
284
else:
285
print("PyQt not available - cannot demonstrate frame editor")
286
```
287
288
### Checking GUI Availability
289
290
```python
291
from pytransform3d.editor import qt_available, qt_version
292
293
print(f"PyQt available: {qt_available}")
294
if qt_available:
295
print(f"PyQt version: {qt_version}")
296
print("GUI editor functionality is available")
297
298
# Import GUI classes safely
299
from pytransform3d.editor import TransformEditor, PositionEulerEditor
300
print("Successfully imported GUI classes")
301
302
else:
303
print("PyQt not available")
304
print("Install PyQt5 or PyQt6 to enable GUI functionality:")
305
print(" pip install PyQt5")
306
print(" or")
307
print(" pip install PyQt6")
308
309
# Fallback to non-GUI alternatives
310
print("Using matplotlib-based visualization instead...")
311
# Use standard matplotlib plotting functions
312
```
313
314
## Features
315
316
The interactive editor provides:
317
318
- **Real-time 3D visualization** of transformation changes
319
- **Slider controls** for position and orientation parameters
320
- **Euler angle representation** for intuitive rotation control
321
- **Live update** of transformation matrix display
322
- **Multi-frame editing** capability within transform manager
323
- **Coordinate frame visualization** with customizable scaling
324
- **Configurable workspace limits** for visualization bounds
325
- **Integration with transform manager** for complex kinematic chains
326
327
The editor is particularly useful for:
328
329
- **Robot joint configuration** and workspace exploration
330
- **Camera pose adjustment** for computer vision applications
331
- **Sensor calibration** and mounting parameter optimization
332
- **Interactive debugging** of transformation chains
333
- **Educational visualization** of 3D transformations
334
- **Rapid prototyping** of spatial relationships