公开 API#
公开入口为 ultralight_py;_native、下划线成员和内部包装构造器不属于公开接口。
通过 Runtime 创建 View,通过 View 或 JSValue 获取 JSValue,通过绑定方法获取 PythonBinding。
原生 Renderer 由 Runtime 管理。项目处于开发阶段,公开接口可能调整。
Application/Window 与独立 API 注册表的完整接口见 窗口与注册表指南。
Runtime 运行时#
| 接口 | 返回与约定 |
|---|---|
Runtime(*, assets_root=None, resource_loader=None, cache_path=None) |
每进程共享的 Runtime;目录接受 str/PathLike,loader 返回 bytes、Resource 或 None |
create_view(width, height, *, transparent=False, session=None) |
CPU 离屏 View;尺寸为正 uint32,不接受 bool |
update() |
推进页面加载、脚本和清理;不隐式渲染 |
refresh_display(display_id=0) |
通知显示刷新,供动画使用 |
render() |
渲染待更新的离屏页面 |
set_error_handler(callback) |
接收 UltralightError;None 恢复默认 unraisablehook |
close()、closed |
幂等关闭全部 View;关闭后不能重新创建 Renderer |
with Runtime() 退出即关闭共享实例,因此不要将临时 Runtime 上下文用于一个仍在使用的宿主。
首次配置被冻结,后续省略参数会复用配置;显式传入不同目录或 loader 会报 ValueError。
cache_path 指定 Ultralight 的磁盘缓存及浏览器持久化存储根目录,例如
Runtime(cache_path="./cache")。显式路径在首次初始化时解析为绝对路径,
不相对于 assets_root;接受 str/PathLike,不接受含 NUL 的路径。
None 保留 SDK 默认配置;后续传入 None 复用首次配置,不会重置目录。
相同目录可以重复指定,不同目录报 ValueError。目录由 SDK 按需创建。
同一 Runtime 的 View 使用默认浏览器 Session,共享存储;该配置不提供账号隔离,
会话隔离使用 Session 会话。
View 页面#
| 接口 | 返回与约定 |
|---|---|
load_html(html, url="")、load_url(url) |
启动异步加载;html 的 URL 用作相对资源基址 |
resize(width, height) |
调整设备像素尺寸,校验同 create_view |
set_callbacks(*, begin_loading=None, window_ready=None, dom_ready=None, finish_loading=None, fail_loading=None, console=None, page_settled=None) |
整组替换旧回调槽;不影响订阅和内部等待 |
on(event, callback) |
返回 Subscription;支持上述七个事件 |
is_ready、is_settled |
当前主页面状态;加载开始或失败时清空 |
navigation_id、load_failure |
导航代次,以及当前主框架 LoadFailure 或 None |
wait_ready(timeout=5)、wait_settled(timeout=5) |
被动异步等待,成功返回 None |
wait_until(predicate, timeout=5) |
被动等待同步谓词,返回首个真值结果 |
wait_for_selector(selector, timeout=5) |
等待主文档中元素存在,返回 True;不判断可见性 |
evaluate(source, *, copy=False) |
求值;容器默认共享,copy=True 递归复制;不负责等待页面加载 |
evaluate_handle(source) |
保留当前文档的 JSValue |
bind_object(name, instance, *, members=None) |
返回 PythonBinding;名称为非空字符串,不能为保留名 Python |
bind_function(name, callback) |
可调用对象专用入口,返回 PythonBinding,保留原对象的身份 |
unbind(name) |
撤销该名称绑定;名称不存在时无操作 |
copy_pixels() |
拥有独立 bytes 的 PixelBuffer,读取不清除脏区域 |
clear_dirty_bounds() |
宿主成功消费帧后清除脏区域 |
close()、closed |
幂等关闭;上下文退出也关闭 |
对象成员、容器运算和转换规则见 对象桥接。
输入方法见 使用指南:输入事件,继承的输入 API 同样属于公开 View 接口。
window_ready 用于在页面脚本执行前安装绑定;dom_ready 用于等待 DOM 可用。
FrameEvent 和 LoadFailure 是独立、只读的事件数据;console 参数为普通字典。
事件可能来自子框架,主页面逻辑应检查 is_main_frame。
页面等待与事件订阅#
所有 View 等待都不调用 update()、refresh_display() 或 render(),也不安装驱动器。
宿主保持完整的帧循环控制权。生命周期等待观察事件;条件等待先检查一次,之后仅在
宿主的 Runtime.update() 或 AppCore 步进完成后重检。没有推进时不会持续轮询。
先调用 load_html/load_url,再等待当前待加载页面。wait_ready 只表示主框架 DOM
已经解析;wait_settled 表示 SDK 的网络与布局静默检测已经完成。后者要求宿主持续调用
完整更新、显示刷新和渲染;Application 负责这一循环。它不保证业务完成或动画永久停止。
新 View 尚未加载时 is_ready 为 False,等待仍受超时限制。
navigation_id 在 Python 发起加载时立即推进,对应的主框架加载开始事件不重复计数。
页面自行发起的新主框架加载也推进代次;子框架和 window_ready 不推进。
普通等待绑定开始等待时的文档,替换时抛 UltralightError(code=PageGone)。
加载失败抛 PageLoadError,其 failure 保存原始 LoadFailure;关闭抛 Closed。
就绪事件后、等待恢复前发生导航或关闭,也会重新检查有效性。
timeout 必须是有限正数,单位为秒,不接受 bool 或 None。超时抛 TimeoutError,取消传播
asyncio.CancelledError;仅取消等待,不停止页面加载或 JS 工作。wait_until 不接受
异步谓词,谓词自身异常直接传递;选择器语法错误传递为 JavaScriptError。
# 宿主帧循环持续推进 runtime,等待本身不推进。
view.load_url("file:///panel.html")
await view.wait_ready(timeout=5)
await view.wait_for_selector("#status")
with view.on("dom_ready", callback) as subscription:
...
订阅由 View 保留;丢弃订阅句柄不会取消订阅,需显式 close() 或退出上下文。
View/Runtime 关闭会释放订阅及旧回调。内部状态先更新,再调用旧回调和按注册顺序调用
独立订阅。单个回调异常进入 Runtime 错误处理器,其余订阅继续执行。
分发期间新增的订阅从下次事件生效,关闭的订阅立即停止接收。
所有事件回调必须同步;page_settled 的参数为 URL 字符串。
可选脚本驱动#
ultralight_py.scripting.ScriptDriver 借用一个 Runtime,driver.page(view) 返回借用 View
的 ScriptPage。同一 Runtime 只创建一个 ScriptDriver,供所有页面共享;构造及使用必须在
主线程同一 asyncio 循环中进行。
from ultralight_py.scripting import ScriptDriver
# 独立脚本显式选择库管理的按需循环。
async with ScriptDriver.auto(runtime, update_interval=1 / 60, display_id=0) as driver:
page = driver.page(view)
await page.navigate("file:///panel.html", wait_until="settled", timeout=5)
result = await page.evaluate("getResult()")
frame = view.copy_pixels()
# 已有宿主调度器:step 只请求宿主推进,执行时机仍由宿主决定。
async with ScriptDriver(runtime, step=host.request_step) as driver:
await driver.page(view).wait_ready()
宿主回调契约为 async def request_step(*, render: bool = False) -> None。
返回表示已经完成一次 update() 和 refresh_display();render=True 时还须完成 render()。
请求可以排队、限频并并入宿主下一帧。适配器不直接调用引擎,多个等待合并为一个在途请求;
后来出现的渲染需求若未进入当前请求,会在下一轮推进时提出。
ScriptPage 提供 View 的四种等待以及 evaluate(source, timeout=30)、
navigate(url, *, wait_until="ready", timeout=30) 和
load_html(html, url="", *, wait_until="ready", timeout=30);默认超时均为 30 秒。
普通文档或条件等待请求更新和显示刷新;等待页面静默及 JS Promise 时请求完整帧。
Beta.2 中 requestAnimationFrame Promise 需要 render 才能完成,因此异步求值也提出渲染需求。
立即满足的条件或立即返回的 JS 值不请求推进。
依赖 rAF 或布局的条件/选择器可传 render=True;明确只需脚本定时器的
page.evaluate(..., render=False) 可关闭求值等待的渲染需求。这些参数只提出需求,
宿主仍决定执行完整帧的时机。
点击、表单提交等动作引起的导航使用上下文,在动作前登记观察器:
async with page.expect_navigation(wait_until="ready", timeout=30):
page.view.evaluate("setTimeout(() => document.querySelector('a.next').click(), 0)")
await page.wait_for_selector("#result")
上下文退出时等待新主页面;旧页面已经就绪不会提前完成。未发生导航会超时。
动作与等待共用一个单调时钟预算,动作异常或取消会立即释放观察器;异步动作也受超时限制。
超时不能抢占同步 Python/JS。同步 JS 导航可能令求值本身报 PageGone,上例用零延时任务
让求值先返回;上下文不会吞掉动作异常。navigate 和 load_html 复用同一上下文。
导航操作在动作前登记请求,允许未完成操作中的引擎导航链继续,导航开始后的新 Python 加载调用会 以 PageGone 打断旧操作。SDK 未提供导航原因/请求 ID,因此操作完成前的页面自行跳转也 归入当前链;完成后的跳转不再追踪。导航链共享一个超时预算,不按跳转次数重新计时。
自动模式有等待需求时按间隔推进,没有需求时停止;它与 integrations.asyncio.install()
互斥。关闭 ScriptDriver 结束自身操作和推进请求,不关闭借用的 View、Runtime,也不
终止其他被动等待。无驱动器时,自定义宿主仍可完全依靠状态与同步订阅工作。
JSValue 与 PythonBinding#
| 接口 | 返回与约定 |
|---|---|
JSValue.get(key) |
属性值句柄;key 为 str/int,不接受 bool |
JSValue.set(key, value) |
设置属性;基础值、Python 实时代理、同文档 JSValue 均可传入 |
JSValue.keys() |
自有可枚举字符串属性名 list,不读取属性值 |
JSValue.call(*args, this=None) |
结果句柄;this 可传同文档 JSValue,保留 JS 方法的接收对象 |
JSValue.to_python(*, copy=False) |
容器默认共享;copy=True 递归复制为独立 Python 基础数据 |
JSValue.close()、closed |
closed 表示显式关闭、文档失效或 View 已关闭 |
PythonBinding.close()、closed |
撤销该绑定导出的全部代理;导航/关闭也会令 closed 为 true |
两种句柄均支持 with。丢弃 PythonBinding 不自动解绑;丢弃 JSValue 会释放保留句柄,
释放可能延迟到主线程调用。没有通用 valid 属性;操作时检查文档有效性。
同一 Python 可调用对象通过 bind_function/bind_object 导出后,回传时恢复原对象。
不同显式绑定仍拥有独立的代理身份和撤销范围。
JSArray(MutableSequence)与 JSObject(MutableMapping)由求值、回调和属性访问自动生成, 支持 close/closed、with 和 to_python(copy=True)。JSObject 仅操作自有可枚举字符串属性。 View.evaluate_async 和 ScriptPage.evaluate 同样接受关键字参数 copy=False。 共享视图、混合键及迁移规则见 对象桥接。
图像输出#
PixelBuffer.to_rgba() 和 ultralight_py.images.to_rgba(frame) 返回 RGBAImage。
RGBAImage 可从根包或 images 模块导入,构造为 RGBAImage(width, height, data: bytes);
不可变属性为 width、height、data、row_bytes(width * 4)、format("RGBA8")。
像素为非预乘 RGBA,按行连续且没有填充。它独立于 Runtime/View,转换无需创建 Renderer。
函数入口接受 PixelBuffer 或 RGBAImage,后者直接返回原对象。
ultralight_py.images 接口 |
输出 |
|---|---|
to_pillow(frame) |
独立可修改的 Pillow RGBA 图像;需要安装 [pillow] 可选依赖 |
to_numpy(frame, *, copy=True) |
可写独立的 C 顺序的连续 uint8 数组,形状为 (height, width, 4);需要安装 [numpy] 可选依赖 |
to_numpy(frame, copy=False) |
转换后 RGBA bytes 的只读视图,保持底层数据存活 |
适配器接受 PixelBuffer 或 RGBAImage,依赖在调用时加载。缺依赖抛带安装提示的 ImportError。 具体行跨度、去预乘、校验和所有权规则见 使用指南:像素。
数据与错误#
Resource(data: bytes, mime_type=None, charset="utf-8") 复制资源数据。
mime_type 省略时存储为空字符串,加载时按文件扩展名推断;不是 Python None。
PixelBuffer 是不可变数据类,字段为 width、height、row_bytes、format、dirty_bounds、data。
format 的实际字符串为 BGRA8Premultiplied;数据格式和行偏移见 使用指南:像素。
undefined 是单例;判断 JS undefined 使用 value is undefined,与 None/null 区分。
UltralightError 继承 RuntimeError,JavaScriptError 继承 UltralightError。
诊断字段为 code、stack、source_url、line、column;位置缺失时由原生诊断默认值表示。
ErrorCode 是原生枚举,支持名称属性 .name、数值 .value 和 int(code),
不承诺 Python Enum 的遍历协议。常见错误如下:
| code | 常见原因 |
|---|---|
| WrongThread | 在非 Python 主线程操作引擎或显式关闭句柄 |
| Closed | Runtime、View、Window、Application 或 API 已关闭 |
| PageGone | JS 句柄所属文档失效,或 Python 绑定已撤销 |
| CrossContext | JSValue 属于另一文档,或 Session/View 属于另一 Runtime |
| Busy | 重入 Renderer、输入,或在资源回调中操作引擎 |
| ScriptException | JS 执行失败,对应 JavaScriptError |
| TraversalLimit | 显式按值复制存在循环/超过深度限制 |
| Unsupported | AppCore Runtime 被手动驱动,或从加速 View 读取 CPU 像素 |
Python 参数校验还可能直接抛 TypeError、ValueError、OverflowError。 Python 桥接异常进入页面后为 JS Error;页面捕获后可以继续运行。 事件与资源异常通过 Runtime 错误处理器报告,规则见 使用指南:页面事件与异常。
线程与可选集成#
引擎操作和显式 close 在 Python 主线程进行,宿主负责驱动循环。 closed 是状态查询,不会启动引擎操作;PythonBinding.closed 可从其他线程查询撤销状态。 资源 loader 可能在后台线程执行,不能操作 Runtime/View/JS,也不能等待主线程推进加载。 输入回调、Renderer 回调和 JS 桥接中的再次输入应排队到宿主下一轮。
ultralight_py.integrations.tkinter.TkInputAdapter(widget, view, *, coordinate_transform=None, wheel_pixels=40)
只管理输入适配。close 解除自身绑定,释放借用引用,不关闭 View、Runtime 或控件。
渲染、帧转换和定时驱动由宿主承担;普通包导入不依赖 tkinter 或 Pillow。
事件回调为同步接口;Promise/asyncio 桥接见下节。
Promise 与 asyncio#
await JSValue.await_result() 等待 JS Promise,返回需要关闭的 JSValue。
非 Promise 抛出 TypeError;Promise 拒绝时抛出带错误信息的 JavaScriptError。
await View.evaluate_async(source) 求值并返回 Python 值,普通结果直接转换。
await JSValue.call_async(*args, this=None) 返回 JSValue,普通结果也保持为句柄。
导航或关闭使等待以 UltralightError(code=ErrorCode.PageGone) 结束。
取消 Python 等待只解除桥接监听,JS Promise 本身继续执行。
绑定函数和对象方法返回可等待对象时,JS 获得 Promise。asyncio 调度 Python 任务;完成值在后续 Runtime 更新或 AppCore 步进中送入 JS;异常使 Promise 拒绝。 解绑、替换绑定、导航和关闭立即撤销投递,并请求取消桥接创建的 Task。 传入的外部 Task/Future 只解除监听,不会被桥接取消。取消是协作式请求。
from ultralight_py.integrations.asyncio import run, install
run(main(), runtime=runtime, update_interval=1 / 60)
# 已有运行中的 asyncio 循环:
driver = install(runtime, update_interval=1 / 60)
try:
result = await view.evaluate_async("Promise.resolve(42)")
finally:
driver.close()
await 不隐式推进引擎。自定义离屏宿主可自行调用 update(),不必安装驱动器。
首次异步操作关联当前循环;同一 Runtime 同时只允许一个循环和一个驱动器。
引擎操作必须在主线程执行。上述离屏驱动器只更新,不渲染,也不关闭传入的 Runtime。
AppCore 应用使用 run/run_async,参见 窗口循环。
更新间隔是目标调度间隔;阻塞宿主线程或长时间执行 JS 会延迟更新。
驱动器关闭或失败会终止关联的跨语言等待和投递;run() 传播驱动错误,
退出时清理定时器、监听和任务。关闭驱动器后的 JS Promise 可能仍未完成。
当前不提供 Tk/asyncio 联合循环或 JS AbortSignal。
Session 会话#
Runtime.create_session(name, *, persistent=False) 创建浏览器存储会话,同一 Session 的 View
共享存储。get_session(name) 返回已打开的会话或 None,不扫描磁盘;
get_or_create_session(name, *, persistent=False) 复用已打开的会话,持久化设置不一致时
抛 ValueError。重复创建同名会话也抛 ValueError。
名称须匹配 [a-z0-9_-]{1,80},default 和 Windows 设备名称保留。
名称无效时抛 ValueError,参数类型错误时抛 TypeError;
persistent 和 force_close 只接受 bool。
创建 View 时通过 session=session 指定会话。View.session 只读,创建后不能切换;
省略时使用 SDK 默认会话,该属性返回 None。使用已关闭的会话创建 View 抛 Closed,
使用另一 Runtime 的会话抛 CrossContext。
Session 提供只读属性 name、persistent、disk_path、closed。
临时会话的 disk_path 为 None;持久化会话使用缓存根目录下的命名子目录,
返回 SDK 报告的绝对 Path。带有效期的 Cookie、localStorage 和 IndexedDB
在正常退出后保留;SDK 没有显式刷新存储的接口。不同会话隔离存储,
sessionStorage 仍按 View 独立。
Runtime 保活已打开的 Session,View 保活所属会话。关闭全部 View 后,会话仍可复用。
session.close() 在有关联 View 存活时抛 Busy;close(force_close=True) 关闭这些 View,
并清理其等待、订阅、JS 句柄和异步桥接,其他会话的 View 继续运行。
关闭幂等,关闭后仍可读取元数据,不删除持久化数据。同名临时会话重新创建后为空,
同名持久化会话复用原存储目录。
上下文退出调用普通 close(),应先关闭关联 View。Runtime 按 View、显式 Session、
Renderer 的顺序关闭。会话操作遵循主线程与资源回调限制;
force_close 只绕过关联 View 的占用检查。会话仅隔离浏览器存储。
参见 多会话示例。