This commit is contained in:
2026-01-22 00:06:13 +08:00
parent f26fc95ea3
commit ed7476e54b
316 changed files with 4962 additions and 14039 deletions

View File

@@ -1,61 +0,0 @@
# ngSkinTools2 模块
ngSkinTools2 是一个强大的 Maya 蒙皮权重编辑工具。
## 📁 文件夹结构
```
ngskintools2/ # ngSkinTools2 核心模块
├── __init__.py # 模块初始化(原始)
├── launcher.py # 启动器脚本(新增)
├── api/ # API 接口
├── ui/ # 用户界面
├── operations/ # 操作功能
└── README.md # 本文档
```
## 🚀 使用方法
### 从工具架启动
1. 打开 Maya
2. 切换到 **Nexus_Rigging** 工具架
3. 点击 **ngSkin** 按钮
### 从 Python 启动
```python
from rigging_tools.ngskintools2 import launcher
launcher.LaunchNgSkinTools()
```
### 直接使用 ngSkinTools2 API
```python
from rigging_tools.ngskintools2 import open_ui
open_ui()
```
## ✨ 主要功能
- 高级权重绘制和编辑
- 权重镜像和传递
- 多层权重管理
- 权重导入/导出
- 影响对象管理
## 📝 注意事项
- 自动检测 Maya 版本并加载对应插件
- 支持 Maya 2018-2026 版本
- 如果当前版本没有完全匹配的插件,会自动使用向下兼容的版本
- 建议在绑定工作流程中使用
## 🔧 版本兼容性
启动器会自动检测当前 Maya 版本并加载对应的插件:
- **完全匹配**:优先使用与 Maya 版本完全匹配的插件
- **向下兼容**:如果没有完全匹配,使用小于等于当前版本的最高版本插件
- **兜底策略**:如果当前版本比所有可用插件都旧,使用最旧的可用插件
支持的 Maya 版本2018, 2019, 2020, 2022, 2023, 2024, 2025, 2026

View File

@@ -432,7 +432,7 @@ class InfluenceMappingConfig(Object):
return
if axis is not None and not isinstance(axis, int):
raise Exception("invalid axis type, need int")
raise Exception("无效的轴类型,需要整数")
self.__mirror_axis = axis
@@ -487,10 +487,10 @@ class InfluenceMapping(Object):
def __init__(self):
self.config = InfluenceMappingConfig() # type:InfluenceMappingConfig
"assigned config"
"分配的配置"
self.influences = [] # type: list[InfluenceInfo]
"Source influences list. Can be assigned to result of :py:meth:`Layers.list_influences`"
"源影响列表. 可以分配给结果 :py:meth:`Layers.list_influences`"
self.destinationInfluences = None
self.calculatedMapping = None
@@ -498,7 +498,7 @@ class InfluenceMapping(Object):
def calculate(self):
mirror_mode = self.config.mirror_axis is not None
log.info("calculate influence mapping, mirror mode: %s", mirror_mode)
log.info("计算影响映射,镜像模式: %s", mirror_mode)
if self.destinationInfluences is None:
self.destinationInfluences = self.influences

View File

