-
Notifications
You must be signed in to change notification settings - Fork 30
Speed setting, mouse stage/beam control, and remote cameras #151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
stefsmeets
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, I like the ModuleFrameMixin better than HasQMixin. I intended to have some modules be more 'generic' like 'io', so they were available everywhere. Other experimental modules were more specific, so they did not need to interact with each other. It's cool to see this system could be re-used for your purpose.
I left some small comments in the PR.
src/instamatic/gui/ctrl_frame.py
Outdated
| cam_dim_x, cam_dim_y = self.ctrl.cam.get_camera_dimensions() | ||
| pixel_delta = np.array([click.y - cam_dim_y / 2, click.x - cam_dim_x / 2]) | ||
| stage_shift = np.dot(pixel_delta, stage_matrix) | ||
| x, y = self.ctrl.stage.xy | ||
| self.ctrl.stage.set(x=x + stage_shift[0], y=y + stage_shift[1]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this can refactored to make it available as a method on stage, something like:
(not entirely sure about the design here)
| cam_dim_x, cam_dim_y = self.ctrl.cam.get_camera_dimensions() | |
| pixel_delta = np.array([click.y - cam_dim_y / 2, click.x - cam_dim_x / 2]) | |
| stage_shift = np.dot(pixel_delta, stage_matrix) | |
| x, y = self.ctrl.stage.xy | |
| self.ctrl.stage.set(x=x + stage_shift[0], y=y + stage_shift[1]) | |
| dimensions = self.ctrl.cam.get_camera_dimensions() | |
| self.ctrl.stage.move_to_pixel(click.x, click.y, matrix=stage_matrix, dimensions=dimensions) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I honestly wrote an implementation of a move method, because yeah, it would have its place in the codebase:
def move(
self,
delta_x: Optional[int_nm] = None,
delta_y: Optional[int_nm] = None,
delta_z: Optional[int_nm] = None,
delta_a: Optional[float_deg] = None,
delta_b: Optional[float_deg] = None,
wait: bool = True,
speed: Optional[float] = None,
) -> None:
"""Move the stage by given values relative to its current position."""
x, y, z, a, b = self.get()
kwargs = {
'x': None if delta_x is None else x + delta_x,
'y': None if delta_y is None else y + delta_y,
'z': None if delta_z is None else z + delta_z,
'a': None if delta_a is None else a + delta_a,
'b': None if delta_b is None else b + delta_b,
'wait': wait
}
if speed is not None:
kwargs['speed'] = speed
self.set(**kwargs)...but then I realized that what would be actually much more intuitive in real conditions would actually be not set nor move but move_in_projection, and it is already implemented in stage... and it does make code shorter.
As for self.ctrl.stage.move_to_pixel... I am not sure. I am not certain it is necessary, and with the current local implementation taking only 4 lines... Maybe it is? It may get rid of some repetition? I do not have enough conviction to run a deep dive investigation right now, maybe it will resurface in the future.
Co-authored-by: Stef Smeets <stefsmeets@users.noreply.github.com>
Co-authored-by: Stef Smeets <stefsmeets@users.noreply.github.com>
|
I admit I have not fully tested the code in vivo yet, once I do I will 🚀 this very message and likely merge around Wednesday. |
|
I tested this branch today alongside another one. In the process I realized, that the second branch is very small and complementary. Therefore, I added a few tiny changes here, as documented in "EDIT" above:
Controlling the stage and beam using the mouse have ultimately proved to be non-essential for "fixing" cRED for FEI machines, but all in all I believe they are valuable addition to the control panel. Consequently, I have generalized the name of this thread. I am happy to report that with all modifications introduced here:
Intending to merge on Wednesday. |
The cRED protocol introduced in 2017 was the first to allow tracking crystals during an experiment. This is done manually, by collecting a de-focused image every N frames and allowing the user to shift the beam accordingly. On JEOL machines, this can be done with a trackball; however, two sources of input can cause communication issues on FEI. Consequently, this PR introduces two additional switches in Instamatic "control" tab: when toggled, 'Move stage with LMB' and 'Move beam with RMB' bind left and right mouse buttons to stage and beamshift controls, allowing control over these motors/deflectors via the stream window:
To allow "control" module to move the beam, I needed it to have access to the beamshift calibration, which required it to access the "io" module associated with the frame at the top of the window that handles paths. This is because all experiment types save beamshift calibration in the current experiment directory. With the current design, this is impossible: the only part of instamatic with access to all modules is the central program
controller(via theAppLoader) responsible for running "tasks".In general, I noticed that often I want module X to have access to some data in module Y. It felt strange to me that there is no mechanism to do it. However, in #147, I modified the
AppLoaderto give all modules access to the taskq. Here, I decided to do the same forapp: by changing 2-lines,self.appnow becomes a class-level attribute for all loaded modules:This way, every module can call
self.app.get_moduleto access another module. This is immensely powerful as it enables modules to communicate and complement. For example, in this PR, "control" module usesself.app.get_module('stream').click_dispatcherto move stage/beam via clicks andself.app.get_module('io').get_experiment_directory()to find beamshift calibration.In addition to changes listed above, this PR suggests a few smaller related modifications:
ENABLE_FOOTFREE_OPTIONby default to True – otherwise cRED just does not work unless you have a foot pedal, which it certainly not the norm.HasQMixintoModuleFrameMixinto reflect its extended role – in future I would like to rework the module/job system in general, as preparing this PR made me realize it has an immense potential to allow external modules while lowering boilerplate and code complexity, plus currentinstamatic.guifolder is frankly quite a mess right now.Edit: since these seemed related and few for a separate PR, I also added the following changes:
CamClient._eval_dict: fixes errors that may occur when running experiments using a streamable camera via server (which by the way is now completely possible and decently fast);ExperimentalCtrl.var_goniotool_txfromIntVartoDoubleVar: necessary to set rotation speeds on a non-Jeol machine (for Jeol, added a server-side convertion of speed back toint);instamatic/server/cam_server.pyimports, doc-strings: can be run as a script again.