API/event/zh: Difference between revisions
imported>OCDoc Import Imported from legacy OpenComputers documentation at ocdoc.cil.li |
Clean up Markdown artifacts from DokuWiki migration |
||
| Line 11: | Line 11: | ||
* 让程序在后台运行时对事件作出响应(驱动模式)。 - 让程序在作为前台程序执行时处理事件(优先模式)。 | * 让程序在后台运行时对事件作出响应(驱动模式)。 - 让程序在作为前台程序执行时处理事件(优先模式)。 | ||
在驱动模式下,你的程序需要先为事件注册回调函数(用 | 在驱动模式下,你的程序需要先为事件注册回调函数(用 <code>event.listen()</code>函数),然后退出,以继续执行主程序(通常是shell)。 在优先模式下,你无需在程序中注册事件,可以直接使用<code>events.pull()</code>函数拉取并处理信号。 | ||
'''注意:'''虽然从技术层面上讲可以同时使用两种工作模式,但不推荐这样做。为了保证所有已注册的函数都能接收到事件,事件只有在所有函数均被调用后才会被消耗掉。因此如果你注册了处理函数,同时又进行了拉取,那么同一个事件会被响应两次。 | '''注意:'''虽然从技术层面上讲可以同时使用两种工作模式,但不推荐这样做。为了保证所有已注册的函数都能接收到事件,事件只有在所有函数均被调用后才会被消耗掉。因此如果你注册了处理函数,同时又进行了拉取,那么同一个事件会被响应两次。 | ||
| Line 17: | Line 17: | ||
== 函数 == | == 函数 == | ||
* | * <code>event.listen(event: string, callback: function): boolean</code> 注册一个新的事件侦听器,会在指定名称的事件出现后被调用。 '''event''' - 需要侦听的信号名称。 | ||
'''callback''' - 在收到指定信号后调用的函数。此函数将会接收注册的事件名称作为第一个参数,然后接收触发事件的[[Component/signals|信号]]所定义的所有参数。 | '''callback''' - 在收到指定信号后调用的函数。此函数将会接收注册的事件名称作为第一个参数,然后接收触发事件的[[Component/signals|信号]]所定义的所有参数。 | ||
'''返回值:'''若注册成功,数据类型为 | '''返回值:'''若注册成功,数据类型为<code>number</code>,代表了事件id,可用于传递给<code>event.cancel</code>函数取消事件。若函数已经注册过此类事件,将会返回<code>false</code>。 | ||
* | * <code>event.ignore(event: string, callback: function): boolean</code> 注销一个先前注册的事件侦听器。 '''event''' - 需要注销的信号名称。 | ||
'''callback''' - 之前注册到此事件的回调函数。 | '''callback''' - 之前注册到此事件的回调函数。 | ||
'''返回值:'''若事件成功注销则返回 | '''返回值:'''若事件成功注销则返回<code>true</code>。若指定的函数并未注册到此类事件则返回<code>false</code>。 | ||
'''注意:'''事件侦听器返回 | '''注意:'''事件侦听器返回<code>false</code>后将会注销自身。等价于调用 <code>event.ignore</code>,并向其传递侦听器函数与所注册的事件名称。 | ||
* | * <code>event.timer(interval: number, callback: function[, times: number]): number</code> 开启一个新的定时器,在经过<code>interval</code>指定的时长后将会被触发。 '''interval''' - 调用回调函数的间隔时长,单位为秒。可以为像0.05这样的小数。 | ||
'''callback''' - 要调用的函数。 | '''callback''' - 要调用的函数。 | ||
'''times''' - 应调用的次数。若省略则函数只会被调用一次。指定为 | '''times''' - 应调用的次数。若省略则函数只会被调用一次。指定为<code>math.huge</code>可以无限次重复调用。 | ||
'''返回值:'''定时器id,可以随时用于取消定时器。 | '''返回值:'''定时器id,可以随时用于取消定时器。 | ||
'''注意:'''定时器的精度可能会发生变化。如果电脑闲置并进入了休眠模式,则电脑只会每游戏刻唤醒一次。因此触发回调函数的时间点可能会偏差至多0.05秒。 | '''注意:'''定时器的精度可能会发生变化。如果电脑闲置并进入了休眠模式,则电脑只会每游戏刻唤醒一次。因此触发回调函数的时间点可能会偏差至多0.05秒。 | ||
* | * <code>event.cancel(timerId: number): boolean</code> 取消某个先前由<code>event.timer</code>函数创建的定时器。 '''timerId''' - 由<code>event.timer</code>函数返回的定时器id。 | ||
'''返回值:'''若定时器被成功终止则返回 | '''返回值:'''若定时器被成功终止则返回<code>true</code>。若不存在指定id对应的定时器则返回<code>false</code>。 | ||
* | * <code>event.pull([timeout: number], [name: string], ...): string, ...</code> 从队列中拉取下一个可用事件,或者等待可用事件出现。 '''timeout''' - 若指定了值,则函数会至多等待此秒数。超时后仍未出现可用事件,函数会返回<code>nil</code>。 | ||
'''name''' - 目标事件的模式字符串,作为事件的过滤器。如果进行了指定则函数只会返回名称符合此模式的事件。值可以为 | '''name''' - 目标事件的模式字符串,作为事件的过滤器。如果进行了指定则函数只会返回名称符合此模式的事件。值可以为<code>nil</code>,此时事件名称不会被过滤。模式字符串的使用可参考<code>string.match</code>函数。 | ||
'''...''' - 任意数量的参数,顺序需要与目标[[Component/signals|信号]]所定义的相同。它们会被作为过滤器来筛选信号返回的额外参数,判断方式为是否完全相等。值可以为 | '''...''' - 任意数量的参数,顺序需要与目标[[Component/signals|信号]]所定义的相同。它们会被作为过滤器来筛选信号返回的额外参数,判断方式为是否完全相等。值可以为<code>nil</code>,这样将不会过滤此参数。 | ||
筛选样例: | 筛选样例: <code>touch</code>信号(在玩家点击2级或3级显示器时触发)带有如下参数:<code> screenX: number, screenY: number, playerName: string</code>(点击处的X、Y坐标,进行点击的玩家ID) 若要仅拉取玩家"Steve"的点击,你需要使用: | ||
<pre> | <pre>local _, x, y = event.pull("touch", nil, nil, "Steve") | ||
</pre> | </pre> | ||
* | * <code>event.pullFiltered([timeout: number], [filter: function]): string, ...</code>(自1.5.9起添加) 从队列中拉取并返回下一个可用事件,或者等待可用事件出现。支持以函数作为过滤器。 '''timeout''' - 若指定了值,则函数会至多等待此秒数。超时后仍未出现可用事件,则函数会返回<code>nil</code>。 | ||
'''filter''' - 若指定了值,则此函数将会被作为事件的过滤器。让程序能够进行高级过滤。 | '''filter''' - 若指定了值,则此函数将会被作为事件的过滤器。让程序能够进行高级过滤。 | ||
样例: | 样例: | ||
<syntaxhighlight lang="lua"> | |||
local allowedPlayers = {"Kubuxu", "Sangar", "Magik6k", "Vexatos"} local function filter(name, ...) | |||
if name ~= "key_up" and name ~= "key_down" and name ~= "touch" then | |||
return false | return false | ||
end | end | ||
| Line 61: | Line 62: | ||
end | end | ||
return false | return false | ||
end | end | ||
local e = {event.pullFiltered(filter)} --程序将会不限时等待拉取key_up、key_down和click事件。过滤器会确保只拉取allowedPlayers表中的玩家触发的事件。 | local e = {event.pullFiltered(filter)} --程序将会不限时等待拉取key_up、key_down和click事件。过滤器会确保只拉取allowedPlayers表中的玩家触发的事件。 | ||
</syntaxhighlight> | |||
* | * <code>event.pullMultiple(...): ...</code>(自1.5.9起添加) <code>pullMultiple</code>函数可接收多个要拉取的事件名称作为参数,同时支持基本的过滤功能。 | ||
* | * <code>event.onError(message: any)</code> 全局的异常处理函数,用于处理事件回调函数抛出的错误。若有事件侦听器抛出异常,此函数将会进行处理,以避免其影响到不相关的代码(指仅是调用了<code>events.pull</code>函数触发函数执行的代码)。默认情况下,此函数会将报错信息记录到临时文件系统中的文件。 如果你想以其他方式处理事件报错,可以将此函数替换为自定义函数。 | ||
* | * <code>event.push(name: string[, ...])</code> 此函数仅仅为[[API/computer|computer.pushSignal]]的别名,并没有对参数进行任何修改。鉴于event(事件) API库中已经有了<code>event.pull</code>,添加此别名会更符合逻辑。 | ||
== 中断 == | == 中断 == | ||
| Line 74: | Line 76: | ||
在OpenOS 1.6.4及更高版本,中断功能已经被删除。下列两个函数现在已经过时。 | 在OpenOS 1.6.4及更高版本,中断功能已经被删除。下列两个函数现在已经过时。 | ||
* | * <code>event.shouldSoftInterrupt(): boolean</code>(自1.5.9起添加,在1.6.4被移除) - <code>event.shouldInterrupt(): boolean</code>(自1.5.9起添加,在1.6.4被移除) | ||
中断是一类用于关闭或停止进程的消息。在OpenOS中 | 中断是一类用于关闭或停止进程的消息。在OpenOS中<code>computer.pullSignal()</code>函数及其封装会产生两种类型的事件。 | ||
若 | 若<code>event.pull*()</code>函数在指定了过滤器但不指定超时时间的情况下执行,一定情况下意味着无限期执行。这两种事件非常有用。 | ||
* 软中断是在按下 | * 软中断是在按下<code>Ctrl+C</code>时产生的事件信号。信号包含两个参数,事件名称<code>"interrupted"</code>和电脑运行时间。 | ||
'' 硬中断在按下 | '' 硬中断在按下<code>Ctrl-Alt-C</code>时产生。它会通过抛出<code>"interrupted"</code>异常以强制退出<code>event.pull''()</code>函数。 | ||
== 简易事件处理样例 == | == 简易事件处理样例 == | ||
| Line 88: | Line 90: | ||
通常用户编写的脚本只会涉及一两个事件,不涉及其他事件的处理。建议将"interrupted"事件作为软件中断处理。 | 通常用户编写的脚本只会涉及一两个事件,不涉及其他事件的处理。建议将"interrupted"事件作为软件中断处理。 | ||
<syntaxhighlight lang="lua"> | |||
while true do | |||
local id, _, x, y = event.pullMultiple("touch", "interrupted") | |||
if id == "interrupted" then | if id == "interrupted" then | ||
print("soft interrupt, closing") | print("soft interrupt, closing") | ||
| Line 97: | Line 100: | ||
print("user clicked", x, y) | print("user clicked", x, y) | ||
end | end | ||
</ | |||
end | |||
</syntaxhighlight> | |||
== 通用事件处理函数 == | == 通用事件处理函数 == | ||
此处提供了一个较好的通用事件处理函数。此样例的主要功能是以 | 此处提供了一个较好的通用事件处理函数。此样例的主要功能是以<code>event.pull()</code>函数返回的事件名称作为回调函数列表的键,用元方法来处理未定义事件。请注意<code>event.pull</code>函数会让程序进入等待状态,直到出现可用事件。 | ||
<syntaxhighlight lang="lua"> | |||
local event = require "event" --加载事件列表,将指向它们的指针存储到event变量中 | |||
local char_space = string.byte(" ") --用数字代替空格字符 local running = true --存储状态的变量,便于循环停止执行 | local char_space = string.byte(" ") --用数字代替空格字符 local running = true --存储状态的变量,便于循环停止执行 | ||
| Line 110: | Line 115: | ||
function unknownEvent() | function unknownEvent() | ||
--如果事件为无关事件,则不进行处理 | |||
end | end | ||
| Line 118: | Line 123: | ||
--简易按键处理函数样例,当用户按下空格键时将running变量设为false function myEventHandlers.key_up(adress, char, code, playerName) | --简易按键处理函数样例,当用户按下空格键时将running变量设为false function myEventHandlers.key_up(adress, char, code, playerName) | ||
if (char == char_space) then | |||
running = false | running = false | ||
end | end | ||
end | end | ||
--主事件处理函数,将事件ID(eventID)从其他参数中分离出来 function handleEvent(eventID, ...) | --主事件处理函数,将事件ID(eventID)从其他参数中分离出来 function handleEvent(eventID, ...) | ||
if (eventID) then --如果一段时间内没有事件被拉取,值可能为nil | |||
myEventHandlers[eventID](...) --调用对应的事件处理函数,并传递剩下的所有参数 | myEventHandlers[eventID](...) --调用对应的事件处理函数,并传递剩下的所有参数 | ||
end | end | ||
end | end | ||
--主事件拉取循环,处理所有事件。在没有任务时会等待。 while running do | --主事件拉取循环,处理所有事件。在没有任务时会等待。 while running do | ||
handleEvent(event.pull()) --等待可用事件出现,然后进行处理 | |||
</ | |||
end | |||
</syntaxhighlight> | |||
如果程序以驱动模式运作,你需要注册事件而不是拉取事件。你可以将所有事件注册到像上面样例一样的全局事件处理函数,也可以将每个事件注册到对应的处理函数。如果你希望上面的样例在后台运行, | 如果程序以驱动模式运作,你需要注册事件而不是拉取事件。你可以将所有事件注册到像上面样例一样的全局事件处理函数,也可以将每个事件注册到对应的处理函数。如果你希望上面的样例在后台运行,<code>while running do</code>循环需要替换为类似下面的代码: | ||
<syntaxhighlight lang="lua"> | |||
event.listen("key_up", handleEvent) --注册handleEvent函数,使其在key_up事件发生时被调用,然后结束程序 | |||
</syntaxhighlight> 也可以直接注册<code>myEventHandlers.key_up</code>,这样的话处理函数会额外收到一个参数(事件名称)作为第一个参数。 | |||
== 目录 == | == 目录 == | ||
{{:API/contents/zh}} | {{:API/contents/zh}} | ||
Latest revision as of 20:26, 24 August 2026
Event(事件) API
Event(事件) API为用户提供了一套基本的事件处理系统,可利用其编写代码以响应操作系统或其他程序/运行库传递的信号。
例如,你可以利用此API捕获按下的按键、在外接显示器连接到电脑或断开连接时进行响应,或是处理传入的网络信息。
概述
event API主要有两种用法:
- 让程序在后台运行时对事件作出响应(驱动模式)。 - 让程序在作为前台程序执行时处理事件(优先模式)。
在驱动模式下,你的程序需要先为事件注册回调函数(用 event.listen()函数),然后退出,以继续执行主程序(通常是shell)。 在优先模式下,你无需在程序中注册事件,可以直接使用events.pull()函数拉取并处理信号。
注意:虽然从技术层面上讲可以同时使用两种工作模式,但不推荐这样做。为了保证所有已注册的函数都能接收到事件,事件只有在所有函数均被调用后才会被消耗掉。因此如果你注册了处理函数,同时又进行了拉取,那么同一个事件会被响应两次。
函数
event.listen(event: string, callback: function): boolean注册一个新的事件侦听器,会在指定名称的事件出现后被调用。 event - 需要侦听的信号名称。
callback - 在收到指定信号后调用的函数。此函数将会接收注册的事件名称作为第一个参数,然后接收触发事件的信号所定义的所有参数。
返回值:若注册成功,数据类型为number,代表了事件id,可用于传递给event.cancel函数取消事件。若函数已经注册过此类事件,将会返回false。
event.ignore(event: string, callback: function): boolean注销一个先前注册的事件侦听器。 event - 需要注销的信号名称。
callback - 之前注册到此事件的回调函数。
返回值:若事件成功注销则返回true。若指定的函数并未注册到此类事件则返回false。
注意:事件侦听器返回false后将会注销自身。等价于调用 event.ignore,并向其传递侦听器函数与所注册的事件名称。
event.timer(interval: number, callback: function[, times: number]): number开启一个新的定时器,在经过interval指定的时长后将会被触发。 interval - 调用回调函数的间隔时长,单位为秒。可以为像0.05这样的小数。
callback - 要调用的函数。
times - 应调用的次数。若省略则函数只会被调用一次。指定为math.huge可以无限次重复调用。
返回值:定时器id,可以随时用于取消定时器。
注意:定时器的精度可能会发生变化。如果电脑闲置并进入了休眠模式,则电脑只会每游戏刻唤醒一次。因此触发回调函数的时间点可能会偏差至多0.05秒。
event.cancel(timerId: number): boolean取消某个先前由event.timer函数创建的定时器。 timerId - 由event.timer函数返回的定时器id。
返回值:若定时器被成功终止则返回true。若不存在指定id对应的定时器则返回false。
event.pull([timeout: number], [name: string], ...): string, ...从队列中拉取下一个可用事件,或者等待可用事件出现。 timeout - 若指定了值,则函数会至多等待此秒数。超时后仍未出现可用事件,函数会返回nil。
name - 目标事件的模式字符串,作为事件的过滤器。如果进行了指定则函数只会返回名称符合此模式的事件。值可以为nil,此时事件名称不会被过滤。模式字符串的使用可参考string.match函数。
... - 任意数量的参数,顺序需要与目标信号所定义的相同。它们会被作为过滤器来筛选信号返回的额外参数,判断方式为是否完全相等。值可以为nil,这样将不会过滤此参数。
筛选样例: touch信号(在玩家点击2级或3级显示器时触发)带有如下参数: screenX: number, screenY: number, playerName: string(点击处的X、Y坐标,进行点击的玩家ID) 若要仅拉取玩家"Steve"的点击,你需要使用:
local _, x, y = event.pull("touch", nil, nil, "Steve")
event.pullFiltered([timeout: number], [filter: function]): string, ...(自1.5.9起添加) 从队列中拉取并返回下一个可用事件,或者等待可用事件出现。支持以函数作为过滤器。 timeout - 若指定了值,则函数会至多等待此秒数。超时后仍未出现可用事件,则函数会返回nil。
filter - 若指定了值,则此函数将会被作为事件的过滤器。让程序能够进行高级过滤。
样例:
local allowedPlayers = {"Kubuxu", "Sangar", "Magik6k", "Vexatos"} local function filter(name, ...)
if name ~= "key_up" and name ~= "key_down" and name ~= "touch" then
return false
end
local nick
if name == "touch" then
nick = select(3, ...)
else
nick = select(4, ...)
end
for _, allowed in ipairs(allowedPlayers) do
if nick == allowed then
return true
end
end
return false
end
local e = {event.pullFiltered(filter)} --程序将会不限时等待拉取key_up、key_down和click事件。过滤器会确保只拉取allowedPlayers表中的玩家触发的事件。event.pullMultiple(...): ...(自1.5.9起添加)pullMultiple函数可接收多个要拉取的事件名称作为参数,同时支持基本的过滤功能。event.onError(message: any)全局的异常处理函数,用于处理事件回调函数抛出的错误。若有事件侦听器抛出异常,此函数将会进行处理,以避免其影响到不相关的代码(指仅是调用了events.pull函数触发函数执行的代码)。默认情况下,此函数会将报错信息记录到临时文件系统中的文件。 如果你想以其他方式处理事件报错,可以将此函数替换为自定义函数。event.push(name: string[, ...])此函数仅仅为computer.pushSignal的别名,并没有对参数进行任何修改。鉴于event(事件) API库中已经有了event.pull,添加此别名会更符合逻辑。
中断
在OpenOS 1.6.4及更高版本,中断功能已经被删除。下列两个函数现在已经过时。
event.shouldSoftInterrupt(): boolean(自1.5.9起添加,在1.6.4被移除) -event.shouldInterrupt(): boolean(自1.5.9起添加,在1.6.4被移除)
中断是一类用于关闭或停止进程的消息。在OpenOS中computer.pullSignal()函数及其封装会产生两种类型的事件。
若event.pull*()函数在指定了过滤器但不指定超时时间的情况下执行,一定情况下意味着无限期执行。这两种事件非常有用。
- 软中断是在按下
Ctrl+C时产生的事件信号。信号包含两个参数,事件名称"interrupted"和电脑运行时间。
硬中断在按下Ctrl-Alt-C时产生。它会通过抛出"interrupted"异常以强制退出event.pull()函数。
简易事件处理样例
通常用户编写的脚本只会涉及一两个事件,不涉及其他事件的处理。建议将"interrupted"事件作为软件中断处理。
while true do
local id, _, x, y = event.pullMultiple("touch", "interrupted")
if id == "interrupted" then
print("soft interrupt, closing")
break
elseif id == "touch" then
print("user clicked", x, y)
end
end通用事件处理函数
此处提供了一个较好的通用事件处理函数。此样例的主要功能是以event.pull()函数返回的事件名称作为回调函数列表的键,用元方法来处理未定义事件。请注意event.pull函数会让程序进入等待状态,直到出现可用事件。
local event = require "event" --加载事件列表,将指向它们的指针存储到event变量中
local char_space = string.byte(" ") --用数字代替空格字符 local running = true --存储状态的变量,便于循环停止执行
function unknownEvent()
--如果事件为无关事件,则不进行处理
end
--存储所有事件处理函数的列表 --会返回占位伪函数unknownEvent,以防无法匹配 local myEventHandlers = setmetatable({}, { __index = function() return unknownEvent end })
--简易按键处理函数样例,当用户按下空格键时将running变量设为false function myEventHandlers.key_up(adress, char, code, playerName)
if (char == char_space) then
running = false
end
end
--主事件处理函数,将事件ID(eventID)从其他参数中分离出来 function handleEvent(eventID, ...)
if (eventID) then --如果一段时间内没有事件被拉取,值可能为nil
myEventHandlers[eventID](...) --调用对应的事件处理函数,并传递剩下的所有参数
end
end
--主事件拉取循环,处理所有事件。在没有任务时会等待。 while running do
handleEvent(event.pull()) --等待可用事件出现,然后进行处理
end如果程序以驱动模式运作,你需要注册事件而不是拉取事件。你可以将所有事件注册到像上面样例一样的全局事件处理函数,也可以将每个事件注册到对应的处理函数。如果你希望上面的样例在后台运行,while running do循环需要替换为类似下面的代码:
event.listen("key_up", handleEvent) --注册handleEvent函数,使其在key_up事件发生时被调用,然后结束程序 也可以直接注册myEventHandlers.key_up,这样的话处理函数会额外收到一个参数(事件名称)作为第一个参数。
目录
| < 100% 15% 15% > | ||
| API | OpenOS | buffer - colors - component - computer - event - filesystem - uuid - internet - keyboard - note - process - rc - robot - serialization - shell - sides - term - text - thread - transforms - unicode |
| ::: | Lua 库 | coroutine - package - io - os |