@@ -56,7 +56,7 @@ class LayerEffects(Object):
mirror_mask = mirror_dq = mirror_weights = everything
logger.info(
"configure mirror: layer %s mask %r weights %r dq %r direction %r",
"配置镜像: 图层 %s 遮罩 %r 权重 %r dq %r 方向 %r",
self.__layer.name,
mirror_mask,
mirror_weights,
@@ -103,7 +103,7 @@ class Layer(Object):
@classmethod
def load(cls, mesh, layer_id):
if layer_id < 0:
raise Exception("invalid layer ID: %s" % layer_id)
raise Exception("无效图层 ID: %s" % layer_id)
result = Layer(mesh, layer_id)
result.reload()
return result
@@ -112,7 +112,7 @@ class Layer(Object):
self.mesh = mesh
self.id = id
self.effects = LayerEffects(self) # type: LayerEffects
"configure effects for this layer"
"配置此图层的效果"
self.__state = None
if state is not None:

View File

@@ -63,7 +63,7 @@ class Mirror(Object):
"""
:type mapping: map[int] -> int
"""
log.info("mapping updated: %r", mapping)
log.info("映射已更新: %r", mapping)
mapping_as_string = ','.join(str(k) + "," + str(v) for (k, v) in list(mapping.items()))
plugin.ngst2Layers(self.target, configureMirrorMapping=True, influencesMapping=mapping_as_string)
@@ -119,7 +119,7 @@ class Mirror(Object):
existing_ref_mesh = self.get_reference_mesh()
if existing_ref_mesh:
cmds.select(existing_ref_mesh)
raise Exception("symmetry mesh already configured for %s: %s" % (str(sc), existing_ref_mesh))
raise Exception("对称网格已配置为 %s: %s" % (str(sc), existing_ref_mesh))
def get_shape(node):
return cmds.listRelatives(node, shapes=True)[0]
@@ -177,7 +177,7 @@ def set_reference_mesh_from_selection():
selection = cmds.ls(sl=True, long=True)
if len(selection) != 2:
log.debug("wrong selection size")
log.debug("错误的选择尺寸")
return
m = Mirror(selection[1])

View File

@@ -83,7 +83,7 @@ class Session(Object):
@signal.on(self.events.targetChanged)
def on_target_change():
log.info("target changed: clearing target context")
log.info("目标已更改:清除目标上下文")
self.context.selected_layers.set([])
self.events.nodeSelectionChanged.emit()

View File

@@ -67,7 +67,7 @@ def add_influences(influences, target):
def long_names(names):
result = set(cmds.ls(names, long=True))
if len(result) != len(names):
raise Exception("could not convert to a list of influences names: " + str(names))
raise Exception("无法转换为影响名称列表: " + str(names))
return result
existing = long_names([i.name if not i.path else i.path for i in list_influences(skin_cluster)])

View File

@@ -103,7 +103,7 @@ def merge_layers(layers):
# verify that all layers are from the same parent
for i, j in zip(layers[:-1], layers[1:]):
if i.mesh != j.mesh:
raise Exception("layers are not from the same mesh")
raise Exception("层不是来自同一个网格")
result = plugin.ngst2tools(
tool="mergeLayers",

View File

@@ -49,7 +49,7 @@ class LayersTransfer(Object):
def calc_influences_mapping_as_flat_list(self):
mapping_pairs = list(self.influences_mapping.asIntIntMapping(self.influences_mapping.calculate()).items())
if len(mapping_pairs) == 0:
raise Exception("no mapping between source and destination influences")
raise Exception("源和目标影响之间没有映射")
# convert dict to flat array
return list(itertools.chain.from_iterable(mapping_pairs))

View File

@@ -54,12 +54,12 @@ class Undo(Object):
self.name = name
def __enter__(self):
log.debug("UNDO chunk %r: start", self.name)
log.debug("UNDO chunk %r: 开始", self.name)
cmds.undoInfo(openChunk=True, chunkName=self.name)
return self
def __exit__(self, _type, value, traceback):
log.debug("UNDO chunk %r: end", self.name)
log.debug("UNDO chunk %r: 结束", self.name)
cmds.undoInfo(closeChunk=True)

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -1,353 +0,0 @@
<!DOCTYPE html>
<!-- saved from url=(0106)https://apps.autodesk.com/RVT/en/Detail/HelpDoc?appId=2231912794620250494&appLang=en&os=Win64&mode=preview -->
<html lang="en" class=" js canvas canvastext no-touch hashchange history draganddrop rgba multiplebgs borderimage boxshadow textshadow cssgradients csstransitions sessionstorage" data-lt-installed="true"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Autodesk App Store - help file</title><link rel="stylesheet" href="./Resources/appstore-combined.min.css"></head>
<body>
<div id="main" class="detail-page helpdoc-page clearfix" style="min-height: 619px;">
<div id="content">
<div id="helpdoc-head">
<img id="helpdoc-head-icon" src="./Resources/resized_5ca803cc-8cd1-4cbe-8825-98efaeef156e_.png" alt="ngSkinTools 2">
<div id="helpdoc-head-description">
<h1 id="helpdoc-product-title">ngSkinTools 2</h1>
<div class="clear"></div>
<hr>
<div class="seller">Viktoras Makauskas</div>
<div class="description">ngSkinTools is a skinning plugin for Autodesk® Maya®, introducing new concepts to character skinning such as layers, any-pose-mirroring, enhanced paint brushes, true smoothing, and more.</div>
</div>
<div class="clear"></div>
</div>
<div class="clear"></div>
<div id="helpdoc-tag"><a href="#description" class="helpdoc-breadcrumb">Description</a>
<a href="#generalinfo" class="helpdoc-breadcrumb">General Usage Instructions</a>
<a href="#screensinfo" class="helpdoc-breadcrumb">Screenshots</a>
<a href="#inunininfo" class="helpdoc-breadcrumb">Installation/Uninstallation</a>
<a href="#knownissueinfo" class="helpdoc-breadcrumb">Known Issues</a>
<a href="#contactinfo" class="helpdoc-breadcrumb">Contact</a>
<a href="#versionhistoryinfo" class="">Version History</a>
<div class="clear"></div>
</div><div class="helpdoc-element description">
<div class="clear"></div>
<h1 id="description">Description</h1>
<div class="helpdoc-text">
<h3 id="layers">Layers</h3>
<p>Skinning
layers are a central feature of ngSkinTools. With them, you break your rig down
into easier manageable parts and edit them separately, then blend everything
together through layer transparency.</p>
<p>Theyre
not just a simple way to make your work more organized - they also physically
isolate groups of influences from the rest of the rig, so paint and edit
operations wont mix-in influences you were not expecting. This also allows you
to do things that were impossible before: per-layer mirroring, adjusting
influence weight up/down through layer transparency, blend transferred weights
with previous weights, to name a few.</p>
<h3 id="viewport-tools">Viewport
tools</h3>
<p>Just
like in the previous version, ngSkinTools brings its own weight painting tools.
Improving viewport experience is the main focus of V2, and it's complete revamp
over the previous implementation.</p>
<ul>
<li>Selecting
influences on screen, a #1 requested feature from users, is nowhere. Just hold
“S” and drag over the surface to select dominant influence from that part of
the mesh, or hover over a joint pivot to select precisely the joints you
want;</li>
<li>In
addition to the usual surface projection mode for the brush, the new “screen”
brush projection mode is useful when you want to quickly set weights for both
sides of the mesh;</li>
<li>Custom
shortcuts while in paint mode allow for quick access to intensity
presets;</li>
<li>Color
feedback is now provided through VP2 APIs, greatly improving the performance
of displayed meshes.</li></ul><br>
<h3 id="smoothing">Smoothing</h3>
<p>Keeping
weights in harmony with each other is not easy. ngSkinTools help you smooth
weights with the control you need, allowing you to control the intensity, number
of iterations and effective radius. For very dense meshes, added “iterations”
argument now allows for the quicker spread of smoothness over larger areas of
the mesh.</p>
<p>The
“relax” tool from V1 is gone. With major performance rework, youll notice that
simple flood-smoothing is now much faster and should be a near-instant operation
even with large meshes.</p>
<p>The
opposite “brother” of smooth brush, “sharpen”, is also there - for cases where
you want to just bring out the dominant influences</p>
<h3 id="mirroring">Mirroring</h3>
<p>Mirroring
is one of the most frequent automated tasks you might want from your skinning
tool. With ngSkinTools, youll be able to:</p>
<ul>
<li>Mirror
rigs in any pose; no need to switch to T-pose;</li>
<li>Have
granular control over left/right/center influences mapping, matching
left/right joints by naming convention, joint labels, etc;</li>
<li>Easily
mirror parts of your rig by leveraging layers;</li>
<li>Automatic
mirroring of weights to the opposite side as you paint so that you dont need
to get distracted from painting while working on symmetrical layers.</li></ul><br>
<h3 id="layer-effects">Layer
effects</h3>
<p>With
the “mirror as a layer effect” feature, ngSkinTools introduce a new concept to
ngSkinTools - layer effects. This differs from automatic mirroring of weights as
its not directly modifying your layer weights; instead, its a post-effect that
happens in the background buffer. This has multiple benefits, like a much
cleaner seamline of left/right sides, the ability to tweak mirroring settings
AFTER weights are painted, etc.</p>
<h3 id="compatibility">Compatibility</h3>
<p>As
it's predecessor, ngSkinTools2 operates on standard Maya skinCluster (also known
as “smooth skin”), so no custom nodes will be required to use your rig. The
plugin has a couple of custom nodes, but theyre only required while you work on
setting up your skin weights and can be deleted after, so your work should stay
compatible with most pipelines out there.</p>
<h3 id="performance">Performance</h3>
<p>A
lot of speed improvements have been made since V2, like improving the
utilization of modern multi-core processors, or eliminating bottlenecks through
much heavier use of performance profiling. Having a responsive, snappy tool is
always a pleasure to work with.</p></div></div>
<div class="helpdoc-element ">
<div class="clear"></div>
<h1 id="generalinfo">General Usage Instructions</h1>
<div class="helpdoc-text"><p>The installer from Autodesk App Store loads the application under the Custom shelf.</p>
<p><a href="http://www.ngskintools.com/docs"target="_blank">http://www.ngskintools.com/docs/</a></p></div>
</div>
<div id="helpdoc-element-screenshot" class="helpdoc-element ">
<div class="clear"></div>
<h1 id="screensinfo">Screenshots</h1>
<div>
<div class="helpdoc-screenshot">
<a href="./Resources/original_89910fa8-2c30-4376-a905-12c8a003d16b_.png" title="" data-mime="Image" rel="gallery">
<div class="helpdoc-img-container">
<span class="helper"></span>
<img class="helpdoc-screenshot-img" src="./Resources/original_89910fa8-2c30-4376-a905-12c8a003d16b_.png">
<span class="helper"></span>
</div>
</a>
</div>
<div class="clear"></div>
</div>
</div>
<div class="helpdoc-command helpdoc-element helpdoc-element-hidden">
<div class="clear"></div>
<h1 id="commandinfo">Commands</h1>
<div>
</div>
</div>
<div class="helpdoc-element ">
<div class="clear"></div>
<h1 id="inunininfo">Installation/Uninstallation</h1>
<div class="helpdoc-text"><div id="pills-tabContent">
<div id="pills-windows">
<p>The installer that ran when you downloaded this plug-in from Autodesk App Store has already installed the plug-in. Windows only: To uninstall this plug-in, simply rerun the installer downloaded, and select the 'Uninstall' button, or you can uninstall it from 'Control Panel\Programs\Programs and Features', just as you would uninstall any other application from your system. The panel on the Plug-ins tab will not be removed until Maya is restarted.</p>
</div>
</div>
<p>Linux and OSX: To uninstall this plug-in, simply delete the module directory from your system. The panel on the Plug-ins tab will not be removed until Maya is restarted.</p>
<div id="pills-tabContent">
<div id="pills-windows">
<p>Download .msi and run it on your computer. The installation will place files in&nbsp;<code>C:\ProgramData\Autodesk\ApplicationPlugins\ngskintools2</code>&nbsp;(unless your&nbsp;<code>%ProgramData%</code>&nbsp;the environment variable is different).</p>
</div>
</div>
<p>Using an autoloader system, nothing needs to be configured additionally. Maya scans autoloader locations for plugins at startup and configures each discovered plugin automatically. The autoloader will create a “ngSkinTools2” shelf with a button to open UI.</p>
<p>Now, restart Maya and a new tab&nbsp;ngSkinTools2&nbsp;should appear on your shelf.</p></div>
</div>
<h3>Available on</h3>
<table>
<tr><td><img height=48 src="./Resources/win64.png" /></td><td><img height=48 src="./Resources/macos64.png" /></td><td><img height=48 src="./Resources/linux64.png" /></td></tr>
<tr><td align="center">Windows</td><td align="center">Mac OSX</td><td align="center">Linux</td></tr>
</table>
<div class="helpdoc-element helpdoc-element-hidden">
<div class="clear"></div>
<h1 id="addinfo">Additional Information</h1>
<div class="helpdoc-text"></div>
</div>
<div class="helpdoc-element ">
<div class="clear"></div>
<h1 id="knownissueinfo">Known Issues</h1>
<div class="helpdoc-text"><p>For upcoming features and bugfixes, visit <a href="http://ngskintools.com/v2/roadmap"target="_blank">ngSkinTools v2 public roadmap</a>.</p></div>
</div>
<div class="helpdoc-element ">
<div class="clear"></div>
<h1 id="contactinfo">Contact</h1>
<div>
<div>
<div class="">Company Name: Viktoras Makauskas</div>
<div class="">Company URL: <a href="http://www.ngskintools.com/" target="_blank">http://www.ngskintools.com</a></div>
<div class="">Support Contact: <a href="mailto:support@ngskintools.com">support@ngskintools.com</a></div>
</div>
<div class="helpdoc-block ">
<div class="clear"></div>
<h1 id="authorinfo">Author/Company Information</h1>
<div class="helpdoc-text">Viktoras Makauskas</div>
</div>
<div class="helpdoc-block ">
<div class="clear"></div>
<h1 id="supportinfo">Support Information</h1>
<div class="helpdoc-text"><p>If you have a problem, a question, a feature request, let me know. Your feedback is what drives the project forward!<strong>&nbsp;</strong></p>
<p><strong>Documentation</strong></p>
<p>Features, tutorials &amp; FAQ available at <a href="http://ngskintools.com/v2/" target="_blank">ngSkinTools official website</a>.&nbsp;</p>
<p><strong>Contact in private</strong></p>
<p>Use the <a href="http://ngskintools.com/contact/" target="_blank">online contact form</a> or email to <a href="mailto:support@ngskintools.com">support@ngskintools.com</a>.</p></div>
</div>
</div>
</div>
<div class="helpdoc-version helpdoc-element">
<div class="clear"></div>
<h1 id="versionhistoryinfo">Version History</h1>
<div>
<table class="helpdoc-table" id="helpdoc-table-version">
<colgroup>
<col style="width:150px">
<col style="width:680px">
</colgroup>
<tbody><tr>
<th>Version Number</th>
<th>Version Description</th>
</tr>
<tr>
<td>
<p>
2.0.24
</p>
</td>
<td style="white-space: pre-wrap;">2.0.23 (2021-Mar-12)
* Fixed: stylus pressure is not updated during the stroke;
* Fixed: Maya crashes when smoothing an empty layer with "adjust existing influences only";
* Fixed: (regression) UI is not opening on macOS;
</td>
</tr>
<tr>
<td>
<p>
2.0.23
</p>
</td>
<td style="white-space: pre-wrap;">2.0.23 (2021-Mar-10)
* Added: adjustable brush size: don't reset to zero when changing brush size in the viewport.
* Added: randomize influence colors in paint/display settings;
* Added: layers on/off "eye" button in layers tree UI;
* Fixed: ngSkinTools will not modify skinCluster's `normalizeWeights` value anymore; for performance boost, you still
can disable skinCluster's normalization by setting `normalizeWeights=None`. In "normalizeWeights:interactive"
skinCluster mode, Maya will no longer complain that "The weight total would have exceeded 1.0". ngSkinTools will try
extra hard to normalize each vertex to a perfect 1.0;
* Fixed: UI is not displayed correctly on high DPI displays when UI scaling is enabled in Maya;
</td>
</tr>
<tr>
<td>
<p>
2.0.22
</p>
</td>
<td style="white-space: pre-wrap;">2.0.22 (2021-Feb-06)
Added: convenience tool for adding influences to existing skin clusters. Select influences, target mesh and select “Tools | Add Influence”;
Fixed: weights will now display properly when viewport option “use default material” is turned on;
Added: (v1 feature) Limit max influences per vertex before writing to skin cluster;
Added: (v1 feature) Prune small weights before writing to skin cluster.
2.0.21 (2021-Jan-11)
Added: copy/paste vertex weights between different selections (tab “tools” - “copy component weights/paste average component weights”);
Added: tool “fill transparency” - for all empty vertices in a layer, assign weights from closest non-empty vertex;
Added: “duplicate layer” operation;
Added: “Merge layers” operation: combine selected layers into one.
2.0.20 (2020-Dec-02)
Fixed: Linux: crashing on startup
2.0.19 (2020-Nov-28)
Added: influences mapping in mirror screen will now allow matching joints by DG connections; if symmetrical joints are linked between themselves with message connections, ngSkinTools will be able to leverage that information when mirroring weights;
Added: symmetry mesh: an option to provide an alternative mesh for calculating vertex mapping for mirroring;
Fixed: deleting visibility node can crash Maya sometimes, e.g. switching to component mode (F8) while paint tool is active
2.0.18 (2020-Nov-20)
Added: “use all joints” option for “weights from closest joint” tool; few internal optimizations to speedup operation;
Added: “weights from closest joint” option: create a new layer
Fixed: “weights from closest joint”: the tool is only using joints as spots, but not as segments;
Fixed: after “weights from closest joint” operation influences list is not refreshed;
Fixed: “weights from closest joint”: “assign” button sometimes disabled;
2.0.17 (2020-Nov-19)
Fixed: “resume in workspace” error while opening UI
2.0.16 (2020-Nov-15)
Added: skin data will be compressed for ngSkinTools data nodes, which should substantially reduce file size for scenes with lots of skinning layers;
Added: paint mode intensity sliders are now exponential: “smooth”, “add” and “sharpen” sliders will now be more precise for lower values, and “scale” mode will allow for more precision when setting high values.
2.0.15 (2020-Nov-10)
Added: new “Set Weights” tab contains tools to apply weights to vertex/edge/polygon selection instead of painting.
Added: a new option for smooth tool - “only adjust existing vertex influences”; when this is turned on, the smooth tool will prevent influences weights spreading across the surface
Fixed: layer mirror effects correctly saved/loaded in files;
Fixed: mask mirror effect was not correctly used by layer blending engine
2.0.14 (2020-Oct-04)
Fixed: occasional crashes when using mirror effect on layers;
Additional stability fixes.
2.0.13 (2020-Oct-04)
Fixed: minor bug in 2.0.12 blocks UI from opening;
2.0.12 (2020-Oct-03)
Added: option to view used influences in influences list;
Added: hide “DQ weights” channel in influences list if skin cluster skinning method is not set to “Weight Blended”;
2.0.11 (2020-Oct-01)
Fixed: broken Linux builds
2.0.10 (2020-Sep-26)
Added: pressing “f” while painting focuses viewport camera to current paint target; for joints and other influences, the current joint pivot is used as camera interest point; when current paint target is a mask, viewport centers around painted values;
Fixed: influence mapping UI error if some influences are no joints;
2.0.9 (2020-Sep-11)
Fixed: undo paint crashing Maya;
Fixed: incorrect brush behavior with multiple viewports open;
Fixed: incorrect mesh display / VP2 transparency setting sensitive;
Fixed: clearing selection while painting does not update the display of current mesh;</td>
</tr>
</tbody></table>
</div>
</div>
</div>
</div></body></html>

View File

@@ -1,158 +1,43 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
ngSkinTools2 启动器
用于从工具架快速启动 ngSkinTools2
ngSkinTools2 Launcher
Provides a simple interface to launch ngSkinTools2 from Maya shelf buttons
"""
import sys
import os
def get_maya_version():
"""
获取当前 Maya 版本号
返回格式: '2023', '2024'
"""
try:
from maya import cmds
maya_version = cmds.about(version=True)
# Maya 版本格式可能是 "2023" 或 "2023.1" 等,取主版本号
return maya_version.split('.')[0]
except Exception as e:
print(f"Failed to get Maya version: {e}")
return None
def find_compatible_plugin_dir(base_dir, maya_version):
"""
查找兼容的插件目录
优先使用完全匹配的版本,如果没有则向下查找最接近的版本
Args:
base_dir: 插件基础目录 (plug-ins/)
maya_version: Maya 版本号字符串,如 '2023'
Returns:
插件目录路径,如果找不到则返回 None
"""
if not maya_version:
return None
try:
maya_ver_int = int(maya_version)
except ValueError:
print(f"Invalid Maya version format: {maya_version}")
return None
# 列出所有可用的插件版本目录
available_versions = []
if os.path.exists(base_dir):
for item in os.listdir(base_dir):
item_path = os.path.join(base_dir, item)
if os.path.isdir(item_path):
try:
ver_int = int(item)
available_versions.append((ver_int, item_path))
except ValueError:
continue
if not available_versions:
return None
# 按版本号排序
available_versions.sort(reverse=True)
# 首先尝试完全匹配
for ver, path in available_versions:
if ver == maya_ver_int:
print(f"Found exact match plugin for Maya {maya_version}: {path}")
return path
# 如果没有完全匹配,使用小于等于当前版本的最高版本
for ver, path in available_versions:
if ver <= maya_ver_int:
print(f"Using compatible plugin for Maya {maya_version}: {path} (version {ver})")
return path
# 如果当前版本比所有可用版本都旧,使用最旧的版本
oldest_ver, oldest_path = available_versions[-1]
print(f"Warning: Maya {maya_version} is older than available plugins. Using oldest: {oldest_path} (version {oldest_ver})")
return oldest_path
def LaunchNgSkinTools():
"""
启动 ngSkinTools2 主界面
自动检测 Maya 版本并加载对应的插件
Launch ngSkinTools2 main UI window
This function handles two scenarios:
1. If module alias exists (ngSkinTools2 -> rigging_tools.ngskintools2), use it
2. If not, create the alias on-the-fly and then launch
"""
try:
from maya import cmds, mel
# 获取当前 Maya 版本
maya_version = get_maya_version()
if not maya_version:
print("Warning: Could not determine Maya version, will try to continue anyway")
else:
print(f"Detected Maya version: {maya_version}")
# 将当前目录添加到 Python 路径,并使用别名
# 这样 ngSkinTools2 内部的 "from ngSkinTools2.ui" 就能找到模块
current_dir = os.path.dirname(os.path.abspath(__file__))
parent_dir = os.path.dirname(current_dir)
# 添加父目录到路径
if parent_dir not in sys.path:
sys.path.insert(0, parent_dir)
# 添加当前目录到路径,作为 ngSkinTools2 模块
if current_dir not in sys.path:
sys.path.insert(0, current_dir)
# 在 sys.modules 中创建别名,让 ngSkinTools2 指向 ngskintools2
import rigging_tools.ngskintools2 as ngskintools2_module
sys.modules['ngSkinTools2'] = ngskintools2_module
# 查找并添加插件路径
# 根据 Maya 版本自动选择对应的插件目录
plugin_base_dir = os.path.join(current_dir, 'plug-ins')
plugin_dir = find_compatible_plugin_dir(plugin_base_dir, maya_version)
if plugin_dir and os.path.exists(plugin_dir):
# 添加插件路径
current_plugin_path = os.environ.get('MAYA_PLUG_IN_PATH', '')
if plugin_dir not in current_plugin_path:
os.environ['MAYA_PLUG_IN_PATH'] = plugin_dir + os.pathsep + current_plugin_path
# 加载插件
# Check if module alias already exists
if 'ngSkinTools2' not in sys.modules:
# Create the alias on-the-fly
try:
# 检查插件是否已加载
if not cmds.pluginInfo('ngSkinTools2', query=True, loaded=True):
cmds.loadPlugin(os.path.join(plugin_dir, 'ngSkinTools2.mll'))
print(f"ngSkinTools2 plugin loaded successfully from {plugin_dir}")
else:
print(f"ngSkinTools2 plugin already loaded")
except RuntimeError:
# 插件不存在,尝试加载
try:
cmds.loadPlugin(os.path.join(plugin_dir, 'ngSkinTools2.mll'))
print(f"ngSkinTools2 plugin loaded successfully from {plugin_dir}")
except Exception as plugin_error:
print(f"Warning: Could not load ngSkinTools2 plugin: {plugin_error}")
except Exception as plugin_error:
print(f"Warning: Could not load ngSkinTools2 plugin: {plugin_error}")
else:
print(f"Warning: Plugin directory not found: {plugin_dir}")
import rigging_tools.ngskintools2
sys.modules['ngSkinTools2'] = rigging_tools.ngskintools2
print("Created ngSkinTools2 module alias")
except ImportError as import_err:
print(f"Error: Failed to import rigging_tools.ngskintools2 - {import_err}")
print("Please ensure the ngskintools2 directory is in the correct location")
return
# 现在可以导入并打开 UI
from rigging_tools.ngskintools2 import open_ui
# Now import and launch
from ngSkinTools2 import open_ui
open_ui()
print("ngSkinTools2 UI opened successfully")
print("ngSkinTools2 launched successfully")
except ImportError as e:
print(f"Error: Failed to import ngSkinTools2 - {e}")
print("Please ensure ngSkinTools2 is properly installed")
except Exception as e:
print(f"Failed to open ngSkinTools2: {e}")
print(f"Error: Failed to launch ngSkinTools2 - {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
LaunchNgSkinTools()

View File

@@ -1,32 +0,0 @@
ngSkinTools Software License Agreement.
This is a legal agreement between you and ngSkinTools author (Viktoras Makauskas) covering your use of
ngSkinTools (the "Software").
1) ngSkinTools is provided as freeware.
2) ngSkinTools Software is owned by Viktoras Makauskas and is protected by copyright laws and international
treaty provisions. Therefore, you must treat the Software like any other copyrighted material.
3) You may not distribute, rent, sub-license or otherwise make available to others the Software or
documentation or copies thereof, except as expressly permitted in this License without prior written consent
from ngSkinTools author (Viktoras Makauskas). In the case of an authorized transfer, the transferee must agree
to be bound by the terms and conditions of this License Agreement.
4) You may not remove any proprietary notices, labels, trademarks on the Software or documentation. You may not
modify, de-compile, disassemble or reverse engineer the Software.
5) Limited warranty: ngSkinTools software and documentation are "as is" without any warranty as to their
performance, merchantability or fitness for any particular purpose. The licensee assumes the entire risk as to
the quality and performance of the software. In no event shall ngSkinTools author or anyone else who has been
involved in the creation, development, production, or delivery of this software be liable for any direct,
incidental or consequential damages, such as, but not limited to, loss of anticipated profits, benefits, use,
or data resulting from the use of this software, or arising out of any breach of warranty.
Copyright (C) 2009-2020 Viktoras Makauskas
http://www.ngskintools.com
support@ngskintools.com
All rights reserved.

View File

@@ -22,4 +22,4 @@ class ObservableValue(Object):
if default != Undefined:
return default
raise Exception("using observable value before setting it")
raise Exception("在设置之前使用可观察值")

View File

@@ -24,7 +24,7 @@ def action_copy_cut(session, parent, cut):
operation(session.state.currentLayer.layer, influences)
operation_name = "Cut" if cut else "Copy"
result = actions.define_action(parent, operation_name + " weights to clipboard", callback=cut_copy_callback)
result = actions.define_action(parent, operation_name + " 将权重复制到剪贴板", callback=cut_copy_callback) # 将权重复制到剪贴板 weights to clipboard
@signal.on(session.events.currentLayerChanged, session.events.currentInfluenceChanged, qtParent=parent)
def on_selection_changed():
@@ -49,13 +49,13 @@ def action_paste(session, parent, operation):
api.paste_weights(session.state.currentLayer.layer, operation, influences=influences)
labels = {
PasteOperation.add: 'Paste weights (add to existing)',
PasteOperation.subtract: 'Paste weight (subtract from existing)',
PasteOperation.replace: 'Paste weights (replace existing)',
PasteOperation.add: '粘贴权重(添加到现有)',
PasteOperation.subtract: '粘贴权重(从现有值中减去)',
PasteOperation.replace: '粘贴权重(替换现有)',
}
result = actions.define_action(parent, labels[operation], callback=paste_callback)
result.setToolTip("Paste previously copied weights from clipboard")
result.setToolTip("从剪贴板粘贴先前复制的权重")
@signal.on(session.events.currentLayerChanged)
def on_selection_changed():

View File

@@ -31,7 +31,7 @@ def buildAction_export(session, parent):
result = actions.define_action(
parent,
"Export Layers to Json...",
"导出图层...",
callback=export_callback,
tooltip="Save layer info to external file, suitable for importing weights to different scene/mesh",
)
@@ -79,7 +79,7 @@ def buildAction_import(session, parent, file_dialog_func=None):
t.customize_callback = transfer_dialog
t.execute()
result = actions.define_action(parent, "Import Layers from Json...", callback=import_callback, tooltip="Load previously exported weights")
result = actions.define_action(parent, "导入图层...", callback=import_callback, tooltip="Load previously exported weights")
@signal.on(session.events.targetChanged, qtParent=parent)
def update():

View File

@@ -28,8 +28,8 @@ def build_action_import_v1(session, parent):
update_state()
session.events.targetChanged.emitIfChanged()
result = actions.define_action(parent, "Convert From v1.0 Layers", callback=do_convert)
result.setToolTip("Convert skinning layers from previous version of ngSkinTools; after completing this action, v1 nodes will be deleted.")
result = actions.define_action(parent, "从v1.0图层转换", callback=do_convert)
result.setToolTip("“转化旧版ngSkinTools的图层;完成此操作后v1节点将被删除。")
@signal.on(session.events.targetChanged)
def update_state():

View File

@@ -28,7 +28,7 @@ def initializeLayers(createFirstLayer=True):
layers = ngSkinTools2.api.init_layers(target)
with ngSkinTools2.api.suspend_updates(target):
if createFirstLayer:
layer = layers.add("Base weights")
layer = layers.add("基础权重") #Base weights
layer.set_current()
Mirror(target).set_mirror_config(config.mirrorInfluencesDefaults)
@@ -36,7 +36,7 @@ def initializeLayers(createFirstLayer=True):
if ngSkinTools2.api.is_slow_mode_skin_cluster(target):
dialogs.info(
"ngSkinTools switched to slow maya API for setting skin cluster weights for this skinCluster, to workaround a Maya bug when skinCluster uses dg nodes as inputs"
"切换为设置皮肤集群权重,以解决 Maya皮肤集群使用装点作为输入时的错误。"
)
@@ -73,15 +73,15 @@ def build_action_initialize_layers(session, parent):
def do_initialize():
if import_v1_actions.can_import(session):
q = (
"Skinning layers from previous version of ngSkinTools are present on this mesh. This operation will initialize "
"skinning layers from scratch, discarding previous layers information. Do you want to continue?"
"来自旧版 ngSkinTools的皮肤层存在于此网格上。此操作将初始化"
"从头开始剥离图层,丢弃之前的图层信息。您要继续吗?"
)
if not dialogs.yesNo(q):
return
initializeLayers()
result = actions.define_action(parent, "Initialize Skinning Layers", callback=do_initialize)
result = actions.define_action(parent, "初始化蒙皮层", callback=do_initialize)
@signal.on(session.events.nodeSelectionChanged)
def update():
@@ -95,7 +95,7 @@ def build_action_initialize_layers(session, parent):
def buildAction_createLayer(session, parent):
from ngSkinTools2.ui import actions
result = actions.define_action(parent, "Create Layer", callback=addLayer, icon=":/newLayerEmpty.png", shortcut=QtCore.Qt.Key_Insert)
result = actions.define_action(parent, "创建图层", callback=addLayer, icon=":/newLayerEmpty.png", shortcut=QtCore.Qt.Key_Insert)
@signal.on(session.events.targetChanged)
def update_to_target():
@@ -109,7 +109,7 @@ def buildAction_createLayer(session, parent):
def buildAction_deleteLayer(session, parent):
from ngSkinTools2.ui import actions
result = actions.define_action(parent, "Delete Layer", callback=deleteSelectedLayers, shortcut=QtCore.Qt.Key_Delete)
result = actions.define_action(parent, "删除图层", callback=deleteSelectedLayers, shortcut=QtCore.Qt.Key_Delete)
@signal.on(session.context.selected_layers.changed, session.events.targetChanged, qtParent=parent)
def update_to_target():
@@ -126,12 +126,12 @@ def setCurrentLayer(layer):
:type layer: ngSkinTools2.api.layers.Layer
"""
if not session.active():
logger.info("didn't set current layer: no session")
logger.info("未设置当前图层:没有会话")
if not session.state.layersAvailable:
logger.info("didn't set current layer: layers not enabled")
logger.info("未设置当前图层:图层未启用")
logger.info("setting current layer to %r on %r", layer, session.state.selectedSkinCluster)
logger.info("将当前图层设置为 %r on %r", layer, session.state.selectedSkinCluster)
layer.set_current()
session.events.currentLayerChanged.emitIfChanged()
@@ -158,7 +158,7 @@ def deleteSelectedLayers():
class ToggleEnabledAction(Action):
name = "Enabled"
name = "启用图层" #Enabled
checkable = True
def __init__(self, session):
@@ -192,7 +192,7 @@ class ToggleEnabledAction(Action):
for i in selected_layers:
i.enabled = enabled
logger.info("layers toggled: %r", selected_layers)
logger.info("图层已切换: %r", selected_layers)
session.events.layerListChanged.emitIfChanged()
@@ -209,8 +209,8 @@ def build_action_randomize_influences_colors(session, parent):
:type session: ngSkinTools2.api.session.Session
"""
result = QAction("Randomize colors", parent)
result.setToolTip("Choose random colors for each influence, selecting from Maya's pallete of indexed colors")
result = QAction("随机颜色", parent)
result.setToolTip("为每个影响选择随机颜色从Maya的索引色板中选择。")
def color_filter(c):
brightness = c[0] * c[0] + c[1] * c[1] + c[2] * c[2]

View File

@@ -9,8 +9,8 @@ log = getLogger("operations/paint")
class FloodAction(Action):
name = "Flood"
tooltip = "Apply current brush to whole selection"
name = "填充" # Flood
tooltip = "将当前画笔应用于整个选择区域"
def run(self):
session.paint_tool.flood(self.session.state.currentLayer.layer, influences=self.session.state.currentLayer.layer.paint_targets)
@@ -28,8 +28,8 @@ class FloodAction(Action):
class PaintAction(Action):
name = "Paint"
tooltip = "Toggle paint tool"
name = "绘制" # Paint
tooltip = "切换绘制工具"
checkable = True
def run(self):

View File

@@ -43,14 +43,14 @@ def list_custom_nodes_for_meshes(meshes):
return list(itertools.chain.from_iterable([list_custom_nodes_for_mesh(i) for i in meshes]))
message_scene_noCustomNodes = 'Scene does not contain any custom ngSkinTools nodes.'
message_selection_noCustomNodes = 'Selection does not contain any custom ngSkinTools nodes.'
message_scene_noCustomNodes = '场景中不包含任何自定义的ngSkinTools节点。'
message_selection_noCustomNodes = '选择不包含任何自定义ngskinTools节点。'
message_scene_warning = (
'This command deletes all custom ngSkinTools nodes. Skin weights ' 'will be preserved, but all layer data will be lost. Do you want to continue?'
'此命令删除所有自定义ngSkinTools节点。蒙皮权重“”将被保留但所有图层数据都将丢失。您想继续吗'
)
message_selection_warning = (
'This command deletes custom ngSkinTools nodes for selection. Skin weights '
'will be preserved, but all layer data will be lost. Do you want to continue?'
'此命令删除要选择的自定义ngSkinTools节点。皮肤重量'
'将被保留,但所有图层数据都将丢失。您想继续吗?'
)
@@ -89,7 +89,7 @@ def remove_custom_nodes(interactive=False, session=None, meshes=None):
if PaintTool.is_painting():
# make sure that painting is canceled to restore mesh display etc
cmds.setToolTo("Move")
cmds.setToolTo("移除") # Move
if session is not None:
session.events.targetChanged.emitIfChanged()

View File

@@ -54,7 +54,7 @@ def create_action__from_closest_joint(parent, session):
if not options.all_influences():
influences = layer.paint_targets
if not influences:
dialogs.info("Select one or more influences in Influences list")
dialogs.info("在影响列表中选择一个或多个影响")
return
if options.create_new_layer():
@@ -77,8 +77,8 @@ def create_action__from_closest_joint(parent, session):
__create_tool_action__(
parent,
session,
action_name=u"Assign From Closest Joint",
action_tooltip="Assign 1.0 weight for closest influence per each vertex in selected layer",
action_name=u"从最近的关节分配",
action_tooltip="为选定层中每个顶点的最近影响分配权重1.0",
exec_handler=exec_handler,
),
options,
@@ -105,8 +105,8 @@ def create_action__unify_weights(parent, session):
__create_tool_action__(
parent,
session,
action_name=u"Unify Weights",
action_tooltip="For selected vertices, make verts the same for all verts",
action_name=u"统一权重", # Unify Weights
action_tooltip="对于选定的顶点,使所有顶点相同。", #对于选定的顶点,使所有顶点相同。
exec_handler=exec_handler,
),
options,
@@ -130,8 +130,8 @@ def create_action__merge_layers(parent, session):
return __create_tool_action__(
parent,
session,
action_name=u"Merge",
action_tooltip="Merge contents of this layer into underlying layer. Pre-effects weights will be used for this",
action_name=u"合并", # Merge
action_tooltip="将本层的元素合并到底层。预效果权重将用于此。",
exec_handler=exec_handler,
enabled_handler=enabled_handler,
)
@@ -155,8 +155,8 @@ def create_action__duplicate_layer(parent, session):
return __create_tool_action__(
parent,
session,
action_name=u"Duplicate",
action_tooltip="Duplicate selected layer(s)",
action_name=u"复制",
action_tooltip="复制选择的图层(多选)",
exec_handler=exec_handler,
)
@@ -176,8 +176,8 @@ def create_action__fill_transparency(parent, session):
return __create_tool_action__(
parent,
session,
action_name=u"Fill Transparency",
action_tooltip="All transparent vertices in the selected layer(s) receive weights from their closest non-empty neighbour vertex",
action_name=u"填充透明度",
action_tooltip="所选图层中的所有透明顶点接收其最近非空邻接顶点的权重,",
exec_handler=exec_handler,
)
@@ -195,8 +195,8 @@ def create_action__copy_component_weights(parent, session):
return __create_tool_action__(
parent,
session,
action_name=u"Copy Component Weights",
action_tooltip="Store components weights in memory for further component-based paste actions",
action_name=u"复制组件权重",
action_tooltip="将组件权重存储在内存中,以便进行进一步的基于组件的粘贴操作",
exec_handler=exec_handler,
)
@@ -214,8 +214,8 @@ def create_action__paste_average_component_weight(parent, session):
return __create_tool_action__(
parent,
session,
action_name=u"Paste Average Component Weight",
action_tooltip="Compute average of copied component weights and set that value to currently selected components",
action_name=u"粘贴平均组件权重",
action_tooltip="计算复制的组件重量的平均值,并将该值设置为当前选定的组件",
exec_handler=exec_handler,
)
@@ -229,7 +229,7 @@ def create_action__add_influences(parent, session):
def exec_handler():
selection = cmds.ls(sl=True, l=True)
if len(selection) < 2:
logger.info("invalid selection: %s", selection)
logger.info("无效选择: %s", selection)
return
api.add_influences(selection[:-1], selection[-1])
cmds.select(selection[-1])
@@ -238,8 +238,8 @@ def create_action__add_influences(parent, session):
return __create_tool_action__(
parent,
session,
action_name=u"Add Influences",
action_tooltip="Add selected influences to current skin cluster.",
action_name=u"增加影响",
action_tooltip="将选定的影响添加到当前皮肤集群。",
exec_handler=exec_handler,
)
@@ -289,7 +289,7 @@ def create_action__select_affected_vertices(parent, session):
return __create_tool_action__(
parent,
session,
action_name=u"Select Affected Vertices",
action_tooltip="Select vertices that have non-zero weight for current influence.",
action_name=u"选择受影响的顶点",
action_tooltip="选择当前影响中权重不为要的顶点。",
exec_handler=exec_handler,
)

View File

@@ -12,10 +12,10 @@ def website_base_url():
class WebsiteLinksActions(Object):
def __init__(self, parent):
self.api_root = make_documentation_action(parent, "API Documentation", "/v2/api")
self.user_guide = make_documentation_action(parent, "User Guide", "/v2/")
self.changelog = make_documentation_action(parent, "Change Log", "/v2/changelog", icon=None)
self.contact = make_documentation_action(parent, "Contact", "/contact/", icon=None)
self.api_root = make_documentation_action(parent, "API 文档", "/v2/api")
self.user_guide = make_documentation_action(parent, "用户指南", "/v2/")
self.changelog = make_documentation_action(parent, "更新日志", "/v2/changelog", icon=None)
self.contact = make_documentation_action(parent, "联系", "/contact/", icon=None)
def make_documentation_action(parent, title, url, icon=":/help.png"):

View File

@@ -24,8 +24,8 @@ class SignalQueue(Object):
def emit(self, handler):
if len(self.queue) > self.max_length:
log.error("queue max length reached: emitting too many events?")
raise Exception("queue max length reached: emitting too many events?")
log.error("队列最大长度已达到:发出的事件过多?")
raise Exception("队列最大长度已达到:正在发出太多事件?")
should_start = len(self.queue) == 0
@@ -53,7 +53,7 @@ class SignalQueue(Object):
current_handler += 1
if len(self.queue) > 50:
log.info("handler queue finished with %d items", len(self.queue))
log.info("处理器队列完成,共 %d ", len(self.queue))
self.queue = []
@@ -77,7 +77,7 @@ class Signal(Object):
def __init__(self, name):
if name is None:
raise Exception("need name for debug purposes later")
raise Exception("需要稍后用于调试目的的名称")
self.name = name
self.handlers = []
self.executing = False

View File

@@ -19,7 +19,7 @@ def show(parent):
layout = QtWidgets.QVBoxLayout()
layout.addStretch()
layout.addWidget(QtWidgets.QLabel("<h1>ngSkinTools</h1>"))
layout.addWidget(QtWidgets.QLabel("Version {0}".format(version.pluginVersion())))
layout.addWidget(QtWidgets.QLabel("当前版本 {0}".format(version.pluginVersion())))
layout.addWidget(QtWidgets.QLabel(version.COPYRIGHT))
url = QtWidgets.QLabel('<a href="{0}" style="color: #007bff;">{0}</a>'.format(version.PRODUCT_URL))
@@ -65,7 +65,7 @@ def show(parent):
def buttonsRow(window):
layout = QtWidgets.QHBoxLayout()
layout.addStretch()
btnClose = QtWidgets.QPushButton("Close")
btnClose = QtWidgets.QPushButton("退出")
btnClose.setMinimumWidth(100 * scale_multiplier)
layout.addWidget(btnClose)
layout.setContentsMargins(20 * scale_multiplier, 15 * scale_multiplier, 20 * scale_multiplier, 15 * scale_multiplier)
@@ -76,7 +76,7 @@ def show(parent):
window = QtWidgets.QWidget(parent, Qt.Window | Qt.WindowTitleHint | Qt.CustomizeWindowHint)
window.resize(600 * scale_multiplier, 500 * scale_multiplier)
window.setAttribute(Qt.WA_DeleteOnClose)
window.setWindowTitle("About ngSkinTools")
window.setWindowTitle("关于 ngSkinTools")
layout = QtWidgets.QVBoxLayout()
window.setLayout(layout)
layout.setContentsMargins(0, 0, 0, 0)

View File

@@ -35,7 +35,7 @@ def build_action_delete_custom_nodes_for_selection(parent, session):
result = define_action(
parent,
"Delete Custom Nodes For Selection",
"删除选中网格的Ng节点",
callback=lambda: removeLayerData.remove_custom_nodes_from_selection(interactive=True, session=session),
)
@@ -86,7 +86,7 @@ class Actions(Object):
self.toolsUnifyWeights, self.toolsUnifyWeightsOptions = tools.create_action__unify_weights(parent, session)
self.toolsDeleteCustomNodes = define_action(
parent, "Delete All Custom Nodes", callback=lambda: removeLayerData.remove_custom_nodes(interactive=True, session=session)
parent, "删除所有网格的Ng节点", callback=lambda: removeLayerData.remove_custom_nodes(interactive=True, session=session)
)
self.toolsDeleteCustomNodesOnSelection = build_action_delete_custom_nodes_for_selection(parent, session)
@@ -135,10 +135,10 @@ class Actions(Object):
context.addAction(self.toggle_layer_enabled)
def addInfluencesActions(self, context):
context.addAction(self.separator(context, "Actions"))
context.addAction(self.separator(context, "动作"))
context.addAction(self.toolsAssignFromClosestJointSelectedInfluences)
context.addAction(self.select_affected_vertices)
context.addAction(self.separator(context, "Clipboard"))
context.addAction(self.separator(context, "剪贴板"))
context.addAction(self.cut_influences)
context.addAction(self.copy_influences)
context.addAction(self.paste_weights)

View File

@@ -36,7 +36,7 @@ def open_as_dialog(parent, matcher, result_callback):
window.close()
def save_defaults():
if not yesNo("Save current settings as default?"):
if not yesNo("将当前设置保存为默认设置?"):
return
config.mirrorInfluencesDefaults = matcher.config.as_json()
@@ -47,18 +47,18 @@ def open_as_dialog(parent, matcher, result_callback):
return widgets.button_row(
[
("Apply", apply),
("Cancel", window.close),
("应用", apply),
("取消", window.close),
],
side_menu=[
("Save As Default", save_defaults),
("Load Defaults", load_defaults),
("储存为默认值", save_defaults),
("加载默认值", load_defaults),
],
)
window = QtWidgets.QDialog(parent)
cleanup.registerCleanupHandler(window.close)
window.setWindowTitle("Influence Mirror Mapping")
window.setWindowTitle("影响镜像映射")
window.setAttribute(QtCore.Qt.WA_DeleteOnClose)
window.resize(720 * scale_multiplier, 500 * scale_multiplier)
window.setLayout(QtWidgets.QVBoxLayout())
@@ -102,10 +102,10 @@ def build_ui(parent, matcher):
split_path = path.rsplit("|", 1)
parent_path, name = split_path if len(split_path) == 2 else ["", split_path[0]]
item = QtWidgets.QTreeWidgetItem([name, '-', '(not in skin cluster)' if is_intermediate else '?'])
item = QtWidgets.QTreeWidgetItem([name, '-', '(不在皮肤簇中)' if is_intermediate else '?'])
tree_items[path] = item
parent_item = None if parent_path == "" else find_item(parent_path, True)
parent_item = None if parent_path is "" else find_item(parent_path, True)
if parent_item is not None:
parent_item.addChild(item)
@@ -142,7 +142,7 @@ def build_ui(parent, matcher):
def pattern():
result = QtWidgets.QTableWidget()
result.setColumnCount(2)
result.setHorizontalHeaderLabels(["Pattern", "Opposite"] if mirror_mode else ["Source", "Destination"])
result.setHorizontalHeaderLabels(["", ""] if mirror_mode else ["来源", "目标"])
result.setEditTriggers(QtWidgets.QTableWidget.AllEditTriggers)
result.verticalHeader().setVisible(False)
@@ -209,12 +209,12 @@ def build_ui(parent, matcher):
def automaticRules():
form = QtWidgets.QFormLayout()
use_joint_names = QtWidgets.QCheckBox("Match by joint name")
use_joint_names = QtWidgets.QCheckBox("匹配骨骼名称")
naming_patterns = pattern()
use_position = QtWidgets.QCheckBox("Match by position")
use_position = QtWidgets.QCheckBox("匹配位置")
tolerance_scroll = tolerance()
use_joint_labels = QtWidgets.QCheckBox("Match by joint label")
use_dg_links = QtWidgets.QCheckBox("Match by dependency graph links")
use_joint_labels = QtWidgets.QCheckBox("匹配骨骼标签")
use_dg_links = QtWidgets.QCheckBox("匹配依存关系图连接")
def update_enabled_disabled():
def enable_form_row(form_item, e):
@@ -260,27 +260,27 @@ def build_ui(parent, matcher):
use_dg_links.setChecked(matcher.config.use_dg_link_matching)
update_enabled_disabled()
g = QtWidgets.QGroupBox("Rules")
g = QtWidgets.QGroupBox("规则")
g.setLayout(form)
form.addRow(use_dg_links)
form.addRow("Attribute name:", dg_attribute)
form.addRow("属性名称:", dg_attribute)
form.addRow(use_joint_labels)
form.addRow(use_joint_names)
form.addRow("Naming scheme:", naming_patterns)
form.addRow("命名方案:", naming_patterns)
form.addRow(use_position)
form.addRow("Position tolerance:", tolerance_scroll.layout())
form.addRow("位置容差:", tolerance_scroll.layout())
update_values()
return g
def scriptedRules():
g = QtWidgets.QGroupBox("Scripted rules")
g = QtWidgets.QGroupBox("脚本规则")
g.setLayout(QtWidgets.QVBoxLayout())
g.layout().addWidget(QtWidgets.QLabel("TODO"))
return g
def manualRules():
g = QtWidgets.QGroupBox("Manual overrides")
g = QtWidgets.QGroupBox("手动覆盖")
g.setLayout(QtWidgets.QVBoxLayout())
g.layout().addWidget(QtWidgets.QLabel("TODO"))
return g
@@ -302,7 +302,7 @@ def build_ui(parent, matcher):
def createMappingView():
view = QtWidgets.QTreeWidget()
view.setColumnCount(3)
view.setHeaderLabels(["Source", "Destination", "Matched by rule"])
view.setHeaderLabels(["来源", "目标", "按规则匹配"])
view.setIndentation(7)
view.setExpandsOnDoubleClick(False)
@@ -316,14 +316,14 @@ def build_ui(parent, matcher):
:type mapping: dict[InfluenceInfo, InfluenceInfo]
"""
for treeItem in list(usedItems.values()):
treeItem.setText(1, "(not matched)")
treeItem.setText(1, "(不匹配)")
treeItem.setText(2, "")
for k, v in list(mapping.items()):
treeItem = usedItems.get(k.path_name(), None)
if treeItem is None:
continue
treeItem.setText(1, "(self)" if k == v['infl'] else v["infl"].shortestPath)
treeItem.setText(1, "(自己)" if k == v['infl'] else v["infl"].shortestPath)
treeItem.setText(2, v["matchedRule"])
treeItem.setData(1, linkedItemRole, v["infl"].path)
@@ -343,7 +343,7 @@ def build_ui(parent, matcher):
matches = matcher.calculate()
mappingView_updateMatches(matches)
g = QtWidgets.QGroupBox("Calculated mapping")
g = QtWidgets.QGroupBox("计算映射")
g.setLayout(QtWidgets.QVBoxLayout())
mappingView, mappingView_updateMatches = createMappingView()
g.layout().addWidget(mappingView)

View File

@@ -19,9 +19,9 @@ def build_used_influences_action(parent):
result = actions.define_action(
parent,
"Used Influences Only",
"只显示有权重的影响物",
callback=toggle,
tooltip="If enabled, influences view will only show influences that have weights on current layer",
tooltip="如果启用,影响视图将仅显示当前层上有权重的影响。",
)
@signal.on(config.influences_show_used_influences_only.changed, qtParent=parent)
@@ -46,7 +46,7 @@ def build_set_influences_sorted_action(parent):
parent,
"Show influences sorted",
callback=toggle,
tooltip="Sort influences by name",
tooltip="按名称排序影响",
)
@signal.on(config.influences_show_used_influences_only.changed, qtParent=parent)
@@ -108,7 +108,7 @@ def build_view(parent, actions, session, filter):
targets = (item_id,)
layer.locked_influences = add_or_remove(layer.locked_influences, targets, lock)
log.info("updated locked influences to %r", layer.locked_influences)
log.info("更新锁定的影响为 %r", layer.locked_influences)
session.events.influencesListUpdated.emit()
return handler
@@ -170,14 +170,14 @@ def build_view(parent, actions, session, filter):
view.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
view.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
actions.addInfluencesActions(view)
view.addAction(actions.separator(parent, "View Options"))
view.addAction(actions.separator(parent, "显示设置"))
view.addAction(actions.show_used_influences_only)
view.addAction(actions.set_influences_sorted)
view.setIndentation(10 * scale_multiplier)
view.header().setStretchLastSection(False)
view.header().setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch)
view.setHeaderLabels(["Influences", ""])
view.setHeaderLabels(["影响物", ""])
view.header().setSectionResizeMode(1, QtWidgets.QHeaderView.Fixed)
view.setColumnWidth(1, 25 * scale_multiplier)
@@ -208,7 +208,7 @@ def build_view(parent, actions, session, filter):
if not session.state.currentLayer.layer:
build_items(view, [], None)
else:
log.info("current layer changed to %s", session.state.currentLayer.layer)
log.info("当前图层更改为 %s", session.state.currentLayer.layer)
refresh_items()
current_influence_changed()
@@ -217,7 +217,7 @@ def build_view(parent, actions, session, filter):
if session.state.currentLayer.layer is None:
return
log.info("current influence changed - updating item selection")
log.info("当前影响已更改-更新项目选择")
with qt.signals_blocked(view):
targets = session.state.currentLayer.layer.paint_targets
first = True
@@ -239,12 +239,12 @@ def build_view(parent, actions, session, filter):
if not session.state.currentLayer.layer:
return
log.info("focused item changed: %r", get_item_id(curr))
log.info("焦点项已更改: %r", get_item_id(curr))
sync_paint_targets_to_selection()
@qt.on(view.itemSelectionChanged)
def sync_paint_targets_to_selection():
log.info("syncing paint targets")
log.info("同步绘画目标")
selected_ids = [get_item_id(item) for item in view.selectedItems()]
selected_ids = [i for i in selected_ids if i is not None]

