插件开发

1. 插件基类

ISAT.widgets.plugin_base.py
 class PluginBase(ABC):
     def __init__(self):
         self.enabled = False

     @abstractmethod
     def init_plugin(self, mainwindow):
         """
         接受主程序调用,进行初始化
         :param mainwindow: 传入mainwindow,便于访问ISAT主程序的各种方法与属性
         """
         pass

     @abstractmethod
     def enable_plugin(self):
         """
         启动插件
         :param mainwindow:
         :return:
         """
         pass

     @abstractmethod
     def disable_plugin(self):
         """
         禁用插件
         :param mainwindow:
         :return:
         """
         pass

     @abstractmethod
     def get_plugin_author(self) -> str:
         """
         获取插件作者
         :return:
         """
         pass

     @abstractmethod
     def get_plugin_version(self) -> str:
         """
         获取插件版本
         :return:
         """
         pass

     @abstractmethod
     def get_plugin_description(self) -> str:
         """
         获取插件描述
         :return:
         """
         pass

     def get_plugin_name(self) -> str:
         """
         获取插件名
         :return:
         """
         return self.__class__.__name__

     def activate_state_changed(self, checkbox_state):
         if checkbox_state:
             self.enable_plugin()
         else:
             self.disable_plugin()

     def before_image_open_event(self, image_path: str):
         """
         当图片打开前调用
         :param image_path: 图片路径
         :return:
         """
         pass

     def after_image_open_event(self):
         """
         当图片打开后调用
         :param:
         :return:
         """
         pass

     def before_annotation_start_event(self):
         """
         当开始标注前调用
         :return:
         """
         pass

     def after_annotation_created_event(self):
         """
         当标注创建完成后调用
         :return:
         """
         pass

     def after_annotation_changed_event(self):
         """
         当标注发生变化后调用,包括创建新标注,拖动顶点,移动多边形,改变多边形属性等
         :return:
         """
         pass

     def before_annotations_save_event(self):
         """
         当保存标注文件前调用
         :return:
         """
         pass

     def after_annotations_saved_event(self):
         """
         当保存标注文件后调用
         :return:
         """
         pass

     def after_sam_encode_finished_event(self, index):
         """
         当sam编码完成后调用
         注意ISAT在单独线程对前后图像进行预编码,通过self.mainwindow.seganythread.results_dict中获取编码结果
         :param index: 编码完成的图片index
         :return:
         """
         pass


     def on_mouse_move_event(self, scene_pos):
         """
         当鼠标移动时调用
         :param scene_pos: 坐标,限制在图像范围内
         :return:
         """
         pass

     def on_mouse_press_event(self, scene_pos):
         """
         当鼠标按下时调用
         :param scene_pos: 坐标,限制在图像范围内
         :return:
         """
         pass

     def on_mouse_release_event(self, scene_pos):
         """
         当鼠标释放时调用
         :param scene_pos: 坐标,限制在图像范围内
         :return:
         """
         pass

     def on_mouse_pressed_and_mouse_move_event(self, scene_pos):
         """
         当鼠标拖动时调用
         :param scene_pos: 坐标,限制在图像范围内
         :return:
         """
         pass

     def application_start_event(self):
         """
         软件启动后触发
         :return:
         """
         pass

     def application_shutdown_event(self):
         """
         软件关闭前触发
         :return:
         """
         pass

小技巧

PluginBase 是所有 ISAT 插件的基类

2. 插件入口

setup.py
 ...
 entry_points={
     "isat.plugins": []
 }
 ...

重要

所有 ISAT 插件都以 Python 包的形式发布。

插件必须使用 setup.py 将插件类添加到 entry_points 的 isat.plugins 中。

3. 插件获取ISAT数据

从插件基类的事件中获取的数据是有限的。

在开发 ISAT 插件时,可以通过 self.mainwindow 直接访问ISAT数据。

4. 创建你的第一个插件

4.1 插件项目结构

推荐使用下面的文件结构作为插件项目结构。

ProjectName
├── MANIFEST.in
├── requirements.txt
├── README.md
├── setup.py
├── ...
└── PluginPackage
   ├── __init__.py
   ├── main.py
   └── ...

4.2 编写插件

  • ProjectName/PluginPackage/__init__.py

    包含版本,作者,插件描述等信息。

    __author__ = "Your name"
    __version__ = "0.0.1"
    __description__ = "A short description of the plugin's functionality."
    
  • ProjectName/PluginPackage/main.py

    实现具体的插件类。

    from ISAT.widgets.plugin_base import PluginBase
    
    class CustomPlugin(PluginBase):
        def __init__(self):
            super().__init__()
    
        def init_plugin(self, mainwindow):
            self.mainwindow = mainwindow
            ...
        ...
    
        # Handle events
        def after_image_open_event(self):
            # do something
    
        ...
    

4.3 编写包文件

  • ProjectName/setup.py

    from setuptools import setup, find_packages
    
    def get_version():
        try:
            from {PluginPackage}.__init__ import __version__
            return __version__
    
        except FileExistsError:
            FileExistsError('__init__.py not exists.')
    
    version, author = get_version()
    
    setup(
        name="isat-plugin-{custom}",                 # It is recommended that the package name start with "isat-plugin".
        version={version},
        author={author},
        keywords=["isat-sam", "isat plugin", ...],
    
        packages=find_packages(),
        include_package_data=True,
    
        python_requires=">=3.8",
        install_requires=[
            'isat-sam>=1.4.0',
        ],
    
        entry_points={
            "isat.plugins": [
                "{custom_plugin} = {PluginPackage}.main:{CustomPlugin}",
            ]
        }
    )
    

4.4 打包

  • 打包为源码包

    cd {ProjectName}
    python setup.py sdist
    
  • 安装

    pip install dist/{isat_plugin_custom}-{version}.tar.gz
    

小技巧

你可以参考插件:AutoAnnotatePlugin and MaskExportPlugin 开发自己的插件。

4.5 分享

  • 你可以直接分享打包后的源码包 dist/xxx.tar.gz,然后通过pip 安装:

    pip install xxx.tar.gz
    
  • 或者,把包上传到 pypi 供用户安装使用:

    pip install {package name}