4.14. GUI Automation¶
Some exemplary automation tasks include:
changing the geometry
changing camera views accurately
displaying specific rays
picking specific rays
debugging
taking automated screenshots of the scene
calling functions with settings not available through the GUI
…
4.14.1. The Control Method¶
To do automated tasks, the TraceGUI.control
method needs to be used instead of TraceGUI.run
.
It requires an automation function as well as an argument tuple.
def automated(GUI):
# do some automation things
...
# create the GUI and provide the automation function to TraceGUI.control()
sim = TraceGUI(RT)
sim.control(func=automated, args=(sim,))
After the control function is executed the program is kept running. This makes it possible to interact with the GUI. Closing the application automatically is described in Section 4.14.10
To avoid race conditions, all actions inside the provided automation function are run sequentially. During this time, the GUI is unresponsive, as the automation function is run in the main thread (as user input would also be) so the interaction with the scene in this time will be limited.
4.14.2. Applying Properties¶
Available TraceGUI properties are discussed in Section 4.13.3. All these can be set programmatically.
While the variables are set, the TraceGUI does not execute the functions or actions that react to these changes automatically.
This is in contrast to the standard TraceGUI.run
method,
where a setting is applied subsequently (for instance, changing the ray color setting updates the color in the scene automatically).
To process all pending events TraceGUI.process
must be called.
This is not needed after every property change, but only if the changes should be visible/executed at this point.
def automated(GUI):
# change properties
GUI.minimalistic_view = True
GUI.hide_labels = True
GUI.ray_count = 1000
# GUI properties were set, but the changes need to be processed
GUI.process()
# dome some other things
...
# create the GUI and provide the automation function to TraceGUI.control()
sim = TraceGUI(RT)
sim.control(func=automated, args=(sim,))
Note that some functions, like TraceGUI.replot
also call TraceGUI.process
internally.
4.14.3. Replotting¶
While TraceGUI.process
reacts to changes in the TraceGUI itself, it does not handle changes of the raytracer or tracing geometry.
When changing the geometry, the changes are not automatically applied to the scene. The geometry is also not automatically raytraced.
To force the redrawing and retracing of the full scene, you can call TraceGUI.replot
.
With the context manager TraceGUI.smart_replot
it is possible to only update changed objects.
For instance, if a detector is moved, there is no need for updating the lenses inside the geometry or retracing the scene.
TraceGUI.smart_replot
handles the detection of changes and updating automatically.
Here is an example:
def automated(GUI):
# replot everything
GUI.replot()
# do some actions and at the end replot only changed objects
# and/or retrace the geometry if needed.
with GUI.smart_replot():
some_action_1()
# create the GUI and provide the automation function to TraceGUI.control()
sim = TraceGUI(RT)
sim.control(func=automated, args=(sim,))
When controlling the TraceGUI through the CommandWindow of the GUI, there is also the option to replot all objects automatically.
The implementation is done internally in the same way by using TraceGUI.smart_replot
.
4.14.4. Controlling the Camera¶
Controlling the camera is done with the functions TraceGUI.set_camera
and TraceGUI.get_camera
.
The former sets the properties, while the latter one returns a dictionary of the current settings.
The following settings are available:
Property |
Description |
---|---|
|
3D coordinates of center of view in mm |
|
half of vertical visible scene height in mm |
|
camera view direction vector (direction of vector perpendicular to your monitor and in your viewing direction) |
|
absolute camera roll angle in degrees |
You can find example code below:
def automated(sim):
# store initial camera properties
cam_props = sim.get_camera()
# change the center of the view as well as the scaling
sim.set_camera(center=[1, -0.5, 2], height=2.5)
# reset to initial view
sim.set_camera(**cam_props)
# create the GUI and provide the automation function to TraceGUI.control()
sim = TraceGUI(RT)
sim.control(func=automated, args=(sim,))
Applying camera properties at startup is possible using the initial_camera
parameter of the TraceGUI class.
This parameter is a dictionary that can include all possible parameters of function TraceGUI.set_camera
.
sim = TraceGUI(RT, initial_camera=dict(direction=[0, 1, 0], roll=45))
4.14.5. Taking Screenshots¶
The TraceGUI.screenshot
function make it possible to capture screenshots of the scene.
A path string is required for this function.
The file type is determined automatically from the file name.
Internally, the mayavi.mlab.savefig
function from mayavi is utilized, therefore supporting this function’s additional parameters.
def automated(sim):
# default call
sim.screenshot("image.png")
# call with additional parameters
sim.screenshot("image2.png", magnification=2)
# create the GUI and provide the automation function to TraceGUI.control()
sim = TraceGUI(RT)
sim.control(func=automated, args=(sim,))
Note that the magnification
parameter leads to a rescaled scene, where some elements change their relative size.
4.14.6. Selecting Rays¶
By default, a random selection of rays is displayed inside the scene where the number is specified by TraceGUI.rays_visible
.
A custom selection can be set using the function TraceGUI.select_rays
.
It takes a mask
parameter, which is a one-dimensional boolean numpy.array
, and an optional max_show
parameter, that specified the maximum amount of rays to display.
Parameter mask
must have the same length as there are rays simulated, which is set by TraceGUI.ray_count
Note that there is a maximum amount of rays that can be displayed (specified by the maximum value of TraceGUI.rays_visible
, by default 50000
).
If the mask
includes more values, a random subset is selected.
Accessing TraceGUI.ray_selection
returns the boolean array for the currently displayed rays.
Typical useful scenarios are debugging or ray analysis. For instance, only rays from a specific source, region or wavelength range can be selected and displayed. See Accessing Ray Properties to learn how to access ray properties. You can find examples for ray selections below.
def automated(GUI):
# display rays with wavelengths between 400 and 450nm
mask = (GUI.raytracer.rays.wl_list >= 400) & (GUI.raytracer.rays.wl_list <= 450)
GUI.select_rays(mask) # no max_show provided, but might be limited by this function
# display 2000 rays that start at x > 0
mask = GUI.raytracer.rays.p_list[:, :, 0] > 0
GUI.select_rays(mask[:, 0], 2000) # slicing with 0 so mask is 1D
# get mask for actually displayed selection
selection = GUI.ray_selection
# create the GUI and provide the automation function to TraceGUI.control()
sim = TraceGUI(RT)
sim.control(func=automated, args=(sim,))
4.14.7. Picking Manually¶
The function TraceGUI.pick_ray
highlights a full ray.
An integer index
is required as to select a given ray.
Only currently displayed rays are pickable, which are defined by TraceGUI.ray_selection
, see Selecting Rays.
So an index=50
means that the 50th True
value of TraceGUI.ray_selection
is picked.
Function TraceGUI.pick_ray_section
highlights a ray at a given intersection.
The ray is highlighted, a crosshair is shown at the intersection position and a ray information text is shown inside the scene.
Compared to the previous function, an additional integer section
parameter is needed.
An optional parameter detailed
defines if more detailed information should be shown.
This would be equivalent to picking a section manually in the scene with the Shift key held.
To deactivate the ray highlighting, information text and cross hair, TraceGUI.reset_picking
needs to be called.
Here is an example:
def automated(sim):
# pick the ray with index 100
sim.pick_ray(index=100)
# pick ray section 2 of ray 50 with default view
sim.pick_ray_section(index=50, section=2)
# pick ray section with detailed view
sim.pick_ray_section(index=50, section=2, detailed=True)
# reset (=hide) the picking view
sim.reset_picking()
# create the GUI and provide the automation function to TraceGUI.control()
sim = TraceGUI(RT)
sim.control(func=automated, args=(sim,))
4.14.8. Showing Plots¶
Available plotting functions include TraceGUI.source_image
, TraceGUI.source_profile
,
TraceGUI.detector_image
, TraceGUI.detector_profile
,
TraceGUI.detector_spectrum
, TraceGUI.source_spectrum
,
TraceGUI.move_to_focus
.
There are more settings available than through the GUI. For example, it is possible to save a image to the disk. Additionally, a custom detector/source extent can be specified, a setting not available through the GUI.
def automated(sim):
# change plot settings
sim.image_pixels = 315
sim.image_mode = "Lightness (CIELUV)"
# show a source profile with a user-defined extent
sim.source_profile(extent=[0, 0.1, 0.2, 0.25])
# save a detector image with higher dpi
sim.detector_image(path="detector.png", sargs=(dpi=600))
# example for an automated focus plots
sim.detector_index = 1
sim.source_index = 0
sim.cost_function_plot = True
sim.move_to_focus()
# create the GUI and provide the automation function to TraceGUI.control()
sim = TraceGUI(RT)
sim.control(func=automated, args=(sim,))
4.14.9. Accessing custom UI elements¶
Custom elements are accessible through a name, consisting of their type and a chronological number. The number corresponds to the order that the element has been created. Assigning values works analogously to all other parameters. To button action is called with a special function.
You can find examples below.
sim.custom_value_2 = 4.5
sim.custom_checkbox_1 = False
sim.custom_selection_3 = "Case 2"
sim.custom_button_action_1()
4.14.10. Closing Down¶
To close the GUI down programmatically, the function TraceGUI.close
can be called:
def automated(sim):
# do some things
...
# close everything down
sim.close()
# create the GUI and provide the automation function to TraceGUI.control()
sim = TraceGUI(RT)
sim.control(func=automated, args=(sim,))
This will close all GUI and plotting windows and exit all background tasks.