View File

@@ -48,12 +48,12 @@ def build_view(parent, actions):
child_layer = item_to_layer(child)
if child_layer.parent_id != parent_layer_id:
log.info("changing layer parent: %r->%r (was %r)", parent_layer_id, child_layer, child_layer.parent_id)
log.info("更改图层父级: %r->%r (was %r)", parent_layer_id, child_layer, child_layer.parent_id)
child_layer.parent = parent_layer_id
new_index = tree_item.childCount() - i - 1
if child_layer.index != new_index:
log.info("changing layer index: %r->%r (was %r)", child_layer, new_index, child_layer.index)
log.info("更改图层索引: %r->%r (was %r)", child_layer, new_index, child_layer.index)
child_layer.index = new_index
sync_item(child, child_layer.id)
@@ -82,7 +82,7 @@ def build_view(parent, actions):
view.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
actions.addLayersActions(view)
view.setHeaderLabels(["Layers", ""])
view.setHeaderLabels(["图层", ""])
# view.setHeaderHidden(True)
view.header().setMinimumSectionSize(1)
view.header().setStretchLastSection(False)
@@ -100,7 +100,7 @@ def build_view(parent, actions):
bar = QtWidgets.QToolBar(parent=parent)
bar.setMovable(False)
bar.setIconSize(QtCore.QSize(visibility_icon_size * scale_multiplier, visibility_icon_size * scale_multiplier))
a = bar.addAction(icon_visible if layer is None or layer.enabled else icon_hidden, "Toggle enabled/disabled")
a = bar.addAction(icon_visible if layer is None or layer.enabled else icon_hidden, "切换 启用/禁用")
@qt.on(a.triggered)
def handler():
@@ -117,11 +117,11 @@ def build_view(parent, actions):
# build map "parent id->list of children "
log.info("syncing items...")
log.info("同步项目q...")
# save selected layers IDs to restore item selection later
selected_layer_ids = {item_to_layer(item).id for item in view.selectedItems()}
log.info("selected layer IDs: %r", selected_layer_ids)
log.info("选择层 IDs: %r", selected_layer_ids)
current_item_id = None if view.currentItem() is None else item_to_layer(view.currentItem()).id
hierarchy = {}
@@ -167,7 +167,7 @@ def build_view(parent, actions):
@signal.on(session.events.layerListChanged, qtParent=view)
def refresh_layer_list():
log.info("event handler for layer list changed")
log.info("图层列表更改的事件处理程序")
if not session.state.layersAvailable:
build_items([])
else:
@@ -177,7 +177,7 @@ def build_view(parent, actions):
@signal.on(session.events.currentLayerChanged, qtParent=view)
def current_layer_changed():
log.info("event handler for currentLayerChanged")
log.info("当前图层更改的事件处理程序")
layer = session.state.currentLayer.layer
current_item = view.currentItem()
if layer is None:
@@ -189,14 +189,14 @@ def build_view(parent, actions):
if prev_layer is None or prev_layer.id != layer.id:
item = tree_items.get(layer.id, None)
if item is not None:
log.info("setting current item to " + item.text(0))
log.info("将当前项目设置为 " + item.text(0))
view.setCurrentItem(item, 0, QtCore.QItemSelectionModel.SelectCurrent | QtCore.QItemSelectionModel.ClearAndSelect)
item.setSelected(True)
@qt.on(view.currentItemChanged)
def current_item_changed(curr, _):
log.info("current item changed")
log.info("当前项目已更改")
if curr is None:
return
@@ -209,7 +209,7 @@ def build_view(parent, actions):
@qt.on(view.itemChanged)
def item_changed(item, column):
log.info("item changed")
log.info("项目已更改")
layers.renameLayer(item_to_layer(item), item.text(column))
@qt.on(view.itemSelectionChanged)
@@ -217,7 +217,7 @@ def build_view(parent, actions):
selection = [item_to_layer(item) for item in view.selectedItems()]
if selection != session.context.selected_layers(default=[]):
log.info("new selected layers: %r", selection)
log.info("新选择图层: %r", selection)
session.context.selected_layers.set(selection)
refresh_layer_list()

