创建扩展
- 什么是 OpenSesame 扩展?
- 相关文件
- 扩展信息
- 编写扩展代码
- 监听事件
- 构建软件包并上传至 pypi
- 例子
- 事件概览
- change_experiment
- change_item
- close
- delete_item
- heartbeat
- ide_jump_to_line
- ide_new_file
- ide_save_current_file
- image_annotations_detect
- jupyter_exception_occurred
- jupyter_execute_finished
- jupyter_execute_result_text
- jupyter_execute_start
- jupyter_interrupt
- jupyter_restart
- jupyter_run_code
- jupyter_run_system_command
- jupyter_show_prompt
- jupyter_write
- new_item
- new_linked_copy
- notify
- open_experiment
- open_item
- pause_experiment
- prepare_change_experiment
- prepare_delete_item
- prepare_open_item
- prepare_purge_unused_items
- prepare_regenerate
- prepare_rename_item
- purge_unused_items
- pyqode_clear_breakpoints
- pyqode_resume_auto_backend_restart
- pyqode_select_indentation_mode
- pyqode_suspend_auto_backend_restart
- regenerate
- register_editor
- rename_item
- resume_experiment
- run_experiment_canceled
- save_experiment
- startup
- unregister_editor
什么是 OpenSesame 扩展?
扩展向 OpenSesame 用户界面添加任意功能。例如,扩展可以在主工具栏或菜单栏中添加一个新条目。(要添加可用于实验的功能,请使用 插件。)
相关文件
一个或多个扩展被放置在扩展包中,它总是opensesame_extensions
(这本身是一个所谓的隐含的命名空间包,但这是一个技术细节)的子包。假设你的扩展包叫做example
,并且它包含一个叫做example_extension
的单一扩展(可以有更多)。这将对应以下文件和文件夹结构:
opensesame_extensions/
example/
__init__.py #可以为空,但必须存在
example_extension/
__init__.py #包含扩展信息
example_extension.py #包含扩展类
扩展信息
扩展信息定义在扩展模块的__init__.py
中,因此在我们的示例中,这是 opensesesame_extensions/example/example_extension/__init__.py
。
"""包含扩展描述的 docstring"""
# 标准图标名称
# - <https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html>
icon = 'applications-accessories'
# 标签和工具提示用户创建默认操作时使用,即将其插入到菜单和/或工具栏(或两者皆非)
label = "示例扩展"
tooltip = "示例工具提示"
menu = {
"index": -1,
"separator_before": True,
"separator_after": True,
"submenu": "示例"
}
toolbar = {
"index": -1,
"separator_before": True,
"separator_after": True
}
# 设置会在 cfg 对象中持续存储
settings = {
"example_setting": "示例值"
}
扩展可以出现在 OpenSesame 的菜单或主工具栏中。这需要您像上面所示在__init__.py
中定义几个字段:
label
是将出现在菜单中的文本。icon
是一个遵循 freedesktop-compliant 图标名称 的规格,用于指定将出现在菜单和/或工具栏中的图标。index
给出扩展在菜单/工具栏中的位置,并像list
索引一样工作。也就是说,负数值相对于最后一个条目,-1 将扩展放在最后。
要让您的扩展响应菜单/工具栏的激活,请像下面的扩展代码中所示实现activate()
方法。
编写扩展代码
主扩展代码放在 [extension_name].py
中。这个文件通常只包含一个名为 [ExtensionName].py
的类,即类名的驼峰形式等于插件名称,继承自 libqtopensesame.extensions.BaseExtension
。 所以一个基本的(非功能性的)扩展类应该是这样的:
from libopensesame.py3compat import *
from libopensesame.oslogging import oslogger
from libqtopensesame.extensions import BaseExtension
class ExampleExtension(BaseExtension):
"""一个列出若干常见事件的示例扩展。类名应为
folder_name 和 file_name 的 CamelCase 版本。所以在
这种情况下,扩展文件夹(即 Python 包)和
.py 文件(即 Python 模块)都叫做 example_extension,而
类名则叫做 ExampleExtension
"""
def activate(self):
oslogger.debug('example_extension 扩展激活')
def event_save_experiment(self, path):
oslogger.debug(f'Event fired: save_experiment(path={path})')
# 更多事件监听器,请参阅示例扩展源代码
监听事件
OpenSesame在发生重要事情时会触发事件。例如,当实验被保存时,“save_experiment”事件会被触发。要让您的扩展监听某个事件,只需实现一个名为event_[event name]
的方法,如上所示。
请注意,某些事件需要关键字参数,例如save_experiment
的path
。您的函数的关键字签名必须符合预期的关键字签名。请参阅下面的事件概览以获取所有事件和预期关键字的完整列表。
构建软件包并上传至 pypi
构建扩展包并将其上传至pypi
的方法与插件相同:
- https://osdoc.cogsci.nl/4.0/zh/dev/plugin
- https://github.com/open-cogsci/opensesame-extension-example
例子
具体示例可见:
其他示例可以在OpenSesame源代码的opensesame_extensions
文件夹中找到:
事件概览
本概览列出了代码中触发的所有事件,因此,您的扩展可以通过实现相应的event_[eventname]()
函数来监听它们。
change_experiment
extension_manager.fire(u'change_experiment')
change_item
触发于:sequence.py
extension_manager.fire(u'change_item', name=self.name)
触发于:qtitem.py
extension_manager.fire(u'change_item', name=self.name)
触发于:loop.py
extension_manager.fire(u'change_item', name=self.name)
close
触发于:qtopensesame.py
extension_manager.fire(u'close')
delete_item
触发于:qtitem_store.py
extension_manager.fire(u'delete_item', name=name)
heartbeat
extension_manager.fire(u'heartbeat')
ide_jump_to_line
extension_manager.fire('ide_jump_to_line', lineno=lineno)
触发于:OpenSesameIDE.py
extension_manager.fire('ide_jump_to_line', lineno=line_number)
ide_new_file
extension_manager.fire(u'ide_new_file', source=code, ext=ext)
ide_save_current_file
触发于:OpenSesameIDE.py
extension_manager.fire('ide_save_current_file')
image_annotations_detect
extension_manager.fire(u'image_annotations_detect', code=code)
jupyter_exception_occurred
触发于:transparent_jupyter_widget.py
extension_manager.fire('jupyter_exception_occurred')
jupyter_execute_finished
触发于:transparent_jupyter_widget.py
extension_manager.fire('jupyter_execute_finished')
jupyter_execute_result_text
触发于:transparent_jupyter_widget.py
extension_manager.fire('jupyter_execute_result_text', text=text)
jupyter_execute_start
触发于:transparent_jupyter_widget.py
extension_manager.fire('jupyter_execute_start')
jupyter_interrupt
触发于:OpenSesameIDE.py
extension_manager.fire(u'jupyter_interrupt')
jupyter_restart
在 console_bridge.py 中触发:
extension_manager.fire(u'jupyter_restart')
在 OpenSesameIDE.py 中触发:
extension_manager.fire(u'jupyter_restart')
jupyter_run_code
在 menubar.py 中触发:
extension_manager.fire('jupyter_run_code',
code=command)
jupyter_run_system_command
在 JupyterNotebook.py 中触发:
extension_manager.fire('jupyter_run_system_command', cmd=cmd)
jupyter_show_prompt
在 console_bridge.py 中触发:
extension_manager.fire(u'jupyter_show_prompt')
jupyter_write
在 console_bridge.py 中触发:
extension_manager.fire(u'jupyter_write', msg=s)
new_item
在 tree_overview.py 中触发:
extension_manager.fire(u'new_item', name=item.name,
_type=item.item_type)
new_linked_copy
在 tree_overview.py 中触发:
extension_manager.fire('new_linked_copy', name=item_name)
notify
在 sequence.py 中触发:
extension_manager.fire(u'notify',
message=_(u'Sequence contains non-existing item: %s')
在 qtitem.py 中触发:
extension_manager.fire(u'notify',
message=_(u'"%s" is set to a variable or '
u'unknown value and can only be edited through '
u'the script.')
在 qtitem.py 中触发:
extension_manager.fire(u'notify',
message=_(u'"%s" is defined using '
'variables and can only be edited through the '
'script.')
在 qtitem.py 中触发:
extension_manager.fire(u'notify',
message=_(u'"%s" is defined using '
u'variables or has an invalid value, and can only be '
u'edited through the script.')
在 logger.py 中触发:
extension_manager.fire(u'notify',
message=_(u'You have multiple unlinked loggers. This can lead to messy log files.')
在 sketchpad_widget.py 中触发:
extension_manager.fire(u'notify', message=notification,
category=u'info')
open_experiment
在 qtopensesame.py 中触发:
extension_manager.fire(u'open_experiment', path=path)
open_item
在 qtitem.py 中触发:
extension_manager.fire(u'open_item', name=self.name)
pause_experiment
在 base_runner.py 中触发:
extension_manager.fire(u'pause_experiment')
prepare_change_experiment
extension_manager.fire(u'prepare_change_experiment')
prepare_delete_item
extension_manager.fire(u'prepare_delete_item', name=name)
prepare_open_item
extension_manager.fire(u'prepare_open_item', name=self.name)
prepare_purge_unused_items
extension_manager.fire(u'prepare_purge_unused_items')
prepare_regenerate
extension_manager.fire(u'prepare_regenerate')
prepare_rename_item
extension_manager.fire(u'prepare_rename_item', from_name=from_name,
to_name=to_name)
purge_unused_items
extension_manager.fire(u'purge_unused_items')
pyqode_clear_breakpoints
extension_manager.fire('pyqode_clear_breakpoints')
pyqode_resume_auto_backend_restart
extension_manager.fire('pyqode_resume_auto_backend_restart')
extension_manager.fire('pyqode_resume_auto_backend_restart')
extension_manager.fire('pyqode_resume_auto_backend_restart')
extension_manager.fire('pyqode_resume_auto_backend_restart')
pyqode_select_indentation_mode
extension_manager.fire('pyqode_select_indentation_mode')
pyqode_suspend_auto_backend_restart
extension_manager.fire('pyqode_suspend_auto_backend_restart')
extension_manager.fire('pyqode_suspend_auto_backend_restart')
extension_manager.fire('pyqode_suspend_auto_backend_restart')
extension_manager.fire('pyqode_suspend_auto_backend_restart')
regenerate
extension_manager.fire(u'regenerate')
extension_manager.fire(u'regenerate')
register_editor
extension_manager.fire(u'register_editor', editor=editor)
rename_item
extension_manager.fire(u'rename_item', from_name=from_name,
to_name=to_name)
resume_experiment
extension_manager.fire(u'resume_experiment')
run_experiment_canceled
extension_manager.fire('run_experiment_canceled')
save_experiment
extension_manager.fire(u'save_experiment', path=self.current_path)
startup
extension_manager.fire(u'startup')
unregister_editor
extension_manager.fire('unregister_editor', editor=editor)