- 为 BoundingBox 类添加获取中心点坐标的便捷方法 - 重构 Mesh2D 悬停提示框绘制逻辑,支持基于摄像机缩放的动态尺寸计算 - 在 ModelRender 中新增带缩放参数的文本渲染方法 - 重写 MultiSelectionBoxRenderer 以适配动态缩放,统一使用像素单位配置 - 优化 ParametersManagement 日志记录方式 - 修复 TextRenderer 字体颜色传递问题 - 更新 TextShader 着色器代码以兼容新的渲染管线和透明度处理
AxisInnovatorsBoxWindowApi
项目概述
AxisInnovatorsBoxWindowApi 是一个为 AxisInnovatorsBox 平台设计的管理API接口库,开发者可通过此API创建自定义插件,实现窗口管理、事件交互等核心功能。该仓库提供了接口定义、类说明文档及插件开发示例代码,帮助开发者快速接入AxisInnovatorsBox生态系统。
功能特性
- 🖥️ 窗口生命周期管理 - 创建/销毁窗口、调整窗口状态(最小化/最大化)
- 🎮 事件驱动交互 - 支持窗口事件监听与自定义事件触发
- 📦 跨语言插件支持 - 基于 Java 平台无缝加载 Python 插件,提供标准插件基类(Java/Python)实现快速扩展
- 📄 动态配置管理 - 通过 properties 配置文件灵活加载多语言插件(支持 Java/Python 插件声明)
- 📊 统一日志追踪 - 集成 Java 平台日志系统,同步记录 Python 插件的运行状态与异常信息
插件加载系统说明
- 插件加载系统核心组件。
- 插件加载系统由 程序内部 完成
注册Jar插件
- Jar插件在/plug-in中添加
@PluginMeta(id = "test", name = "测试插件",
supportedVersions = {"0.0.2"},
description = "测试插件",
icon = "",
registeredName = "test")
public class Template {
public static PluginDescriptor INSTANCE = null;
public Template() {
GlobalEventBus.EVENT_BUS.register(this);
}
@SubscribeEvent
public void onStartup(StartupEvent event) {
MainWindow.ToolCategory category = new MainWindow.ToolCategory("测试插件", "test", "测试插件");
event.main().getRegistrationTool().addToolCategory(
category,
INSTANCE,
"templatePlugin"
);
}
}
- 插件加载系统会自动填充 INSTANCE 内容
- 使用PluginMeta注册插件信息
Python插件注册
- Python插件在/plug-in/python中添加
- Python可以直接调用Java类实现对插件系统的控制
- 插件还需要单独的放在一个子文件夹中,如/plug-in/python/Examples
- Python插件需要声明一个metadata.json文件,如:
{
"id": "testing",
"name": "测试",
"version": "0.0.1",
"description": "测试插件",
"author": "tzdwindows 7",
"dependencies": [],
"_comment": {
"warning": "本文件为插件元数据配置,修改后需重启应用生效",
"path": "插件资源应放置在plugins/{id}/目录下"
}
}
- Python插件需要声明一个main.py文件做为插件的主脚本,如:
"""
工具模块初始化脚本
功能:向Axis Innovators Box注册自定义工具类别和工具项
作者:tzdwindows 7
版本:1.1
"""
from com.axis.innovators.box.python import PyLocalSide
from javax.swing import AbstractAction
class MyAction(AbstractAction):
def actionPerformed(self, event):
"""工具项点击事件处理"""
print("[DEBUG] Tool item clicked! Event source:", event.getSource())
def onStartup():
"""
系统启动时自动执行的初始化逻辑
功能:
1. 创建工具类别
2. 创建工具项并绑定动作
3. 注册到系统全局工具集
"""
print('[INFO] 正在初始化自定义工具...')
# --------------------------
# 创建工具类别(参数顺序:显示名称,图标资源名,描述)
# --------------------------
tool_category = PyLocalSide.getToolCategory(
u"数据分析工具", # 显示名称(GUI可见)
u"analytics_icon.png", # 图标文件名(需存在于资源目录)
u"高级数据分析功能集合" # 悬停提示描述
)
# --------------------------
# 创建工具项(参数顺序:显示名称,图标,描述,ID,动作对象)
# --------------------------
tool_action = MyAction()
tool_item = PyLocalSide.getToolItem(
u"数据可视化", # 工具项显示名称
u"chart_icon.png", # 工具项图标
u"生成交互式数据图表", # 工具项描述
1001, # 工具项唯一ID(需在配置中统一管理)
tool_action # 点击触发的动作
)
tool_category.addTool(tool_item)
# --------------------------
# 注册工具类别到系统(参数:类别对象,全局唯一注册名称)
# --------------------------
PyLocalSide.addToolCategory(
tool_category,
u"custom_module::data_analysis_tools" # 推荐命名规则:模块名::功能名
)
print('[SUCCESS] 工具类别注册成功')
if __name__ == '__main__':
result = 0
errorResult = ""
# 确保Jython运行时可以访问onStartup函数
# 原理:将函数显式绑定到全局字典
globals()['onStartup'] = onStartup
声明CorePlugins
- CorePlugins核心组件可以修改部分模块的字节码。
- CorePlugins需要在jar的属性中添加 CorePlugins: CorePlugins类位置
- 自动化构建,在build.gradle中添加如下代码:
jar {
manifest {
attributes 'CorePlugin': 'com.axis.core.template.TemplateLoadingCorePlugin'
}
}
- CorePlugins核心组件需要实现 CorePlugins 接口,如:
package com.axis.core.template;
import com.axis.innovators.box.plugins.LoadingCorePlugin;
import com.axis.innovators.template.Template;
/**
* 注册core插件
*/
public class TemplateLoadingCorePlugin implements LoadingCorePlugin {
@Override
public String getMainClass() {
// 返回主类名
return Template.class.getName();
}
@Override
public String[] getASMTransformerClass() {
// 返回字节码转换器类名
return new String[]{TemplateTransformer.class.getName()};
}
}
- IClassTransformer的实现,如:
package com.axis.core.template;
import com.axis.innovators.box.plugins.IClassTransformer;
import org.objectweb.asm.*;
/**
* core plugin transformer
* @author tzdwindows 7
*/
public class TemplateTransformer implements IClassTransformer {
@Override
public byte[] transform(String s, String s1, byte[] bytes) {
ClassReader classReader = new ClassReader(bytes);
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassVisitor classVisitor = new ClassVisitor(Opcodes.ASM5, classWriter) {
@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
if ((access & Opcodes.ACC_PRIVATE) != 0) {
access = (access & ~Opcodes.ACC_PRIVATE) | Opcodes.ACC_PUBLIC;
System.out.println("Changing field access to public: " + name);
}
return super.visitField(access, name, descriptor, signature, value);
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
System.out.println(name + " , descriptor:" + descriptor);
if ((access & Opcodes.ACC_PRIVATE) != 0) {
access = (access & ~Opcodes.ACC_PRIVATE) | Opcodes.ACC_PUBLIC;
System.out.println("Changing method access to public: " + name);
}
return super.visitMethod(access, name, descriptor, signature, exceptions);
}
};
classReader.accept(classVisitor, 0);
return classWriter.toByteArray();
}
}
事件系统说明
- 事件驱动架构核心组件。
- 事件总线系统,支持跨模块通信。
- 事件总线由 EventBus & GlobalEventBus 实现
1.. EventBus & GlobalEventBus 说明
应用程序内的事件驱动架构核心组件:
package com.axis.innovators.box.events;
/**
* 事件总线系统(支持多总线实例隔离)
*/
public class EventBus {
// 核心方法
public void register(Object listener); // 注册监听器
public void unregister(Object target); // 注销监听器
public boolean post(Object event); // 发布事件
public void shutdown(); // 关闭总线
}
public class GlobalEventBus {
public static final EventBus EVENT_BUS = new EventBus(); // 全局单例总线
}
- EventBus:用于处理应用程序内各个模块之间的事件通信。
- GlobalEventBus:用于处理应用程序内各个模块之间的事件通信,支持多总线实例隔离。
2. EventBus & GlobalEventBus 使用示例
示例1:基础事件处理
// 1. 定义事件类型
public class UserLoginEvent {
private final String username;
private boolean cancelled;
public UserLoginEvent(String username) {
this.username = username;
}
// Getter/Setter...
}
// 2. 创建监听器类
public class SecurityLogger {
@SubscribeEvent
public void logLoginAttempt(UserLoginEvent event) {
System.out.println("[安全审计] 登录尝试: " + event.getUsername());
}
}
// 3. 使用全局总线
public class Main {
public static void main(String[] args) {
// 注册监听器
GlobalEventBus.EVENT_BUS.register(new SecurityLogger());
// 模拟用户登录
UserLoginEvent loginEvent = new UserLoginEvent("admin");
GlobalEventBus.EVENT_BUS.post(loginEvent);
}
}
- 示例2:事件取消机制
// 1. 定义可取消事件
public class FileDeleteEvent {
private final Path filePath;
private boolean cancelled;
// 构造方法/getters/setters...
}
// 2. 创建权限校验监听器
public class PermissionValidator {
@SubscribeEvent
public void validateDeletePermission(FileDeleteEvent event) {
if (!checkAdminAccess()) {
event.setCancelled(true);
System.out.println("文件删除被拒绝:权限不足");
}
}
private boolean checkAdminAccess() {
// 权限校验逻辑
return false;
}
}
// 3. 主业务流程
public class FileManager {
public void deleteFile(Path path) {
FileDeleteEvent event = new FileDeleteEvent(path);
GlobalEventBus.EVENT_BUS.post(event);
if (!event.isCancelled()) {
// 执行删除操作
System.out.println("正在删除文件: " + path);
}
}
}
3.所有系统所支持的事件
com.axis.innovators.box.events.CategoryRenderingEvent: 分类栏的渲染事件com.axis.innovators.box.events.MainWindowEvents: 主窗口事件com.axis.innovators.box.events.OpenFileEvents: 接收文件事件com.axis.innovators.box.events.SettingsLoadEvents: 程序初始化事件com.axis.innovators.box.events.StartupEvent: 程序启动事件com.axis.innovators.box.events.TABUIEvents: 选项卡Ui属性事件
HTML窗口集成指南
我们实现了一套高性能的HTML渲染系统,通过Java Chromium Embedded Framework (JCEF) 将HTML内容无缝集成到Java桌面应用中,底层基于jcefmaven项目。
核心实现步骤
1. 创建HTML窗口
// 创建窗口引用
AtomicReference<BrowserWindowJDialog> htmlWindow = new AtomicReference<>();
SwingUtilities.invokeLater(() -> {
// 通过窗口注册表创建子窗口
WindowRegistry.getInstance().createNewChildWindow("main", builder -> {
htmlWindow.set(builder
.title("Axis Innovators Box AI 工具箱") // 窗口标题
.parentFrame(parentFrame) // 父级窗口
.icon(getApplicationIcon()) // 应用图标
.size(1280, 720) // 初始尺寸
.htmlPath(getHtmlResourcePath()) // HTML文件路径
.operationHandler(createOperationHandler()) // 自定义操作处理器
.build());
});
// 配置消息路由
configureMessageRouter(htmlWindow.get());
});
2. 辅助方法
// 获取应用图标
private Image getApplicationIcon() {
return new ImageIcon(Objects.requireNonNull(
MainApplication.class.getClassLoader()
.getResource("icons/logo.png")
)).getImage();
}
// 获取HTML资源路径
private String getHtmlResourcePath() {
return FolderCreator.getJavaScriptFolder() + "/AIaToolbox_dark.html";
}
3. 配置消息路由器
private void configureMessageRouter(BrowserWindowJDialog window) {
CefMessageRouter msgRouter = window.getMsgRouter();
if (msgRouter == null) return;
msgRouter.addHandler(new CefMessageRouterHandlerAdapter() {
@Override
public boolean onQuery(CefBrowser browser, CefFrame frame, long queryId,
String request, boolean persistent, CefQueryCallback callback) {
// 处理来自HTML的请求
handleBrowserRequest(request, callback);
return true; // 表示已处理该请求
}
@Override
public void onQueryCanceled(CefBrowser browser, CefFrame frame, long queryId) {
// 处理请求取消逻辑
System.out.println("请求被取消: " + queryId);
}
}, true); // true表示优先处理
}
HTML事件
HTML窗口内可以捕捉到一些Java的事件
| 事件名 | 介绍 | 触发时机 |
|---|---|---|
javaFontsLoaded |
Java字体加载完成 | Java字体信息传输到HTML时,或在在更新主题时 |
javaThemeChanged |
在主题发生变化时触发 | 在更新主题时 |
具体示例
// 监听Java字体加载事件
document.addEventListener('javaFontsLoaded', function(event) {
const fontInfo = event.detail;
console.log('接收到Java字体信息:', fontInfo);
// 应用Java字体到界面
applyJavaFonts(fontInfo);
});
// 监听Java主题变化事件
document.addEventListener('javaThemeChanged', function(event) {
const themeInfo = event.detail;
console.log('接收到Java主题信息:', themeInfo);
applyJavaTheme(themeInfo);
});
窗口管理系统说明
通过WindowRegistry统一管理应用窗口:
| 方法 | 说明 |
|---|---|
createNewWindow(String id, Consumer<Builder> config) |
创建主窗口 |
createNewChildWindow(String id, Consumer<Builder> config) |
创建模态子窗口 |
getWindow(String id) |
获取已注册窗口 |
unregisterWindow(String id) |
关闭指定窗口 |
CefMessageRouter 使用指南
实现Java与JavaScript双向通信的核心组件:
-
消息处理流程:
- JavaScript → Java: 通过
window.cefQuery()发送请求 - Java → JavaScript: 使用
CefFrame.executeJavaScript()执行脚本
- JavaScript → Java: 通过
-
核心方法:
// JavaScript调用示例
function callJavaMethod(data) {
window.cefQuery({
request: JSON.stringify(data),
onSuccess: response => console.log("Success:", response),
onFailure: (err, msg) => console.error("Error:", msg)
});
}
- 最佳实践:
- 使用JSON格式进行数据交换
- 为不同功能模块使用独立的路由处理器
- 在窗口关闭前移除所有路由处理器
生命周期管理
// 关闭窗口时清理资源
htmlWindow.get().addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
window.getMsgRouter().dispose();
CefApp.getInstance().dispose();
}
});
核心类说明
1. AxisInnovatorsBox
窗口实例的核心操作类,提供以下功能:
getMain(): 获取当前AxisInnovatorsBox实例getMainWindow(): 获取主窗口实例quit(): 退出程序organizingCrashReports(Exception): 组织崩溃报告,用于在应用程序发生异常时生成崩溃报告。popupWindow(WindowsJDialog): 弹出新的窗口,并将其添加到窗口列表中。isWindowStartup(WindowsJDialog): 判断指定的窗口是否已经启动。clearWindow(WindowsJDialog): 清除指定的窗口,并将其从窗口列表中移除。reloadAllWindow(): 重新加载窗口。getRegistrationTool(): 获取注册工具实例。getArgs(): 获取命令行参数。isWindow(): 判断窗口是否已经启动。getVersion(): 获取应用程序的版本号。getRegistrationTopic(): 获取注册主题实例。getAuthor(): 获取应用程序的作者信息。getStateManager(): 获取状态管理器实例。
2. RegistrationTool
负责在 应用程序启动阶段 注册和管理工具分类的核心组件,具备插件系统集成能力:
package com.axis.innovators.box.register;
/**
* 工具分类注册中心(窗口启动前必须完成注册)
*/
public class RegistrationTool {
// 构造方法关联主程序实例
public RegistrationTool(AxisInnovatorsBox main) { ... }
// 核心功能方法
public boolean addToolCategory(ToolCategory category, String regName);
public void addToolCategory(ToolCategory category, PluginDescriptor descriptor, String regName);
public ToolCategory getToolCategory(UUID id);
public UUID getUUID(String registeredName);
}
addToolCategory(ToolCategory category, String regName): 向工具分类注册中心添加一个新的工具分类。addToolCategory(ToolCategory category, PluginDescriptor descriptor, String regName): 向工具分类注册中心添加一个新的工具分类,同时关联插件描述符。getToolCategory(UUID id): 通过UUID获取工具分类。getUUID(String registeredName): 通过注册名称获取UUID。
注册示例
// 创建调试工具分类
MainWindow.ToolCategory debugCategory = new MainWindow.ToolCategory(
"逆向分析工具",
"icons/debugger.png",
"二进制逆向分析工具集"
);
// 添加工具项(带点击事件)
debugCategory.addTool(new MainWindow.ToolItem(
"内存分析器",
"icons/memory.png",
"实时查看进程内存映射",
1,
new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, "启动内存分析模块...");
}
}
));
// 注册到系统(必须在窗口初始化前完成)
try {
registrationTool.addToolCategory(debugCategory, "system:reverseEngineering");
} catch (RegistrationError ex) {
System.err.println("注册失败: " + ex.getMessage());
}
3. RegistrationTopic
负责在 应用程序初始化阶段 统一管理UI主题注册的核心组件,支持类名/LookAndFeel双模式主题注入:
package com.axis.innovators.box.register;
/**
* 主题注册中心(窗口初始化前必须完成注册)
*/
public class RegistrationTopic {
// 核心注册方法
public void addTopic(String topicClass, String name, String tip, Icon icon, String regName);
public void addTopic(LookAndFeel laf, String name, String tip, Icon icon, String regName);
// 状态管理方法
public boolean isLoading(String themeName);
public void setLoading(String themeName);
}
addTopic(String topicClass, String name, String tip, Icon icon, String regName): 向主题注册中心添加一个新的主题。addTopic(LookAndFeel laf, String name, String tip, Icon icon, String regName): 向主题注册中心添加一个新的主题,同时关联LookAndFeel。isLoading(String themeName): 判断指定主题是否正在加载。setLoading(String themeName): 设置指定主题为正在加载状态。
注册示例
try {
// 重复注册相同名称
topicRegistry.addTopic("com.axis.light.MaterialTheme",
"质感浅色",
"Material Design风格",
materialIcon,
"theme:light"); // 已存在同名注册
} catch (RegistrationError ex) {
// 捕获异常并提示:theme:light duplicate registered names
JOptionPane.showMessageDialog(null, ex.getMessage());
}
4. StateManager
应用程序状态管理工具类,提供跨会话的配置持久化能力:
package com.axis.innovators.box.tools;
/**
* 状态持久化管理器(线程安全)
*/
public class StateManager {
// 构造方法
public StateManager(); // 默认使用toolbox.properties
public StateManager(String customFileName); // 自定义状态文件名
// 核心操作方法
public void saveState(String key, [int|long|boolean...] value);
public [String|int|boolean...] getStateAs[Type](String key);
}
saveState(String key, [int|long|boolean...] value): 保存状态到配置文件。getStateAs[Type](String key): 从配置文件获取状态。[String|int|boolean...]: 支持多种数据类型保存到配置文件,并支持多种数据类型从配置文件获取。
5. RegistrationSettingsItem
负责管理系统 设置中心 的配置面板注册,支持插件化扩展设置项的核心组件:
package com.axis.innovators.box.register;
/**
* 设置项注册中心(集成插件配置扩展能力)
*/
public class RegistrationSettingsItem extends WindowsJDialog {
// 核心注册方法
public void addSettings(JPanel panel, String title, Icon icon, String tip, String regName);
public void addSettings(JPanel panel, String title, Icon icon, String tip,
PluginDescriptor plugin, String regName);
// 查询方法
public static List<RegistrationSettingsItem> getRegistrationsByPlugin(PluginDescriptor plugin);
}
addSettings(JPanel panel, String title, Icon icon, String tip, String regName): 向设置项注册中心添加一个新的设置项。addSettings(JPanel panel, String title, Icon icon, String tip, PluginDescriptor plugin, String regName): 向设置项注册中心添加一个新的设置项,同时关联插件描述符。getRegistrationsByPlugin(PluginDescriptor plugin): 通过插件描述符获取关联的设置项列表。
6. LanguageManager
应用程序多语言管理核心组件,支持动态加载与合并多语言资源:
package com.axis.innovators.box.register;
/**
* 国际化语言管理中心(支持插件扩展语言包)
*/
public class LanguageManager {
// 核心操作方法
public static void addLanguage(Language lang);
public static void loadLanguage(String regName);
public static Language getLoadedLanguages();
public static Language getLanguage(String identifier);
}
addLanguage(Language lang): 向语言管理中心添加一个新的语言包。loadLanguage(String regName): 加载指定语言包。getLoadedLanguages(): 获取当前系统加载的语言包。getLanguage(String identifier): 通过标识符获取语言包。
Description
Languages
Java
41.4%
JavaScript
33.5%
C
17.3%
HTML
3.9%
CSS
2.5%
Other
1.4%