View File

@@ -46,20 +46,20 @@ def build_menu(parent, actions):
sub_item.setTearOffEnabled(True)
return sub_item
sub = top_level_menu("File")
sub.addSeparator().setText("Import/Export")
sub = top_level_menu("文件")
sub.addSeparator().setText("导入/导出")
sub.addAction(actions.importFile)
sub.addAction(actions.exportFile)
sub = top_level_menu("Layers")
sub.addSeparator().setText("Layer actions")
sub = top_level_menu("图层")
sub.addSeparator().setText("图层操作")
sub.addAction(actions.initialize)
sub.addAction(actions.import_v1)
actions.addLayersActions(sub)
sub.addSeparator().setText("Copy")
sub.addSeparator().setText("复制")
sub.addAction(actions.transfer)
sub = top_level_menu("Tools")
sub = top_level_menu("工具")
sub.addAction(actions.add_influences)
sub.addAction(actions.toolsAssignFromClosestJoint)
sub.addSeparator()
@@ -68,17 +68,17 @@ def build_menu(parent, actions):
sub.addAction(actions.toolsDeleteCustomNodesOnSelection)
sub.addAction(actions.toolsDeleteCustomNodes)
sub = top_level_menu("View")
sub = top_level_menu("查看")
sub.addAction(actions.show_used_influences_only)
sub = top_level_menu("Help")
sub = top_level_menu("帮助")
sub.addAction(actions.documentation.user_guide)
sub.addAction(actions.documentation.api_root)
sub.addAction(actions.documentation.changelog)
sub.addAction(actions.documentation.contact)
sub.addSeparator()
sub.addAction(actions.check_for_updates)
sub.addAction("About...").triggered.connect(lambda: aboutwindow.show(parent))
sub.addAction("关于...").triggered.connect(lambda: aboutwindow.show(parent))
return menu
@@ -107,11 +107,11 @@ def build_ui(parent):
tabs = QtWidgets.QTabWidget(window)
tabs.addTab(tabPaint.build_ui(tabs, actions), "Paint")
tabs.addTab(tabSetWeights.build_ui(tabs), "Set Weights")
tabs.addTab(tabMirror.build_ui(tabs), "Mirror")
tabs.addTab(tabLayerEffects.build_ui(), "Effects")
tabs.addTab(tabTools.build_ui(actions, session), "Tools")
tabs.addTab(tabPaint.build_ui(tabs, actions), "绘制")
tabs.addTab(tabSetWeights.build_ui(tabs), "设置权重")
tabs.addTab(tabMirror.build_ui(tabs), "镜像")
tabs.addTab(tabLayerEffects.build_ui(), "样式")
tabs.addTab(tabTools.build_ui(actions, session), "工具")
@signal.on(options.current_tab.changed)
def set_current_tab():

