TEM3: event tutorial

This tutorial covers the TEM3 event mechanism, including:

  1. Basics of EventManager and EventKind

  2. Event queue control (enable/disable, size, retrieval)

  3. Callback registration and deregistration

  4. 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

datasize

Size of data stored in the queue

maxsize

Maximum number of events the queue can hold

enabled

Whether the event is enabled

TEM3.EventKind

Available event types are enumerated in TEM3.EventKind. Representative examples are shown below.

Event Name

Description

ChgMag

Magnification change

ChgFunctionMode

Function mode change

ChangedTemStemMode

TEM/STEM mode change

ChgStageX / ChgStageY / ChgStageZ

Stage X/Y/Z position change

ChgStageTX / ChgStageTY

Stage tilt change

ChgDefocus

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

regist_method(event_name, func, key)

Register a callback function with a key

List

method_list(event_name)

Retrieve the list of registered callbacks

Deregister

unregist_method(event_name, key)

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

ChgMag

Magnification change

ChgFunctionMode

Function mode change

ChangedTemStemMode

TEM/STEM mode switch

ChgStageX / ChgStageY / ChgStageZ

Stage position change

ChgStageTX / ChgStageTY

Stage tilt change

ChgDefocus

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

regist_method(event_name, func, key)

Register a function to be called on event

Wait

time.sleep(15)

Wait 15 seconds while TEM events may occur

Deregister callback

unregist_method(event_name, key)

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>