Updated
							
								
								
									
										21
									
								
								Scripts/Animation/dwpicker/LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,21 @@
 | 
			
		||||
MIT License
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2020 DreamWall
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
SOFTWARE.
 | 
			
		||||
							
								
								
									
										56
									
								
								Scripts/Animation/dwpicker/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,56 @@
 | 
			
		||||
# Dreamwall Picker
 | 
			
		||||
 | 
			
		||||
适用于 Autodesk Maya 2017(或更高版本)的动画选择器
 | 
			
		||||
 | 
			
		||||
作者:Lionel Brouyère, Olivier Evers
 | 
			
		||||
> 该工具是 Hotbox Designer(Lionel Brouyère)的一个分支。
 | 
			
		||||
> 一个跨 DCC 的菜单、标记菜单和热盒设计器。
 | 
			
		||||
> https://github.com/luckylyk/hotbox_designer
 | 
			
		||||
 | 
			
		||||
### 功能
 | 
			
		||||
- 简单快速的选择器创建。
 | 
			
		||||
- 导入 2022 年之前完成的 AnimSchool 选择器。
 | 
			
		||||
- 将选择器存储在 Maya 场景中。
 | 
			
		||||
- 高级选择器编辑器。
 | 
			
		||||
- 执行 AnimSchool 选择器的所有功能以及更多功能...
 | 
			
		||||
<center><img src="https://raw.githubusercontent.com/DreamWall-Animation/dwpicker/main/screenshots/picker.gif" alt="drawing" align="center" width="250"/> <img src="https://s10.gifyu.com/images/createbuttons.gif" alt="drawing" align="center" width="400"/>
 | 
			
		||||
<img src="https://raw.githubusercontent.com/DreamWall-Animation/dwpicker/main/screenshots/editor.gif" alt="drawing" align="center" width="370"/>
 | 
			
		||||
 | 
			
		||||
### 安装
 | 
			
		||||
将名为 "dwpicker" 的文件夹(不是 dwpicker-main)放入 Maya 脚本文件夹中
 | 
			
		||||
 | 
			
		||||
| 操作系统 | 路径                                                  |
 | 
			
		||||
| ------   | ------                                                |
 | 
			
		||||
| Linux    | ~/<用户名>/maya/scripts                               |
 | 
			
		||||
| Windows  | \Users\<用户名>\Documents\maya\scripts                |
 | 
			
		||||
| Mac OS X | ~<用户名>/Library/Preferences/Autodesk/maya/scripts   |
 | 
			
		||||
 | 
			
		||||
### 如何运行
 | 
			
		||||
 | 
			
		||||
```python
 | 
			
		||||
import dwpicker
 | 
			
		||||
dwpicker.show()
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 常见问题
 | 
			
		||||
 | 
			
		||||
#### 它能在 Maya 2025 上运行吗?
 | 
			
		||||
可以!(自版本 0.11.2 起)
 | 
			
		||||
 | 
			
		||||
#### 我的绑定包含多个命名空间或嵌套命名空间。
 | 
			
		||||
此功能当前不支持。选择器旨在提供单级命名空间的灵活性,允许一个选择器在场景中为同一绑定的多个实例服务。切换选择器的命名空间很简单。然而,尽管我们努力保持这种灵活性,但我们尚未找到支持嵌套命名空间的简单方法。虽然有潜在的解决方案,但它们看起来都相当复杂,难以为用户理解和实现。也许将来会有一个绝妙的主意出现,但目前,这个功能不在我们的计划中。我们欢迎您提出任何建议!
 | 
			
		||||
 | 
			
		||||
#### 为什么我不能使用相对路径来存储我的图像文件?
 | 
			
		||||
当您打开选择器时,它会将文件直接导入场景中,失去原始路径引用。我们选择导入数据而不是直接引用它们,因为许多动画师更喜欢为他们的特定镜头需求自定义选择器(例如,为特定镜头添加道具或约束按钮)。这种方法使得使用相对路径变得复杂。
 | 
			
		||||
 | 
			
		||||
#### 如何在我将选择器分享给其他人时保留图像,而他们的文件存储在其他地方?
 | 
			
		||||
尽管不支持相对路径,但与其他 Maya 路径属性类似,您可以在路径中包含环境变量。设置自定义环境变量可能很复杂。因此,我们建议使用一个默认变量:DWPICKER_PROJECT_DIRECTORY,可在选择器首选项窗口中使用。
 | 
			
		||||
 | 
			
		||||
如果您将 DWPICKER_PROJECT_DIRECTORY 配置为 `c:/my_pickers`,并且您有一个图像路径为:
 | 
			
		||||
`c:/my_pickers/my_character/background.png`,可以这样输入以使路径动态化:`$DWPICKER_PROJECT_DIRECTORY/my_character/background.png`
 | 
			
		||||
当您从 UI 中选择文件时,它会自动创建包含变量的路径。
 | 
			
		||||
 | 
			
		||||
### 支持
 | 
			
		||||
最好在 GitHub 页面上发布问题。\
 | 
			
		||||
如果您没有 GitHub 帐户,可以发送邮件至 `brouyere |a| dreamwall.be`。\
 | 
			
		||||
请在邮件主题中以 ***[dwpicker]*** 开头。(请注意,使用这种方式回复的延迟可能会更长)。
 | 
			
		||||
							
								
								
									
										0
									
								
								Scripts/Animation/dwpicker/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										134
									
								
								Scripts/Animation/dwpicker/dwpicker/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,134 @@
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
from maya import cmds
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if int(cmds.about(majorVersion=True)) >= 2025:
 | 
			
		||||
    print('>> PySide6 Maya version found. PySide2 remap activated.')
 | 
			
		||||
    sys.path.append('{}/qt_remapping'.format(os.path.dirname(__file__)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from dwpicker.main import DwPicker, WINDOW_CONTROL_NAME
 | 
			
		||||
from dwpicker.optionvar import ensure_optionvars_exists
 | 
			
		||||
from dwpicker.qtutils import remove_workspace_control
 | 
			
		||||
from dwpicker.updatechecker import warn_if_update_available
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_dwpicker = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def show(editable=True, pickers=None, ignore_scene_pickers=False):
 | 
			
		||||
    """
 | 
			
		||||
    This is the dwpicker default startup function.
 | 
			
		||||
    editable: bool
 | 
			
		||||
        This allow users to do local edit on their picker. This is NOT
 | 
			
		||||
        affecting the original file.
 | 
			
		||||
    pickers: list[str]
 | 
			
		||||
        Path to pickers to open. If scene contains already pickers,
 | 
			
		||||
        they are going to be ignored.
 | 
			
		||||
    ignore_scene_pickers:
 | 
			
		||||
        This is loading the picker empty, ignoring the scene content.
 | 
			
		||||
    """
 | 
			
		||||
    ensure_optionvars_exists()
 | 
			
		||||
    global _dwpicker
 | 
			
		||||
    if not _dwpicker:
 | 
			
		||||
        warn_if_update_available()
 | 
			
		||||
        _dwpicker = DwPicker()
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        _dwpicker.show(dockable=True)
 | 
			
		||||
    except RuntimeError:
 | 
			
		||||
        # Workspace control already exists, UI restore as probably failed.
 | 
			
		||||
        remove_workspace_control(WINDOW_CONTROL_NAME)
 | 
			
		||||
        _dwpicker.show()
 | 
			
		||||
 | 
			
		||||
    _dwpicker.set_editable(editable)
 | 
			
		||||
    if not ignore_scene_pickers and not pickers:
 | 
			
		||||
        _dwpicker.load_saved_pickers()
 | 
			
		||||
 | 
			
		||||
    if not pickers:
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    _dwpicker.clear()
 | 
			
		||||
    for filename in pickers:
 | 
			
		||||
        try:
 | 
			
		||||
            print(filename)
 | 
			
		||||
            _dwpicker.add_picker_from_file(filename)
 | 
			
		||||
        except BaseException:
 | 
			
		||||
            import traceback
 | 
			
		||||
            print("Not able to load: {}".format(filename))
 | 
			
		||||
            print(traceback.format_exc())
 | 
			
		||||
    _dwpicker.store_local_pickers_data()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def toggle():
 | 
			
		||||
    if not _dwpicker:
 | 
			
		||||
        return show()
 | 
			
		||||
    _dwpicker.setVisible(not _dwpicker.isVisible())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def close():
 | 
			
		||||
    global _dwpicker
 | 
			
		||||
    if not _dwpicker:
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    _dwpicker.unregister_callbacks()
 | 
			
		||||
    for i in range(_dwpicker.tab.count()):
 | 
			
		||||
        picker = _dwpicker.tab.widget(i)
 | 
			
		||||
        picker.unregister_callbacks()
 | 
			
		||||
 | 
			
		||||
    _dwpicker.close()
 | 
			
		||||
    _dwpicker = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class disable():
 | 
			
		||||
    """
 | 
			
		||||
    This context manager temporarily disable the picker callbacks.
 | 
			
		||||
    This is usefull to decorate code which change the maya selection multiple
 | 
			
		||||
    times. This can lead constant refresh of the picker and lead performance
 | 
			
		||||
    issue. This should fix it.
 | 
			
		||||
    """
 | 
			
		||||
    def __enter__(self):
 | 
			
		||||
        if _dwpicker is None:
 | 
			
		||||
            return
 | 
			
		||||
        _dwpicker.unregister_callbacks()
 | 
			
		||||
        for i in range(_dwpicker.tab.count()):
 | 
			
		||||
            picker = _dwpicker.tab.widget(i)
 | 
			
		||||
            picker.unregister_callbacks()
 | 
			
		||||
 | 
			
		||||
    def __exit__(self, *_):
 | 
			
		||||
        if _dwpicker is None:
 | 
			
		||||
            return
 | 
			
		||||
        _dwpicker.register_callbacks()
 | 
			
		||||
        for i in range(_dwpicker.tab.count()):
 | 
			
		||||
            picker = _dwpicker.tab.widget(i)
 | 
			
		||||
            picker.register_callbacks()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def current():
 | 
			
		||||
    """
 | 
			
		||||
    Get the current picker widget visible in the main tab widget.
 | 
			
		||||
    """
 | 
			
		||||
    if not _dwpicker:
 | 
			
		||||
        return
 | 
			
		||||
    return _dwpicker.tab.currentWidget()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def refresh():
 | 
			
		||||
    """
 | 
			
		||||
    Trigger this function to refresh ui if the picker datas has been changed
 | 
			
		||||
    manually inside the scene.
 | 
			
		||||
    """
 | 
			
		||||
    if not _dwpicker:
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def open_picker_file(filepath):
 | 
			
		||||
    """
 | 
			
		||||
    Add programmatically a picker to the main UI.
 | 
			
		||||
    """
 | 
			
		||||
    if not _dwpicker:
 | 
			
		||||
        return cmds.warning('Please open picker first.')
 | 
			
		||||
    _dwpicker.add_picker_from_file(filepath)
 | 
			
		||||
    _dwpicker.store_local_pickers_data()
 | 
			
		||||
							
								
								
									
										86
									
								
								Scripts/Animation/dwpicker/dwpicker/align.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,86 @@
 | 
			
		||||
from PySide2 import QtCore
 | 
			
		||||
from dwpicker.geometry import split_line
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def align_shapes(shapes, direction):
 | 
			
		||||
    _direction_matches[direction](shapes)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def align_left(shapes):
 | 
			
		||||
    left = min(s.rect.left() for s in shapes)
 | 
			
		||||
    for shape in shapes:
 | 
			
		||||
        shape.rect.moveLeft(left)
 | 
			
		||||
        shape.synchronize_rect()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def align_h_center(shapes):
 | 
			
		||||
    x = sum(s.rect.center().x() for s in shapes) / len(shapes)
 | 
			
		||||
    for shape in shapes:
 | 
			
		||||
        shape.rect.moveCenter(QtCore.QPointF(x, shape.rect.center().y()))
 | 
			
		||||
        shape.synchronize_rect()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def align_right(shapes):
 | 
			
		||||
    right = max(s.rect.right() for s in shapes)
 | 
			
		||||
    for shape in shapes:
 | 
			
		||||
        shape.rect.moveRight(right)
 | 
			
		||||
        shape.synchronize_rect()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def align_top(shapes):
 | 
			
		||||
    top = min(s.rect.top() for s in shapes)
 | 
			
		||||
    for shape in shapes:
 | 
			
		||||
        shape.rect.moveTop(top)
 | 
			
		||||
        shape.synchronize_rect()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def align_v_center(shapes):
 | 
			
		||||
    y = sum(s.rect.center().y() for s in shapes) / len(shapes)
 | 
			
		||||
    for shape in shapes:
 | 
			
		||||
        shape.rect.moveCenter(QtCore.QPointF(shape.rect.center().x(), y))
 | 
			
		||||
        shape.synchronize_rect()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def align_bottom(shapes):
 | 
			
		||||
    bottom = max(s.rect.bottom() for s in shapes)
 | 
			
		||||
    for shape in shapes:
 | 
			
		||||
        shape.rect.moveBottom(bottom)
 | 
			
		||||
        shape.synchronize_rect()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def arrange_horizontal(shapes):
 | 
			
		||||
    if len(shapes) < 3:
 | 
			
		||||
        return
 | 
			
		||||
    shapes = sorted(shapes, key=lambda s: s.rect.center().x())
 | 
			
		||||
    centers = split_line(
 | 
			
		||||
        point1=shapes[0].rect.center(),
 | 
			
		||||
        point2=shapes[-1].rect.center(),
 | 
			
		||||
        step_number=len(shapes))
 | 
			
		||||
    for shape, center in zip(shapes, centers):
 | 
			
		||||
        point = QtCore.QPointF(center.x(), shape.rect.center().y())
 | 
			
		||||
        shape.rect.moveCenter(point)
 | 
			
		||||
        shape.synchronize_rect()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def arrange_vertical(shapes):
 | 
			
		||||
    if len(shapes) < 3:
 | 
			
		||||
        return
 | 
			
		||||
    shapes = sorted(shapes, key=lambda s: s.rect.center().y())
 | 
			
		||||
    centers = split_line(
 | 
			
		||||
        point1=shapes[0].rect.center(),
 | 
			
		||||
        point2=shapes[-1].rect.center(),
 | 
			
		||||
        step_number=len(shapes))
 | 
			
		||||
    for shape, center in zip(shapes, centers):
 | 
			
		||||
        point = QtCore.QPointF(shape.rect.center().x(), center.y())
 | 
			
		||||
        shape.rect.moveCenter(point)
 | 
			
		||||
        shape.synchronize_rect()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_direction_matches = {
 | 
			
		||||
    'left': align_left,
 | 
			
		||||
    'h_center': align_h_center,
 | 
			
		||||
    'right': align_right,
 | 
			
		||||
    'top': align_top,
 | 
			
		||||
    'v_center': align_v_center,
 | 
			
		||||
    'bottom': align_bottom
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								Scripts/Animation/dwpicker/dwpicker/appinfos.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,4 @@
 | 
			
		||||
VERSION = 0, 11, 2  # Version, Feature, Hotfix.
 | 
			
		||||
RELEASE_DATE = 'june 7th 2024'
 | 
			
		||||
DW_WEBSITE = 'https://fr.dreamwall.be/'
 | 
			
		||||
DW_GITHUB = 'https://github.com/DreamWall-Animation'
 | 
			
		||||
							
								
								
									
										29
									
								
								Scripts/Animation/dwpicker/dwpicker/arrayutils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,29 @@
 | 
			
		||||
 | 
			
		||||
def move_elements_to_array_end(array, elements):
 | 
			
		||||
    return [e for e in array if e not in elements] + [e for e in elements]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def move_elements_to_array_begin(array, elements):
 | 
			
		||||
    return [e for e in elements] + [e for e in array if e not in elements]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def move_up_array_elements(array, elements):
 | 
			
		||||
    for element in reversed(array):
 | 
			
		||||
        if element not in elements:
 | 
			
		||||
            continue
 | 
			
		||||
        index = array.index(element)
 | 
			
		||||
        if index == len(array):
 | 
			
		||||
            continue
 | 
			
		||||
        array.insert(index + 2, element)
 | 
			
		||||
        array.pop(index)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def move_down_array_elements(array, elements):
 | 
			
		||||
    for shape in array:
 | 
			
		||||
        if shape not in elements:
 | 
			
		||||
            continue
 | 
			
		||||
        index = array.index(shape)
 | 
			
		||||
        if index == 0:
 | 
			
		||||
            continue
 | 
			
		||||
        array.pop(index)
 | 
			
		||||
        array.insert(index - 1, shape)
 | 
			
		||||
							
								
								
									
										22
									
								
								Scripts/Animation/dwpicker/dwpicker/clipboard.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,22 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_clipboard_data = None
 | 
			
		||||
_clipboard_settings_data = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def set(data):
 | 
			
		||||
    global _clipboard_data
 | 
			
		||||
    _clipboard_data = data
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get():
 | 
			
		||||
    return _clipboard_data
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def set_settings(settings):
 | 
			
		||||
    global _clipboard_settings_data
 | 
			
		||||
    _clipboard_settings_data = settings
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_settings():
 | 
			
		||||
    return _clipboard_settings_data or {}
 | 
			
		||||
							
								
								
									
										272
									
								
								Scripts/Animation/dwpicker/dwpicker/colorwheel.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,272 @@
 | 
			
		||||
import math
 | 
			
		||||
from PySide2 import QtWidgets, QtGui, QtCore
 | 
			
		||||
from dwpicker.qtutils import get_cursor
 | 
			
		||||
from dwpicker.geometry import (
 | 
			
		||||
    get_relative_point, get_point_on_line, get_absolute_angle_c)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONICAL_GRADIENT = (
 | 
			
		||||
    (0.0, (0, 255, 255)),
 | 
			
		||||
    (0.16, (0, 0, 255)),
 | 
			
		||||
    (0.33, (255, 0, 255)),
 | 
			
		||||
    (0.5, (255, 0, 0)),
 | 
			
		||||
    (0.66, (255, 255, 0)),
 | 
			
		||||
    (0.83, (0, 255, 0)),
 | 
			
		||||
    (1.0, (0, 255, 255)))
 | 
			
		||||
TRANSPARENT = 0, 0, 0, 0
 | 
			
		||||
BLACK = 'black'
 | 
			
		||||
WHITE = 'white'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ColorDialog(QtWidgets.QDialog):
 | 
			
		||||
    def __init__(self, hexacolor, parent=None):
 | 
			
		||||
        super(ColorDialog, self).__init__(parent)
 | 
			
		||||
        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
 | 
			
		||||
        self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
 | 
			
		||||
        self.colorwheel = ColorWheel()
 | 
			
		||||
        self.colorwheel.set_current_color(QtGui.QColor(hexacolor))
 | 
			
		||||
        self.ok = QtWidgets.QPushButton('ok')
 | 
			
		||||
        self.ok.released.connect(self.accept)
 | 
			
		||||
 | 
			
		||||
        self.layout = QtWidgets.QVBoxLayout(self)
 | 
			
		||||
        self.layout.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        self.layout.setSpacing(0)
 | 
			
		||||
        self.layout.addWidget(self.colorwheel)
 | 
			
		||||
        self.layout.addWidget(self.ok)
 | 
			
		||||
 | 
			
		||||
    def colorname(self):
 | 
			
		||||
        return self.colorwheel.current_color().name()
 | 
			
		||||
 | 
			
		||||
    def exec_(self):
 | 
			
		||||
        point = get_cursor(self)
 | 
			
		||||
        point.setX(point.x() - 50)
 | 
			
		||||
        point.setY(point.y() - 75)
 | 
			
		||||
        self.move(point)
 | 
			
		||||
        return super(ColorDialog, self).exec_()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ColorWheel(QtWidgets.QWidget):
 | 
			
		||||
    currentColorChanged = QtCore.Signal(QtGui.QColor)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(ColorWheel, self).__init__(parent)
 | 
			
		||||
        self._is_clicked = False
 | 
			
		||||
        self._rect = QtCore.QRectF(25, 25, 50, 50)
 | 
			
		||||
        self._current_color = QtGui.QColor(WHITE)
 | 
			
		||||
        self._color_point = QtCore.QPoint(150, 50)
 | 
			
		||||
        self._current_tool = None
 | 
			
		||||
        self._angle = 180
 | 
			
		||||
        self.setFixedSize(100, 100)
 | 
			
		||||
        self.initUI()
 | 
			
		||||
 | 
			
		||||
    def initUI(self):
 | 
			
		||||
        self._conicalGradient = QtGui.QConicalGradient(
 | 
			
		||||
            self.width() / 2, self.height() / 2, 180)
 | 
			
		||||
        for pos, (r, g, b) in CONICAL_GRADIENT:
 | 
			
		||||
            self._conicalGradient.setColorAt(pos, QtGui.QColor(r, g, b))
 | 
			
		||||
 | 
			
		||||
        top = self._rect.top()
 | 
			
		||||
        bottom = self._rect.top() + self._rect.height()
 | 
			
		||||
        self._vertical_gradient = QtGui.QLinearGradient(0, top, 0, bottom)
 | 
			
		||||
        self._vertical_gradient.setColorAt(0.0, QtGui.QColor(*TRANSPARENT))
 | 
			
		||||
        self._vertical_gradient.setColorAt(1.0, QtGui.QColor(BLACK))
 | 
			
		||||
 | 
			
		||||
        left = self._rect.left()
 | 
			
		||||
        right = self._rect.left() + self._rect.width()
 | 
			
		||||
        self._horizontal_gradient = QtGui.QLinearGradient(left, 0, right, 0)
 | 
			
		||||
        self._horizontal_gradient.setColorAt(0.0, QtGui.QColor(WHITE))
 | 
			
		||||
 | 
			
		||||
    def paintEvent(self, _):
 | 
			
		||||
        try:
 | 
			
		||||
            painter = QtGui.QPainter()
 | 
			
		||||
            painter.begin(self)
 | 
			
		||||
            self.paint(painter)
 | 
			
		||||
        except BaseException:
 | 
			
		||||
            pass  # avoid crash
 | 
			
		||||
            # TODO: log the error
 | 
			
		||||
        finally:
 | 
			
		||||
            painter.end()
 | 
			
		||||
 | 
			
		||||
    def mousePressEvent(self, event):
 | 
			
		||||
        tool = 'rect' if self._rect.contains(event.pos()) else 'wheel'
 | 
			
		||||
        self._current_tool = tool
 | 
			
		||||
        self.mouse_update(event)
 | 
			
		||||
 | 
			
		||||
    def mouseMoveEvent(self, event):
 | 
			
		||||
        self._is_clicked = True
 | 
			
		||||
        self.mouse_update(event)
 | 
			
		||||
 | 
			
		||||
    def mouse_update(self, event):
 | 
			
		||||
        if self._current_tool == 'rect':
 | 
			
		||||
            self.color_point = event.pos()
 | 
			
		||||
        else:
 | 
			
		||||
            center = self._get_center()
 | 
			
		||||
            a = QtCore.QPoint(event.pos().x(), center.y())
 | 
			
		||||
            self._angle = get_absolute_angle_c(a=a, b=event.pos(), c=center)
 | 
			
		||||
 | 
			
		||||
        self.repaint()
 | 
			
		||||
        self.currentColorChanged.emit(self.current_color())
 | 
			
		||||
 | 
			
		||||
    def mouseReleaseEvent(self, event):
 | 
			
		||||
        self._is_clicked = False
 | 
			
		||||
 | 
			
		||||
    def paint(self, painter):
 | 
			
		||||
        painter.setRenderHint(QtGui.QPainter.Antialiasing)
 | 
			
		||||
 | 
			
		||||
        pen = QtGui.QPen(QtGui.QColor(0, 0, 0, 0))
 | 
			
		||||
        pen.setWidth(0)
 | 
			
		||||
        pen.setJoinStyle(QtCore.Qt.MiterJoin)
 | 
			
		||||
 | 
			
		||||
        painter.setBrush(self._conicalGradient)
 | 
			
		||||
        painter.setPen(pen)
 | 
			
		||||
        painter.drawRoundedRect(
 | 
			
		||||
            6, 6, (self.width() - 12), (self.height() - 12),
 | 
			
		||||
            self.width(), self.height())
 | 
			
		||||
 | 
			
		||||
        painter.setBrush(self.palette().color(QtGui.QPalette.Window))
 | 
			
		||||
        painter.drawRoundedRect(
 | 
			
		||||
            12.5, 12.5, (self.width() - 25), (self.height() - 25),
 | 
			
		||||
            self.width(), self.height())
 | 
			
		||||
 | 
			
		||||
        self._horizontal_gradient.setColorAt(
 | 
			
		||||
            1.0, self._get_current_wheel_color())
 | 
			
		||||
        painter.setBrush(self._horizontal_gradient)
 | 
			
		||||
        painter.drawRect(self._rect)
 | 
			
		||||
 | 
			
		||||
        painter.setBrush(self._vertical_gradient)
 | 
			
		||||
        painter.drawRect(self._rect)
 | 
			
		||||
 | 
			
		||||
        pen.setColor(QtGui.QColor(BLACK))
 | 
			
		||||
        pen.setWidth(3)
 | 
			
		||||
        painter.setPen(pen)
 | 
			
		||||
 | 
			
		||||
        angle = math.radians(self._angle)
 | 
			
		||||
        painter.drawLine(
 | 
			
		||||
            get_point_on_line(angle, 37),
 | 
			
		||||
            get_point_on_line(angle, 46))
 | 
			
		||||
 | 
			
		||||
        pen.setWidth(5)
 | 
			
		||||
        pen.setCapStyle(QtCore.Qt.RoundCap)
 | 
			
		||||
        painter.setPen(pen)
 | 
			
		||||
        painter.drawPoint(self._color_point)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def color_point(self):
 | 
			
		||||
        return self._color_point
 | 
			
		||||
 | 
			
		||||
    @color_point.setter
 | 
			
		||||
    def color_point(self, point):
 | 
			
		||||
        if point.x() < self._rect.left():
 | 
			
		||||
            x = self._rect.left()
 | 
			
		||||
        elif point.x() > self._rect.left() + self._rect.width():
 | 
			
		||||
            x = self._rect.left() + self._rect.width()
 | 
			
		||||
        else:
 | 
			
		||||
            x = point.x()
 | 
			
		||||
 | 
			
		||||
        if point.y() < self._rect.top():
 | 
			
		||||
            y = self._rect.top()
 | 
			
		||||
        elif point.y() > self._rect.top() + self._rect.height():
 | 
			
		||||
            y = self._rect.top() + self._rect.height()
 | 
			
		||||
        else:
 | 
			
		||||
            y = point.y()
 | 
			
		||||
 | 
			
		||||
        self._color_point = QtCore.QPoint(x, y)
 | 
			
		||||
 | 
			
		||||
    def _get_current_wheel_color(self):
 | 
			
		||||
        degree = 360 - self._angle
 | 
			
		||||
        return QtGui.QColor(*degree_to_color(degree))
 | 
			
		||||
 | 
			
		||||
    def _get_center(self):
 | 
			
		||||
        return QtCore.QPoint(self.width() / 2, self.height() / 2)
 | 
			
		||||
 | 
			
		||||
    def current_color(self):
 | 
			
		||||
        point = get_relative_point(self._rect, self.color_point)
 | 
			
		||||
        x_factor = 1.0 - (float(point.x()) / self._rect.width())
 | 
			
		||||
        y_factor = 1.0 - (float(point.y()) / self._rect.height())
 | 
			
		||||
        r, g, b, _ = self._get_current_wheel_color().getRgb()
 | 
			
		||||
 | 
			
		||||
        # fade to white
 | 
			
		||||
        differences = 255.0 - r, 255.0 - g, 255.0 - b
 | 
			
		||||
        r += round(differences[0] * x_factor)
 | 
			
		||||
        g += round(differences[1] * x_factor)
 | 
			
		||||
        b += round(differences[2] * x_factor)
 | 
			
		||||
 | 
			
		||||
        # fade to black
 | 
			
		||||
        r = round(r * y_factor)
 | 
			
		||||
        g = round(g * y_factor)
 | 
			
		||||
        b = round(b * y_factor)
 | 
			
		||||
 | 
			
		||||
        return QtGui.QColor(r, g, b)
 | 
			
		||||
 | 
			
		||||
    def set_current_color(self, color):
 | 
			
		||||
        [r, g, b] = color.getRgb()[:3]
 | 
			
		||||
        self._angle = 360.0 - (QtGui.QColor(r, g, b).getHslF()[0] * 360.0)
 | 
			
		||||
        self._angle = self._angle if self._angle != 720.0 else 0
 | 
			
		||||
 | 
			
		||||
        x = ((((
 | 
			
		||||
            sorted([r, g, b], reverse=True)[0] -
 | 
			
		||||
            sorted([r, g, b])[0]) / 255.0) * self._rect.width()) +
 | 
			
		||||
            self._rect.left())
 | 
			
		||||
 | 
			
		||||
        y = ((((
 | 
			
		||||
            255 - (sorted([r, g, b], reverse=True)[0])) / 255.0) *
 | 
			
		||||
            self._rect.height()) + self._rect.top())
 | 
			
		||||
 | 
			
		||||
        self._current_color = color
 | 
			
		||||
        self._color_point = QtCore.QPoint(x, y)
 | 
			
		||||
        self.repaint()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def degree_to_color(degree):
 | 
			
		||||
    if degree is None:
 | 
			
		||||
        return None
 | 
			
		||||
    degree = degree / 360.0
 | 
			
		||||
 | 
			
		||||
    r, g, b = 255.0, 255.0, 255.0
 | 
			
		||||
    contain_red = (
 | 
			
		||||
        (degree >= 0.0 and degree <= 0.33)
 | 
			
		||||
        or (degree >= 0.66 and degree <= 1.0))
 | 
			
		||||
 | 
			
		||||
    if contain_red:
 | 
			
		||||
        if degree >= 0.66 and degree <= 0.83:
 | 
			
		||||
            factor = degree - 0.66
 | 
			
		||||
            r = round(255 * (factor / .16))
 | 
			
		||||
        if (degree > 0.0 and degree < 0.16) or (degree > 0.83 and degree < 1.0):
 | 
			
		||||
            r = 255
 | 
			
		||||
        elif degree >= 0.16 and degree <= 0.33:
 | 
			
		||||
            factor = degree - 0.16
 | 
			
		||||
            r = 255 - round(255 * (factor / .16))
 | 
			
		||||
    else:
 | 
			
		||||
        r = 0
 | 
			
		||||
    r = min(r, 255)
 | 
			
		||||
    r = max(r, 0)
 | 
			
		||||
 | 
			
		||||
    # GREEN
 | 
			
		||||
    if degree >= 0.0 and degree <= 0.66:
 | 
			
		||||
        if degree <= 0.16:
 | 
			
		||||
            g = round(255.0 * (degree / .16))
 | 
			
		||||
        elif degree < 0.5:
 | 
			
		||||
            g = 255
 | 
			
		||||
        if degree >= 0.5:
 | 
			
		||||
            factor = degree - 0.5
 | 
			
		||||
            g = 255 - round(255.0 * (factor / .16))
 | 
			
		||||
    else:
 | 
			
		||||
        g = 0
 | 
			
		||||
    g = min(g, 255.0)
 | 
			
		||||
    g = max(g, 0)
 | 
			
		||||
 | 
			
		||||
    # BLUE
 | 
			
		||||
    if degree >= 0.33 and degree <= 1.0:
 | 
			
		||||
        if degree <= 0.5:
 | 
			
		||||
            factor = degree - 0.33
 | 
			
		||||
            b = round(255 * (factor / .16))
 | 
			
		||||
        elif degree < 0.83:
 | 
			
		||||
            b = 255.0
 | 
			
		||||
        if degree >= 0.83 and degree <= 1.0:
 | 
			
		||||
            factor = degree - 0.83
 | 
			
		||||
            b = 255.0 - round(255.0 * (factor / .16))
 | 
			
		||||
    else:
 | 
			
		||||
        b = 0
 | 
			
		||||
    b = min(b, 255)
 | 
			
		||||
    b = max(b, 0)
 | 
			
		||||
    return r, g, b
 | 
			
		||||
							
								
								
									
										122
									
								
								Scripts/Animation/dwpicker/dwpicker/commands.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,122 @@
 | 
			
		||||
from copy import deepcopy
 | 
			
		||||
from PySide2 import QtWidgets, QtCore
 | 
			
		||||
from dwpicker.templates import COMMAND
 | 
			
		||||
from dwpicker.qtutils import icon
 | 
			
		||||
from dwpicker.dialog import CommandEditorDialog
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CommandsEditor(QtWidgets.QWidget):
 | 
			
		||||
    valueSet = QtCore.Signal(object)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(CommandsEditor, self).__init__(parent)
 | 
			
		||||
        self.warning = QtWidgets.QLabel('Select only one shape')
 | 
			
		||||
 | 
			
		||||
        self.commands = QtWidgets.QListWidget()
 | 
			
		||||
        self.commands.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
 | 
			
		||||
        self.commands.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
 | 
			
		||||
        self.commands.setHorizontalScrollBarPolicy(
 | 
			
		||||
            QtCore.Qt.ScrollBarAlwaysOff)
 | 
			
		||||
 | 
			
		||||
        self.add_command = QtWidgets.QPushButton('Add command')
 | 
			
		||||
        self.add_command.released.connect(self.call_create_command)
 | 
			
		||||
        self.add_command.setEnabled(False)
 | 
			
		||||
 | 
			
		||||
        layout = QtWidgets.QVBoxLayout(self)
 | 
			
		||||
        layout.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        layout.setSpacing(0)
 | 
			
		||||
        layout.addWidget(self.warning)
 | 
			
		||||
        layout.addWidget(self.commands)
 | 
			
		||||
        layout.addWidget(self.add_command)
 | 
			
		||||
 | 
			
		||||
    def set_options(self, options):
 | 
			
		||||
        self.commands.clear()
 | 
			
		||||
        if len(options) != 1:
 | 
			
		||||
            self.warning.setVisible(True)
 | 
			
		||||
            self.add_command.setEnabled(False)
 | 
			
		||||
            return
 | 
			
		||||
        self.warning.setVisible(False)
 | 
			
		||||
        self.add_command.setEnabled(True)
 | 
			
		||||
        for command in options[0]['action.commands']:
 | 
			
		||||
            self.call_add_command(command)
 | 
			
		||||
 | 
			
		||||
    def call_create_command(self):
 | 
			
		||||
        command = deepcopy(COMMAND)
 | 
			
		||||
        dialog = CommandEditorDialog(command)
 | 
			
		||||
        if not dialog.exec_():
 | 
			
		||||
            return
 | 
			
		||||
        self.call_add_command(dialog.command_data())
 | 
			
		||||
        self.valueSet.emit(self.commands_data())
 | 
			
		||||
 | 
			
		||||
    def call_add_command(self, command=None):
 | 
			
		||||
        widget = CommandItemWidget(command)
 | 
			
		||||
        widget.editRequested.connect(self.edit_command)
 | 
			
		||||
        widget.deletedRequested.connect(self.delete_command)
 | 
			
		||||
        item = QtWidgets.QListWidgetItem()
 | 
			
		||||
        item.widget = widget
 | 
			
		||||
        item.setSizeHint(
 | 
			
		||||
            QtCore.QSize(
 | 
			
		||||
                self.commands.width() -
 | 
			
		||||
                self.commands.verticalScrollBar().width(),
 | 
			
		||||
                widget.sizeHint().height()))
 | 
			
		||||
        self.commands.addItem(item)
 | 
			
		||||
        self.commands.setItemWidget(item, widget)
 | 
			
		||||
 | 
			
		||||
    def edit_command(self, widget):
 | 
			
		||||
        for r in range(self.commands.count()):
 | 
			
		||||
            item = self.commands.item(r)
 | 
			
		||||
            if item.widget != widget:
 | 
			
		||||
                continue
 | 
			
		||||
            dialog = CommandEditorDialog(item.widget.command)
 | 
			
		||||
            if not dialog.exec_():
 | 
			
		||||
                return
 | 
			
		||||
            widget.command = dialog.command_data()
 | 
			
		||||
            widget.update_label()
 | 
			
		||||
            self.valueSet.emit(self.commands_data())
 | 
			
		||||
 | 
			
		||||
    def delete_command(self, widget):
 | 
			
		||||
        for r in range(self.commands.count()):
 | 
			
		||||
            item = self.commands.item(r)
 | 
			
		||||
            if item.widget != widget:
 | 
			
		||||
                continue
 | 
			
		||||
            self.commands.takeItem(r)
 | 
			
		||||
            self.valueSet.emit(self.commands_data())
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
    def commands_data(self):
 | 
			
		||||
        return [
 | 
			
		||||
            self.commands.item(r).widget.command
 | 
			
		||||
            for r in range(self.commands.count())]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CommandItemWidget(QtWidgets.QWidget):
 | 
			
		||||
    editRequested = QtCore.Signal(object)
 | 
			
		||||
    deletedRequested = QtCore.Signal(object)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, command, parent=None):
 | 
			
		||||
        super(CommandItemWidget, self).__init__(parent)
 | 
			
		||||
 | 
			
		||||
        self.command = command
 | 
			
		||||
        self.label = QtWidgets.QLabel(self.get_label())
 | 
			
		||||
        self.edit = QtWidgets.QPushButton(icon('edit2.png'), '')
 | 
			
		||||
        self.edit.released.connect(lambda: self.editRequested.emit(self))
 | 
			
		||||
        self.delete = QtWidgets.QPushButton(icon('delete2.png'), '')
 | 
			
		||||
        self.delete.released.connect(lambda: self.deletedRequested.emit(self))
 | 
			
		||||
 | 
			
		||||
        layout = QtWidgets.QHBoxLayout(self)
 | 
			
		||||
        layout.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        layout.setSpacing(0)
 | 
			
		||||
        layout.addWidget(self.label)
 | 
			
		||||
        layout.addStretch()
 | 
			
		||||
        layout.addWidget(self.edit)
 | 
			
		||||
        layout.addWidget(self.delete)
 | 
			
		||||
 | 
			
		||||
    def get_label(self):
 | 
			
		||||
        language = '<a style="color: #FFFF00"><i>({0})</i></a>'.format(
 | 
			
		||||
            self.command['language'])
 | 
			
		||||
        touchs = [self.command['button'] + 'Click']
 | 
			
		||||
        touchs.extend([m for m in ('ctrl', 'shift') if self.command[m]])
 | 
			
		||||
        return '{} {}'.format('+'.join(touchs), language)
 | 
			
		||||
 | 
			
		||||
    def update_label(self):
 | 
			
		||||
        self.label.setText(self.get_label())
 | 
			
		||||
							
								
								
									
										82
									
								
								Scripts/Animation/dwpicker/dwpicker/compatibility.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,82 @@
 | 
			
		||||
"""
 | 
			
		||||
This module contain a function to ingest picker done with older version.
 | 
			
		||||
If the structure changed, it can convert automatically the data to the new
 | 
			
		||||
version.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from dwpicker.appinfos import VERSION
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def ensure_retro_compatibility(picker_data):
 | 
			
		||||
    """
 | 
			
		||||
    This function ensure retro compatibility.
 | 
			
		||||
    """
 | 
			
		||||
    # If a new release involve a data structure change in the picker, implement
 | 
			
		||||
    # the way to update the data here using this pattern:
 | 
			
		||||
    #
 | 
			
		||||
    # if version < (youre version number):
 | 
			
		||||
    #     picker_data = your code update
 | 
			
		||||
    version = picker_data['general'].get('version') or (0, 0, 0)
 | 
			
		||||
    picker_data['general']['version'] = VERSION
 | 
			
		||||
 | 
			
		||||
    if tuple(version) < (0, 3, 0):
 | 
			
		||||
        # Add new options added to version 0, 3, 0.
 | 
			
		||||
        picker_data['general']['zoom_locked'] = False
 | 
			
		||||
 | 
			
		||||
    if tuple(version) < (0, 4, 0):
 | 
			
		||||
        picker_data['general'].pop('centerx')
 | 
			
		||||
        picker_data['general'].pop('centery')
 | 
			
		||||
 | 
			
		||||
    if tuple(version) < (0, 10, 0):
 | 
			
		||||
        for shape in picker_data['shapes']:
 | 
			
		||||
            shape['visibility_layer'] = None
 | 
			
		||||
 | 
			
		||||
    if tuple(version) < (0, 11, 0):
 | 
			
		||||
        for shape in picker_data['shapes']:
 | 
			
		||||
            update_shape_actions_for_v0_11_0(shape)
 | 
			
		||||
 | 
			
		||||
    return picker_data
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def update_shape_actions_for_v0_11_0(shape):
 | 
			
		||||
    """
 | 
			
		||||
    With release 0.11.0 comes a new configurable action system.
 | 
			
		||||
    """
 | 
			
		||||
    if 'action.namespace' in shape:
 | 
			
		||||
        del shape['action.namespace']
 | 
			
		||||
    if 'action.type' in shape:
 | 
			
		||||
        del shape['action.type']
 | 
			
		||||
 | 
			
		||||
    shape['action.commands'] = []
 | 
			
		||||
 | 
			
		||||
    if shape['action.left.command']:
 | 
			
		||||
        shape['action.commands'].append({
 | 
			
		||||
            'enabled': shape['action.left'],
 | 
			
		||||
            'button': 'left',
 | 
			
		||||
            'language': shape['action.left.language'],
 | 
			
		||||
            'command': shape['action.left.command'],
 | 
			
		||||
            'alt': False,
 | 
			
		||||
            'ctrl': False,
 | 
			
		||||
            'shift': False,
 | 
			
		||||
            'deferred': False,
 | 
			
		||||
            'force_compact_undo': False})
 | 
			
		||||
 | 
			
		||||
    if shape['action.right.command']:
 | 
			
		||||
        shape['action.commands'].append({
 | 
			
		||||
            'enabled': shape['action.right'],
 | 
			
		||||
            'button': 'left',
 | 
			
		||||
            'language': shape['action.right.language'],
 | 
			
		||||
            'command': shape['action.right.command'],
 | 
			
		||||
            'alt': False,
 | 
			
		||||
            'ctrl': False,
 | 
			
		||||
            'shift': False,
 | 
			
		||||
            'deferred': False,
 | 
			
		||||
            'force_compact_undo': False})
 | 
			
		||||
 | 
			
		||||
    keys_to_clear = (
 | 
			
		||||
        'action.left', 'action.left.language',
 | 
			
		||||
        'action.left.command', 'action.right', 'action.right.language',
 | 
			
		||||
        'action.right.command')
 | 
			
		||||
 | 
			
		||||
    for key in keys_to_clear:
 | 
			
		||||
        del shape[key]
 | 
			
		||||
							
								
								
									
										577
									
								
								Scripts/Animation/dwpicker/dwpicker/designer/attributes.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,577 @@
 | 
			
		||||
import maya.cmds as cmds
 | 
			
		||||
from functools import partial
 | 
			
		||||
from PySide2 import QtCore, QtWidgets
 | 
			
		||||
 | 
			
		||||
from dwpicker.qtutils import VALIGNS, HALIGNS
 | 
			
		||||
from dwpicker.commands import CommandsEditor
 | 
			
		||||
from dwpicker.designer.layer import VisibilityLayersEditor
 | 
			
		||||
from dwpicker.widgets import (
 | 
			
		||||
    BoolCombo, BrowseEdit, ColorEdit, IntEdit, FloatEdit, LayerEdit,
 | 
			
		||||
    TextEdit, Title, WidgetToggler)
 | 
			
		||||
 | 
			
		||||
LEFT_CELL_WIDTH = 80
 | 
			
		||||
SHAPE_TYPES = 'square', 'round', 'rounded_rect'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AttributeEditor(QtWidgets.QWidget):
 | 
			
		||||
    generalOptionSet = QtCore.Signal(str, object)
 | 
			
		||||
    imageModified = QtCore.Signal()
 | 
			
		||||
    optionSet = QtCore.Signal(str, object)
 | 
			
		||||
    rectModified = QtCore.Signal(str, float)
 | 
			
		||||
    removeLayer = QtCore.Signal(str)
 | 
			
		||||
    selectLayerContent = QtCore.Signal(str)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(AttributeEditor, self).__init__(parent)
 | 
			
		||||
        self.widget = QtWidgets.QWidget()
 | 
			
		||||
 | 
			
		||||
        self.generals = GeneralSettings()
 | 
			
		||||
        self.generals.optionModified.connect(self.generalOptionSet.emit)
 | 
			
		||||
        self.generals.layers.removeLayer.connect(self.removeLayer.emit)
 | 
			
		||||
        mtd = self.selectLayerContent.emit
 | 
			
		||||
        self.generals.layers.selectLayerContent.connect(mtd)
 | 
			
		||||
        self.generals_toggler = WidgetToggler('Picker options', self.generals)
 | 
			
		||||
 | 
			
		||||
        self.shape = ShapeSettings()
 | 
			
		||||
        self.shape.optionSet.connect(self.optionSet.emit)
 | 
			
		||||
        self.shape.rectModified.connect(self.rectModified.emit)
 | 
			
		||||
        self.shape_toggler = WidgetToggler('Shape', self.shape)
 | 
			
		||||
 | 
			
		||||
        self.image = ImageSettings()
 | 
			
		||||
        self.image.optionSet.connect(self.image_modified)
 | 
			
		||||
        self.image_toggler = WidgetToggler('Image', self.image)
 | 
			
		||||
 | 
			
		||||
        self.appearence = AppearenceSettings()
 | 
			
		||||
        self.appearence.optionSet.connect(self.optionSet.emit)
 | 
			
		||||
        self.appearence_toggler = WidgetToggler('Appearence', self.appearence)
 | 
			
		||||
 | 
			
		||||
        self.text = TextSettings()
 | 
			
		||||
        self.text.optionSet.connect(self.optionSet.emit)
 | 
			
		||||
        self.text_toggler = WidgetToggler('Text', self.text)
 | 
			
		||||
 | 
			
		||||
        self.action = ActionSettings()
 | 
			
		||||
        self.action.optionSet.connect(self.optionSet.emit)
 | 
			
		||||
        self.action_toggler = WidgetToggler('Action', self.action)
 | 
			
		||||
 | 
			
		||||
        self.layout = QtWidgets.QVBoxLayout(self.widget)
 | 
			
		||||
        self.layout.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        self.layout.setSpacing(0)
 | 
			
		||||
        self.layout.addWidget(self.generals_toggler)
 | 
			
		||||
        self.layout.addWidget(self.generals)
 | 
			
		||||
        self.layout.addWidget(self.shape_toggler)
 | 
			
		||||
        self.layout.addWidget(self.shape)
 | 
			
		||||
        self.layout.addWidget(self.image_toggler)
 | 
			
		||||
        self.layout.addWidget(self.image)
 | 
			
		||||
        self.layout.addWidget(self.appearence_toggler)
 | 
			
		||||
        self.layout.addWidget(self.appearence)
 | 
			
		||||
        self.layout.addWidget(self.text_toggler)
 | 
			
		||||
        self.layout.addWidget(self.text)
 | 
			
		||||
        self.layout.addWidget(self.action_toggler)
 | 
			
		||||
        self.layout.addWidget(self.action)
 | 
			
		||||
        self.layout.addStretch(1)
 | 
			
		||||
 | 
			
		||||
        self.scroll_area = QtWidgets.QScrollArea()
 | 
			
		||||
        self.scroll_area.setWidget(self.widget)
 | 
			
		||||
 | 
			
		||||
        self.main_layout = QtWidgets.QVBoxLayout(self)
 | 
			
		||||
        self.main_layout.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        self.main_layout.addWidget(self.scroll_area)
 | 
			
		||||
 | 
			
		||||
        self.setFixedWidth(self.sizeHint().width() * 1.075)
 | 
			
		||||
 | 
			
		||||
    def set_generals(self, options):
 | 
			
		||||
        self.blockSignals(True)
 | 
			
		||||
        self.generals.set_options(options)
 | 
			
		||||
        self.blockSignals(False)
 | 
			
		||||
 | 
			
		||||
    def set_options(self, options):
 | 
			
		||||
        self.blockSignals(True)
 | 
			
		||||
        self.shape.set_options(options)
 | 
			
		||||
        self.image.set_options(options)
 | 
			
		||||
        self.appearence.set_options(options)
 | 
			
		||||
        self.text.set_options(options)
 | 
			
		||||
        self.action.set_options(options)
 | 
			
		||||
        self.blockSignals(False)
 | 
			
		||||
 | 
			
		||||
    def image_modified(self, option, value):
 | 
			
		||||
        self.optionSet.emit(option, value)
 | 
			
		||||
        self.imageModified.emit()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GeneralSettings(QtWidgets.QWidget):
 | 
			
		||||
    optionModified = QtCore.Signal(str, object)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(GeneralSettings, self).__init__(parent)
 | 
			
		||||
        self.name = TextEdit()
 | 
			
		||||
        self.name.valueSet.connect(self.name_changed)
 | 
			
		||||
        self.zoom_locked = BoolCombo(False)
 | 
			
		||||
        self.zoom_locked.valueSet.connect(self.zoom_changed)
 | 
			
		||||
        self.layers = VisibilityLayersEditor()
 | 
			
		||||
 | 
			
		||||
        form_layout = QtWidgets.QFormLayout()
 | 
			
		||||
        form_layout.setSpacing(0)
 | 
			
		||||
        form_layout.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        form_layout.setHorizontalSpacing(5)
 | 
			
		||||
        form_layout.addRow('Name', self.name)
 | 
			
		||||
        form_layout.addRow('Zoom-locked', self.zoom_locked)
 | 
			
		||||
 | 
			
		||||
        layout = QtWidgets.QVBoxLayout(self)
 | 
			
		||||
        layout.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        layout.addLayout(form_layout)
 | 
			
		||||
        layout.addWidget(Title('Visibility Layers'))
 | 
			
		||||
        layout.addWidget(self.layers)
 | 
			
		||||
 | 
			
		||||
    def set_shapes(self, shapes):
 | 
			
		||||
        self.layers.set_shapes(shapes)
 | 
			
		||||
 | 
			
		||||
    def set_options(self, options):
 | 
			
		||||
        self.name.setText(options['name'])
 | 
			
		||||
        self.zoom_locked.setCurrentText(str(options['zoom_locked']))
 | 
			
		||||
 | 
			
		||||
    def name_changed(self, value):
 | 
			
		||||
        self.optionModified.emit('name', value)
 | 
			
		||||
 | 
			
		||||
    def zoom_changed(self, state):
 | 
			
		||||
        self.optionModified.emit('zoom_locked', state)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ShapeSettings(QtWidgets.QWidget):
 | 
			
		||||
    optionSet = QtCore.Signal(str, object)
 | 
			
		||||
    rectModified = QtCore.Signal(str, float)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(ShapeSettings, self).__init__(parent)
 | 
			
		||||
        self.shape = QtWidgets.QComboBox()
 | 
			
		||||
        self.shape.addItems(SHAPE_TYPES)
 | 
			
		||||
        self.shape.currentIndexChanged.connect(self.shape_changed)
 | 
			
		||||
 | 
			
		||||
        self.layer = LayerEdit()
 | 
			
		||||
        method = partial(self.optionSet.emit, 'visibility_layer')
 | 
			
		||||
        self.layer.valueSet.connect(method)
 | 
			
		||||
 | 
			
		||||
        self.left = IntEdit(minimum=0)
 | 
			
		||||
        method = partial(self.rectModified.emit, 'shape.left')
 | 
			
		||||
        self.left.valueSet.connect(method)
 | 
			
		||||
        self.top = IntEdit(minimum=0)
 | 
			
		||||
        method = partial(self.rectModified.emit, 'shape.right')
 | 
			
		||||
        self.top.valueSet.connect(method)
 | 
			
		||||
        self.width = IntEdit(minimum=0)
 | 
			
		||||
        method = partial(self.rectModified.emit, 'shape.width')
 | 
			
		||||
        self.width.valueSet.connect(method)
 | 
			
		||||
        self.height = IntEdit(minimum=0)
 | 
			
		||||
        method = partial(self.rectModified.emit, 'shape.height')
 | 
			
		||||
        self.height.valueSet.connect(method)
 | 
			
		||||
        self.cornersx = IntEdit(minimum=0)
 | 
			
		||||
        method = partial(self.optionSet.emit, 'shape.cornersx')
 | 
			
		||||
        self.cornersx.valueSet.connect(method)
 | 
			
		||||
        self.cornersy = IntEdit(minimum=0)
 | 
			
		||||
        method = partial(self.optionSet.emit, 'shape.cornersy')
 | 
			
		||||
        self.cornersy.valueSet.connect(method)
 | 
			
		||||
 | 
			
		||||
        self.layout = QtWidgets.QFormLayout(self)
 | 
			
		||||
        self.layout.setSpacing(0)
 | 
			
		||||
        self.layout.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        self.layout.setHorizontalSpacing(5)
 | 
			
		||||
        self.layout.addRow('Shape', self.shape)
 | 
			
		||||
        self.layout.addRow('Visibility layer', self.layer)
 | 
			
		||||
        self.layout.addItem(QtWidgets.QSpacerItem(0, 8))
 | 
			
		||||
        self.layout.addRow(Title('Dimensions'))
 | 
			
		||||
        self.layout.addRow('left', self.left)
 | 
			
		||||
        self.layout.addRow('top', self.top)
 | 
			
		||||
        self.layout.addRow('width', self.width)
 | 
			
		||||
        self.layout.addRow('height', self.height)
 | 
			
		||||
        self.layout.addRow('roundness x', self.cornersx)
 | 
			
		||||
        self.layout.addRow('roundness y', self.cornersy)
 | 
			
		||||
        for label in self.findChildren(QtWidgets.QLabel):
 | 
			
		||||
            if not isinstance(label, Title):
 | 
			
		||||
                label.setFixedWidth(LEFT_CELL_WIDTH)
 | 
			
		||||
 | 
			
		||||
    def shape_changed(self, _):
 | 
			
		||||
        self.optionSet.emit('shape', self.shape.currentText())
 | 
			
		||||
 | 
			
		||||
    def set_options(self, options):
 | 
			
		||||
        values = list({option['visibility_layer'] for option in options})
 | 
			
		||||
        value = values[0] if len(values) == 1 else '' if not values else '...'
 | 
			
		||||
        self.layer.set_layer(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['shape'] for option in options})
 | 
			
		||||
        value = values[0] if len(values) == 1 else '...'
 | 
			
		||||
        self.shape.setCurrentText(value)
 | 
			
		||||
 | 
			
		||||
        values = list({int(round((option['shape.left']))) for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.left.setText(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['shape.top'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.top.setText(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['shape.width'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.width.setText(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['shape.height'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.height.setText(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['shape.cornersx'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.cornersx.setText(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['shape.cornersy'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.cornersy.setText(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ImageSettings(QtWidgets.QWidget):
 | 
			
		||||
    optionSet = QtCore.Signal(str, object)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(ImageSettings, self).__init__(parent)
 | 
			
		||||
        self.path = BrowseEdit()
 | 
			
		||||
        self.path.valueSet.connect(partial(self.optionSet.emit, 'image.path'))
 | 
			
		||||
 | 
			
		||||
        self.fit = BoolCombo(True)
 | 
			
		||||
        self.fit.valueSet.connect(partial(self.optionSet.emit, 'image.fit'))
 | 
			
		||||
 | 
			
		||||
        self.width = FloatEdit()
 | 
			
		||||
        method = partial(self.optionSet.emit, 'image.width')
 | 
			
		||||
        self.width.valueSet.connect(method)
 | 
			
		||||
 | 
			
		||||
        self.height = FloatEdit()
 | 
			
		||||
        method = partial(self.optionSet.emit, 'image.height')
 | 
			
		||||
        self.height.valueSet.connect(method)
 | 
			
		||||
 | 
			
		||||
        self.layout = QtWidgets.QFormLayout(self)
 | 
			
		||||
        self.layout.setSpacing(0)
 | 
			
		||||
        self.layout.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        self.layout.setHorizontalSpacing(5)
 | 
			
		||||
        self.layout.addRow('Path', self.path)
 | 
			
		||||
        self.layout.addRow('Fit to shape', self.fit)
 | 
			
		||||
        self.layout.addRow('Width', self.width)
 | 
			
		||||
        self.layout.addRow('Height', self.height)
 | 
			
		||||
        for label in self.findChildren(QtWidgets.QLabel):
 | 
			
		||||
            if not isinstance(label, Title):
 | 
			
		||||
                label.setFixedWidth(LEFT_CELL_WIDTH)
 | 
			
		||||
 | 
			
		||||
    def set_options(self, options):
 | 
			
		||||
        values = list({option['image.path'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.path.set_value(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['image.fit'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.fit.setCurrentText(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['image.width'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.width.setText(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['image.height'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.height.setText(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AppearenceSettings(QtWidgets.QWidget):
 | 
			
		||||
    optionSet = QtCore.Signal(str, object)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(AppearenceSettings, self).__init__(parent)
 | 
			
		||||
 | 
			
		||||
        self.border = BoolCombo(True)
 | 
			
		||||
        method = partial(self.optionSet.emit, 'border')
 | 
			
		||||
        self.border.valueSet.connect(method)
 | 
			
		||||
 | 
			
		||||
        self.borderwidth_normal = FloatEdit(minimum=0.0)
 | 
			
		||||
        method = partial(self.optionSet.emit, 'borderwidth.normal')
 | 
			
		||||
        self.borderwidth_normal.valueSet.connect(method)
 | 
			
		||||
 | 
			
		||||
        self.borderwidth_hovered = FloatEdit(minimum=0.0)
 | 
			
		||||
        method = partial(self.optionSet.emit, 'borderwidth.hovered')
 | 
			
		||||
        self.borderwidth_hovered.valueSet.connect(method)
 | 
			
		||||
 | 
			
		||||
        self.borderwidth_clicked = FloatEdit(minimum=0.0)
 | 
			
		||||
        method = partial(self.optionSet.emit, 'borderwidth.clicked')
 | 
			
		||||
        self.borderwidth_clicked.valueSet.connect(method)
 | 
			
		||||
 | 
			
		||||
        self.bordercolor_normal = ColorEdit()
 | 
			
		||||
        method = partial(self.optionSet.emit, 'bordercolor.normal')
 | 
			
		||||
        self.bordercolor_normal.valueSet.connect(method)
 | 
			
		||||
 | 
			
		||||
        self.bordercolor_hovered = ColorEdit()
 | 
			
		||||
        method = partial(self.optionSet.emit, 'bordercolor.hovered')
 | 
			
		||||
        self.bordercolor_hovered.valueSet.connect(method)
 | 
			
		||||
 | 
			
		||||
        self.bordercolor_clicked = ColorEdit()
 | 
			
		||||
        method = partial(self.optionSet.emit, 'bordercolor.clicked')
 | 
			
		||||
        self.bordercolor_clicked.valueSet.connect(method)
 | 
			
		||||
 | 
			
		||||
        self.bordercolor_transparency = FloatEdit(minimum=0, maximum=255)
 | 
			
		||||
        method = partial(self.optionSet.emit, 'bordercolor.transparency')
 | 
			
		||||
        self.bordercolor_transparency.valueSet.connect(method)
 | 
			
		||||
 | 
			
		||||
        self.backgroundcolor_normal = ColorEdit()
 | 
			
		||||
        method = partial(self.optionSet.emit, 'bgcolor.normal')
 | 
			
		||||
        self.backgroundcolor_normal.valueSet.connect(method)
 | 
			
		||||
 | 
			
		||||
        self.backgroundcolor_hovered = ColorEdit()
 | 
			
		||||
        method = partial(self.optionSet.emit, 'bgcolor.hovered')
 | 
			
		||||
        self.backgroundcolor_hovered.valueSet.connect(method)
 | 
			
		||||
 | 
			
		||||
        self.backgroundcolor_clicked = ColorEdit()
 | 
			
		||||
        method = partial(self.optionSet.emit, 'bgcolor.clicked')
 | 
			
		||||
        self.backgroundcolor_clicked.valueSet.connect(method)
 | 
			
		||||
 | 
			
		||||
        self.backgroundcolor_transparency = FloatEdit(minimum=0, maximum=255)
 | 
			
		||||
        method = partial(self.optionSet.emit, 'bgcolor.transparency')
 | 
			
		||||
        self.backgroundcolor_transparency.valueSet.connect(method)
 | 
			
		||||
 | 
			
		||||
        self.layout = QtWidgets.QFormLayout(self)
 | 
			
		||||
        self.layout.setSpacing(0)
 | 
			
		||||
        self.layout.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        self.layout.setHorizontalSpacing(5)
 | 
			
		||||
        self.layout.addRow('border visible', self.border)
 | 
			
		||||
        self.layout.addRow(Title('Border width (pxf)'))
 | 
			
		||||
        self.layout.addRow('normal', self.borderwidth_normal)
 | 
			
		||||
        self.layout.addRow('hovered', self.borderwidth_hovered)
 | 
			
		||||
        self.layout.addRow('clicked', self.borderwidth_clicked)
 | 
			
		||||
        self.layout.addItem(QtWidgets.QSpacerItem(0, 8))
 | 
			
		||||
        self.layout.addRow(Title('Border color'))
 | 
			
		||||
        self.layout.addRow('normal', self.bordercolor_normal)
 | 
			
		||||
        self.layout.addRow('hovered', self.bordercolor_hovered)
 | 
			
		||||
        self.layout.addRow('clicked', self.bordercolor_clicked)
 | 
			
		||||
        self.layout.addRow('transparency', self.bordercolor_transparency)
 | 
			
		||||
        self.layout.addItem(QtWidgets.QSpacerItem(0, 8))
 | 
			
		||||
        self.layout.addRow(Title('Background color'))
 | 
			
		||||
        self.layout.addRow('normal', self.backgroundcolor_normal)
 | 
			
		||||
        self.layout.addRow('hovered', self.backgroundcolor_hovered)
 | 
			
		||||
        self.layout.addRow('clicked', self.backgroundcolor_clicked)
 | 
			
		||||
        self.layout.addRow('transparency', self.backgroundcolor_transparency)
 | 
			
		||||
        for label in self.findChildren(QtWidgets.QLabel):
 | 
			
		||||
            if not isinstance(label, Title):
 | 
			
		||||
                label.setFixedWidth(LEFT_CELL_WIDTH)
 | 
			
		||||
 | 
			
		||||
    def set_options(self, options):
 | 
			
		||||
        values = list({option['border'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.border.setCurrentText(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['borderwidth.normal'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.borderwidth_normal.setText(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['borderwidth.hovered'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.borderwidth_hovered.setText(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['borderwidth.clicked'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.borderwidth_clicked.setText(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['bordercolor.normal'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.bordercolor_normal.set_color(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['bordercolor.hovered'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.bordercolor_hovered.set_color(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['bordercolor.clicked'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.bordercolor_clicked.set_color(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['bordercolor.transparency'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.bordercolor_transparency.setText(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['bgcolor.normal'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.backgroundcolor_normal.set_color(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['bgcolor.hovered'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.backgroundcolor_hovered.set_color(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['bgcolor.clicked'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.backgroundcolor_clicked.set_color(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['bgcolor.transparency'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.backgroundcolor_transparency.setText(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ActionSettings(QtWidgets.QWidget):
 | 
			
		||||
    optionSet = QtCore.Signal(str, object)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(ActionSettings, self).__init__(parent)
 | 
			
		||||
        self._targets = QtWidgets.QLineEdit()
 | 
			
		||||
        self._targets.returnPressed.connect(self.targets_changed)
 | 
			
		||||
 | 
			
		||||
        self._add_targets = QtWidgets.QPushButton('Add')
 | 
			
		||||
        self._remove_targets = QtWidgets.QPushButton('Remove')
 | 
			
		||||
        self._replace_targets = QtWidgets.QPushButton('Replace')
 | 
			
		||||
        self._targets_layout = QtWidgets.QHBoxLayout()
 | 
			
		||||
        self._targets_layout.addWidget(self._add_targets)
 | 
			
		||||
        self._targets_layout.addWidget(self._remove_targets)
 | 
			
		||||
        self._targets_layout.addWidget(self._replace_targets)
 | 
			
		||||
 | 
			
		||||
        self._add_targets.clicked.connect(self.call_add_targets)
 | 
			
		||||
        self._remove_targets.clicked.connect(self.call_remove_targets)
 | 
			
		||||
        self._replace_targets.clicked.connect(self.call_replace_targets)
 | 
			
		||||
 | 
			
		||||
        self._commands = CommandsEditor()
 | 
			
		||||
        method = partial(self.optionSet.emit, 'action.commands')
 | 
			
		||||
        self._commands.valueSet.connect(method)
 | 
			
		||||
 | 
			
		||||
        form = QtWidgets.QFormLayout()
 | 
			
		||||
        form.setSpacing(0)
 | 
			
		||||
        form.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        form.setHorizontalSpacing(5)
 | 
			
		||||
        form.addRow(Title('Selection'))
 | 
			
		||||
        form.addRow('Targets', self._targets)
 | 
			
		||||
        form.addRow('Add Selected', self._targets_layout)
 | 
			
		||||
        self.layout = QtWidgets.QVBoxLayout(self)
 | 
			
		||||
        self.layout.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        self.layout.setSpacing(0)
 | 
			
		||||
        self.layout.addLayout(form)
 | 
			
		||||
        self.layout.addWidget(Title('Scripts'))
 | 
			
		||||
        self.layout.addWidget(self._commands)
 | 
			
		||||
        for label in self.findChildren(QtWidgets.QLabel):
 | 
			
		||||
            if not isinstance(label, Title):
 | 
			
		||||
                label.setFixedWidth(LEFT_CELL_WIDTH)
 | 
			
		||||
 | 
			
		||||
    def targets(self):
 | 
			
		||||
        targets = str(self._targets.text())
 | 
			
		||||
        try:
 | 
			
		||||
            return [t.strip(" ") for t in targets.split(',')]
 | 
			
		||||
        except ValueError:
 | 
			
		||||
            return []
 | 
			
		||||
 | 
			
		||||
    def call_add_targets(self):
 | 
			
		||||
        selection = cmds.ls(selection=True, flatten=True)
 | 
			
		||||
        if not selection:
 | 
			
		||||
            return
 | 
			
		||||
        targets = self.targets()
 | 
			
		||||
        edits = [item for item in selection if item not in targets]
 | 
			
		||||
        targets = targets if targets != [''] else []
 | 
			
		||||
        self._targets.setText(', '.join(targets + edits))
 | 
			
		||||
        self._targets.setFocus()
 | 
			
		||||
        self.targets_changed()
 | 
			
		||||
 | 
			
		||||
    def call_remove_targets(self):
 | 
			
		||||
        selection = cmds.ls(selection=True, flatten=True)
 | 
			
		||||
        if not selection:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        targets = [item for item in self.targets() if item not in selection]
 | 
			
		||||
        self._targets.setText(', '.join(targets))
 | 
			
		||||
        self._targets.setFocus()
 | 
			
		||||
        self.targets_changed()
 | 
			
		||||
 | 
			
		||||
    def call_replace_targets(self):
 | 
			
		||||
        selection = cmds.ls(selection=True, flatten=True)
 | 
			
		||||
        if not selection:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        self._targets.setText(', '.join(selection))
 | 
			
		||||
        self._targets.setFocus()
 | 
			
		||||
        self.targets_changed()
 | 
			
		||||
 | 
			
		||||
    def targets_changed(self):
 | 
			
		||||
        if not self._targets.text():
 | 
			
		||||
            self.optionSet.emit('action.targets', [])
 | 
			
		||||
            return
 | 
			
		||||
        values = [t.strip(" ") for t in self._targets.text().split(",")]
 | 
			
		||||
        self.optionSet.emit('action.targets', values)
 | 
			
		||||
 | 
			
		||||
    def set_options(self, options):
 | 
			
		||||
        values = list({o for opt in options for o in opt['action.targets']})
 | 
			
		||||
        self._targets.setText(", ".join(sorted(values)))
 | 
			
		||||
        self._commands.set_options(options)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TextSettings(QtWidgets.QWidget):
 | 
			
		||||
    optionSet = QtCore.Signal(str, object)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(TextSettings, self).__init__(parent)
 | 
			
		||||
        self.text = TextEdit()
 | 
			
		||||
        method = partial(self.optionSet.emit, 'text.content')
 | 
			
		||||
        self.text.valueSet.connect(method)
 | 
			
		||||
 | 
			
		||||
        self.size = FloatEdit(minimum=0.0)
 | 
			
		||||
        self.size.valueSet.connect(partial(self.optionSet.emit, 'text.size'))
 | 
			
		||||
 | 
			
		||||
        self.bold = BoolCombo()
 | 
			
		||||
        self.bold.valueSet.connect(partial(self.optionSet.emit, 'text.bold'))
 | 
			
		||||
 | 
			
		||||
        self.italic = BoolCombo()
 | 
			
		||||
        method = partial(self.optionSet.emit, 'text.italic')
 | 
			
		||||
        self.italic.valueSet.connect(method)
 | 
			
		||||
 | 
			
		||||
        self.color = ColorEdit()
 | 
			
		||||
        self.color.valueSet.connect(partial(self.optionSet.emit, 'text.color'))
 | 
			
		||||
 | 
			
		||||
        self.halignement = QtWidgets.QComboBox()
 | 
			
		||||
        self.halignement.addItems(HALIGNS.keys())
 | 
			
		||||
        self.halignement.currentIndexChanged.connect(self.halign_changed)
 | 
			
		||||
        self.valignement = QtWidgets.QComboBox()
 | 
			
		||||
        self.valignement.addItems(VALIGNS.keys())
 | 
			
		||||
        self.valignement.currentIndexChanged.connect(self.valign_changed)
 | 
			
		||||
 | 
			
		||||
        self.layout = QtWidgets.QFormLayout(self)
 | 
			
		||||
        self.layout.setSpacing(0)
 | 
			
		||||
        self.layout.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        self.layout.setHorizontalSpacing(5)
 | 
			
		||||
        self.layout.addRow('Content', self.text)
 | 
			
		||||
        self.layout.addItem(QtWidgets.QSpacerItem(0, 8))
 | 
			
		||||
        self.layout.addRow(Title('Options'))
 | 
			
		||||
        self.layout.addRow('Size', self.size)
 | 
			
		||||
        self.layout.addRow('Bold', self.bold)
 | 
			
		||||
        self.layout.addRow('Italic', self.italic)
 | 
			
		||||
        self.layout.addRow('Color', self.color)
 | 
			
		||||
        self.layout.addItem(QtWidgets.QSpacerItem(0, 8))
 | 
			
		||||
        self.layout.addRow(Title('Alignement'))
 | 
			
		||||
        self.layout.addRow('Horizontal', self.halignement)
 | 
			
		||||
        self.layout.addRow('Vertical', self.valignement)
 | 
			
		||||
        for label in self.findChildren(QtWidgets.QLabel):
 | 
			
		||||
            if not isinstance(label, Title):
 | 
			
		||||
                label.setFixedWidth(LEFT_CELL_WIDTH)
 | 
			
		||||
 | 
			
		||||
    def valign_changed(self):
 | 
			
		||||
        self.optionSet.emit('text.valign', self.valignement.currentText())
 | 
			
		||||
 | 
			
		||||
    def halign_changed(self):
 | 
			
		||||
        self.optionSet.emit('text.halign', self.halignement.currentText())
 | 
			
		||||
 | 
			
		||||
    def set_options(self, options):
 | 
			
		||||
        values = list({option['text.content'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.text.setText(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['text.size'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.size.setText(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['text.bold'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.bold.setCurrentText(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['text.italic'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.italic.setCurrentText(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['text.color'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.color.set_color(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['text.halign'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.halignement.setCurrentText(value)
 | 
			
		||||
 | 
			
		||||
        values = list({option['text.valign'] for option in options})
 | 
			
		||||
        value = str(values[0]) if len(values) == 1 else None
 | 
			
		||||
        self.valignement.setCurrentText(value)
 | 
			
		||||
							
								
								
									
										191
									
								
								Scripts/Animation/dwpicker/dwpicker/designer/editarea.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,191 @@
 | 
			
		||||
from PySide2 import QtCore, QtGui, QtWidgets
 | 
			
		||||
 | 
			
		||||
from dwpicker.interactive import Manipulator, SelectionSquare
 | 
			
		||||
from dwpicker.geometry import Transform, get_combined_rects
 | 
			
		||||
from dwpicker.painting import draw_editor, draw_shape
 | 
			
		||||
from dwpicker.qtutils import get_cursor
 | 
			
		||||
from dwpicker.selection import Selection, get_selection_mode
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ShapeEditArea(QtWidgets.QWidget):
 | 
			
		||||
    selectedShapesChanged = QtCore.Signal()
 | 
			
		||||
    increaseUndoStackRequested = QtCore.Signal()
 | 
			
		||||
    centerMoved = QtCore.Signal(int, int)
 | 
			
		||||
    callContextMenu = QtCore.Signal(QtCore.QPoint)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, options, parent=None):
 | 
			
		||||
        super(ShapeEditArea, self).__init__(parent)
 | 
			
		||||
        self.setFixedSize(750, 550)
 | 
			
		||||
        self.setMouseTracking(True)
 | 
			
		||||
        self.options = options
 | 
			
		||||
 | 
			
		||||
        self.selection = Selection()
 | 
			
		||||
        self.selection_square = SelectionSquare()
 | 
			
		||||
        self.manipulator = Manipulator()
 | 
			
		||||
        self.transform = Transform()
 | 
			
		||||
 | 
			
		||||
        self.shapes = []
 | 
			
		||||
        self.clicked_shape = None
 | 
			
		||||
        self.clicked = False
 | 
			
		||||
        self.selecting = False
 | 
			
		||||
        self.handeling = False
 | 
			
		||||
        self.manipulator_moved = False
 | 
			
		||||
        self.increase_undo_on_release = False
 | 
			
		||||
        self.lock_background_shape = True
 | 
			
		||||
 | 
			
		||||
        self.ctrl_pressed = False
 | 
			
		||||
        self.shit_pressed = False
 | 
			
		||||
 | 
			
		||||
    def set_lock_background_shape(self, state):
 | 
			
		||||
        self.lock_background_shape = state
 | 
			
		||||
 | 
			
		||||
    def get_hovered_shape(self, cursor):
 | 
			
		||||
        for shape in reversed(self.list_shapes()):
 | 
			
		||||
            if shape.rect.contains(cursor):
 | 
			
		||||
                return shape
 | 
			
		||||
 | 
			
		||||
    def list_shapes(self):
 | 
			
		||||
        if self.lock_background_shape:
 | 
			
		||||
            return [
 | 
			
		||||
                shape for shape in self.shapes
 | 
			
		||||
                if not shape.is_background()]
 | 
			
		||||
        return self.shapes
 | 
			
		||||
 | 
			
		||||
    def mousePressEvent(self, event):
 | 
			
		||||
        self.setFocus(QtCore.Qt.MouseFocusReason)  # This is not automatic
 | 
			
		||||
        if event.button() != QtCore.Qt.LeftButton:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        cursor = get_cursor(self)
 | 
			
		||||
        self.clicked = True
 | 
			
		||||
        hovered_shape = self.get_hovered_shape(cursor)
 | 
			
		||||
        self.transform.direction = self.manipulator.get_direction(cursor)
 | 
			
		||||
 | 
			
		||||
        conditions = (
 | 
			
		||||
            hovered_shape and
 | 
			
		||||
            hovered_shape not in self.selection and
 | 
			
		||||
            not self.transform.direction)
 | 
			
		||||
 | 
			
		||||
        if conditions:
 | 
			
		||||
            self.selection.set([hovered_shape])
 | 
			
		||||
            self.update_selection()
 | 
			
		||||
 | 
			
		||||
        elif not hovered_shape and not self.transform.direction:
 | 
			
		||||
            self.selection.set([])
 | 
			
		||||
            self.update_selection()
 | 
			
		||||
            self.selection_square.clicked(cursor)
 | 
			
		||||
 | 
			
		||||
        if self.manipulator.rect is not None:
 | 
			
		||||
            self.transform.set_rect(self.manipulator.rect)
 | 
			
		||||
            self.transform.reference_rect = QtCore.QRect(self.manipulator.rect)
 | 
			
		||||
            self.transform.set_reference_point(cursor)
 | 
			
		||||
 | 
			
		||||
        self.handeling = bool(hovered_shape) or self.transform.direction
 | 
			
		||||
        self.selecting = not self.handeling and self.clicked
 | 
			
		||||
        self.repaint()
 | 
			
		||||
 | 
			
		||||
    def mouseMoveEvent(self, _):
 | 
			
		||||
        cursor = get_cursor(self)
 | 
			
		||||
        if self.handeling:
 | 
			
		||||
            rect = self.manipulator.rect
 | 
			
		||||
            if self.transform.direction:
 | 
			
		||||
                self.transform.resize((s.rect for s in self.selection), cursor)
 | 
			
		||||
                self.manipulator.update_geometries()
 | 
			
		||||
            elif rect is not None:
 | 
			
		||||
                self.transform.move((s.rect for s in self.selection), cursor)
 | 
			
		||||
                self.manipulator.update_geometries()
 | 
			
		||||
            for shape in self.selection:
 | 
			
		||||
                shape.synchronize_rect()
 | 
			
		||||
                shape.synchronize_image()
 | 
			
		||||
 | 
			
		||||
            self.manipulator_moved = True
 | 
			
		||||
            self.increase_undo_on_release = True
 | 
			
		||||
            self.selectedShapesChanged.emit()
 | 
			
		||||
 | 
			
		||||
        elif self.selecting:
 | 
			
		||||
            self.selection_square.handle(cursor)
 | 
			
		||||
            for shape in self.list_shapes():
 | 
			
		||||
                shape.hovered = self.selection_square.intersects(shape.rect)
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            for shape in self.list_shapes():
 | 
			
		||||
                shape.hovered = shape.rect.contains(cursor)
 | 
			
		||||
 | 
			
		||||
        self.repaint()
 | 
			
		||||
 | 
			
		||||
    def mouseReleaseEvent(self, event):
 | 
			
		||||
        context_menu_condition = (
 | 
			
		||||
            event.button() == QtCore.Qt.RightButton and
 | 
			
		||||
            not self.clicked and
 | 
			
		||||
            not self.handeling and
 | 
			
		||||
            not self.selecting)
 | 
			
		||||
        if context_menu_condition:
 | 
			
		||||
            return self.callContextMenu.emit(event.pos())
 | 
			
		||||
 | 
			
		||||
        if event.button() != QtCore.Qt.LeftButton:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        if self.increase_undo_on_release:
 | 
			
		||||
            self.increaseUndoStackRequested.emit()
 | 
			
		||||
            self.increase_undo_on_release = False
 | 
			
		||||
 | 
			
		||||
        if self.selecting:
 | 
			
		||||
            self.select_shapes()
 | 
			
		||||
 | 
			
		||||
        self.selection_square.release()
 | 
			
		||||
        self.clicked = False
 | 
			
		||||
        self.handeling = False
 | 
			
		||||
        self.selecting = False
 | 
			
		||||
        self.repaint()
 | 
			
		||||
 | 
			
		||||
    def select_shapes(self):
 | 
			
		||||
        shapes = [
 | 
			
		||||
            s for s in self.list_shapes()
 | 
			
		||||
            if s.rect.intersects(self.selection_square.rect)]
 | 
			
		||||
        if shapes:
 | 
			
		||||
            self.selection.set(shapes)
 | 
			
		||||
            self.update_selection()
 | 
			
		||||
 | 
			
		||||
    def keyPressEvent(self, event):
 | 
			
		||||
        self.key_event(event, True)
 | 
			
		||||
 | 
			
		||||
    def keyReleaseEvent(self, event):
 | 
			
		||||
        self.key_event(event, False)
 | 
			
		||||
 | 
			
		||||
    def key_event(self, event, pressed):
 | 
			
		||||
        if event.key() == QtCore.Qt.Key_Shift:
 | 
			
		||||
            self.transform.square = pressed
 | 
			
		||||
            self.shit_pressed = pressed
 | 
			
		||||
 | 
			
		||||
        if event.key() == QtCore.Qt.Key_Control:
 | 
			
		||||
            self.ctrl_pressed = pressed
 | 
			
		||||
 | 
			
		||||
        self.selection.mode = get_selection_mode(
 | 
			
		||||
            shift=self.shit_pressed,
 | 
			
		||||
            ctrl=self.ctrl_pressed)
 | 
			
		||||
 | 
			
		||||
        self.repaint()
 | 
			
		||||
 | 
			
		||||
    def update_selection(self):
 | 
			
		||||
        rect = get_combined_rects([shape.rect for shape in self.selection])
 | 
			
		||||
        self.manipulator.set_rect(rect)
 | 
			
		||||
        self.selectedShapesChanged.emit()
 | 
			
		||||
 | 
			
		||||
    def paintEvent(self, _):
 | 
			
		||||
        try:
 | 
			
		||||
            painter = QtGui.QPainter()
 | 
			
		||||
            painter.begin(self)
 | 
			
		||||
            self.paint(painter)
 | 
			
		||||
        except BaseException:
 | 
			
		||||
            pass  # avoid crash
 | 
			
		||||
            # TODO: log the error
 | 
			
		||||
        finally:
 | 
			
		||||
            painter.end()
 | 
			
		||||
 | 
			
		||||
    def paint(self, painter):
 | 
			
		||||
        painter.setRenderHint(QtGui.QPainter.Antialiasing)
 | 
			
		||||
        draw_editor(painter, self.rect(), snap=self.transform.snap)
 | 
			
		||||
        for shape in self.shapes:
 | 
			
		||||
            draw_shape(painter, shape)
 | 
			
		||||
        self.manipulator.draw(painter, get_cursor(self))
 | 
			
		||||
        self.selection_square.draw(painter)
 | 
			
		||||
							
								
								
									
										557
									
								
								Scripts/Animation/dwpicker/dwpicker/designer/editor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,557 @@
 | 
			
		||||
from functools import partial
 | 
			
		||||
from math import ceil
 | 
			
		||||
 | 
			
		||||
from PySide2 import QtWidgets, QtCore
 | 
			
		||||
from maya import cmds
 | 
			
		||||
 | 
			
		||||
from dwpicker import clipboard
 | 
			
		||||
from dwpicker.align import align_shapes, arrange_horizontal, arrange_vertical
 | 
			
		||||
from dwpicker.arrayutils import (
 | 
			
		||||
    move_elements_to_array_end, move_elements_to_array_begin,
 | 
			
		||||
    move_up_array_elements, move_down_array_elements)
 | 
			
		||||
from dwpicker.dialog import SearchAndReplaceDialog, warning, SettingsPaster
 | 
			
		||||
from dwpicker.interactive import Shape, get_shape_rect_from_options
 | 
			
		||||
from dwpicker.geometry import get_combined_rects, rect_symmetry
 | 
			
		||||
from dwpicker.optionvar import BG_LOCKED, TRIGGER_REPLACE_ON_MIRROR
 | 
			
		||||
from dwpicker.picker import frame_shapes
 | 
			
		||||
from dwpicker.qtutils import set_shortcut, get_cursor
 | 
			
		||||
from dwpicker.templates import BUTTON, TEXT, BACKGROUND
 | 
			
		||||
 | 
			
		||||
from dwpicker.designer.editarea import ShapeEditArea
 | 
			
		||||
from dwpicker.designer.menu import MenuWidget
 | 
			
		||||
from dwpicker.designer.attributes import AttributeEditor
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DIRECTION_OFFSETS = {
 | 
			
		||||
    'Left': (-1, 0), 'Right': (1, 0), 'Up': (0, -1), 'Down': (0, 1)}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PickerEditor(QtWidgets.QWidget):
 | 
			
		||||
    pickerDataModified = QtCore.Signal(object)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, picker_data, undo_manager, parent=None):
 | 
			
		||||
        super(PickerEditor, self).__init__(parent, QtCore.Qt.Window)
 | 
			
		||||
        title = "Picker editor - " + picker_data['general']['name']
 | 
			
		||||
        self.setWindowTitle(title)
 | 
			
		||||
        self.options = picker_data['general']
 | 
			
		||||
        self.undo_manager = undo_manager
 | 
			
		||||
 | 
			
		||||
        self.shape_editor = ShapeEditArea(self.options)
 | 
			
		||||
        self.shape_editor.callContextMenu.connect(self.call_context_menu)
 | 
			
		||||
        bg_locked = bool(cmds.optionVar(query=BG_LOCKED))
 | 
			
		||||
        self.shape_editor.set_lock_background_shape(bg_locked)
 | 
			
		||||
        self.set_picker_data(picker_data)
 | 
			
		||||
        self.shape_editor.selectedShapesChanged.connect(self.selection_changed)
 | 
			
		||||
        method = self.set_data_modified
 | 
			
		||||
        self.shape_editor.increaseUndoStackRequested.connect(method)
 | 
			
		||||
        self.scrollarea = QtWidgets.QScrollArea()
 | 
			
		||||
        alignment = QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter
 | 
			
		||||
        self.scrollarea.setFocusPolicy(QtCore.Qt.NoFocus)
 | 
			
		||||
        policy = QtWidgets.QSizePolicy.Expanding
 | 
			
		||||
        self.scrollarea.setSizePolicy(policy, policy)
 | 
			
		||||
        # HACK: Stupid hack to force scroll area to fix layout size.
 | 
			
		||||
        self.scrollarea.sizeHint = lambda: QtCore.QSize(10000, 10000)
 | 
			
		||||
        self.scrollarea.setAlignment(alignment)
 | 
			
		||||
        self.scrollarea.setWidget(self.shape_editor)
 | 
			
		||||
 | 
			
		||||
        self.menu = MenuWidget()
 | 
			
		||||
        self.menu.copyRequested.connect(self.copy)
 | 
			
		||||
        self.menu.copySettingsRequested.connect(self.copy_settings)
 | 
			
		||||
        self.menu.deleteRequested.connect(self.delete_selection)
 | 
			
		||||
        self.menu.frameShapes.connect(self.frame_shapes)
 | 
			
		||||
        self.menu.pasteRequested.connect(self.paste)
 | 
			
		||||
        self.menu.pasteSettingsRequested.connect(self.paste_settings)
 | 
			
		||||
        self.menu.sizeChanged.connect(self.editor_size_changed)
 | 
			
		||||
        self.menu.snapValuesChanged.connect(self.snap_value_changed)
 | 
			
		||||
        self.menu.useSnapToggled.connect(self.use_snap)
 | 
			
		||||
 | 
			
		||||
        method = self.shape_editor.set_lock_background_shape
 | 
			
		||||
        self.menu.lockBackgroundShapeToggled.connect(method)
 | 
			
		||||
        width, height = self.options['width'], self.options['height']
 | 
			
		||||
        self.menu.set_size_values(width, height)
 | 
			
		||||
        self.menu.undoRequested.connect(self.undo)
 | 
			
		||||
        self.menu.redoRequested.connect(self.redo)
 | 
			
		||||
        method = partial(self.create_shape, BUTTON)
 | 
			
		||||
        self.menu.addButtonRequested.connect(method)
 | 
			
		||||
        method = partial(self.create_shape, TEXT)
 | 
			
		||||
        self.menu.addTextRequested.connect(method)
 | 
			
		||||
        method = partial(self.create_shape, BACKGROUND, before=True)
 | 
			
		||||
        self.menu.addBackgroundRequested.connect(method)
 | 
			
		||||
        method = self.set_selection_move_down
 | 
			
		||||
        self.menu.moveDownRequested.connect(method)
 | 
			
		||||
        method = self.set_selection_move_up
 | 
			
		||||
        self.menu.moveUpRequested.connect(method)
 | 
			
		||||
        method = self.set_selection_on_top
 | 
			
		||||
        self.menu.onTopRequested.connect(method)
 | 
			
		||||
        method = self.set_selection_on_bottom
 | 
			
		||||
        self.menu.onBottomRequested.connect(method)
 | 
			
		||||
        self.menu.symmetryRequested.connect(self.do_symmetry)
 | 
			
		||||
        self.menu.searchAndReplaceRequested.connect(self.search_and_replace)
 | 
			
		||||
        self.menu.alignRequested.connect(self.align_selection)
 | 
			
		||||
        self.menu.arrangeRequested.connect(self.arrange_selection)
 | 
			
		||||
        self.menu.load_ui_states()
 | 
			
		||||
 | 
			
		||||
        set_shortcut("Ctrl+Z", self.shape_editor, self.undo)
 | 
			
		||||
        set_shortcut("Ctrl+Y", self.shape_editor, self.redo)
 | 
			
		||||
        set_shortcut("Ctrl+C", self.shape_editor, self.copy)
 | 
			
		||||
        set_shortcut("Ctrl+V", self.shape_editor, self.paste)
 | 
			
		||||
        set_shortcut("Ctrl+R", self.shape_editor, self.search_and_replace)
 | 
			
		||||
        set_shortcut("del", self.shape_editor, self.delete_selection)
 | 
			
		||||
        set_shortcut("Ctrl+D", self.shape_editor, self.deselect_all)
 | 
			
		||||
        set_shortcut("Ctrl+A", self.shape_editor, self.select_all)
 | 
			
		||||
        set_shortcut("Ctrl+I", self.shape_editor, self.invert_selection)
 | 
			
		||||
        for direction in ['Left', 'Right', 'Up', 'Down']:
 | 
			
		||||
            method = partial(self.move_selection, direction)
 | 
			
		||||
            shortcut = set_shortcut(direction, self.shape_editor, method)
 | 
			
		||||
            shortcut.setAutoRepeat(True)
 | 
			
		||||
 | 
			
		||||
        self.attribute_editor = AttributeEditor()
 | 
			
		||||
        self.attribute_editor.set_generals(self.options)
 | 
			
		||||
        self.attribute_editor.generals.set_shapes(self.shape_editor.shapes)
 | 
			
		||||
        self.attribute_editor.generalOptionSet.connect(self.generals_modified)
 | 
			
		||||
        self.attribute_editor.optionSet.connect(self.option_set)
 | 
			
		||||
        self.attribute_editor.rectModified.connect(self.rect_modified)
 | 
			
		||||
        self.attribute_editor.imageModified.connect(self.image_modified)
 | 
			
		||||
        self.attribute_editor.removeLayer.connect(self.remove_layer)
 | 
			
		||||
        self.attribute_editor.selectLayerContent.connect(self.select_layer)
 | 
			
		||||
 | 
			
		||||
        self.hlayout = QtWidgets.QHBoxLayout()
 | 
			
		||||
        self.hlayout.setSizeConstraint(QtWidgets.QLayout.SetMaximumSize)
 | 
			
		||||
        self.hlayout.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        self.hlayout.addStretch(1)
 | 
			
		||||
        self.hlayout.addWidget(self.scrollarea)
 | 
			
		||||
        self.hlayout.addStretch(1)
 | 
			
		||||
        self.hlayout.addWidget(self.attribute_editor)
 | 
			
		||||
 | 
			
		||||
        self.vlayout = QtWidgets.QVBoxLayout(self)
 | 
			
		||||
        self.vlayout.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        self.vlayout.setSpacing(0)
 | 
			
		||||
        self.vlayout.addWidget(self.menu)
 | 
			
		||||
        self.vlayout.addLayout(self.hlayout)
 | 
			
		||||
 | 
			
		||||
    def copy(self):
 | 
			
		||||
        clipboard.set([
 | 
			
		||||
            s.options.copy() for s in self.shape_editor.selection])
 | 
			
		||||
 | 
			
		||||
    def copy_settings(self):
 | 
			
		||||
        if len(self.shape_editor.selection) != 1:
 | 
			
		||||
            return warning('Copy settings', 'Please select only one shape')
 | 
			
		||||
        shape = self.shape_editor.selection[0]
 | 
			
		||||
        clipboard.set_settings(shape.options.copy())
 | 
			
		||||
 | 
			
		||||
    def sizeHint(self):
 | 
			
		||||
        return QtCore.QSize(1300, 750)
 | 
			
		||||
 | 
			
		||||
    def paste(self):
 | 
			
		||||
        clipboad_copy = [s.copy() for s in clipboard.get()]
 | 
			
		||||
        shape_datas = self.picker_data()['shapes'][:] + clipboad_copy
 | 
			
		||||
        picker_data = {
 | 
			
		||||
            'general': self.options,
 | 
			
		||||
            'shapes': shape_datas}
 | 
			
		||||
        self.set_picker_data(picker_data)
 | 
			
		||||
        self.undo_manager.set_data_modified(picker_data)
 | 
			
		||||
        self.pickerDataModified.emit(picker_data)
 | 
			
		||||
        # select new shapes
 | 
			
		||||
        shapes = self.shape_editor.shapes[-len(clipboard.get()):]
 | 
			
		||||
        self.shape_editor.selection.replace(shapes)
 | 
			
		||||
        self.shape_editor.update_selection()
 | 
			
		||||
        self.shape_editor.repaint()
 | 
			
		||||
 | 
			
		||||
    def paste_settings(self):
 | 
			
		||||
        dialog = SettingsPaster()
 | 
			
		||||
        if not dialog.exec_():
 | 
			
		||||
            return
 | 
			
		||||
        settings = clipboard.get_settings()
 | 
			
		||||
        settings = {k: v for k, v in settings.items() if k in dialog.settings}
 | 
			
		||||
        for shape in self.shape_editor.selection:
 | 
			
		||||
            shape.options.update(settings)
 | 
			
		||||
            shape.rect = get_shape_rect_from_options(shape.options)
 | 
			
		||||
            shape.synchronize_image()
 | 
			
		||||
        self.set_data_modified()
 | 
			
		||||
        self.selection_changed()
 | 
			
		||||
        self.shape_editor.update_selection()
 | 
			
		||||
        self.shape_editor.repaint()
 | 
			
		||||
 | 
			
		||||
    def undo(self):
 | 
			
		||||
        result = self.undo_manager.undo()
 | 
			
		||||
        if result is False:
 | 
			
		||||
            return
 | 
			
		||||
        self.update_undo_manager()
 | 
			
		||||
 | 
			
		||||
    def redo(self):
 | 
			
		||||
        self.undo_manager.redo()
 | 
			
		||||
        self.update_undo_manager()
 | 
			
		||||
 | 
			
		||||
    def update_undo_manager(self):
 | 
			
		||||
        data = self.undo_manager.data
 | 
			
		||||
        self.set_picker_data(data)
 | 
			
		||||
        self.pickerDataModified.emit(self.picker_data())
 | 
			
		||||
        self.attribute_editor.generals.set_shapes(self.shape_editor.shapes)
 | 
			
		||||
 | 
			
		||||
    def deselect_all(self):
 | 
			
		||||
        self.shape_editor.selection.clear()
 | 
			
		||||
        self.shape_editor.update_selection()
 | 
			
		||||
        self.shape_editor.repaint()
 | 
			
		||||
 | 
			
		||||
    def select_all(self):
 | 
			
		||||
        shapes = self.shape_editor.list_shapes()
 | 
			
		||||
        self.shape_editor.selection.add(shapes)
 | 
			
		||||
        self.shape_editor.update_selection()
 | 
			
		||||
        self.shape_editor.repaint()
 | 
			
		||||
 | 
			
		||||
    def invert_selection(self):
 | 
			
		||||
        self.shape_editor.selection.invert(self.shape_editor.shapes)
 | 
			
		||||
        if self.menu.lock_bg.isChecked():
 | 
			
		||||
            shapes = [
 | 
			
		||||
                s for s in self.shape_editor.selection
 | 
			
		||||
                if not s.is_background()]
 | 
			
		||||
            self.shape_editor.selection.set(shapes)
 | 
			
		||||
        self.shape_editor.update_selection()
 | 
			
		||||
        self.shape_editor.repaint()
 | 
			
		||||
 | 
			
		||||
    def set_data_modified(self):
 | 
			
		||||
        self.undo_manager.set_data_modified(self.picker_data())
 | 
			
		||||
        self.pickerDataModified.emit(self.picker_data())
 | 
			
		||||
 | 
			
		||||
    def use_snap(self, state):
 | 
			
		||||
        snap = self.menu.snap_values() if state else None
 | 
			
		||||
        self.shape_editor.transform.snap = snap
 | 
			
		||||
        self.shape_editor.repaint()
 | 
			
		||||
 | 
			
		||||
    def snap_value_changed(self):
 | 
			
		||||
        self.shape_editor.transform.snap = self.menu.snap_values()
 | 
			
		||||
        self.set_data_modified()
 | 
			
		||||
        self.shape_editor.repaint()
 | 
			
		||||
 | 
			
		||||
    def generals_modified(self, key, value):
 | 
			
		||||
        self.options[key] = value
 | 
			
		||||
        if key == 'name':
 | 
			
		||||
            title = "Picker editor - " + self.options['name']
 | 
			
		||||
            self.setWindowTitle(title)
 | 
			
		||||
        self.pickerDataModified.emit(self.picker_data())
 | 
			
		||||
 | 
			
		||||
    def option_set(self, option, value):
 | 
			
		||||
        for shape in self.shape_editor.selection:
 | 
			
		||||
            shape.options[option] = value
 | 
			
		||||
        self.shape_editor.repaint()
 | 
			
		||||
        self.set_data_modified()
 | 
			
		||||
        if option == 'visibility_layer':
 | 
			
		||||
            self.attribute_editor.generals.set_shapes(self.shape_editor.shapes)
 | 
			
		||||
 | 
			
		||||
    def editor_size_changed(self):
 | 
			
		||||
        size = self.menu.get_size()
 | 
			
		||||
        self.shape_editor.setFixedSize(size)
 | 
			
		||||
        self.options['width'] = size.width()
 | 
			
		||||
        self.options['height'] = size.height()
 | 
			
		||||
        self.set_data_modified()
 | 
			
		||||
 | 
			
		||||
    def rect_modified(self, option, value):
 | 
			
		||||
        shapes = self.shape_editor.selection
 | 
			
		||||
        for shape in shapes:
 | 
			
		||||
            shape.options[option] = value
 | 
			
		||||
            if option == 'shape.height':
 | 
			
		||||
                shape.rect.setHeight(value)
 | 
			
		||||
                shape.synchronize_image()
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            elif option == 'shape.width':
 | 
			
		||||
                shape.rect.setWidth(value)
 | 
			
		||||
                shape.synchronize_image()
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            width = shape.rect.width()
 | 
			
		||||
            height = shape.rect.height()
 | 
			
		||||
            if option == 'shape.left':
 | 
			
		||||
                shape.rect.setLeft(value)
 | 
			
		||||
            else:
 | 
			
		||||
                shape.rect.setTop(value)
 | 
			
		||||
            shape.rect.setWidth(width)
 | 
			
		||||
            shape.rect.setHeight(height)
 | 
			
		||||
            shape.synchronize_image()
 | 
			
		||||
 | 
			
		||||
        self.update_manipulator_rect()
 | 
			
		||||
        self.set_data_modified()
 | 
			
		||||
 | 
			
		||||
    def selection_changed(self):
 | 
			
		||||
        shapes = self.shape_editor.selection
 | 
			
		||||
        options = [shape.options for shape in shapes]
 | 
			
		||||
        self.attribute_editor.set_options(options)
 | 
			
		||||
 | 
			
		||||
    def frame_shapes(self):
 | 
			
		||||
        shapes = self.shape_editor.shapes
 | 
			
		||||
        width = self.options['width']
 | 
			
		||||
        height = self.options['height']
 | 
			
		||||
        frame_shapes(shapes)
 | 
			
		||||
        width = int(ceil(max(s.rect.right() for s in shapes)))
 | 
			
		||||
        height = int(ceil(max(s.rect.bottom() for s in shapes)))
 | 
			
		||||
        self.shape_editor.repaint()
 | 
			
		||||
        self.update_manipulator_rect()
 | 
			
		||||
        # This mark data as changed, no need to repeat.
 | 
			
		||||
        self.menu.set_size_values(width, height)
 | 
			
		||||
 | 
			
		||||
    def create_shape(
 | 
			
		||||
            self, template, before=False, position=None, targets=None):
 | 
			
		||||
        options = template.copy()
 | 
			
		||||
        shape = Shape(options)
 | 
			
		||||
        if not position:
 | 
			
		||||
            shape.rect.moveCenter(self.shape_editor.rect().center())
 | 
			
		||||
        else:
 | 
			
		||||
            shape.rect.moveTopLeft(position)
 | 
			
		||||
        if targets:
 | 
			
		||||
            shape.set_targets(targets)
 | 
			
		||||
        shape.synchronize_rect()
 | 
			
		||||
        if before is True:
 | 
			
		||||
            self.shape_editor.shapes.insert(0, shape)
 | 
			
		||||
        else:
 | 
			
		||||
            self.shape_editor.shapes.append(shape)
 | 
			
		||||
        self.shape_editor.repaint()
 | 
			
		||||
        self.set_data_modified()
 | 
			
		||||
 | 
			
		||||
    def update_targets(self, shape):
 | 
			
		||||
        # shape = self.shape_editor.selection[0]
 | 
			
		||||
        shape.set_targets(cmds.ls(selection=True))
 | 
			
		||||
        self.shape_editor.repaint()
 | 
			
		||||
        self.set_data_modified()
 | 
			
		||||
 | 
			
		||||
    def image_modified(self):
 | 
			
		||||
        for shape in self.shape_editor.selection:
 | 
			
		||||
            shape.synchronize_image()
 | 
			
		||||
        self.shape_editor.repaint()
 | 
			
		||||
 | 
			
		||||
    def set_selection_move_down(self):
 | 
			
		||||
        array = self.shape_editor.shapes
 | 
			
		||||
        elements = self.shape_editor.selection
 | 
			
		||||
        move_down_array_elements(array, elements)
 | 
			
		||||
        self.shape_editor.repaint()
 | 
			
		||||
        self.set_data_modified()
 | 
			
		||||
 | 
			
		||||
    def set_selection_move_up(self):
 | 
			
		||||
        array = self.shape_editor.shapes
 | 
			
		||||
        elements = self.shape_editor.selection
 | 
			
		||||
        move_up_array_elements(array, elements)
 | 
			
		||||
        self.shape_editor.repaint()
 | 
			
		||||
        self.set_data_modified()
 | 
			
		||||
 | 
			
		||||
    def set_selection_on_top(self):
 | 
			
		||||
        array = self.shape_editor.shapes
 | 
			
		||||
        elements = self.shape_editor.selection
 | 
			
		||||
        self.shape_editor.shapes = move_elements_to_array_end(array, elements)
 | 
			
		||||
        self.shape_editor.repaint()
 | 
			
		||||
        self.set_data_modified()
 | 
			
		||||
 | 
			
		||||
    def set_selection_on_bottom(self):
 | 
			
		||||
        array = self.shape_editor.shapes
 | 
			
		||||
        elements = self.shape_editor.selection
 | 
			
		||||
        shapes = move_elements_to_array_begin(array, elements)
 | 
			
		||||
        self.shape_editor.shapes = shapes
 | 
			
		||||
        self.shape_editor.repaint()
 | 
			
		||||
        self.set_data_modified()
 | 
			
		||||
 | 
			
		||||
    def delete_selection(self):
 | 
			
		||||
        for shape in reversed(self.shape_editor.selection.shapes):
 | 
			
		||||
            self.shape_editor.shapes.remove(shape)
 | 
			
		||||
            self.shape_editor.selection.remove(shape)
 | 
			
		||||
        self.update_manipulator_rect()
 | 
			
		||||
        self.set_data_modified()
 | 
			
		||||
 | 
			
		||||
    def update_manipulator_rect(self):
 | 
			
		||||
        rects = [shape.rect for shape in self.shape_editor.selection]
 | 
			
		||||
        rect = get_combined_rects(rects)
 | 
			
		||||
        self.shape_editor.manipulator.set_rect(rect)
 | 
			
		||||
        self.shape_editor.repaint()
 | 
			
		||||
 | 
			
		||||
    def picker_data(self):
 | 
			
		||||
        return {
 | 
			
		||||
            'general': self.options,
 | 
			
		||||
            'shapes': [shape.options for shape in self.shape_editor.shapes]}
 | 
			
		||||
 | 
			
		||||
    def set_picker_data(self, picker_data, reset_stacks=False):
 | 
			
		||||
        self.options = picker_data['general']
 | 
			
		||||
        self.shape_editor.options = self.options
 | 
			
		||||
        shapes = [Shape(options) for options in picker_data['shapes']]
 | 
			
		||||
        self.shape_editor.shapes = shapes
 | 
			
		||||
        self.shape_editor.manipulator.set_rect(None)
 | 
			
		||||
        self.shape_editor.repaint()
 | 
			
		||||
        if reset_stacks is True:
 | 
			
		||||
            self.undo_manager.reset_stacks()
 | 
			
		||||
 | 
			
		||||
    def do_symmetry(self, horizontal=True):
 | 
			
		||||
        shapes = self.shape_editor.selection.shapes
 | 
			
		||||
        for shape in shapes:
 | 
			
		||||
            rect_symmetry(
 | 
			
		||||
                rect=shape.rect,
 | 
			
		||||
                point=self.shape_editor.manipulator.rect.center(),
 | 
			
		||||
                horizontal=horizontal)
 | 
			
		||||
            shape.synchronize_rect()
 | 
			
		||||
        self.shape_editor.repaint()
 | 
			
		||||
        if not cmds.optionVar(query=TRIGGER_REPLACE_ON_MIRROR):
 | 
			
		||||
            self.set_data_modified()
 | 
			
		||||
            return
 | 
			
		||||
        if not self.search_and_replace():
 | 
			
		||||
            self.set_data_modified()
 | 
			
		||||
 | 
			
		||||
    def search_and_replace(self):
 | 
			
		||||
        dialog = SearchAndReplaceDialog()
 | 
			
		||||
        if not dialog.exec_():
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
        if dialog.filter == 0:  # Search on all shapes.
 | 
			
		||||
            shapes = self.shape_editor.shapes
 | 
			
		||||
        else:
 | 
			
		||||
            shapes = self.shape_editor.selection
 | 
			
		||||
 | 
			
		||||
        pattern = dialog.search.text()
 | 
			
		||||
        replace = dialog.replace.text()
 | 
			
		||||
 | 
			
		||||
        for s in shapes:
 | 
			
		||||
            if not dialog.field:  # Targets
 | 
			
		||||
                if not s.targets():
 | 
			
		||||
                    continue
 | 
			
		||||
                targets = [t.replace(pattern, replace) for t in s.targets()]
 | 
			
		||||
                s.options['action.targets'] = targets
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            if dialog.field <= 2:
 | 
			
		||||
                key = ('text.content', 'image.path')[dialog.field - 1]
 | 
			
		||||
                result = s.options[key].replace(pattern, replace)
 | 
			
		||||
                s.options[key] = result
 | 
			
		||||
            else:  # Command code
 | 
			
		||||
                for command in s.options['action.commands']:
 | 
			
		||||
                    result = command['command'].replace(pattern, replace)
 | 
			
		||||
                    command['command'] = result
 | 
			
		||||
 | 
			
		||||
        self.set_data_modified()
 | 
			
		||||
        self.shape_editor.repaint()
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def move_selection(self, direction):
 | 
			
		||||
        offset = DIRECTION_OFFSETS[direction]
 | 
			
		||||
        rects = (s.rect for s in self.shape_editor.selection)
 | 
			
		||||
        rects = (s.rect for s in self.shape_editor.selection)
 | 
			
		||||
        rect = self.shape_editor.manipulator.rect
 | 
			
		||||
        reference_rect = QtCore.QRect(rect)
 | 
			
		||||
 | 
			
		||||
        self.shape_editor.transform.set_rect(rect)
 | 
			
		||||
        self.shape_editor.transform.reference_rect = reference_rect
 | 
			
		||||
        self.shape_editor.transform.shift(rects, offset)
 | 
			
		||||
        self.shape_editor.manipulator.update_geometries()
 | 
			
		||||
        for shape in self.shape_editor.selection:
 | 
			
		||||
            shape.synchronize_rect()
 | 
			
		||||
        self.shape_editor.repaint()
 | 
			
		||||
        self.shape_editor.selectedShapesChanged.emit()
 | 
			
		||||
        self.pickerDataModified.emit(self.picker_data())
 | 
			
		||||
 | 
			
		||||
    def align_selection(self, direction):
 | 
			
		||||
        if not self.shape_editor.selection:
 | 
			
		||||
            return
 | 
			
		||||
        align_shapes(self.shape_editor.selection, direction)
 | 
			
		||||
        rects = [s.rect for s in self.shape_editor.selection]
 | 
			
		||||
        self.shape_editor.manipulator.set_rect(get_combined_rects(rects))
 | 
			
		||||
        self.shape_editor.manipulator.update_geometries()
 | 
			
		||||
        self.shape_editor.repaint()
 | 
			
		||||
        self.shape_editor.selectedShapesChanged.emit()
 | 
			
		||||
        self.pickerDataModified.emit(self.picker_data())
 | 
			
		||||
 | 
			
		||||
    def arrange_selection(self, direction):
 | 
			
		||||
        if not self.shape_editor.selection:
 | 
			
		||||
            return
 | 
			
		||||
        if direction == 'horizontal':
 | 
			
		||||
            arrange_horizontal(self.shape_editor.selection)
 | 
			
		||||
        else:
 | 
			
		||||
            arrange_vertical(self.shape_editor.selection)
 | 
			
		||||
        rects = [s.rect for s in self.shape_editor.selection]
 | 
			
		||||
        self.shape_editor.manipulator.set_rect(get_combined_rects(rects))
 | 
			
		||||
        self.shape_editor.manipulator.update_geometries()
 | 
			
		||||
        self.shape_editor.repaint()
 | 
			
		||||
        self.shape_editor.selectedShapesChanged.emit()
 | 
			
		||||
        self.pickerDataModified.emit(self.picker_data())
 | 
			
		||||
 | 
			
		||||
    def call_context_menu(self, position):
 | 
			
		||||
        targets = cmds.ls(selection=True)
 | 
			
		||||
        button = QtWidgets.QAction('Add selection button', self)
 | 
			
		||||
        method = partial(
 | 
			
		||||
            self.create_shape, BUTTON.copy(),
 | 
			
		||||
            position=position, targets=targets)
 | 
			
		||||
        button.triggered.connect(method)
 | 
			
		||||
        template = BUTTON.copy()
 | 
			
		||||
        template.update(clipboard.get_settings())
 | 
			
		||||
        method = partial(
 | 
			
		||||
            self.create_shape, template,
 | 
			
		||||
            position=position, targets=targets)
 | 
			
		||||
        text = 'Add selection button (using settings clipboard)'
 | 
			
		||||
        button2 = QtWidgets.QAction(text, self)
 | 
			
		||||
        button2.triggered.connect(method)
 | 
			
		||||
 | 
			
		||||
        cursor = get_cursor(self.shape_editor)
 | 
			
		||||
        shape = self.shape_editor.get_hovered_shape(cursor)
 | 
			
		||||
        method = partial(self.update_targets, shape)
 | 
			
		||||
        text = 'Update targets'
 | 
			
		||||
        button3 = QtWidgets.QAction(text, self)
 | 
			
		||||
        button3.setEnabled(bool(shape))
 | 
			
		||||
        button3.triggered.connect(method)
 | 
			
		||||
 | 
			
		||||
        menu = QtWidgets.QMenu()
 | 
			
		||||
        menu.addAction(button)
 | 
			
		||||
        menu.addAction(button2)
 | 
			
		||||
        menu.addAction(button3)
 | 
			
		||||
        menu.addSection('Visibility Layers')
 | 
			
		||||
 | 
			
		||||
        layers = sorted(list({
 | 
			
		||||
            s.visibility_layer()
 | 
			
		||||
            for s in self.shape_editor.shapes
 | 
			
		||||
            if s.visibility_layer()}))
 | 
			
		||||
 | 
			
		||||
        add_selection = QtWidgets.QMenu('Assign to layer', self)
 | 
			
		||||
        add_selection.setEnabled(bool(layers))
 | 
			
		||||
        menu.addMenu(add_selection)
 | 
			
		||||
        for layer in layers:
 | 
			
		||||
            action = QtWidgets.QAction(layer, self)
 | 
			
		||||
            action.triggered.connect(partial(self.set_visibility_layer, layer))
 | 
			
		||||
            add_selection.addAction(action)
 | 
			
		||||
 | 
			
		||||
        remove_selection = QtWidgets.QAction('Remove assigned layer', self)
 | 
			
		||||
        remove_selection.setEnabled(bool(self.shape_editor.selection.shapes))
 | 
			
		||||
        remove_selection.triggered.connect(self.set_visibility_layer)
 | 
			
		||||
        menu.addAction(remove_selection)
 | 
			
		||||
 | 
			
		||||
        create_layer = QtWidgets.QAction('Create layer from selection', self)
 | 
			
		||||
        create_layer.triggered.connect(self.create_visibility_layer)
 | 
			
		||||
        create_layer.setEnabled(bool(self.shape_editor.selection.shapes))
 | 
			
		||||
 | 
			
		||||
        menu.addAction(create_layer)
 | 
			
		||||
        menu.exec_(self.shape_editor.mapToGlobal(position))
 | 
			
		||||
 | 
			
		||||
    def set_visibility_layer(self, layer=''):
 | 
			
		||||
        for shape in self.shape_editor.selection:
 | 
			
		||||
            shape.options['visibility_layer'] = layer
 | 
			
		||||
        self.layers_modified()
 | 
			
		||||
 | 
			
		||||
    def layers_modified(self):
 | 
			
		||||
        self.set_data_modified()
 | 
			
		||||
        self.attribute_editor.generals.set_shapes(self.shape_editor.shapes)
 | 
			
		||||
        self.selection_changed()
 | 
			
		||||
 | 
			
		||||
    def create_visibility_layer(self):
 | 
			
		||||
        text, result = QtWidgets.QInputDialog.getText(
 | 
			
		||||
            self, 'Create visibility layer', 'Layer name')
 | 
			
		||||
        if not text or not result:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        for shape in self.shape_editor.selection:
 | 
			
		||||
            shape.options['visibility_layer'] = text
 | 
			
		||||
        self.layers_modified()
 | 
			
		||||
 | 
			
		||||
    def select_layer(self, layer):
 | 
			
		||||
        shapes = [
 | 
			
		||||
            shape for shape in self.shape_editor.shapes
 | 
			
		||||
            if shape.visibility_layer() == layer]
 | 
			
		||||
        self.shape_editor.selection.set(shapes)
 | 
			
		||||
        self.shape_editor.update_selection()
 | 
			
		||||
        self.shape_editor.repaint()
 | 
			
		||||
        self.selection_changed()
 | 
			
		||||
 | 
			
		||||
    def remove_layer(self, layer):
 | 
			
		||||
        for shape in self.shape_editor.shapes:
 | 
			
		||||
            if shape.visibility_layer() == layer:
 | 
			
		||||
                shape.options['visibility_layer'] = None
 | 
			
		||||
        self.layers_modified()
 | 
			
		||||
							
								
								
									
										114
									
								
								Scripts/Animation/dwpicker/dwpicker/designer/highlighter.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,114 @@
 | 
			
		||||
import keyword
 | 
			
		||||
from PySide2 import QtGui, QtCore
 | 
			
		||||
from dwpicker.languages import PYTHON, MEL
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MELKEYWORDS = [
 | 
			
		||||
    'if', 'else', 'int', 'float', 'double', 'string', 'array'
 | 
			
		||||
    'var', 'return', 'case', 'then', 'continue', 'break', 'global', 'proc']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
TEXT_STYLES = {
 | 
			
		||||
    'keyword': {
 | 
			
		||||
        'color': 'white',
 | 
			
		||||
        'bold': True,
 | 
			
		||||
        'italic': False},
 | 
			
		||||
    'number': {
 | 
			
		||||
        'color': 'cyan',
 | 
			
		||||
        'bold': False,
 | 
			
		||||
        'italic': False},
 | 
			
		||||
    'comment': {
 | 
			
		||||
        'color': (0.7, 0.5, 0.5),
 | 
			
		||||
        'bold': False,
 | 
			
		||||
        'italic': False},
 | 
			
		||||
    'function': {
 | 
			
		||||
        'color': '#ff0571',
 | 
			
		||||
        'bold': False,
 | 
			
		||||
        'italic': True},
 | 
			
		||||
    'string': {
 | 
			
		||||
        'color': 'yellow',
 | 
			
		||||
        'bold': False,
 | 
			
		||||
        'italic': False},
 | 
			
		||||
    'boolean': {
 | 
			
		||||
        'color': '#a18852',
 | 
			
		||||
        'bold': True,
 | 
			
		||||
        'italic': False}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PATTERNS = {
 | 
			
		||||
    PYTHON: {
 | 
			
		||||
        'keyword': r'\b|'.join(keyword.kwlist),
 | 
			
		||||
        'number': r'\b[+-]?[0-9]+[lL]?\b',
 | 
			
		||||
        'comment': r'#[^\n]*',
 | 
			
		||||
        'function': r'\b[A-Za-z0-9_]+(?=\()',
 | 
			
		||||
        'string': r'".*"|\'.*\'',
 | 
			
		||||
        'boolean': r'\bTrue\b|\bFalse\b'},
 | 
			
		||||
    MEL: {
 | 
			
		||||
        'keyword': r'\b|'.join(MELKEYWORDS),
 | 
			
		||||
        'number': r'\b[+-]?[0-9]+[lL]?\b',
 | 
			
		||||
        'comment': r'//[^\n]*',
 | 
			
		||||
        'function': r'\b[A-Za-z0-9_]+(?=\()',
 | 
			
		||||
        'string': r'".*"|\'.*\'',
 | 
			
		||||
        'boolean': r'\btrue\b|\bfalse\b'}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Highlighter(QtGui.QSyntaxHighlighter):
 | 
			
		||||
    PATTERNS = []
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(Highlighter, self).__init__(parent)
 | 
			
		||||
        self.rules = []
 | 
			
		||||
        for name, properties in TEXT_STYLES.items():
 | 
			
		||||
            if name not in self.PATTERNS:
 | 
			
		||||
                continue
 | 
			
		||||
            text_format = create_textcharformat(
 | 
			
		||||
                color=properties['color'],
 | 
			
		||||
                bold=properties['bold'],
 | 
			
		||||
                italic=properties['italic'])
 | 
			
		||||
 | 
			
		||||
            rule = QtCore.QRegularExpression(self.PATTERNS[name]), text_format
 | 
			
		||||
            self.rules.append(rule)
 | 
			
		||||
 | 
			
		||||
    def highlightBlock(self, text):
 | 
			
		||||
        for pattern, format_ in self.rules:
 | 
			
		||||
            expression = QtCore.QRegularExpression(pattern)
 | 
			
		||||
            iterator = expression.globalMatch(text)
 | 
			
		||||
            while iterator.hasNext():
 | 
			
		||||
                match = iterator.next()
 | 
			
		||||
                index = match.capturedStart()
 | 
			
		||||
                length = match.capturedLength()
 | 
			
		||||
                self.setFormat(index, length, format_)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PythonHighlighter(Highlighter):
 | 
			
		||||
    PATTERNS = PATTERNS[PYTHON]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MelHighlighter(Highlighter):
 | 
			
		||||
    PATTERNS = PATTERNS[MEL]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
HIGHLIGHTERS = {
 | 
			
		||||
    PYTHON: PythonHighlighter,
 | 
			
		||||
    MEL: MelHighlighter}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_highlighter(language):
 | 
			
		||||
    return HIGHLIGHTERS.get(language, Highlighter)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_textcharformat(color, bold=False, italic=False):
 | 
			
		||||
    char_format = QtGui.QTextCharFormat()
 | 
			
		||||
    qcolor = QtGui.QColor()
 | 
			
		||||
    if isinstance(color, str):
 | 
			
		||||
        qcolor.setNamedColor(color)
 | 
			
		||||
    else:
 | 
			
		||||
        r, g, b = color
 | 
			
		||||
        qcolor.setRgbF(r, g, b)
 | 
			
		||||
    char_format.setForeground(qcolor)
 | 
			
		||||
    if bold:
 | 
			
		||||
        char_format.setFontWeight(QtGui.QFont.Bold)
 | 
			
		||||
    if italic:
 | 
			
		||||
        char_format.setFontItalic(True)
 | 
			
		||||
    return char_format
 | 
			
		||||
							
								
								
									
										101
									
								
								Scripts/Animation/dwpicker/dwpicker/designer/layer.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,101 @@
 | 
			
		||||
 | 
			
		||||
from PySide2 import QtWidgets, QtCore
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class VisibilityLayersEditor(QtWidgets.QWidget):
 | 
			
		||||
    removeLayer = QtCore.Signal(str)
 | 
			
		||||
    selectLayerContent = QtCore.Signal(str)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(VisibilityLayersEditor, self).__init__(parent)
 | 
			
		||||
        self.model = VisbilityLayersModel()
 | 
			
		||||
        self.table = QtWidgets.QTableView()
 | 
			
		||||
        self.table.horizontalHeader().setSectionResizeMode(
 | 
			
		||||
            QtWidgets.QHeaderView.ResizeToContents)
 | 
			
		||||
        self.table.verticalHeader().setSectionResizeMode(
 | 
			
		||||
            QtWidgets.QHeaderView.ResizeToContents)
 | 
			
		||||
        self.table.setShowGrid(False)
 | 
			
		||||
        self.table.setAlternatingRowColors(True)
 | 
			
		||||
        self.table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
 | 
			
		||||
        self.table.setSelectionMode(
 | 
			
		||||
            QtWidgets.QAbstractItemView.SingleSelection)
 | 
			
		||||
        self.table.setModel(self.model)
 | 
			
		||||
        self.table.setFixedHeight(100)
 | 
			
		||||
 | 
			
		||||
        self.select_content = QtWidgets.QPushButton('Select layer content')
 | 
			
		||||
        self.select_content.released.connect(self.call_select_layer)
 | 
			
		||||
        self.remove_layer = QtWidgets.QPushButton('Remove selected layer')
 | 
			
		||||
        self.remove_layer.released.connect(self.call_remove_layer)
 | 
			
		||||
 | 
			
		||||
        layout = QtWidgets.QVBoxLayout(self)
 | 
			
		||||
        layout.setSpacing(0)
 | 
			
		||||
        layout.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        layout.addWidget(self.table)
 | 
			
		||||
        layout.addWidget(self.select_content)
 | 
			
		||||
        layout.addWidget(self.remove_layer)
 | 
			
		||||
 | 
			
		||||
    def selected_layer(self):
 | 
			
		||||
        indexes = self.table.selectedIndexes()
 | 
			
		||||
        if not indexes:
 | 
			
		||||
            return
 | 
			
		||||
        return self.model.layers_data[indexes[0].row()][0]
 | 
			
		||||
 | 
			
		||||
    def call_remove_layer(self):
 | 
			
		||||
        layer = self.selected_layer()
 | 
			
		||||
        if not layer:
 | 
			
		||||
            return
 | 
			
		||||
        self.removeLayer.emit(layer)
 | 
			
		||||
 | 
			
		||||
    def call_select_layer(self):
 | 
			
		||||
        layer = self.selected_layer()
 | 
			
		||||
        if not layer:
 | 
			
		||||
            return
 | 
			
		||||
        self.selectLayerContent.emit(layer)
 | 
			
		||||
 | 
			
		||||
    def set_shapes(self, shapes):
 | 
			
		||||
        self.model.set_shapes(shapes)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class VisbilityLayersModel(QtCore.QAbstractTableModel):
 | 
			
		||||
    HEADERS = 'name', 'shapes'
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(VisbilityLayersModel, self).__init__(parent)
 | 
			
		||||
        self.layers_data = []
 | 
			
		||||
 | 
			
		||||
    def rowCount(self, _):
 | 
			
		||||
        return len(self.layers_data)
 | 
			
		||||
 | 
			
		||||
    def columnCount(self, _):
 | 
			
		||||
        return len(self.HEADERS)
 | 
			
		||||
 | 
			
		||||
    def set_shapes(self, shapes):
 | 
			
		||||
        self.layoutAboutToBeChanged.emit()
 | 
			
		||||
        data = {}
 | 
			
		||||
        for shape in shapes:
 | 
			
		||||
            if not shape.visibility_layer():
 | 
			
		||||
                continue
 | 
			
		||||
            data[shape.visibility_layer()] = data.setdefault(
 | 
			
		||||
                shape.visibility_layer(), 0) + 1
 | 
			
		||||
        self.layers_data = sorted(data.items())
 | 
			
		||||
        self.layoutChanged.emit()
 | 
			
		||||
 | 
			
		||||
    def headerData(self, section, orientation, role):
 | 
			
		||||
        if orientation == QtCore.Qt.Vertical or role != QtCore.Qt.DisplayRole:
 | 
			
		||||
            return
 | 
			
		||||
        return self.HEADERS[section]
 | 
			
		||||
 | 
			
		||||
    def data(self, index, role):
 | 
			
		||||
        if not index.isValid():
 | 
			
		||||
            return
 | 
			
		||||
        if role == QtCore.Qt.TextAlignmentRole:
 | 
			
		||||
            if index.column() == 1:
 | 
			
		||||
                return QtCore.Qt.AlignCenter
 | 
			
		||||
 | 
			
		||||
        if role != QtCore.Qt.DisplayRole:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        if index.column() == 0:
 | 
			
		||||
            return self.layers_data[index.row()][0]
 | 
			
		||||
        if index.column() == 1:
 | 
			
		||||
            return str(self.layers_data[index.row()][1])
 | 
			
		||||
							
								
								
									
										270
									
								
								Scripts/Animation/dwpicker/dwpicker/designer/menu.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,270 @@
 | 
			
		||||
from functools import partial
 | 
			
		||||
from maya import cmds
 | 
			
		||||
from PySide2 import QtGui, QtWidgets, QtCore
 | 
			
		||||
 | 
			
		||||
from dwpicker.optionvar import (
 | 
			
		||||
    BG_LOCKED, SNAP_ITEMS, SNAP_GRID_X, SNAP_GRID_Y, save_optionvar)
 | 
			
		||||
from dwpicker.qtutils import icon
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MenuWidget(QtWidgets.QWidget):
 | 
			
		||||
    addBackgroundRequested = QtCore.Signal()
 | 
			
		||||
    addButtonRequested = QtCore.Signal()
 | 
			
		||||
    addTextRequested = QtCore.Signal()
 | 
			
		||||
    arrangeRequested = QtCore.Signal(str)
 | 
			
		||||
    centerValuesChanged = QtCore.Signal(int, int)
 | 
			
		||||
    copyRequested = QtCore.Signal()
 | 
			
		||||
    copySettingsRequested = QtCore.Signal()
 | 
			
		||||
    deleteRequested = QtCore.Signal()
 | 
			
		||||
    editCenterToggled = QtCore.Signal(bool)
 | 
			
		||||
    frameShapes = QtCore.Signal()
 | 
			
		||||
    lockBackgroundShapeToggled = QtCore.Signal(bool)
 | 
			
		||||
    moveDownRequested = QtCore.Signal()
 | 
			
		||||
    moveUpRequested = QtCore.Signal()
 | 
			
		||||
    onBottomRequested = QtCore.Signal()
 | 
			
		||||
    onTopRequested = QtCore.Signal()
 | 
			
		||||
    pasteRequested = QtCore.Signal()
 | 
			
		||||
    pasteSettingsRequested = QtCore.Signal()
 | 
			
		||||
    redoRequested = QtCore.Signal()
 | 
			
		||||
    searchAndReplaceRequested = QtCore.Signal()
 | 
			
		||||
    sizeChanged = QtCore.Signal()
 | 
			
		||||
    snapValuesChanged = QtCore.Signal()
 | 
			
		||||
    symmetryRequested = QtCore.Signal(bool)
 | 
			
		||||
    undoRequested = QtCore.Signal()
 | 
			
		||||
    useSnapToggled = QtCore.Signal(bool)
 | 
			
		||||
    alignRequested = QtCore.Signal(str)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(MenuWidget, self).__init__(parent=parent)
 | 
			
		||||
 | 
			
		||||
        self.delete = QtWidgets.QAction(icon('delete.png'), '', self)
 | 
			
		||||
        self.delete.setToolTip('Delete selection')
 | 
			
		||||
        self.delete.triggered.connect(self.deleteRequested.emit)
 | 
			
		||||
 | 
			
		||||
        self.copy = QtWidgets.QAction(icon('copy.png'), '', self)
 | 
			
		||||
        self.copy.setToolTip('Copy selection')
 | 
			
		||||
        self.copy.triggered.connect(self.copyRequested.emit)
 | 
			
		||||
 | 
			
		||||
        self.paste = QtWidgets.QAction(icon('paste.png'), '', self)
 | 
			
		||||
        self.paste.setToolTip('Paste')
 | 
			
		||||
        self.paste.triggered.connect(self.pasteRequested.emit)
 | 
			
		||||
 | 
			
		||||
        self.undo = QtWidgets.QAction(icon('undo.png'), '', self)
 | 
			
		||||
        self.undo.setToolTip('Undo')
 | 
			
		||||
        self.undo.triggered.connect(self.undoRequested.emit)
 | 
			
		||||
        self.redo = QtWidgets.QAction(icon('redo.png'), '', self)
 | 
			
		||||
        self.redo.setToolTip('Redo')
 | 
			
		||||
        self.redo.triggered.connect(self.redoRequested.emit)
 | 
			
		||||
 | 
			
		||||
        icon_ = icon('copy_settings.png')
 | 
			
		||||
        self.copy_settings = QtWidgets.QAction(icon_, '', self)
 | 
			
		||||
        self.copy_settings.setToolTip('Copy settings')
 | 
			
		||||
        self.copy_settings.triggered.connect(self.copySettingsRequested.emit)
 | 
			
		||||
        icon_ = icon('paste_settings.png')
 | 
			
		||||
        self.paste_settings = QtWidgets.QAction(icon_, '', self)
 | 
			
		||||
        self.paste_settings.setToolTip('Paste settings')
 | 
			
		||||
        self.paste_settings.triggered.connect(self.pasteSettingsRequested.emit)
 | 
			
		||||
 | 
			
		||||
        self.search = QtWidgets.QAction(icon('search.png'), '', self)
 | 
			
		||||
        self.search.triggered.connect(self.searchAndReplaceRequested.emit)
 | 
			
		||||
        self.search.setToolTip('Search and replace')
 | 
			
		||||
 | 
			
		||||
        icon_ = icon('lock-non-interactive.png')
 | 
			
		||||
        self.lock_bg = QtWidgets.QAction(icon_, '', self)
 | 
			
		||||
        self.lock_bg.setToolTip('Lock background items')
 | 
			
		||||
        self.lock_bg.setCheckable(True)
 | 
			
		||||
        self.lock_bg.triggered.connect(self.save_ui_states)
 | 
			
		||||
        self.lock_bg.toggled.connect(self.lockBackgroundShapeToggled.emit)
 | 
			
		||||
 | 
			
		||||
        validator = QtGui.QIntValidator()
 | 
			
		||||
        self.picker_width = QtWidgets.QLineEdit('600')
 | 
			
		||||
        self.picker_width.setFixedWidth(35)
 | 
			
		||||
        self.picker_width.setValidator(validator)
 | 
			
		||||
        self.picker_width.textEdited.connect(self.size_changed)
 | 
			
		||||
        self.picker_height = QtWidgets.QLineEdit('300')
 | 
			
		||||
        self.picker_height.setFixedWidth(35)
 | 
			
		||||
        self.picker_height.setValidator(validator)
 | 
			
		||||
        self.picker_height.textEdited.connect(self.size_changed)
 | 
			
		||||
 | 
			
		||||
        self.snap = QtWidgets.QAction(icon('snap.png'), '', self)
 | 
			
		||||
        self.snap.setToolTip('Snap grid enable')
 | 
			
		||||
        self.snap.setCheckable(True)
 | 
			
		||||
        self.snap.triggered.connect(self.snap_toggled)
 | 
			
		||||
        validator = QtGui.QIntValidator(5, 150)
 | 
			
		||||
        self.snapx = QtWidgets.QLineEdit('10')
 | 
			
		||||
        self.snapx.setFixedWidth(35)
 | 
			
		||||
        self.snapx.setValidator(validator)
 | 
			
		||||
        self.snapx.setEnabled(False)
 | 
			
		||||
        self.snapx.textEdited.connect(self.snap_value_changed)
 | 
			
		||||
        self.snapy = QtWidgets.QLineEdit('10')
 | 
			
		||||
        self.snapy.setFixedWidth(35)
 | 
			
		||||
        self.snapy.setValidator(validator)
 | 
			
		||||
        self.snapy.setEnabled(False)
 | 
			
		||||
        self.snapy.textEdited.connect(self.snap_value_changed)
 | 
			
		||||
        self.snap.toggled.connect(self.snapx.setEnabled)
 | 
			
		||||
        self.snap.toggled.connect(self.snapy.setEnabled)
 | 
			
		||||
 | 
			
		||||
        icon_ = icon('addbutton.png')
 | 
			
		||||
        self.addbutton = QtWidgets.QAction(icon_, '', self)
 | 
			
		||||
        self.addbutton.setToolTip('Add button')
 | 
			
		||||
        self.addbutton.triggered.connect(self.addButtonRequested.emit)
 | 
			
		||||
        self.addtext = QtWidgets.QAction(icon('addtext.png'), '', self)
 | 
			
		||||
        self.addtext.setToolTip('Add text')
 | 
			
		||||
        self.addtext.triggered.connect(self.addTextRequested.emit)
 | 
			
		||||
        self.addbg = QtWidgets.QAction(icon('addbg.png'), '', self)
 | 
			
		||||
        self.addbg.setToolTip('Add background shape')
 | 
			
		||||
        self.addbg.triggered.connect(self.addBackgroundRequested.emit)
 | 
			
		||||
 | 
			
		||||
        self.frame_shapes = QtWidgets.QAction(icon('frame.png'), '', self)
 | 
			
		||||
        self.frame_shapes.setToolTip('Frame buttons')
 | 
			
		||||
        self.frame_shapes.triggered.connect(self.frameShapes.emit)
 | 
			
		||||
 | 
			
		||||
        icon_ = icon('onbottom.png')
 | 
			
		||||
        self.onbottom = QtWidgets.QAction(icon_, '', self)
 | 
			
		||||
        self.onbottom.setToolTip('Set selected shapes on bottom')
 | 
			
		||||
        self.onbottom.triggered.connect(self.onBottomRequested.emit)
 | 
			
		||||
        icon_ = icon('movedown.png')
 | 
			
		||||
        self.movedown = QtWidgets.QAction(icon_, '', self)
 | 
			
		||||
        self.movedown.setToolTip('Move down selected shapes')
 | 
			
		||||
        self.movedown.triggered.connect(self.moveDownRequested.emit)
 | 
			
		||||
        self.moveup = QtWidgets.QAction(icon('moveup.png'), '', self)
 | 
			
		||||
        self.moveup.setToolTip('Move up selected shapes')
 | 
			
		||||
        self.moveup.triggered.connect(self.moveUpRequested.emit)
 | 
			
		||||
        self.ontop = QtWidgets.QAction(icon('ontop.png'), '', self)
 | 
			
		||||
        self.ontop.setToolTip('Set selected shapes on top')
 | 
			
		||||
        self.ontop.triggered.connect(self.onTopRequested.emit)
 | 
			
		||||
 | 
			
		||||
        self.hsymmetry = QtWidgets.QAction(icon('h_symmetry.png'), '', self)
 | 
			
		||||
        method = partial(self.symmetryRequested.emit, True)
 | 
			
		||||
        self.hsymmetry.triggered.connect(method)
 | 
			
		||||
        self.vsymmetry = QtWidgets.QAction(icon('v_symmetry.png'), '', self)
 | 
			
		||||
        method = partial(self.symmetryRequested.emit, False)
 | 
			
		||||
        self.vsymmetry.triggered.connect(method)
 | 
			
		||||
 | 
			
		||||
        method = partial(self.alignRequested.emit, 'left')
 | 
			
		||||
        self.align_left = QtWidgets.QAction(icon('align_left.png'), '', self)
 | 
			
		||||
        self.align_left.triggered.connect(method)
 | 
			
		||||
        file_ = 'align_h_center.png'
 | 
			
		||||
        method = partial(self.alignRequested.emit, 'h_center')
 | 
			
		||||
        self.align_h_center = QtWidgets.QAction(icon(file_), '', self)
 | 
			
		||||
        self.align_h_center.triggered.connect(method)
 | 
			
		||||
        method = partial(self.alignRequested.emit, 'right')
 | 
			
		||||
        self.align_right = QtWidgets.QAction(icon('align_right.png'), '', self)
 | 
			
		||||
        self.align_right.triggered.connect(method)
 | 
			
		||||
        method = partial(self.alignRequested.emit, 'top')
 | 
			
		||||
        self.align_top = QtWidgets.QAction(icon('align_top.png'), '', self)
 | 
			
		||||
        self.align_top.triggered.connect(method)
 | 
			
		||||
        file_ = 'align_v_center.png'
 | 
			
		||||
        self.align_v_center = QtWidgets.QAction(icon(file_), '', self)
 | 
			
		||||
        method = partial(self.alignRequested.emit, 'v_center')
 | 
			
		||||
        self.align_v_center.triggered.connect(method)
 | 
			
		||||
        file_ = 'align_bottom.png'
 | 
			
		||||
        method = partial(self.alignRequested.emit, 'bottom')
 | 
			
		||||
        self.align_bottom = QtWidgets.QAction(icon(file_), '', self)
 | 
			
		||||
        self.align_bottom.triggered.connect(method)
 | 
			
		||||
 | 
			
		||||
        file_ = 'arrange_h.png'
 | 
			
		||||
        method = partial(self.arrangeRequested.emit, 'horizontal')
 | 
			
		||||
        self.arrange_horizontal = QtWidgets.QAction(icon(file_), '', self)
 | 
			
		||||
        self.arrange_horizontal.triggered.connect(method)
 | 
			
		||||
 | 
			
		||||
        file_ = 'arrange_v.png'
 | 
			
		||||
        method = partial(self.arrangeRequested.emit, 'vertical')
 | 
			
		||||
        self.arrange_vertical = QtWidgets.QAction(icon(file_), '', self)
 | 
			
		||||
        self.arrange_vertical.triggered.connect(method)
 | 
			
		||||
 | 
			
		||||
        self.toolbar = QtWidgets.QToolBar()
 | 
			
		||||
        self.toolbar.addAction(self.delete)
 | 
			
		||||
        self.toolbar.addAction(self.copy)
 | 
			
		||||
        self.toolbar.addAction(self.paste)
 | 
			
		||||
        self.toolbar.addAction(self.copy_settings)
 | 
			
		||||
        self.toolbar.addAction(self.paste_settings)
 | 
			
		||||
        self.toolbar.addSeparator()
 | 
			
		||||
        self.toolbar.addAction(self.undo)
 | 
			
		||||
        self.toolbar.addAction(self.redo)
 | 
			
		||||
        self.toolbar.addSeparator()
 | 
			
		||||
        self.toolbar.addAction(self.search)
 | 
			
		||||
        self.toolbar.addSeparator()
 | 
			
		||||
        self.toolbar.addAction(self.lock_bg)
 | 
			
		||||
        self.toolbar.addSeparator()
 | 
			
		||||
        self.toolbar.addAction(self.snap)
 | 
			
		||||
        self.toolbar.addWidget(self.snapx)
 | 
			
		||||
        self.toolbar.addWidget(self.snapy)
 | 
			
		||||
        self.toolbar.addSeparator()
 | 
			
		||||
        self.toolbar.addWidget(QtWidgets.QLabel('size'))
 | 
			
		||||
        self.toolbar.addWidget(self.picker_width)
 | 
			
		||||
        self.toolbar.addWidget(self.picker_height)
 | 
			
		||||
        self.toolbar.addSeparator()
 | 
			
		||||
        self.toolbar.addAction(self.addbutton)
 | 
			
		||||
        self.toolbar.addAction(self.addtext)
 | 
			
		||||
        self.toolbar.addAction(self.addbg)
 | 
			
		||||
        self.toolbar.addSeparator()
 | 
			
		||||
        self.toolbar.addAction(self.frame_shapes)
 | 
			
		||||
        self.toolbar.addSeparator()
 | 
			
		||||
        self.toolbar.addAction(self.hsymmetry)
 | 
			
		||||
        self.toolbar.addAction(self.vsymmetry)
 | 
			
		||||
        self.toolbar.addSeparator()
 | 
			
		||||
        self.toolbar.addAction(self.onbottom)
 | 
			
		||||
        self.toolbar.addAction(self.movedown)
 | 
			
		||||
        self.toolbar.addAction(self.moveup)
 | 
			
		||||
        self.toolbar.addAction(self.ontop)
 | 
			
		||||
        self.toolbar.addSeparator()
 | 
			
		||||
        self.toolbar.addAction(self.align_left)
 | 
			
		||||
        self.toolbar.addAction(self.align_h_center)
 | 
			
		||||
        self.toolbar.addAction(self.align_right)
 | 
			
		||||
        self.toolbar.addAction(self.align_top)
 | 
			
		||||
        self.toolbar.addAction(self.align_v_center)
 | 
			
		||||
        self.toolbar.addAction(self.align_bottom)
 | 
			
		||||
        self.toolbar.addAction(self.arrange_horizontal)
 | 
			
		||||
        self.toolbar.addAction(self.arrange_vertical)
 | 
			
		||||
 | 
			
		||||
        self.layout = QtWidgets.QVBoxLayout(self)
 | 
			
		||||
        self.layout.setContentsMargins(0, 0, 10, 0)
 | 
			
		||||
        self.layout.addWidget(self.toolbar)
 | 
			
		||||
 | 
			
		||||
        self.load_ui_states()
 | 
			
		||||
 | 
			
		||||
    def load_ui_states(self):
 | 
			
		||||
        self.snap.setChecked(cmds.optionVar(query=SNAP_ITEMS))
 | 
			
		||||
        value = str(cmds.optionVar(query=SNAP_GRID_X))
 | 
			
		||||
        self.snapx.setText(value)
 | 
			
		||||
        value = str(cmds.optionVar(query=SNAP_GRID_Y))
 | 
			
		||||
        self.snapy.setText(value)
 | 
			
		||||
        self.lock_bg.setChecked(bool(cmds.optionVar(query=BG_LOCKED)))
 | 
			
		||||
 | 
			
		||||
    def save_ui_states(self):
 | 
			
		||||
        save_optionvar(BG_LOCKED, int(self.lock_bg.isChecked()))
 | 
			
		||||
        save_optionvar(SNAP_ITEMS, int(self.snap.isChecked()))
 | 
			
		||||
        save_optionvar(SNAP_GRID_X, int(self.snapx.text()))
 | 
			
		||||
        save_optionvar(SNAP_GRID_Y, int(self.snapy.text()))
 | 
			
		||||
 | 
			
		||||
    def size_changed(self, *_):
 | 
			
		||||
        self.sizeChanged.emit()
 | 
			
		||||
 | 
			
		||||
    def edit_center_toggled(self):
 | 
			
		||||
        self.editCenterToggled.emit(self.editcenter.isChecked())
 | 
			
		||||
 | 
			
		||||
    def snap_toggled(self):
 | 
			
		||||
        self.useSnapToggled.emit(self.snap.isChecked())
 | 
			
		||||
        self.save_ui_states()
 | 
			
		||||
 | 
			
		||||
    def snap_values(self):
 | 
			
		||||
        x = int(self.snapx.text()) if self.snapx.text() else 1
 | 
			
		||||
        y = int(self.snapy.text()) if self.snapy.text() else 1
 | 
			
		||||
        x = x if x > 0 else 1
 | 
			
		||||
        y = y if y > 0 else 1
 | 
			
		||||
        return x, y
 | 
			
		||||
 | 
			
		||||
    def snap_value_changed(self, _):
 | 
			
		||||
        self.snapValuesChanged.emit()
 | 
			
		||||
        self.save_ui_states()
 | 
			
		||||
 | 
			
		||||
    def set_size_values(self, width, height):
 | 
			
		||||
        self.picker_width.setText(str(width))
 | 
			
		||||
        self.picker_height.setText(str(height))
 | 
			
		||||
        self.sizeChanged.emit()
 | 
			
		||||
 | 
			
		||||
    def get_size(self):
 | 
			
		||||
        width = int(self.picker_width.text()) if self.picker_width.text() else 1
 | 
			
		||||
        height = int(self.picker_height.text()) if self.picker_height.text() else 1
 | 
			
		||||
        return QtCore.QSize(width, height)
 | 
			
		||||
							
								
								
									
										445
									
								
								Scripts/Animation/dwpicker/dwpicker/dialog.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,445 @@
 | 
			
		||||
from functools import partial
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
from PySide2 import QtWidgets, QtCore, QtGui
 | 
			
		||||
from maya import cmds
 | 
			
		||||
 | 
			
		||||
from dwpicker.designer.highlighter import get_highlighter
 | 
			
		||||
from dwpicker.optionvar import (
 | 
			
		||||
    save_optionvar, CHECK_FOR_UPDATE,
 | 
			
		||||
    SEARCH_FIELD_INDEX, LAST_IMAGE_DIRECTORY_USED, SETTINGS_GROUP_TO_COPY,
 | 
			
		||||
    SHAPES_FILTER_INDEX, SETTINGS_TO_COPY)
 | 
			
		||||
from dwpicker.languages import MEL, PYTHON
 | 
			
		||||
from dwpicker.path import get_image_directory
 | 
			
		||||
from dwpicker.namespace import selected_namespace
 | 
			
		||||
from dwpicker.templates import BUTTON
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
SEARCH_AND_REPLACE_FIELDS = 'Targets', 'Label', 'Image path', 'Command'
 | 
			
		||||
SHAPES_FILTERS = 'All shapes', 'Selected shapes'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def warning(title, message, parent=None):
 | 
			
		||||
    return QtWidgets.QMessageBox.warning(
 | 
			
		||||
        parent,
 | 
			
		||||
        title,
 | 
			
		||||
        message,
 | 
			
		||||
        QtWidgets.QMessageBox.Ok,
 | 
			
		||||
        QtWidgets.QMessageBox.Ok)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def question(title, message, buttons=None, parent=None):
 | 
			
		||||
    buttons = buttons or QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel
 | 
			
		||||
    result = QtWidgets.QMessageBox.question(
 | 
			
		||||
        parent, title, message, buttons, QtWidgets.QMessageBox.Ok)
 | 
			
		||||
    return result == QtWidgets.QMessageBox.Ok
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_image_path(parent=None):
 | 
			
		||||
    filename = QtWidgets.QFileDialog.getOpenFileName(
 | 
			
		||||
        parent, "Repath image...",
 | 
			
		||||
        get_image_directory(),
 | 
			
		||||
        filter="Images (*.jpg *.gif *.png *.tga)")[0]
 | 
			
		||||
    if not filename:
 | 
			
		||||
        return None
 | 
			
		||||
    directory = os.path.dirname(filename)
 | 
			
		||||
    save_optionvar(LAST_IMAGE_DIRECTORY_USED, directory)
 | 
			
		||||
    return filename
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NamespaceDialog(QtWidgets.QDialog):
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(NamespaceDialog, self).__init__(parent=parent)
 | 
			
		||||
        self.setWindowTitle('Select namespace ...')
 | 
			
		||||
        self.namespace_combo = QtWidgets.QComboBox()
 | 
			
		||||
        self.namespace_combo.setEditable(True)
 | 
			
		||||
        namespaces = [':'] + cmds.namespaceInfo(
 | 
			
		||||
            listOnlyNamespaces=True, recurse=True)
 | 
			
		||||
        self.namespace_combo.addItems(namespaces)
 | 
			
		||||
        self.namespace_combo.setCurrentText(selected_namespace())
 | 
			
		||||
 | 
			
		||||
        self.detect_selection = QtWidgets.QPushButton('Detect from selection')
 | 
			
		||||
        self.detect_selection.released.connect(self.call_detect_selection)
 | 
			
		||||
        self.ok = QtWidgets.QPushButton('Ok')
 | 
			
		||||
        self.ok.released.connect(self.accept)
 | 
			
		||||
        self.cancel = QtWidgets.QPushButton('Cancel')
 | 
			
		||||
        self.cancel.released.connect(self.reject)
 | 
			
		||||
 | 
			
		||||
        self.button_layout = QtWidgets.QHBoxLayout()
 | 
			
		||||
        self.button_layout.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        self.button_layout.addStretch(1)
 | 
			
		||||
        self.button_layout.addWidget(self.detect_selection)
 | 
			
		||||
        self.button_layout.addSpacing(16)
 | 
			
		||||
        self.button_layout.addWidget(self.ok)
 | 
			
		||||
        self.button_layout.addWidget(self.cancel)
 | 
			
		||||
 | 
			
		||||
        self.layout = QtWidgets.QVBoxLayout(self)
 | 
			
		||||
        self.layout.addWidget(self.namespace_combo)
 | 
			
		||||
        self.layout.addLayout(self.button_layout)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def namespace(self):
 | 
			
		||||
        return self.namespace_combo.currentText()
 | 
			
		||||
 | 
			
		||||
    def call_detect_selection(self):
 | 
			
		||||
        self.namespace_combo.setCurrentText(selected_namespace())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SettingsPaster(QtWidgets.QDialog):
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(SettingsPaster, self).__init__(parent)
 | 
			
		||||
        self.setWindowTitle('Paste settings')
 | 
			
		||||
        self.groups = {}
 | 
			
		||||
        self.categories = {}
 | 
			
		||||
        enable_settings = cmds.optionVar(query=SETTINGS_TO_COPY).split(';')
 | 
			
		||||
        for setting in sorted(BUTTON.keys()):
 | 
			
		||||
            text = ' '.join(setting.split('.')[1:]).capitalize()
 | 
			
		||||
            checkbox = QtWidgets.QCheckBox(text or setting.capitalize())
 | 
			
		||||
            checkbox.setting = setting
 | 
			
		||||
            checkbox.setChecked(setting in enable_settings)
 | 
			
		||||
            checkbox.stateChanged.connect(self.updated)
 | 
			
		||||
            name = setting.split('.')[0]
 | 
			
		||||
            self.categories.setdefault(name, []).append(checkbox)
 | 
			
		||||
        enable_groups = cmds.optionVar(query=SETTINGS_GROUP_TO_COPY).split(';')
 | 
			
		||||
 | 
			
		||||
        groups_layout = QtWidgets.QVBoxLayout()
 | 
			
		||||
        self.group_layouts = QtWidgets.QHBoxLayout()
 | 
			
		||||
        checkboxes_count = 0
 | 
			
		||||
        for category, checkboxes in self.categories.items():
 | 
			
		||||
            if checkboxes_count > 12:
 | 
			
		||||
                checkboxes_count = 0
 | 
			
		||||
                groups_layout.addStretch(1)
 | 
			
		||||
                self.group_layouts.addLayout(groups_layout)
 | 
			
		||||
                groups_layout = QtWidgets.QVBoxLayout()
 | 
			
		||||
            group = QtWidgets.QGroupBox(category)
 | 
			
		||||
            group.setCheckable(True)
 | 
			
		||||
            group.setChecked(category in enable_groups)
 | 
			
		||||
            group.toggled.connect(self.updated)
 | 
			
		||||
            group_layout = QtWidgets.QVBoxLayout(group)
 | 
			
		||||
            for checkbox in checkboxes:
 | 
			
		||||
                group_layout.addWidget(checkbox)
 | 
			
		||||
            self.groups[category] = group
 | 
			
		||||
            groups_layout.addWidget(group)
 | 
			
		||||
            checkboxes_count += len(checkboxes)
 | 
			
		||||
        groups_layout.addStretch(1)
 | 
			
		||||
        self.group_layouts.addLayout(groups_layout)
 | 
			
		||||
 | 
			
		||||
        self.paste = QtWidgets.QPushButton('Paste')
 | 
			
		||||
        self.paste.released.connect(self.accept)
 | 
			
		||||
        self.cancel = QtWidgets.QPushButton('Cancel')
 | 
			
		||||
        self.cancel.released.connect(self.reject)
 | 
			
		||||
        self.buttons_layout = QtWidgets.QHBoxLayout()
 | 
			
		||||
        self.buttons_layout.addStretch(1)
 | 
			
		||||
        self.buttons_layout.addWidget(self.paste)
 | 
			
		||||
        self.buttons_layout.addWidget(self.cancel)
 | 
			
		||||
 | 
			
		||||
        self.layout = QtWidgets.QVBoxLayout(self)
 | 
			
		||||
        self.layout.addLayout(self.group_layouts)
 | 
			
		||||
        self.layout.addLayout(self.buttons_layout)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def settings(self):
 | 
			
		||||
        return [
 | 
			
		||||
            cb.setting for category, checkboxes in self.categories.items()
 | 
			
		||||
            for cb in checkboxes if cb.isChecked() and
 | 
			
		||||
            self.groups[category].isChecked()]
 | 
			
		||||
 | 
			
		||||
    def updated(self, *_):
 | 
			
		||||
        cat = ';'.join([c for c, g in self.groups.items() if g.isChecked()])
 | 
			
		||||
        save_optionvar(SETTINGS_GROUP_TO_COPY, cat)
 | 
			
		||||
        save_optionvar(SETTINGS_TO_COPY, ';'.join(self.settings))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SearchAndReplaceDialog(QtWidgets.QDialog):
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(SearchAndReplaceDialog, self).__init__(parent=parent)
 | 
			
		||||
        self.setWindowTitle('Search and replace in shapes')
 | 
			
		||||
        self.sizeHint = lambda: QtCore.QSize(320, 80)
 | 
			
		||||
 | 
			
		||||
        self.filters = QtWidgets.QComboBox()
 | 
			
		||||
        self.filters.addItems(SHAPES_FILTERS)
 | 
			
		||||
        self.filters.setCurrentIndex(cmds.optionVar(query=SHAPES_FILTER_INDEX))
 | 
			
		||||
        function = partial(save_optionvar, SHAPES_FILTER_INDEX)
 | 
			
		||||
        self.filters.currentIndexChanged.connect(function)
 | 
			
		||||
        self.fields = QtWidgets.QComboBox()
 | 
			
		||||
        self.fields.addItems(SEARCH_AND_REPLACE_FIELDS)
 | 
			
		||||
        self.fields.setCurrentIndex(cmds.optionVar(query=SEARCH_FIELD_INDEX))
 | 
			
		||||
        function = partial(save_optionvar, SEARCH_FIELD_INDEX)
 | 
			
		||||
        self.fields.currentIndexChanged.connect(function)
 | 
			
		||||
        self.search = QtWidgets.QLineEdit()
 | 
			
		||||
        self.replace = QtWidgets.QLineEdit()
 | 
			
		||||
 | 
			
		||||
        self.ok = QtWidgets.QPushButton('Replace')
 | 
			
		||||
        self.ok.released.connect(self.accept)
 | 
			
		||||
        self.cancel = QtWidgets.QPushButton('Cancel')
 | 
			
		||||
        self.cancel.released.connect(self.reject)
 | 
			
		||||
 | 
			
		||||
        self.options = QtWidgets.QFormLayout()
 | 
			
		||||
        self.options.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        self.options.addRow('Apply on: ', self.filters)
 | 
			
		||||
        self.options.addRow('Field to search: ', self.fields)
 | 
			
		||||
        self.options.addRow('Search: ', self.search)
 | 
			
		||||
        self.options.addRow('Replace by: ', self.replace)
 | 
			
		||||
 | 
			
		||||
        self.button_layout = QtWidgets.QHBoxLayout()
 | 
			
		||||
        self.button_layout.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        self.button_layout.addStretch(1)
 | 
			
		||||
        self.button_layout.addWidget(self.ok)
 | 
			
		||||
        self.button_layout.addWidget(self.cancel)
 | 
			
		||||
 | 
			
		||||
        self.layout = QtWidgets.QVBoxLayout(self)
 | 
			
		||||
        self.layout.addLayout(self.options)
 | 
			
		||||
        self.layout.addLayout(self.button_layout)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def field(self):
 | 
			
		||||
        '''
 | 
			
		||||
        0 = Targets
 | 
			
		||||
        1 = Label
 | 
			
		||||
        2 = Command
 | 
			
		||||
        3 = Image path
 | 
			
		||||
        '''
 | 
			
		||||
        return self.fields.currentIndex()
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def filter(self):
 | 
			
		||||
        '''
 | 
			
		||||
        0 = Apply on all shapes
 | 
			
		||||
        1 = Apply on selected shapes
 | 
			
		||||
        '''
 | 
			
		||||
        return self.filters.currentIndex()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MissingImages(QtWidgets.QDialog):
 | 
			
		||||
    def __init__(self, paths, parent=None):
 | 
			
		||||
        super(MissingImages, self).__init__(parent)
 | 
			
		||||
        self.setWindowTitle('Missing images')
 | 
			
		||||
        self.model = PathModel(paths)
 | 
			
		||||
        self.paths = QtWidgets.QTableView()
 | 
			
		||||
        self.paths.setAlternatingRowColors(True)
 | 
			
		||||
        self.paths.setShowGrid(False)
 | 
			
		||||
        self.paths.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
 | 
			
		||||
        mode = QtWidgets.QHeaderView.ResizeToContents
 | 
			
		||||
        self.paths.verticalHeader().resizeSections(mode)
 | 
			
		||||
        self.paths.verticalHeader().hide()
 | 
			
		||||
        self.paths.horizontalHeader().show()
 | 
			
		||||
        self.paths.horizontalHeader().resizeSections(mode)
 | 
			
		||||
        self.paths.horizontalHeader().setStretchLastSection(True)
 | 
			
		||||
        mode = QtWidgets.QAbstractItemView.ScrollPerPixel
 | 
			
		||||
        self.paths.setHorizontalScrollMode(mode)
 | 
			
		||||
        self.paths.setVerticalScrollMode(mode)
 | 
			
		||||
        self.paths.setModel(self.model)
 | 
			
		||||
 | 
			
		||||
        self.browse = QtWidgets.QPushButton('B')
 | 
			
		||||
        self.browse.setFixedWidth(30)
 | 
			
		||||
        self.browse.released.connect(self.call_browse)
 | 
			
		||||
        self.update = QtWidgets.QPushButton('Update')
 | 
			
		||||
        self.update.released.connect(self.accept)
 | 
			
		||||
        self.skip = QtWidgets.QPushButton('Skip')
 | 
			
		||||
        self.skip.released.connect(self.reject)
 | 
			
		||||
        self.validators = QtWidgets.QHBoxLayout()
 | 
			
		||||
        self.validators.addStretch(1)
 | 
			
		||||
        self.validators.addWidget(self.browse)
 | 
			
		||||
        self.validators.addWidget(self.update)
 | 
			
		||||
        self.validators.addWidget(self.skip)
 | 
			
		||||
 | 
			
		||||
        self.layout = QtWidgets.QVBoxLayout(self)
 | 
			
		||||
        self.layout.addWidget(self.paths)
 | 
			
		||||
        self.layout.addLayout(self.validators)
 | 
			
		||||
 | 
			
		||||
    def output(self, path):
 | 
			
		||||
        for p, output in zip(self.model.paths, self.model.outputs):
 | 
			
		||||
            if p == path:
 | 
			
		||||
                return output
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def outputs(self):
 | 
			
		||||
        return self.model.outputs
 | 
			
		||||
 | 
			
		||||
    def resizeEvent(self, _):
 | 
			
		||||
        mode = QtWidgets.QHeaderView.ResizeToContents
 | 
			
		||||
        self.paths.verticalHeader().resizeSections(mode)
 | 
			
		||||
        self.paths.horizontalHeader().resizeSections(mode)
 | 
			
		||||
 | 
			
		||||
    def call_browse(self):
 | 
			
		||||
        directory = QtWidgets.QFileDialog.getExistingDirectory(
 | 
			
		||||
            self, "Select image folder")
 | 
			
		||||
        if not directory:
 | 
			
		||||
            return
 | 
			
		||||
        filenames = os.listdir(directory)
 | 
			
		||||
        self.model.layoutAboutToBeChanged.emit()
 | 
			
		||||
        for i, path in enumerate(self.model.paths):
 | 
			
		||||
            filename = os.path.basename(path)
 | 
			
		||||
            if filename in filenames:
 | 
			
		||||
                filepath = os.path.join(directory, filename)
 | 
			
		||||
                self.model.outputs[i] = filepath
 | 
			
		||||
        self.model.layoutChanged.emit()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PathModel(QtCore.QAbstractTableModel):
 | 
			
		||||
    HEADERS = 'filename', 'directory'
 | 
			
		||||
 | 
			
		||||
    def __init__(self, paths, parent=None):
 | 
			
		||||
        super(PathModel, self).__init__(parent)
 | 
			
		||||
        self.paths = paths
 | 
			
		||||
        self.outputs = paths[:]
 | 
			
		||||
 | 
			
		||||
    def rowCount(self, *_):
 | 
			
		||||
        return len(self.paths)
 | 
			
		||||
 | 
			
		||||
    def columnCount(self, *_):
 | 
			
		||||
        return 2
 | 
			
		||||
 | 
			
		||||
    def flags(self, index):
 | 
			
		||||
        flags = super(PathModel, self).flags(index)
 | 
			
		||||
        if index.column() == 1:
 | 
			
		||||
            flags |= QtCore.Qt.ItemIsEditable
 | 
			
		||||
        return flags
 | 
			
		||||
 | 
			
		||||
    def headerData(self, position, orientation, role):
 | 
			
		||||
        if orientation != QtCore.Qt.Horizontal:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        if role != QtCore.Qt.DisplayRole:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        return self.HEADERS[position]
 | 
			
		||||
 | 
			
		||||
    def data(self, index, role):
 | 
			
		||||
        if not index.isValid():
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        row, col = index.row(), index.column()
 | 
			
		||||
        if role == QtCore.Qt.DisplayRole:
 | 
			
		||||
            if col == 0:
 | 
			
		||||
                return os.path.basename(self.outputs[row])
 | 
			
		||||
            if col == 1:
 | 
			
		||||
                return os.path.dirname(self.outputs[row])
 | 
			
		||||
 | 
			
		||||
        elif role == QtCore.Qt.BackgroundColorRole:
 | 
			
		||||
            if not os.path.exists(self.outputs[row]):
 | 
			
		||||
                return QtGui.QColor(QtCore.Qt.darkRed)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UpdateAvailableDialog(QtWidgets.QDialog):
 | 
			
		||||
    def __init__(self, version, parent=None):
 | 
			
		||||
        super(UpdateAvailableDialog, self).__init__(parent=parent)
 | 
			
		||||
        self.setWindowTitle('Update available')
 | 
			
		||||
 | 
			
		||||
        # Widgets
 | 
			
		||||
        text = '\n    New DreamWall Picker version "{0}" is available !    \n'
 | 
			
		||||
        label = QtWidgets.QLabel(text.format(version))
 | 
			
		||||
 | 
			
		||||
        ok_btn = QtWidgets.QPushButton('Open GitHub page')
 | 
			
		||||
        ok_btn.released.connect(self.accept)
 | 
			
		||||
 | 
			
		||||
        cancel_btn = QtWidgets.QPushButton('Close')
 | 
			
		||||
        cancel_btn.released.connect(self.reject)
 | 
			
		||||
 | 
			
		||||
        self.check_cb = QtWidgets.QCheckBox('Check for update at startup')
 | 
			
		||||
        self.check_cb.stateChanged.connect(
 | 
			
		||||
            self.change_check_for_update_preference)
 | 
			
		||||
        self.check_cb.setChecked(cmds.optionVar(query=CHECK_FOR_UPDATE))
 | 
			
		||||
 | 
			
		||||
        # Layouts
 | 
			
		||||
        button_layout = QtWidgets.QHBoxLayout()
 | 
			
		||||
        button_layout.addStretch(1)
 | 
			
		||||
        button_layout.addWidget(ok_btn)
 | 
			
		||||
        button_layout.addWidget(cancel_btn)
 | 
			
		||||
 | 
			
		||||
        cb_layout = QtWidgets.QHBoxLayout()
 | 
			
		||||
        cb_layout.addStretch(1)
 | 
			
		||||
        cb_layout.addWidget(self.check_cb)
 | 
			
		||||
 | 
			
		||||
        layout = QtWidgets.QVBoxLayout(self)
 | 
			
		||||
        layout.addWidget(label)
 | 
			
		||||
        layout.addLayout(cb_layout)
 | 
			
		||||
        layout.addLayout(button_layout)
 | 
			
		||||
 | 
			
		||||
    def change_check_for_update_preference(self):
 | 
			
		||||
        save_optionvar(CHECK_FOR_UPDATE, int(self.check_cb.isChecked()))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CommandEditorDialog(QtWidgets.QDialog):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, command, parent=None):
 | 
			
		||||
        super(CommandEditorDialog, self).__init__(parent)
 | 
			
		||||
        self.setWindowTitle('Edit/Create command')
 | 
			
		||||
        self.languages = QtWidgets.QComboBox()
 | 
			
		||||
        self.languages.addItems([MEL, PYTHON])
 | 
			
		||||
        self.languages.setCurrentText(command['language'])
 | 
			
		||||
        self.languages.currentIndexChanged.connect(self.language_changed)
 | 
			
		||||
 | 
			
		||||
        self.button = QtWidgets.QComboBox()
 | 
			
		||||
        self.button.addItems(['left', 'right'])
 | 
			
		||||
        self.button.setCurrentText(command['button'])
 | 
			
		||||
 | 
			
		||||
        self.enabled = QtWidgets.QCheckBox('Enabled')
 | 
			
		||||
        self.enabled.setChecked(command['enabled'])
 | 
			
		||||
 | 
			
		||||
        self.ctrl = QtWidgets.QCheckBox('Ctrl')
 | 
			
		||||
        self.ctrl.setChecked(command['ctrl'])
 | 
			
		||||
        self.shift = QtWidgets.QCheckBox('Shift')
 | 
			
		||||
        self.shift.setChecked(command['shift'])
 | 
			
		||||
        self.eval_deferred = QtWidgets.QCheckBox('Eval deferred (python only)')
 | 
			
		||||
        self.eval_deferred.setChecked(command['deferred'])
 | 
			
		||||
        self.unique_undo = QtWidgets.QCheckBox('Unique undo')
 | 
			
		||||
        self.unique_undo.setChecked(command['force_compact_undo'])
 | 
			
		||||
 | 
			
		||||
        self.command = QtWidgets.QTextEdit()
 | 
			
		||||
        self.command.setFixedHeight(100)
 | 
			
		||||
        self.command.setPlainText(command['command'])
 | 
			
		||||
 | 
			
		||||
        self.ok = QtWidgets.QPushButton('Ok')
 | 
			
		||||
        self.ok.released.connect(self.accept)
 | 
			
		||||
        self.cancel = QtWidgets.QPushButton('Cancel')
 | 
			
		||||
        self.cancel.released.connect(self.reject)
 | 
			
		||||
 | 
			
		||||
        form = QtWidgets.QFormLayout()
 | 
			
		||||
        form.setSpacing(0)
 | 
			
		||||
        form.addRow('Language', self.languages)
 | 
			
		||||
        form.addRow('Mouse button', self.button)
 | 
			
		||||
 | 
			
		||||
        modifiers_group = QtWidgets.QGroupBox('Modifiers')
 | 
			
		||||
        modifiers_layout = QtWidgets.QVBoxLayout(modifiers_group)
 | 
			
		||||
        modifiers_layout.addWidget(self.ctrl)
 | 
			
		||||
        modifiers_layout.addWidget(self.shift)
 | 
			
		||||
 | 
			
		||||
        options_group = QtWidgets.QGroupBox('Options')
 | 
			
		||||
        options_layout = QtWidgets.QVBoxLayout(options_group)
 | 
			
		||||
        options_layout.addWidget(self.eval_deferred)
 | 
			
		||||
        options_layout.addWidget(self.unique_undo)
 | 
			
		||||
        options_layout.addLayout(form)
 | 
			
		||||
 | 
			
		||||
        code = QtWidgets.QGroupBox('Code')
 | 
			
		||||
        code_layout = QtWidgets.QVBoxLayout(code)
 | 
			
		||||
        code_layout.setSpacing(0)
 | 
			
		||||
        code_layout.addWidget(self.command)
 | 
			
		||||
 | 
			
		||||
        buttons_layout = QtWidgets.QHBoxLayout()
 | 
			
		||||
        buttons_layout.addStretch(1)
 | 
			
		||||
        buttons_layout.addWidget(self.ok)
 | 
			
		||||
        buttons_layout.addWidget(self.cancel)
 | 
			
		||||
 | 
			
		||||
        layout = QtWidgets.QVBoxLayout(self)
 | 
			
		||||
        layout.addWidget(options_group)
 | 
			
		||||
        layout.addWidget(modifiers_group)
 | 
			
		||||
        layout.addWidget(code)
 | 
			
		||||
        layout.addLayout(buttons_layout)
 | 
			
		||||
        self.language_changed()
 | 
			
		||||
 | 
			
		||||
    def language_changed(self, *_):
 | 
			
		||||
        language = self.languages.currentText()
 | 
			
		||||
        highlighter = get_highlighter(language)
 | 
			
		||||
        highlighter(self.command.document())
 | 
			
		||||
 | 
			
		||||
    def command_data(self):
 | 
			
		||||
        return {
 | 
			
		||||
            'enabled': self.enabled.isChecked(),
 | 
			
		||||
            'button': self.button.currentText(),
 | 
			
		||||
            'language': self.languages.currentText(),
 | 
			
		||||
            'command': self.command.toPlainText(),
 | 
			
		||||
            'ctrl': self.ctrl.isChecked(),
 | 
			
		||||
            'shift': self.shift.isChecked(),
 | 
			
		||||
            'deferred': self.eval_deferred.isChecked(),
 | 
			
		||||
            'force_compact_undo': self.unique_undo.isChecked()}
 | 
			
		||||
							
								
								
									
										549
									
								
								Scripts/Animation/dwpicker/dwpicker/geometry.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,549 @@
 | 
			
		||||
import math
 | 
			
		||||
from PySide2 import QtCore
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
POINT_RADIUS = 8
 | 
			
		||||
POINT_OFFSET = 4
 | 
			
		||||
DIRECTIONS = [
 | 
			
		||||
    'top_left',
 | 
			
		||||
    'bottom_left',
 | 
			
		||||
    'top_right',
 | 
			
		||||
    'bottom_right',
 | 
			
		||||
    'left',
 | 
			
		||||
    'right',
 | 
			
		||||
    'top',
 | 
			
		||||
    'bottom']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ViewportMapper():
 | 
			
		||||
    """
 | 
			
		||||
    Used to translate/map between:
 | 
			
		||||
        - abstract/data/units coordinates
 | 
			
		||||
        - viewport/display/pixels coordinates
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.zoom = 1
 | 
			
		||||
        self.origin = QtCore.QPointF(0, 0)
 | 
			
		||||
        # We need the viewport size to be able to center the view or to
 | 
			
		||||
        # automatically set zoom from selection:
 | 
			
		||||
        self.viewsize = QtCore.QSize(300, 300)
 | 
			
		||||
 | 
			
		||||
    def to_viewport(self, value):
 | 
			
		||||
        return value * self.zoom
 | 
			
		||||
 | 
			
		||||
    def to_units(self, pixels):
 | 
			
		||||
        return pixels / self.zoom
 | 
			
		||||
 | 
			
		||||
    def to_viewport_coords(self, units_point):
 | 
			
		||||
        return QtCore.QPointF(
 | 
			
		||||
            self.to_viewport(units_point.x()) - self.origin.x(),
 | 
			
		||||
            self.to_viewport(units_point.y()) - self.origin.y())
 | 
			
		||||
 | 
			
		||||
    def to_units_coords(self, pixels_point):
 | 
			
		||||
        return QtCore.QPointF(
 | 
			
		||||
            self.to_units(pixels_point.x() + self.origin.x()),
 | 
			
		||||
            self.to_units(pixels_point.y() + self.origin.y()))
 | 
			
		||||
 | 
			
		||||
    def to_viewport_rect(self, units_rect):
 | 
			
		||||
        return QtCore.QRectF(
 | 
			
		||||
            (units_rect.left() * self.zoom) - self.origin.x(),
 | 
			
		||||
            (units_rect.top() * self.zoom) - self.origin.y(),
 | 
			
		||||
            units_rect.width() * self.zoom,
 | 
			
		||||
            units_rect.height() * self.zoom)
 | 
			
		||||
 | 
			
		||||
    def to_units_rect(self, pixels_rect):
 | 
			
		||||
        top_left = self.to_units_coords(pixels_rect.topLeft())
 | 
			
		||||
        width = self.to_units(pixels_rect.width())
 | 
			
		||||
        height = self.to_units(pixels_rect.height())
 | 
			
		||||
        return QtCore.QRectF(top_left.x(), top_left.y(), width, height)
 | 
			
		||||
 | 
			
		||||
    def zoomin(self, factor=10.0):
 | 
			
		||||
        self.zoom += self.zoom * factor
 | 
			
		||||
        self.zoom = min(self.zoom, 5.0)
 | 
			
		||||
 | 
			
		||||
    def zoomout(self, factor=10.0):
 | 
			
		||||
        self.zoom -= self.zoom * factor
 | 
			
		||||
        self.zoom = max(self.zoom, .1)
 | 
			
		||||
 | 
			
		||||
    def center_on_point(self, units_center):
 | 
			
		||||
        """Given current zoom and viewport size, set the origin point."""
 | 
			
		||||
        self.origin = QtCore.QPointF(
 | 
			
		||||
            units_center.x() * self.zoom - self.viewsize.width() / 2,
 | 
			
		||||
            units_center.y() * self.zoom - self.viewsize.height() / 2)
 | 
			
		||||
 | 
			
		||||
    def focus(self, units_rect):
 | 
			
		||||
        self.zoom = min([
 | 
			
		||||
            float(self.viewsize.width()) / units_rect.width(),
 | 
			
		||||
            float(self.viewsize.height()) / units_rect.height()])
 | 
			
		||||
        if self.zoom > 1:
 | 
			
		||||
            self.zoom *= 0.7  # lower zoom to add some breathing space
 | 
			
		||||
        self.zoom = max(self.zoom, .1)
 | 
			
		||||
        self.center_on_point(units_rect.center())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_topleft_rect(rect):
 | 
			
		||||
    """
 | 
			
		||||
    this function return a manipulator rect for the transform
 | 
			
		||||
    handler.
 | 
			
		||||
      *__________________________
 | 
			
		||||
       |                        |
 | 
			
		||||
       |                        |
 | 
			
		||||
       |________________________|
 | 
			
		||||
    """
 | 
			
		||||
    if rect is None:
 | 
			
		||||
        return None
 | 
			
		||||
    point = rect.topLeft()
 | 
			
		||||
    return QtCore.QRectF(
 | 
			
		||||
        point.x() - (POINT_RADIUS / 2.0) - POINT_OFFSET,
 | 
			
		||||
        point.y() - (POINT_RADIUS / 2.0) - POINT_OFFSET,
 | 
			
		||||
        POINT_RADIUS, POINT_RADIUS)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_bottomleft_rect(rect):
 | 
			
		||||
    """
 | 
			
		||||
    this function return a manipulator rect for the transform
 | 
			
		||||
    handler.
 | 
			
		||||
       __________________________
 | 
			
		||||
       |                        |
 | 
			
		||||
       |                        |
 | 
			
		||||
       |________________________|
 | 
			
		||||
      *
 | 
			
		||||
    """
 | 
			
		||||
    if rect is None:
 | 
			
		||||
        return None
 | 
			
		||||
    point = rect.bottomLeft()
 | 
			
		||||
    return QtCore.QRectF(
 | 
			
		||||
        point.x() - (POINT_RADIUS / 2.0) - POINT_OFFSET,
 | 
			
		||||
        point.y() + (POINT_RADIUS / 2.0) - POINT_OFFSET,
 | 
			
		||||
        POINT_RADIUS, POINT_RADIUS)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_topright_rect(rect):
 | 
			
		||||
    """
 | 
			
		||||
    this function return a manipulator rect for the transform
 | 
			
		||||
    handler.
 | 
			
		||||
       __________________________*
 | 
			
		||||
       |                        |
 | 
			
		||||
       |                        |
 | 
			
		||||
       |________________________|
 | 
			
		||||
    """
 | 
			
		||||
    if rect is None:
 | 
			
		||||
        return None
 | 
			
		||||
    point = rect.topRight()
 | 
			
		||||
    return QtCore.QRectF(
 | 
			
		||||
        point.x() + (POINT_RADIUS / 2.0) - POINT_OFFSET,
 | 
			
		||||
        point.y() - (POINT_RADIUS / 2.0) - POINT_OFFSET,
 | 
			
		||||
        POINT_RADIUS, POINT_RADIUS)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_bottomright_rect(rect):
 | 
			
		||||
    """
 | 
			
		||||
    this function return a manipulator rect for the transform
 | 
			
		||||
    handler.
 | 
			
		||||
       __________________________
 | 
			
		||||
       |                        |
 | 
			
		||||
       |                        |
 | 
			
		||||
       |________________________|
 | 
			
		||||
                                 *
 | 
			
		||||
    """
 | 
			
		||||
    if rect is None:
 | 
			
		||||
        return None
 | 
			
		||||
    point = rect.bottomRight()
 | 
			
		||||
    return QtCore.QRectF(
 | 
			
		||||
        point.x() + (POINT_RADIUS / 2.0) - POINT_OFFSET,
 | 
			
		||||
        point.y() + (POINT_RADIUS / 2.0) - POINT_OFFSET,
 | 
			
		||||
        POINT_RADIUS, POINT_RADIUS)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_left_side_rect(rect):
 | 
			
		||||
    """
 | 
			
		||||
    this function return a manipulator rect for the transform
 | 
			
		||||
    handler.
 | 
			
		||||
       __________________________
 | 
			
		||||
       |                        |
 | 
			
		||||
      *|                        |
 | 
			
		||||
       |________________________|
 | 
			
		||||
    """
 | 
			
		||||
    if rect is None:
 | 
			
		||||
        return None
 | 
			
		||||
    top = rect.top() + (rect.height() / 2.0)
 | 
			
		||||
    return QtCore.QRectF(
 | 
			
		||||
        rect.left() - (POINT_RADIUS / 2.0) - POINT_OFFSET,
 | 
			
		||||
        top - (POINT_RADIUS / 2.0),
 | 
			
		||||
        POINT_RADIUS, POINT_RADIUS)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_right_side_rect(rect):
 | 
			
		||||
    """
 | 
			
		||||
    this function return a manipulator rect for the transform
 | 
			
		||||
    handler.
 | 
			
		||||
       __________________________
 | 
			
		||||
       |                        |
 | 
			
		||||
       |                        |*
 | 
			
		||||
       |________________________|
 | 
			
		||||
    """
 | 
			
		||||
    if rect is None:
 | 
			
		||||
        return None
 | 
			
		||||
    top = rect.top() + (rect.height() / 2.0)
 | 
			
		||||
    return QtCore.QRectF(
 | 
			
		||||
        rect.right() + (POINT_RADIUS / 2.0) - POINT_OFFSET,
 | 
			
		||||
        top - (POINT_RADIUS / 2.0),
 | 
			
		||||
        POINT_RADIUS, POINT_RADIUS)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_top_side_rect(rect):
 | 
			
		||||
    """
 | 
			
		||||
    this function return a manipulator rect for the transform
 | 
			
		||||
    handler.
 | 
			
		||||
       _____________*____________
 | 
			
		||||
       |                        |
 | 
			
		||||
       |                        |
 | 
			
		||||
       |________________________|
 | 
			
		||||
    """
 | 
			
		||||
    if rect is None:
 | 
			
		||||
        return None
 | 
			
		||||
    return QtCore.QRectF(
 | 
			
		||||
        rect.left() + (rect.width() / 2.0) - (POINT_RADIUS / 2.0),
 | 
			
		||||
        rect.top() - (POINT_RADIUS / 2.0) - POINT_OFFSET,
 | 
			
		||||
        POINT_RADIUS, POINT_RADIUS)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_bottom_side_rect(rect):
 | 
			
		||||
    """
 | 
			
		||||
    this function return a manipulator rect for the transform
 | 
			
		||||
    handler.
 | 
			
		||||
       __________________________
 | 
			
		||||
       |                        |
 | 
			
		||||
       |                        |
 | 
			
		||||
       |________________________|
 | 
			
		||||
                    *
 | 
			
		||||
    """
 | 
			
		||||
    if rect is None:
 | 
			
		||||
        return None
 | 
			
		||||
    return QtCore.QRectF(
 | 
			
		||||
        rect.left() + (rect.width() / 2.0) - (POINT_RADIUS / 2.0),
 | 
			
		||||
        rect.bottom() + (POINT_RADIUS / 2.0) - POINT_OFFSET,
 | 
			
		||||
        POINT_RADIUS, POINT_RADIUS)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def grow_rect(rect, value):
 | 
			
		||||
    if rect is None:
 | 
			
		||||
        return None
 | 
			
		||||
    return QtCore.QRectF(
 | 
			
		||||
        rect.left() - value,
 | 
			
		||||
        rect.top() - value,
 | 
			
		||||
        rect.width() + (value * 2),
 | 
			
		||||
        rect.height() + (value * 2))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def relative(value, in_min, in_max, out_min, out_max):
 | 
			
		||||
    """
 | 
			
		||||
    this function resolve simple equation and return the unknown value
 | 
			
		||||
    in between two values.
 | 
			
		||||
    a, a" = in_min, out_min
 | 
			
		||||
    b, b " = out_max, out_max
 | 
			
		||||
    c = value
 | 
			
		||||
    ? is the unknown processed by function.
 | 
			
		||||
    a --------- c --------- b
 | 
			
		||||
    a" --------------- ? ---------------- b"
 | 
			
		||||
    """
 | 
			
		||||
    factor = float((value - in_min)) / (in_max - in_min)
 | 
			
		||||
    width = out_max - out_min
 | 
			
		||||
    return out_min + (width * (factor))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def distance(a, b):
 | 
			
		||||
    """ return distance between two points """
 | 
			
		||||
    x = (b.x() - a.x())**2
 | 
			
		||||
    y = (b.y() - a.y())**2
 | 
			
		||||
    return math.sqrt(abs(x + y))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_relative_point(rect, point):
 | 
			
		||||
    x = point.x() - rect.left()
 | 
			
		||||
    y = point.y() - rect.top()
 | 
			
		||||
    return QtCore.QPoint(x, y)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_quarter(a, b, c):
 | 
			
		||||
    quarter = None
 | 
			
		||||
    if b.y() <= a.y() and b.x() < c.x():
 | 
			
		||||
        quarter = 0
 | 
			
		||||
    elif b.y() < a.y() and b.x() >= c.x():
 | 
			
		||||
        quarter = 1
 | 
			
		||||
    elif b.y() >= a.y() and b.x() > c.x():
 | 
			
		||||
        quarter = 2
 | 
			
		||||
    elif b.y() >= a.y() and b.x() <= c.x():
 | 
			
		||||
        quarter = 3
 | 
			
		||||
    return quarter
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_point_on_line(angle, ray):
 | 
			
		||||
    x = 50 + ray * math.cos(float(angle))
 | 
			
		||||
    y = 50 + ray * math.sin(float(angle))
 | 
			
		||||
    return QtCore.QPoint(x, y)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_angle_c(a, b, c):
 | 
			
		||||
    return math.degrees(math.atan(distance(a, b) / distance(a, c)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_absolute_angle_c(a, b, c):
 | 
			
		||||
    quarter = get_quarter(a, b, c)
 | 
			
		||||
    try:
 | 
			
		||||
        angle_c = get_angle_c(a, b, c)
 | 
			
		||||
    except ZeroDivisionError:
 | 
			
		||||
        return 360 - (90 * quarter)
 | 
			
		||||
 | 
			
		||||
    if quarter == 0:
 | 
			
		||||
        return round(180.0 + angle_c, 1)
 | 
			
		||||
    elif quarter == 1:
 | 
			
		||||
        return round(270.0 + (90 - angle_c), 1)
 | 
			
		||||
    elif quarter == 2:
 | 
			
		||||
        return round(angle_c, 1)
 | 
			
		||||
    elif quarter == 3:
 | 
			
		||||
        return math.fabs(round(90.0 + (90 - angle_c), 1))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def proportional_rect(rect, percent=None):
 | 
			
		||||
    """ return a scaled rect with a percentage """
 | 
			
		||||
    factor = float(percent) / 100
 | 
			
		||||
    width = rect.width() * factor
 | 
			
		||||
    height = rect.height() * factor
 | 
			
		||||
    left = rect.left() + round((rect.width() - width) / 2)
 | 
			
		||||
    top = rect.top() + round((rect.height() - height) / 2)
 | 
			
		||||
    return QtCore.QRect(left, top, width, height)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def resize_rect_with_reference(rect, in_reference_rect, out_reference_rect):
 | 
			
		||||
    """
 | 
			
		||||
    __________________________________  B
 | 
			
		||||
    |    ________________  A         |
 | 
			
		||||
    |    |               |           |
 | 
			
		||||
    |    |_______________|           |
 | 
			
		||||
    |                                |
 | 
			
		||||
    |________________________________|
 | 
			
		||||
    __________________________  C
 | 
			
		||||
    |    ?                   |
 | 
			
		||||
    |                        |
 | 
			
		||||
    |________________________|
 | 
			
		||||
    A = rect given
 | 
			
		||||
    B = in_reference_rect
 | 
			
		||||
    C = out_reference_rect
 | 
			
		||||
    the function process the fourth rect,
 | 
			
		||||
    it scale the A rect using the B, C scales as reference
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    left = relative(
 | 
			
		||||
        value=rect.left(),
 | 
			
		||||
        in_min=in_reference_rect.left(),
 | 
			
		||||
        in_max=in_reference_rect.right(),
 | 
			
		||||
        out_min=out_reference_rect.left(),
 | 
			
		||||
        out_max=out_reference_rect.right())
 | 
			
		||||
    top = relative(
 | 
			
		||||
        value=rect.top(),
 | 
			
		||||
        in_min=in_reference_rect.top(),
 | 
			
		||||
        in_max=in_reference_rect.bottom(),
 | 
			
		||||
        out_min=out_reference_rect.top(),
 | 
			
		||||
        out_max=out_reference_rect.bottom())
 | 
			
		||||
    right = relative(
 | 
			
		||||
        value=rect.right(),
 | 
			
		||||
        in_min=in_reference_rect.left(),
 | 
			
		||||
        in_max=in_reference_rect.right(),
 | 
			
		||||
        out_min=out_reference_rect.left(),
 | 
			
		||||
        out_max=out_reference_rect.right())
 | 
			
		||||
    bottom = relative(
 | 
			
		||||
        value=rect.bottom(),
 | 
			
		||||
        in_min=in_reference_rect.top(),
 | 
			
		||||
        in_max=in_reference_rect.bottom(),
 | 
			
		||||
        out_min=out_reference_rect.top(),
 | 
			
		||||
        out_max=out_reference_rect.bottom())
 | 
			
		||||
    rect.setCoords(left, top, right, bottom)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def resize_rect_with_direction(rect, cursor, direction, force_square=False):
 | 
			
		||||
    if direction == 'top_left':
 | 
			
		||||
        if cursor.x() < rect.right() and cursor.y() < rect.bottom():
 | 
			
		||||
            rect.setTopLeft(cursor)
 | 
			
		||||
            if force_square:
 | 
			
		||||
                left = rect.right() - rect.height()
 | 
			
		||||
                rect.setLeft(left)
 | 
			
		||||
 | 
			
		||||
    elif direction == 'bottom_left':
 | 
			
		||||
        if cursor.x() < rect.right() and cursor.y() > rect.top():
 | 
			
		||||
            rect.setBottomLeft(cursor)
 | 
			
		||||
            if force_square:
 | 
			
		||||
                rect.setHeight(rect.width())
 | 
			
		||||
 | 
			
		||||
    elif direction == 'top_right':
 | 
			
		||||
        if cursor.x() > rect.left() and cursor.y() < rect.bottom():
 | 
			
		||||
            rect.setTopRight(cursor)
 | 
			
		||||
            if force_square:
 | 
			
		||||
                rect.setWidth(rect.height())
 | 
			
		||||
 | 
			
		||||
    elif direction == 'bottom_right':
 | 
			
		||||
        if cursor.x() > rect.left() and cursor.y() > rect.top():
 | 
			
		||||
            rect.setBottomRight(cursor)
 | 
			
		||||
            if force_square:
 | 
			
		||||
                rect.setHeight(rect.width())
 | 
			
		||||
 | 
			
		||||
    elif direction == 'left':
 | 
			
		||||
        if cursor.x() < rect.right():
 | 
			
		||||
            rect.setLeft(cursor.x())
 | 
			
		||||
            if force_square:
 | 
			
		||||
                rect.setHeight(rect.width())
 | 
			
		||||
 | 
			
		||||
    elif direction == 'right':
 | 
			
		||||
        if cursor.x() > rect.left():
 | 
			
		||||
            rect.setRight(cursor.x())
 | 
			
		||||
            if force_square:
 | 
			
		||||
                rect.setHeight(rect.width())
 | 
			
		||||
 | 
			
		||||
    elif direction == 'top':
 | 
			
		||||
        if cursor.y() < rect.bottom():
 | 
			
		||||
            rect.setTop(cursor.y())
 | 
			
		||||
            if force_square:
 | 
			
		||||
                rect.setWidth(rect.height())
 | 
			
		||||
 | 
			
		||||
    elif direction == 'bottom':
 | 
			
		||||
        if cursor.y() > rect.top():
 | 
			
		||||
            rect.setBottom(cursor.y())
 | 
			
		||||
            if force_square:
 | 
			
		||||
                rect.setWidth(rect.height())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Transform:
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.snap = None
 | 
			
		||||
        self.direction = None
 | 
			
		||||
        self.rect = None
 | 
			
		||||
        self.mode = None
 | 
			
		||||
        self.square = False
 | 
			
		||||
        self.reference_x = None
 | 
			
		||||
        self.reference_y = None
 | 
			
		||||
        self.reference_rect = None
 | 
			
		||||
 | 
			
		||||
    def set_rect(self, rect):
 | 
			
		||||
        if not isinstance(rect, QtCore.QRect):
 | 
			
		||||
            raise ValueError()
 | 
			
		||||
        self.rect = rect
 | 
			
		||||
        if rect is None:
 | 
			
		||||
            self.reference_x = None
 | 
			
		||||
            self.reference_y = None
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
    def set_reference_point(self, cursor):
 | 
			
		||||
        self.reference_x = cursor.x() - self.rect.left()
 | 
			
		||||
        self.reference_y = cursor.y() - self.rect.top()
 | 
			
		||||
 | 
			
		||||
    def resize(self, rects, cursor):
 | 
			
		||||
        if self.snap is not None:
 | 
			
		||||
            x, y = snap(cursor.x(), cursor.y(), self.snap)
 | 
			
		||||
            cursor.setX(x)
 | 
			
		||||
            cursor.setY(y)
 | 
			
		||||
        resize_rect_with_direction(
 | 
			
		||||
            self.rect, cursor, self.direction, force_square=self.square)
 | 
			
		||||
        self.apply_relative_transformation(rects)
 | 
			
		||||
 | 
			
		||||
    def apply_relative_transformation(self, rects):
 | 
			
		||||
        for rect in rects:
 | 
			
		||||
            resize_rect_with_reference(
 | 
			
		||||
                rect, self.reference_rect, self.rect)
 | 
			
		||||
 | 
			
		||||
        self.reference_rect = QtCore.QRect(
 | 
			
		||||
            self.rect.topLeft(), self.rect.size())
 | 
			
		||||
 | 
			
		||||
    def move(self, rects, cursor):
 | 
			
		||||
        x = cursor.x() - self.reference_x
 | 
			
		||||
        y = cursor.y() - self.reference_y
 | 
			
		||||
        if self.snap is not None:
 | 
			
		||||
            x, y = snap(x, y, self.snap)
 | 
			
		||||
        self.apply_topleft(rects, x, y)
 | 
			
		||||
 | 
			
		||||
    def shift(self, rects, offset):
 | 
			
		||||
        x, y = offset
 | 
			
		||||
        if self.snap is not None:
 | 
			
		||||
            x *= self.snap[0]
 | 
			
		||||
            y *= self.snap[1]
 | 
			
		||||
        x = self.rect.left() + x
 | 
			
		||||
        y = self.rect.top() + y
 | 
			
		||||
        if self.snap:
 | 
			
		||||
            x, y = snap(x, y, self.snap)
 | 
			
		||||
        self.apply_topleft(rects, x, y)
 | 
			
		||||
 | 
			
		||||
    def apply_topleft(self, rects, x, y):
 | 
			
		||||
        width = self.rect.width()
 | 
			
		||||
        height = self.rect.height()
 | 
			
		||||
        self.rect.setTopLeft(QtCore.QPoint(x, y))
 | 
			
		||||
        self.rect.setWidth(width)
 | 
			
		||||
        self.rect.setHeight(height)
 | 
			
		||||
        self.apply_relative_transformation(rects)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def snap(x, y, snap):
 | 
			
		||||
    x = snap[0] * round(x / snap[0])
 | 
			
		||||
    y = snap[1] * round(y / snap[1])
 | 
			
		||||
    return x, y
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_combined_rects(rects):
 | 
			
		||||
    """
 | 
			
		||||
    this function analyse list of rects and return
 | 
			
		||||
    a rect with the smaller top and left and highest right and bottom
 | 
			
		||||
    __________________________________ ?
 | 
			
		||||
    |              | A               |
 | 
			
		||||
    |              |                 |
 | 
			
		||||
    |______________|      ___________| B
 | 
			
		||||
    |                     |          |
 | 
			
		||||
    |_____________________|__________|
 | 
			
		||||
    """
 | 
			
		||||
    if not rects:
 | 
			
		||||
        return None
 | 
			
		||||
    l = min(rect.left() for rect in rects)
 | 
			
		||||
    t = min(rect.top() for rect in rects)
 | 
			
		||||
    r = max(rect.right() for rect in rects)
 | 
			
		||||
    b = max(rect.bottom() for rect in rects)
 | 
			
		||||
 | 
			
		||||
    return QtCore.QRect(l, t, r-l, b-t)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def rect_symmetry(rect, point, horizontal=True):
 | 
			
		||||
    """
 | 
			
		||||
     ______  rect           ______  result
 | 
			
		||||
    |      |               |      |
 | 
			
		||||
    |______|               |______|
 | 
			
		||||
                   . point
 | 
			
		||||
 | 
			
		||||
    Compute symmetry for a rect from a given point and axis
 | 
			
		||||
    """
 | 
			
		||||
    center = rect.center()
 | 
			
		||||
    if horizontal:
 | 
			
		||||
        dist = (center.x() - point.x()) * 2
 | 
			
		||||
        vector = QtCore.QPoint(dist, 0)
 | 
			
		||||
    else:
 | 
			
		||||
        dist = (center.y() - point.y()) * 2
 | 
			
		||||
        vector = QtCore.QPoint(0, dist)
 | 
			
		||||
    center = rect.center() - vector
 | 
			
		||||
    rect.moveCenter(center)
 | 
			
		||||
    return rect
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def split_line(point1, point2, step_number):
 | 
			
		||||
    """
 | 
			
		||||
    split a line on given number of points.
 | 
			
		||||
    """
 | 
			
		||||
    if step_number <= 1:
 | 
			
		||||
        return [point2]
 | 
			
		||||
    x_values = split_range(point1.x(), point2.x(), step_number)
 | 
			
		||||
    y_values = split_range(point1.y(), point2.y(), step_number)
 | 
			
		||||
    return [QtCore.QPoint(x, y) for x, y in zip(x_values, y_values)]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def split_range(input_, output, step_number):
 | 
			
		||||
    difference = output - input_
 | 
			
		||||
    step = difference / float(step_number - 1)
 | 
			
		||||
    return [int(input_ + (step * i)) for i in range(step_number)]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    assert split_range(0, 10, 11) == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 | 
			
		||||
							
								
								
									
										40
									
								
								Scripts/Animation/dwpicker/dwpicker/hotkeys.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,40 @@
 | 
			
		||||
 | 
			
		||||
from maya import cmds
 | 
			
		||||
from dwpicker.optionvar import save_optionvar, DEFAULT_HOTKEYS, OPTIONVARS
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_hotkeys_config():
 | 
			
		||||
    # For config retro compatibility, we always ensure that default value is
 | 
			
		||||
    # set in case of new shortcut added in the system. We also ensure that old
 | 
			
		||||
    # shortcut is going to be removed from the config.
 | 
			
		||||
    default = build_config_from_string(OPTIONVARS[DEFAULT_HOTKEYS])
 | 
			
		||||
    saved = build_config_from_string(cmds.optionVar(query=DEFAULT_HOTKEYS))
 | 
			
		||||
    for key in default.keys():
 | 
			
		||||
        if key in saved:
 | 
			
		||||
            default[key] = saved[key]
 | 
			
		||||
    return default
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_config_from_string(value):
 | 
			
		||||
    config = {}
 | 
			
		||||
    for entry in value.split(';'):
 | 
			
		||||
        function_name = entry.split('=')[0]
 | 
			
		||||
        enabled = bool(int(entry.split('=')[-1].split(',')[-1]))
 | 
			
		||||
        key_sequence = entry.split('=')[-1].split(',')[0]
 | 
			
		||||
        config[function_name] = {
 | 
			
		||||
            'enabled': enabled if key_sequence != 'None' else False,
 | 
			
		||||
            'key_sequence': None if key_sequence == 'None' else key_sequence}
 | 
			
		||||
    return config
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def set_hotkey_config(function, key_sequence, enabled):
 | 
			
		||||
    config = get_hotkeys_config()
 | 
			
		||||
    config[function] = {'enabled': enabled, 'key_sequence': key_sequence}
 | 
			
		||||
    save_hotkey_config(config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def save_hotkey_config(config):
 | 
			
		||||
    value = ';'.join([
 | 
			
		||||
        '{0}={1},{2}'.format(function, data['key_sequence'], int(data['enabled']))
 | 
			
		||||
        for function, data in config.items()])
 | 
			
		||||
    save_optionvar(DEFAULT_HOTKEYS, value)
 | 
			
		||||
							
								
								
									
										194
									
								
								Scripts/Animation/dwpicker/dwpicker/hotkeyseditor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,194 @@
 | 
			
		||||
 | 
			
		||||
from PySide2 import QtWidgets, QtCore, QtGui
 | 
			
		||||
from dwpicker.hotkeys import get_hotkeys_config, save_hotkey_config
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HotkeysEditor(QtWidgets.QWidget):
 | 
			
		||||
    hotkey_changed = QtCore.Signal()
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(HotkeysEditor, self).__init__(parent)
 | 
			
		||||
        self.model = HotkeysTableModel()
 | 
			
		||||
        self.model.hotkey_changed.connect(self.hotkey_changed.emit)
 | 
			
		||||
        self.table = QtWidgets.QTableView()
 | 
			
		||||
        self.table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
 | 
			
		||||
        self.table.setModel(self.model)
 | 
			
		||||
        self.table.selectionModel().selectionChanged.connect(
 | 
			
		||||
            self.selection_changed)
 | 
			
		||||
        self.hotkey_editor = HotkeyEditor()
 | 
			
		||||
        self.hotkey_editor.hotkey_edited.connect(self.update_hotkeys)
 | 
			
		||||
        self.clear = QtWidgets.QPushButton('Clear')
 | 
			
		||||
        self.clear.released.connect(self.do_clear)
 | 
			
		||||
 | 
			
		||||
        hotkey_layout = QtWidgets.QVBoxLayout()
 | 
			
		||||
        hotkey_layout.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        hotkey_layout.addWidget(self.hotkey_editor)
 | 
			
		||||
        hotkey_layout.addWidget(self.clear)
 | 
			
		||||
        hotkey_layout.addStretch(1)
 | 
			
		||||
 | 
			
		||||
        layout = QtWidgets.QHBoxLayout(self)
 | 
			
		||||
        layout.addWidget(self.table)
 | 
			
		||||
        layout.addLayout(hotkey_layout)
 | 
			
		||||
 | 
			
		||||
    def do_clear(self):
 | 
			
		||||
        self.hotkey_editor.clear_values()
 | 
			
		||||
        self.update_hotkeys()
 | 
			
		||||
        self.hotkey_changed.emit()
 | 
			
		||||
 | 
			
		||||
    def update_hotkeys(self):
 | 
			
		||||
        self.model.set_keysequence(
 | 
			
		||||
            self.hotkey_editor.function_name,
 | 
			
		||||
            self.hotkey_editor.key_sequence())
 | 
			
		||||
 | 
			
		||||
    def selection_changed(self, *_):
 | 
			
		||||
        indexes = self.table.selectionModel().selectedIndexes()
 | 
			
		||||
        if not indexes:
 | 
			
		||||
            self.hotkey_editor.clear()
 | 
			
		||||
            return
 | 
			
		||||
        row = indexes[0].row()
 | 
			
		||||
        function_name = sorted(list(self.model.config))[row]
 | 
			
		||||
        data = self.model.config[function_name]
 | 
			
		||||
        self.hotkey_editor.set_key_sequence(
 | 
			
		||||
            function_name, data['key_sequence'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HotkeyEditor(QtWidgets.QWidget):
 | 
			
		||||
    hotkey_edited = QtCore.Signal()
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(HotkeyEditor, self).__init__(parent)
 | 
			
		||||
        self.function_name = None
 | 
			
		||||
        self.function_name_label = QtWidgets.QLabel()
 | 
			
		||||
        self.alt = QtWidgets.QCheckBox('Alt')
 | 
			
		||||
        self.alt.released.connect(self.emit_hotkey_edited)
 | 
			
		||||
        self.ctrl = QtWidgets.QCheckBox('Ctrl')
 | 
			
		||||
        self.ctrl.released.connect(self.emit_hotkey_edited)
 | 
			
		||||
        self.shift = QtWidgets.QCheckBox('Shift')
 | 
			
		||||
        self.shift.released.connect(self.emit_hotkey_edited)
 | 
			
		||||
        self.string = KeyField()
 | 
			
		||||
        self.string.changed.connect(self.hotkey_edited.emit)
 | 
			
		||||
 | 
			
		||||
        modifiers = QtWidgets.QHBoxLayout()
 | 
			
		||||
        modifiers.addWidget(self.alt)
 | 
			
		||||
        modifiers.addWidget(self.ctrl)
 | 
			
		||||
        modifiers.addWidget(self.shift)
 | 
			
		||||
 | 
			
		||||
        layout = QtWidgets.QVBoxLayout(self)
 | 
			
		||||
        layout.addWidget(self.function_name_label)
 | 
			
		||||
        layout.addLayout(modifiers)
 | 
			
		||||
        layout.addWidget(self.string)
 | 
			
		||||
 | 
			
		||||
    def clear(self):
 | 
			
		||||
        self.function_name = None
 | 
			
		||||
        self.clear_values()
 | 
			
		||||
        self.function_name_label.setText('')
 | 
			
		||||
 | 
			
		||||
    def clear_values(self):
 | 
			
		||||
        self.ctrl.setChecked(False)
 | 
			
		||||
        self.alt.setChecked(False)
 | 
			
		||||
        self.shift.setChecked(False)
 | 
			
		||||
        self.string.setText('')
 | 
			
		||||
 | 
			
		||||
    def emit_hotkey_edited(self, *_):
 | 
			
		||||
        self.hotkey_edited.emit()
 | 
			
		||||
 | 
			
		||||
    def key_sequence(self):
 | 
			
		||||
        if not self.string.text():
 | 
			
		||||
            return None
 | 
			
		||||
        sequence = []
 | 
			
		||||
        if self.ctrl.isChecked():
 | 
			
		||||
            sequence.append('CTRL')
 | 
			
		||||
        if self.alt.isChecked():
 | 
			
		||||
            sequence.append('ALT')
 | 
			
		||||
        if self.shift.isChecked():
 | 
			
		||||
            sequence.append('SHIFT')
 | 
			
		||||
        sequence.append(self.string.text())
 | 
			
		||||
        return '+'.join(sequence)
 | 
			
		||||
 | 
			
		||||
    def set_key_sequence(self, function_name, key_sequence):
 | 
			
		||||
        self.function_name = function_name
 | 
			
		||||
        self.function_name_label.setText(function_name.title())
 | 
			
		||||
        if key_sequence is None:
 | 
			
		||||
            self.ctrl.setChecked(False)
 | 
			
		||||
            self.alt.setChecked(False)
 | 
			
		||||
            self.shift.setChecked(False)
 | 
			
		||||
            self.string.setText('')
 | 
			
		||||
            return
 | 
			
		||||
        self.ctrl.setChecked('ctrl' in key_sequence.lower())
 | 
			
		||||
        self.alt.setChecked('alt' in key_sequence.lower())
 | 
			
		||||
        self.shift.setChecked('shift' in key_sequence.lower())
 | 
			
		||||
        self.string.setText(key_sequence.split('+')[-1])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class KeyField(QtWidgets.QLineEdit):
 | 
			
		||||
    changed = QtCore.Signal()
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(KeyField, self).__init__(parent)
 | 
			
		||||
        self.setReadOnly(True)
 | 
			
		||||
 | 
			
		||||
    def keyPressEvent(self, event):
 | 
			
		||||
        if event.key() == QtCore.Qt.Key_Shift:
 | 
			
		||||
            return
 | 
			
		||||
        self.setText(QtGui.QKeySequence(event.key()).toString())
 | 
			
		||||
        self.changed.emit()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HotkeysTableModel(QtCore.QAbstractTableModel):
 | 
			
		||||
    HEADERS = 'Function', 'Key sequence'
 | 
			
		||||
    hotkey_changed = QtCore.Signal()
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(HotkeysTableModel, self).__init__(parent)
 | 
			
		||||
        self.config = get_hotkeys_config()
 | 
			
		||||
 | 
			
		||||
    def rowCount(self, *_):
 | 
			
		||||
        return len(self.config)
 | 
			
		||||
 | 
			
		||||
    def columnCount(self, *_):
 | 
			
		||||
        return len(self.HEADERS)
 | 
			
		||||
 | 
			
		||||
    def set_keysequence(self, function_name, key_sequence):
 | 
			
		||||
        self.layoutAboutToBeChanged.emit()
 | 
			
		||||
        self.config[function_name]['key_sequence'] = key_sequence
 | 
			
		||||
        if key_sequence is None:
 | 
			
		||||
            self.config[function_name]['enabled'] = False
 | 
			
		||||
        save_hotkey_config(self.config)
 | 
			
		||||
        self.layoutChanged.emit()
 | 
			
		||||
        self.hotkey_changed.emit()
 | 
			
		||||
 | 
			
		||||
    def flags(self, index):
 | 
			
		||||
        flags = QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
 | 
			
		||||
        if index.column() == 0:
 | 
			
		||||
            flags |= QtCore.Qt.ItemIsUserCheckable
 | 
			
		||||
        return flags
 | 
			
		||||
 | 
			
		||||
    def headerData(self, section, orientation, role):
 | 
			
		||||
        if orientation == QtCore.Qt.Vertical or role != QtCore.Qt.DisplayRole:
 | 
			
		||||
            return
 | 
			
		||||
        return self.HEADERS[section]
 | 
			
		||||
 | 
			
		||||
    def setData(self, index, value, role):
 | 
			
		||||
 | 
			
		||||
        if role != QtCore.Qt.CheckStateRole or index.column() != 0:
 | 
			
		||||
            return
 | 
			
		||||
        function = sorted(list(self.config))[index.row()]
 | 
			
		||||
        self.config[function]['enabled'] = value
 | 
			
		||||
        save_hotkey_config(self.config)
 | 
			
		||||
        self.hotkey_changed.emit()
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def data(self, index, role):
 | 
			
		||||
        if not index.isValid():
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        function = sorted(list(self.config))[index.row()]
 | 
			
		||||
        data = self.config[function]
 | 
			
		||||
        if role == QtCore.Qt.DisplayRole:
 | 
			
		||||
            if index.column() == 0:
 | 
			
		||||
                return function.title()
 | 
			
		||||
            else:
 | 
			
		||||
                return data['key_sequence']
 | 
			
		||||
        if role == QtCore.Qt.CheckStateRole and index.column() == 0:
 | 
			
		||||
            return (
 | 
			
		||||
                QtCore.Qt.Checked if data['enabled'] else QtCore.Qt.Unchecked)
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/addbg.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 3.5 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/addbutton.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 5.4 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/addtext.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 4.3 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/align_bottom.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 12 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/align_h_center.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 12 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/align_left.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 12 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/align_right.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 12 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/align_top.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 11 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/align_v_center.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 12 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/arrange_h.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 15 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/arrange_v.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 17 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/center.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 3.5 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/copy.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.5 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/copy_settings.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 2.4 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/delete.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 2.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/delete2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 5.4 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/edit.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 784 B  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/edit2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 731 B  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/frame.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 22 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/h_symmetry.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 13 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/link.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 14 KiB  | 
| 
		 After Width: | Height: | Size: 22 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/manager-delete.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.9 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/manager-edit.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 963 B  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/manager-export.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 6.5 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/manager-import.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 6.3 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/manager-new.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 998 B  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/movedown.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 3.6 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/moveup.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 3.6 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/new.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 4.5 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/onbottom.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 3.6 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/ontop.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 3.5 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/open.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 24 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/paste.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.6 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/paste_settings.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 2.3 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/picker.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 567 B  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/play.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1020 B  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/redo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/reload.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 11 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/save.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 15 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/search.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 21 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/snap.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 4.3 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/touch.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 15 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/undo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/unlink.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 478 B  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/dwpicker/icons/v_symmetry.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 16 KiB  | 
@@ -0,0 +1 @@
 | 
			
		||||
from dwpicker.ingest.animschool.converter import convert
 | 
			
		||||
@@ -0,0 +1,122 @@
 | 
			
		||||
import json
 | 
			
		||||
import os
 | 
			
		||||
from PySide2 import QtGui
 | 
			
		||||
 | 
			
		||||
from dwpicker.templates import PICKER, BUTTON, BACKGROUND
 | 
			
		||||
from dwpicker.ingest.animschool.parser import parse_animschool_picker, save_png
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def rgb_to_hex(r, g, b):
 | 
			
		||||
    return '#{r:02x}{g:02x}{b:02x}'.format(r=r, g=g, b=b)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _label_width(text):
 | 
			
		||||
    width = 0
 | 
			
		||||
    for letter in text:
 | 
			
		||||
        if letter == " ":
 | 
			
		||||
            width += 3
 | 
			
		||||
        elif letter.isupper():
 | 
			
		||||
            width += 7
 | 
			
		||||
        else:
 | 
			
		||||
            width += 6
 | 
			
		||||
    return width
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def convert_to_picker_button(button):
 | 
			
		||||
    if len(button['label']):
 | 
			
		||||
        button['w'] = max((button['w'], _label_width(button['label'])))
 | 
			
		||||
    delta = {
 | 
			
		||||
        'text.content': button['label'],
 | 
			
		||||
        'shape.left': button['x'] - (button['w'] // 2),
 | 
			
		||||
        'shape.top': button['y'] - (button['h'] // 2),
 | 
			
		||||
        'shape.width': button['w'],
 | 
			
		||||
        'shape.height': button['h']}
 | 
			
		||||
 | 
			
		||||
    if button['action'] == 'select':
 | 
			
		||||
        delta['action.targets'] = button['targets']
 | 
			
		||||
        if len(button['targets']) > 1:
 | 
			
		||||
            delta['shape'] = 'rounded_square' if button['label'] else 'round'
 | 
			
		||||
            delta['shape.cornersx'] = delta['shape.width'] / 10
 | 
			
		||||
            delta['shape.cornersy'] = delta['shape.height'] / 10
 | 
			
		||||
 | 
			
		||||
    else:
 | 
			
		||||
        delta['action.left.language'] = button['lang']
 | 
			
		||||
        delta['action.left.command'] = button['targets'][0]
 | 
			
		||||
 | 
			
		||||
    delta['bgcolor.normal'] = rgb_to_hex(*button['bgcolor'])
 | 
			
		||||
    delta['text.color'] = rgb_to_hex(*button['txtcolor'])
 | 
			
		||||
    delta['border'] = button['action'] == 'command'
 | 
			
		||||
    delta['border'] = button['action'] == 'command'
 | 
			
		||||
 | 
			
		||||
    picker_button = BUTTON.copy()
 | 
			
		||||
    picker_button.update(delta)
 | 
			
		||||
    return picker_button
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def frame_picker_buttons(picker):
 | 
			
		||||
    shapes = picker['shapes']
 | 
			
		||||
    offset_x = min(shape['shape.left'] for shape in shapes)
 | 
			
		||||
    offset_y = min(shape['shape.top'] for shape in shapes)
 | 
			
		||||
    offset = -min([offset_x, 0]), -min([offset_y, 0])
 | 
			
		||||
 | 
			
		||||
    for shape in shapes:
 | 
			
		||||
        shape['shape.left'] += offset[0]
 | 
			
		||||
        shape['shape.top'] += offset[1]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def fit_picker_to_content(picker):
 | 
			
		||||
    shapes = picker['shapes']
 | 
			
		||||
    width = max(s['shape.left'] + s['shape.width'] for s in shapes)
 | 
			
		||||
    height = max(s['shape.top'] + s['shape.height'] for s in shapes)
 | 
			
		||||
    picker['general']['width'] = int(width)
 | 
			
		||||
    picker['general']['height'] = int(height)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def image_to_background_shape(imagepath):
 | 
			
		||||
    shape = BACKGROUND.copy()
 | 
			
		||||
    shape['image.path'] = imagepath
 | 
			
		||||
    image = QtGui.QImage(imagepath)
 | 
			
		||||
    shape['image.width'] = image.size().width()
 | 
			
		||||
    shape['image.height'] = image.size().height()
 | 
			
		||||
    shape['shape.width'] = image.size().width()
 | 
			
		||||
    shape['shape.height'] = image.size().height()
 | 
			
		||||
    shape['bgcolor.transparency'] = 255
 | 
			
		||||
    return shape
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_picker_from_pkr(title, buttons, imagepath, dst):
 | 
			
		||||
    picker = {
 | 
			
		||||
        'general': PICKER.copy(),
 | 
			
		||||
        'shapes': [convert_to_picker_button(b) for b in buttons]}
 | 
			
		||||
    picker['general']['name'] = title
 | 
			
		||||
    if imagepath:
 | 
			
		||||
        picker['shapes'].insert(0, image_to_background_shape(imagepath))
 | 
			
		||||
    frame_picker_buttons(picker)
 | 
			
		||||
    fit_picker_to_content(picker)
 | 
			
		||||
    with open(dst, "w") as f:
 | 
			
		||||
        json.dump(picker, f, indent=2)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def convert(filepath, directory=None):
 | 
			
		||||
    directory = directory or os.path.dirname(filepath)
 | 
			
		||||
    title, buttons, png_data = parse_animschool_picker(filepath)
 | 
			
		||||
    picker_filename = os.path.splitext(os.path.basename(filepath))[0]
 | 
			
		||||
    png_path = unique_filename(directory, picker_filename, 'png')
 | 
			
		||||
    png_path = png_path if png_data else None
 | 
			
		||||
    dst = unique_filename(directory, picker_filename, 'json')
 | 
			
		||||
    if png_path:
 | 
			
		||||
        save_png(png_data, png_path)
 | 
			
		||||
    build_picker_from_pkr(title, buttons, png_path, dst)
 | 
			
		||||
    return dst
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def unique_filename(directory, filename, extension):
 | 
			
		||||
    filepath = os.path.join(directory, filename) + '.' + extension
 | 
			
		||||
    i = 0
 | 
			
		||||
    while os.path.exists(filepath):
 | 
			
		||||
        filepath = '{base}.{index}.{extension}'.format(
 | 
			
		||||
            base=os.path.join(directory, filename),
 | 
			
		||||
            index=str(i).zfill(3),
 | 
			
		||||
            extension=extension)
 | 
			
		||||
        i += 1
 | 
			
		||||
    return filepath
 | 
			
		||||
							
								
								
									
										275
									
								
								Scripts/Animation/dwpicker/dwpicker/ingest/animschool/parser.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,275 @@
 | 
			
		||||
"""
 | 
			
		||||
Module to parse and extract data from AnimSchool picker file.
 | 
			
		||||
This works for Animschool until 2021 release.
 | 
			
		||||
 | 
			
		||||
PKR file structure description:
 | 
			
		||||
 | 
			
		||||
    -- header --
 | 
			
		||||
    4 bytes (singed int): Picker Version.
 | 
			
		||||
    4 bytes (singed int): Title number (x) of bytes length.
 | 
			
		||||
    x bytes (hex text): Title.
 | 
			
		||||
 | 
			
		||||
    -- PNG data --
 | 
			
		||||
    ...
 | 
			
		||||
 | 
			
		||||
    --- buttons ---
 | 
			
		||||
    4 bytes (singed int): Number of buttons
 | 
			
		||||
 | 
			
		||||
    -- Button array --
 | 
			
		||||
    for _ in range(number_of_buttons)
 | 
			
		||||
        - 4 bytes (singed int): Button id as signed int.
 | 
			
		||||
        - 4 bytes (singed int): Center position X.
 | 
			
		||||
        - 4 bytes (singed int): Center position Y.
 | 
			
		||||
        - 4 bytes (singed int):
 | 
			
		||||
                Size for old AnimSchool versions (4 and older)
 | 
			
		||||
                This is still there but unused in 2021 version.
 | 
			
		||||
        - 4 bytes (singed int): Width.
 | 
			
		||||
        - 4 bytes (singed int): Height.
 | 
			
		||||
        - 4 bytes (bool): Button type.
 | 
			
		||||
                True = Command button.
 | 
			
		||||
                False = Selection button.
 | 
			
		||||
        - 4 bytes (bool): Languages used for command button.
 | 
			
		||||
                True = Python.
 | 
			
		||||
                False = Mel.
 | 
			
		||||
        - 4 bytes (hex __RRGGBB): Background color.
 | 
			
		||||
        - 4 bytes (hex __RRGGBB): Text color.
 | 
			
		||||
        - 4 bytes (singed int): Label number (x) of bytes length.
 | 
			
		||||
        - x bytes (hexa text): Label.
 | 
			
		||||
        - 4 bytes (singed int): Number (x) of targets.
 | 
			
		||||
                This is automatically 1 for command button
 | 
			
		||||
 | 
			
		||||
        for _ in range(number_of_targets):
 | 
			
		||||
            - 4 bytes (singed int): Target name number (x) of bytes length.
 | 
			
		||||
            - x bytes (hexa text): Target name.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
The script export pkr data in 3 different objects:
 | 
			
		||||
 | 
			
		||||
    PNG data:
 | 
			
		||||
        This is a one to one of the png binari data encapsulated in the pkr
 | 
			
		||||
        file.
 | 
			
		||||
 | 
			
		||||
    Title:
 | 
			
		||||
        As simple string
 | 
			
		||||
 | 
			
		||||
    Buttons:
 | 
			
		||||
        Translate the binari buttons as readable python dict!
 | 
			
		||||
        {
 | 
			
		||||
            "id": int,
 | 
			
		||||
            "x": int,
 | 
			
		||||
            "y": int,
 | 
			
		||||
            "w": int,
 | 
			
		||||
            "h": int,
 | 
			
		||||
            "action": str: "select" | "command",
 | 
			
		||||
            "lang": str: "mel" | "python",
 | 
			
		||||
            "bgcolor": [r:int, g:int, b:int],
 | 
			
		||||
            "txtcolor": [r:int, g:int, b:int],
 | 
			
		||||
            "label": str,
 | 
			
		||||
            "targets": List[str]
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from binascii import hexlify, unhexlify
 | 
			
		||||
import json
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
PNG_HEADER = b'89504e470d0a1a0a'
 | 
			
		||||
PNG_FOOTER = b'ae426082'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def split_data(content, number_of_bytes=4):
 | 
			
		||||
    if isinstance(number_of_bytes, bytes):
 | 
			
		||||
        number_of_bytes = int(number_of_bytes, 16)
 | 
			
		||||
    return content[:number_of_bytes * 2], content[number_of_bytes * 2:]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def bytes_to_string(stringdata):
 | 
			
		||||
    return ''.join(
 | 
			
		||||
        b.decode('cp1252')
 | 
			
		||||
        for b in unhexlify(stringdata).split(b'\x00'))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def bytes_to_int(i):
 | 
			
		||||
    if i[:4] == b'00' * 2:
 | 
			
		||||
        return int(i, 16)
 | 
			
		||||
    elif i[:4] == b'ff' * 2:
 | 
			
		||||
        return -65535 + int(i[-4:], 16)
 | 
			
		||||
    raise Exception('Count not interpret data as int')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def print_(data, max_bytes=64):
 | 
			
		||||
    string = repr(data)[2:-1][:max_bytes * 2]
 | 
			
		||||
    beautified = ''
 | 
			
		||||
    for i in range(len(string)):
 | 
			
		||||
        beautified += string[i].upper()
 | 
			
		||||
        if i % 2:
 | 
			
		||||
            beautified += ' '
 | 
			
		||||
        if (i + 1) % 16 == 0 and i != 0:
 | 
			
		||||
            beautified += '\n'
 | 
			
		||||
    print(beautified)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def bytes_to_rgb(data):
 | 
			
		||||
    data = int(data, 16)
 | 
			
		||||
    b = data & 255
 | 
			
		||||
    g = (data >> 8) & 255
 | 
			
		||||
    r = (data >> 16) & 255
 | 
			
		||||
    return r, g, b
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def extract_string(data):
 | 
			
		||||
    string_size, data = split_data(data)
 | 
			
		||||
    string, data = split_data(data, string_size)
 | 
			
		||||
    string = bytes_to_string(string)
 | 
			
		||||
    return string, data
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def extract_png_data(data):
 | 
			
		||||
    png_len_size, data = split_data(data)
 | 
			
		||||
    png_len_size = bytes_to_int(png_len_size)
 | 
			
		||||
 | 
			
		||||
    if not png_len_size:
 | 
			
		||||
        return None, data
 | 
			
		||||
 | 
			
		||||
    png_len, data = split_data(data, png_len_size)
 | 
			
		||||
    png_len = int(bytes_to_string(png_len))  # lol
 | 
			
		||||
    if png_len == 0:
 | 
			
		||||
        _, data = split_data(data, 4)  # remove some leftover data
 | 
			
		||||
        return None, data
 | 
			
		||||
 | 
			
		||||
    _, data = split_data(data, 4)
 | 
			
		||||
    png_end = int((data.find(PNG_FOOTER) + len(PNG_FOOTER)) / 2)
 | 
			
		||||
    return split_data(data, png_end)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def extract_button_targets(data):
 | 
			
		||||
    number_of_targets, data = split_data(data)
 | 
			
		||||
    targets = []
 | 
			
		||||
    number_of_targets = int(number_of_targets, 16)
 | 
			
		||||
    for _ in range(number_of_targets):
 | 
			
		||||
        target_name, data = extract_string(data)
 | 
			
		||||
        targets.append(target_name)
 | 
			
		||||
    return targets, data
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def extract_button_data(data, version=5, verbose=True):
 | 
			
		||||
    button_id, data = split_data(data)
 | 
			
		||||
    button_id = bytes_to_int(button_id)
 | 
			
		||||
    if verbose:
 | 
			
		||||
        print('Button #{button_id}'.format(button_id=button_id))
 | 
			
		||||
    x, data = split_data(data)
 | 
			
		||||
    x = bytes_to_int(x)
 | 
			
		||||
    y, data = split_data(data)
 | 
			
		||||
    y = bytes_to_int(y)
 | 
			
		||||
    old_height, data = split_data(data)
 | 
			
		||||
    if version > 4:
 | 
			
		||||
        width, data = split_data(data)
 | 
			
		||||
        width = bytes_to_int(width)
 | 
			
		||||
        height, data = split_data(data)
 | 
			
		||||
        height = bytes_to_int(height)
 | 
			
		||||
    else:
 | 
			
		||||
        width, height = bytes_to_int(old_height), bytes_to_int(old_height)
 | 
			
		||||
    action, data = split_data(data)
 | 
			
		||||
    action = bytes_to_int(action)
 | 
			
		||||
    assert action in [0, 1]
 | 
			
		||||
    action = 'command' if action else 'select'
 | 
			
		||||
    lang, data = split_data(data)
 | 
			
		||||
    lang = bytes_to_int(lang)
 | 
			
		||||
    assert lang in [0, 1]
 | 
			
		||||
    lang = 'python' if lang else 'mel'
 | 
			
		||||
    bgcolor, data = split_data(data)
 | 
			
		||||
    bgcolor = bytes_to_rgb(bgcolor)
 | 
			
		||||
    txtcolor, data = split_data(data)
 | 
			
		||||
    txtcolor = bytes_to_rgb(txtcolor)
 | 
			
		||||
    label_size, data = split_data(data)
 | 
			
		||||
    if label_size == b'ff' * 4:
 | 
			
		||||
        label = ''
 | 
			
		||||
    else:
 | 
			
		||||
        label, data = split_data(data, label_size)
 | 
			
		||||
        label = bytes_to_string(label)
 | 
			
		||||
    targets, data = extract_button_targets(data)
 | 
			
		||||
    button = dict(
 | 
			
		||||
        id=button_id, x=x, y=y, w=width, h=height, action=action,
 | 
			
		||||
        lang=lang, bgcolor=bgcolor, txtcolor=txtcolor, label=label,
 | 
			
		||||
        targets=targets)
 | 
			
		||||
    return button, data
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parse_animschool_picker(picker_path, verbose=False):
 | 
			
		||||
    with open(picker_path, 'rb') as file:
 | 
			
		||||
        data = hexlify(file.read())
 | 
			
		||||
 | 
			
		||||
    # Get version
 | 
			
		||||
    version, data = split_data(data)
 | 
			
		||||
    version = bytes_to_int(version)
 | 
			
		||||
    print("this picker is build with AnimSchool v" + str(version))
 | 
			
		||||
 | 
			
		||||
    # Get title
 | 
			
		||||
    title, data = extract_string(data)
 | 
			
		||||
    if verbose:
 | 
			
		||||
        print('Title: "{title}"'.format(title=title))
 | 
			
		||||
 | 
			
		||||
    # Extract PNG
 | 
			
		||||
    png_data, data = extract_png_data(data)
 | 
			
		||||
    if verbose and png_data:
 | 
			
		||||
        print('PNG data found')
 | 
			
		||||
 | 
			
		||||
    # Get number of buttons
 | 
			
		||||
    number_of_buttons, data = split_data(data)
 | 
			
		||||
    number_of_buttons = int(number_of_buttons, 16)
 | 
			
		||||
    if verbose:
 | 
			
		||||
        print('Number of buttons: "{num}"'.format(num=number_of_buttons))
 | 
			
		||||
 | 
			
		||||
    # Parse buttons one by one:
 | 
			
		||||
    buttons = []
 | 
			
		||||
    while data:
 | 
			
		||||
        button, data = extract_button_data(data, version, verbose)
 | 
			
		||||
        buttons.append(button)
 | 
			
		||||
 | 
			
		||||
    if len(buttons) != number_of_buttons:
 | 
			
		||||
        raise Exception('Parsing buttons went wrong.')
 | 
			
		||||
 | 
			
		||||
    return title, buttons, png_data
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def extract_to_files(pkr_path, verbose=False):
 | 
			
		||||
    """
 | 
			
		||||
    Extract data and image to .json and .png (if any) next to the .pkr
 | 
			
		||||
    """
 | 
			
		||||
    title, buttons, png_data = parse_animschool_picker(pkr_path, verbose)
 | 
			
		||||
    # Save to json
 | 
			
		||||
    with open(pkr_path + '.json', 'w') as f:
 | 
			
		||||
        json.dump([title, buttons], f, indent=4)
 | 
			
		||||
    # Write PNG to file:
 | 
			
		||||
    png_path = pkr_path + '.png'
 | 
			
		||||
    if png_data and not os.path.exists(png_path):
 | 
			
		||||
        save_png(png_data, png_path)
 | 
			
		||||
    return title, buttons, png_data
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def save_png(png_data, dst):
 | 
			
		||||
    print('Saving PNG to "{dst}"'.format(dst=dst))
 | 
			
		||||
    with open(dst, 'wb') as f:
 | 
			
		||||
        f.write(unhexlify(png_data))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    import sys
 | 
			
		||||
    arg = sys.argv[-1]
 | 
			
		||||
    if arg == 'dir':
 | 
			
		||||
        # Extract json and png for all .pkr files in current dir:
 | 
			
		||||
        import glob
 | 
			
		||||
        for pkr_path in glob.glob('./*.pkr'):
 | 
			
		||||
            print(os.path.basename(pkr_path))
 | 
			
		||||
            try:
 | 
			
		||||
                extract_to_files(pkr_path)
 | 
			
		||||
            except BaseException:
 | 
			
		||||
                print('Failed to parse {pkr_path}'.format(pkr_path=pkr_path))
 | 
			
		||||
    elif arg.endswith('.pkr') and os.path.exists(arg):
 | 
			
		||||
        # Extract given path to json and png:
 | 
			
		||||
        import pprint
 | 
			
		||||
        print('Parsing {arg}'.format(arg=arg))
 | 
			
		||||
        title, buttons, png_data = extract_to_files(arg, verbose=True)
 | 
			
		||||
        print(title)
 | 
			
		||||
        pprint.pprint(buttons)
 | 
			
		||||
							
								
								
									
										213
									
								
								Scripts/Animation/dwpicker/dwpicker/interactive.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,213 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from PySide2 import QtCore, QtGui
 | 
			
		||||
 | 
			
		||||
from dwpicker.geometry import (
 | 
			
		||||
    DIRECTIONS, get_topleft_rect, get_bottomleft_rect, get_topright_rect,
 | 
			
		||||
    get_bottomright_rect, get_left_side_rect, get_right_side_rect,
 | 
			
		||||
    get_top_side_rect, get_bottom_side_rect, proportional_rect)
 | 
			
		||||
from dwpicker.languages import execute_code
 | 
			
		||||
from dwpicker.painting import (
 | 
			
		||||
    draw_selection_square, draw_manipulator, get_hovered_path)
 | 
			
		||||
from dwpicker.path import expand_path
 | 
			
		||||
from dwpicker.selection import select_targets
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
EXCECUTION_WARNING = """\
 | 
			
		||||
Code execution failed for shape: "{name}"
 | 
			
		||||
{error}.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SelectionSquare():
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.rect = None
 | 
			
		||||
        self.handeling = False
 | 
			
		||||
 | 
			
		||||
    def clicked(self, cursor):
 | 
			
		||||
        self.handeling = True
 | 
			
		||||
        self.rect = QtCore.QRectF(cursor, cursor)
 | 
			
		||||
 | 
			
		||||
    def handle(self, cursor):
 | 
			
		||||
        self.rect.setBottomRight(cursor)
 | 
			
		||||
 | 
			
		||||
    def release(self):
 | 
			
		||||
        self.handeling = False
 | 
			
		||||
        self.rect = None
 | 
			
		||||
 | 
			
		||||
    def intersects(self, rect):
 | 
			
		||||
        if not rect or not self.rect:
 | 
			
		||||
            return False
 | 
			
		||||
        return self.rect.intersects(rect)
 | 
			
		||||
 | 
			
		||||
    def draw(self, painter):
 | 
			
		||||
        if self.rect is None:
 | 
			
		||||
            return
 | 
			
		||||
        draw_selection_square(painter, self.rect)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Manipulator():
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self._rect = None
 | 
			
		||||
        self._is_hovered = False
 | 
			
		||||
 | 
			
		||||
        self._tl_corner_rect = None
 | 
			
		||||
        self._bl_corner_rect = None
 | 
			
		||||
        self._tr_corner_rect = None
 | 
			
		||||
        self._br_corner_rect = None
 | 
			
		||||
        self._l_side_rect = None
 | 
			
		||||
        self._r_side_rect = None
 | 
			
		||||
        self._t_side_rect = None
 | 
			
		||||
        self._b_side_rect = None
 | 
			
		||||
 | 
			
		||||
        self.hovered_path = None
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def rect(self):
 | 
			
		||||
        return self._rect
 | 
			
		||||
 | 
			
		||||
    def handler_rects(self):
 | 
			
		||||
        return [
 | 
			
		||||
            self._tl_corner_rect, self._bl_corner_rect, self._tr_corner_rect,
 | 
			
		||||
            self._br_corner_rect, self._l_side_rect, self._r_side_rect,
 | 
			
		||||
            self._t_side_rect, self._b_side_rect]
 | 
			
		||||
 | 
			
		||||
    def get_direction(self, cursor):
 | 
			
		||||
        if self.rect is None:
 | 
			
		||||
            return None
 | 
			
		||||
        for i, rect in enumerate(self.handler_rects()):
 | 
			
		||||
            if rect.contains(cursor):
 | 
			
		||||
                return DIRECTIONS[i]
 | 
			
		||||
 | 
			
		||||
    def hovered_rects(self, cursor):
 | 
			
		||||
        rects = []
 | 
			
		||||
        for rect in self.handler_rects() + [self.rect]:
 | 
			
		||||
            if not rect:
 | 
			
		||||
                continue
 | 
			
		||||
            if rect.contains(cursor):
 | 
			
		||||
                rects.append(rect)
 | 
			
		||||
        return rects
 | 
			
		||||
 | 
			
		||||
    def set_rect(self, rect):
 | 
			
		||||
        self._rect = rect
 | 
			
		||||
        self.update_geometries()
 | 
			
		||||
 | 
			
		||||
    def update_geometries(self):
 | 
			
		||||
        rect = self.rect
 | 
			
		||||
        self._tl_corner_rect = get_topleft_rect(rect) if rect else None
 | 
			
		||||
        self._bl_corner_rect = get_bottomleft_rect(rect) if rect else None
 | 
			
		||||
        self._tr_corner_rect = get_topright_rect(rect) if rect else None
 | 
			
		||||
        self._br_corner_rect = get_bottomright_rect(rect) if rect else None
 | 
			
		||||
        self._l_side_rect = get_left_side_rect(rect) if rect else None
 | 
			
		||||
        self._r_side_rect = get_right_side_rect(rect) if rect else None
 | 
			
		||||
        self._t_side_rect = get_top_side_rect(rect) if rect else None
 | 
			
		||||
        self._b_side_rect = get_bottom_side_rect(rect) if rect else None
 | 
			
		||||
        self.hovered_path = get_hovered_path(rect) if rect else None
 | 
			
		||||
 | 
			
		||||
    def draw(self, painter, cursor):
 | 
			
		||||
        if self.rect is not None and all(self.handler_rects()):
 | 
			
		||||
            draw_manipulator(painter, self, cursor)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_shape_rect_from_options(options):
 | 
			
		||||
    return QtCore.QRectF(
 | 
			
		||||
        options['shape.left'],
 | 
			
		||||
        options['shape.top'],
 | 
			
		||||
        options['shape.width'],
 | 
			
		||||
        options['shape.height'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Shape():
 | 
			
		||||
    def __init__(self, options):
 | 
			
		||||
        self.hovered = False
 | 
			
		||||
        self.clicked = False
 | 
			
		||||
        self.selected = False
 | 
			
		||||
        self.options = options
 | 
			
		||||
        self.rect = get_shape_rect_from_options(options)
 | 
			
		||||
        self.pixmap = None
 | 
			
		||||
        self.image_rect = None
 | 
			
		||||
        self.synchronize_image()
 | 
			
		||||
 | 
			
		||||
    def set_hovered(self, cursor):
 | 
			
		||||
        self.hovered = self.rect.contains(cursor)
 | 
			
		||||
 | 
			
		||||
    def set_clicked(self, cursor):
 | 
			
		||||
        self.clicked = self.rect.contains(cursor)
 | 
			
		||||
 | 
			
		||||
    def release(self, cursor):
 | 
			
		||||
        self.clicked = False
 | 
			
		||||
        self.hovered = self.rect.contains(cursor)
 | 
			
		||||
 | 
			
		||||
    def synchronize_rect(self):
 | 
			
		||||
        self.options['shape.left'] = self.rect.left()
 | 
			
		||||
        self.options['shape.top'] = self.rect.top()
 | 
			
		||||
        self.options['shape.width'] = self.rect.width()
 | 
			
		||||
        self.options['shape.height'] = self.rect.height()
 | 
			
		||||
 | 
			
		||||
    def content_rect(self):
 | 
			
		||||
        if self.options['shape'] == 'round':
 | 
			
		||||
            return proportional_rect(self.rect, 70)
 | 
			
		||||
        return self.rect
 | 
			
		||||
 | 
			
		||||
    def execute(self, button, shift=False, ctrl=False):
 | 
			
		||||
        commands = _find_commands(
 | 
			
		||||
            self.options['action.commands'],
 | 
			
		||||
            button, shift=shift, ctrl=ctrl)
 | 
			
		||||
        for command in commands:
 | 
			
		||||
            try:
 | 
			
		||||
                execute_code(
 | 
			
		||||
                    language=command['language'],
 | 
			
		||||
                    code=command['command'],
 | 
			
		||||
                    deferred=command['deferred'],
 | 
			
		||||
                    compact_undo=command['force_compact_undo'])
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                import traceback
 | 
			
		||||
                print(EXCECUTION_WARNING.format(
 | 
			
		||||
                    name=self.options['text.content'], error=e))
 | 
			
		||||
                print(traceback.format_exc())
 | 
			
		||||
 | 
			
		||||
    def select(self, selection_mode='replace'):
 | 
			
		||||
        select_targets([self], selection_mode=selection_mode)
 | 
			
		||||
 | 
			
		||||
    def targets(self):
 | 
			
		||||
        return self.options['action.targets']
 | 
			
		||||
 | 
			
		||||
    def set_targets(self, targets):
 | 
			
		||||
        self.options['action.targets'] = targets
 | 
			
		||||
 | 
			
		||||
    def is_interactive(self):
 | 
			
		||||
        return bool(
 | 
			
		||||
            [c for c in self.options['action.commands'] if c['enabled']])
 | 
			
		||||
 | 
			
		||||
    def is_background(self):
 | 
			
		||||
        return not any([
 | 
			
		||||
            bool(self.targets()),
 | 
			
		||||
            bool(self.options['action.commands'])])
 | 
			
		||||
 | 
			
		||||
    def visibility_layer(self):
 | 
			
		||||
        return self.options['visibility_layer']
 | 
			
		||||
 | 
			
		||||
    def synchronize_image(self):
 | 
			
		||||
        path = expand_path(self.options['image.path'])
 | 
			
		||||
        self.pixmap = QtGui.QPixmap(path)
 | 
			
		||||
        if self.options['image.fit'] is True:
 | 
			
		||||
            self.image_rect = None
 | 
			
		||||
            return
 | 
			
		||||
        self.image_rect = QtCore.QRectF(
 | 
			
		||||
            self.rect.left(),
 | 
			
		||||
            self.rect.top(),
 | 
			
		||||
            self.options['image.width'],
 | 
			
		||||
            self.options['image.height'])
 | 
			
		||||
        self.image_rect.moveCenter(self.rect.center())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _find_commands(commands, button, ctrl=False, shift=False):
 | 
			
		||||
    result = []
 | 
			
		||||
    for command in commands:
 | 
			
		||||
        conditions = (
 | 
			
		||||
            command['button'] == button and
 | 
			
		||||
            command['ctrl'] == ctrl and
 | 
			
		||||
            command['shift'] == shift)
 | 
			
		||||
        if conditions:
 | 
			
		||||
            result.append(command)
 | 
			
		||||
    return result
 | 
			
		||||
							
								
								
									
										52
									
								
								Scripts/Animation/dwpicker/dwpicker/languages.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,52 @@
 | 
			
		||||
 | 
			
		||||
PYTHON = 'python'
 | 
			
		||||
MEL = 'mel'
 | 
			
		||||
 | 
			
		||||
DEFERRED_PYTHON = """\
 | 
			
		||||
from maya import cmds
 | 
			
		||||
cmds.evalDeferred(\"\"\"{code}\"\"\", lowestPriority=True)
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
DEFERRED_MEL = """\
 | 
			
		||||
evalDeferred "{code}" -lowestPriority;"""
 | 
			
		||||
 | 
			
		||||
STACK_UNDO_PYTHON = """\
 | 
			
		||||
from maya import cmds
 | 
			
		||||
cmds.undoInfo(openChunk=True)
 | 
			
		||||
{code}
 | 
			
		||||
cmds.undoInfo(closeChunk=True)
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
STACK_UNDO_MEL = """\
 | 
			
		||||
undoInfo -openChunk;
 | 
			
		||||
{code}
 | 
			
		||||
undoInfo -closeChunk;
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def execute_code(language, code, deferred=False, compact_undo=False):
 | 
			
		||||
    return EXECUTORS[language](code, deferred, compact_undo)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def execute_python(code, deferred=False, compact_undo=False):
 | 
			
		||||
    if compact_undo:
 | 
			
		||||
        code = STACK_UNDO_PYTHON.format(code=code)
 | 
			
		||||
    if deferred:
 | 
			
		||||
        code = DEFERRED_PYTHON.format(code=code)
 | 
			
		||||
    exec(code, globals())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def execute_mel(code, deferred=False, compact_undo=False):
 | 
			
		||||
    from maya import mel
 | 
			
		||||
    if compact_undo:
 | 
			
		||||
        code = STACK_UNDO_MEL.format(code=code)
 | 
			
		||||
    if deferred:
 | 
			
		||||
        print('Eval deferred not supported for mel command.')
 | 
			
		||||
        # code = DEFERRED_MEL.format(code=code)
 | 
			
		||||
    mel.eval(code.replace(u'\u2029', '\n'))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
EXECUTORS = {
 | 
			
		||||
    PYTHON: execute_python,
 | 
			
		||||
    MEL: execute_mel,
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										865
									
								
								Scripts/Animation/dwpicker/dwpicker/main.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,865 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
import os
 | 
			
		||||
import json
 | 
			
		||||
import webbrowser
 | 
			
		||||
from copy import deepcopy
 | 
			
		||||
from functools import partial
 | 
			
		||||
 | 
			
		||||
from PySide2 import QtWidgets, QtCore, QtGui
 | 
			
		||||
 | 
			
		||||
from maya import cmds
 | 
			
		||||
import maya.OpenMaya as om
 | 
			
		||||
 | 
			
		||||
from dwpicker.appinfos import VERSION, RELEASE_DATE, DW_GITHUB, DW_WEBSITE
 | 
			
		||||
from dwpicker.compatibility import ensure_retro_compatibility
 | 
			
		||||
from dwpicker.designer.editor import PickerEditor
 | 
			
		||||
from dwpicker.dialog import CommandEditorDialog
 | 
			
		||||
from dwpicker.dialog import (
 | 
			
		||||
    warning, question, get_image_path, NamespaceDialog)
 | 
			
		||||
from dwpicker.ingest import animschool
 | 
			
		||||
from dwpicker.interactive import Shape
 | 
			
		||||
from dwpicker.hotkeys import get_hotkeys_config
 | 
			
		||||
from dwpicker.namespace import (
 | 
			
		||||
    switch_namespace, selected_namespace, detect_picker_namespace,
 | 
			
		||||
    pickers_namespaces)
 | 
			
		||||
from dwpicker.optionvar import (
 | 
			
		||||
    AUTO_FOCUS_BEHAVIOR, AUTO_SWITCH_TAB, CHECK_IMAGES_PATHS,
 | 
			
		||||
    AUTO_SET_NAMESPACE, DISABLE_IMPORT_CALLBACKS,
 | 
			
		||||
    DISPLAY_QUICK_OPTIONS, INSERT_TAB_AFTER_CURRENT, LAST_OPEN_DIRECTORY,
 | 
			
		||||
    LAST_IMPORT_DIRECTORY, LAST_COMMAND_LANGUAGE, LAST_SAVE_DIRECTORY,
 | 
			
		||||
    NAMESPACE_TOOLBAR, USE_ICON_FOR_UNSAVED_TAB, WARN_ON_TAB_CLOSED,
 | 
			
		||||
    save_optionvar, append_recent_filename, save_opened_filenames)
 | 
			
		||||
from dwpicker.path import get_import_directory, get_open_directory
 | 
			
		||||
from dwpicker.picker import PickerView, list_targets
 | 
			
		||||
from dwpicker.preference import PreferencesWindow
 | 
			
		||||
from dwpicker.qtutils import set_shortcut, icon, maya_main_window, DockableBase
 | 
			
		||||
from dwpicker.quick import QuickOptions
 | 
			
		||||
from dwpicker.references import ensure_images_path_exists
 | 
			
		||||
from dwpicker.scenedata import (
 | 
			
		||||
    load_local_picker_data, store_local_picker_data,
 | 
			
		||||
    clean_stray_picker_holder_nodes)
 | 
			
		||||
from dwpicker.templates import BUTTON, PICKER, BACKGROUND, COMMAND
 | 
			
		||||
from dwpicker.undo import UndoManager
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
ABOUT = """\
 | 
			
		||||
DreamWall Picker
 | 
			
		||||
    Licence MIT
 | 
			
		||||
    Version: {version}
 | 
			
		||||
    Release date: {release}
 | 
			
		||||
    Authors: Lionel Brouyère, Olivier Evers
 | 
			
		||||
    Contributor(s): Herizoran
 | 
			
		||||
 | 
			
		||||
Features:
 | 
			
		||||
    Animation picker widget.
 | 
			
		||||
    Quick picker creation.
 | 
			
		||||
    Advanced picker editin.
 | 
			
		||||
    Read AnimSchoolPicker files (december 2021 version and latest)
 | 
			
		||||
    Free and open source, today and forever.
 | 
			
		||||
 | 
			
		||||
This tool is a fork of Hotbox Designer (Lionel Brouyère).
 | 
			
		||||
A menus, markmenu and hotbox designer cross DCC.
 | 
			
		||||
https://github.com/luckylyk/hotbox_designer
 | 
			
		||||
""".format(
 | 
			
		||||
    version=".".join(str(n) for n in VERSION),
 | 
			
		||||
    release=RELEASE_DATE)
 | 
			
		||||
WINDOW_TITLE = "DreamWall - Picker"
 | 
			
		||||
WINDOW_CONTROL_NAME = "dwPickerWindow"
 | 
			
		||||
CLOSE_CALLBACK_COMMAND = "import dwpicker;dwpicker._dwpicker.close_event()"
 | 
			
		||||
CLOSE_TAB_WARNING = """\
 | 
			
		||||
Close the tab will remove completely the picker data from the scene.
 | 
			
		||||
Are you sure to continue ?"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_multiple_shapes(targets, override):
 | 
			
		||||
    shapes = [BUTTON.copy() for _ in range(len(targets))]
 | 
			
		||||
    for shape, target in zip(shapes, targets):
 | 
			
		||||
        if override:
 | 
			
		||||
            shape.update(override)
 | 
			
		||||
        shape['action.targets'] = [target]
 | 
			
		||||
    return [Shape(shape) for shape in shapes]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DwPicker(DockableBase, QtWidgets.QWidget):
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        super(DwPicker, self).__init__(control_name=WINDOW_CONTROL_NAME)
 | 
			
		||||
        self.setWindowTitle(WINDOW_TITLE)
 | 
			
		||||
        self.shortcuts = {}
 | 
			
		||||
 | 
			
		||||
        self.editable = True
 | 
			
		||||
        self.callbacks = []
 | 
			
		||||
        self.stored_focus = None
 | 
			
		||||
        self.editors = []
 | 
			
		||||
        self.generals = []
 | 
			
		||||
        self.undo_managers = []
 | 
			
		||||
        self.pickers = []
 | 
			
		||||
        self.filenames = []
 | 
			
		||||
        self.modified_states = []
 | 
			
		||||
        self.preferences_window = PreferencesWindow(
 | 
			
		||||
            callback=self.load_ui_states, parent=maya_main_window())
 | 
			
		||||
        self.preferences_window.need_update_callbacks.connect(
 | 
			
		||||
            self.reload_callbacks)
 | 
			
		||||
        self.preferences_window.hotkey_changed.connect(self.register_shortcuts)
 | 
			
		||||
 | 
			
		||||
        self.namespace_label = QtWidgets.QLabel("Namespace: ")
 | 
			
		||||
        self.namespace_combo = QtWidgets.QComboBox()
 | 
			
		||||
        self.namespace_combo.setFixedWidth(235)
 | 
			
		||||
        method = self.change_namespace_combo
 | 
			
		||||
        self.namespace_combo.currentIndexChanged.connect(method)
 | 
			
		||||
        self.namespace_refresh = QtWidgets.QPushButton("")
 | 
			
		||||
        self.namespace_refresh.setIcon(icon("reload.png"))
 | 
			
		||||
        self.namespace_refresh.setFixedSize(17, 17)
 | 
			
		||||
        self.namespace_refresh.setIconSize(QtCore.QSize(15, 15))
 | 
			
		||||
        self.namespace_refresh.released.connect(self.update_namespaces)
 | 
			
		||||
        self.namespace_picker = QtWidgets.QPushButton("")
 | 
			
		||||
        self.namespace_picker.setIcon(icon("picker.png"))
 | 
			
		||||
        self.namespace_picker.setFixedSize(17, 17)
 | 
			
		||||
        self.namespace_picker.setIconSize(QtCore.QSize(15, 15))
 | 
			
		||||
        self.namespace_picker.released.connect(self.pick_namespace)
 | 
			
		||||
        self.namespace_widget = QtWidgets.QWidget()
 | 
			
		||||
        self.namespace_layout = QtWidgets.QHBoxLayout(self.namespace_widget)
 | 
			
		||||
        self.namespace_layout.setContentsMargins(10, 2, 2, 2)
 | 
			
		||||
        self.namespace_layout.setSpacing(0)
 | 
			
		||||
        self.namespace_layout.addWidget(self.namespace_label)
 | 
			
		||||
        self.namespace_layout.addSpacing(4)
 | 
			
		||||
        self.namespace_layout.addWidget(self.namespace_combo)
 | 
			
		||||
        self.namespace_layout.addSpacing(2)
 | 
			
		||||
        self.namespace_layout.addWidget(self.namespace_refresh)
 | 
			
		||||
        self.namespace_layout.addWidget(self.namespace_picker)
 | 
			
		||||
        self.namespace_layout.addStretch(1)
 | 
			
		||||
 | 
			
		||||
        self.tab = QtWidgets.QTabWidget()
 | 
			
		||||
        self.tab.setTabsClosable(True)
 | 
			
		||||
        self.tab.setMovable(True)
 | 
			
		||||
        self.tab.tabBar().tabMoved.connect(self.tab_moved)
 | 
			
		||||
        self.tab.tabBar().tabBarDoubleClicked.connect(self.change_title)
 | 
			
		||||
        self.tab.currentChanged.connect(self.tab_index_changed)
 | 
			
		||||
        method = partial(self.close_tab, store=True)
 | 
			
		||||
        self.tab.tabCloseRequested.connect(method)
 | 
			
		||||
 | 
			
		||||
        self.quick_options = QuickOptions()
 | 
			
		||||
 | 
			
		||||
        self.menubar = DwPickerMenu(parent=self)
 | 
			
		||||
        self.menubar.new.triggered.connect(self.call_new)
 | 
			
		||||
        self.menubar.open.triggered.connect(self.call_open)
 | 
			
		||||
        self.menubar.save.triggered.connect(self.call_save)
 | 
			
		||||
        self.menubar.save_as.triggered.connect(self.call_save_as)
 | 
			
		||||
        self.menubar.exit.triggered.connect(self.close)
 | 
			
		||||
        self.menubar.import_.triggered.connect(self.call_import)
 | 
			
		||||
        self.menubar.undo.triggered.connect(self.call_undo)
 | 
			
		||||
        self.menubar.redo.triggered.connect(self.call_redo)
 | 
			
		||||
        self.menubar.advanced_edit.triggered.connect(self.call_edit)
 | 
			
		||||
        self.menubar.preferences.triggered.connect(self.call_preferences)
 | 
			
		||||
        self.menubar.change_title.triggered.connect(self.change_title)
 | 
			
		||||
        method = self.change_namespace_dialog
 | 
			
		||||
        self.menubar.change_namespace.triggered.connect(method)
 | 
			
		||||
        self.menubar.add_background.triggered.connect(self.add_background)
 | 
			
		||||
        self.menubar.tools.triggered.connect(self.call_tools)
 | 
			
		||||
        self.menubar.dw.triggered.connect(self.call_dreamwall)
 | 
			
		||||
        self.menubar.about.triggered.connect(self.call_about)
 | 
			
		||||
 | 
			
		||||
        self.layout = QtWidgets.QVBoxLayout(self)
 | 
			
		||||
        self.layout.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        self.layout.setSpacing(0)
 | 
			
		||||
        self.layout.setMenuBar(self.menubar)
 | 
			
		||||
        self.layout.addWidget(self.namespace_widget)
 | 
			
		||||
        self.layout.addWidget(self.tab)
 | 
			
		||||
        self.layout.addWidget(self.quick_options)
 | 
			
		||||
 | 
			
		||||
        self.load_ui_states()
 | 
			
		||||
        self.register_shortcuts()
 | 
			
		||||
 | 
			
		||||
    def register_shortcuts(self):
 | 
			
		||||
        # Unregister all shortcuts before create new ones
 | 
			
		||||
        function_names_actions = {
 | 
			
		||||
            'focus': (self.reset, None),
 | 
			
		||||
            'new': (self.call_new, self.menubar.new),
 | 
			
		||||
            'open': (self.call_open, self.menubar.open),
 | 
			
		||||
            'save': (self.call_save, self.menubar.save),
 | 
			
		||||
            'close': (self.close, self.menubar.exit),
 | 
			
		||||
            'undo': (self.call_undo, self.menubar.undo),
 | 
			
		||||
            'redo': (self.call_redo, self.menubar.redo),
 | 
			
		||||
            'edit': (self.call_edit, self.menubar.advanced_edit),
 | 
			
		||||
            'next_tab': (self.call_next_tab, None),
 | 
			
		||||
            'previous_tab': (self.call_previous_tab, None),
 | 
			
		||||
            }
 | 
			
		||||
        for function_name, sc in self.shortcuts.items():
 | 
			
		||||
            sc.activated.disconnect(function_names_actions[function_name][0])
 | 
			
		||||
            seq = QtGui.QKeySequence()
 | 
			
		||||
            action = function_names_actions[function_name][1]
 | 
			
		||||
            if not action:
 | 
			
		||||
                continue
 | 
			
		||||
            action.setShortcut(seq)
 | 
			
		||||
 | 
			
		||||
        self.shortcuts = {}
 | 
			
		||||
        shortcut_context = QtCore.Qt.WidgetWithChildrenShortcut
 | 
			
		||||
        for function_name, data in get_hotkeys_config().items():
 | 
			
		||||
            if not data['enabled']:
 | 
			
		||||
                continue
 | 
			
		||||
            method = function_names_actions[function_name][0]
 | 
			
		||||
            ks = data['key_sequence']
 | 
			
		||||
            if ks is None:
 | 
			
		||||
                continue
 | 
			
		||||
            sc = set_shortcut(ks, self, method, shortcut_context)
 | 
			
		||||
            self.shortcuts[function_name] = sc
 | 
			
		||||
            # HACK: Need to implement twice the shortcut to display key
 | 
			
		||||
            # sequence in the menu and keep it active when the view is docked.
 | 
			
		||||
            action = function_names_actions[function_name][1]
 | 
			
		||||
            if action is None:
 | 
			
		||||
                continue
 | 
			
		||||
            action.setShortcut(ks)
 | 
			
		||||
            action.setShortcutContext(shortcut_context)
 | 
			
		||||
 | 
			
		||||
    def show(self, *args, **kwargs):
 | 
			
		||||
        super(DwPicker, self).show(
 | 
			
		||||
            closeCallback=CLOSE_CALLBACK_COMMAND, *args, **kwargs)
 | 
			
		||||
        self.register_callbacks()
 | 
			
		||||
 | 
			
		||||
    def close_event(self):
 | 
			
		||||
        self.preferences_window.close()
 | 
			
		||||
 | 
			
		||||
    def update_namespaces(self, *_):
 | 
			
		||||
        namespaces = sorted(list(set(
 | 
			
		||||
            (cmds.namespaceInfo(listOnlyNamespaces=True, recurse=True)) +
 | 
			
		||||
            (pickers_namespaces(self.pickers)))))
 | 
			
		||||
        self.namespace_combo.blockSignals(True)
 | 
			
		||||
        self.namespace_combo.clear()
 | 
			
		||||
        self.namespace_combo.addItem("*Root*")
 | 
			
		||||
        self.namespace_combo.addItems(namespaces)
 | 
			
		||||
        self.namespace_combo.blockSignals(False)
 | 
			
		||||
 | 
			
		||||
    def tab_index_changed(self, index):
 | 
			
		||||
        if not self.pickers:
 | 
			
		||||
            return
 | 
			
		||||
        picker = self.pickers[index]
 | 
			
		||||
        if not picker:
 | 
			
		||||
            return
 | 
			
		||||
        namespace = detect_picker_namespace(picker.shapes)
 | 
			
		||||
        self.namespace_combo.blockSignals(True)
 | 
			
		||||
        if self.namespace_combo.findText(namespace) == -1 and namespace:
 | 
			
		||||
            self.namespace_combo.addItem(namespace)
 | 
			
		||||
        if namespace:
 | 
			
		||||
            self.namespace_combo.setCurrentText(namespace)
 | 
			
		||||
        else:
 | 
			
		||||
            self.namespace_combo.setCurrentIndex(0)
 | 
			
		||||
        self.namespace_combo.blockSignals(False)
 | 
			
		||||
 | 
			
		||||
    def tab_moved(self, newindex, oldindex):
 | 
			
		||||
        lists = (
 | 
			
		||||
            self.editors,
 | 
			
		||||
            self.generals,
 | 
			
		||||
            self.pickers,
 | 
			
		||||
            self.filenames,
 | 
			
		||||
            self.modified_states)
 | 
			
		||||
 | 
			
		||||
        for l in lists:
 | 
			
		||||
            l.insert(newindex, l.pop(oldindex))
 | 
			
		||||
 | 
			
		||||
        self.store_local_pickers_data()
 | 
			
		||||
 | 
			
		||||
    def leaveEvent(self, _):
 | 
			
		||||
        mode = cmds.optionVar(query=AUTO_FOCUS_BEHAVIOR)
 | 
			
		||||
        if mode == 'off':
 | 
			
		||||
            return
 | 
			
		||||
        cmds.setFocus("MayaWindow")
 | 
			
		||||
 | 
			
		||||
    def enterEvent(self, _):
 | 
			
		||||
        mode = cmds.optionVar(query=AUTO_FOCUS_BEHAVIOR)
 | 
			
		||||
        if mode == 'bilateral':
 | 
			
		||||
            cmds.setFocus(self.objectName())
 | 
			
		||||
 | 
			
		||||
    def dockCloseEventTriggered(self):
 | 
			
		||||
        save_opened_filenames([fn for fn in self.filenames if fn])
 | 
			
		||||
        if not any(self.modified_states):
 | 
			
		||||
            return super(DwPicker, self).dockCloseEventTriggered()
 | 
			
		||||
 | 
			
		||||
        msg = (
 | 
			
		||||
            'Some picker have unsaved modification. \n'
 | 
			
		||||
            'Would you like to save them ?')
 | 
			
		||||
        result = QtWidgets.QMessageBox.question(
 | 
			
		||||
            None, 'Save ?', msg,
 | 
			
		||||
            buttons=(
 | 
			
		||||
                QtWidgets.QMessageBox.SaveAll |
 | 
			
		||||
                QtWidgets.QMessageBox.Close),
 | 
			
		||||
            button=QtWidgets.QMessageBox.SaveAll)
 | 
			
		||||
 | 
			
		||||
        if result == QtWidgets.QMessageBox.Close:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        for i in range(self.tab.count()-1, -1, -1):
 | 
			
		||||
            self.save_tab(i)
 | 
			
		||||
 | 
			
		||||
        save_opened_filenames(self.filenames)
 | 
			
		||||
        return super(DwPicker, self).dockCloseEventTriggered()
 | 
			
		||||
 | 
			
		||||
    def reload_callbacks(self):
 | 
			
		||||
        self.unregister_callbacks()
 | 
			
		||||
        self.register_callbacks()
 | 
			
		||||
 | 
			
		||||
    def register_callbacks(self):
 | 
			
		||||
        self.unregister_callbacks()
 | 
			
		||||
        callbacks = {
 | 
			
		||||
            om.MSceneMessage.kBeforeNew: [
 | 
			
		||||
                self.close_tabs, self.update_namespaces],
 | 
			
		||||
            om.MSceneMessage.kAfterOpen: [
 | 
			
		||||
                self.load_saved_pickers, self.update_namespaces],
 | 
			
		||||
            om.MSceneMessage.kAfterCreateReference: [
 | 
			
		||||
                self.load_saved_pickers, self.update_namespaces]}
 | 
			
		||||
        if not cmds.optionVar(query=DISABLE_IMPORT_CALLBACKS):
 | 
			
		||||
            callbacks[om.MSceneMessage.kAfterImport] = [
 | 
			
		||||
                self.load_saved_pickers, self.update_namespaces]
 | 
			
		||||
 | 
			
		||||
        for event, methods in callbacks.items():
 | 
			
		||||
            for method in methods:
 | 
			
		||||
                callback = om.MSceneMessage.addCallback(event, method)
 | 
			
		||||
                self.callbacks.append(callback)
 | 
			
		||||
 | 
			
		||||
        method = self.auto_switch_tab
 | 
			
		||||
        cb = om.MEventMessage.addEventCallback('SelectionChanged', method)
 | 
			
		||||
        self.callbacks.append(cb)
 | 
			
		||||
        method = self.auto_switch_namespace
 | 
			
		||||
        cb = om.MEventMessage.addEventCallback('SelectionChanged', method)
 | 
			
		||||
        self.callbacks.append(cb)
 | 
			
		||||
 | 
			
		||||
        for picker in self.pickers:
 | 
			
		||||
            picker.register_callbacks()
 | 
			
		||||
 | 
			
		||||
    def unregister_callbacks(self):
 | 
			
		||||
        for cb in self.callbacks:
 | 
			
		||||
            om.MMessage.removeCallback(cb)
 | 
			
		||||
            self.callbacks.remove(cb)
 | 
			
		||||
        for picker in self.pickers:
 | 
			
		||||
            picker.unregister_callbacks()
 | 
			
		||||
 | 
			
		||||
    def auto_switch_namespace(self, *_, **__):
 | 
			
		||||
        if not cmds.optionVar(query=AUTO_SET_NAMESPACE):
 | 
			
		||||
            return
 | 
			
		||||
        self.pick_namespace()
 | 
			
		||||
 | 
			
		||||
    def auto_switch_tab(self, *_, **__):
 | 
			
		||||
        if not cmds.optionVar(query=AUTO_SWITCH_TAB):
 | 
			
		||||
            return
 | 
			
		||||
        nodes = cmds.ls(selection=True)
 | 
			
		||||
        if not nodes:
 | 
			
		||||
            return
 | 
			
		||||
        picker = self.tab.currentWidget()
 | 
			
		||||
        if not picker:
 | 
			
		||||
            return
 | 
			
		||||
        targets = list_targets(picker.shapes)
 | 
			
		||||
        if nodes[-1] in targets:
 | 
			
		||||
            return
 | 
			
		||||
        for i, picker in enumerate(self.pickers):
 | 
			
		||||
            if nodes[-1] in list_targets(picker.shapes):
 | 
			
		||||
                self.tab.setCurrentIndex(i)
 | 
			
		||||
                return
 | 
			
		||||
 | 
			
		||||
    def load_saved_pickers(self, *_, **__):
 | 
			
		||||
        self.clear()
 | 
			
		||||
        pickers = load_local_picker_data()
 | 
			
		||||
        if cmds.optionVar(query=CHECK_IMAGES_PATHS):
 | 
			
		||||
            picker = ensure_images_path_exists(pickers)
 | 
			
		||||
        for picker in pickers:
 | 
			
		||||
            self.add_picker(picker)
 | 
			
		||||
        clean_stray_picker_holder_nodes()
 | 
			
		||||
 | 
			
		||||
    def store_local_pickers_data(self):
 | 
			
		||||
        if not self.editable:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        if not self.tab.count():
 | 
			
		||||
            store_local_picker_data([])
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        pickers = [self.picker_data(i) for i in range(self.tab.count())]
 | 
			
		||||
        store_local_picker_data(pickers)
 | 
			
		||||
 | 
			
		||||
    def save_tab(self, index):
 | 
			
		||||
        msg = (
 | 
			
		||||
            'Picker contain unsaved modification !\n'
 | 
			
		||||
            'Woud you like to continue ?')
 | 
			
		||||
        result = QtWidgets.QMessageBox.question(
 | 
			
		||||
            None, 'Save ?', msg,
 | 
			
		||||
            buttons=(
 | 
			
		||||
                QtWidgets.QMessageBox.Save |
 | 
			
		||||
                QtWidgets.QMessageBox.Yes |
 | 
			
		||||
                QtWidgets.QMessageBox.Cancel),
 | 
			
		||||
            button=QtWidgets.QMessageBox.Cancel)
 | 
			
		||||
 | 
			
		||||
        if result == QtWidgets.QMessageBox.Cancel:
 | 
			
		||||
            return False
 | 
			
		||||
        elif result == QtWidgets.QMessageBox.Save and not self.call_save(index):
 | 
			
		||||
            return False
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def close_tabs(self, *_):
 | 
			
		||||
        for i in range(self.tab.count()-1, -1, -1):
 | 
			
		||||
            self.close_tab(i)
 | 
			
		||||
        self.store_local_pickers_data()
 | 
			
		||||
 | 
			
		||||
    def clear(self):
 | 
			
		||||
        for i in range(self.tab.count()-1, -1, -1):
 | 
			
		||||
            self.close_tab(i, force=True)
 | 
			
		||||
 | 
			
		||||
    def close_tab(self, index, force=False, store=False):
 | 
			
		||||
        if self.modified_states[index] and force is False:
 | 
			
		||||
            if not self.save_tab(index):
 | 
			
		||||
                return
 | 
			
		||||
        elif (cmds.optionVar(query=WARN_ON_TAB_CLOSED) and
 | 
			
		||||
              not question('Warning', CLOSE_TAB_WARNING)):
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        editor = self.editors.pop(index)
 | 
			
		||||
        if editor:
 | 
			
		||||
            editor.close()
 | 
			
		||||
        picker = self.pickers.pop(index)
 | 
			
		||||
        picker.unregister_callbacks()
 | 
			
		||||
        picker.close()
 | 
			
		||||
        self.generals.pop(index)
 | 
			
		||||
        self.modified_states.pop(index)
 | 
			
		||||
        self.undo_managers.pop(index)
 | 
			
		||||
        self.filenames.pop(index)
 | 
			
		||||
        self.tab.removeTab(index)
 | 
			
		||||
        if store:
 | 
			
		||||
            self.store_local_pickers_data()
 | 
			
		||||
 | 
			
		||||
    def load_ui_states(self):
 | 
			
		||||
        value = bool(cmds.optionVar(query=DISPLAY_QUICK_OPTIONS))
 | 
			
		||||
        self.quick_options.setVisible(value)
 | 
			
		||||
        value = bool(cmds.optionVar(query=NAMESPACE_TOOLBAR))
 | 
			
		||||
        self.namespace_widget.setVisible(value)
 | 
			
		||||
        self.update_namespaces()
 | 
			
		||||
        for i in range(self.tab.count()):
 | 
			
		||||
            self.set_modified_state(i, self.modified_states[i])
 | 
			
		||||
 | 
			
		||||
    def add_picker_from_file(self, filename):
 | 
			
		||||
        with open(filename, "r") as f:
 | 
			
		||||
            data = ensure_retro_compatibility(json.load(f))
 | 
			
		||||
            ensure_images_path_exists([data])
 | 
			
		||||
            self.add_picker(data, filename=filename)
 | 
			
		||||
        append_recent_filename(filename)
 | 
			
		||||
 | 
			
		||||
    def reset(self):
 | 
			
		||||
        picker = self.tab.currentWidget()
 | 
			
		||||
        if picker:
 | 
			
		||||
            picker.reset()
 | 
			
		||||
 | 
			
		||||
    def create_picker(self, data):
 | 
			
		||||
        picker = PickerView()
 | 
			
		||||
        picker.editable = self.editable
 | 
			
		||||
        picker.register_callbacks()
 | 
			
		||||
        picker.addButtonRequested.connect(self.add_button)
 | 
			
		||||
        picker.updateButtonRequested.connect(self.update_button)
 | 
			
		||||
        picker.deleteButtonRequested.connect(self.delete_buttons)
 | 
			
		||||
        if self.editable:
 | 
			
		||||
            method = partial(self.data_changed_from_picker, picker)
 | 
			
		||||
            picker.dataChanged.connect(method)
 | 
			
		||||
        shapes = [Shape(s) for s in data['shapes']]
 | 
			
		||||
        picker.set_shapes(shapes)
 | 
			
		||||
        picker.reset()
 | 
			
		||||
        picker.zoom_locked = data['general']['zoom_locked']
 | 
			
		||||
        return picker
 | 
			
		||||
 | 
			
		||||
    def add_picker(self, data, filename=None, modified_state=False):
 | 
			
		||||
        picker = self.create_picker(data)
 | 
			
		||||
        insert = cmds.optionVar(query=INSERT_TAB_AFTER_CURRENT)
 | 
			
		||||
        if not insert or self.tab.currentIndex() == self.tab.count() - 1:
 | 
			
		||||
            self.generals.append(data['general'])
 | 
			
		||||
            self.pickers.append(picker)
 | 
			
		||||
            self.editors.append(None)
 | 
			
		||||
            self.undo_managers.append(UndoManager(data))
 | 
			
		||||
            self.filenames.append(filename)
 | 
			
		||||
            self.modified_states.append(modified_state)
 | 
			
		||||
            self.tab.addTab(picker, data['general']['name'])
 | 
			
		||||
            self.tab.setCurrentIndex(self.tab.count() - 1)
 | 
			
		||||
        else:
 | 
			
		||||
            index = self.tab.currentIndex() + 1
 | 
			
		||||
            self.generals.insert(index, data['general'])
 | 
			
		||||
            self.pickers.insert(index, picker)
 | 
			
		||||
            self.editors.insert(index, None)
 | 
			
		||||
            self.undo_managers.insert(index, UndoManager(data))
 | 
			
		||||
            self.filenames.insert(index, filename)
 | 
			
		||||
            self.modified_states.insert(index, modified_state)
 | 
			
		||||
            self.tab.insertTab(index, picker, data['general']['name'])
 | 
			
		||||
            self.tab.setCurrentIndex(index)
 | 
			
		||||
        picker.reset()
 | 
			
		||||
 | 
			
		||||
    def call_open(self):
 | 
			
		||||
        filenames = QtWidgets.QFileDialog.getOpenFileNames(
 | 
			
		||||
            None, "Open a picker...",
 | 
			
		||||
            get_open_directory(),
 | 
			
		||||
            filter="Dreamwall Picker (*.json)")[0]
 | 
			
		||||
        if not filenames:
 | 
			
		||||
            return
 | 
			
		||||
        save_optionvar(LAST_OPEN_DIRECTORY, os.path.dirname(filenames[0]))
 | 
			
		||||
        for filename in filenames:
 | 
			
		||||
            self.add_picker_from_file(filename)
 | 
			
		||||
        self.store_local_pickers_data()
 | 
			
		||||
 | 
			
		||||
    def call_preferences(self):
 | 
			
		||||
        self.preferences_window.show()
 | 
			
		||||
 | 
			
		||||
    def call_save(self, index=None):
 | 
			
		||||
        index = self.tab.currentIndex() if type(index) is not int else index
 | 
			
		||||
        filename = self.filenames[index]
 | 
			
		||||
        if not filename:
 | 
			
		||||
            return self.call_save_as(index=index)
 | 
			
		||||
        return self.save_picker(index, filename)
 | 
			
		||||
 | 
			
		||||
    def call_save_as(self, index=None):
 | 
			
		||||
        index = self.tab.currentIndex() if type(index) is not int else index
 | 
			
		||||
        filename = QtWidgets.QFileDialog.getSaveFileName(
 | 
			
		||||
            None, "Save a picker ...",
 | 
			
		||||
            cmds.optionVar(query=LAST_SAVE_DIRECTORY),
 | 
			
		||||
            filter="Dreamwall Picker (*.json)")[0]
 | 
			
		||||
 | 
			
		||||
        if not filename:
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
        if os.path.exists(filename):
 | 
			
		||||
            msg = '{} already, exists. Do you want to erase it ?'
 | 
			
		||||
            if not question('File exist', msg.format(filename)):
 | 
			
		||||
                return False
 | 
			
		||||
 | 
			
		||||
        self.save_picker(index, filename)
 | 
			
		||||
 | 
			
		||||
    def call_undo(self):
 | 
			
		||||
        index = self.tab.currentIndex()
 | 
			
		||||
        if index < 0:
 | 
			
		||||
            return
 | 
			
		||||
        undo_manager = self.undo_managers[index]
 | 
			
		||||
        undo_manager.undo()
 | 
			
		||||
        self.data_changed_from_undo_manager(index)
 | 
			
		||||
 | 
			
		||||
    def call_redo(self):
 | 
			
		||||
        index = self.tab.currentIndex()
 | 
			
		||||
        if index < 0:
 | 
			
		||||
            return
 | 
			
		||||
        undo_manager = self.undo_managers[index]
 | 
			
		||||
        undo_manager.redo()
 | 
			
		||||
        self.data_changed_from_undo_manager(index)
 | 
			
		||||
 | 
			
		||||
    def save_picker(self, index, filename):
 | 
			
		||||
        self.filenames[index] = filename
 | 
			
		||||
        save_optionvar(LAST_SAVE_DIRECTORY, os.path.dirname(filename))
 | 
			
		||||
        append_recent_filename(filename)
 | 
			
		||||
        with open(filename, 'w') as f:
 | 
			
		||||
            json.dump(self.picker_data(index), f, indent=2)
 | 
			
		||||
 | 
			
		||||
        self.set_modified_state(index, False)
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def call_import(self):
 | 
			
		||||
        sources = QtWidgets.QFileDialog.getOpenFileNames(
 | 
			
		||||
            None, "Import a picker...",
 | 
			
		||||
            get_import_directory(),
 | 
			
		||||
            filter="Anim School Picker (*.pkr)")[0]
 | 
			
		||||
        if not sources:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        dst = QtWidgets.QFileDialog.getExistingDirectory(
 | 
			
		||||
            None,
 | 
			
		||||
            "Conversion destination",
 | 
			
		||||
            os.path.dirname(sources[0]),
 | 
			
		||||
            options=QtWidgets.QFileDialog.ShowDirsOnly)
 | 
			
		||||
        if not dst:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        save_optionvar(LAST_IMPORT_DIRECTORY, os.path.dirname(sources[0]))
 | 
			
		||||
        for src in sources:
 | 
			
		||||
            filename = animschool.convert(src, dst)
 | 
			
		||||
            self.add_picker_from_file(filename)
 | 
			
		||||
 | 
			
		||||
    def call_new(self):
 | 
			
		||||
        self.add_picker({
 | 
			
		||||
            'general': PICKER.copy(),
 | 
			
		||||
            'shapes': []})
 | 
			
		||||
        self.store_local_pickers_data()
 | 
			
		||||
 | 
			
		||||
    def picker_data(self, index=None):
 | 
			
		||||
        index = self.tab.currentIndex() if type(index) is not int else index
 | 
			
		||||
        if index < 0:
 | 
			
		||||
            return None
 | 
			
		||||
        picker = self.tab.widget(index)
 | 
			
		||||
        return {
 | 
			
		||||
            'version': VERSION,
 | 
			
		||||
            'general': self.generals[index],
 | 
			
		||||
            'shapes': [shape.options for shape in picker.shapes]}
 | 
			
		||||
 | 
			
		||||
    def call_edit(self):
 | 
			
		||||
        index = self.tab.currentIndex()
 | 
			
		||||
        if index < 0:
 | 
			
		||||
            QtWidgets.QMessageBox.warning(self, "Warning", "No picker set")
 | 
			
		||||
            return
 | 
			
		||||
        if self.editors[index] is None:
 | 
			
		||||
            data = self.picker_data()
 | 
			
		||||
            undo_manager = self.undo_managers[index]
 | 
			
		||||
            editor = PickerEditor(
 | 
			
		||||
                picker_data=data,
 | 
			
		||||
                undo_manager=undo_manager,
 | 
			
		||||
                parent=self)
 | 
			
		||||
            picker = self.pickers[index]
 | 
			
		||||
            method = partial(self.data_changed_from_editor, picker=picker)
 | 
			
		||||
            editor.pickerDataModified.connect(method)
 | 
			
		||||
            self.editors[index] = editor
 | 
			
		||||
 | 
			
		||||
        self.editors[index].show()
 | 
			
		||||
 | 
			
		||||
    def call_next_tab(self):
 | 
			
		||||
        index = self.tab.currentIndex() + 1
 | 
			
		||||
        if index == self.tab.count():
 | 
			
		||||
            index = 0
 | 
			
		||||
        self.tab.setCurrentIndex(index)
 | 
			
		||||
 | 
			
		||||
    def call_previous_tab(self):
 | 
			
		||||
        index = self.tab.currentIndex() - 1
 | 
			
		||||
        if index < 0:
 | 
			
		||||
            index = self.tab.count() - 1
 | 
			
		||||
        self.tab.setCurrentIndex(index)
 | 
			
		||||
 | 
			
		||||
    def set_editable(self, state):
 | 
			
		||||
        self.editable = state
 | 
			
		||||
        self.menubar.set_editable(state)
 | 
			
		||||
        for picker in self.pickers:
 | 
			
		||||
            picker.editable = state
 | 
			
		||||
 | 
			
		||||
    def set_modified_state(self, index, state):
 | 
			
		||||
        """
 | 
			
		||||
        Update the tab icon. Add a "save" icon if tab contains unsaved
 | 
			
		||||
        modifications.
 | 
			
		||||
        """
 | 
			
		||||
        if not self.filenames[index]:
 | 
			
		||||
            return
 | 
			
		||||
        self.modified_states[index] = state
 | 
			
		||||
        use_icon = cmds.optionVar(query=USE_ICON_FOR_UNSAVED_TAB)
 | 
			
		||||
        icon_ = icon('save.png') if state and use_icon else QtGui.QIcon()
 | 
			
		||||
        self.tab.setTabIcon(index, icon_)
 | 
			
		||||
        title = self.generals[index]['name']
 | 
			
		||||
        title = "*" + title if state and not use_icon else title
 | 
			
		||||
        self.tab.setTabText(index, title)
 | 
			
		||||
 | 
			
		||||
    def call_tools(self):
 | 
			
		||||
        webbrowser.open(DW_GITHUB)
 | 
			
		||||
 | 
			
		||||
    def call_dreamwall(self):
 | 
			
		||||
        webbrowser.open(DW_WEBSITE)
 | 
			
		||||
 | 
			
		||||
    def call_about(self):
 | 
			
		||||
        QtWidgets.QMessageBox.about(self, 'About', ABOUT)
 | 
			
		||||
 | 
			
		||||
    def sizeHint(self):
 | 
			
		||||
        return QtCore.QSize(500, 800)
 | 
			
		||||
 | 
			
		||||
    def add_button(self, x, y, button_type):
 | 
			
		||||
        targets = cmds.ls(selection=True)
 | 
			
		||||
        if not targets and button_type <= 1:
 | 
			
		||||
            return warning("Warning", "No targets selected")
 | 
			
		||||
 | 
			
		||||
        if button_type == 1:
 | 
			
		||||
            overrides = self.quick_options.values
 | 
			
		||||
            shapes = build_multiple_shapes(targets, overrides)
 | 
			
		||||
            if not shapes:
 | 
			
		||||
                return
 | 
			
		||||
            picker = self.tab.currentWidget()
 | 
			
		||||
            picker.drag_shapes = shapes
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        data = BUTTON.copy()
 | 
			
		||||
        data['shape.left'] = x
 | 
			
		||||
        data['shape.top'] = y
 | 
			
		||||
        data.update(self.quick_options.values)
 | 
			
		||||
        if button_type == 0:
 | 
			
		||||
            data['action.targets'] = targets
 | 
			
		||||
        else:
 | 
			
		||||
            text, result = (
 | 
			
		||||
                QtWidgets.QInputDialog.getText(self, 'Button text', 'text'))
 | 
			
		||||
            if not result:
 | 
			
		||||
                return
 | 
			
		||||
            data['text.content'] = text
 | 
			
		||||
            command = deepcopy(COMMAND)
 | 
			
		||||
            languages = ['python', 'mel']
 | 
			
		||||
            language = languages[cmds.optionVar(query=LAST_COMMAND_LANGUAGE)]
 | 
			
		||||
            command['language'] = language
 | 
			
		||||
            dialog = CommandEditorDialog(command)
 | 
			
		||||
            if not dialog.exec_():
 | 
			
		||||
                return
 | 
			
		||||
            command = dialog.command_data()
 | 
			
		||||
            index = languages.index(command['language'])
 | 
			
		||||
            save_optionvar(LAST_COMMAND_LANGUAGE, index)
 | 
			
		||||
            data['action.commands'] = [command]
 | 
			
		||||
 | 
			
		||||
        width = max([data['shape.width'], len(data['text.content']) * 7])
 | 
			
		||||
        data['shape.width'] = width
 | 
			
		||||
        self.add_shape_to_current_picker(Shape(data))
 | 
			
		||||
 | 
			
		||||
    def update_button(self, shape):
 | 
			
		||||
        picker = self.tab.currentWidget()
 | 
			
		||||
        shape.set_targets(cmds.ls(selection=True))
 | 
			
		||||
        self.data_changed_from_picker(picker)
 | 
			
		||||
 | 
			
		||||
    def delete_buttons(self):
 | 
			
		||||
        picker = self.tab.currentWidget()
 | 
			
		||||
        selected_shapes = [s for s in picker.shapes if s.selected]
 | 
			
		||||
        for shape in selected_shapes:
 | 
			
		||||
            picker.shapes.remove(shape)
 | 
			
		||||
        self.data_changed_from_picker(picker)
 | 
			
		||||
 | 
			
		||||
    def add_shape_to_current_picker(self, shape, prepend=False):
 | 
			
		||||
        picker = self.tab.currentWidget()
 | 
			
		||||
        if prepend:
 | 
			
		||||
            picker.shapes.insert(0, shape)
 | 
			
		||||
        else:
 | 
			
		||||
            picker.shapes.append(shape)
 | 
			
		||||
        self.data_changed_from_picker(picker)
 | 
			
		||||
 | 
			
		||||
    def data_changed_from_picker(self, picker):
 | 
			
		||||
        index = self.tab.indexOf(picker)
 | 
			
		||||
        data = self.picker_data(index)
 | 
			
		||||
        if self.editors[index]:
 | 
			
		||||
            self.editors[index].set_picker_data(data)
 | 
			
		||||
        self.set_modified_state(index, True)
 | 
			
		||||
        picker.repaint()
 | 
			
		||||
        self.undo_managers[index].set_data_modified(data)
 | 
			
		||||
        self.store_local_pickers_data()
 | 
			
		||||
 | 
			
		||||
    def data_changed_from_editor(self, data, picker):
 | 
			
		||||
        index = self.tab.indexOf(picker)
 | 
			
		||||
        self.generals[index] = data['general']
 | 
			
		||||
        shapes = [Shape(s) for s in data['shapes']]
 | 
			
		||||
        picker.set_shapes(shapes)
 | 
			
		||||
        picker.zoom_locked = data['general']['zoom_locked']
 | 
			
		||||
        self.set_title(index, data['general']['name'])
 | 
			
		||||
        self.set_modified_state(index, True)
 | 
			
		||||
        self.store_local_pickers_data()
 | 
			
		||||
 | 
			
		||||
    def data_changed_from_undo_manager(self, index):
 | 
			
		||||
        data = self.undo_managers[index].data
 | 
			
		||||
        if self.editors[index]:
 | 
			
		||||
            self.editors[index].set_picker_data(data)
 | 
			
		||||
        self.data_changed_from_editor(data, self.pickers[index])
 | 
			
		||||
 | 
			
		||||
    def change_title(self, index=None):
 | 
			
		||||
        if not self.editable:
 | 
			
		||||
            return
 | 
			
		||||
        index = self.tab.currentIndex() if type(index) is not int else index
 | 
			
		||||
        if index < 0:
 | 
			
		||||
            return
 | 
			
		||||
        title, operate = QtWidgets.QInputDialog.getText(
 | 
			
		||||
            None, 'Change picker title', 'New title')
 | 
			
		||||
 | 
			
		||||
        if not operate:
 | 
			
		||||
            return
 | 
			
		||||
        self.set_title(index, title)
 | 
			
		||||
        self.data_changed_from_picker(self.tab.widget(index))
 | 
			
		||||
 | 
			
		||||
    def set_title(self, index, title):
 | 
			
		||||
        self.generals[index]['name'] = title
 | 
			
		||||
        use_icon = cmds.optionVar(query=USE_ICON_FOR_UNSAVED_TAB)
 | 
			
		||||
        if not use_icon and self.modified_states[index]:
 | 
			
		||||
            title = "*" + title
 | 
			
		||||
        self.tab.setTabText(index, title)
 | 
			
		||||
 | 
			
		||||
    def change_namespace_dialog(self):
 | 
			
		||||
        dialog = NamespaceDialog()
 | 
			
		||||
        if not dialog.exec_():
 | 
			
		||||
            return
 | 
			
		||||
        namespace = dialog.namespace
 | 
			
		||||
        self.change_namespace(namespace)
 | 
			
		||||
 | 
			
		||||
    def change_namespace_combo(self):
 | 
			
		||||
        index = self.namespace_combo.currentIndex()
 | 
			
		||||
        text = self.namespace_combo.currentText()
 | 
			
		||||
        namespace = text if index else ":"
 | 
			
		||||
        self.change_namespace(namespace)
 | 
			
		||||
 | 
			
		||||
    def pick_namespace(self):
 | 
			
		||||
        namespace = selected_namespace()
 | 
			
		||||
        self.namespace_combo.setCurrentText(namespace)
 | 
			
		||||
 | 
			
		||||
    def change_namespace(self, namespace):
 | 
			
		||||
        picker = self.tab.currentWidget()
 | 
			
		||||
        for shape in picker.shapes:
 | 
			
		||||
            if not shape.targets():
 | 
			
		||||
                continue
 | 
			
		||||
            targets = [switch_namespace(t, namespace) for t in shape.targets()]
 | 
			
		||||
            shape.options['action.targets'] = targets
 | 
			
		||||
 | 
			
		||||
        self.data_changed_from_picker(picker)
 | 
			
		||||
 | 
			
		||||
    def add_background(self):
 | 
			
		||||
        filename = get_image_path(self)
 | 
			
		||||
        if not filename:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        shape = BACKGROUND.copy()
 | 
			
		||||
        shape['image.path'] = filename
 | 
			
		||||
        image = QtGui.QImage(filename)
 | 
			
		||||
        shape['image.width'] = image.size().width()
 | 
			
		||||
        shape['image.height'] = image.size().height()
 | 
			
		||||
        shape['shape.width'] = image.size().width()
 | 
			
		||||
        shape['shape.height'] = image.size().height()
 | 
			
		||||
        shape['bgcolor.transparency'] = 255
 | 
			
		||||
        shape = Shape(shape)
 | 
			
		||||
        self.add_shape_to_current_picker(shape, prepend=True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DwPickerMenu(QtWidgets.QMenuBar):
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(DwPickerMenu, self).__init__(parent)
 | 
			
		||||
        self.new = QtWidgets.QAction('&New', parent)
 | 
			
		||||
        self.open = QtWidgets.QAction('&Open', parent)
 | 
			
		||||
        self.import_ = QtWidgets.QAction('&Import', parent)
 | 
			
		||||
        self.save = QtWidgets.QAction('&Save', parent)
 | 
			
		||||
        self.save_as = QtWidgets.QAction('&Save as', parent)
 | 
			
		||||
        self.exit = QtWidgets.QAction('Exit', parent)
 | 
			
		||||
 | 
			
		||||
        self.undo = QtWidgets.QAction('Undo', parent)
 | 
			
		||||
        self.redo = QtWidgets.QAction('Redo', parent)
 | 
			
		||||
 | 
			
		||||
        self.advanced_edit = QtWidgets.QAction('Advanced &editing', parent)
 | 
			
		||||
        self.preferences = QtWidgets.QAction('Preferences', parent)
 | 
			
		||||
        self.change_title = QtWidgets.QAction('Change picker title', parent)
 | 
			
		||||
        self.change_namespace = QtWidgets.QAction('Change namespace', parent)
 | 
			
		||||
        self.add_background = QtWidgets.QAction('Add background item', parent)
 | 
			
		||||
 | 
			
		||||
        self.tools = QtWidgets.QAction('Other DreamWall &tools', parent)
 | 
			
		||||
        self.dw = QtWidgets.QAction('&About DreamWall', parent)
 | 
			
		||||
        self.about = QtWidgets.QAction('&About DwPicker', parent)
 | 
			
		||||
 | 
			
		||||
        self.file = QtWidgets.QMenu('&File', parent)
 | 
			
		||||
        self.file.addAction(self.new)
 | 
			
		||||
        self.file.addAction(self.open)
 | 
			
		||||
        self.file.addAction(self.import_)
 | 
			
		||||
        self.file.addSeparator()
 | 
			
		||||
        self.file.addAction(self.save)
 | 
			
		||||
        self.file.addAction(self.save_as)
 | 
			
		||||
        self.file.addSeparator()
 | 
			
		||||
        self.file.addAction(self.exit)
 | 
			
		||||
 | 
			
		||||
        self.edit = QtWidgets.QMenu('&Edit', parent)
 | 
			
		||||
        self.edit.addAction(self.undo)
 | 
			
		||||
        self.edit.addAction(self.redo)
 | 
			
		||||
        self.edit.addSeparator()
 | 
			
		||||
        self.edit.addAction(self.advanced_edit)
 | 
			
		||||
        self.edit.addAction(self.preferences)
 | 
			
		||||
        self.edit.addSeparator()
 | 
			
		||||
        self.edit.addAction(self.change_title)
 | 
			
		||||
        self.edit.addSeparator()
 | 
			
		||||
        self.edit.addAction(self.change_namespace)
 | 
			
		||||
        self.edit.addAction(self.add_background)
 | 
			
		||||
 | 
			
		||||
        self.help = QtWidgets.QMenu('&Help', parent)
 | 
			
		||||
        self.help.addAction(self.tools)
 | 
			
		||||
        self.help.addAction(self.dw)
 | 
			
		||||
        self.help.addSeparator()
 | 
			
		||||
        self.help.addAction(self.about)
 | 
			
		||||
 | 
			
		||||
        self.addMenu(self.file)
 | 
			
		||||
        self.addMenu(self.edit)
 | 
			
		||||
        self.addMenu(self.help)
 | 
			
		||||
 | 
			
		||||
    def set_editable(self, state):
 | 
			
		||||
        self.undo.setEnabled(state)
 | 
			
		||||
        self.redo.setEnabled(state)
 | 
			
		||||
        self.change_title.setEnabled(state)
 | 
			
		||||
        self.advanced_edit.setEnabled(state)
 | 
			
		||||
        self.add_background.setEnabled(state)
 | 
			
		||||
							
								
								
									
										65
									
								
								Scripts/Animation/dwpicker/dwpicker/namespace.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,65 @@
 | 
			
		||||
 | 
			
		||||
from contextlib import contextmanager
 | 
			
		||||
from maya import cmds
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def detect_picker_namespace(shapes):
 | 
			
		||||
    targets = {target for shape in shapes for target in shape.targets()}
 | 
			
		||||
    namespaces = {ns for ns in [node_namespace(t) for t in targets] if ns}
 | 
			
		||||
    if len(namespaces) != 1:
 | 
			
		||||
        return None
 | 
			
		||||
    return list(namespaces)[0]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def pickers_namespaces(pickers):
 | 
			
		||||
    targets = {t for p in pickers for s in p.shapes for t in s.targets()}
 | 
			
		||||
    namespaces = {ns for ns in [node_namespace(t) for t in targets] if ns}
 | 
			
		||||
    return sorted(list(namespaces))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def node_namespace(node):
 | 
			
		||||
    basename = node.split("|")[-1]
 | 
			
		||||
    if ":" not in node:
 | 
			
		||||
        return None
 | 
			
		||||
    return basename.split(":")[0]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@contextmanager
 | 
			
		||||
def maya_namespace(
 | 
			
		||||
        namespace='', create_if_missing=True, restore_current_namespace=True):
 | 
			
		||||
    """Context manager to temporarily set a namespace"""
 | 
			
		||||
    initial_namespace = ':' + cmds.namespaceInfo(currentNamespace=True)
 | 
			
		||||
    if not namespace.startswith(':'):
 | 
			
		||||
        namespace = ':' + namespace
 | 
			
		||||
    try:
 | 
			
		||||
        if not cmds.namespace(absoluteName=True, exists=namespace):
 | 
			
		||||
            if create_if_missing:
 | 
			
		||||
                cmds.namespace(setNamespace=':')
 | 
			
		||||
                namespace = cmds.namespace(addNamespace=namespace)
 | 
			
		||||
            else:
 | 
			
		||||
                cmds.namespace(initial_namespace)
 | 
			
		||||
                raise ValueError(namespace + " doesn't exist.")
 | 
			
		||||
        cmds.namespace(setNamespace=namespace)
 | 
			
		||||
        yield namespace
 | 
			
		||||
    finally:
 | 
			
		||||
        if restore_current_namespace:
 | 
			
		||||
            cmds.namespace(setNamespace=initial_namespace)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def switch_namespace(name, namespace):
 | 
			
		||||
    basename = name.split("|")[-1]
 | 
			
		||||
    name = basename if ":" not in basename else basename.split(":")[-1]
 | 
			
		||||
    if not namespace:
 | 
			
		||||
        return name
 | 
			
		||||
    return namespace + ":" + name
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def selected_namespace():
 | 
			
		||||
    selection = cmds.ls(selection=True)
 | 
			
		||||
    if not selection:
 | 
			
		||||
        return ":"
 | 
			
		||||
    node = selection[0]
 | 
			
		||||
    basename = node.split("|")[-1]
 | 
			
		||||
    if ":" not in node:
 | 
			
		||||
        return None
 | 
			
		||||
    return basename.split(":")[0]
 | 
			
		||||
							
								
								
									
										159
									
								
								Scripts/Animation/dwpicker/dwpicker/optionvar.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,159 @@
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
from maya import cmds
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
AUTO_FOCUS_BEHAVIORS = ['off', 'bilateral', 'pickertomaya']
 | 
			
		||||
ZOOM_BUTTONS = ["left", "middle", "right"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
AUTO_FOCUS_BEHAVIOR = 'dwpicker_auto_focus_behavior'
 | 
			
		||||
AUTO_COLLAPSE_IMG_PATH_FROM_ENV = 'dwpicker_auto_collapse_image_path_from_env'
 | 
			
		||||
AUTO_SET_NAMESPACE = 'dwpicker_auto_set_namespace'
 | 
			
		||||
AUTO_SWITCH_TAB = 'dwpicker_auto_switch_tab'
 | 
			
		||||
BG_LOCKED = 'dwpicker_designer_background_items_locked'
 | 
			
		||||
CHECK_IMAGES_PATHS = 'dwpicker_check_images_paths'
 | 
			
		||||
CHECK_FOR_UPDATE = 'dwpicker_check_for_update'
 | 
			
		||||
CUSTOM_PROD_PICKER_DIRECTORY = 'dwpicker_custom_prod_picker_directory'
 | 
			
		||||
DEFAULT_BG_COLOR = 'dwpicker_default_background_color'
 | 
			
		||||
DEFAULT_HOTKEYS = 'dwpicker_default_hotkeys'
 | 
			
		||||
DEFAULT_LABEL = 'dwpicker_default_label_color'
 | 
			
		||||
DEFAULT_HEIGHT = 'dwpicker_default_height'
 | 
			
		||||
DEFAULT_TEXT_COLOR = 'dwpicker_default_text_color'
 | 
			
		||||
DEFAULT_WIDTH = 'dwpicker_default_width'
 | 
			
		||||
DISABLE_IMPORT_CALLBACKS = 'dwpicker_disable_import_callbacks'
 | 
			
		||||
DISPLAY_QUICK_OPTIONS = 'dwpicker_display_quick_options'
 | 
			
		||||
OVERRIDE_PROD_PICKER_DIRECTORY_ENV = 'dwpicker_override_picker_directory_env'
 | 
			
		||||
INSERT_TAB_AFTER_CURRENT = 'dwpicker_insert_tab_after_current'
 | 
			
		||||
LAST_COMMAND_LANGUAGE = 'dwpicker_last_command_language_used'
 | 
			
		||||
LAST_IMAGE_DIRECTORY_USED = 'dwpicker_last_directory_used'
 | 
			
		||||
LAST_IMPORT_DIRECTORY = 'dwpicker_last_file_import_directory'
 | 
			
		||||
LAST_OPEN_DIRECTORY = 'dwpicker_last_file_open_directory'
 | 
			
		||||
LAST_SAVE_DIRECTORY = 'dwpicker_last_file_save_directory'
 | 
			
		||||
OPENED_FILES = 'dwpicker_opened_files'
 | 
			
		||||
NAMESPACE_TOOLBAR = 'dwpicker_display_dwtoolbar'
 | 
			
		||||
RECENT_FILES = 'dwpicker_recent_files'
 | 
			
		||||
SEARCH_FIELD_INDEX = 'dwpicker_designer_search_field_index'
 | 
			
		||||
SETTINGS_GROUP_TO_COPY = 'dwpicker_settings_group_to_copy'
 | 
			
		||||
SETTINGS_TO_COPY = 'dwpicker_settings_to_copy'
 | 
			
		||||
SHAPES_FILTER_INDEX = 'dwpicker_designer_shape_filter_index'
 | 
			
		||||
SNAP_ITEMS = 'dwpicker_designer_snap_items'
 | 
			
		||||
SNAP_GRID_X = 'dwpicker_designer_snap_x'
 | 
			
		||||
SNAP_GRID_Y = 'dwpicker_designer_snap_y'
 | 
			
		||||
SYNCHRONYZE_SELECTION = 'dwpicker_synchronize_selection'
 | 
			
		||||
TRIGGER_REPLACE_ON_MIRROR = 'dwpicker_trigger_search_and_replace_on_mirror'
 | 
			
		||||
USE_BASE64_DATA_ENCODING = 'dwpicker_use_base64_data_encoding'
 | 
			
		||||
USE_ICON_FOR_UNSAVED_TAB = 'dwpicker_use_icon_for_unsaved_tab'
 | 
			
		||||
USE_PROD_PICKER_DIR_AS_DEFAULT = 'dwpicker_user_prod_picker_dir_for_import'
 | 
			
		||||
ZOOM_BUTTON = 'dwpicker_picker_zoom_mouse_button'
 | 
			
		||||
WARN_ON_TAB_CLOSED = 'dwpicker_warn_on_tab_closed'
 | 
			
		||||
ZOOM_SENSITIVITY = 'dwpicker_zoom_sensitivity'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
OPTIONVARS = {
 | 
			
		||||
    AUTO_FOCUS_BEHAVIOR: AUTO_FOCUS_BEHAVIORS[-1],
 | 
			
		||||
    AUTO_SWITCH_TAB: 0,
 | 
			
		||||
    AUTO_SET_NAMESPACE: 0,
 | 
			
		||||
    AUTO_COLLAPSE_IMG_PATH_FROM_ENV: 1,
 | 
			
		||||
    BG_LOCKED: 1,
 | 
			
		||||
    CHECK_IMAGES_PATHS: 1,
 | 
			
		||||
    # We disable this default feature for maya 2023. It seems that the github
 | 
			
		||||
    # request can cause a maya crash due to an incompatibility with the python
 | 
			
		||||
    # with this specific version of Maya.
 | 
			
		||||
    CHECK_FOR_UPDATE: int(cmds.about(majorVersion=True) != '2023'),
 | 
			
		||||
    CUSTOM_PROD_PICKER_DIRECTORY: '',
 | 
			
		||||
    DEFAULT_BG_COLOR: '#777777',
 | 
			
		||||
    DEFAULT_HEIGHT: 20,
 | 
			
		||||
    DEFAULT_LABEL: '',
 | 
			
		||||
    DEFAULT_TEXT_COLOR: '#000000',
 | 
			
		||||
    DEFAULT_HOTKEYS: (
 | 
			
		||||
        'focus=F,1;new=CTRL+N,1;open=CTRL+O,1;save=CTRL+S,1;close=CTRL+Q,1;'
 | 
			
		||||
        'undo=CTRL+Z,1;redo=CTRL+Y,1;edit=CTRL+E,1;next_tab=None,0;'
 | 
			
		||||
        'previous_tab=None,0'),
 | 
			
		||||
    DEFAULT_WIDTH: 30,
 | 
			
		||||
    DISABLE_IMPORT_CALLBACKS: 1,
 | 
			
		||||
    DISPLAY_QUICK_OPTIONS: 1,
 | 
			
		||||
    OVERRIDE_PROD_PICKER_DIRECTORY_ENV: 0,
 | 
			
		||||
    INSERT_TAB_AFTER_CURRENT: 0,
 | 
			
		||||
    LAST_OPEN_DIRECTORY: os.path.expanduser("~"),
 | 
			
		||||
    LAST_SAVE_DIRECTORY: os.path.expanduser("~"),
 | 
			
		||||
    LAST_IMPORT_DIRECTORY: os.path.expanduser("~"),
 | 
			
		||||
    LAST_COMMAND_LANGUAGE: 0,  # 0 = python, 1 = mel
 | 
			
		||||
    LAST_IMAGE_DIRECTORY_USED: os.path.expanduser("~"),
 | 
			
		||||
    NAMESPACE_TOOLBAR: 0,
 | 
			
		||||
    OPENED_FILES: '',
 | 
			
		||||
    RECENT_FILES: '',
 | 
			
		||||
    SEARCH_FIELD_INDEX: 0,
 | 
			
		||||
    SHAPES_FILTER_INDEX: 0,
 | 
			
		||||
    SETTINGS_GROUP_TO_COPY: 'bordercolor;text;image;bgcolor;shape;borderwidth;border',
 | 
			
		||||
    SETTINGS_TO_COPY: (
 | 
			
		||||
        'bgcolor.clicked;bgcolor.hovered;bgcolor.normal;bgcolor.transparency;'
 | 
			
		||||
        'border;bordercolor.clicked;bordercolor.hovered;bordercolor.normal;'
 | 
			
		||||
        'bordercolor.transparency;borderwidth.clicked;borderwidth.hovered;'
 | 
			
		||||
        'borderwidth.normal;image.fit;image.height;image.width;shape;'
 | 
			
		||||
        'shape.cornersx;shape.cornersy;shape.height;shape.left;'
 | 
			
		||||
        'shape.top;shape.width;text.bold;text.color;text.halign;text.italic;'
 | 
			
		||||
        'text.size;text.valign'),
 | 
			
		||||
    SNAP_ITEMS: 0,
 | 
			
		||||
    SNAP_GRID_X: 10,
 | 
			
		||||
    SNAP_GRID_Y: 10,
 | 
			
		||||
    SYNCHRONYZE_SELECTION: 1,
 | 
			
		||||
    TRIGGER_REPLACE_ON_MIRROR: 0,
 | 
			
		||||
    USE_BASE64_DATA_ENCODING: 0,
 | 
			
		||||
    USE_ICON_FOR_UNSAVED_TAB: 1,
 | 
			
		||||
    USE_PROD_PICKER_DIR_AS_DEFAULT: 0,
 | 
			
		||||
    WARN_ON_TAB_CLOSED: 0,
 | 
			
		||||
    ZOOM_BUTTON: ZOOM_BUTTONS[2],
 | 
			
		||||
    ZOOM_SENSITIVITY: 50
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
TYPES = {
 | 
			
		||||
    int: 'intValue',
 | 
			
		||||
    float: 'floatValue',
 | 
			
		||||
    str: 'stringValue'}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Ensure backward compatibility.
 | 
			
		||||
if sys.version_info[0] == 2:
 | 
			
		||||
    TYPES[unicode] = 'stringValue'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def ensure_optionvars_exists():
 | 
			
		||||
    for optionvar, default_value in OPTIONVARS.items():
 | 
			
		||||
        if cmds.optionVar(exists=optionvar):
 | 
			
		||||
            continue
 | 
			
		||||
        save_optionvar(optionvar, default_value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def save_optionvar(optionvar, value):
 | 
			
		||||
    kwargs = {TYPES.get(type(value)): [optionvar, value]}
 | 
			
		||||
    cmds.optionVar(**kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def save_opened_filenames(filenames):
 | 
			
		||||
    save_optionvar(OPENED_FILES, ";".join(filenames))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def append_recent_filename(filename):
 | 
			
		||||
    filename = os.path.normpath(filename)
 | 
			
		||||
    stored_filenames = cmds.optionVar(query=RECENT_FILES)
 | 
			
		||||
    if not stored_filenames:
 | 
			
		||||
        cmds.optionVar(stringValue=[RECENT_FILES, filename + ';'])
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    # Just reorder list if the filename is already in the recent filenames.
 | 
			
		||||
    stored_filenames = stored_filenames.split(';')
 | 
			
		||||
    for stored_filename in stored_filenames:
 | 
			
		||||
        if os.path.normpath(stored_filename) == filename:
 | 
			
		||||
            stored_filenames.remove(stored_filename)
 | 
			
		||||
            stored_filenames.insert(0, filename)
 | 
			
		||||
            cmds.optionVar(
 | 
			
		||||
                stringValue=[RECENT_FILES, ';'.join(stored_filenames)])
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
    # Append to list if new filename.
 | 
			
		||||
    if len(stored_filenames) >= 10:
 | 
			
		||||
        stored_filenames = stored_filenames[:9]
 | 
			
		||||
    stored_filenames.insert(0, filename)
 | 
			
		||||
    cmds.optionVar(stringValue=[RECENT_FILES, ';'.join(stored_filenames)])
 | 
			
		||||
							
								
								
									
										152
									
								
								Scripts/Animation/dwpicker/dwpicker/painting.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,152 @@
 | 
			
		||||
from PySide2 import QtCore, QtGui
 | 
			
		||||
from maya import cmds
 | 
			
		||||
 | 
			
		||||
from dwpicker.optionvar import ZOOM_SENSITIVITY
 | 
			
		||||
from dwpicker.qtutils import VALIGNS, HALIGNS
 | 
			
		||||
from dwpicker.geometry import grow_rect, ViewportMapper
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
SELECTION_COLOR = '#3388FF'
 | 
			
		||||
MANIPULATOR_BORDER = 5
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def factor_sensitivity(factor):
 | 
			
		||||
    sensitivity = cmds.optionVar(query=ZOOM_SENSITIVITY) / 50.0
 | 
			
		||||
    return factor * sensitivity
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def draw_editor(painter, rect, snap=None, viewportmapper=None):
 | 
			
		||||
    viewportmapper = viewportmapper or ViewportMapper()
 | 
			
		||||
    rect = viewportmapper.to_viewport_rect(rect)
 | 
			
		||||
    # draw border
 | 
			
		||||
    pen = QtGui.QPen(QtGui.QColor('#333333'))
 | 
			
		||||
    pen.setStyle(QtCore.Qt.DashDotLine)
 | 
			
		||||
    pen.setWidthF(viewportmapper.to_viewport(3))
 | 
			
		||||
    brush = QtGui.QBrush(QtGui.QColor(255, 255, 255, 25))
 | 
			
		||||
    painter.setPen(pen)
 | 
			
		||||
    painter.setBrush(brush)
 | 
			
		||||
    painter.drawRect(rect)
 | 
			
		||||
 | 
			
		||||
    if snap is None:
 | 
			
		||||
        return
 | 
			
		||||
    # draw snap grid
 | 
			
		||||
    snap = viewportmapper.to_viewport(snap[0]), viewportmapper.to_viewport(snap[1])
 | 
			
		||||
    pen = QtGui.QPen(QtGui.QColor('red'))
 | 
			
		||||
    painter.setPen(pen)
 | 
			
		||||
    x = 0
 | 
			
		||||
    y = 0
 | 
			
		||||
    while y < rect.bottom():
 | 
			
		||||
        painter.drawPoint(x, y)
 | 
			
		||||
        x += snap[0]
 | 
			
		||||
        if x > rect.right():
 | 
			
		||||
            x = 0
 | 
			
		||||
            y += snap[1]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def draw_shape(painter, shape, viewportmapper=None):
 | 
			
		||||
    viewportmapper = viewportmapper or ViewportMapper()
 | 
			
		||||
    options = shape.options
 | 
			
		||||
    content_rect = shape.content_rect()
 | 
			
		||||
    if shape.clicked or shape.selected:
 | 
			
		||||
        bordercolor = QtGui.QColor(options['bordercolor.clicked'])
 | 
			
		||||
        backgroundcolor = QtGui.QColor(options['bgcolor.clicked'])
 | 
			
		||||
        bordersize = options['borderwidth.clicked']
 | 
			
		||||
    elif shape.hovered:
 | 
			
		||||
        bordercolor = QtGui.QColor(options['bordercolor.hovered'])
 | 
			
		||||
        backgroundcolor = QtGui.QColor(options['bgcolor.hovered'])
 | 
			
		||||
        bordersize = options['borderwidth.hovered']
 | 
			
		||||
    else:
 | 
			
		||||
        bordercolor = QtGui.QColor(options['bordercolor.normal'])
 | 
			
		||||
        backgroundcolor = QtGui.QColor(options['bgcolor.normal'])
 | 
			
		||||
        bordersize = options['borderwidth.normal']
 | 
			
		||||
 | 
			
		||||
    textcolor = QtGui.QColor(options['text.color'])
 | 
			
		||||
    alpha = options['bordercolor.transparency'] if options['border'] else 255
 | 
			
		||||
    bordercolor.setAlpha(255 - alpha)
 | 
			
		||||
    backgroundcolor.setAlpha(255 - options['bgcolor.transparency'])
 | 
			
		||||
 | 
			
		||||
    pen = QtGui.QPen(bordercolor)
 | 
			
		||||
    pen.setStyle(QtCore.Qt.SolidLine)
 | 
			
		||||
    pen.setWidthF(viewportmapper.to_viewport(bordersize))
 | 
			
		||||
    painter.setPen(pen)
 | 
			
		||||
    painter.setBrush(QtGui.QBrush(backgroundcolor))
 | 
			
		||||
    rect = viewportmapper.to_viewport_rect(shape.rect)
 | 
			
		||||
    if options['shape'] == 'square':
 | 
			
		||||
        painter.drawRect(rect)
 | 
			
		||||
    elif options['shape'] == 'round':
 | 
			
		||||
        painter.drawEllipse(rect)
 | 
			
		||||
    else:  # 'rounded_rect'
 | 
			
		||||
        x = viewportmapper.to_viewport(options['shape.cornersx'])
 | 
			
		||||
        y = viewportmapper.to_viewport(options['shape.cornersy'])
 | 
			
		||||
        painter.drawRoundedRect(rect, x, y)
 | 
			
		||||
 | 
			
		||||
    if shape.pixmap is not None:
 | 
			
		||||
        rect = shape.image_rect or content_rect
 | 
			
		||||
        rect = viewportmapper.to_viewport_rect(rect)
 | 
			
		||||
        painter.drawPixmap(rect.toRect(), shape.pixmap)
 | 
			
		||||
 | 
			
		||||
    painter.setPen(QtGui.QPen(textcolor))
 | 
			
		||||
    painter.setBrush(QtGui.QBrush(textcolor))
 | 
			
		||||
    option = QtGui.QTextOption()
 | 
			
		||||
    flags = VALIGNS[options['text.valign']] | HALIGNS[options['text.halign']]
 | 
			
		||||
    option.setAlignment(flags)
 | 
			
		||||
    font = QtGui.QFont()
 | 
			
		||||
    font.setBold(options['text.bold'])
 | 
			
		||||
    font.setItalic(options['text.italic'])
 | 
			
		||||
    size = round(viewportmapper.to_viewport(options['text.size']))
 | 
			
		||||
    font.setPixelSize(size)
 | 
			
		||||
    painter.setFont(font)
 | 
			
		||||
    text = options['text.content']
 | 
			
		||||
    content_rect = viewportmapper.to_viewport_rect(content_rect)
 | 
			
		||||
    painter.drawText(content_rect, flags, text)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def draw_selection_square(painter, rect, viewportmapper=None):
 | 
			
		||||
    viewportmapper = viewportmapper or ViewportMapper()
 | 
			
		||||
    rect = viewportmapper.to_viewport_rect(rect)
 | 
			
		||||
    bordercolor = QtGui.QColor(SELECTION_COLOR)
 | 
			
		||||
    backgroundcolor = QtGui.QColor(SELECTION_COLOR)
 | 
			
		||||
    backgroundcolor.setAlpha(85)
 | 
			
		||||
    painter.setPen(QtGui.QPen(bordercolor))
 | 
			
		||||
    painter.setBrush(QtGui.QBrush(backgroundcolor))
 | 
			
		||||
    painter.drawRect(rect)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def draw_manipulator(painter, manipulator, cursor, viewportmapper=None):
 | 
			
		||||
    viewportmapper = viewportmapper or ViewportMapper()
 | 
			
		||||
    hovered = manipulator.hovered_rects(cursor)
 | 
			
		||||
 | 
			
		||||
    if manipulator.rect in hovered:
 | 
			
		||||
        pen = QtGui.QPen(QtGui.QColor(0, 0, 0, 0))
 | 
			
		||||
        brush = QtGui.QBrush(QtGui.QColor(125, 125, 125))
 | 
			
		||||
        brush.setStyle(QtCore.Qt.FDiagPattern)
 | 
			
		||||
        painter.setPen(pen)
 | 
			
		||||
        painter.setBrush(brush)
 | 
			
		||||
        painter.drawPath(manipulator.hovered_path)
 | 
			
		||||
 | 
			
		||||
    pen = QtGui.QPen(QtGui.QColor('black'))
 | 
			
		||||
    brush = QtGui.QBrush(QtGui.QColor('white'))
 | 
			
		||||
    painter.setBrush(brush)
 | 
			
		||||
    for rect in manipulator.handler_rects():
 | 
			
		||||
        rect = viewportmapper.to_viewport_rect(rect)
 | 
			
		||||
        pen.setWidth(3 if rect in hovered else 1)
 | 
			
		||||
        painter.setPen(pen)
 | 
			
		||||
        painter.drawEllipse(rect)
 | 
			
		||||
 | 
			
		||||
    pen.setWidth(1)
 | 
			
		||||
    pen.setStyle(QtCore.Qt.DashLine)  # if not moving else QtCore.Qt.SolidLine)
 | 
			
		||||
    painter.setPen(pen)
 | 
			
		||||
    painter.setBrush(QtGui.QBrush(QtGui.QColor(0, 0, 0, 0)))
 | 
			
		||||
    rect = viewportmapper.to_viewport_rect(manipulator.rect)
 | 
			
		||||
    painter.drawRect(rect)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_hovered_path(rect, viewportmapper=None):
 | 
			
		||||
    viewportmapper = viewportmapper or ViewportMapper()
 | 
			
		||||
    rect = viewportmapper.to_viewport_rect(rect)
 | 
			
		||||
    manipulator_rect = grow_rect(
 | 
			
		||||
        rect, viewportmapper.to_viewport(MANIPULATOR_BORDER))
 | 
			
		||||
    path = QtGui.QPainterPath()
 | 
			
		||||
    path.addRect(rect)
 | 
			
		||||
    path.addRect(manipulator_rect)
 | 
			
		||||
    return path
 | 
			
		||||
							
								
								
									
										78
									
								
								Scripts/Animation/dwpicker/dwpicker/path.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,78 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
from maya import cmds
 | 
			
		||||
from dwpicker.optionvar import (
 | 
			
		||||
    AUTO_COLLAPSE_IMG_PATH_FROM_ENV, CUSTOM_PROD_PICKER_DIRECTORY,
 | 
			
		||||
    LAST_IMPORT_DIRECTORY, LAST_IMAGE_DIRECTORY_USED, LAST_OPEN_DIRECTORY,
 | 
			
		||||
    OVERRIDE_PROD_PICKER_DIRECTORY_ENV, USE_PROD_PICKER_DIR_AS_DEFAULT)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def unix_path(path, isroot=False):
 | 
			
		||||
    path = path.replace('\\', '/')
 | 
			
		||||
    condition = (
 | 
			
		||||
        os.name == 'nt' and
 | 
			
		||||
        isroot and
 | 
			
		||||
        path.startswith('/') and
 | 
			
		||||
        not path.startswith('//'))
 | 
			
		||||
 | 
			
		||||
    if condition:
 | 
			
		||||
        path = '/' + path
 | 
			
		||||
 | 
			
		||||
    path = path.rstrip(r'\/')
 | 
			
		||||
    return path
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def format_path(path):
 | 
			
		||||
    if path is None:
 | 
			
		||||
        return
 | 
			
		||||
    path = unix_path(path)
 | 
			
		||||
    if not cmds.optionVar(query=AUTO_COLLAPSE_IMG_PATH_FROM_ENV):
 | 
			
		||||
        return path
 | 
			
		||||
    root = get_picker_project_directory()
 | 
			
		||||
    if not root or not path.lower().startswith(root.lower()):
 | 
			
		||||
        return path
 | 
			
		||||
    return '$DWPICKER_PROJECT_DIRECTORY/{}'.format(
 | 
			
		||||
        path[len(root):].lstrip('/'))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_picker_project_directory():
 | 
			
		||||
    if cmds.optionVar(query=OVERRIDE_PROD_PICKER_DIRECTORY_ENV):
 | 
			
		||||
        return unix_path(cmds.optionVar(query=CUSTOM_PROD_PICKER_DIRECTORY))
 | 
			
		||||
    return unix_path(os.getenv('DWPICKER_PROJECT_DIRECTORY'))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def expand_path(path):
 | 
			
		||||
    backup = None
 | 
			
		||||
    if cmds.optionVar(query=OVERRIDE_PROD_PICKER_DIRECTORY_ENV):
 | 
			
		||||
        root = unix_path(cmds.optionVar(query=CUSTOM_PROD_PICKER_DIRECTORY))
 | 
			
		||||
        backup = os.getenv('DWPICKER_PROJECT_DIRECTORY')
 | 
			
		||||
        os.environ['DWPICKER_PROJECT_DIRECTORY'] = root
 | 
			
		||||
    result = os.path.expandvars(path)
 | 
			
		||||
    if backup:
 | 
			
		||||
        os.environ['DWPICKER_PROJECT_DIRECTORY'] = backup
 | 
			
		||||
    return result
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_open_directory():
 | 
			
		||||
    if cmds.optionVar(query=USE_PROD_PICKER_DIR_AS_DEFAULT):
 | 
			
		||||
        directory = get_picker_project_directory()
 | 
			
		||||
        if directory:
 | 
			
		||||
            return directory
 | 
			
		||||
    return cmds.optionVar(query=LAST_OPEN_DIRECTORY)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_import_directory():
 | 
			
		||||
    if cmds.optionVar(query=USE_PROD_PICKER_DIR_AS_DEFAULT):
 | 
			
		||||
        directory = get_picker_project_directory()
 | 
			
		||||
        if directory:
 | 
			
		||||
            return directory
 | 
			
		||||
    return cmds.optionVar(query=LAST_IMPORT_DIRECTORY)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_image_directory():
 | 
			
		||||
    if cmds.optionVar(query=USE_PROD_PICKER_DIR_AS_DEFAULT):
 | 
			
		||||
        directory = get_picker_project_directory()
 | 
			
		||||
        if directory:
 | 
			
		||||
            return directory
 | 
			
		||||
    return cmds.optionVar(query=LAST_IMAGE_DIRECTORY_USED)
 | 
			
		||||
							
								
								
									
										465
									
								
								Scripts/Animation/dwpicker/dwpicker/picker.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,465 @@
 | 
			
		||||
from functools import partial
 | 
			
		||||
 | 
			
		||||
from maya import cmds
 | 
			
		||||
import maya.OpenMaya as om
 | 
			
		||||
from PySide2 import QtWidgets, QtGui, QtCore
 | 
			
		||||
 | 
			
		||||
from dwpicker.interactive import SelectionSquare
 | 
			
		||||
from dwpicker.dialog import warning
 | 
			
		||||
from dwpicker.geometry import split_line, get_combined_rects
 | 
			
		||||
from dwpicker.optionvar import (
 | 
			
		||||
    SYNCHRONYZE_SELECTION, ZOOM_BUTTON, ZOOM_SENSITIVITY)
 | 
			
		||||
from dwpicker.painting import ViewportMapper, draw_shape
 | 
			
		||||
from dwpicker.qtutils import get_cursor
 | 
			
		||||
from dwpicker.selection import (
 | 
			
		||||
    select_targets, select_shapes_from_selection, get_selection_mode,
 | 
			
		||||
    NameclashError)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def align_shapes_on_line(shapes, point1, point2):
 | 
			
		||||
    centers = split_line(point1, point2, len(shapes))
 | 
			
		||||
    for center, shape in zip(centers, shapes):
 | 
			
		||||
        shape.rect.moveCenter(center)
 | 
			
		||||
        shape.synchronize_rect()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def frame_shapes(shapes):
 | 
			
		||||
    offset_x = min(shape.rect.left() for shape in shapes)
 | 
			
		||||
    offset_y = min(shape.rect.top() for shape in shapes)
 | 
			
		||||
    offset = -min([offset_x, 0]), -min([offset_y, 0])
 | 
			
		||||
 | 
			
		||||
    for shape in shapes:
 | 
			
		||||
        shape.rect.moveLeft(shape.rect.left() + offset[0])
 | 
			
		||||
        shape.rect.moveTop(shape.rect.top() + offset[1])
 | 
			
		||||
        shape.synchronize_rect()
 | 
			
		||||
        shape.synchronize_image()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def set_shapes_hovered(shapes, cursor, hidden_layers=None, selection_rect=None):
 | 
			
		||||
    """
 | 
			
		||||
    It set hovered the shape if his rect contains the cursor.
 | 
			
		||||
    """
 | 
			
		||||
    if not shapes:
 | 
			
		||||
        return
 | 
			
		||||
    cursor = cursor.toPoint()
 | 
			
		||||
    selection_rect = selection_rect or QtCore.QRect(cursor, cursor)
 | 
			
		||||
    selection_shapes = [s for s in shapes if s.targets()]
 | 
			
		||||
    selection_shapes_intersect_selection = [
 | 
			
		||||
        s for s in selection_shapes
 | 
			
		||||
        if s.rect.contains(cursor) or
 | 
			
		||||
        s.rect.intersects(selection_rect)]
 | 
			
		||||
    selection_shapes_hovered = [
 | 
			
		||||
        s for s in selection_shapes_intersect_selection if
 | 
			
		||||
        not s.visibility_layer() or
 | 
			
		||||
        not hidden_layers or
 | 
			
		||||
        s.visibility_layer() not in hidden_layers]
 | 
			
		||||
    targets = list_targets(selection_shapes_hovered)
 | 
			
		||||
 | 
			
		||||
    for s in selection_shapes:
 | 
			
		||||
        state = next((False for t in s.targets() if t not in targets), True)
 | 
			
		||||
        s.hovered = state
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def detect_hovered_shape(shapes, cursor):
 | 
			
		||||
    if not shapes:
 | 
			
		||||
        return
 | 
			
		||||
    for shape in reversed(shapes):
 | 
			
		||||
        if not (shape.is_interactive() or shape.targets()):
 | 
			
		||||
            continue
 | 
			
		||||
        if shape.rect.contains(cursor):
 | 
			
		||||
            return shape
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def list_targets(shapes):
 | 
			
		||||
    return {t for s in shapes for t in s.targets()}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PickerView(QtWidgets.QWidget):
 | 
			
		||||
    dataChanged = QtCore.Signal()
 | 
			
		||||
    addButtonRequested = QtCore.Signal(int, int, int)
 | 
			
		||||
    updateButtonRequested = QtCore.Signal(object)
 | 
			
		||||
    deleteButtonRequested = QtCore.Signal()
 | 
			
		||||
 | 
			
		||||
    def __init__(self, editable=True, parent=None):
 | 
			
		||||
        super(PickerView, self).__init__(parent)
 | 
			
		||||
        self.callbacks = []
 | 
			
		||||
        self.editable = editable
 | 
			
		||||
        self.mode_manager = ModeManager()
 | 
			
		||||
        self.viewportmapper = ViewportMapper()
 | 
			
		||||
        self.selection_square = SelectionSquare()
 | 
			
		||||
        self.layers_menu = VisibilityLayersMenu()
 | 
			
		||||
        self.setMouseTracking(True)
 | 
			
		||||
        self.shapes = None
 | 
			
		||||
        self.clicked_shape = None
 | 
			
		||||
        self.context_menu = None
 | 
			
		||||
        self.drag_shapes = []
 | 
			
		||||
        self.zoom_locked = False
 | 
			
		||||
 | 
			
		||||
    def register_callbacks(self):
 | 
			
		||||
        method = self.sync_with_maya_selection
 | 
			
		||||
        cb = om.MEventMessage.addEventCallback('SelectionChanged', method)
 | 
			
		||||
        self.callbacks.append(cb)
 | 
			
		||||
 | 
			
		||||
    def unregister_callbacks(self):
 | 
			
		||||
        for callback in self.callbacks:
 | 
			
		||||
            om.MMessage.removeCallback(callback)
 | 
			
		||||
            self.callbacks.remove(callback)
 | 
			
		||||
 | 
			
		||||
    def sync_with_maya_selection(self, *_):
 | 
			
		||||
        if not cmds.optionVar(query=SYNCHRONYZE_SELECTION):
 | 
			
		||||
            return
 | 
			
		||||
        select_shapes_from_selection(self.shapes)
 | 
			
		||||
        self.repaint()
 | 
			
		||||
 | 
			
		||||
    def set_shapes(self, shapes):
 | 
			
		||||
        self.shapes = shapes
 | 
			
		||||
        self.mode_manager.shapes = shapes
 | 
			
		||||
        self.layers_menu.set_shapes(shapes)
 | 
			
		||||
        self.repaint()
 | 
			
		||||
 | 
			
		||||
    def visible_shapes(self):
 | 
			
		||||
        return [
 | 
			
		||||
            s for s in self.shapes if
 | 
			
		||||
            not s.visibility_layer()
 | 
			
		||||
            or s.visibility_layer() not in self.layers_menu.hidden_layers]
 | 
			
		||||
 | 
			
		||||
    def reset(self):
 | 
			
		||||
        shapes = self.visible_shapes()
 | 
			
		||||
        shapes_rects = [s.rect for s in shapes if s.selected]
 | 
			
		||||
        if not shapes_rects:
 | 
			
		||||
            shapes_rects = [s.rect for s in shapes]
 | 
			
		||||
        if not shapes_rects:
 | 
			
		||||
            self.repaint()
 | 
			
		||||
            return
 | 
			
		||||
        self.viewportmapper.viewsize = self.size()
 | 
			
		||||
        rect = get_combined_rects(shapes_rects)
 | 
			
		||||
        self.viewportmapper.focus(rect)
 | 
			
		||||
        self.repaint()
 | 
			
		||||
 | 
			
		||||
    def resizeEvent(self, event):
 | 
			
		||||
        self.viewportmapper.viewsize = self.size()
 | 
			
		||||
        size = (event.size() - event.oldSize()) / 2
 | 
			
		||||
        offset = QtCore.QPointF(size.width(), size.height())
 | 
			
		||||
        self.viewportmapper.origin -= offset
 | 
			
		||||
        self.repaint()
 | 
			
		||||
 | 
			
		||||
    def mousePressEvent(self, event):
 | 
			
		||||
        self.setFocus(QtCore.Qt.MouseFocusReason)
 | 
			
		||||
        self.shapes.extend(self.drag_shapes)
 | 
			
		||||
        cursor = self.viewportmapper.to_units_coords(event.pos()).toPoint()
 | 
			
		||||
        self.clicked_shape = detect_hovered_shape(self.shapes, cursor)
 | 
			
		||||
        hsh = any(s.hovered for s in self.shapes)
 | 
			
		||||
        self.mode_manager.update(
 | 
			
		||||
            event,
 | 
			
		||||
            pressed=True,
 | 
			
		||||
            has_shape_hovered=hsh,
 | 
			
		||||
            dragging=bool(self.drag_shapes))
 | 
			
		||||
 | 
			
		||||
    def mouseReleaseEvent(self, event):
 | 
			
		||||
        shift = self.mode_manager.shift_pressed
 | 
			
		||||
        ctrl = self.mode_manager.ctrl_pressed
 | 
			
		||||
        selection_mode = get_selection_mode(shift=shift, ctrl=ctrl)
 | 
			
		||||
        cursor = self.viewportmapper.to_units_coords(event.pos()).toPoint()
 | 
			
		||||
        zoom = self.mode_manager.zoom_button_pressed
 | 
			
		||||
        interact = (
 | 
			
		||||
            self.clicked_shape and
 | 
			
		||||
            self.clicked_shape is detect_hovered_shape(self.shapes, cursor) and
 | 
			
		||||
            self.clicked_shape.is_interactive())
 | 
			
		||||
 | 
			
		||||
        if zoom and self.mode_manager.alt_pressed:
 | 
			
		||||
            self.release(event)
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        if self.mode_manager.mode == ModeManager.DRAGGING:
 | 
			
		||||
            self.drag_shapes = []
 | 
			
		||||
            self.dataChanged.emit()
 | 
			
		||||
 | 
			
		||||
        elif self.mode_manager.mode == ModeManager.SELECTION and not interact:
 | 
			
		||||
            try:
 | 
			
		||||
                select_targets(self.shapes, selection_mode=selection_mode)
 | 
			
		||||
            except NameclashError as e:
 | 
			
		||||
                warning('Selection Error', str(e), parent=self)
 | 
			
		||||
                self.release(event)
 | 
			
		||||
                return
 | 
			
		||||
        if not self.clicked_shape:
 | 
			
		||||
            if self.mode_manager.right_click_pressed:
 | 
			
		||||
                self.call_context_menu()
 | 
			
		||||
 | 
			
		||||
        elif self.clicked_shape is detect_hovered_shape(self.shapes, cursor):
 | 
			
		||||
            show_context = (
 | 
			
		||||
                self.mode_manager.right_click_pressed and
 | 
			
		||||
                not self.clicked_shape.is_interactive())
 | 
			
		||||
            left_clicked = self.mode_manager.left_click_pressed
 | 
			
		||||
            if show_context:
 | 
			
		||||
                self.call_context_menu()
 | 
			
		||||
            elif left_clicked and self.clicked_shape.targets():
 | 
			
		||||
                self.clicked_shape.select(selection_mode)
 | 
			
		||||
 | 
			
		||||
            if interact:
 | 
			
		||||
                button = (
 | 
			
		||||
                    'left' if self.mode_manager.left_click_pressed
 | 
			
		||||
                    else 'right')
 | 
			
		||||
                self.clicked_shape.execute(
 | 
			
		||||
                    button=button,
 | 
			
		||||
                    ctrl=self.mode_manager.ctrl_pressed,
 | 
			
		||||
                    shift=self.mode_manager.shift_pressed)
 | 
			
		||||
 | 
			
		||||
        self.release(event)
 | 
			
		||||
 | 
			
		||||
    def release(self, event):
 | 
			
		||||
        self.mode_manager.update(event, pressed=False)
 | 
			
		||||
        self.selection_square.release()
 | 
			
		||||
        self.clicked_shape = None
 | 
			
		||||
        self.repaint()
 | 
			
		||||
 | 
			
		||||
    def wheelEvent(self, event):
 | 
			
		||||
        # To center the zoom on the mouse, we save a reference mouse position
 | 
			
		||||
        # and compare the offset after zoom computation.
 | 
			
		||||
        if self.zoom_locked:
 | 
			
		||||
            return
 | 
			
		||||
        factor = .25 if event.angleDelta().y() > 0 else -.25
 | 
			
		||||
        self.zoom(factor, event.pos())
 | 
			
		||||
        self.repaint()
 | 
			
		||||
 | 
			
		||||
    def zoom(self, factor, reference):
 | 
			
		||||
        abspoint = self.viewportmapper.to_units_coords(reference)
 | 
			
		||||
        if factor > 0:
 | 
			
		||||
            self.viewportmapper.zoomin(abs(factor))
 | 
			
		||||
        else:
 | 
			
		||||
            self.viewportmapper.zoomout(abs(factor))
 | 
			
		||||
        relcursor = self.viewportmapper.to_viewport_coords(abspoint)
 | 
			
		||||
        vector = relcursor - reference
 | 
			
		||||
        self.viewportmapper.origin = self.viewportmapper.origin + vector
 | 
			
		||||
 | 
			
		||||
    def mouseMoveEvent(self, event):
 | 
			
		||||
        selection_rect = self.selection_square.rect
 | 
			
		||||
        if selection_rect:
 | 
			
		||||
            selection_rect = self.viewportmapper.to_units_rect(selection_rect)
 | 
			
		||||
            selection_rect = selection_rect.toRect()
 | 
			
		||||
 | 
			
		||||
        set_shapes_hovered(
 | 
			
		||||
            self.shapes,
 | 
			
		||||
            self.viewportmapper.to_units_coords(event.pos()),
 | 
			
		||||
            self.layers_menu.hidden_layers,
 | 
			
		||||
            selection_rect)
 | 
			
		||||
 | 
			
		||||
        if self.mode_manager.mode == ModeManager.DRAGGING:
 | 
			
		||||
            point1 = self.viewportmapper.to_units_coords(
 | 
			
		||||
                self.mode_manager.anchor)
 | 
			
		||||
            point2 = self.viewportmapper.to_units_coords(event.pos())
 | 
			
		||||
            align_shapes_on_line(self.drag_shapes, point1, point2)
 | 
			
		||||
 | 
			
		||||
        elif self.mode_manager.mode == ModeManager.SELECTION:
 | 
			
		||||
            if not self.selection_square.handeling:
 | 
			
		||||
                self.selection_square.clicked(event.pos())
 | 
			
		||||
            self.selection_square.handle(event.pos())
 | 
			
		||||
            return self.repaint()
 | 
			
		||||
 | 
			
		||||
        elif self.mode_manager.mode == ModeManager.ZOOMING:
 | 
			
		||||
            if self.zoom_locked:
 | 
			
		||||
                return self.repaint()
 | 
			
		||||
            offset = self.mode_manager.mouse_offset(event.pos())
 | 
			
		||||
            if offset is not None and self.mode_manager.zoom_anchor:
 | 
			
		||||
                sensitivity = float(cmds.optionVar(query=ZOOM_SENSITIVITY))
 | 
			
		||||
                factor = (offset.x() + offset.y()) / sensitivity
 | 
			
		||||
                self.zoom(factor, self.mode_manager.zoom_anchor)
 | 
			
		||||
 | 
			
		||||
        elif self.mode_manager.mode == ModeManager.NAVIGATION:
 | 
			
		||||
            if self.zoom_locked:
 | 
			
		||||
                return self.repaint()
 | 
			
		||||
            offset = self.mode_manager.mouse_offset(event.pos())
 | 
			
		||||
            if offset is not None:
 | 
			
		||||
                self.viewportmapper.origin = (
 | 
			
		||||
                    self.viewportmapper.origin - offset)
 | 
			
		||||
 | 
			
		||||
        self.repaint()
 | 
			
		||||
 | 
			
		||||
    def call_context_menu(self):
 | 
			
		||||
        if not self.editable:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        self.context_menu = PickerMenu()
 | 
			
		||||
        position = get_cursor(self)
 | 
			
		||||
 | 
			
		||||
        method = partial(self.add_button, position, button_type=0)
 | 
			
		||||
        self.context_menu.add_single.triggered.connect(method)
 | 
			
		||||
        self.context_menu.add_single.setEnabled(bool(cmds.ls(selection=True)))
 | 
			
		||||
 | 
			
		||||
        method = partial(self.add_button, position, button_type=1)
 | 
			
		||||
        self.context_menu.add_multiple.triggered.connect(method)
 | 
			
		||||
        state = len(cmds.ls(selection=True)) > 1
 | 
			
		||||
        self.context_menu.add_multiple.setEnabled(state)
 | 
			
		||||
 | 
			
		||||
        method = partial(self.add_button, position, button_type=2)
 | 
			
		||||
        self.context_menu.add_command.triggered.connect(method)
 | 
			
		||||
 | 
			
		||||
        method = partial(self.updateButtonRequested.emit, self.clicked_shape)
 | 
			
		||||
        self.context_menu.update_button.triggered.connect(method)
 | 
			
		||||
        state = bool(self.clicked_shape) and bool(cmds.ls(selection=True))
 | 
			
		||||
        self.context_menu.update_button.setEnabled(state)
 | 
			
		||||
 | 
			
		||||
        method = self.deleteButtonRequested.emit
 | 
			
		||||
        self.context_menu.delete_selected.triggered.connect(method)
 | 
			
		||||
 | 
			
		||||
        if self.layers_menu.displayed:
 | 
			
		||||
            self.context_menu.addMenu(self.layers_menu)
 | 
			
		||||
        self.context_menu.exec_(QtGui.QCursor.pos())
 | 
			
		||||
 | 
			
		||||
    def add_button(self, position, button_type=0):
 | 
			
		||||
        """
 | 
			
		||||
        Button types:
 | 
			
		||||
            0 = Single button from selection.
 | 
			
		||||
            1 = Multiple buttons from selection.
 | 
			
		||||
            2 = Command button.
 | 
			
		||||
        """
 | 
			
		||||
        position = self.viewportmapper.to_units_coords(position).toPoint()
 | 
			
		||||
        self.addButtonRequested.emit(position.x(), position.y(), button_type)
 | 
			
		||||
 | 
			
		||||
    def paintEvent(self, event):
 | 
			
		||||
        try:
 | 
			
		||||
            painter = QtGui.QPainter()
 | 
			
		||||
            painter.begin(self)
 | 
			
		||||
            painter.setRenderHints(QtGui.QPainter.Antialiasing)
 | 
			
		||||
            if not self.shapes:
 | 
			
		||||
                return
 | 
			
		||||
            hidden_layers = self.layers_menu.hidden_layers
 | 
			
		||||
            for shape in self.shapes:
 | 
			
		||||
                visible = (
 | 
			
		||||
                    not shape.visibility_layer() or
 | 
			
		||||
                    not shape.visibility_layer() in hidden_layers)
 | 
			
		||||
                if not visible:
 | 
			
		||||
                    continue
 | 
			
		||||
                draw_shape(painter, shape, self.viewportmapper)
 | 
			
		||||
            self.selection_square.draw(painter)
 | 
			
		||||
        except BaseException:
 | 
			
		||||
            pass  # avoid crash
 | 
			
		||||
            # TODO: log the error
 | 
			
		||||
        finally:
 | 
			
		||||
            painter.end()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PickerMenu(QtWidgets.QMenu):
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(PickerMenu, self).__init__(parent)
 | 
			
		||||
        self.add_single = QtWidgets.QAction('Add single button', self)
 | 
			
		||||
        self.add_multiple = QtWidgets.QAction('Add multiple buttons', self)
 | 
			
		||||
        self.update_button = QtWidgets.QAction('Update button', self)
 | 
			
		||||
        self.add_command = QtWidgets.QAction('Add command', self)
 | 
			
		||||
        text = 'Delete selected button(s)'
 | 
			
		||||
        self.delete_selected = QtWidgets.QAction(text, self)
 | 
			
		||||
 | 
			
		||||
        self.addAction(self.add_single)
 | 
			
		||||
        self.addAction(self.add_multiple)
 | 
			
		||||
        self.addAction(self.update_button)
 | 
			
		||||
        self.addSeparator()
 | 
			
		||||
        self.addAction(self.add_command)
 | 
			
		||||
        self.addSeparator()
 | 
			
		||||
        self.addAction(self.delete_selected)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ModeManager:
 | 
			
		||||
    FLY_OVER = 'fly_over'
 | 
			
		||||
    SELECTION = 'selection'
 | 
			
		||||
    NAVIGATION = 'navigation'
 | 
			
		||||
    DRAGGING = 'dragging'
 | 
			
		||||
    ZOOMING = 'zooming'
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.shapes = []
 | 
			
		||||
        self.left_click_pressed = False
 | 
			
		||||
        self.right_click_pressed = False
 | 
			
		||||
        self.middle_click_pressed = False
 | 
			
		||||
        self.mouse_ghost = None
 | 
			
		||||
        self.has_shape_hovered = False
 | 
			
		||||
        self.dragging = False
 | 
			
		||||
        self.anchor = None
 | 
			
		||||
        self.zoom_anchor = None
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def ctrl_pressed(self):
 | 
			
		||||
        modifiers = QtWidgets.QApplication.keyboardModifiers()
 | 
			
		||||
        return modifiers == (modifiers | QtCore.Qt.ControlModifier)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def shift_pressed(self):
 | 
			
		||||
        modifiers = QtWidgets.QApplication.keyboardModifiers()
 | 
			
		||||
        return modifiers == (modifiers | QtCore.Qt.ShiftModifier)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def alt_pressed(self):
 | 
			
		||||
        modifiers = QtWidgets.QApplication.keyboardModifiers()
 | 
			
		||||
        return modifiers == (modifiers | QtCore.Qt.AltModifier)
 | 
			
		||||
 | 
			
		||||
    def update(
 | 
			
		||||
            self,
 | 
			
		||||
            event,
 | 
			
		||||
            pressed=False,
 | 
			
		||||
            has_shape_hovered=False,
 | 
			
		||||
            dragging=False):
 | 
			
		||||
 | 
			
		||||
        self.dragging = dragging
 | 
			
		||||
        self.has_shape_hovered = has_shape_hovered
 | 
			
		||||
        self.update_mouse(event, pressed)
 | 
			
		||||
 | 
			
		||||
    def update_mouse(self, event, pressed):
 | 
			
		||||
        if event.button() == QtCore.Qt.LeftButton:
 | 
			
		||||
            self.left_click_pressed = pressed
 | 
			
		||||
            self.anchor = event.pos() if self.dragging else None
 | 
			
		||||
        elif event.button() == QtCore.Qt.RightButton:
 | 
			
		||||
            self.right_click_pressed = pressed
 | 
			
		||||
        elif event.button() == QtCore.Qt.MiddleButton:
 | 
			
		||||
            self.middle_click_pressed = pressed
 | 
			
		||||
        if self.zoom_button_pressed:
 | 
			
		||||
            self.zoom_anchor = event.pos() if pressed else None
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def mode(self):
 | 
			
		||||
        if self.dragging:
 | 
			
		||||
            return ModeManager.DRAGGING
 | 
			
		||||
        elif self.zoom_button_pressed and self.alt_pressed:
 | 
			
		||||
            return ModeManager.ZOOMING
 | 
			
		||||
        elif self.middle_click_pressed:
 | 
			
		||||
            return ModeManager.NAVIGATION
 | 
			
		||||
        elif self.left_click_pressed:
 | 
			
		||||
            return ModeManager.SELECTION
 | 
			
		||||
        self.mouse_ghost = None
 | 
			
		||||
        return ModeManager.FLY_OVER
 | 
			
		||||
 | 
			
		||||
    def mouse_offset(self, position):
 | 
			
		||||
        result = position - self.mouse_ghost if self.mouse_ghost else None
 | 
			
		||||
        self.mouse_ghost = position
 | 
			
		||||
        return result or None
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def zoom_button_pressed(self):
 | 
			
		||||
        button = cmds.optionVar(query=ZOOM_BUTTON)
 | 
			
		||||
        return any((
 | 
			
		||||
            button == 'left' and self.left_click_pressed,
 | 
			
		||||
            button == 'middle' and self.middle_click_pressed,
 | 
			
		||||
            button == 'right' and self.right_click_pressed))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class VisibilityLayersMenu(QtWidgets.QMenu):
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(VisibilityLayersMenu, self).__init__('Visibility layers', parent)
 | 
			
		||||
        self.hidden_layers = []
 | 
			
		||||
        self.displayed = False
 | 
			
		||||
 | 
			
		||||
    def set_shapes(self, shapes):
 | 
			
		||||
        layers = sorted(
 | 
			
		||||
            {s.visibility_layer() for s in shapes if s.visibility_layer()})
 | 
			
		||||
        self.clear()
 | 
			
		||||
        action = QtWidgets.QAction('Show all')
 | 
			
		||||
        for layer in layers:
 | 
			
		||||
            action = QtWidgets.QAction(layer, self)
 | 
			
		||||
            action.setCheckable(True)
 | 
			
		||||
            action.setChecked(layer not in self.hidden_layers)
 | 
			
		||||
            action.toggled.connect(partial(self.set_hidden_layer, layer))
 | 
			
		||||
            self.addAction(action)
 | 
			
		||||
        self.displayed = bool(layers)
 | 
			
		||||
 | 
			
		||||
    def set_hidden_layer(self, layer, state):
 | 
			
		||||
        if state is False and layer not in self.hidden_layers:
 | 
			
		||||
            self.hidden_layers.append(layer)
 | 
			
		||||
        if state is True and layer in self.hidden_layers:
 | 
			
		||||
            self.hidden_layers.remove(layer)
 | 
			
		||||
							
								
								
									
										279
									
								
								Scripts/Animation/dwpicker/dwpicker/preference.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,279 @@
 | 
			
		||||
import os
 | 
			
		||||
from PySide2 import QtWidgets, QtCore
 | 
			
		||||
from maya import cmds
 | 
			
		||||
from dwpicker.hotkeyseditor import HotkeysEditor
 | 
			
		||||
from dwpicker.optionvar import (
 | 
			
		||||
    save_optionvar,
 | 
			
		||||
    AUTO_COLLAPSE_IMG_PATH_FROM_ENV, AUTO_FOCUS_BEHAVIOR, AUTO_SET_NAMESPACE,
 | 
			
		||||
    AUTO_FOCUS_BEHAVIORS, AUTO_SWITCH_TAB, CHECK_IMAGES_PATHS,
 | 
			
		||||
    CUSTOM_PROD_PICKER_DIRECTORY, CHECK_FOR_UPDATE, DISPLAY_QUICK_OPTIONS,
 | 
			
		||||
    DISABLE_IMPORT_CALLBACKS, OVERRIDE_PROD_PICKER_DIRECTORY_ENV,
 | 
			
		||||
    INSERT_TAB_AFTER_CURRENT, NAMESPACE_TOOLBAR, SYNCHRONYZE_SELECTION,
 | 
			
		||||
    TRIGGER_REPLACE_ON_MIRROR, USE_BASE64_DATA_ENCODING,
 | 
			
		||||
    USE_PROD_PICKER_DIR_AS_DEFAULT, USE_ICON_FOR_UNSAVED_TAB,
 | 
			
		||||
    WARN_ON_TAB_CLOSED, ZOOM_SENSITIVITY, ZOOM_BUTTON, ZOOM_BUTTONS)
 | 
			
		||||
from dwpicker.path import unix_path
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MAX_SENSITIVITY = 500
 | 
			
		||||
AUTO_FOCUSES = {
 | 
			
		||||
    'Disable': AUTO_FOCUS_BEHAVIORS[0],
 | 
			
		||||
    'Bilateral': AUTO_FOCUS_BEHAVIORS[1],
 | 
			
		||||
    'From picker to Maya only': AUTO_FOCUS_BEHAVIORS[2]}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PreferencesWindow(QtWidgets.QWidget):
 | 
			
		||||
    need_update_callbacks = QtCore.Signal()
 | 
			
		||||
    hotkey_changed = QtCore.Signal()
 | 
			
		||||
 | 
			
		||||
    def __init__(self, callback=None, parent=None):
 | 
			
		||||
        super(PreferencesWindow, self).__init__(parent, QtCore.Qt.Tool)
 | 
			
		||||
        self.setWindowTitle("Preferences")
 | 
			
		||||
        self.general_preferences = GeneralPreferences(callback)
 | 
			
		||||
        self.general_preferences.disable_import_callbacks.released.connect(
 | 
			
		||||
            self.need_update_callbacks.emit)
 | 
			
		||||
        self.hotkeys_editor = HotkeysEditor()
 | 
			
		||||
        self.hotkeys_editor.hotkey_changed.connect(self.hotkey_changed.emit)
 | 
			
		||||
 | 
			
		||||
        tab = QtWidgets.QTabWidget()
 | 
			
		||||
        tab.addTab(self.general_preferences, 'General')
 | 
			
		||||
        tab.addTab(self.hotkeys_editor, 'Hotkeys')
 | 
			
		||||
 | 
			
		||||
        layout = QtWidgets.QHBoxLayout(self)
 | 
			
		||||
        layout.addWidget(tab)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GeneralPreferences(QtWidgets.QWidget):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, callback=None, parent=None):
 | 
			
		||||
        super(GeneralPreferences, self).__init__(parent)
 | 
			
		||||
        self.callback = callback
 | 
			
		||||
 | 
			
		||||
        text = "Display namespace toolbar."
 | 
			
		||||
        self.namespace_toolbar = QtWidgets.QCheckBox(text)
 | 
			
		||||
        self.quick_options = QtWidgets.QCheckBox("Display quick options.")
 | 
			
		||||
        text = "Auto switch tab with selection."
 | 
			
		||||
        self.autoswitch_tab = QtWidgets.QCheckBox(text)
 | 
			
		||||
        text = "Auto switch namespace."
 | 
			
		||||
        self.autoswitch_namespace = QtWidgets.QCheckBox(text)
 | 
			
		||||
        self.sychronize = QtWidgets.QCheckBox("Synchronize picker selection.")
 | 
			
		||||
        text = "Missing images warning."
 | 
			
		||||
        self.check_images_paths = QtWidgets.QCheckBox(text)
 | 
			
		||||
        text = "Disable callback at import time. (Use with Studio Library)"
 | 
			
		||||
        self.disable_import_callbacks = QtWidgets.QCheckBox(text)
 | 
			
		||||
        text = "Use icon to mark unsaved tab."
 | 
			
		||||
        self.unsaved_tab_icon = QtWidgets.QCheckBox(text)
 | 
			
		||||
        text = "Insert new tab after current tab."
 | 
			
		||||
        self.insert_after_current = QtWidgets.QCheckBox(text)
 | 
			
		||||
        text = "Warning before closing a tab."
 | 
			
		||||
        self.warn_on_tab_close = QtWidgets.QCheckBox(text)
 | 
			
		||||
        self.ui_group = QtWidgets.QGroupBox("Ui")
 | 
			
		||||
        self.ui_layout = QtWidgets.QVBoxLayout(self.ui_group)
 | 
			
		||||
        self.ui_layout.addWidget(self.namespace_toolbar)
 | 
			
		||||
        self.ui_layout.addWidget(self.quick_options)
 | 
			
		||||
        self.ui_layout.addWidget(self.disable_import_callbacks)
 | 
			
		||||
        self.ui_layout.addWidget(self.autoswitch_namespace)
 | 
			
		||||
        self.ui_layout.addWidget(self.autoswitch_tab)
 | 
			
		||||
        self.ui_layout.addWidget(self.sychronize)
 | 
			
		||||
        self.ui_layout.addWidget(self.check_images_paths)
 | 
			
		||||
        self.ui_layout.addWidget(self.unsaved_tab_icon)
 | 
			
		||||
        self.ui_layout.addWidget(self.insert_after_current)
 | 
			
		||||
        self.ui_layout.addWidget(self.warn_on_tab_close)
 | 
			
		||||
 | 
			
		||||
        notfound = "environment variable not found"
 | 
			
		||||
        text = '$DWPICKER_PROJECT_DIRECTORY:{}'.format(
 | 
			
		||||
            os.getenv("DWPICKER_PROJECT_DIRECTORY", notfound))
 | 
			
		||||
        self.project_dir_env = QtWidgets.QLineEdit(text)
 | 
			
		||||
        self.project_dir_env.setReadOnly(True)
 | 
			
		||||
        text = (
 | 
			
		||||
            "Auto-collapse path with environment "
 | 
			
		||||
            "variable $DWPICKER_PROJECT_DIRECTORY")
 | 
			
		||||
        self.auto_collapse_path = QtWidgets.QCheckBox(text)
 | 
			
		||||
        text = "Override $DWPICKER_PROJECT_DIRECTORY"
 | 
			
		||||
        self.override_variable = QtWidgets.QCheckBox(text)
 | 
			
		||||
        self.custom_prod_path = QtWidgets.QLineEdit()
 | 
			
		||||
        text = "Force file dialog to use this directory"
 | 
			
		||||
        self.force_file_dialog_directory = QtWidgets.QCheckBox(text)
 | 
			
		||||
 | 
			
		||||
        custom_path_layout = QtWidgets.QHBoxLayout()
 | 
			
		||||
        custom_path_layout.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        custom_path_layout.addWidget(self.override_variable)
 | 
			
		||||
        custom_path_layout.addWidget(self.custom_prod_path)
 | 
			
		||||
 | 
			
		||||
        self.env_group = QtWidgets.QGroupBox("Environment Variables")
 | 
			
		||||
        self.env_layout = QtWidgets.QVBoxLayout(self.env_group)
 | 
			
		||||
        self.env_layout.addWidget(self.project_dir_env)
 | 
			
		||||
        self.env_layout.addWidget(self.auto_collapse_path)
 | 
			
		||||
        self.env_layout.addLayout(custom_path_layout)
 | 
			
		||||
        self.env_layout.addWidget(self.force_file_dialog_directory)
 | 
			
		||||
 | 
			
		||||
        text = "Encode in-scene data as base64."
 | 
			
		||||
        self.use_base64_encoding = QtWidgets.QCheckBox(text)
 | 
			
		||||
 | 
			
		||||
        self.data_group = QtWidgets.QGroupBox("Data")
 | 
			
		||||
        self.data_layout = QtWidgets.QVBoxLayout(self.data_group)
 | 
			
		||||
        self.data_layout.addWidget(self.use_base64_encoding)
 | 
			
		||||
 | 
			
		||||
        self.auto_focus = QtWidgets.QComboBox()
 | 
			
		||||
        self.auto_focus.addItems(list(AUTO_FOCUSES))
 | 
			
		||||
 | 
			
		||||
        self.focus_group = QtWidgets.QGroupBox("Auto-focus")
 | 
			
		||||
        self.focus_layout = QtWidgets.QFormLayout(self.focus_group)
 | 
			
		||||
        self.focus_layout.addRow("Behavior", self.auto_focus)
 | 
			
		||||
 | 
			
		||||
        msg = "Prompt search and replace after mirror."
 | 
			
		||||
        self.search_on_mirror = QtWidgets.QCheckBox(msg)
 | 
			
		||||
        self.advanced_group = QtWidgets.QGroupBox("Advanced editor")
 | 
			
		||||
        self.advanced_layout = QtWidgets.QVBoxLayout(self.advanced_group)
 | 
			
		||||
        self.advanced_layout.addWidget(self.search_on_mirror)
 | 
			
		||||
 | 
			
		||||
        self.zoom_sensitivity = QtWidgets.QSlider(QtCore.Qt.Horizontal)
 | 
			
		||||
        self.zoom_sensitivity.setMaximum(MAX_SENSITIVITY)
 | 
			
		||||
        self.zoom_sensitivity.setMinimum(1)
 | 
			
		||||
        self.zoom_sensitivity.setSingleStep(1)
 | 
			
		||||
        self.zoom_button = QtWidgets.QComboBox()
 | 
			
		||||
        for item in ZOOM_BUTTONS:
 | 
			
		||||
            self.zoom_button.addItem(item)
 | 
			
		||||
 | 
			
		||||
        self.zoom_group = QtWidgets.QGroupBox("Zoom options")
 | 
			
		||||
        self.zoom_layout = QtWidgets.QFormLayout(self.zoom_group)
 | 
			
		||||
        self.zoom_layout.addRow("Sensitivity", self.zoom_sensitivity)
 | 
			
		||||
        self.zoom_layout.addRow("Mouse button", self.zoom_button)
 | 
			
		||||
 | 
			
		||||
        msg = "Check for new version at startup."
 | 
			
		||||
        self.check_for_update = QtWidgets.QCheckBox(msg)
 | 
			
		||||
        self.update_group = QtWidgets.QGroupBox("Update check")
 | 
			
		||||
        self.update_layout = QtWidgets.QVBoxLayout(self.update_group)
 | 
			
		||||
        self.update_layout.addWidget(self.check_for_update)
 | 
			
		||||
 | 
			
		||||
        central_widget = QtWidgets.QWidget()
 | 
			
		||||
        self.sublayout = QtWidgets.QVBoxLayout(central_widget)
 | 
			
		||||
        self.sublayout.addWidget(self.ui_group)
 | 
			
		||||
        self.sublayout.addWidget(self.env_group)
 | 
			
		||||
        self.sublayout.addWidget(self.data_group)
 | 
			
		||||
        self.sublayout.addWidget(self.focus_group)
 | 
			
		||||
        self.sublayout.addWidget(self.advanced_group)
 | 
			
		||||
        self.sublayout.addWidget(self.zoom_group)
 | 
			
		||||
        self.sublayout.addWidget(self.update_group)
 | 
			
		||||
 | 
			
		||||
        scroll = QtWidgets.QScrollArea()
 | 
			
		||||
        scroll.setWidgetResizable(True)
 | 
			
		||||
        scroll.setWidget(central_widget)
 | 
			
		||||
        scroll.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
 | 
			
		||||
        scroll.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
 | 
			
		||||
 | 
			
		||||
        layout = QtWidgets.QVBoxLayout(self)
 | 
			
		||||
        layout.addWidget(scroll)
 | 
			
		||||
 | 
			
		||||
        self.load_ui_states()
 | 
			
		||||
 | 
			
		||||
        self.auto_collapse_path.released.connect(self.save_ui_states)
 | 
			
		||||
        self.autoswitch_tab.released.connect(self.save_ui_states)
 | 
			
		||||
        self.autoswitch_namespace.released.connect(self.save_ui_states)
 | 
			
		||||
        self.auto_focus.currentIndexChanged.connect(self.save_ui_states)
 | 
			
		||||
        self.check_for_update.released.connect(self.save_ui_states)
 | 
			
		||||
        self.check_images_paths.released.connect(self.save_ui_states)
 | 
			
		||||
        self.custom_prod_path.textEdited.connect(self.save_ui_states)
 | 
			
		||||
        self.disable_import_callbacks.released.connect(self.save_ui_states)
 | 
			
		||||
        self.force_file_dialog_directory.released.connect(self.save_ui_states)
 | 
			
		||||
        self.override_variable.released.connect(self.save_ui_states)
 | 
			
		||||
        self.insert_after_current.released.connect(self.save_ui_states)
 | 
			
		||||
        self.quick_options.released.connect(self.save_ui_states)
 | 
			
		||||
        self.namespace_toolbar.released.connect(self.save_ui_states)
 | 
			
		||||
        self.use_base64_encoding.released.connect(self.save_ui_states)
 | 
			
		||||
        self.unsaved_tab_icon.released.connect(self.save_ui_states)
 | 
			
		||||
        self.sychronize.released.connect(self.save_ui_states)
 | 
			
		||||
        self.search_on_mirror.released.connect(self.save_ui_states)
 | 
			
		||||
        self.warn_on_tab_close.released.connect(self.save_ui_states)
 | 
			
		||||
        self.zoom_sensitivity.valueChanged.connect(self.save_ui_states)
 | 
			
		||||
        self.zoom_button.currentIndexChanged.connect(self.save_ui_states)
 | 
			
		||||
 | 
			
		||||
    def sizeHint(self):
 | 
			
		||||
        return QtCore.QSize(520, 600)
 | 
			
		||||
 | 
			
		||||
    def load_ui_states(self):
 | 
			
		||||
        state = bool(cmds.optionVar(query=AUTO_COLLAPSE_IMG_PATH_FROM_ENV))
 | 
			
		||||
        self.auto_collapse_path.setChecked(state)
 | 
			
		||||
        value = cmds.optionVar(query=AUTO_FOCUS_BEHAVIOR)
 | 
			
		||||
        text = {v: k for k, v in AUTO_FOCUSES.items()}[value]
 | 
			
		||||
        self.auto_focus.setCurrentText(text)
 | 
			
		||||
        state = bool(cmds.optionVar(query=AUTO_SET_NAMESPACE))
 | 
			
		||||
        self.autoswitch_namespace.setChecked(state)
 | 
			
		||||
        state = bool(cmds.optionVar(query=AUTO_SWITCH_TAB))
 | 
			
		||||
        self.autoswitch_tab.setChecked(state)
 | 
			
		||||
        state = bool(cmds.optionVar(query=DISABLE_IMPORT_CALLBACKS))
 | 
			
		||||
        self.disable_import_callbacks.setChecked(state)
 | 
			
		||||
        value = cmds.optionVar(query=CUSTOM_PROD_PICKER_DIRECTORY)
 | 
			
		||||
        self.custom_prod_path.setText(value)
 | 
			
		||||
        state = bool(cmds.optionVar(query=CHECK_IMAGES_PATHS))
 | 
			
		||||
        self.check_images_paths.setChecked(state)
 | 
			
		||||
        state = bool(cmds.optionVar(query=CHECK_FOR_UPDATE))
 | 
			
		||||
        self.check_for_update.setChecked(state)
 | 
			
		||||
        state = bool(cmds.optionVar(query=USE_PROD_PICKER_DIR_AS_DEFAULT))
 | 
			
		||||
        self.force_file_dialog_directory.setChecked(state)
 | 
			
		||||
        state = bool(cmds.optionVar(query=OVERRIDE_PROD_PICKER_DIRECTORY_ENV))
 | 
			
		||||
        self.override_variable.setChecked(state)
 | 
			
		||||
        self.custom_prod_path.setEnabled(state)
 | 
			
		||||
        state = bool(cmds.optionVar(query=NAMESPACE_TOOLBAR))
 | 
			
		||||
        self.namespace_toolbar.setChecked(state)
 | 
			
		||||
        state = bool(cmds.optionVar(query=DISPLAY_QUICK_OPTIONS))
 | 
			
		||||
        self.quick_options.setChecked(state)
 | 
			
		||||
        state = bool(cmds.optionVar(query=SYNCHRONYZE_SELECTION))
 | 
			
		||||
        self.sychronize.setChecked(state)
 | 
			
		||||
        state = bool(cmds.optionVar(query=USE_BASE64_DATA_ENCODING))
 | 
			
		||||
        self.use_base64_encoding.setChecked(state)
 | 
			
		||||
        state = bool(cmds.optionVar(query=USE_ICON_FOR_UNSAVED_TAB))
 | 
			
		||||
        self.unsaved_tab_icon.setChecked(state)
 | 
			
		||||
        state = bool(cmds.optionVar(query=WARN_ON_TAB_CLOSED))
 | 
			
		||||
        self.warn_on_tab_close.setChecked(state)
 | 
			
		||||
        state = bool(cmds.optionVar(query=INSERT_TAB_AFTER_CURRENT))
 | 
			
		||||
        self.insert_after_current.setChecked(state)
 | 
			
		||||
        state = bool(cmds.optionVar(query=TRIGGER_REPLACE_ON_MIRROR))
 | 
			
		||||
        self.search_on_mirror.setChecked(state)
 | 
			
		||||
 | 
			
		||||
        value = MAX_SENSITIVITY - cmds.optionVar(query=ZOOM_SENSITIVITY)
 | 
			
		||||
        self.zoom_sensitivity.setSliderPosition(value)
 | 
			
		||||
        value = cmds.optionVar(query=ZOOM_BUTTON)
 | 
			
		||||
        self.zoom_button.setCurrentText(value)
 | 
			
		||||
 | 
			
		||||
    def save_ui_states(self, *_):
 | 
			
		||||
        value = int(self.auto_collapse_path.isChecked())
 | 
			
		||||
        save_optionvar(AUTO_COLLAPSE_IMG_PATH_FROM_ENV, value)
 | 
			
		||||
        value = AUTO_FOCUSES[self.auto_focus.currentText()]
 | 
			
		||||
        save_optionvar(AUTO_FOCUS_BEHAVIOR, value)
 | 
			
		||||
        value = int(self.autoswitch_namespace.isChecked())
 | 
			
		||||
        save_optionvar(AUTO_SET_NAMESPACE, value)
 | 
			
		||||
        value = int(self.autoswitch_tab.isChecked())
 | 
			
		||||
        save_optionvar(AUTO_SWITCH_TAB, value)
 | 
			
		||||
        value = int(self.check_images_paths.isChecked())
 | 
			
		||||
        save_optionvar(CHECK_IMAGES_PATHS, value)
 | 
			
		||||
        value = int(self.check_for_update.isChecked())
 | 
			
		||||
        save_optionvar(CHECK_FOR_UPDATE, value)
 | 
			
		||||
        value = unix_path(self.custom_prod_path.text())
 | 
			
		||||
        save_optionvar(CUSTOM_PROD_PICKER_DIRECTORY, value)
 | 
			
		||||
        value = int(self.insert_after_current.isChecked())
 | 
			
		||||
        save_optionvar(INSERT_TAB_AFTER_CURRENT, value)
 | 
			
		||||
        value = int(self.disable_import_callbacks.isChecked())
 | 
			
		||||
        save_optionvar(DISABLE_IMPORT_CALLBACKS, value)
 | 
			
		||||
        value = int(self.force_file_dialog_directory.isChecked())
 | 
			
		||||
        save_optionvar(USE_PROD_PICKER_DIR_AS_DEFAULT, value)
 | 
			
		||||
        value = self.override_variable.isChecked()
 | 
			
		||||
        save_optionvar(OVERRIDE_PROD_PICKER_DIRECTORY_ENV, int(value))
 | 
			
		||||
        self.custom_prod_path.setEnabled(value)
 | 
			
		||||
        value = int(self.quick_options.isChecked())
 | 
			
		||||
        save_optionvar(DISPLAY_QUICK_OPTIONS, value)
 | 
			
		||||
        value = int(self.namespace_toolbar.isChecked())
 | 
			
		||||
        save_optionvar(NAMESPACE_TOOLBAR, value)
 | 
			
		||||
        value = int(self.use_base64_encoding.isChecked())
 | 
			
		||||
        save_optionvar(USE_BASE64_DATA_ENCODING, value)
 | 
			
		||||
        value = int(self.unsaved_tab_icon.isChecked())
 | 
			
		||||
        save_optionvar(USE_ICON_FOR_UNSAVED_TAB, value)
 | 
			
		||||
        value = int(self.search_on_mirror.isChecked())
 | 
			
		||||
        save_optionvar(TRIGGER_REPLACE_ON_MIRROR, value)
 | 
			
		||||
        value = int(self.warn_on_tab_close.isChecked())
 | 
			
		||||
        save_optionvar(WARN_ON_TAB_CLOSED, value)
 | 
			
		||||
        save_optionvar(ZOOM_BUTTON, self.zoom_button.currentText())
 | 
			
		||||
        value = MAX_SENSITIVITY - int(self.zoom_sensitivity.value()) + 1
 | 
			
		||||
        save_optionvar(ZOOM_SENSITIVITY, value)
 | 
			
		||||
        if self.callback:
 | 
			
		||||
            self.callback()
 | 
			
		||||
							
								
								
									
										14
									
								
								Scripts/Animation/dwpicker/dwpicker/qt_remapping/PySide2.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,14 @@
 | 
			
		||||
 | 
			
		||||
from PySide6 import QtCore, QtGui, QtWidgets
 | 
			
		||||
from PySide6 import __version__
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
QtWidgets.QShortcut = QtGui.QShortcut
 | 
			
		||||
QtWidgets.QAction = QtGui.QAction
 | 
			
		||||
 | 
			
		||||
QtGui.QMouseEvent.pos = QtGui.QMouseEvent.position
 | 
			
		||||
QtGui.QMouseEvent.globalPos = QtGui.QMouseEvent.globalPosition
 | 
			
		||||
 | 
			
		||||
QtGui.QWheelEvent.pos = QtGui.QWheelEvent.position
 | 
			
		||||
 | 
			
		||||
QtCore.Qt.BackgroundColorRole = QtCore.Qt.BackgroundRole
 | 
			
		||||
@@ -0,0 +1 @@
 | 
			
		||||
from shiboken6 import wrapInstance
 | 
			
		||||
							
								
								
									
										109
									
								
								Scripts/Animation/dwpicker/dwpicker/qtutils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,109 @@
 | 
			
		||||
import inspect
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
from PySide2 import QtGui, QtWidgets, QtCore
 | 
			
		||||
from maya import cmds
 | 
			
		||||
import maya.OpenMayaUI as omui
 | 
			
		||||
from maya.app.general.mayaMixin import MayaQWidgetDockableMixin
 | 
			
		||||
import shiboken2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Ensure backward compatibility.
 | 
			
		||||
if sys.version_info[0] == 3:
 | 
			
		||||
    long = int
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
VALIGNS = {
 | 
			
		||||
    'top': QtCore.Qt.AlignTop,
 | 
			
		||||
    'center': QtCore.Qt.AlignVCenter,
 | 
			
		||||
    'bottom': QtCore.Qt.AlignBottom}
 | 
			
		||||
HALIGNS = {
 | 
			
		||||
    'left': QtCore.Qt.AlignLeft,
 | 
			
		||||
    'center': QtCore.Qt.AlignHCenter,
 | 
			
		||||
    'right': QtCore.Qt.AlignRight}
 | 
			
		||||
HERE = os.path.dirname(__file__)
 | 
			
		||||
ERROR_IMPORT_MSG = ('''
 | 
			
		||||
ERROR: Dwpicker: DwPicker is not found in Python paths.
 | 
			
		||||
    - Please use sys.path.append('<dwpicker forlder>') and relaunch it.
 | 
			
		||||
    - Or add '<picker folder>' to environment variable PYTHONPATH''')
 | 
			
		||||
 | 
			
		||||
RESTORE_CMD = ("""
 | 
			
		||||
try:
 | 
			
		||||
    import {0}
 | 
			
		||||
    {0}.{1}.restore()
 | 
			
		||||
except ImportError:
 | 
			
		||||
    print("{2}")
 | 
			
		||||
""")
 | 
			
		||||
mixin_windows = {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if sys.version_info[0] != 2:
 | 
			
		||||
    long = int
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def icon(filename):
 | 
			
		||||
    return QtGui.QIcon(os.path.join(HERE, 'icons', filename))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_cursor(widget):
 | 
			
		||||
    return widget.mapFromGlobal(QtGui.QCursor.pos())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def set_shortcut(keysequence, parent, method, context=None):
 | 
			
		||||
    shortcut = QtWidgets.QShortcut(QtGui.QKeySequence(keysequence), parent)
 | 
			
		||||
    shortcut.setContext(context or QtCore.Qt.WidgetWithChildrenShortcut)
 | 
			
		||||
    shortcut.activated.connect(method)
 | 
			
		||||
    return shortcut
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def remove_workspace_control(control_name):
 | 
			
		||||
    workspace_control_name = control_name + "WorkspaceControl"
 | 
			
		||||
    cmds.deleteUI(workspace_control_name, control=True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def maya_main_window():
 | 
			
		||||
    ptr = omui.MQtUtil.mainWindow()
 | 
			
		||||
    if ptr is not None:
 | 
			
		||||
        return shiboken2.wrapInstance(long(ptr), QtWidgets.QWidget)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DockableBase(MayaQWidgetDockableMixin):
 | 
			
		||||
    """
 | 
			
		||||
    Code from https://kainev.com/qt-for-maya-dockable-windows
 | 
			
		||||
    Thanks for this !
 | 
			
		||||
 | 
			
		||||
    Convenience class for creating dockable Maya windows.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, control_name, **kwargs):
 | 
			
		||||
        super(DockableBase, self).__init__(**kwargs)
 | 
			
		||||
        self.setObjectName(control_name)
 | 
			
		||||
 | 
			
		||||
    def show(self, dockable=True, *_, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Show UI with generated uiScript argument
 | 
			
		||||
        """
 | 
			
		||||
        modulename = inspect.getmodule(self).__name__
 | 
			
		||||
        classname = self.__class__.__name__
 | 
			
		||||
        command = RESTORE_CMD.format(modulename, classname, ERROR_IMPORT_MSG)
 | 
			
		||||
        super(DockableBase, self).show(
 | 
			
		||||
            dockable=dockable, uiScript=command, **kwargs)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def restore(cls):
 | 
			
		||||
        """
 | 
			
		||||
        Internal method to restore the UI when Maya is opened.
 | 
			
		||||
        """
 | 
			
		||||
        # Create UI instance
 | 
			
		||||
        instance = cls()
 | 
			
		||||
        # Get the empty WorkspaceControl created by Maya
 | 
			
		||||
        workspace_control = omui.MQtUtil.getCurrentParent()
 | 
			
		||||
        # Grab the pointer to our instance as a Maya object
 | 
			
		||||
        mixinPtr = omui.MQtUtil.findControl(instance.objectName())
 | 
			
		||||
        # Add our UI to the WorkspaceControl
 | 
			
		||||
        omui.MQtUtil.addWidgetToMayaLayout(
 | 
			
		||||
            long(mixinPtr), long(workspace_control))
 | 
			
		||||
        # Store reference to UI
 | 
			
		||||
        global mixin_windows
 | 
			
		||||
        mixin_windows[instance.objectName()] = instance
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										120
									
								
								Scripts/Animation/dwpicker/dwpicker/quick.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,120 @@
 | 
			
		||||
from PySide2 import QtWidgets, QtGui, QtCore
 | 
			
		||||
from maya import cmds
 | 
			
		||||
 | 
			
		||||
from dwpicker.colorwheel import ColorDialog
 | 
			
		||||
from dwpicker.optionvar import (
 | 
			
		||||
    save_optionvar, DEFAULT_LABEL, DEFAULT_HEIGHT, DEFAULT_WIDTH,
 | 
			
		||||
    DEFAULT_TEXT_COLOR, DEFAULT_BG_COLOR)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class QuickOptions(QtWidgets.QWidget):
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(QuickOptions, self).__init__(parent=parent)
 | 
			
		||||
        self.bg_color = ColorButton()
 | 
			
		||||
        self.bg_color.colorChanged.connect(self.save_ui_states)
 | 
			
		||||
        self.text_color = ColorButton()
 | 
			
		||||
        self.text_color.colorChanged.connect(self.save_ui_states)
 | 
			
		||||
        validator = QtGui.QIntValidator()
 | 
			
		||||
        self.width = QtWidgets.QLineEdit()
 | 
			
		||||
        self.width.returnPressed.connect(self.save_ui_states)
 | 
			
		||||
        self.width.setValidator(validator)
 | 
			
		||||
        self.width.setFixedWidth(50)
 | 
			
		||||
        self.height = QtWidgets.QLineEdit()
 | 
			
		||||
        self.height.returnPressed.connect(self.save_ui_states)
 | 
			
		||||
        self.height.setValidator(validator)
 | 
			
		||||
        self.height.setFixedWidth(50)
 | 
			
		||||
        self.label = QtWidgets.QLineEdit()
 | 
			
		||||
        self.label.returnPressed.connect(self.save_ui_states)
 | 
			
		||||
 | 
			
		||||
        self.layout = QtWidgets.QHBoxLayout(self)
 | 
			
		||||
        self.layout.setSpacing(0)
 | 
			
		||||
        self.layout.addWidget(QtWidgets.QLabel('Bg-color: '))
 | 
			
		||||
        self.layout.addWidget(self.bg_color)
 | 
			
		||||
        self.layout.addSpacing(12)
 | 
			
		||||
        self.layout.addWidget(QtWidgets.QLabel('Text-color: '))
 | 
			
		||||
        self.layout.addWidget(self.text_color)
 | 
			
		||||
        self.layout.addSpacing(12)
 | 
			
		||||
        self.layout.addWidget(QtWidgets.QLabel('Size: '))
 | 
			
		||||
        self.layout.addWidget(self.width)
 | 
			
		||||
        self.layout.addWidget(self.height)
 | 
			
		||||
        self.layout.addSpacing(12)
 | 
			
		||||
        self.layout.addWidget(QtWidgets.QLabel('Label: '))
 | 
			
		||||
        self.layout.addWidget(self.label)
 | 
			
		||||
 | 
			
		||||
        self.load_ui_states()
 | 
			
		||||
 | 
			
		||||
    def save_ui_states(self, *_):
 | 
			
		||||
        values = self.values
 | 
			
		||||
        save_optionvar(DEFAULT_BG_COLOR, values['bgcolor.normal'])
 | 
			
		||||
        save_optionvar(DEFAULT_TEXT_COLOR, values['text.color'])
 | 
			
		||||
        save_optionvar(DEFAULT_WIDTH, values['shape.width'])
 | 
			
		||||
        save_optionvar(DEFAULT_HEIGHT, values['shape.height'])
 | 
			
		||||
        save_optionvar(DEFAULT_LABEL, values['text.content'])
 | 
			
		||||
 | 
			
		||||
    def load_ui_states(self):
 | 
			
		||||
        self.values = {
 | 
			
		||||
            'bgcolor.normal': cmds.optionVar(query=DEFAULT_BG_COLOR),
 | 
			
		||||
            'text.color': cmds.optionVar(query=DEFAULT_TEXT_COLOR),
 | 
			
		||||
            'shape.width': cmds.optionVar(query=DEFAULT_WIDTH),
 | 
			
		||||
            'shape.height': cmds.optionVar(query=DEFAULT_HEIGHT),
 | 
			
		||||
            'text.content': cmds.optionVar(query=DEFAULT_LABEL)}
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def values(self):
 | 
			
		||||
        return {
 | 
			
		||||
            'bgcolor.normal': self.bg_color.name,
 | 
			
		||||
            'text.color': self.text_color.name,
 | 
			
		||||
            'shape.width': int(self.width.text()) if self.width.text() else 10,
 | 
			
		||||
            'shape.height': int(self.height.text()) if self.height.text() else 10,
 | 
			
		||||
            'text.content': self.label.text()}
 | 
			
		||||
 | 
			
		||||
    @values.setter
 | 
			
		||||
    def values(self, values):
 | 
			
		||||
        self.bg_color.name = values['bgcolor.normal']
 | 
			
		||||
        self.text_color.name = values['text.color']
 | 
			
		||||
        self.width.setText(str(values['shape.width']))
 | 
			
		||||
        self.height.setText(str(values['shape.height']))
 | 
			
		||||
        self.label.setText(str(values['text.content']))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ColorButton(QtWidgets.QAbstractButton):
 | 
			
		||||
    colorChanged = QtCore.Signal()
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(ColorButton, self).__init__(parent=parent)
 | 
			
		||||
        self.setFixedSize(20, 20)
 | 
			
		||||
        self.color = QtGui.QColor(QtCore.Qt.black)
 | 
			
		||||
        self.released.connect(self.pick_color)
 | 
			
		||||
 | 
			
		||||
    def pick_color(self):
 | 
			
		||||
        dialog = ColorDialog(self.name)
 | 
			
		||||
        if not dialog.exec_():
 | 
			
		||||
            return
 | 
			
		||||
        self.name = dialog.colorname()
 | 
			
		||||
        self.colorChanged.emit()
 | 
			
		||||
        self.repaint()
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def name(self):
 | 
			
		||||
        return self.color.name()
 | 
			
		||||
 | 
			
		||||
    @name.setter
 | 
			
		||||
    def name(self, value):
 | 
			
		||||
        self.color.setNamedColor(value)
 | 
			
		||||
 | 
			
		||||
    def paintEvent(self, _):
 | 
			
		||||
        try:
 | 
			
		||||
            painter = QtGui.QPainter()
 | 
			
		||||
            painter.begin(self)
 | 
			
		||||
            painter.setBrush(QtGui.QBrush(self.color))
 | 
			
		||||
            if self.rect().contains(QtGui.QCursor.pos()):
 | 
			
		||||
                color = QtCore.Qt.transparent
 | 
			
		||||
            else:
 | 
			
		||||
                color = QtCore.Qt.gray
 | 
			
		||||
            painter.setPen(QtGui.QPen(color))
 | 
			
		||||
            painter.drawRect(self.rect())
 | 
			
		||||
        except BaseException:
 | 
			
		||||
            pass  # avoid crash
 | 
			
		||||
            # TODO: log the error
 | 
			
		||||
        finally:
 | 
			
		||||
            painter.end()
 | 
			
		||||
							
								
								
									
										42
									
								
								Scripts/Animation/dwpicker/dwpicker/references.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,42 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
from dwpicker.dialog import MissingImages
 | 
			
		||||
from dwpicker.path import expand_path
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
IMAGE_MISSING_WARNING = (
 | 
			
		||||
    '\nImage is not found.\nWould you like to set a new path ?')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def ensure_images_path_exists(pickers):
 | 
			
		||||
    """
 | 
			
		||||
    As images are stored as path in the picker, this function ensure the paths
 | 
			
		||||
    exists. If not, it proposes to set a new path. If more than an image is not
 | 
			
		||||
    found, it will automatically look up into directories given in previous
 | 
			
		||||
    repath to find the images.
 | 
			
		||||
    """
 | 
			
		||||
    missing_images = list_missing_images(pickers)
 | 
			
		||||
    if not missing_images:
 | 
			
		||||
        return
 | 
			
		||||
    dialog = MissingImages(missing_images)
 | 
			
		||||
    if not dialog.exec_():
 | 
			
		||||
        return
 | 
			
		||||
    for picker_data in pickers:
 | 
			
		||||
        for shape in picker_data['shapes']:
 | 
			
		||||
            path = expand_path(shape['image.path'])
 | 
			
		||||
            if path in missing_images:
 | 
			
		||||
                new_path = dialog.output(path)
 | 
			
		||||
                if not new_path:
 | 
			
		||||
                    continue
 | 
			
		||||
                shape['image.path'] = new_path
 | 
			
		||||
    return pickers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def list_missing_images(pickers_data):
 | 
			
		||||
    return sorted(list(set([
 | 
			
		||||
        shape['image.path']
 | 
			
		||||
        for picker_data in pickers_data
 | 
			
		||||
        for shape in picker_data['shapes'] if
 | 
			
		||||
        shape['image.path'] and not
 | 
			
		||||
        os.path.exists(expand_path(shape['image.path']))])))
 | 
			
		||||
							
								
								
									
										89
									
								
								Scripts/Animation/dwpicker/dwpicker/scenedata.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,89 @@
 | 
			
		||||
import sys
 | 
			
		||||
import json
 | 
			
		||||
import base64
 | 
			
		||||
 | 
			
		||||
from maya import cmds
 | 
			
		||||
 | 
			
		||||
from dwpicker.compatibility import ensure_retro_compatibility
 | 
			
		||||
from dwpicker.optionvar import USE_BASE64_DATA_ENCODING
 | 
			
		||||
from dwpicker.namespace import maya_namespace
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PICKER_HOLDER_NODE = '_dwpicker_data'
 | 
			
		||||
PICKER_HOLDER_ATTRIBUTE = '_dwpicker_data'
 | 
			
		||||
LS_EXP = ["*." + PICKER_HOLDER_ATTRIBUTE, "*:*." + PICKER_HOLDER_ATTRIBUTE]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_picker_holder_node():
 | 
			
		||||
    if cmds.objExists(PICKER_HOLDER_NODE):
 | 
			
		||||
        return PICKER_HOLDER_NODE
 | 
			
		||||
    return create_picker_holder_node()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_picker_holder_node():
 | 
			
		||||
    with maya_namespace(":"):
 | 
			
		||||
        node = cmds.createNode('script', name=PICKER_HOLDER_NODE)
 | 
			
		||||
    cmds.setAttr(node + '.nodeState', 1)
 | 
			
		||||
    cmds.addAttr(node, longName=PICKER_HOLDER_ATTRIBUTE, dataType='string')
 | 
			
		||||
    return node
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def store_local_picker_data(pickers):
 | 
			
		||||
    data = encode_data(pickers)
 | 
			
		||||
    node = get_picker_holder_node()
 | 
			
		||||
    cmds.setAttr(node + '.' + PICKER_HOLDER_ATTRIBUTE, data, type='string')
 | 
			
		||||
    clean_stray_picker_holder_nodes()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def load_local_picker_data():
 | 
			
		||||
    nodes = list_picker_holder_nodes()
 | 
			
		||||
    pickers = []
 | 
			
		||||
    for node in nodes:
 | 
			
		||||
        data = cmds.getAttr(node + '.' + PICKER_HOLDER_ATTRIBUTE)
 | 
			
		||||
        data = decode_data(data)
 | 
			
		||||
        pickers.extend(ensure_retro_compatibility(p) for p in data)
 | 
			
		||||
    return pickers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def encode_data(pickers):
 | 
			
		||||
    data = json.dumps(pickers)
 | 
			
		||||
    if not cmds.optionVar(query=USE_BASE64_DATA_ENCODING):
 | 
			
		||||
        return data
 | 
			
		||||
    # Ensure backward compatibility.
 | 
			
		||||
    if sys.version_info[0] == 2:
 | 
			
		||||
        return base64.b64encode(bytes(data))
 | 
			
		||||
    return base64.b64encode(bytes(data, "utf-8"))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def decode_data(data):
 | 
			
		||||
    try:
 | 
			
		||||
        return json.loads(data)
 | 
			
		||||
    except ValueError:  # Happe if data encoded is encoded as base 64 string.
 | 
			
		||||
        return json.loads(base64.b64decode(data))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def list_picker_holder_nodes():
 | 
			
		||||
    """
 | 
			
		||||
    Look up in the scene all the nodes holding an attribute named
 | 
			
		||||
    "_dwpicker_holder" which are not set on the "_dwpicker_holder" node.
 | 
			
		||||
    This mignt happed if a node node is imported (creating a namespace or a
 | 
			
		||||
    incrementation).
 | 
			
		||||
    """
 | 
			
		||||
    return [node.split(".")[0] for node in cmds.ls(LS_EXP)]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def clean_stray_picker_holder_nodes():
 | 
			
		||||
    """
 | 
			
		||||
    If the scene contains multiple picker holder nodes, we remove them
 | 
			
		||||
    automatically to avoid repeated pickers.
 | 
			
		||||
    """
 | 
			
		||||
    for node in list_picker_holder_nodes():
 | 
			
		||||
        if node == PICKER_HOLDER_NODE:
 | 
			
		||||
            continue
 | 
			
		||||
        try:
 | 
			
		||||
            cmds.delete(node)
 | 
			
		||||
        except BaseException:
 | 
			
		||||
            # Node is locked or in reference and cannot be removed.
 | 
			
		||||
            # As we cant remove it, we reset his data to avoid double pickers.
 | 
			
		||||
            cmds.setAttr(
 | 
			
		||||
                node + "." + PICKER_HOLDER_ATTRIBUTE, "", dataType="string")
 | 
			
		||||
							
								
								
									
										127
									
								
								Scripts/Animation/dwpicker/dwpicker/selection.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,127 @@
 | 
			
		||||
 | 
			
		||||
from maya import cmds
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NameclashError(BaseException):
 | 
			
		||||
    def __init__(self, nodes=None):
 | 
			
		||||
        self.clashes = [node for node in nodes or [] if len(cmds.ls(node)) > 1]
 | 
			
		||||
        message = 'Some nodes exists more than once:\n'
 | 
			
		||||
        nodes = '\n  - '.join(self.clashes)
 | 
			
		||||
        super(NameclashError, self).__init__(message + nodes)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def select_targets(shapes, selection_mode='replace'):
 | 
			
		||||
    shapes = [s for s in shapes if s.targets()]
 | 
			
		||||
    hovered = [s for s in shapes if s.hovered]
 | 
			
		||||
    targets = [t for s in hovered for t in s.targets() if cmds.objExists(t)]
 | 
			
		||||
 | 
			
		||||
    if selection_mode in ('add', 'replace', 'invert'):
 | 
			
		||||
        try:
 | 
			
		||||
            return cmds.select(list(targets), add=selection_mode == 'add')
 | 
			
		||||
        except ValueError:
 | 
			
		||||
            raise NameclashError(targets)
 | 
			
		||||
    elif selection_mode == 'remove':
 | 
			
		||||
        selection = [n for n in cmds.ls(sl=True) if n not in targets]
 | 
			
		||||
        try:
 | 
			
		||||
            return cmds.select(selection)
 | 
			
		||||
        except ValueError:
 | 
			
		||||
            raise NameclashError(targets)
 | 
			
		||||
 | 
			
		||||
    # Invert selection
 | 
			
		||||
    selected = [s for s in shapes if s.selected]
 | 
			
		||||
    to_select = [s for s in shapes if s in hovered and s not in selected]
 | 
			
		||||
    # List targets unaffected by selection
 | 
			
		||||
    targets = {
 | 
			
		||||
        t for s in selected for t in s.targets()
 | 
			
		||||
        if cmds.objExists(t) and not s.hovered}
 | 
			
		||||
    # List targets in reversed selection
 | 
			
		||||
    invert_t = {t for s in to_select for t in s.targets() if cmds.objExists(t)}
 | 
			
		||||
    targets.union(invert_t)
 | 
			
		||||
    try:
 | 
			
		||||
        cmds.select(targets)
 | 
			
		||||
    except ValueError:
 | 
			
		||||
        raise NameclashError(targets)
 | 
			
		||||
    return
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def select_shapes_from_selection(shapes):
 | 
			
		||||
    selection = cmds.ls(sl=True)
 | 
			
		||||
    for shape in shapes:
 | 
			
		||||
        if not shape.targets():
 | 
			
		||||
            shape.selected = False
 | 
			
		||||
            continue
 | 
			
		||||
        for target in shape.targets():
 | 
			
		||||
            if target not in selection:
 | 
			
		||||
                shape.selected = False
 | 
			
		||||
                break
 | 
			
		||||
        else:
 | 
			
		||||
            shape.selected = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Selection():
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.shapes = []
 | 
			
		||||
        self.mode = 'replace'
 | 
			
		||||
 | 
			
		||||
    def set(self, shapes):
 | 
			
		||||
        if self.mode == 'add':
 | 
			
		||||
            if shapes is None:
 | 
			
		||||
                return
 | 
			
		||||
            return self.add(shapes)
 | 
			
		||||
        elif self.mode == 'replace':
 | 
			
		||||
            if shapes is None:
 | 
			
		||||
                return self.clear()
 | 
			
		||||
            return self.replace(shapes)
 | 
			
		||||
        elif self.mode == 'invert':
 | 
			
		||||
            if shapes is None:
 | 
			
		||||
                return
 | 
			
		||||
            return self.invert(shapes)
 | 
			
		||||
        elif self.mode == 'remove':
 | 
			
		||||
            if shapes is None:
 | 
			
		||||
                return
 | 
			
		||||
            for shape in shapes:
 | 
			
		||||
                if shape in self.shapes:
 | 
			
		||||
                    self.remove(shape)
 | 
			
		||||
 | 
			
		||||
    def replace(self, shapes):
 | 
			
		||||
        self.shapes = shapes
 | 
			
		||||
 | 
			
		||||
    def add(self, shapes):
 | 
			
		||||
        self.shapes.extend([s for s in shapes if s not in self])
 | 
			
		||||
 | 
			
		||||
    def remove(self, shape):
 | 
			
		||||
        self.shapes.remove(shape)
 | 
			
		||||
 | 
			
		||||
    def invert(self, shapes):
 | 
			
		||||
        for shape in shapes:
 | 
			
		||||
            if shape not in self.shapes:
 | 
			
		||||
                self.add([shape])
 | 
			
		||||
            else:
 | 
			
		||||
                self.remove(shape)
 | 
			
		||||
 | 
			
		||||
    def clear(self):
 | 
			
		||||
        self.shapes = []
 | 
			
		||||
 | 
			
		||||
    def __len__(self):
 | 
			
		||||
        return len(self.shapes)
 | 
			
		||||
 | 
			
		||||
    def __bool__(self):
 | 
			
		||||
        return bool(self.shapes)
 | 
			
		||||
 | 
			
		||||
    __nonzero__ = __bool__
 | 
			
		||||
 | 
			
		||||
    def __getitem__(self, i):
 | 
			
		||||
        return self.shapes[i]
 | 
			
		||||
 | 
			
		||||
    def __iter__(self):
 | 
			
		||||
        return self.shapes.__iter__()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_selection_mode(ctrl, shift):
 | 
			
		||||
    if not ctrl and not shift:
 | 
			
		||||
        return 'replace'
 | 
			
		||||
    elif ctrl and shift:
 | 
			
		||||
        return 'invert'
 | 
			
		||||
    elif shift:
 | 
			
		||||
        return 'add'
 | 
			
		||||
    return 'remove'
 | 
			
		||||
							
								
								
									
										130
									
								
								Scripts/Animation/dwpicker/dwpicker/templates.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,130 @@
 | 
			
		||||
from dwpicker.appinfos import VERSION
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUTTON = {
 | 
			
		||||
    'visibility_layer': None,
 | 
			
		||||
    'shape': 'square',  # or round
 | 
			
		||||
    'shape.left': 0.0,
 | 
			
		||||
    'shape.top': 0.0,
 | 
			
		||||
    'shape.width': 120.0,
 | 
			
		||||
    'shape.height': 25.0,
 | 
			
		||||
    'shape.cornersx': 4,
 | 
			
		||||
    'shape.cornersy': 4,
 | 
			
		||||
    'border': True,
 | 
			
		||||
    'borderwidth.normal': 1.0,
 | 
			
		||||
    'borderwidth.hovered': 1.25,
 | 
			
		||||
    'borderwidth.clicked': 2,
 | 
			
		||||
    'bordercolor.normal': '#000000',
 | 
			
		||||
    'bordercolor.hovered': '#393939',
 | 
			
		||||
    'bordercolor.clicked': '#FFFFFF',
 | 
			
		||||
    'bordercolor.transparency': 0,
 | 
			
		||||
    'bgcolor.normal': '#888888',
 | 
			
		||||
    'bgcolor.hovered': '#AAAAAA',
 | 
			
		||||
    'bgcolor.clicked': '#DDDDDD',
 | 
			
		||||
    'bgcolor.transparency': 0,
 | 
			
		||||
    'text.content': 'Button',
 | 
			
		||||
    'text.size': 12,
 | 
			
		||||
    'text.bold': False,
 | 
			
		||||
    'text.italic': False,
 | 
			
		||||
    'text.color': '#FFFFFF',
 | 
			
		||||
    'text.valign': 'center',  # or 'top' or bottom
 | 
			
		||||
    'text.halign': 'center',  # or 'left' or 'right'
 | 
			
		||||
    'action.targets': [],
 | 
			
		||||
    'action.commands': [],
 | 
			
		||||
    'image.path': '',
 | 
			
		||||
    'image.fit': True,
 | 
			
		||||
    'image.height': 32,
 | 
			
		||||
    'image.width': 32}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
TEXT = {
 | 
			
		||||
    'visibility_layer': None,
 | 
			
		||||
    'shape': 'square',  # or round
 | 
			
		||||
    'shape.left': 0.0,
 | 
			
		||||
    'shape.top': 0.0,
 | 
			
		||||
    'shape.width': 200.0,
 | 
			
		||||
    'shape.height': 50.0,
 | 
			
		||||
    'shape.cornersx': 4,
 | 
			
		||||
    'shape.cornersy': 4,
 | 
			
		||||
    'border': False,
 | 
			
		||||
    'borderwidth.normal': 0,
 | 
			
		||||
    'borderwidth.hovered': 0,
 | 
			
		||||
    'borderwidth.clicked': 0,
 | 
			
		||||
    'bordercolor.normal': '#000000',
 | 
			
		||||
    'bordercolor.hovered': '#393939',
 | 
			
		||||
    'bordercolor.clicked': '#FFFFFF',
 | 
			
		||||
    'bordercolor.transparency': 0,
 | 
			
		||||
    'bgcolor.normal': '#888888',
 | 
			
		||||
    'bgcolor.hovered': '#AAAAAA',
 | 
			
		||||
    'bgcolor.clicked': '#DDDDDD',
 | 
			
		||||
    'bgcolor.transparency': 255,
 | 
			
		||||
    'text.content': 'Text',
 | 
			
		||||
    'text.size': 16,
 | 
			
		||||
    'text.bold': True,
 | 
			
		||||
    'text.italic': False,
 | 
			
		||||
    'text.color': '#FFFFFF',
 | 
			
		||||
    'text.valign': 'top',  # or 'top' or bottom
 | 
			
		||||
    'text.halign': 'left',  # or 'left' or 'right'
 | 
			
		||||
    'action.targets': [],
 | 
			
		||||
    'action.commands': [],
 | 
			
		||||
    'image.path': '',
 | 
			
		||||
    'image.fit': False,
 | 
			
		||||
    'image.height': 32,
 | 
			
		||||
    'image.width': 32}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BACKGROUND = {
 | 
			
		||||
    'visibility_layer': None,
 | 
			
		||||
    'shape': 'square',  # or round or rounded_rect
 | 
			
		||||
    'shape.left': 0.0,
 | 
			
		||||
    'shape.top': 0.0,
 | 
			
		||||
    'shape.width': 400.0,
 | 
			
		||||
    'shape.height': 400.0,
 | 
			
		||||
    'shape.cornersx': 4,
 | 
			
		||||
    'shape.cornersy': 4,
 | 
			
		||||
    'border': False,
 | 
			
		||||
    'borderwidth.normal': 0,
 | 
			
		||||
    'borderwidth.hovered': 0,
 | 
			
		||||
    'borderwidth.clicked': 0,
 | 
			
		||||
    'bordercolor.normal': '#888888',
 | 
			
		||||
    'bordercolor.hovered': '#888888',
 | 
			
		||||
    'bordercolor.clicked': '#888888',
 | 
			
		||||
    'bordercolor.transparency': 0,
 | 
			
		||||
    'bgcolor.normal': '#888888',
 | 
			
		||||
    'bgcolor.hovered': '#888888',
 | 
			
		||||
    'bgcolor.clicked': '#888888',
 | 
			
		||||
    'bgcolor.transparency': 0,
 | 
			
		||||
    'text.content': '',
 | 
			
		||||
    'text.size': 12,
 | 
			
		||||
    'text.bold': False,
 | 
			
		||||
    'text.italic': False,
 | 
			
		||||
    'text.color': '#FFFFFF',
 | 
			
		||||
    'text.valign': 'center',  # or 'top' or bottom
 | 
			
		||||
    'text.halign': 'center',  # or 'left' or 'right'
 | 
			
		||||
    'action.targets': [],
 | 
			
		||||
    'action.commands': [],
 | 
			
		||||
    'image.path': '',
 | 
			
		||||
    'image.fit': False,
 | 
			
		||||
    'image.height': 32,
 | 
			
		||||
    'image.width': 32}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
COMMAND = {
 | 
			
		||||
    'enabled': True,
 | 
			
		||||
    'button': 'left',  # right
 | 
			
		||||
    'language': 'python',  # or mel
 | 
			
		||||
    'command': '',
 | 
			
		||||
    'ctrl': False,
 | 
			
		||||
    'shift': False,
 | 
			
		||||
    'deferred': False,
 | 
			
		||||
    'force_compact_undo': False,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PICKER = {
 | 
			
		||||
    'name': 'Untitled',
 | 
			
		||||
    'version': VERSION,
 | 
			
		||||
    'zoom_locked': False,
 | 
			
		||||
    'width': 900,
 | 
			
		||||
    'height': 600,
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										49
									
								
								Scripts/Animation/dwpicker/dwpicker/undo.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,49 @@
 | 
			
		||||
 | 
			
		||||
from copy import deepcopy
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UndoManager():
 | 
			
		||||
    def __init__(self, data):
 | 
			
		||||
        self._current_state = data
 | 
			
		||||
        self._modified = False
 | 
			
		||||
        self._undo_stack = []
 | 
			
		||||
        self._redo_stack = []
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def data(self):
 | 
			
		||||
        return deepcopy(self._current_state)
 | 
			
		||||
 | 
			
		||||
    def undo(self):
 | 
			
		||||
        if not self._undo_stack:
 | 
			
		||||
            print('No undostack.')
 | 
			
		||||
            return False
 | 
			
		||||
        self._redo_stack.append(deepcopy(self._current_state))
 | 
			
		||||
        self._current_state = deepcopy(self._undo_stack[-1])
 | 
			
		||||
        del self._undo_stack[-1]
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def redo(self):
 | 
			
		||||
        if not self._redo_stack:
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
        self._undo_stack.append(deepcopy(self._current_state))
 | 
			
		||||
        self._current_state = deepcopy(self._redo_stack[-1])
 | 
			
		||||
        del self._redo_stack[-1]
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def set_data_modified(self, data):
 | 
			
		||||
        self._redo_stack = []
 | 
			
		||||
        self._undo_stack.append(deepcopy(self._current_state))
 | 
			
		||||
        self._current_state = deepcopy(data)
 | 
			
		||||
        self._modified = True
 | 
			
		||||
 | 
			
		||||
    def set_data_saved(self):
 | 
			
		||||
        self._modified = False
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def data_saved(self):
 | 
			
		||||
        return not self._modified
 | 
			
		||||
 | 
			
		||||
    def reset_stacks(self):
 | 
			
		||||
        self._undo_stack = []
 | 
			
		||||
        self._redo_stack = []
 | 
			
		||||
							
								
								
									
										35
									
								
								Scripts/Animation/dwpicker/dwpicker/updatechecker.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,35 @@
 | 
			
		||||
import re
 | 
			
		||||
import webbrowser
 | 
			
		||||
try:
 | 
			
		||||
    from urllib.request import urlopen
 | 
			
		||||
except ImportError:
 | 
			
		||||
    from urllib2 import urlopen  # python2
 | 
			
		||||
 | 
			
		||||
from maya import cmds
 | 
			
		||||
 | 
			
		||||
from dwpicker.dialog import UpdateAvailableDialog
 | 
			
		||||
from dwpicker.appinfos import VERSION
 | 
			
		||||
from dwpicker.optionvar import CHECK_FOR_UPDATE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
APPINFOS_URL = (
 | 
			
		||||
    'https://raw.githubusercontent.com/DreamWall-Animation/dwpicker/main/'
 | 
			
		||||
    'dwpicker/appinfos.py')
 | 
			
		||||
LATEST_RELEASE_URL = (
 | 
			
		||||
    'https://github.com/DreamWall-Animation/dwpicker/releases/latest')
 | 
			
		||||
VERSION_PATTERN = r'\d(\.|,).\d(\.|,).\d'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def warn_if_update_available():
 | 
			
		||||
    if not cmds.optionVar(query=CHECK_FOR_UPDATE):
 | 
			
		||||
        return
 | 
			
		||||
    try:
 | 
			
		||||
        appinfos = urlopen(APPINFOS_URL).read().decode()
 | 
			
		||||
        latest_version_str = re.search(VERSION_PATTERN, appinfos)[0]
 | 
			
		||||
        latest_version = tuple(
 | 
			
		||||
            int(n) for n in latest_version_str.replace(',', '.').split('.'))
 | 
			
		||||
        if VERSION < latest_version:
 | 
			
		||||
            if UpdateAvailableDialog(latest_version_str).exec_():
 | 
			
		||||
                webbrowser.open(LATEST_RELEASE_URL)
 | 
			
		||||
    except BaseException:
 | 
			
		||||
        print('DwPicker: could not check for new version')
 | 
			
		||||
							
								
								
									
										291
									
								
								Scripts/Animation/dwpicker/dwpicker/widgets.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,291 @@
 | 
			
		||||
from PySide2 import QtGui, QtCore, QtWidgets
 | 
			
		||||
 | 
			
		||||
from dwpicker.colorwheel import ColorDialog
 | 
			
		||||
from dwpicker.dialog import get_image_path
 | 
			
		||||
from dwpicker.path import format_path
 | 
			
		||||
from dwpicker.qtutils import icon
 | 
			
		||||
 | 
			
		||||
# don't use style sheet like that, find better design
 | 
			
		||||
TOGGLER_STYLESHEET = (
 | 
			
		||||
    'background: rgb(0, 0, 0, 75); text-align: left; font: bold')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BoolCombo(QtWidgets.QComboBox):
 | 
			
		||||
    valueSet = QtCore.Signal(bool)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, state=True, parent=None):
 | 
			
		||||
        super(BoolCombo, self).__init__(parent)
 | 
			
		||||
        self.addItem('True')
 | 
			
		||||
        self.addItem('False')
 | 
			
		||||
        self.setCurrentText(str(state))
 | 
			
		||||
        self.currentIndexChanged.connect(self.current_index_changed)
 | 
			
		||||
 | 
			
		||||
    def state(self):
 | 
			
		||||
        return self.currentText() == 'True'
 | 
			
		||||
 | 
			
		||||
    def current_index_changed(self):
 | 
			
		||||
        self.valueSet.emit(self.state())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BrowseEdit(QtWidgets.QWidget):
 | 
			
		||||
    valueSet = QtCore.Signal(str)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(BrowseEdit, self).__init__(parent)
 | 
			
		||||
 | 
			
		||||
        self.text = QtWidgets.QLineEdit()
 | 
			
		||||
        self.text.returnPressed.connect(self.apply)
 | 
			
		||||
        self.text.focusOutEvent = self.text_focus_out_event
 | 
			
		||||
        self.button = QtWidgets.QPushButton('B')
 | 
			
		||||
        self.button.setFixedSize(21, 21)
 | 
			
		||||
        self.button.released.connect(self.browse)
 | 
			
		||||
 | 
			
		||||
        self.layout = QtWidgets.QHBoxLayout(self)
 | 
			
		||||
        self.layout.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        self.layout.setSpacing(0)
 | 
			
		||||
        self.layout.addWidget(self.text)
 | 
			
		||||
        self.layout.addWidget(self.button)
 | 
			
		||||
 | 
			
		||||
        self._value = self.value()
 | 
			
		||||
 | 
			
		||||
    def text_focus_out_event(self, _):
 | 
			
		||||
        self.apply()
 | 
			
		||||
 | 
			
		||||
    def browse(self):
 | 
			
		||||
        filename = get_image_path(self)
 | 
			
		||||
        format_path(filename)
 | 
			
		||||
        if not filename:
 | 
			
		||||
            return
 | 
			
		||||
        self.text.setText(filename)
 | 
			
		||||
        self.apply()
 | 
			
		||||
 | 
			
		||||
    def apply(self):
 | 
			
		||||
        text = format_path(self.text.text())
 | 
			
		||||
        self.text.setText(text)
 | 
			
		||||
        self.valueSet.emit(text)
 | 
			
		||||
 | 
			
		||||
    def value(self):
 | 
			
		||||
        value = format(self.text.text())
 | 
			
		||||
        return value if value != '' else None
 | 
			
		||||
 | 
			
		||||
    def set_value(self, value):
 | 
			
		||||
        self.text.setText(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class WidgetToggler(QtWidgets.QPushButton):
 | 
			
		||||
    def __init__(self, label, widget, parent=None):
 | 
			
		||||
        super(WidgetToggler, self).__init__(parent)
 | 
			
		||||
        self.setStyleSheet(TOGGLER_STYLESHEET)
 | 
			
		||||
        self.setText(' v ' + label)
 | 
			
		||||
        self.widget = widget
 | 
			
		||||
        self.setCheckable(True)
 | 
			
		||||
        self.setChecked(True)
 | 
			
		||||
        self.toggled.connect(self._call_toggled)
 | 
			
		||||
 | 
			
		||||
    def _call_toggled(self, state):
 | 
			
		||||
        if state is True:
 | 
			
		||||
            self.widget.show()
 | 
			
		||||
            self.setText(self.text().replace('>', 'v'))
 | 
			
		||||
        else:
 | 
			
		||||
            self.widget.hide()
 | 
			
		||||
            self.setText(self.text().replace('v', '>'))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ColorEdit(QtWidgets.QWidget):
 | 
			
		||||
    valueSet = QtCore.Signal(str)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(ColorEdit, self).__init__(parent)
 | 
			
		||||
 | 
			
		||||
        self.pixmap = QtWidgets.QLabel()
 | 
			
		||||
        self.pixmap.setFixedSize(21, 21)
 | 
			
		||||
        color = QtWidgets.QApplication.palette().color(
 | 
			
		||||
            QtGui.QPalette.Base)
 | 
			
		||||
        self.pixmap.setPixmap(_color_pixmap(color, self.pixmap.size()))
 | 
			
		||||
        self.text = QtWidgets.QLineEdit()
 | 
			
		||||
        self.text.returnPressed.connect(self.apply)
 | 
			
		||||
        self.text.focusInEvent = self.focusInEvent
 | 
			
		||||
        self.text.focusOutEvent = self.focusOutEvent
 | 
			
		||||
        self.button = QtWidgets.QPushButton(icon('picker.png'), '')
 | 
			
		||||
        self.button.setFixedSize(21, 21)
 | 
			
		||||
        self.button.released.connect(self.pick_color)
 | 
			
		||||
 | 
			
		||||
        self.layout = QtWidgets.QHBoxLayout(self)
 | 
			
		||||
        self.layout.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        self.layout.setSpacing(0)
 | 
			
		||||
        self.layout.addWidget(self.pixmap)
 | 
			
		||||
        self.layout.addWidget(self.text)
 | 
			
		||||
        self.layout.addWidget(self.button)
 | 
			
		||||
        self.layout.setStretchFactor(self.pixmap, 1)
 | 
			
		||||
        self.layout.setStretchFactor(self.text, 5)
 | 
			
		||||
        self.layout.setStretchFactor(self.button, 1)
 | 
			
		||||
 | 
			
		||||
        self._value = self.value()
 | 
			
		||||
 | 
			
		||||
    def focusInEvent(self, event):
 | 
			
		||||
        self._value = self.value()
 | 
			
		||||
        return super(ColorEdit, self).focusInEvent(event)
 | 
			
		||||
 | 
			
		||||
    def focusOutEvent(self, event):
 | 
			
		||||
        self.apply()
 | 
			
		||||
        return super(ColorEdit, self).focusOutEvent(event)
 | 
			
		||||
 | 
			
		||||
    def showEvent(self, event):
 | 
			
		||||
        super(ColorEdit, self).showEvent(event)
 | 
			
		||||
        self.pixmap.setFixedSize(21, 21)
 | 
			
		||||
 | 
			
		||||
    def pick_color(self):
 | 
			
		||||
        color = self.text.text() or None
 | 
			
		||||
        dialog = ColorDialog(color)
 | 
			
		||||
        if dialog.exec_():
 | 
			
		||||
            self.text.setText(dialog.colorname())
 | 
			
		||||
            self.pixmap.setPixmap(
 | 
			
		||||
                _color_pixmap(dialog.colorname(), self.pixmap.size()))
 | 
			
		||||
            self.apply()
 | 
			
		||||
 | 
			
		||||
    def apply(self):
 | 
			
		||||
        if self._value != self.value():
 | 
			
		||||
            self.valueSet.emit(self.value())
 | 
			
		||||
        self._value = self.value()
 | 
			
		||||
 | 
			
		||||
    def value(self):
 | 
			
		||||
        value = self.text.text()
 | 
			
		||||
        return value if value != '' else None
 | 
			
		||||
 | 
			
		||||
    def set_color(self, color=None):
 | 
			
		||||
        self.text.setText(color)
 | 
			
		||||
        color = color or QtWidgets.QApplication.palette().color(
 | 
			
		||||
            QtGui.QPalette.Base)
 | 
			
		||||
        self.pixmap.setPixmap(_color_pixmap(color, self.pixmap.size()))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _color_pixmap(colorname, qsize):
 | 
			
		||||
    pixmap = QtGui.QPixmap(qsize)
 | 
			
		||||
    painter = QtGui.QPainter(pixmap)
 | 
			
		||||
    painter.setBrush(QtGui.QColor(colorname))
 | 
			
		||||
    painter.setPen(QtCore.Qt.black)
 | 
			
		||||
    painter.drawRect(0, 0, qsize.width(), qsize.height())
 | 
			
		||||
    painter.end()
 | 
			
		||||
    return pixmap
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LineEdit(QtWidgets.QLineEdit):
 | 
			
		||||
    valueSet = QtCore.Signal(float)
 | 
			
		||||
    VALIDATOR_CLS = QtGui.QDoubleValidator
 | 
			
		||||
 | 
			
		||||
    def __init__(self, minimum=None, maximum=None, parent=None):
 | 
			
		||||
        super(LineEdit, self).__init__(parent)
 | 
			
		||||
        self.validator = self.VALIDATOR_CLS() if self.VALIDATOR_CLS else None
 | 
			
		||||
        if minimum is not None:
 | 
			
		||||
            self.validator.setBottom(minimum)
 | 
			
		||||
        if maximum is not None:
 | 
			
		||||
            self.validator.setTop(maximum)
 | 
			
		||||
        self.setValidator(self.validator)
 | 
			
		||||
        self._value = self.value()
 | 
			
		||||
        self.returnPressed.connect(self.apply)
 | 
			
		||||
 | 
			
		||||
    def focusInEvent(self, event):
 | 
			
		||||
        self._value = self.value()
 | 
			
		||||
        return super(LineEdit, self).focusInEvent(event)
 | 
			
		||||
 | 
			
		||||
    def focusOutEvent(self, event):
 | 
			
		||||
        self.apply()
 | 
			
		||||
        return super(LineEdit, self).focusOutEvent(event)
 | 
			
		||||
 | 
			
		||||
    def apply(self):
 | 
			
		||||
        if self._value != self.value():
 | 
			
		||||
            self.valueSet.emit(self.value())
 | 
			
		||||
        self._value = self.value()
 | 
			
		||||
 | 
			
		||||
    def value(self):
 | 
			
		||||
        if self.text() == '':
 | 
			
		||||
            return None
 | 
			
		||||
        return float(self.text().replace(',', '.'))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TextEdit(LineEdit):
 | 
			
		||||
    VALIDATOR_CLS = None
 | 
			
		||||
    valueSet = QtCore.Signal(str)
 | 
			
		||||
 | 
			
		||||
    def value(self):
 | 
			
		||||
        if self.text() == '':
 | 
			
		||||
            return None
 | 
			
		||||
        return self.text()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FloatEdit(LineEdit):
 | 
			
		||||
    valueSet = QtCore.Signal(float)
 | 
			
		||||
    VALIDATOR_CLS = QtGui.QDoubleValidator
 | 
			
		||||
 | 
			
		||||
    def value(self):
 | 
			
		||||
        if self.text() == '':
 | 
			
		||||
            return None
 | 
			
		||||
        return float(self.text().replace(',', '.'))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class IntEdit(LineEdit):
 | 
			
		||||
    valueSet = QtCore.Signal(int)
 | 
			
		||||
    VALIDATOR_CLS = QtGui.QIntValidator
 | 
			
		||||
 | 
			
		||||
    def value(self):
 | 
			
		||||
        if self.text() == '':
 | 
			
		||||
            return None
 | 
			
		||||
        return int(float(self.text()))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Title(QtWidgets.QLabel):
 | 
			
		||||
    def __init__(self, title, parent=None):
 | 
			
		||||
        super(Title, self).__init__(parent)
 | 
			
		||||
        self.setFixedHeight(20)
 | 
			
		||||
        self.setStyleSheet('background: rgb(0, 0, 0, 25)')
 | 
			
		||||
        self.setText('<b>   ' + title)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TouchEdit(QtWidgets.QLineEdit):
 | 
			
		||||
    def keyPressEvent(self, event):
 | 
			
		||||
        self.setText(QtGui.QKeySequence(event.key()).toString().lower())
 | 
			
		||||
        self.textEdited.emit(self.text())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CommandButton(QtWidgets.QWidget):
 | 
			
		||||
    released = QtCore.Signal()
 | 
			
		||||
    playReleased = QtCore.Signal()
 | 
			
		||||
 | 
			
		||||
    def __init__(self, label, parent=None):
 | 
			
		||||
        super(CommandButton, self).__init__(parent)
 | 
			
		||||
        self.mainbutton = QtWidgets.QPushButton(label)
 | 
			
		||||
        self.mainbutton.released.connect(self.released.emit)
 | 
			
		||||
        self.playbutton = QtWidgets.QPushButton(icon('play.png'), '')
 | 
			
		||||
        self.playbutton.released.connect(self.playReleased.emit)
 | 
			
		||||
        self.playbutton.setFixedSize(22, 22)
 | 
			
		||||
        self.layout = QtWidgets.QHBoxLayout(self)
 | 
			
		||||
        self.layout.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        self.layout.setSpacing(2)
 | 
			
		||||
        self.layout.addWidget(self.mainbutton)
 | 
			
		||||
        self.layout.addWidget(self.playbutton)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LayerEdit(QtWidgets.QWidget):
 | 
			
		||||
    valueSet = QtCore.Signal(object)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent=None):
 | 
			
		||||
        super(LayerEdit, self).__init__(parent)
 | 
			
		||||
        self.layer = QtWidgets.QLineEdit()
 | 
			
		||||
        self.layer.setReadOnly(True)
 | 
			
		||||
        self.reset = QtWidgets.QPushButton('x')
 | 
			
		||||
        self.reset.released.connect(self.do_reset)
 | 
			
		||||
 | 
			
		||||
        self.layout = QtWidgets.QHBoxLayout(self)
 | 
			
		||||
        self.layout.setContentsMargins(0, 0, 0, 0)
 | 
			
		||||
        self.layout.setSpacing(0)
 | 
			
		||||
        self.layout.addWidget(self.layer)
 | 
			
		||||
        self.layout.addWidget(self.reset)
 | 
			
		||||
 | 
			
		||||
    def set_layer(self, text):
 | 
			
		||||
        self.layer.setText(text or '')
 | 
			
		||||
 | 
			
		||||
    def do_reset(self):
 | 
			
		||||
        if not self.layer.text():
 | 
			
		||||
            return
 | 
			
		||||
        self.layer.setText('')
 | 
			
		||||
        self.valueSet.emit(None)
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/screenshots/createbuttons.gif
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 916 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/screenshots/editor.gif
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 2.1 MiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/dwpicker/screenshots/picker.gif
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 895 KiB  | 
							
								
								
									
										35
									
								
								Scripts/Animation/dwpicker/scripts/change_namespace.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,35 @@
 | 
			
		||||
"""
 | 
			
		||||
The multiple nested namespaces is currently not supported by the namespace
 | 
			
		||||
switch system. This can cause issue for picker having to support sub namespace
 | 
			
		||||
on part of the rig.
 | 
			
		||||
This piece of code allow to set manually a namespace through the selected
 | 
			
		||||
shapes"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import dwpicker
 | 
			
		||||
 | 
			
		||||
namespace = 'write:nested:namespace:here'
 | 
			
		||||
picker = dwpicker.current()
 | 
			
		||||
 | 
			
		||||
## Edit Shapes from picker view selection
 | 
			
		||||
 | 
			
		||||
# selection = [s for s in picker.shapes if s.selected]
 | 
			
		||||
# for shape in selection:
 | 
			
		||||
#     targets = [namespace + ':' + t.split(':')[-1] for t in shape.targets()]
 | 
			
		||||
#     shape.options['action.targets'] = targets
 | 
			
		||||
 | 
			
		||||
# dwpicker._dwpicker.data_changed_from_picker(picker)
 | 
			
		||||
 | 
			
		||||
## Edit Shapes from advanced editor selection
 | 
			
		||||
 | 
			
		||||
index = dwpicker._dwpicker.pickers.index(picker)
 | 
			
		||||
editor = dwpicker._dwpicker.editors[index]
 | 
			
		||||
if editor is None:
 | 
			
		||||
    raise RuntimeWarning("Please open current picker's avanced editor")
 | 
			
		||||
 | 
			
		||||
selection = editor.shape_editor.selection
 | 
			
		||||
for shape in selection:
 | 
			
		||||
    targets = [namespace + ':' + t.split(':')[-1] for t in shape.targets()]
 | 
			
		||||
    shape.options['action.targets'] = targets
 | 
			
		||||
 | 
			
		||||
editor.set_data_modified()
 | 
			
		||||
							
								
								
									
										24
									
								
								Scripts/Animation/dwpicker/scripts/clean_reload_dwpicker.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,24 @@
 | 
			
		||||
"""
 | 
			
		||||
A developper hack to reload the Dreamwall picker without having to restart
 | 
			
		||||
Maya each time.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# If the picker is not in a known PYTHONPATH.
 | 
			
		||||
import sys
 | 
			
		||||
sys.path.insert(0, "<dwpicker path>")
 | 
			
		||||
 | 
			
		||||
# Code to clean modules and relaunch a Dreamwall picker with updated code.
 | 
			
		||||
try:
 | 
			
		||||
    # Important step to not let some callbacks left behind.
 | 
			
		||||
    dwpicker.close()
 | 
			
		||||
except:
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
for module in list(sys.modules):
 | 
			
		||||
    if "dwpicker" in module:
 | 
			
		||||
        print("deleted: " + module)
 | 
			
		||||
        del sys.modules[module]
 | 
			
		||||
 | 
			
		||||
import dwpicker
 | 
			
		||||
dwpicker.show()
 | 
			
		||||
@@ -0,0 +1,13 @@
 | 
			
		||||
# This code as to be ran in a Maya able to use 'dwpicker' package.
 | 
			
		||||
import os
 | 
			
		||||
from dwpicker.ingest import animschool
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
SOURCE_DIRECTORY = "" # Replace those variables before conversion
 | 
			
		||||
DESTINATION_DIRECTORY = ""
 | 
			
		||||
 | 
			
		||||
for f in os.listdir(SOURCE_DIRECTORY):
 | 
			
		||||
    if not f.lower().endswith(".pkr"):
 | 
			
		||||
        continue
 | 
			
		||||
    filepath = os.path.join(SOURCE_DIRECTORY, f)
 | 
			
		||||
    animschool.convert(filepath, DESTINATION_DIRECTORY)
 | 
			
		||||
							
								
								
									
										33
									
								
								Scripts/Animation/dwpicker/scripts/create_buttons.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,33 @@
 | 
			
		||||
import dwpicker
 | 
			
		||||
from dwpicker.scenedata import load_local_picker_data, store_local_picker_data
 | 
			
		||||
from dwpicker.templates import BUTTON
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def add_button(index, options, refresh_ui=True):
 | 
			
		||||
    """
 | 
			
		||||
    This works with pick closed as well.
 | 
			
		||||
    @param int index: the tab position of the dwpicker.
 | 
			
		||||
    @param dict options:
 | 
			
		||||
        This is a dictionnary of the shape options. List of possible options
 | 
			
		||||
        are can be found here dwpicker.templates.BUTTON
 | 
			
		||||
        (too much very many long to be documented here ;) )
 | 
			
		||||
    @param bool refresh_ui:
 | 
			
		||||
        this update the ui. Can be disabled for loop purpose.
 | 
			
		||||
    """
 | 
			
		||||
    pickers = load_local_picker_data()
 | 
			
		||||
    button = BUTTON.copy()
 | 
			
		||||
    button.update(options)
 | 
			
		||||
    pickers[index]['shapes'].append(button)
 | 
			
		||||
    store_local_picker_data(pickers)
 | 
			
		||||
    if refresh_ui:
 | 
			
		||||
        dwpicker.refresh()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
options = {
 | 
			
		||||
    'text.content': 'Button',
 | 
			
		||||
    'shape.left': 250,
 | 
			
		||||
    'shape.top': 150,
 | 
			
		||||
    'shape.width': 120.0,
 | 
			
		||||
    'shape.height': 25.0,
 | 
			
		||||
}
 | 
			
		||||
add_button(0, options)
 | 
			
		||||
@@ -0,0 +1,7 @@
 | 
			
		||||
import dwpicker
 | 
			
		||||
from dwpicker.picker import detect_picker_namespace
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
picker = dwpicker.current()
 | 
			
		||||
if picker:
 | 
			
		||||
    namespace = detect_picker_namespace(picker.shapes)
 | 
			
		||||