View File

@@ -19,4 +19,4 @@ def bind(ui, model):
model.set(ui.value())
else:
raise Exception("could not bind control to model")
raise Exception("无法将控件绑定到模型")

View File

@@ -99,10 +99,10 @@ def save_option(varName, value):
elif is_string(value):
key = 'sv'
else:
raise ValueError("could not save option %s: invalid value %r" % (varName, value))
raise ValueError("无法保存选项 %s: 无效值 %r" % (varName, value))
kvargs = {key: (varName, value)}
log.info("saving optionvar: %r", kvargs)
log.info("保存选项变量: %r", kvargs)
cmds.optionVar(**kvargs)
@@ -153,11 +153,11 @@ class Config(Object):
def __get_value__(self, name, default_value):
result = self.__state__.get(name, default_value)
log.info("config: return %s=%r", name, result)
log.info("配置: return %s=%r", name, result)
return result
def __set_value__(self, name, value):
log.info("config: save %s=%r", name, value)
log.info("配置: save %s=%r", name, value)
self.__state__[name] = value
self.save()

View File

@@ -36,9 +36,9 @@ def build_ui():
def build_properties():
layout = QtWidgets.QVBoxLayout()
opacity = widgets.NumberSliderGroup(tooltip="multiply layer mask to control overall transparency of the layer.")
opacity = widgets.NumberSliderGroup(tooltip="多层蒙版来控制图层的整体透明度.")
opacity.set_value(1.0)
layout.addLayout(createTitledRow("Opacity:", opacity.layout()))
layout.addLayout(createTitledRow("不透明度:", opacity.layout()))
def default_selection_opacity(layers):
if len(layers) > 0:
@@ -65,7 +65,7 @@ def build_ui():
update_values()
group = QtWidgets.QGroupBox("Layer properties")
group = QtWidgets.QGroupBox("图层属性")
group.setLayout(layout)
return group
@@ -76,18 +76,18 @@ def build_ui():
i.effects.configure_mirror(**{option: value})
mirror_direction = QtWidgets.QComboBox()
mirror_direction.addItem("Positive to negative", MirrorOptions.directionPositiveToNegative)
mirror_direction.addItem("Negative to positive", MirrorOptions.directionNegativeToPositive)
mirror_direction.addItem("Flip", MirrorOptions.directionFlip)
mirror_direction.addItem("从正到负", MirrorOptions.directionPositiveToNegative)
mirror_direction.addItem("从负到正", MirrorOptions.directionNegativeToPositive)
mirror_direction.addItem("翻转", MirrorOptions.directionFlip)
mirror_direction.setMinimumWidth(1)
@qt.on(mirror_direction.currentIndexChanged)
def value_changed():
configure_mirror_all_layers("mirror_direction", mirror_direction.currentData())
configure_mirror_all_layers("镜像方向", mirror_direction.currentData())
influences = QtWidgets.QCheckBox("Influence weights")
mask = QtWidgets.QCheckBox("Layer mask")
dq = QtWidgets.QCheckBox("Dual quaternion weights")
influences = QtWidgets.QCheckBox("影响物权重")
mask = QtWidgets.QCheckBox("图层蒙板")
dq = QtWidgets.QCheckBox("双四元数权重")
def configure_checkbox(checkbox, option):
@qt.on(checkbox.stateChanged)
@@ -125,21 +125,21 @@ def build_ui():
return result
layout = QtWidgets.QVBoxLayout()
layout.addLayout(createTitledRow("Mirror effect on:", elements()))
layout.addLayout(createTitledRow("Mirror direction:", mirror_direction))
layout.addLayout(createTitledRow("镜像效果打开:", elements()))
layout.addLayout(createTitledRow("镜像方向:", mirror_direction))
group = QtWidgets.QGroupBox("Mirror")
group = QtWidgets.QGroupBox("镜像")
group.setLayout(layout)
return group
def build_skin_properties():
use_max_influences = QtWidgets.QCheckBox("Limit max influences per vertex")
use_max_influences = QtWidgets.QCheckBox("限制每个顶点的最大影响物")
max_influences = widgets.NumberSliderGroup(min_value=1, max_value=5, tooltip="", value_type=int)
use_prune_weight = QtWidgets.QCheckBox("Prune small weights before writing to skin cluster")
use_prune_weight = QtWidgets.QCheckBox("在写入蒙皮簇之前修剪小权重")
prune_weight = widgets.NumberSliderGroup(decimals=6, min_value=0.000001, max_value=0.05, tooltip="")
prune_weight.set_value(prune_weight.min_value)
prune_weight.set_expo("start", 3)
prune_weight.set_expo("开始", 3)
@signal.on(session.events.targetChanged)
def update_ui():
@@ -164,7 +164,7 @@ def build_ui():
@qt.on(use_max_influences.stateChanged, use_prune_weight.stateChanged)
@signal.on(max_influences.valueChanged, prune_weight.valueChanged)
def update_values():
log.info("updating effects tab")
log.info("更新效果标签页")
if session.state.layersAvailable:
session.state.layers.influence_limit_per_vertex = max_influences.value() if use_max_influences.isChecked() else 0
@@ -174,11 +174,11 @@ def build_ui():
layout = QtWidgets.QVBoxLayout()
layout.addWidget(use_max_influences)
layout.addLayout(createTitledRow("Max influences:", max_influences.layout()))
layout.addLayout(createTitledRow("最大影响物:", max_influences.layout()))
layout.addWidget(use_prune_weight)
layout.addLayout(createTitledRow("Prune below:", prune_weight.layout()))
layout.addLayout(createTitledRow("修剪以下:", prune_weight.layout()))
group = QtWidgets.QGroupBox("Skin Properties")
group = QtWidgets.QGroupBox("蒙皮属性")
group.setLayout(layout)
update_ui()

View File

@@ -16,10 +16,10 @@ def build_ui(parent_window):
def build_mirroring_options_group():
def get_mirror_direction():
mirror_direction = QtWidgets.QComboBox()
mirror_direction.addItem("Guess from stroke", MirrorOptions.directionGuess)
mirror_direction.addItem("Positive to negative", MirrorOptions.directionPositiveToNegative)
mirror_direction.addItem("Negative to positive", MirrorOptions.directionNegativeToPositive)
mirror_direction.addItem("Flip", MirrorOptions.directionFlip)
mirror_direction.addItem("从笔划猜测", MirrorOptions.directionGuess)
mirror_direction.addItem("从正到负", MirrorOptions.directionPositiveToNegative)
mirror_direction.addItem("从负到正", MirrorOptions.directionNegativeToPositive)
mirror_direction.addItem("翻转", MirrorOptions.directionFlip)
mirror_direction.setMinimumWidth(1)
qt.select_data(mirror_direction, config.mirror_direction())
@@ -65,19 +65,19 @@ def build_ui(parent_window):
return seam_width_ctrl.layout()
def elements():
influences = bind_checkbox(QtWidgets.QCheckBox("Influence weights"), config.mirror_weights)
mask = bind_checkbox(QtWidgets.QCheckBox("Layer mask"), config.mirror_mask)
dq = bind_checkbox(QtWidgets.QCheckBox("Dual quaternion weights"), config.mirror_dq)
influences = bind_checkbox(QtWidgets.QCheckBox("影响物权重"), config.mirror_weights)
mask = bind_checkbox(QtWidgets.QCheckBox("图层遮罩"), config.mirror_mask)
dq = bind_checkbox(QtWidgets.QCheckBox("双四元权重"), config.mirror_dq)
return influences, mask, dq
result = QtWidgets.QGroupBox("Mirroring options")
result = QtWidgets.QGroupBox("镜像选项")
layout = QtWidgets.QVBoxLayout()
result.setLayout(layout)
layout.addLayout(createTitledRow("Axis:", axis()))
layout.addLayout(createTitledRow("Direction:", get_mirror_direction()))
layout.addLayout(createTitledRow("Seam width:", mirror_seam_width()))
layout.addLayout(createTitledRow("Elements to mirror:", *elements()))
layout.addLayout(createTitledRow("镜像轴:", axis()))
layout.addLayout(createTitledRow("方向:", get_mirror_direction()))
layout.addLayout(createTitledRow("接缝宽度:", mirror_seam_width()))
layout.addLayout(createTitledRow("镜像要素:", *elements()))
return result
@@ -86,10 +86,10 @@ def build_ui(parent_window):
def mirror_mesh_group():
mesh_name_edit = QtWidgets.QLineEdit("mesh1")
mesh_name_edit.setReadOnly(True)
select_button = QtWidgets.QPushButton("Select")
create_button = QtWidgets.QPushButton("Create")
select_button = QtWidgets.QPushButton("选择")
create_button = QtWidgets.QPushButton("创建")
set_button = QtWidgets.QPushButton("Set")
set_button.setToolTip("Select symmetry mesh and a skinned target first")
set_button.setToolTip("首先选择对称网格和蒙皮目标")
layout = QtWidgets.QHBoxLayout()
layout.addWidget(mesh_name_edit)
@@ -144,13 +144,13 @@ def build_ui(parent_window):
return layout
vertex_mapping_mode = QtWidgets.QComboBox()
vertex_mapping_mode.addItem("Closest point on surface", VertexTransferMode.closestPoint)
vertex_mapping_mode.addItem("UV space", VertexTransferMode.uvSpace)
vertex_mapping_mode.addItem("曲面上最近的点", VertexTransferMode.closestPoint)
vertex_mapping_mode.addItem("UV空间", VertexTransferMode.uvSpace)
result = QtWidgets.QGroupBox("Vertex Mapping")
result = QtWidgets.QGroupBox("顶点映射")
layout = QtWidgets.QVBoxLayout()
layout.addLayout(createTitledRow("Mapping mode:", vertex_mapping_mode))
layout.addLayout(createTitledRow("Symmetry mesh:", mirror_mesh_group()))
layout.addLayout(createTitledRow("映射模式:", vertex_mapping_mode))
layout.addLayout(createTitledRow("对称网格:", mirror_mesh_group()))
result.setLayout(layout)
@qt.on(vertex_mapping_mode.currentIndexChanged)
@@ -166,7 +166,7 @@ def build_ui(parent_window):
def influence_mapping_group():
def edit_mapping():
mapping = QtWidgets.QPushButton("Preview and edit mapping")
mapping = QtWidgets.QPushButton("预览和编辑映射")
single_window_policy = qt.SingleWindowPolicy()
@@ -182,7 +182,7 @@ def build_ui(parent_window):
layout = QtWidgets.QVBoxLayout()
layout.addWidget(edit_mapping())
result = QtWidgets.QGroupBox("Influences mapping")
result = QtWidgets.QGroupBox("影响物映射")
result.setLayout(layout)
return result
@@ -192,7 +192,7 @@ def build_ui(parent_window):
tab.innerLayout.addWidget(influence_mapping_group())
tab.innerLayout.addStretch()
btn_mirror = QtWidgets.QPushButton("Mirror")
btn_mirror = QtWidgets.QPushButton("镜像")
tab.lowerButtonsRow.addWidget(btn_mirror)
@qt.on(btn_mirror.clicked)

View File

@@ -50,14 +50,14 @@ def build_ui(parent, global_actions):
t.addAction(a)
t = QtWidgets.QToolBar()
create_brush_mode_button(t, PaintMode.replace, "Replace", "Whatever")
create_brush_mode_button(t, PaintMode.add, "Add", "")
create_brush_mode_button(t, PaintMode.scale, "Scale", "")
create_brush_mode_button(t, PaintMode.replace, "替换", "替换")
create_brush_mode_button(t, PaintMode.add, "添加", "")
create_brush_mode_button(t, PaintMode.scale, "减少", "")
row.addWidget(t)
t = QtWidgets.QToolBar()
create_brush_mode_button(t, PaintMode.smooth, "Smooth", "")
create_brush_mode_button(t, PaintMode.sharpen, "Sharpen", "")
create_brush_mode_button(t, PaintMode.smooth, "平滑", "")
create_brush_mode_button(t, PaintMode.sharpen, "锐化", "")
row.addWidget(t)
@on_signal(et.tool_settings_changed, scope=row)
@@ -96,9 +96,9 @@ def build_ui(parent, global_actions):
update_to_tool()
qt.on(a.toggled)(toggled)
add_brush_shape_action(':/circleSolid.png', 'Solid', BrushShape.solid, checked=True)
add_brush_shape_action(':/circlePoly.png', 'Smooth', BrushShape.smooth)
add_brush_shape_action(':/circleGaus.png', 'Gaus', BrushShape.gaus)
add_brush_shape_action(':/circleSolid.png', '硬边圆', BrushShape.solid, checked=True)
add_brush_shape_action(':/circlePoly.png', '软边圆', BrushShape.smooth)
add_brush_shape_action(':/circleGaus.png', '喷枪', BrushShape.gaus)
return result
@@ -136,23 +136,23 @@ def build_ui(parent, global_actions):
update_to_tool()
add(
'Surface',
'Using first surface hit under the mouse, update all nearby vertices that are connected by surface to the hit location. '
+ 'Only current shell will be updated.',
'表面',
'绘制由与曲面相连的模型顶点.'
+ '仅更新当前shell.',
BrushProjectionMode.surface,
use_volume=False,
checked=True,
)
add(
'Volume',
'Using first surface hit under the mouse, update all nearby vertices, including those from other shells.',
'体积',
'绘制附近所有模型顶点,包括没有连接的顶点.',
BrushProjectionMode.surface,
use_volume=True,
checked=False,
)
add(
'Screen',
'Use screen projection of a brush, updating all vertices on all surfaces that are within the brush radius.',
'屏幕',
'从屏幕方向投影到笔刷半径内所有曲面上的顶点.',
BrushProjectionMode.screen,
use_volume=False,
checked=False,
@@ -163,53 +163,53 @@ def build_ui(parent, global_actions):
def stylus_pressure_selection():
# noinspection PyShadowingNames
result = QtWidgets.QComboBox()
result.addItem("Unused")
result.addItem("Multiply intensity")
result.addItem("Multiply opacity")
result.addItem("Multiply radius")
result.addItem("未使用")
result.addItem("倍增强度")
result.addItem("倍增不透明度")
result.addItem("倍增半径")
return result
layout = QtWidgets.QVBoxLayout()
layout.addLayout(createTitledRow("Brush projection:", brush_projection_mode_row()))
layout.addLayout(createTitledRow("Brush mode:", brush_mode_row3()))
layout.addLayout(createTitledRow("Brush shape:", brush_shape_row()))
layout.addLayout(createTitledRow("笔刷投影:", brush_projection_mode_row()))
layout.addLayout(createTitledRow("笔刷模式:", brush_mode_row3()))
layout.addLayout(createTitledRow("笔刷形状:", brush_shape_row()))
intensity = widgets.NumberSliderGroup()
radius = widgets.NumberSliderGroup(
max_value=100, tooltip="You can also set brush radius by just holding <b>B</b> " "and mouse-dragging in the viewport"
max_value=100, tooltip="可以通过在视口中按住 <b>B</b> " "并鼠标左键按住拖动来设置笔刷半径"
)
iterations = widgets.NumberSliderGroup(value_type=int, min_value=1, max_value=100)
layout.addLayout(createTitledRow("Intensity:", intensity.layout()))
layout.addLayout(createTitledRow("Brush radius:", radius.layout()))
layout.addLayout(createTitledRow("Brush iterations:", iterations.layout()))
layout.addLayout(createTitledRow("强度:", intensity.layout()))
layout.addLayout(createTitledRow("笔刷半径:", radius.layout()))
layout.addLayout(createTitledRow("笔刷迭代次数:", iterations.layout()))
influences_limit = widgets.NumberSliderGroup(value_type=int, min_value=0, max_value=10)
layout.addLayout(createTitledRow("Influences limit:", influences_limit.layout()))
layout.addLayout(createTitledRow("影响物限制:", influences_limit.layout()))
@signal.on(influences_limit.valueChanged)
def influences_limit_changed():
paint.influences_limit = influences_limit.value()
update_ui()
fixed_influences = QtWidgets.QCheckBox("Only adjust existing vertex influences")
fixed_influences = QtWidgets.QCheckBox("仅调整顶点现有影响物,防止周围权重扩散")
fixed_influences.setToolTip(
"When this option is enabled, smooth will only adjust existing influences per vertex, "
"and won't include other influences from nearby vertices"
"启用此选项后,平滑-将仅调整每个顶点的现有影响物, "
"而不会包含来自附近顶点的影响物,防止周围权重扩散"
)
layout.addLayout(createTitledRow("Weight bleeding:", fixed_influences))
layout.addLayout(createTitledRow("平滑辅助:", fixed_influences))
@qt.on(fixed_influences.stateChanged)
def fixed_influences_changed():
paint.fixed_influences_per_vertex = fixed_influences.isChecked()
limit_to_component_selection = QtWidgets.QCheckBox("Limit to component selection")
limit_to_component_selection.setToolTip("When this option is enabled, smoothing will only happen between selected components")
layout.addLayout(createTitledRow("Isolation:", limit_to_component_selection))
limit_to_component_selection = QtWidgets.QCheckBox("限制在选中组件中平滑")
limit_to_component_selection.setToolTip("启用此选项后,仅在选定组件之间进行平滑")
layout.addLayout(createTitledRow("隔离选中组件:", limit_to_component_selection))
@qt.on(limit_to_component_selection.stateChanged)
def limit_to_component_selection_changed():
paint.limit_to_component_selection = limit_to_component_selection.isChecked()
interactive_mirror = QtWidgets.QCheckBox("Interactive mirror")
interactive_mirror = QtWidgets.QCheckBox("交互式镜像")
layout.addLayout(createTitledRow("", interactive_mirror))
@qt.on(interactive_mirror.stateChanged)
@@ -217,7 +217,7 @@ def build_ui(parent, global_actions):
paint.mirror = interactive_mirror.isChecked()
update_ui()
sample_joint_on_stroke_start = QtWidgets.QCheckBox("Sample current joint on stroke start")
sample_joint_on_stroke_start = QtWidgets.QCheckBox("在笔画开始时取样当前骨骼")
layout.addLayout(createTitledRow("", sample_joint_on_stroke_start))
@qt.on(sample_joint_on_stroke_start.stateChanged)
@@ -225,8 +225,8 @@ def build_ui(parent, global_actions):
paint.sample_joint_on_stroke_start = sample_joint_on_stroke_start.isChecked()
update_ui()
redistribute_removed_weight = QtWidgets.QCheckBox("Distribute to other influences")
layout.addLayout(createTitledRow("Removed weight:", redistribute_removed_weight))
redistribute_removed_weight = QtWidgets.QCheckBox("分配给其它影响物")
layout.addLayout(createTitledRow("移除的权重:", redistribute_removed_weight))
@qt.on(redistribute_removed_weight.stateChanged)
def redistribute_removed_weight_changed():
@@ -234,12 +234,12 @@ def build_ui(parent, global_actions):
update_ui()
stylus = stylus_pressure_selection()
layout.addLayout(createTitledRow("Stylus pressure:", stylus))
layout.addLayout(createTitledRow("手绘板压力:", stylus))
@on_signal(et.tool_settings_changed, scope=layout)
def update_ui():
log.info("updating paint settings ui")
log.info("brush mode:%s, brush shape: %s", paint.mode, paint.brush_shape)
log.info("更新的绘制设置的ui")
log.info("画笔模式:%s, 画笔形状: %s", paint.mode, paint.brush_shape)
paint.update_plugin_brush_radius()
paint.update_plugin_brush_intensity()
@@ -280,7 +280,7 @@ def build_ui(parent, global_actions):
@signal.on(radius.valueChanged, qtParent=layout)
def radius_edited():
log.info("updated brush radius")
log.info("更新笔刷半径")
paint.brush_radius = radius.value()
update_ui()
@@ -301,18 +301,18 @@ def build_ui(parent, global_actions):
update_ui()
result = QtWidgets.QGroupBox("Brush behavior")
result = QtWidgets.QGroupBox("笔刷行为")
result.setLayout(layout)
return result
def build_display_settings():
result = QtWidgets.QGroupBox("Display settings")
result = QtWidgets.QGroupBox("显示设置")
layout = QtWidgets.QVBoxLayout()
influences_display = QtWidgets.QComboBox()
influences_display.addItem("All influences, multiple colors", WeightsDisplayMode.allInfluences)
influences_display.addItem("Current influence, grayscale", WeightsDisplayMode.currentInfluence)
influences_display.addItem("Current influence, colored", WeightsDisplayMode.currentInfluenceColored)
influences_display.addItem("所有权重,多种颜色", WeightsDisplayMode.allInfluences)
influences_display.addItem("当前权重,灰度", WeightsDisplayMode.currentInfluence)
influences_display.addItem("当前权重,彩色", WeightsDisplayMode.currentInfluenceColored)
influences_display.setMinimumWidth(1)
influences_display.setCurrentIndex(paint.display_settings.weights_display_mode)
@@ -327,11 +327,11 @@ def build_ui(parent, global_actions):
display_layout = QtWidgets.QVBoxLayout()
display_layout.addWidget(influences_display)
display_layout.addWidget(display_toolbar)
layout.addLayout(createTitledRow("Influences display:", display_layout))
layout.addLayout(createTitledRow("权重显示:", display_layout))
mask_display = QtWidgets.QComboBox()
mask_display.addItem("Default", MaskDisplayMode.default_)
mask_display.addItem("Color ramp", MaskDisplayMode.color_ramp)
mask_display.addItem("默认", MaskDisplayMode.default_)
mask_display.addItem("彩色渐变", MaskDisplayMode.color_ramp)
mask_display.setMinimumWidth(1)
mask_display.setCurrentIndex(paint.display_settings.weights_display_mode)
@@ -340,14 +340,14 @@ def build_ui(parent, global_actions):
paint.display_settings.mask_display_mode = mask_display.currentData()
update_ui_to_tool()
layout.addLayout(createTitledRow("Mask display:", mask_display))
layout.addLayout(createTitledRow("表面显示:", mask_display))
show_effects = QtWidgets.QCheckBox("Show layer effects")
show_effects = QtWidgets.QCheckBox("显示图层效果")
layout.addLayout(createTitledRow("", show_effects))
show_masked = QtWidgets.QCheckBox("Show masked weights")
show_masked = QtWidgets.QCheckBox("显示蒙皮权重")
layout.addLayout(createTitledRow("", show_masked))
show_selected_verts_only = QtWidgets.QCheckBox("Hide unselected vertices")
show_selected_verts_only = QtWidgets.QCheckBox("隐藏未选择的顶点")
layout.addLayout(createTitledRow("", show_selected_verts_only))
@qt.on(show_effects.stateChanged)
@@ -363,7 +363,7 @@ def build_ui(parent, global_actions):
paint.display_settings.show_selected_verts_only = show_selected_verts_only.isChecked()
mesh_toolbar = QtWidgets.QToolBar()
toggle_original_mesh = QAction("Show Original Mesh", mesh_toolbar)
toggle_original_mesh = QAction("显示原本模型", mesh_toolbar)
toggle_original_mesh.setCheckable(True)
mesh_toolbar.addAction(toggle_original_mesh)
layout.addLayout(createTitledRow("", mesh_toolbar))
@@ -374,7 +374,7 @@ def build_ui(parent, global_actions):
update_ui_to_tool()
wireframe_color_button = widgets.ColorButton()
layout.addLayout(createTitledRow("Wireframe color:", wireframe_color_button))
layout.addLayout(createTitledRow("线框颜色:", wireframe_color_button))
@signal.on(wireframe_color_button.color_changed)
def update_wireframe_color():

View File

@@ -1,3 +1,4 @@
# -*- coding: UTF-8 -*-
from ngSkinTools2 import signal
from ngSkinTools2.api import PaintMode, PaintModeSettings, flood_weights
from ngSkinTools2.api.log import getLogger
@@ -70,14 +71,14 @@ def build_ui(parent):
toolbar.addAction(a)
t = QtWidgets.QToolBar()
create_mode_button(t, PaintMode.replace, "Replace", "")
create_mode_button(t, PaintMode.add, "Add", "")
create_mode_button(t, PaintMode.scale, "Scale", "")
create_mode_button(t, PaintMode.replace, "替换", "")
create_mode_button(t, PaintMode.add, "添加", "")
create_mode_button(t, PaintMode.scale, "减少", "")
row.addWidget(t)
t = QtWidgets.QToolBar()
create_mode_button(t, PaintMode.smooth, "Smooth", "")
create_mode_button(t, PaintMode.sharpen, "Sharpen", "")
create_mode_button(t, PaintMode.smooth, "平滑", "")
create_mode_button(t, PaintMode.sharpen, "锐化", "")
row.addWidget(t)
actions[model.current_settings.mode].setChecked(True)
@@ -109,20 +110,20 @@ def build_ui(parent):
model.current_settings.iterations = iterations.value()
update_ui()
fixed_influences = QtWidgets.QCheckBox("Only adjust existing vertex influences")
fixed_influences = QtWidgets.QCheckBox("仅调整顶点现有影响物,防止周围权重扩散")
fixed_influences.setToolTip(
"When this option is enabled, smooth will only adjust existing influences per vertex, "
"and won't include other influences from nearby vertices"
"启用此选项后,平滑-将仅调整每个顶点的现有影响物, "
"而不会包含来自附近顶点的影响物,防止周围权重扩散"
)
volume_neighbours = QtWidgets.QCheckBox("Smooth across gaps and thin surfaces")
volume_neighbours = QtWidgets.QCheckBox("平滑间隙和薄表面")
volume_neighbours.setToolTip(
"Use all nearby neighbours, regardless if they belong to same surface. "
"This will allow for smoothing to happen across gaps and thin surfaces."
"使用所有附近相邻的面,无论它们是否属于同一个曲面."
"这将允许在间隙和薄曲面之间进行平滑."
)
limit_to_component_selection = QtWidgets.QCheckBox("Limit to component selection")
limit_to_component_selection.setToolTip("When this option is enabled, smoothing will only happen between selected components")
limit_to_component_selection = QtWidgets.QCheckBox("在选择的组件中平滑")
limit_to_component_selection.setToolTip("启用此选项后,仅在选定组件之间进行平滑")
@qt.on(fixed_influences.stateChanged)
@ui_lock.skip_if_updating
@@ -154,16 +155,16 @@ def build_ui(parent):
volume_neighbours.setChecked(model.current_settings.use_volume_neighbours)
volume_neighbours.setEnabled(model.current_settings.mode == PaintMode.smooth)
settings_group = QtWidgets.QGroupBox("Mode Settings")
settings_group = QtWidgets.QGroupBox("模式设置")
layout = QtWidgets.QVBoxLayout()
layout.addLayout(createTitledRow("Mode:", mode_row()))
layout.addLayout(createTitledRow("Intensity:", intensity.layout()))
layout.addLayout(createTitledRow("Iterations:", iterations.layout()))
layout.addLayout(createTitledRow("Influences limit:", influences_limit.layout()))
layout.addLayout(createTitledRow("Weight bleeding:", fixed_influences))
layout.addLayout(createTitledRow("Volume smoothing:", volume_neighbours))
layout.addLayout(createTitledRow("Isolation:", limit_to_component_selection))
layout.addLayout(createTitledRow("模式:", mode_row()))
layout.addLayout(createTitledRow("强度:", intensity.layout()))
layout.addLayout(createTitledRow("迭代次数:", iterations.layout()))
layout.addLayout(createTitledRow("影响物限制:", influences_limit.layout()))
layout.addLayout(createTitledRow("平滑辅助:", fixed_influences))
layout.addLayout(createTitledRow("体积平滑:", volume_neighbours))
layout.addLayout(createTitledRow("隔离选中组件:", limit_to_component_selection))
settings_group.setLayout(layout)
update_ui()
@@ -173,7 +174,7 @@ def build_ui(parent):
def common_settings():
layout = QtWidgets.QVBoxLayout()
mirror = QtWidgets.QCheckBox("Mirror")
mirror = QtWidgets.QCheckBox("镜像")
layout.addLayout(createTitledRow("", mirror))
@qt.on(mirror.stateChanged)
@@ -182,8 +183,8 @@ def build_ui(parent):
for _, v in model.presets.items():
v.mirror = mirror.isChecked()
redistribute_removed_weight = QtWidgets.QCheckBox("Distribute to other influences")
layout.addLayout(createTitledRow("Removed weight:", redistribute_removed_weight))
redistribute_removed_weight = QtWidgets.QCheckBox("分配给其它影响物")
layout.addLayout(createTitledRow("移除的权重:", redistribute_removed_weight))
@qt.on(redistribute_removed_weight.stateChanged)
def redistribute_removed_weight_changed():
@@ -195,7 +196,7 @@ def build_ui(parent):
mirror.setChecked(model.current_settings.mirror)
redistribute_removed_weight.setChecked(model.current_settings.distribute_to_other_influences)
group = QtWidgets.QGroupBox("Common Settings")
group = QtWidgets.QGroupBox("常用设置")
group.setLayout(layout)
update_ui()
@@ -203,8 +204,8 @@ def build_ui(parent):
return group
def apply_button():
btn = QtWidgets.QPushButton("Apply")
btn.setToolTip("Apply selected operation to vertex")
btn = QtWidgets.QPushButton("应用")
btn.setToolTip("将设定操作应用于选中顶点")
@qt.on(btn.clicked)
def clicked():

View File

@@ -1,3 +1,4 @@
# -*- coding: UTF-8 -*-
from ngSkinTools2 import signal
from ngSkinTools2.api.pyside import QtWidgets
from ngSkinTools2.api.session import Session
@@ -20,7 +21,7 @@ def build_ui(actions, session):
def influences_options():
result = QtWidgets.QVBoxLayout()
button_group = QtWidgets.QButtonGroup()
for index, i in enumerate(["Use all available influences", "Use selected influences"]):
for index, i in enumerate(["使用所有可用的影响物", "使用选定的影响物"]):
radio = QtWidgets.QRadioButton(i)
button_group.addButton(radio, index)
result.addWidget(radio)
@@ -38,7 +39,7 @@ def build_ui(actions, session):
return result
new_layer = QtWidgets.QCheckBox("Create new layer")
new_layer = QtWidgets.QCheckBox("创建新图层")
@qt.on(new_layer.toggled)
def update_new_layer():
@@ -53,11 +54,11 @@ def build_ui(actions, session):
update_ui()
result = QtWidgets.QGroupBox("Assign weights from closest joint")
result = QtWidgets.QGroupBox("从最近的关节指定权重")
layout = QtWidgets.QVBoxLayout()
result.setLayout(layout)
layout.addLayout(createTitledRow("Target layer", new_layer))
layout.addLayout(createTitledRow("Influences", influences_options()))
layout.addLayout(createTitledRow("目标层", new_layer))
layout.addLayout(createTitledRow("影响物", influences_options()))
layout.addWidget(btn)
return result
@@ -69,25 +70,25 @@ def build_ui(actions, session):
model_binds.bind(intensity, options.overall_effect)
single_cluster_mode = QtWidgets.QCheckBox(
"Single group mode",
"单组模式",
)
single_cluster_mode.setToolTip("average weights across whole selection, ignoring separate shells or selection gaps")
single_cluster_mode.setToolTip("整个选择的平均权重,忽略单独的壳或选择间隙")
model_binds.bind(single_cluster_mode, options.single_cluster_mode)
btn = QtWidgets.QPushButton()
qt.bind_action_to_button(actions.toolsUnifyWeights, btn)
result = QtWidgets.QGroupBox("Unify weights")
result = QtWidgets.QGroupBox("统一权重")
layout = QtWidgets.QVBoxLayout()
result.setLayout(layout)
layout.addLayout(createTitledRow("Intensity:", intensity.layout()))
layout.addLayout(createTitledRow("Clustering:", single_cluster_mode))
layout.addLayout(createTitledRow("强度:", intensity.layout()))
layout.addLayout(createTitledRow("集群:", single_cluster_mode))
layout.addWidget(btn)
return result
def other_tools_group():
result = QtWidgets.QGroupBox("Other")
result = QtWidgets.QGroupBox("其它")
layout = QtWidgets.QVBoxLayout()
result.setLayout(layout)
layout.addWidget(to_button(actions.fill_layer_transparency))

View File

@@ -25,7 +25,7 @@ def build_layers_ui(parent, actions, session):
filter = QtWidgets.QComboBox()
filter.setMinimumHeight(22 * scale_multiplier)
filter.setEditable(True)
filter.lineEdit().setPlaceholderText("Search...")
filter.lineEdit().setPlaceholderText("搜索...")
result.addWidget(filter)
# noinspection PyShadowingNames
clear = QAction(result)
@@ -83,7 +83,7 @@ def build_no_layers_ui(parent, actions, session):
selection_display = QtWidgets.QLabel("pPlane1")
selection_display.setStyleSheet("font-weight: bold")
selection_note = QtWidgets.QLabel("Skinning Layers cannot be attached to this object")
selection_note = QtWidgets.QLabel("蒙皮层无法附加到此对象")
selection_note.setWordWrap(True)
layout.addStretch(1)
@@ -104,11 +104,11 @@ def build_no_layers_ui(parent, actions, session):
selection_display.setText(session.state.selectedSkinCluster)
selection_display.setVisible(is_skinned)
note = "Select a mesh with a skin cluster attached."
note = "选择附着蒙皮节点的网格."
if is_skinned:
note = "Skinning layers are not yet initialized for this mesh."
note = "尚未为此网格初始化蒙皮层."
if import_v1_actions.can_import(session):
note = "Skinning layers from previous ngSkinTools version are initialized on this mesh."
note = "ngSkinTools的旧版本的蒙皮层在此网格上初始化."
selection_note.setText(note)

View File

@@ -1,3 +1,4 @@
# -*- coding: UTF-8 -*-
from ngSkinTools2 import api, cleanup, signal
from ngSkinTools2.api import VertexTransferMode
from ngSkinTools2.api.pyside import QtCore, QtWidgets
@@ -43,8 +44,8 @@ def open(parent, model):
return widgets.button_row(
[
("Transfer", apply),
("Cancel", window.close),
("传递", apply),
("取消", window.close),
]
)
@@ -55,32 +56,32 @@ def open(parent, model):
result = QtWidgets.QVBoxLayout()
vertexMappingMode = QtWidgets.QComboBox()
vertexMappingMode.addItem("Closest point on surface", VertexTransferMode.closestPoint)
vertexMappingMode.addItem("UV space", VertexTransferMode.uvSpace)
vertexMappingMode.addItem("By vertex ID (source and destination vert count must match)", VertexTransferMode.vertexId)
vertexMappingMode.addItem("表面上最近的点", VertexTransferMode.closestPoint)
vertexMappingMode.addItem("UV空间", VertexTransferMode.uvSpace)
vertexMappingMode.addItem("按顶点ID(源和目标顶点数必须匹配)", VertexTransferMode.vertexId)
g = QtWidgets.QGroupBox("Selection")
g = QtWidgets.QGroupBox("选择")
layout = QtWidgets.QVBoxLayout()
g.setLayout(layout)
sourceLabel = QtWidgets.QLabel()
layout.addLayout(createTitledRow("Source:", sourceLabel))
layout.addLayout(createTitledRow("来源:", sourceLabel))
destinationLabel = QtWidgets.QLabel()
layout.addLayout(createTitledRow("Destination:", destinationLabel))
layout.addLayout(createTitledRow("目标:", destinationLabel))
result.addWidget(g)
g = QtWidgets.QGroupBox("Vertex mapping")
g = QtWidgets.QGroupBox("顶点映射")
layout = QtWidgets.QVBoxLayout()
layout.addLayout(createTitledRow("Mapping mode:", vertexMappingMode))
layout.addLayout(createTitledRow("映射模式:", vertexMappingMode))
g.setLayout(layout)
result.addWidget(g)
g = QtWidgets.QGroupBox("Influences mapping")
g = QtWidgets.QGroupBox("影响物映射")
layout = QtWidgets.QVBoxLayout()
g.setLayout(layout)
edit = QtWidgets.QPushButton("Configure")
edit = QtWidgets.QPushButton("配置")
qt.on(edit.clicked)(view_influences_settings)
button_row = QtWidgets.QHBoxLayout()
@@ -90,12 +91,12 @@ def open(parent, model):
result.addWidget(g)
g = QtWidgets.QGroupBox("Other options")
g = QtWidgets.QGroupBox("其他选项")
layout = QtWidgets.QVBoxLayout()
g.setLayout(layout)
keep_layers = QtWidgets.QCheckBox("Keep existing layers on destination")
keep_layers_row = qt.wrap_layout_into_widget(createTitledRow("Destination layers:", keep_layers))
keep_layers = QtWidgets.QCheckBox("保留目标网格的已有图层")
keep_layers_row = qt.wrap_layout_into_widget(createTitledRow("目标图层:", keep_layers))
layout.addWidget(keep_layers_row)
@qt.on(keep_layers.stateChanged)
@@ -137,12 +138,12 @@ def open(parent, model):
tabs = QtWidgets.QTabWidget()
tabs.addTab(qt.wrap_layout_into_widget(build_settings()), "Settings")
tabs.addTab(qt.wrap_layout_into_widget(build_influenes_tab()), "Influences mapping")
tabs.addTab(qt.wrap_layout_into_widget(build_settings()), "设置")
tabs.addTab(qt.wrap_layout_into_widget(build_influenes_tab()), "影响物映射")
window = QtWidgets.QDialog(parent)
cleanup.registerCleanupHandler(window.close)
window.setWindowTitle("Transfer")
window.setWindowTitle("传递")
window.setAttribute(QtCore.Qt.WA_DeleteOnClose)
window.resize(720 * scale_multiplier, 500 * scale_multiplier)
window.setLayout(QtWidgets.QVBoxLayout())
@@ -196,7 +197,7 @@ def build_transfer_action(session, parent):
t.customize_callback = transfer_dialog
t.execute()
result = define_action(parent, "Transfer layers...", callback=handler)
result = define_action(parent, "传递图层...", callback=handler)
@signal.on(session.events.nodeSelectionChanged)
def on_selection_changed():

View File

@@ -1,3 +1,4 @@
# -*- coding: UTF-8 -*-
import webbrowser
from ngSkinTools2.api import versioncheck
@@ -27,10 +28,10 @@ def show(parent, silent_mode):
result.setLayout(layout)
layout.setContentsMargins(20, 30, 20, 20)
header = QtWidgets.QLabel("<strong>Checking for update...</strong>")
result1 = QtWidgets.QLabel("Current version: <strong>2.0.0</strong>")
result2 = QtWidgets.QLabel("Update available: 2.0.1")
download = QtWidgets.QPushButton("Download ngSkinTools v2.0.1")
header = QtWidgets.QLabel(u"<strong>检查更新中...</strong>")
result1 = QtWidgets.QLabel(u"当前版本: <strong>2.1.4</strong>")
result2 = QtWidgets.QLabel("最新版本: 2.1.4")
download = QtWidgets.QPushButton("下载 中文ngSkinTools v2.0.1")
# layout.addWidget(QtWidgets.QLabel("Checking for updates..."))
layout.addWidget(header)
layout.addWidget(result1)
@@ -52,16 +53,16 @@ def show(parent, silent_mode):
:type info: ngSkinTools2.api.versioncheck.
"""
header.setText("<strong>{0}</strong>".format('Update available!' if info.update_available else 'ngSkinTools is up to date.'))
header.setText("<strong>{0}</strong>".format('更新可用!' if info.update_available else '中文ngSkinTools 已经是最新版.'))
result1.setVisible(True)
result1.setText("Current version: <strong>{0}</strong>".format(version.pluginVersion()))
result1.setText(u"当前版本: <strong>{0}</strong>".format(version.pluginVersion()))
if info.update_available:
result2.setVisible(True)
result2.setText(
"Update available: <strong>{0}</strong>, released on {1}".format(info.latest_version, info.update_date.strftime("%d %B, %Y"))
u"最新版本: <strong>{0}</strong>, 发布于 {1}".format(info.latest_version, info.update_date.strftime("%d %B, %Y"))
)
download.setVisible(True)
download.setText("Download ngSkinTools v" + info.latest_version)
download.setText(u"下载 中文ngSkinTools v" + info.latest_version)
@qt.on(download.clicked)
def open_link():
@@ -71,10 +72,10 @@ def show(parent, silent_mode):
# noinspection PyShadowingNames
def buttonsRow(window):
btn_close = QtWidgets.QPushButton("Close")
btn_close = QtWidgets.QPushButton("取消")
btn_close.setMinimumWidth(100 * scale_multiplier)
check_do_on_startup = bind_checkbox(QtWidgets.QCheckBox("Check for updates at startup"), config.checkForUpdatesAtStartup)
check_do_on_startup = bind_checkbox(QtWidgets.QCheckBox("启动时检查更新"), config.checkForUpdatesAtStartup)
layout = QtWidgets.QHBoxLayout()
layout.addWidget(check_do_on_startup)
@@ -88,7 +89,7 @@ def show(parent, silent_mode):
window = QtWidgets.QWidget(parent, Qt.Window | Qt.WindowTitleHint | Qt.CustomizeWindowHint)
window.resize(400 * scale_multiplier, 200 * scale_multiplier)
window.setAttribute(Qt.WA_DeleteOnClose)
window.setWindowTitle("ngSkinTools2 version update")
window.setWindowTitle("ngSkinTools2 (中文) 版本更新")
layout = QtWidgets.QVBoxLayout()
window.setLayout(layout)
layout.setContentsMargins(0, 0, 0, 0)
@@ -106,14 +107,14 @@ def show(parent, silent_mode):
if info.update_available:
window.show()
else:
log.info("not showing the window")
log.info("未显示窗口")
window.close()
cleanup.registerCleanupHandler(window.close)
@qt.on(window.destroyed)
def closed():
log.info("deleting update window")
log.info("删除更新窗口")
versioncheck.download_update_info(success_callback=success_signal.emit, failure_callback=error_signal.emit)
@@ -129,4 +130,4 @@ def show_and_start_update(parent):
def build_action_check_for_updates(parent):
from ngSkinTools2.ui import actions
return actions.define_action(parent, "Check for Updates...", callback=lambda: show_and_start_update(parent))
return actions.define_action(parent, "检查更新...", callback=lambda: show_and_start_update(parent))

View File

@@ -66,7 +66,7 @@ class SemanticVersion(Object):
result = pattern.match(stringVersion)
if result is None:
raise Exception("Invalid version string: '{0}'".format(stringVersion))
raise Exception("无效的版本字符串: '{0}'".format(stringVersion))
self.major = toInt(result.group(1))
self.minor = toInt(result.group(3))