TEM3: event tutorial¶
This tutorial covers the TEM3 event mechanism, including:
Basics of EventManager and EventKind
Event queue control (enable/disable, size, retrieval)
Callback registration and deregistration
Real-machine event monitoring (optional)
Note
Operations involving actual instrument control must follow your facility’s operating rules.
The first part of this tutorial uses simulation (put_event) and can be explored safely.
1from datetime import datetime, timedelta
2import time
3
4from PyJEM import TEM3
1. Checking EventManager and Event Types¶
Use TEM3.EventManager() to obtain the event management object.
queue_info()¶
queue_info() returns the queue state of all events as a dictionary. Each entry contains the following fields:
Field |
Description |
|---|---|
|
Size of data stored in the queue |
|
Maximum number of events the queue can hold |
|
Whether the event is enabled |
TEM3.EventKind¶
Available event types are enumerated in TEM3.EventKind. Representative examples are shown below.
Event Name |
Description |
|---|---|
|
Magnification change |
|
Function mode change |
|
TEM/STEM mode change |
|
Stage X/Y/Z position change |
|
Stage tilt change |
|
Defocus change |
1# 1) Create event manager and inspect available events
2em = TEM3.EventManager()
3
4print("Event queue summary (first 8):")
5info = em.queue_info()
6for i, (name, meta) in enumerate(info.items()):
7 if i >= 8:
8 break
9 print(f"{name}: size={meta['datasize']} maxsize={meta['maxsize']} enabled={meta['enabled']}")
10
11print("\nSample event names:")
12print([e.name for e in list(TEM3.EventKind)[:10]])
Event queue summary (first 8):
ChangeSpectrumMode: size=0 maxsize=10 enabled=True
ChangedAcdHeatEnd: size=0 maxsize=10 enabled=True
ChangedAcdHeatStart: size=0 maxsize=10 enabled=True
ChangedBakeOutEnd: size=0 maxsize=10 enabled=True
ChangedBakeOutStart: size=0 maxsize=10 enabled=True
ChangedGIFMode: size=0 maxsize=10 enabled=True
ChangedHomePositionOfDeflector: size=0 maxsize=10 enabled=True
ChangedRelativeValueOfDeflector: size=0 maxsize=10 enabled=True
Sample event names:
['ChangeSpectrumMode', 'ChangedAcdHeatEnd', 'ChangedAcdHeatStart', 'ChangedBakeOutEnd', 'ChangedBakeOutStart', 'ChangedGIFMode', 'ChangedHomePositionOfDeflector', 'ChangedRelativeValueOfDeflector', 'ChangedSaafLimitStatus', 'ChangedSaafMode']
2. Registering Callbacks¶
Register, list, and deregister functions to be called when an event occurs.
The following cell demonstrates the full cycle of registering, invoking, and deregistering a callback for the ChgMag event.
Operation |
Method |
Description |
|---|---|---|
Register |
|
Register a callback function with a key |
List |
|
Retrieve the list of registered callbacks |
Deregister |
|
Remove a callback by key |
1event_name = TEM3.EventKind.ChgMag.name
2
3def on_chg_mag(*args):
4 print("[callback] ChgMag received:", args)
5
6# Register callback
7em.regist_method(event_name, on_chg_mag, "demo_chg_mag")
8print("registered callbacks:", len(em.method_list(event_name)))
9
10# Simulate callback dispatch for tutorial purpose
11for cb in em.method_list(event_name):
12 cb({"from": "tutorial", "at": datetime.now().isoformat()})
13
14# Unregister callback
15em.unregist_method(event_name, "demo_chg_mag")
16print("callbacks after unregist:", len(em.method_list(event_name)))
registered callbacks: 1
[callback] ChgMag received: ({'from': 'tutorial', 'at': '2026-03-23T14:47:27.028220'},)
callbacks after unregist: 0
3. Monitoring Real-Machine Events (Hardware Required)¶
This section requires a live TEM3 connection.
Once connected, changing magnification or other settings on the instrument will enqueue the corresponding events.
The following cell connects to TEM3 and monitors multiple events for 20 seconds.
Operating the instrument (magnification, stage, mode, etc.) will record the corresponding events in the queue.
Events monitored:
Event Name |
Triggering Operation |
|---|---|
|
Magnification change |
|
Function mode change |
|
TEM/STEM mode switch |
|
Stage position change |
|
Stage tilt change |
|
Defocus change |
After monitoring ends, a summary of total event count, per-type counts, and a timeline of the first events is printed.
1# Real-machine event monitor: log what happened
2from collections import Counter
3
4connected = TEM3.connect()
5print("TEM3 connected:", connected)
6
7if not connected:
8 print("Skip monitoring because TEM3 is not connected.")
9else:
10 em = TEM3.EventManager()
11
12 # Add or remove events to match your operation target
13 watch_events = [
14 TEM3.EventKind.ChgMag.name,
15 TEM3.EventKind.ChgFunctionMode.name,
16 TEM3.EventKind.ChangedTemStemMode.name,
17 TEM3.EventKind.ChgStageX.name,
18 TEM3.EventKind.ChgStageY.name,
19 TEM3.EventKind.ChgStageZ.name,
20 TEM3.EventKind.ChgStageTX.name,
21 TEM3.EventKind.ChgStageTY.name,
22 TEM3.EventKind.ChgDefocus.name,
23 ]
24
25 for ev_name in watch_events:
26 em.set_enabled(ev_name, True)
27 em.set_maxsize(ev_name, 100)
28
29 monitor_seconds = 20
30 print(f"Monitoring {len(watch_events)} events for {monitor_seconds} seconds...")
31 print("Operate TEM now (change mag/stage/mode, etc.).")
32
33 end = datetime.now() + timedelta(seconds=monitor_seconds)
34 event_log = []
35
36 while datetime.now() < end:
37 for ev_name in watch_events:
38 ev = em.get_event(ev_name)
39 if ev is not None:
40 event_log.append(ev)
41 print(f"[{ev.time}] {ev.name}: {ev.data}")
42 time.sleep(0.1)
43
44 print("\n--- Summary ---")
45 print("total events:", len(event_log))
46
47 if event_log:
48 counts = Counter(e.name for e in event_log)
49 print("event counts:")
50 for name, count in counts.most_common():
51 print(f" {name}: {count}")
52
53 print("\nfirst 10 events (timeline):")
54 for e in event_log[:10]:
55 print(f" {e.time} | {e.name} | {e.data}")
56 else:
57 print("No events captured.")
TEM3 connected: True
Monitoring 9 events for 20 seconds...
Operate TEM now (change mag/stage/mode, etc.).
[2026-04-03 15:37:08.120401] ChgMag: (2000000, 'X', 'x2.0M')
[2026-04-03 15:40:59.074381] ChgFunctionMode: (2, 'SMMAG')
[2026-04-03 15:37:09.459789] ChgMag: (2500000, 'X', 'x2.5M')
[2026-04-03 15:41:00.419068] ChgFunctionMode: (2, 'SMMAG')
[2026-04-03 15:37:13.635347] ChgMag: (2000000, 'X', 'x2.0M')
[2026-04-03 15:41:01.972621] ChgFunctionMode: (2, 'SMMAG')
[2026-04-03 15:37:15.454976] ChgMag: (1500000, 'X', 'x1.5M')
[2026-04-03 15:41:03.532489] ChgFunctionMode: (2, 'SMMAG')
[2026-04-03 15:40:51.466562] ChgMag: (20000, 'X', 'x20k')
[2026-04-03 15:41:05.061735] ChgFunctionMode: (2, 'SMMAG')
[2026-04-03 15:40:51.950044] ChgMag: (25000, 'X', 'x25k')
[2026-04-03 15:41:06.621841] ChgFunctionMode: (2, 'SMMAG')
[2026-04-03 15:40:53.273855] ChgMag: (30000, 'X', 'x30k')
[2026-04-03 15:41:08.183915] ChgFunctionMode: (2, 'SMMAG')
[2026-04-03 15:40:54.640979] ChgMag: (40000, 'X', 'x40k')
[2026-04-03 15:41:09.785154] ChgFunctionMode: (2, 'SMMAG')
[2026-04-03 15:40:56.187460] ChgMag: (50000, 'X', 'x50k')
[2026-04-03 15:41:11.302722] ChgFunctionMode: (2, 'SMMAG')
[2026-04-03 15:40:57.564335] ChgMag: (60000, 'X', 'x60k')
[2026-04-03 15:41:12.625081] ChgFunctionMode: (2, 'SMMAG')
[2026-04-03 15:40:59.074381] ChgMag: (80000, 'X', 'x80k')
[2026-04-03 15:41:00.419068] ChgMag: (100000, 'X', 'x100k')
[2026-04-03 15:41:01.972621] ChgMag: (120000, 'X', 'x120k')
[2026-04-03 15:41:03.532489] ChgMag: (150000, 'X', 'x150k')
[2026-04-03 15:41:05.061735] ChgMag: (200000, 'X', 'x200k')
[2026-04-03 15:41:06.621841] ChgMag: (250000, 'X', 'x250k')
[2026-04-03 15:41:08.183915] ChgMag: (300000, 'X', 'x300k')
[2026-04-03 15:41:09.785154] ChgMag: (400000, 'X', 'x400k')
[2026-04-03 15:41:11.302722] ChgMag: (500000, 'X', 'x500k')
[2026-04-03 15:41:12.625081] ChgMag: (600000, 'X', 'x600k')
[2026-04-03 15:41:21.429149] ChgMag: (20000, 'X', 'x20k')
[2026-04-03 15:41:21.429149] ChgFunctionMode: (2, 'SMMAG')
[2026-04-03 15:41:21.916072] ChgMag: (25000, 'X', 'x25k')
[2026-04-03 15:41:21.916072] ChgFunctionMode: (2, 'SMMAG')
[2026-04-03 15:41:23.242699] ChgMag: (30000, 'X', 'x30k')
[2026-04-03 15:41:23.242699] ChgFunctionMode: (2, 'SMMAG')
[2026-04-03 15:41:24.813252] ChgMag: (40000, 'X', 'x40k')
[2026-04-03 15:41:24.813252] ChgFunctionMode: (2, 'SMMAG')
[2026-04-03 15:41:26.195036] ChgMag: (50000, 'X', 'x50k')
[2026-04-03 15:41:26.195036] ChgFunctionMode: (2, 'SMMAG')
[2026-04-03 15:41:27.716587] ChgMag: (60000, 'X', 'x60k')
[2026-04-03 15:41:27.716587] ChgFunctionMode: (2, 'SMMAG')
[2026-04-03 15:41:29.101735] ChgMag: (80000, 'X', 'x80k')
[2026-04-03 15:41:29.101735] ChgFunctionMode: (2, 'SMMAG')
[2026-04-03 15:41:30.610721] ChgMag: (100000, 'X', 'x100k')
[2026-04-03 15:41:30.610721] ChgFunctionMode: (2, 'SMMAG')
[2026-04-03 15:41:32.157029] ChgMag: (120000, 'X', 'x120k')
[2026-04-03 15:41:32.157029] ChgFunctionMode: (2, 'SMMAG')
[2026-04-03 15:41:33.732630] ChgFunctionMode: (2, 'SMMAG')
[2026-04-03 15:41:33.732630] ChgMag: (150000, 'X', 'x150k')
[2026-04-03 15:41:35.304930] ChgMag: (200000, 'X', 'x200k')
[2026-04-03 15:41:35.304930] ChgFunctionMode: (2, 'SMMAG')
[2026-04-03 15:41:36.870486] ChgMag: (250000, 'X', 'x250k')
[2026-04-03 15:41:36.870486] ChgFunctionMode: (2, 'SMMAG')
[2026-04-03 15:41:38.446246] ChgMag: (300000, 'X', 'x300k')
[2026-04-03 15:41:38.446246] ChgFunctionMode: (2, 'SMMAG')
--- Summary ---
total events: 56
event counts:
ChgMag: 33
ChgFunctionMode: 23
first 10 events (timeline):
2026-04-03 15:37:08.120401 | ChgMag | (2000000, 'X', 'x2.0M')
2026-04-03 15:40:59.074381 | ChgFunctionMode | (2, 'SMMAG')
2026-04-03 15:37:09.459789 | ChgMag | (2500000, 'X', 'x2.5M')
2026-04-03 15:41:00.419068 | ChgFunctionMode | (2, 'SMMAG')
2026-04-03 15:37:13.635347 | ChgMag | (2000000, 'X', 'x2.0M')
2026-04-03 15:41:01.972621 | ChgFunctionMode | (2, 'SMMAG')
2026-04-03 15:37:15.454976 | ChgMag | (1500000, 'X', 'x1.5M')
2026-04-03 15:41:03.532489 | ChgFunctionMode | (2, 'SMMAG')
2026-04-03 15:40:51.466562 | ChgMag | (20000, 'X', 'x20k')
2026-04-03 15:41:05.061735 | ChgFunctionMode | (2, 'SMMAG')
Callback Monitoring for ChgMag Events¶
Register a callback function with regist_method and monitor the ChgMag (magnification change) event for 15 seconds.
Unlike a polling loop, the callback is invoked immediately when an event occurs.
Step |
Method |
Description |
|---|---|---|
Register callback |
|
Register a function to be called on event |
Wait |
|
Wait 15 seconds while TEM events may occur |
Deregister callback |
|
Remove the callback after monitoring ends |
1connected = TEM3.connect()
2print("TEM3 connected:", connected)
3
4if not connected:
5 print("Skip monitoring because TEM3 is not connected.")
6else:
7 watch_event = TEM3.EventKind.ChgMag.name
8 em.set_enabled(watch_event, True)
9 em.set_maxsize(watch_event, 20)
10
11 captured = []
12
13 def on_chg_mag(*args):
14 captured.append(args)
15 print(f"[{datetime.now().isoformat()}] [ChgMag]", args)
16
17 em.regist_method(watch_event, on_chg_mag, "monitor_chg_mag")
18 print("Monitoring for 15 seconds via callback...")
19 print("Try changing magnification on TEM during this period.")
20
21 time.sleep(15)
22
23 em.unregist_method(watch_event, "monitor_chg_mag")
24 print("captured count:", len(captured))
TEM3 connected: True
Monitoring for 15 seconds via callback...
Try changing magnification on TEM during this period.
[2026-04-03T15:40:51.466562] [ChgMag] (20000, 'X', 'x20k')
[2026-04-03T15:40:51.950044] [ChgMag] (25000, 'X', 'x25k')
[2026-04-03T15:40:53.273855] [ChgMag] (30000, 'X', 'x30k')
[2026-04-03T15:40:54.640979] [ChgMag] (40000, 'X', 'x40k')
[2026-04-03T15:40:56.187460] [ChgMag] (50000, 'X', 'x50k')
[2026-04-03T15:40:57.564335] [ChgMag] (60000, 'X', 'x60k')
[2026-04-03T15:40:59.074381] [ChgMag] (80000, 'X', 'x80k')
[2026-04-03T15:41:00.419068] [ChgMag] (100000, 'X', 'x100k')
captured count: 8
Appendix: Manipulating the Event Queue (Offline)¶
For offline testing, use put_event to inject simulated events and verify queue behavior.
1event_name = TEM3.EventKind.ChgMag.name
2
3# Configure queue behavior
4em.set_enabled(event_name, True)
5em.set_maxsize(event_name, 3)
6
7# Put 5 events into queue (maxsize=3 -> old events are dropped)
8for i in range(5):
9 em.put_event(event_name, {"sim_index": i, "note": "simulated event"})
10
11print("qsize after put:", em.qsize(event_name))
12
13# Drain queue
14print("drain queue:")
15while em.qsize(event_name) > 0:
16 ev = em.get_event(event_name)
17 print(ev)
qsize after put: 3
drain queue:
<Event name=ChgMag data={'sim_index': 2, 'note': 'simulated event'} time=2026-03-23 14:46:48.972974>
<Event name=ChgMag data={'sim_index': 3, 'note': 'simulated event'} time=2026-03-23 14:46:48.972974>
<Event name=ChgMag data={'sim_index': 4, 'note': 'simulated event'} time=2026-03-23 14:46:48.972974>