ZW3D二次开发_入门_Action与Ribbon菜单定制
ZW3D二开入门目录 - 知乎 (zhihu.com)
最新测试环境:2025 SP
前言
这一章介绍如何定制自己的Ribbon菜单,以及如何定制ZW3D原生Ribbon菜单。修改菜单时建议先关闭ZW3D,修改后再启动ZW3D。
学习时间:120min+
学习本节前,请先学习基础概念
ZW3D二次开发_入门_菜单介绍 - 知乎 (zhihu.com)
定制Ribbon菜单
1.新建工作区
首先需要新建一些文件夹来放置各种菜单,我选择在桌面新建一个叫RibbonPageUI的文件夹,文件夹内的结构如下(vs code把RibbonPageUI显示成RIBBONPAGEUI了,实际上是RibbonPageUI):
除了RibbonPageUI这个文件夹名字可以改,其它的都要按照固定写法,不然ZW3D会读取不到菜单。
2.定义"行为"
在ResourcePool下新建一个Action.zcui文件,这个文件用来定义行为,文件名是随意的,因为最终ZW3D是根据XML中的内容辨识一个.zcui文件定义了什么东西。不过还是建议使用XX_Action_XX这种命名方式,因为后期文件多了以后,维护起来比较方便。
后续不管是做Ribbon菜单,还是做其它菜单,都要基于Action.zcui文件,做菜单的过程,其实就是把一组行为装进菜单容器的过程。下面先定义一个“ID_~CommandA”行为,在Action.zcui写入如下内容:
<?xml version="1.0" encoding="UTF-8"?>
<Actions system="false"><Action name="ID_~CommandA" type="button"><Form /><Text><Ribbon>命令A</Ribbon><Menu>命令A</Menu></Text><Icon>测试命令A</Icon><Hint>这是测试命令A</Hint><Help /><Desc /><Script>~CommandA</Script></Action>
</Actions>
介绍一下这里面比较关键的字段
字段 | 说明 | 属性 |
---|---|---|
Ribbon | 行为插入Ribbon菜单时的显示的文本信息 | 无 |
Menu | 行为插入普通菜单时的显示的文本信息,一般跟Ribbon设置成一样的值即可 | 无 |
Icon | 定义图标,有些菜单或者样式本身不支持图标显示的话,就不会显示图标、仅显示文字,图标是用png格式的图片。 如果图标不在搜索路径下,则需要写完整路径,如: <Icon>C:\Users\Administrator\Pictures\测试命令A.png</Icon> 如果图标在搜索路径下,则写图片名就行: <Icon>测试命令A</Icon> |
无 |
Script | 定义执行脚本,即点击按钮后,向ZW3D发送什么命令 | 无 |
Action | 定义行为的基本信息 | #name# 行为的名字,相当于赋予行为一个“代号”,它的格式一定是ID_<Script> #type# 行为的类型,目前只有button类型 |
基于上述信息,我们在“Resource\icons”中添加如下四个图片,后面我们会在引导程序中将icons文件夹的父文件夹添加到搜索路径,所以本例涉及的所有行为的Icon字段写图标名就行,不需要写全路径。
在写自定义Ribbon菜单之前,我们可以仿照“ID_~CommandA”,在Action.zcui里再定义“ID_~CommandB”、“ID_~CommandC”、“ID_~CommandD”三个行为。
3.定义自定义Ribbon文件
我们准备做下面这种菜单:
解析一下这个Ribbon的结构:
最外层是一个名为“测试命令1”Ribbon页,内部包含两个组,分别是“1组”和“2组”,每个组有且仅有一个组面板。
“1组”下有4个控件,4个控件的行为分别是“ID_~CommandA”、“ID_~CommandB”、“ID_~CommandC”、“ID_~CommandD”,其中A与B是大图标样式,C与D是小图标样式。
“2组”下有一个控件菜单,控件菜单中包含了4个控件,每个控件的行为分别是“ID_~CommandA”、“ID_~CommandB”、“ID_~CommandC”、“ID_~CommandD”,默认控件是“ID_~CommandA”。
基于上述信息,在ResourcePool文件夹中新建RibbonPage.zcui文件,然后在“Resource\Settings\Default\ResourcePool\RibbonPage.zcui”里写如下内容:
<?xml version="1.0" encoding="UTF-8"?>
<RibbonPages><RibbonPage name="RIBBON_PAGE1" text="测试命令1" visible="true"><RibbonGroup name="RIBBON_PAGE1_GROUP1" text = "1组" visible="true"><GroupPanel name="RIBBON_PAGE1_GROUP1_PANEL1"><Control action="ID_~CommandA" visible="true" buttonStyle="4" /><Control action="ID_~CommandB" visible="true" buttonStyle="4" /><Control action="ID_~CommandC" visible="true" buttonStyle="2" /><Control action="ID_~CommandD" visible="true" buttonStyle="2" /></GroupPanel></RibbonGroup><RibbonGroup name="RIBBON_PAGE1_GROUP2" text = "2组" visible="true"><GroupPanel name="RIBBON_PAGE1_GROUP2_PANEL1"><ControlMenu name="RIBBON_PAGE1_GROUP2_PANEL1_MENU1" defaultAction="ID_~CommandA" visible="true" buttonStyle="4"><Item action="ID_~CommandA" visible="true" /><Item action="ID_~CommandB" visible="true" /><Item action="ID_~CommandC" visible="true" /><Item action="ID_~CommandD" visible="true" /></ControlMenu></GroupPanel></RibbonGroup></RibbonPage>
</RibbonPages>
介绍一下相关的字段
字段 | 说明 | 属性 |
---|---|---|
RibbonPages | Ribbon菜单集合 | |
RibbonPage | Ribbon菜单定义 | #name# (必需),Ribbon菜单的名字 #text# (必需),显示文本 #visible# (必需),显示状态,建议="true" |
RibbonGroup | 定义Ribbon菜单下的组 | #name# (必需),组的名字 #text# (必需),显示文本 #visible# (必需),显示状态,建议="true" |
GroupPanel | 定义组下的组面板,一个RibbonGroup里面有且仅有一个GroupPanel,至少目前测试下来是这样 | #name# (必需),组面板的名字 |
Control | 定义控件,一个组面板里可以有多个控件 | #action# (必需),引用行为的名字#visible# (必需),显示状态,建议="true" #buttonStyle# (必需),按钮样式,0=仅图标;1=仅文本;2=小图标;4=大图标 |
ControlMenu | 定义控件菜单,可以下拉选择 | #name# (必需),控件菜单的名字 #defaultAction# (必需),默认引用行为,一般用第一个下拉控件的行为 #visible# (必需),默认显示状态,建议="true" #buttonStyle# (必需),按钮样式,0=仅图标;1=仅文本;2=小图标;4=大图标 |
Item | 定义控件菜单中的下拉控件 | #action# (必需),引用行为的名字 #visible# (必需),显示状态,建议="true" |
4.编写策略文件
在Strategy文件夹下新建一个Environment-10-Part文件夹,表示我要修改零件环境下的菜单,然后在Environment-10-Part文件夹中新建一个LayoutStrategy-4-Expert.zcui文件夹,表示我要修改专家角色的菜单,然后在“Resource\Settings\Default\Strategy\Environment-10-Part\LayoutStrategy-4-Expert.zcui”中写入下面的内容:
<?xml version="1.0" encoding="UTF-8"?>
<Strategy profileType="UIProfile" environment="10" role="4-Expert"><DefaultCustomizations><Insert type="RibbonPage" name="RIBBON_PAGE1" topCollection="Layout_10_Part" leftSibling="#InsertAtLast" /></DefaultCustomizations>
</Strategy>
<Strategy>字段定义了一个策略,其中environment属性表示要适配的环境,role表示要适配的角色,下面解释一下角色与环境的概念。
角色是指下图里的四个可选项,双击可以切换,区别就是初级只显示一部分基础功能,专家显示全部功能。
四个角色的role属性如下:
角色 | role |
---|---|
全角色通用 | #AllRoles |
初级 | 1-Primary |
中级 | 2-Intermediate_default |
高级 | 3-Advanced |
专家 | 4-Expert |
环境是指软件当前打开的哪个界面,比如初始界面,零件,装配,工程图,这些界面下的菜单是不一样的,所以引入了环境的概念,下面是环境对应的environment属性以及大概的解释:
环境(英文) | 环境(中文说明) | environment |
---|---|---|
Top | 软件初始界面 | 0 |
Root | 多根文件初始界面(.Z3) | 1 |
Z3 | 多根文件零件/装配激活界面(.Z3) | 2 |
Sketch | 草图(.Z3/.Z3SKH) | 3 |
Sheet | *工程图(.Z3/.Z3DRW) | 4 |
Packet | 工程图包(旧版本才有该环境) | 5 |
Cam | 加工(.Z3/.Z3CAM) | 6 |
Animation | 动画 | 7 |
ExplodedView | 爆炸视图 | 8 |
3DSketch | 三维草图 | 9 |
Part | *单根零件(.Z3PRT/Standard) | 10 |
ECAD Assembly | ECAD装配(.Z3ASM/ECAD) | 11 |
ECAD Part | ECAD零件(.Z3PRT/ECAD) | 12 |
Assembly | *单根装配(.Z3ASM/Standard) | 13 |
Master Layout | 布局(.Z3PRT/Master Layout) | 17 |
4,10,13是最常用的,对应制图,零件和装配。
<Insert type="RibbonPage"...>,表示要插入一个Ribbon菜单,topCollection="Layout_10_Part"属性表示菜单插入到零件环境。不同环境的topCollection名称可以打开原生菜单定义文件看看,默认在“C:\Program Files\ZWSOFT\ZW3D 2025\Settings\ResourcePool\Layouts”路径。
leftSibling="#InsertAtLast"表示该自定义的菜单要插入到Ribbon选项卡的最后面。
如果想指定某个Ribbon在我们自定义Ribbon的左边或者右边,可以设置leftSibling(rightSibing)=其它Ribbon的名字。比如想让原生的“造型”Ribbon选项卡在自定义Ribbon菜单的右边,可以用rightSibing="UiFtrTool"属性,类似的如果想让原生的“造型”Ribbon选项卡在自定义Ribbon菜单的左边,可以设置leftSibing="UiFtrTool"。
5.编写引导程序
菜单随便放在一个位置,ZW3D不会无缘无故自己加载它,需要我们写一个DLL作为引导程序,DLL的开发介绍可以参考这个文章:
ZW3D二次开发入门_二次开发程序创建(2025+VS2022) - 知乎 (zhihu.com)
我的工程叫Z3Project,所以引导程序如下:
int Z3ProjectInit() {cvxPathAdd("C:\\Users\\Administrator\\Desktop\\RibbonPageUI\\Resource");//添加搜索路径
ZwProfileModuleNameRegister("RibbonPageUI");//注册自定义菜单模块
return 0;
}int Z3ProjectExit() {return 0;
}
cvxPathAdd是添加搜索路径,需要定位到自定义菜单路径的Resource文件夹,ZwProfileModuleNameRegister是注册自定义菜单模块,即Resource文件夹上一级文件夹的名称。
写完引导程序后,我们还需要预加载引导程序,即确保ZW3D启动时,引导程序一定会被加载,有两种常用方式可参考:
1.手动启动ZW3D,手动加载引导程序DLL,再关闭ZW3D,下次打开ZW3D时,引导程序DLL就会被自动加载
2.如果你的插件要给别人用,可以在安装包里写注册表来预加载DLL,注册表写法可参考下图信息(Resource是指定zrc资源文件用的,可以不写)
现在打开ZW3D,新建一个零件,就能看见菜单了:
6.多环境差异化策略
假设自定义Ribbon菜单需要部署在零件、装配、工程图下,那么我们需要写三个策略文件,内部基本一致,都是插入“RIBBON_PAGE1”,结构如下所示:
如果在不同环境下,需要对自定义Ribbon做局部调整,一个比较容易想到的方法是,根据需求再做几个RibbonPage,然后把这些不同的RibbonPage插入到具体环境中。这样明显是可行的,不过这样会产生很多重复工作量,维护起来也不方便,推荐使用写策略文件的方式来实现差异化,下面是关于RibbonPage常用的一些动作与对应字段属性。
动作 | 字段 | 属性 |
---|---|---|
插入”Ribbon菜单“ | Insert | #type#="RibbonPage" (必需) #name# (必需),Ribbon菜单的名字 #leftSibling/rightSibling# (必需),定义自定义菜单左边(右边)的菜单,"#InsertAtLast"表示插入到最后 #topCollection# (必需),插入哪个菜单集 **内嵌property字段的属性** #text# 显示文本,对于已定义的元素,(可选),对于未定义的元素,(必需) #visible# 显示状态,对于已定义的元素,(不填写),对于未定义的元素,(必需)填写 |
删除”Ribbon菜单“ | Remove | #type#="RibbonPage" (必需) #name# (必需),Ribbon菜单的名字 #topCollection# (必需),从哪个菜单集删除Ribbon菜单 |
插入“组”(不能插入已定义的组) | Insert | #type#="RibbonGroup" (必需) #name#=组的名字 (必需) #topParent# (必需),插入哪个RibbonPage #parent# (必需),插入哪个父节点 #leftSibling/rightSibling# (必需),定义自定义菜单左边/右边的菜单,"#InsertAtLast"表示插入到最后 **内嵌property字段的属性** #text# (必需),显示文本 #visible# (必需),显示状态 |
删除“组” | Remove | #type#="RibbonGroup" (必需) #name#=指定组的名字 (必需) #topParent# (必需),删除哪个RibbonPage下的组 #parent# (必需),删除哪个父节点下的组 |
插入”组“下的“组面板”(插入组后一定要插入组面板,控件无法直接加到组里,只能加到组面版里,无法插入已定义的组面板) | Insert | #type#="GroupPanel" (必需) #name#=组面板的名 (必需) #topParent# 插入哪个RibbonPage (必需) #parent# 插入哪个父节点 (必需) |
插入“控件” | Insert | #type#="Control" (必需) #name#=行为的名字 (必需) #topParent# 插入哪个<RibbonPage> (必需) #parent# 插入哪个<GroupPanel> (必需) #leftSibling/rightSibling# (必需),定义自定义菜单左边/右边的菜单,"#InsertAtLast"表示插入到最后 **内嵌property字段的属性** #buttonStyle# (必需),按钮样式,0=仅图标;1=仅文本;2=小图标;4=大按钮 #visible# (必需),显示状态 |
删除“控件” | Remove | #type#="Control" (必需) #name#=指定行为的名字 (必需) #topParent# (必需),删除哪个RibbonPage下的控件 #parent# (必需),删除哪个父节点下的控件 |
插入“控件菜单”(不能插入已定义的控件菜单) | Insert | #type#="ControlMenu" (必需) #name#=控件菜单名 (必需) #topParent# 插入哪个<RibbonPage> (必需) #parent# 插入哪个父节点下 (必需) #leftSibling/rightSibling# (必需),定义自定义菜单左边/右边的菜单,"#InsertAtLast"表示插入到最后 **内嵌property字段的属性** #defaultAction# (必需),默认行为的名字 #buttonStyle# (必需),按钮样式,0=仅图标;1=仅文本;2=小图标;4=大按钮 #visible# (必需),显示状态 |
删除“控件菜单” | Remove | #type#="ControlMenu" (必需) #name#=指定控件菜单的名字 (必需) #topParent# (必需),删除哪个RibbonPage下的控件菜单 #parent# (必需),删除哪个父节点下的控件菜单 |
插入“控件菜单”下的控件 | Insert | #type#="Item" (必需) #name#=行为的名字 (必需) #topParent# (必需),插入哪个RibbonPage #parent# (必需),插入哪个父节点下 #leftSibling/rightSibling# (必需),定义自定义菜单左边/右边的菜单,"#InsertAtLast"表示插入到最后 **内嵌property字段的属性** #visible# (必需),显示状态 |
删除“控件菜单”下的控件 | Remove | #type#="Item" (必需) #name#=指定行为的名字 (必需) #topParent# (必需),删除哪个RibbonPage下的控件 #parent# (必需),删除哪个父节点下的控件 |
修改 | Modify | 支持以下属性的字段都可以修改 #visible# #text# |
根据上面的信息,如果我们想在装配环境下,删除“1组”的后两个控件,删除“2组”,新建“3组”(注意,新建组时也需要新建组面版),并向3组中添加2个独立控件和一个控件菜单,并向控件菜单里添加两个控件,则需要对Environment-13-Assembly的LayoutStrategy-4-Expert.zcui文件做如下改动:
<?xml version="1.0" encoding="UTF-8"?>
<Strategy profileType="UIProfile" environment="13" role="4-Expert"><DefaultCustomizations><Insert type="RibbonPage" name="RIBBON_PAGE1" topCollection="Layout_13_Asm" leftSibling="#InsertAtLast" /><Remove type="Control" name="ID_~CommandC" topParent="RIBBON_PAGE1" parent="RIBBON_PAGE1_GROUP1_PANEL1" /><Remove type="Control" name="ID_~CommandD" topParent="RIBBON_PAGE1" parent="RIBBON_PAGE1_GROUP1_PANEL1" /><Remove type="RibbonGroup" name="RIBBON_PAGE1_GROUP2" topParent="RIBBON_PAGE1" parent="RIBBON_PAGE1" /><Insert type="RibbonGroup" name="RIBBON_PAGE1_GROUP3" topParent="RIBBON_PAGE1" parent="RIBBON_PAGE1" leftSibling="#InsertAtLast"><property text = "3组" visible="true" /></Insert><Insert type="GroupPanel" name="RIBBON_PAGE1_GROUP3_PANEL1" topParent="RIBBON_PAGE1" parent="RIBBON_PAGE1_GROUP3" /><Insert type="Control" name="ID_~CommandA" topParent="RIBBON_PAGE1" parent="RIBBON_PAGE1_GROUP3_PANEL1" leftSibling="#InsertAtLast"><property buttonStyle="4" visible="true" /></Insert><Insert type="Control" name="ID_~CommandB" topParent="RIBBON_PAGE1" parent="RIBBON_PAGE1_GROUP3_PANEL1" leftSibling="#InsertAtLast"><property buttonStyle="4" visible="true" /></Insert><Insert type="ControlMenu" name="RIBBON_PAGE1_GROUP3_PANEL1_MENU1" topParent="RIBBON_PAGE1" parent="RIBBON_PAGE1_GROUP3_PANEL1" leftSibling="#InsertAtLast"><property buttonStyle="4" defaultAction="ID_~CommandA" visible = "true" /></Insert><Insert type="Item" name="ID_~CommandA" topParent="RIBBON_PAGE1" parent="RIBBON_PAGE1_GROUP3_PANEL1_MENU1" leftSibling="#InsertAtLast"><property visible="true" /></Insert><Insert type="Item" name="ID_~CommandB" topParent="RIBBON_PAGE1" parent="RIBBON_PAGE1_GROUP3_PANEL1_MENU1" leftSibling="#InsertAtLast"><property visible="true" /></Insert></DefaultCustomizations>
</Strategy>
改动后,可以看到自定义Ribbon的前后对比情况
7.定制原生菜单
对原生菜单进行编辑,与上述差异化策略写法一致,唯一的不同点是,操作的对象都是原生菜单里的内容。
假设我想把零件环境专家角色的“造型”RibbonPage中的“编辑模型”组屏蔽掉,然后在“基础造型”组中插入“抽壳”命令,即下图所示:
想做到上图的效果,我们对Environment-10-Part的LayoutStrategy-4-Expert.zcui文件做如下改动:
<?xml version="1.0" encoding="UTF-8"?>
<Strategy profileType="UIProfile" environment="10" role="4-Expert"><DefaultCustomizations><Insert type="RibbonPage" name="RIBBON_PAGE1" topCollection="Layout_10_Part" leftSibling="#InsertAtLast" /><Remove type="RibbonGroup" name="GROUP122719" topParent="UiFtrTool" parent="UiFtrTool" /><Insert type="Control" name="ID_!FtShell1" topParent="UiFtrTool" parent="BasicShapes_GroupPanel" leftSibling="#InsertAtLast"><property buttonStyle="4" visible="true"/></Insert></DefaultCustomizations>
</Strategy>
原生RibbonPage的名字可以打开具体环境下的Layout文件查看,以零件环境为例,默认在C:\Program Files\ZWSOFT\ZW3D 2025\Settings\ResourcePool\Layouts\Layout_10_Part.zcui的<RibbonPages>中。
这15个<RibbonPage>就对应了零件环境下的这些Ribbon,至于如何确认具体哪个RibbonPage对应哪个界面里的Ribbon选项卡,这个目前没啥好办法
如果想知道原生Ribbon的各种内部标识是什么,可以用VS Code搜索安装路径下的Settings文件夹,默认是”C:\Program Files\ZWSOFT\ZW3D 2025\Settings\ResourcePool\RibbonPages.zcui“,比如我首先从Layout_10_Part.zcui知道了零件环境的“造型“RibbonPage叫”UiFtrTool“,那我们可以去Settings下的RibbonPages.zcui找到它的定义:
这部分定义与界面是对应的,可以从这里得知各个组以及控件的name属性。如果后续还想把自定义的行为加入原生Ribbon中,或者把原生行为加到自定义的Ribbon中,也是一样的写法,注意name写对即可。
8.无法正确显示菜单的终极解决方案
1.如果不管怎么改,菜单显示始终有问题,建议直接删除appdata下的临时菜单文件,然后再启动ZW3D试试,临时菜单文件夹默认在:
C:\Users\Administrator\AppData\Roaming\ZWSOFT\ZW3D\ZW3D 2025\custom\profiles
附件
本章涉及的菜单文件如下
链接:https://pan.baidu.com/s/1X-4TpDYfX3cvOwNNvXdTLw
提取码:tj1u