配置ipython启动自动导入自定义模块
·
要在 IPython 启动时自动导入所需的模块,最可靠的方法是借助其启动目录(startup)。IPython 会在这个目录里查找并执行指定的Python(.py)或IPython(.ipy)脚本文件,从而在交互式会话开始前,就自动完成所有预先配置的工作。
环境准备:找到startup目录
操作的核心就是把你写好的自动导入脚本,放到正确的startup文件夹里
1. 找到配置文件目录:打开终端(命令行界面),输入ipython locate命令。这个命令会告诉你 IPython的配置文件目录在系统的哪个位置, 假设返回结果示例为/home/用户名/.ipython。实际路径可能因操作系统而异。
$ ipython locate
/home/blctrl/.ipython
2. 进入startup目录:根据上一步找到的路径进入startup文件夹。
$ cd /home/blctrl/.ipython/profile_default/startup
如果profile_default下没有 startup 文件夹,请用下面命令新建:
$ mkdir -p /home/blctrl/.ipython/profile_default/startup
3. 使用启动目录脚本
1) 创建启动脚本:在startup文件夹下,创建一个Python文件。文件名可以随意,但最好有意义。推荐使用数字前缀来控制脚本的执行顺序.
(base) ~/.ipython/profile_default/startup$ ls
00_devices.py 01_writer.py 02_re.py README
00_devices.py用于导入需要使用的设备:
from ophyd import EpicsMotor
tth = EpicsMotor("PMC16C:m1", name="tth")
tth.wait_for_connection()
th = EpicsMotor("PMC16C:m2", name="th")
th.wait_for_connection()
chi = EpicsMotor("PMC16C:m3", name="chi")
chi.wait_for_connection()
phi = EpicsMotor("PMC16C:m4", name="phi")
phi.wait_for_connection()
from ophyd.scaler import EpicsScaler
scaler = EpicsScaler("NCT16:scaler1", name='scaler')
scaler.wait_for_connection()
scaler.channels.read_attrs = ['chan1', 'chan2', 'chan3']
scaler.channels.chan1.name = 'PCT'
scaler.channels.chan2.name = 'Mon'
scaler.channels.chan3.name = 'Det'
scaler.preset_time.set(1.0)
01_writer.py用于导入用于在本地指定路径下保存csv格式文件的python类:
import csv
import os
from datetime import datetime
class DataSaver:
"""Bluesky 回调:自动将扫描数据保存为 CSV 文件"""
def __init__(self, data_dir="./data"):
self.data_dir = data_dir
os.makedirs(self.data_dir, exist_ok=True)
self.file = None
self.writer = None
self.filename = None
def __call__(self, name, doc):
if name == 'start':
# 生成文件名:scan_{scan_id}_{时间戳}.csv
scan_id = doc.get('scan_id', 0)
timestamp = datetime.fromtimestamp(doc['time']).strftime("%Y%m%d_%H%M%S")
self.filename = os.path.join(self.data_dir, f"scan_{scan_id}_{timestamp}.csv")
self.file = open(self.filename, 'w', newline='')
self.writer = csv.writer(self.file)
# 可写入元数据注释(可选)
self.writer.writerow([f"# scan_id: {scan_id}"])
self.writer.writerow([f"# uid: {doc.get('uid', '')}"])
# 列名将在 descriptor 中确定,先留空
self.headers_written = False
elif name == 'descriptor':
self.data_keys = list(doc.get('data_keys', {}).keys())
if not getattr(self, 'headers_written', False):
self.writer.writerow(['seq_num', 'time'] + self.data_keys)
self.headers_written = True
self.file.flush()
elif name == 'event':
seq_num = doc.get('seq_num', 0)
t = doc.get('time', 0)
data = doc.get('data', {})
row = [seq_num, t] + [data.get(k, None) for k in self.data_keys]
self.writer.writerow(row)
self.file.flush()
elif name == 'stop':
if self.file:
self.file.close()
print(f"数据已保存到: {self.filename}")
02_re.py用于导入需要使用的计划, 运行引擎的运行参数等:
from bluesky.plans import scan, list_scan, grid_scan, rel_scan, count, rel_grid_scan, rel_grid_scan
from bluesky.plan_stubs import mv, mvr, read
from bluesky.callbacks.best_effort import BestEffortCallback
from databroker import Broker
from bluesky import RunEngine
ds = DataSaver(data_dir='/home/bl0202/data')
db = Broker.named('temp')
bec = BestEffortCallback()
RE = RunEngine()
RE.subscribe(db.insert)
RE.subscribe(bec)
RE.subscribe(ds)
启动IPython:
$ ipython
Python 3.11.15 | packaged by conda-forge | (main, Mar 5 2026, 16:45:40) [GCC 14.3.0]
Type 'copyright', 'credits' or 'license' for more information
IPython 9.10.1 -- An enhanced Interactive Python. Type '?' for help.
Tip: You can use `files = !ls *.png`
In [1]: %matplotlib qt
使用count计划计划进行测试:
In [2]: RE(count([scaler], num=5, delay=1))
Transient Scan ID: 1 Time: 2026-05-14 16:18:56
Persistent Unique Scan ID: '4de04f99-40c3-4253-945d-ec8e7712da1f'
New stream: 'primary'
+-----------+------------+------------+------------+------------+
| seq_num | time | PCT | Mon | Det |
+-----------+------------+------------+------------+------------+
| 1 | 16:18:57.2 | 1000000 | 0 | 0 |
| 2 | 16:18:58.2 | 1000000 | 0 | 0 |
| 3 | 16:18:59.3 | 1000000 | 0 | 0 |
| 4 | 16:19:00.4 | 1000000 | 0 | 0 |
| 5 | 16:19:01.4 | 1000000 | 0 | 0 |
数据已保存到: /home/bl0202/data/scan_1_20260514_161856.csv
+-----------+------------+------------+------------+------------+
generator count ['4de04f99'] (scan num: 1)
使用scan计划进行测试:
<pre><font color="#26A269">n [</font><font color="#33DA7A"><b>4</b></font><font color="#26A269">]: </font>RE(scan([scaler], tth, -<font color="#26A269">1</font>,<font color="#26A269">1</font>,<font color="#26A269">6</font>))
Transient Scan ID: 2 Time: 2026-05-14 16:19:34
Persistent Unique Scan ID: '52d0f735-e45d-4090-a1da-36dd16a18e52'
New stream: 'primary'
+-----------+------------+------------+------------+------------+------------+
| seq_num | time | tth | PCT | Mon | Det |
+-----------+------------+------------+------------+------------+------------+
| 1 | 16:19:44.2 | -1.0000 | 1000000 | 0 | 0 |
| 2 | 16:19:53.3 | -0.6000 | 1000000 | 0 | 0 |
| 3 | 16:20:02.4 | -0.2000 | 1000000 | 0 | 0 |
| 4 | 16:20:11.5 | 0.2000 | 1000000 | 0 | 0 |
| 5 | 16:20:20.6 | 0.6000 | 1000000 | 0 | 0 |
| 6 | 16:20:29.7 | 1.0000 | 1000000 | 0 | 0 |
数据已保存到: /home/bl0202/data/scan_2_20260514_161934.csv
+-----------+------------+------------+------------+------------+------------+
generator scan ['52d0f735'] (scan num: 2)
使用list_scan计划进行测试:
In [5]: RE(list_scan([scaler], tth, [-1,-0.5, 0, 0.5, 1]))
Transient Scan ID: 3 Time: 2026-05-14 16:22:08
Persistent Unique Scan ID: '1935608f-37af-4fa3-ad8f-ca9846df61ac'
New stream: 'primary'
+-----------+------------+------------+------------+------------+------------+
| seq_num | time | tth | PCT | Mon | Det |
+-----------+------------+------------+------------+------------+------------+
| 1 | 16:22:17.8 | -1.0000 | 1000000 | 0 | 0 |
| 2 | 16:22:26.9 | -0.5000 | 1000000 | 0 | 0 |
| 3 | 16:22:36.0 | 0.0000 | 1000000 | 0 | 0 |
| 4 | 16:22:45.1 | 0.5000 | 1000000 | 0 | 0 |
| 5 | 16:22:54.2 | 1.0000 | 1000000 | 0 | 0 |
数据已保存到: /home/bl0202/data/scan_3_20260514_162208.csv
+-----------+------------+------------+------------+------------+------------+
generator list_scan ['1935608f'] (scan num: 3)
使用grid_scan进行测试:
In [6]: RE(grid_scan([scaler], tth, -1,1,6, th,-2,2,6))
Transient Scan ID: 4 Time: 2026-05-14 16:23:26
Persistent Unique Scan ID: 'f1aabdbe-cd3b-4d75-81fe-9fa938195ccc'
New stream: 'primary'
+-----------+------------+------------+------------+------------+------------+------------+
| seq_num | time | tth | th | PCT | Mon | Det |
+-----------+------------+------------+------------+------------+------------+------------+
| 1 | 16:23:45.7 | -1.0000 | -2.0000 | 1000000 | 0 | 0 |
| 2 | 16:23:54.8 | -1.0000 | -1.2000 | 1000000 | 0 | 0 |
...
| 35 | 16:29:46.8 | 1.0000 | 1.2000 | 1000000 | 0 | 0 |
| 36 | 16:29:55.9 | 1.0000 | 2.0000 | 1000000 | 0 | 0 |
数据已保存到: /home/bl0202/data/scan_4_20260514_162326.csv
+-----------+------------+------------+------------+------------+------------+------------+
generator grid_scan ['f1aabdbe'] (scan num: 4)
同时测试数据也在指定路径下进行了保存:
:~/data$ ls
scan_2_20260513_155455.csv scan_2_20260514_161934.csv scan_3_20260514_162208.csv scan_4_20260514_162326.csv
使用 startup 目录是首选:它更灵活,可以组织多个脚本,维护也方便。
- 命名决定顺序:文件会按字典序(即文件名顺序)执行。你可以用数字前缀来控制,比如 01_imports.py 先于 02_settings.py 运行。
- .py vs .ipy:如果脚本里只有 Python 代码(如 import ...),用 .py 即可。如果包含 IPython 特有的魔术命令(如 %matplotlib),则需要使用 .ipy 后缀。
- scripts目录扩展:你也可以把自定义的Python脚本整个放在profile_default/startup/ 下,当做一个可执行的库来自动导入,实现隐式导入。
更多推荐



所有评论(0)