TypePHP\Qt
指南
控件
深入
参考
FAQ
  • English
  • 简体中文
GitHub
指南
控件
深入
参考
FAQ
  • English
  • 简体中文
GitHub
  • 指南

    • 指南
    • 安装
    • Qt 的安装与编译
    • 快速上手
    • 项目结构
    • 架构原理
    • 状态与视图
  • 界面

    • 事件与处理器
    • 属性
    • 布局
    • 对话框与系统集成
    • 多窗口、托盘与定时器
    • 增量补丁

事件与处理器

注册

// 按 id + 类型注册
$app->on('greet_btn', 'click', function () use (&$state) { ... });

// 不带 id 的事件(如托盘点击)只能按类型注册
$app->onAny('tray', function () use (&$state) { ... });

处理器签名

处理器可以只声明它需要的参数:

$app->on('btn', 'click', function () { ... });              // 不要事件
$app->on('input', 'change', function (array $event) { ... }); // 要事件

框架在注册时用反射探测必需参数个数,分发时按实际个数调用。

这不是可选的优化

AOT 编译后的闭包对实参个数做精确校验,多传一个就抛 ArgumentCountError;普通 PHP 解释器会静默忽略。所以 function () {} 形式的处理器在解释器下全对、在编译产物里一点那个按钮就崩。

框架已经替你处理了(注册时探测 arity),但你自己写回调时要留意这一点。详见 AOT 注意事项。

事件类型

事件触发控件valuepayload
clickbutton, linklink 的 href—
changelineedit, textedit, spin, doublespin, slider, combo新值combo 带 index
submitlineedit文本—
togglecheckbox、radio、可切换 button、可切换 group'0' / '1'—
selectlist, table, tree行 / 项 idindex
activatelist, table, tree行 / 项 id—
tabtabs, stack索引index
menu菜单项—checked
timer定时器——
tray系统托盘手势—
press / releasebutton——
commitlineedit(失焦或回车)文本—
itemClicklist(每次点击都发,重复点已选中的行也发)项 id—
celltable(单元格被编辑,需 editable)新文本row、col
expand / collapsetree节点 idexpanded
closetabs(关闭按钮,需 closable)索引index

事件对象是一个关联数组:

[
    'type'    => 'click',        // 事件类型
    'id'      => 'greet_btn',    // 控件 id
    'value'   => '...',          // 新值 / 行 id(视类型而定)
    'payload' => ['index' => 2], // 额外结构(可选)
]

各类型的完整例子

按钮

$app->on('save_btn', 'click', function () use ($app, $state) {
    $state->save();
});

输入框 —— 实时 vs 回车

// 每次输入都触发
$app->on('search_input', 'change', function (array $event) use ($state) {
    $state->query = (string) $event['value'];
});

// 只在回车时触发
$app->on('search_input', 'submit', function (array $event) use ($state) {
    $state->query = (string) $event['value'];
    $state->search();
});

勾选 / 单选

$app->on('dark_toggle', 'toggle', function (array $event) use ($state) {
    $state->dark = $event['value'] === '1';   // 注意是字符串
});

表格 / 列表选中

$app->on('task_table', 'select', function (array $event) use ($state) {
    $state->selectedId = (string) $event['value'];   // 行 id
});

$app->on('task_table', 'activate', function (array $event) use ($state) {
    $state->open((string) $event['value']);          // 双击
});

下拉框

$app->on('theme_combo', 'change', function (array $event) use ($state) {
    $state->theme = (string) $event['value'];            // 文本
    $state->themeIndex = (int) $event['payload']['index']; // 索引
});

标签页

$app->on('main_tabs', 'tab', function (array $event) use ($state) {
    $state->activeTab = (int) $event['payload']['index'];
});

菜单

$app->on('menu.about', 'menu', function () use ($app) {
    $app->alert('TypePHP\\Qt 示例');
});

$app->on('menu.wrap', 'menu', function (array $event) use ($state) {
    $state->wrap = (bool) ($event['payload']['checked'] ?? false);
});

定时器

$app->setTimer('clock', 1000);        // 每 1000ms 一次;间隔 <= 0 即停

$app->on('clock', 'timer', function () use ($state) {
    $state->ticks++;
});

托盘

托盘激活发的是不带 id 的事件,只能用 onAny。激活方式在 value 里:

$app->onAny('tray', function (array $event) use ($state) {
    $state->lastTrayKind = (string) $event['value'];   // 'left' | 'right' | 'double' | 'middle'
});
value手势
left左键单击(Trigger)
right右键单击(Context)—— 仅在没绑托盘菜单时
double双击
middle中键单击(需要鼠标有中键)

绑了菜单的右击

如果 setTray([... 'menu' => [...]]) 绑了菜单,右击由 Qt 接管并弹出菜单, 不再发 right 事件(这是 Qt 自身的行为)。菜单项随后发普通的 menu 事件:

$app->on('tray.quit', 'menu', function () use ($app) { $app->close(); });

通配:onAny

$app->onAny('click', function (array $event) use ($state) {
    $state->lastClicked = $event['id'];   // 所有按钮的点击都汇到这里
});

on($id, $type, …) 和 onAny($type, …) 可以同时注册,两个都会触发 —— 先特例、后通配。 这让 onAny 成为放横切关注点(埋点、日志、全局快捷键)的可靠位置, 不用担心别处注册了什么。

处理器里能做什么

只改状态。不要直接操作控件 —— 那是声明式视图的事。

少数命令式 API(不在控件树里,所以不参与 diff)例外:

$app->setTitle('新标题');
$app->setStatus(['就绪', '共 3 项']);
$app->setMenu([...]);
$app->setTray([...]);
$app->setTimer('id', 500);
$app->resize(800, 600);

错误处理

处理器抛异常不会崩进程。框架捕获它、弹错误框、记进 lastError(),循环继续:

$app->on('risky', 'click', function () {
    throw new RuntimeException('文件不存在');
});

// 之后
$app->lastError();   // '文件不存在 @ /path/main.php:42'

在无头模式(headless(true))下不弹框,直接记录 —— 这样 --selftest 能跑完并报告哪个用例失败。

在 GitHub 上编辑此页
最后更新: 2026/10/6 03:13
Next
属性