RAC 胖客户端定制开发:菜单、工具栏与 UI 扩展实战
Teamcenter Rich Client(RAC)是基于 Eclipse RCP(Rich Client Platform)构建的胖客户端。相比 Web 客户端(AWC),RAC 提供了更强大的功能定制能力和更丰富的交互体验。很多企业选择 RAC 作为主力客户端,正是因为它支持深度的 UI 定制。
本文将从菜单扩展、工具栏定制、视图开发、对话框扩展等维度,系统讲解 RAC 胖客户端的定制开发实战。
一、RAC 客户端架构
1.1 Eclipse RCP 基础
1
2
3
4
5
6
7
8
9
10
11
| Eclipse RCP 架构:
├── Workbench(工作台)
│ ├── Perspective(透视图)
│ │ ├── View(视图)
│ │ └── Editor(编辑器)
│ ├── Menu(菜单)
│ ├── Toolbar(工具栏)
│ └── Status Line(状态栏)
└── Plugin(插件)
├── Extension(扩展点)
└── Extension Point(扩展点定义)
|
1.2 Teamcenter RAC 结构
1
2
3
4
5
6
7
8
9
10
11
| Teamcenter RAC 安装目录:
├── tc_root/
│ ├── bin/
│ │ └── rac_client/
│ │ ├── eclipse/ # Eclipse 运行时
│ │ ├── plugins/ # 插件目录
│ │ ├── features/ # 特性目录
│ │ ├── configuration/ # 配置文件
│ │ └── dropins/ # 自定义插件(推荐部署位置)
│ ├── portal/ # Web 门户
│ └── tcdata/ # TC 数据
|
1.3 开发环境搭建
1
2
3
4
5
6
7
8
9
10
11
12
| 开发工具:
├── Eclipse IDE(推荐 4.26+)
├── Teamcenter RAC SDK
├── BMIDE(业务建模)
└── JDK 11+
配置步骤:
1. 安装 Eclipse IDE for RCP and RAP Developers
2. 导入 Teamcenter RAC 插件源码
3. 配置 Target Platform 指向 RAC 安装目录
4. 创建新的 Plugin Project
5. 添加 Teamcenter 依赖
|
二、菜单扩展
2.1 菜单扩展原理
Teamcenter RAC 使用 Eclipse 的 org.eclipse.ui.menus 扩展点来注册菜单项:
1
2
3
4
5
6
7
8
9
10
| <extension point="org.eclipse.ui.menus">
<menuContribution locationURI="menu:com.teamcenter.rac.main.menu?after=additions">
<menu id="com.mycompany.menu" label="我的工具">
<command commandId="com.mycompany.command.openTool"
label="打开工具"
style="push">
</command>
</menu>
</menuContribution>
</extension>
|
2.2 实战:添加自定义菜单
Step 1:创建 Plugin 项目
1
2
3
4
5
| Eclipse → File → New → Plug-in Project
→ Name: com.mycompany.rac.extension
→ Target: Eclipse 4.x
→ Options: Generate an activator
→ Finish
|
Step 2:定义命令(Command)
1
2
3
4
5
6
7
| <!-- plugin.xml -->
<extension point="org.eclipse.ui.commands">
<command id="com.mycompany.command.openCustomDialog"
name="打开自定义对话框"
categoryId="com.teamcenter.rac.commands.category">
</command>
</extension>
|
Step 3:定义 Handler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| package com.mycompany.rac.handlers;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.handlers.HandlerUtil;
public class OpenCustomDialogHandler extends AbstractHandler {
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
Shell shell = HandlerUtil.getActiveWorkbenchWindow(event)
.getShell();
MessageDialog.openInformation(
shell,
"自定义对话框",
"Hello from Teamcenter RAC Extension!"
);
return null;
}
}
|
Step 4:注册 Handler
1
2
3
4
5
| <extension point="org.eclipse.ui.handlers">
<handler commandId="com.mycompany.command.openCustomDialog"
class="com.mycompany.rac.handlers.OpenCustomDialogHandler">
</handler>
</extension>
|
Step 5:添加到菜单
1
2
3
4
5
6
7
8
| <extension point="org.eclipse.ui.menus">
<menuContribution locationURI="menu:com.teamcenter.rac.main.menu?after=additions">
<command commandId="com.mycompany.command.openCustomDialog"
label="我的工具"
style="push">
</command>
</menuContribution>
</extension>
|
2.3 上下文菜单扩展
上下文菜单(右键菜单)的添加方式略有不同:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| <extension point="org.eclipse.ui.popupMenus">
<objectContribution
objectClass="com.teamcenter.rac.kernel.TCComponentItem"
id="com.mycompany.itemContribution">
<menu id="com.mycompany.itemMenu"
label="自定义操作"
path="additions">
<separator name="group1"/>
</menu>
<action
id="com.mycompany.itemAction"
label="查看自定义信息"
class="com.mycompany.rac.actions.ItemCustomAction"
enablesFor="1"
menubarPath="com.mycompany.itemMenu/group1">
</action>
</objectContribution>
</extension>
|
Action 实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
| package com.mycompany.rac.actions;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPart;
import com.teamcenter.rac.kernel.TCComponentItem;
import com.teamcenter.rac.util.Registry;
public class ItemCustomAction implements IObjectActionDelegate {
private IStructuredSelection selection;
@Override
public void setActivePart(IAction action, IWorkbenchPart targetPart) {
}
@Override
public void selectionChanged(IAction action, ISelection selection) {
if (selection instanceof IStructuredSelection) {
this.selection = (IStructuredSelection) selection;
}
}
@Override
public void run(IAction action) {
if (selection != null && !selection.isEmpty()) {
Object obj = selection.getFirstElement();
if (obj instanceof TCComponentItem) {
TCComponentItem item = (TCComponentItem) obj;
String itemId = item.getProperty("item_id");
String itemName = item.getProperty("object_string");
// 执行自定义操作
System.out.println("Selected item: " + itemId + " - " + itemName);
}
}
}
}
|
三、工具栏定制
3.1 添加工具栏按钮
1
2
3
4
5
6
7
8
9
10
| <extension point="org.eclipse.ui.menus">
<menuContribution locationURI="toolbar:com.teamcenter.rac.main.toolbar?after=additions">
<command commandId="com.mycompany.command.openCustomDialog"
label="我的工具"
icon="icons/tool.png"
style="push"
tooltip="打开自定义工具">
</command>
</menuContribution>
</extension>
|
3.2 工具栏按钮状态管理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| public class ToggleToolHandler extends AbstractHandler {
private boolean isActive = false;
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
isActive = !isActive;
// 更新按钮状态
ICommandService cmdService = (ICommandService)
HandlerUtil.getActiveWorkbenchWindow(event)
.getService(ICommandService.class);
Command cmd = cmdService.getCommand("com.mycompany.command.toggleTool");
State state = cmd.getState("org.eclipse.ui.handlers.RadioState");
if (state != null) {
state.setValue(isActive ? "true" : "false");
}
return null;
}
}
|
3.3 动态工具栏
根据当前上下文动态显示/隐藏工具栏按钮:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| <extension point="org.eclipse.ui.menus">
<menuContribution locationURI="toolbar:com.teamcenter.rac.main.toolbar">
<command commandId="com.mycompany.command.bomTool"
label="BOM 工具"
style="push">
<visibleWhen>
<with variable="selection">
<iterate>
<instanceof value="com.teamcenter.rac.kernel.TCComponentBOMLine"/>
</iterate>
</with>
</visibleWhen>
</command>
</menuContribution>
</extension>
|
四、视图(View)开发
4.1 创建自定义视图
1
2
3
4
5
6
7
| <extension point="org.eclipse.ui.views">
<view id="com.mycompany.rac.view.customView"
name="自定义视图"
class="com.mycompany.rac.views.CustomView"
category="com.teamcenter.rac.views.category">
</view>
</extension>
|
4.2 视图实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
| package com.mycompany.rac.views;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.part.ViewPart;
import com.teamcenter.rac.kernel.TCComponent;
import com.teamcenter.rac.kernel.TCException;
import com.teamcenter.rac.kernel.TCSession;
import com.teamcenter.rac.ui.UITools;
public class CustomView extends ViewPart {
private Table table;
@Override
public void createPartControl(Composite parent) {
// 创建表格
table = new Table(parent, SWT.BORDER | SWT.FULL_SELECTION);
table.setHeaderVisible(true);
table.setLinesVisible(true);
// 添加列
TableColumn col1 = new TableColumn(table, SWT.LEFT);
col1.setText("零部件编号");
col1.setWidth(150);
TableColumn col2 = new TableColumn(table, SWT.LEFT);
col2.setText("名称");
col2.setWidth(200);
TableColumn col3 = new TableColumn(table, SWT.LEFT);
col3.setText("版本");
col3.setWidth(80);
// 加载数据
loadData();
}
private void loadData() {
try {
TCSession session = (TCSession) UITools.getDefaultSession();
// 查询数据并填充表格
TCComponent[] items = session.find("Item", "item_id", "*");
for (TCComponent item : items) {
TableItem row = new TableItem(table, SWT.NONE);
row.setText(new String[]{
item.getProperty("item_id"),
item.getProperty("object_string"),
item.getProperty("object_type")
});
}
} catch (TCException e) {
e.printStackTrace();
}
}
@Override
public void setFocus() {
table.setFocus();
}
}
|
4.3 视图与透视图集成
1
2
3
4
5
6
7
8
9
| <!-- 将视图添加到 Teamcenter 透视图中 -->
<extension point="org.eclipse.ui.perspectiveExtensions">
<perspectiveExtension targetID="com.teamcenter.rac.perspective">
<view id="com.mycompany.rac.view.customView"
relationship="right"
relative="com.teamcenter.rac.view.navigator">
</view>
</perspectiveExtension>
</extension>
|
五、对话框(Dialog)开发
5.1 简单对话框
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| public class SimpleInputDialog extends TitleAreaDialog {
private Text inputText;
private String inputValue;
public SimpleInputDialog(Shell parentShell) {
super(parentShell);
}
@Override
protected Control createDialogArea(Composite parent) {
Composite area = (Composite) super.createDialogArea(parent);
Composite container = new Composite(area, SWT.NONE);
container.setLayout(new GridLayout(2, false));
new Label(container, SWT.NONE).setText("请输入:");
inputText = new Text(container, SWT.BORDER);
inputText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
setTitle("自定义输入对话框");
setMessage("请输入需要的参数");
return area;
}
@Override
protected void okPressed() {
inputValue = inputText.getText();
super.okPressed();
}
public String getInputValue() {
return inputValue;
}
}
|
5.2 使用对话框
1
2
3
4
5
6
| SimpleInputDialog dialog = new SimpleInputDialog(shell);
if (dialog.open() == Window.OK) {
String input = dialog.getInputValue();
// 处理输入
System.out.println("用户输入: " + input);
}
|
六、属性页(Property Page)扩展
6.1 添加自定义属性页
1
2
3
4
5
6
7
| <extension point="org.eclipse.ui.propertyPages">
<page id="com.mycompany.rac.propPage.itemCustom"
name="自定义信息"
class="com.mycompany.rac.propPages.ItemCustomPropPage"
objectClass="com.teamcenter.rac.kernel.TCComponentItem">
</page>
</extension>
|
6.2 属性页实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
| package com.mycompany.rac.propPages;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.ui.IWorkbenchPropertyPage;
import org.eclipse.ui.dialogs.PropertyPage;
import com.teamcenter.rac.kernel.TCComponentItem;
public class ItemCustomPropPage extends PropertyPage
implements IWorkbenchPropertyPage {
@Override
protected Control createContents(Composite parent) {
Composite composite = new Composite(parent, SWT.NONE);
composite.setLayout(new GridLayout(2, false));
TCComponentItem item = (TCComponentItem) getElement().getAdapter(
TCComponentItem.class);
try {
// 添加自定义属性显示
new Label(composite, SWT.NONE).setText("自定义编号:");
Text customId = new Text(composite, SWT.READ_ONLY);
customId.setText(item.getProperty("custom_id"));
customId.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
new Label(composite, SWT.NONE).setText("自定义描述:");
Text customDesc = new Text(composite, SWT.READ_ONLY);
customDesc.setText(item.getProperty("custom_desc"));
customDesc.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
} catch (Exception e) {
e.printStackTrace();
}
return composite;
}
}
|
七、部署与调试
7.1 部署方式
方式一:dropins 目录(推荐)
1
2
3
4
5
| 将编译好的插件 JAR 放入:
rac_client/dropins/com.mycompany.rac.extension/
├── plugins/
│ └── com.mycompany.rac.extension_1.0.0.jar
└── eclipsec.exe (Windows) / eclipse (Linux)
|
方式二:features 目录
1
2
3
4
| 创建 Feature 项目
→ 导出为 JAR
→ 放入 rac_client/features/
→ 更新 rac_client/configuration/config.ini
|
7.2 调试方法
启动 RAC 调试模式:
1
2
3
4
5
| # Windows
eclipsec.exe -consoleLog -debug -clean
# Linux
./eclipse -consoleLog -debug -clean
|
查看日志:
1
2
3
4
5
6
7
| 日志文件位置:
rac_client/configuration/.metadata/.log
常见错误:
- Bundle 未激活:检查 MANIFEST.MF
- 扩展点未注册:检查 plugin.xml
- Class Not Found:检查依赖
|
7.3 打包发布
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| 1. 使用 Eclipse 导出插件:
File → Export → Deployable plugins and fragments
→ 选择目标插件
→ 导出为 JAR
2. 打包为更新站点(Update Site):
File → Export → Deployable features
→ 选择特性
→ 生成更新站点
3. 使用 p2 安装:
帮助 → 安装新软件
→ 添加更新站点 URL
→ 选择安装
|
八、最佳实践
8.1 开发规范
- 包命名:使用公司域名反写,如
com.mycompany.rac.xxx - 版本管理:每次更新递增版本号
- 依赖最小化:只依赖必要的插件
- 异常处理:捕获并记录所有异常
- 用户反馈:操作完成后给出明确提示
8.2 性能优化
- 懒加载:视图数据按需加载
- 缓存机制:重复查询使用缓存
- 异步操作:耗时操作在后台线程执行
- UI 线程安全:使用
Display.asyncExec 更新 UI
1
2
3
4
5
6
7
8
9
| // 后台线程查询数据
new Thread(() -> {
TCComponent[] items = queryData();
// 回到 UI 线程更新界面
Display.getDefault().asyncExec(() -> {
updateTable(items);
});
}).start();
|
8.3 兼容性注意事项
- 版本兼容:测试不同 TC 版本的兼容性
- API 变更:关注 Teamcenter API 变更通知
- Eclipse 版本:确认 RCP 版本匹配
- Java 版本:使用 JDK 版本与 TC 一致
九、总结
RAC 胖客户端的定制开发能力是 Teamcenter 的重要优势。掌握以下技能,可以打造符合企业需求的高效客户端:
- 菜单扩展:添加自定义菜单和上下文菜单
- 工具栏定制:创建动态工具栏按钮
- 视图开发:开发自定义数据展示视图
- 对话框开发:创建交互式输入界面
- 属性页扩展:添加自定义属性显示
- 调试部署:掌握调试方法和部署流程
RAC 开发需要 Eclipse RCP 基础,建议先掌握 RCP 核心概念,再深入学习 Teamcenter 特有的扩展点和 API。