我知道编辑这个网站吗?
the ImageJ2 platform本页介绍与 the ImageJ2 platform 相关的内容。点击徽标查看详情。

开发 ImageJ2 插件

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
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 中的插件,所以有太多的插件无法明确引用,更不用说在教程中主题了。要获得可添加功能的想法,一个好的起点是在 javadocImageJ search portal 中寻找服务。许多服务类型都有补充插件,可以轻松扩展功能。特别是,scijava-commonConverters存储库将包含基本操作的插件定义。

一些需要扩展的更有用的插件类型的简短列表:

示例项目

还记得imagej-common我们said you should clone吗?现在是时候使用它了!

由于 ImageJ API 被设计为具有最大程度的灵活性和可扩展性,因此如果您刚刚开始开发,可能很难准确地确定您应该处理代码库的哪一部分。imagej/tutorials存储库包含精选的最小项目,说明如何构建您自己的项目来执行常见任务。这些项目中的大多数还通过代码中的注释提供了大量文档,以突出显示特定的功能和示例。

您不需要了解此存储库中的每个项目,也不一定按特定顺序浏览它们!相反,您应该通读以下主题,并重点关注那些看起来特别有趣且与您的目标相关的项目。您的学习目标应该是了解这些选定项目中的代码,以及该代码的更改将如何反映在用户和其他开发人员的体验中。

由于这些教程使用了Git进行源代码控制,因此您可以完全自由地修改和使用代码。最糟糕的情况是,您总是可以通过以下命令看到一个大的重置按钮:

git reset --hard 'HEAD@{u}'

总是有选项可以保存或恢复您的工作,例如 stashingbranching,但它们的使用取决于您个人的习惯和对 Git 的其他了解。

###尖端

  • 这些示例中的大多数都有 Main method 来查看代码的运行情况。 -所有这些项目都是Mavenized
  • 您可以查看pom.xml来查找特定项目正在使用的which libraries
  • 您可以通过从任何项目的结构目录(任何包含pom.xml的目录)运行mvn来从命令行compile and build
  • 构建项目会在 $PROJECT/target/ 目录中生成 jar 输出。 -为了获得更“真实”的体验,您可以将构建的jar放入ImageJ installationImageJ2.app/jars/目录中,以尝试任何示例插件。
  • 如果您不确定如何在 ImageJ 中找到您的插件,请使用search bar!
  • 您还可以将每个项目作为maven project导入到Eclipse/NetBeans/IntelliJ IDEA中。

第一步

IntroToImageJ API类记录了ImageJ中的许多常见函数和结构,是一个很好的起点。

基本插件

这些项目提供了最少的示例和完整的在线文档。

目标任务

这些项目是 ImageJ API 中特定“示例”的示例。

与运维人员合作

处理用户输入

插件开发

ImageJ 遵守interface-driven design。从实践的角度来看,这意味着:

如果您正在创建**新的插件类型…

  • 使用基本插件类型的接口
  • 创建一个实现此接口的抽象类来处理所有样板文件。
  • 您的抽象类可能会扩展 scijava-commonscijava-common 中提供的通用抽象类

如果您正在实现现有的插件类型…

-重点扩展适当的抽象类!让编译器告诉您缺少哪些方法。

###采用现有项目

你已经created your own GitHub repository了,对吧?

当您刚刚开始使用 GitMaven 等工具时,了解如何设置和配置新项目的分数差异并不容易。复制一个工作项目作为起点并从那里开始要轻松分区。

example projects 的设计是为了作为新项目的起点。一旦您明确想要编写哪种插件,请选择讨论您选择的领域的项目,然后将其复制到您自己的 GitHub 存储库。从那里,您可以根据需要进行更改。

同时,如果您还没有这样做,我们强烈建议将您的项目导入到 IDE(如 Eclipse)中。这使得developmentrefactoring变得更容易。现代 IDE 还具有 GitMaven 集成,所以您能够利用示例项目设置为 Mavenized Git 存储库这一事实。

除了修改和开发源代码本身之外,您还应该做一些事情来正确识别和配置您的项目:

更新你的 POM

  • 对于您的parent pom,我们建议您延长pom-scijava。这将提供dependency management的许多常见可用依赖项,包括整个ImageJ2 software stack和所有Fiji组件。尝试使用pom-scijava的最新可用版本。
  • 更新您的groupId。 ImageJ 项目使用 net.imagej groupId,而 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 中找到)以匹配。

任选更改

后续步骤

还有更专门用于开发特定类型插件的指南:

完成插件并希望将其提供给用户后,您可以使用以下熟悉的文章: