This guide provides a technical overview of plugins, including how to create new plugins.
If you are interested in developing an existing plugin instead, see Contributing to a plugin.
If you have completed a plugin that you would like to share with the community, see Distributing your plugins.
If you are new to Java development, see the beginner’s guide to starting a Fiji project.
For instructions on plugin development for the original ImageJ, see Developing Plugins for ImageJ.
要求
由于 ImageJ2 是使用 SciJava principles of project management 构建的,因此本指南假设您基本熟悉这些主题和工具,特别是:
| Git | Maven |
另外,您至少应该克隆@Plugin。这将为您提供本指南中讨论的教程的本地副本,以及提供您自己的开发使用的模板。
为了获得完整的“开发者体验”,您可以通过GitHub Bootcamp。至少,一旦您完成了created your own repository并克隆了本地副本,当您的very own plugin到达时,您就已经准备好一个家了!
什么是“插件”?
从概念上讲,插件是添加到 ImageJ 的一项新功能。 ImageJ 的几乎所有方面都是“可插入的”,这意味着可以“临时”提供插件来执行指定的功能。ImageJ 核心只需要哪些常规操作可用;那么当程序运行时,如何完成请求的操作的选项将由当时可用的插件决定。
从技术上讲,ImageJ 构建在 SciJava Common 插件框架之上。在此框架内,插件带有 imagej/tutorials repository 注释的 Java 类 annotated。当用户启动应用程序时,以这种方式注释的类会在 “compile-time” 处自动发现并索引(而不是“runtime”)。
插件类型
运行时可以发现的插件数量没有限制。为了允许有效地搜索每个插件,类都使用特定的 类型 进行注释 - 通常是 Java interface - 插件将通过该类型进行索引。该索引遵循 Java 类型层次结构。
例如,给定以下插件:
@Plugin(type=Service.class)
public class MyService implements Service { }
@Plugin(type=SpecialService.class)
public class SpecialService implements Service { }
Quiz! Which of these plugins would we expect back if asking the Context for plugins of type Service plugin?
Answer: It would give back both the MyService and SpecialService plugins, since SpecialService is a subclass of Service.
Quiz! What if we asked for plugins of type SpecialService?
Answer: It would just return the SpecialService plugin, since MyService is not a SpecialService.
插件优先级
当从第 Context 搜索多个插件时,会获得匹配项。在这些情况下,插件类别遵循类的@Plugin annotation的优先级的返回顺序。优先级只是双重值;作为起点,可以使用 Context类中的优先级常量。
例如,给定以下插件:
@Plugin(priority=Priority.HIGH)
public class MyService implements Service { }
@Plugin(priority=224)
public class SpecialService implements Service { }
Quiz! Which plugin would be returned first if we asked the Context for a Service plugin?
Answer: The SpecialService plugin would come back first. If we look at the Priority class we see that HIGH simply resolves to 100.
当引用优先级特定的常量时,我们还可以使用“相对优先级”。这是一个很好的方法,即使这些常量将来发生变化,排序也能保持不变:
@Plugin(priority=Priority.HIGH+124)
public class SpecialService implements Service { }
SciJava 插件框架由什么组成?
###背景
对发现的所有@Plugin注释类的引用都包含在一个统一的Priority中。每个应用程序负责创建自己的Context来管理插件和上下文状态。
在 ImageJ 中,当 LogService 时会自动创建 Context,因此插件开发人员需要创建自己的。事实上,创建您自己的 Context 通常会导致问题,因为这会导致插件与 ImageJ 使用的容器不同的容器。相反,公共 Context 中的实例由框架自动提供 - 您只需询问即可。
通常,ImageJ 插件开发人员将编写 Service 和/或 Command 插件。如果您需要使用其他插件 - 例如 the application starts up -您不手动创建它,因为这实际上会断开您与Context的连接(您的Service和/或Command插件由应用程序容器创建并由插件框架自动管理)。相反,您应该通过添加所需类型的字段并使用PluginService其进行注释来向Context实例询问。例如:
@Plugin
public class MyPlugin {
// This @Parameter notation is 'asking' the Context
// for an instance of LogService.
@Parameter
private LogService logService;
public void log(String message) {
// Just use the LogService!
// There is no need to construct it, since the Context
// has already provided an appropriate instance.
logService.info(message);
}
}
这规定Context能够为您提供请求的服务的适当实例。
在某些情况下,手动构建插件是不可避免的。了解如果上面的 §§1§§§ 类是手动构造的,即通过new MyPlugin()—LogService配置将会null。仅当通过框架搜索插件实例本身时将会发生自动填充。当您必须手动构建插件实例时,您仍然可以通过其注入机制将其重新连接到现有的Context:
public class MyService {
// This service will manually create plugin instances
// So, we need a reference to our containing Context
// Then we can use it to inject our plugins.
@Parameter
private Context context;
public void doStuff() {
// Manually create a plugin instance
// It is not connected to a Context yet
MyPlugin plugin = new MyPlugin();
// Inject the plugin instance with our Context,
// so the logService field of the plugin will be
// populated.
context.inject(plugin);
// Now that our plugin is injected, we can use
// it with the knowledge that its parameters
// have been populated
plugin.log("Success!");
}
}
服务
服务为 SciJava 框架提供了两个重要的功能:实用方法和持久状态。如果您想添加可在整个 SciJava 框架中使用的可重用 Java 方法,那么您应该创建一个 Service 来提供此功能。如果您需要跟踪上下文范围的变量或配置,则应使用Service来封装该状态。
从概念上讲,Service在每个上下文的基础上满足static utility classes的角色。这样,每个Service类中只有一个instance可以与给定的Context实例关联;在Context过程中自动发生的关联。此外,当要求Context实现给定的Service此时,仅返回最高优先级的实例。
服务通常构建或重用全局定义的功能。例如,@Parameter annotation在搜索和使用插件实例中暗示在。对于此类重用,@Parameters可用于声明服务间要求。在Context启动期间,这些关系将自动解决。
命令
Services提供内部功能,而Commands是设计为菜单执行的插件,通常与用户交互以实现某些期望的结果。打开ImageJ GUI时,命令会填充结构:以非开发人员可以使用的方式公开功能和算法。
在编写Commands时,您通常会在无法由Context自动解析的字段上声明@Parameter annotation,例如数值或文件路径。Commands不像Service那样在Context启动实例化时,而是重新创建和执行。
当执行Command时,它会执行一系列重置步骤,以使用关联的Context填充其@Parameters。如果任何参数未解决且UI可用时,框架将自动构建并显示适当的对话框以获取用户输入。通过这种方式,输入收集与功能操作分离,使开发人员能够专注于真正重要的事情,从而消耗重复代码。这也意味着命令通常可以运行headlessly,而无需任何额外的开发工作。
Command开发中的常见模式是包装Service功能。例如,从路径打开图像是ImageJ中的基本操作。因此,开发者直接使用OpenDataset command。然后,用户通过DatasetIOService从菜单中获得相同的功能 - imagej-common本身通常调用DatasetIOService。
其他插件
因为几乎所有东西都是 ImageJ 中的插件,所以有太多的插件无法明确引用,更不用说在教程中主题了。要获得可添加功能的想法,一个好的起点是在 javadoc 或 ImageJ search portal 中寻找服务。许多服务类型都有补充插件,可以轻松扩展功能。特别是,scijava-common和Converters存储库将包含基本操作的插件定义。
一些需要扩展的更有用的插件类型的简短列表:
- Ops提供一组可重用的图像处理算法。
- Image formats允许在ImageJ中打开新类型的图像。
- Input Preprocessors允许框架在正常的Java类层次结构中限制外部交换类型。
- Displays让你控制
@Parameters的人口。 - imagej/tutorials repository控制UI元素如何呈现给用户。 如果您知道要修改的函数,但无法确定其在代码中的位置,请ask other developers.您现在是社区的一员了!
示例项目
还记得imagej-common我们said you should clone吗?现在是时候使用它了!
由于 ImageJ API 被设计为具有最大程度的灵活性和可扩展性,因此如果您刚刚开始开发,可能很难准确地确定您应该处理代码库的哪一部分。imagej/tutorials存储库包含精选的最小项目,说明如何构建您自己的项目来执行常见任务。这些项目中的大多数还通过代码中的注释提供了大量文档,以突出显示特定的功能和示例。
您不需要了解此存储库中的每个项目,也不一定按特定顺序浏览它们!相反,您应该通读以下主题,并重点关注那些看起来特别有趣且与您的目标相关的项目。您的学习目标应该是了解这些选定项目中的代码,以及该代码的更改将如何反映在用户和其他开发人员的体验中。
由于这些教程使用了Git进行源代码控制,因此您可以完全自由地修改和使用代码。最糟糕的情况是,您总是可以通过以下命令看到一个大的重置按钮:
git reset --hard 'HEAD@{u}'
总是有选项可以保存或恢复您的工作,例如 stashing 或 branching,但它们的使用取决于您个人的习惯和对 Git 的其他了解。
###尖端
- 这些示例中的大多数都有 Main method 来查看代码的运行情况。 -所有这些项目都是Mavenized。
- 您可以查看pom.xml来查找特定项目正在使用的which libraries。
- 您可以通过从任何项目的结构目录(任何包含
pom.xml的目录)运行mvn来从命令行compile and build。 - 构建项目会在
$PROJECT/target/目录中生成jar输出。 -为了获得更“真实”的体验,您可以将构建的jar放入ImageJ installation的ImageJ2.app/jars/目录中,以尝试任何示例插件。 - 如果您不确定如何在 ImageJ 中找到您的插件,请使用search bar!
- 您还可以将每个项目作为maven project导入到Eclipse/NetBeans/IntelliJ IDEA中。
第一步
IntroToImageJ API类记录了ImageJ中的许多常见函数和结构,是一个很好的起点。
基本插件
这些项目提供了最少的示例和完整的在线文档。
- example-imagej-command - ImageJ命令插件的最小模板
- 查看一些simple commands,看看它们如何与用户互动
- Getting started with modules——面向众多用户的插件类型的基础,包括commands
目标任务
这些项目是 ImageJ API 中特定“示例”的示例。
与运维人员合作
处理用户输入
插件开发
- Create a new plugin type
启动你自己的插件
一般标准
ImageJ 遵守interface-driven design。从实践的角度来看,这意味着:
如果您正在创建**新的插件类型…
- 使用基本插件类型的接口
- 创建一个实现此接口的抽象类来处理所有样板文件。
- 您的抽象类可能会扩展 scijava-common 或 scijava-common 中提供的通用抽象类
如果您正在实现现有的插件类型…
-重点扩展适当的抽象类!让编译器告诉您缺少哪些方法。
###采用现有项目
你已经created your own GitHub repository了,对吧?
当您刚刚开始使用 Git 和 Maven 等工具时,了解如何设置和配置新项目的分数差异并不容易。复制一个工作项目作为起点并从那里开始要轻松分区。
example projects 的设计是为了作为新项目的起点。一旦您明确想要编写哪种插件,请选择讨论您选择的领域的项目,然后将其复制到您自己的 GitHub 存储库。从那里,您可以根据需要进行更改。
同时,如果您还没有这样做,我们强烈建议将您的项目导入到 IDE(如 Eclipse)中。这使得development和refactoring变得更容易。现代 IDE 还具有 Git 和 Maven 集成,所以您能够利用示例项目设置为 Mavenized Git 存储库这一事实。
除了修改和开发源代码本身之外,您还应该做一些事情来正确识别和配置您的项目:
更新你的 POM
- 对于您的parent pom,我们建议您延长
pom-scijava。这将提供dependency management的许多常见可用依赖项,包括整个ImageJ2 software stack和所有Fiji组件。尝试使用pom-scijava的最新可用版本。 - 更新您的groupId。 ImageJ 项目使用
net.imagejgroupId,而 Fiji 项目使用sc.fiji——或者如果您不打算将您的插件与核心 ImageJ 或 Fiji 项目一起分发,您也可以使用您自己的 groupId。 - 根据项目的预期用途将您的artifactId更新为适当的内容。
- 将您的
和 更新为适合您的新artifactId的内容。 - 将
块添加到您的 pom 中,以识别您自己的身份(有关格式设置,请参阅[this example](https://github.com/scijava/pom-scijava/blob/pom-scijava-16.1.0/pom.xml#L32-L47))。
代码更改
- 如果您更新了 pom 的 groupId,您应该类似地更新 package 结构(在
src/main/java中找到)以匹配。
任选更改
- 如果您想使用其他 ImageJ or Fiji projects 作为库,则需要将它们作为依赖项添加到
pom.xml的 dependency block 中。请注意,您不需要指定,因为它们由 `pom-scijava` pom 管理。 - 如果您复制的
pom.xml有main method specification,您可能需要根据情况删除或更新它。 - 如果您想将非 Java 文件添加到您的插件中,例如示例图像或demo scripts,请参阅standard maven layout。
后续步骤
还有更专门用于开发特定类型插件的指南:
完成插件并希望将其提供给用户后,您可以使用以下熟悉的文章: