master bbfc07541807 cached
21 files
45.3 KB
12.7k tokens
57 symbols
1 requests
Download .txt
Repository: fredrikaverpil/pyvfx-boilerplate
Branch: master
Commit: bbfc07541807
Files: 21
Total size: 45.3 KB

Directory structure:
gitextract_ynxoaokt/

├── .github/
│   └── workflows/
│       ├── pypi.yml
│       └── tests.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── setup.cfg
├── setup.py
└── src/
    └── pyvfx_boilerplate/
        ├── __init__.py
        ├── boilerplate_ui.py
        ├── cli.py
        ├── init.py
        ├── mayapalette.py
        ├── menu.py
        ├── resources/
        │   ├── main_window.ui
        │   ├── module.ui
        │   ├── qpalette_maya2015.json
        │   └── qpalette_maya2016.json
        └── utils/
            ├── __init__.py
            ├── get_set_palette_data_qt4.py
            └── get_set_palette_data_qt5.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/pypi.yml
================================================
# https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions
# https://packaging.python.org/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/

name: PyPI
on:
  release:
    types: [created]

jobs:
  build-and-publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Setup Python
        uses: actions/setup-python@v1
        with:
          python-version: '3.x'
          architecture: x64

      - name: Build wheel
        run: |
          pip install --upgrade wheel setuptools
          python setup.py bdist_wheel

      - name: Publish to PyPI
        uses: pypa/gh-action-pypi-publish@master
        with:
          user: __token__
          password: ${{ secrets.pypi_password }}


================================================
FILE: .github/workflows/tests.yml
================================================
# https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions

name: Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ['2.x', '3.x']
    name: Python ${{ matrix.python-version }}
    steps:
      - uses: actions/checkout@v2
      - name: Setup Python
        uses: actions/setup-python@v1
        with:
          python-version: ${{ matrix.python-version }}
          architecture: x64

      - name: Build wheel
        run: |
          pip install --upgrade wheel setuptools
          python setup.py bdist_wheel

      - run: pip install isort && isort --recursive --diff .
      - run: pip install black && black --check .
        if: matrix.python-version == '3.x'
      - run: pip install flake8 && flake8


================================================
FILE: .gitignore
================================================
# Python
*.pyc

# Folders
.eggs
*.egg-info/
build
dist
venv



================================================
FILE: CHANGELOG.md
================================================
# Changelog

### Version 3.3.0

- adding Blender, 3DS Max, Houdini, and Unreal Engine support
- see below for installing PySide2 into Blender

Ultra Easy Guide To install PySide2 into Blender on macOS:

1) install blender
2) open blender at least once, and then close blender
3) open terminal and run the commands below
```bash
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
/Applications/Blender.app/Contents/Resources/2.81/python/bin/python3.7m get-pip.py
/Applications/Blender.app/Contents/Resources/2.81/python/bin/pip install PySide2, pyvfx-boilerplate
```

Ultra Easy Guide To install PySide into Unreal on Windows:

this currently fails: (testing on 4.24.2)
1) install Unreal
2) open a windows cmd or powershell and run the commands below
```bash
cd C:/Program Files/Unreal/UE_4.24/Engine/Binaries/ThirdParty/Python/Win64/
python.exe -m pip install --upgrade pip
python.exe -m pip install --no-warn-script-location PySide pyvfx-boilerplate
```

### Version 3.2.1

- rearrangement of namespace package location
- updated to setuptools_scm to handle version numbering

### Version 3.2.0

- uses kwargs to pass more arguments to the gui show
- auto docks into attribute editor panel in maya if dockable=True in kwargs

### Version 3.1.0

#### Changes from 2.0 (nzanepro fork)

- Updated to work with latest Qt.py (1.2.0b2)
- Tested with maya (2018.4), nuke (11.2v4), and PySide2 (5.11.2)
- Install via pip and you will get Qt.py installed as a dependency (see below)
- Now includes MayaQWidgetDockableMixin in maya
- Better Maya menuing via python instead of pymel, pyvfx now has a root menu, and other modules can be added to the menu.

- Example new app via inheritance of Boilerplate (includes extension of MayaQWidgetDockableMixin):

https://github.com/nzanepro/pyvfx.boilerplateinherited

#### Changes from 1.0

- Complete rewrite of the boilerplate.
- Requires (and bundles) the [`Qt.py`](https://github.com/mottosso/Qt.py)
- Tested with Python 2.7.11 and 3.5.1.
- Uses `PySide.QUiTools` instead of `pysideuic`, which was used in v1.0.
- No longer uses the complex "wrap instance" approach in favor for simpler code. Because of this, UIs are no longer loaded into `self`.
- Maya palette styling in standalone mode.
- Git repo name change: all lowercase.
- Pretty much PEP8 compliant.
- Properly parented window in Maya
- Writing of .pyc (bytecode) files disabled to prevent issues between Python 2 and 3.
- Can be run in multiple ways (see examples).


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2016 Fredrik Averpil

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.



================================================
FILE: README.md
================================================
# pyvfx-boilerplate

![Tests](https://github.com/fredrikaverpil/pyvfx-boilerplate/workflows/Tests/badge.svg) ![PyPI](https://github.com/fredrikaverpil/pyvfx-boilerplate/workflows/PyPI/badge.svg)

A boilerplate for creating PyQt4/PySide and PyQt5/PySide2 applications running in Maya, Nuke, Blender, 3DS Max, Houdini, Unreal Engine or completely standalone.

## Documentation

### Version 3.x

- The entire boilerplate was re-written so it could be packaged and distributed with PyPi.
- Adding Blender, 3DS Max, Houdini, and Unreal Engine support.

For details, see [CHANGELOG.md](CHANGELOG.md).

### Noteworthy known issues

- Does not work with Nuke 10.0v1 on OS X: [#7](https://github.com/fredrikaverpil/pyvfx-boilerplate/issues/7)
- Maya palette glitchy in standalone mode with PySide/PyQt4 on OS X (disabled by default): [#9](https://github.com/fredrikaverpil/pyvfx-boilerplate/issues/9)
- Window will not stay on top of Nuke (OS X) without Qt.Tool or Qt.WindowStaysOnTopHint: [#12](https://github.com/fredrikaverpil/pyvfx-boilerplate/issues/12)
### Installation

Easy way:

```bash
pip install pyvfx-boilerplate
```

Long way:

```bash
git clone https://github.com/fredrikaverpil/pyvfx-boilerplate.git
cd pyvfx-boilerplate
python setup.py sdist bdist_wheel
pip install dist/*
```

### Example usage

Pip installs a program named `pyvfx-boilerplate` as an example Run as standalone:
(you may need to additionally install PyQt4, PyQt5, PySide or PySide2 for standalone to work depending on your system configuration)

```bash
pyvfx-boilerplate
```

Run in script editor of Maya or Nuke:

```python
import sys
sys.path.append('/path/to/pyvfx-boilerplate')
from pyvfx_boilerplate import boilerplate_ui
bpr = boilerplate_ui.BoilerplateRunner()
bpr.run_main()
```

### Modifying the boilerplate

- See inheritance example above

### Development guidelines

Since the boilerplate relies on [`Qt.py`](https://github.com/mottosso/Qt.py), you should design your application as if you were designing it for PyQt5/PySide2. This means creating widgets using `QtWidgets` rather than `QtGui`. The `Qt.py` module takes care of the remapping and makes for compatibility with PyQt4/PySide. Read more over at the [`Qt.py` repository](https://github.com/mottosso/Qt.py).

Tip: when you cannot rely on `Qt.py`, create an issue (probably over at [`Qt.py`](https://github.com/mottosso/Qt.py)) and/or detect which binding is being used and write some custom code:

```python
from Qt import QtCompat

if QtCompat.__binding__ in ('PyQt4', 'PySide'):
    # Do something if PyQt4 or PySide is used

if QtCompat__binding.startswith('PySide'):
    # Do something if PySide or PySide2 is used

if QtCompat__binding == 'PySide2':
    # Do something if PySide2 is used
```

### Issues

Something wrong, have a question or wish to file a feature request?

Open up an issue [here](https://github.com/fredrikaverpil/pyvfx-boilerplate/issues)!

### Contribute

If you wish to contribute, pull requests are more than welcome!


================================================
FILE: setup.cfg
================================================
[bdist_wheel]
universal=1

================================================
FILE: setup.py
================================================
# https://docs.python.org/3/distutils/setupscript.html#additional-meta-data

import setuptools

with open("README.md", "r") as fh:
    long_description = fh.read()

name = "pyvfx-boilerplate"
url = "https://github.com/fredrikaverpil/pyvfx-boilerplate"
description = "A boilerplate Qt Py* app that runs in dcc apps, py2, or py3."
package_dir = "src"
cli_modules = [
    "pyvfx-boilerplate=pyvfx_boilerplate.cli:main",
]

setuptools.setup(
    setup_requires=["setuptools_scm"],
    use_scm_version={"local_scheme": "node-and-timestamp"},
    name=name,
    description=description,
    long_description=long_description,
    long_description_content_type="text/markdown",
    url=url,
    packages=setuptools.find_packages(package_dir),
    package_dir={"": package_dir},
    entry_points={"console_scripts": cli_modules},
    include_package_data=True,
    install_requires=["Qt.py"],
    classifiers=[
        "Programming Language :: Python :: 2.7",
        "Programming Language :: Python :: 3.7",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
)


================================================
FILE: src/pyvfx_boilerplate/__init__.py
================================================
__path__ = __import__("pkgutil").extend_path(__path__, __name__)


================================================
FILE: src/pyvfx_boilerplate/boilerplate_ui.py
================================================
"""This uses a Qt binding of "any" kind, thanks to the Qt.py module,
to produce an UI. First, one .ui file is loaded and then attaches
another .ui file onto the first. Think of it as creating a modular UI.

More on Qt.py:
https://github.com/mottosso/Qt.py
"""

import atexit
import os
import platform
import sys

import Qt
from Qt import QtCompat, QtCore, QtWidgets  # pylint: disable=E0611

from . import mayapalette

try:
    import maya.cmds as cmds
    from maya.app.general.mayaMixin import MayaQWidgetDockableMixin

    MAYA = True
except ImportError:
    MAYA = False

try:
    import nuke
    import nukescripts

    NUKE = True
except ImportError:
    NUKE = False

try:
    import hou

    HOUDINI = True
except ImportError:
    HOUDINI = False

try:
    import MaxPlus

    THREEDSMAX = True
except ImportError:
    THREEDSMAX = False

try:
    import bpy

    BLENDER = True
except ImportError:
    BLENDER = False

try:
    import unreal

    UNREAL = True
except ImportError:
    UNREAL = False

# ----------------------------------------------------------------------
# Configuration
# ----------------------------------------------------------------------

# Full path to where .ui files are stored
UI_PATH = os.path.join(os.path.dirname(__file__), "resources")

# Palette filepath
PALETTE_FILEPATH = os.path.join(UI_PATH, "qpalette_maya2016.json")
WTITLE = "Boilerplate"
WOBJ = "boilerPlate"

# ----------------------------------------------------------------------
# Main script
# ----------------------------------------------------------------------


class _Boilerplate(QtWidgets.QMainWindow):
    """
    Don't subclass this directly, subclass Boilerplate without the '_'
    """

    def __init__(self, parent=None, win_title=WTITLE, win_object=WOBJ):

        super(_Boilerplate, self).__init__(parent)

        # Set object name and window title
        self.setObjectName(win_object)
        self.setWindowTitle(win_title)

        # Window type
        self.setWindowFlags(QtCore.Qt.Window)

        if MAYA:
            # Makes Maya perform magic which makes the window stay
            # on top in OS X and Linux. As an added bonus, it'll
            # make Maya remember the window position
            self.setProperty("saveWindowPref", True)

        self.setupUi()

    def setupUi(self):
        # Filepaths
        main_window_file = os.path.join(UI_PATH, "main_window.ui")
        module_file = os.path.join(UI_PATH, "module.ui")

        # Load UIs
        self.main_widget = QtCompat.load_ui(main_window_file)  # Main window UI
        self.module_widget = QtCompat.load_ui(module_file)  # Module UI

        # Attach module to main window UI's boilerVerticalLayout layout
        self.main_widget.boilerVerticalLayout.addWidget(self.module_widget)

        # Edit widget which resides in module UI
        self.module_widget.boilerLabel.setText("Push the button!")

        # Edit widget which reside in main window UI
        self.main_widget.boilerPushButton.setText("Push me!")

        # Set the main widget
        self.setCentralWidget(self.main_widget)

        # Define minimum size of UI
        self.setMinimumSize(200, 200)

        # Signals
        # The "pushButton" widget resides in the main window UI
        self.main_widget.boilerPushButton.clicked.connect(self.say_hello)

    def say_hello(self):
        """Set the label text.
        The "label" widget resides in the module
        """
        self.module_widget.boilerLabel.setText("Hello world!")
        self.module_widget.boilerLabel.update()


if MAYA:

    class Boilerplate(MayaQWidgetDockableMixin, _Boilerplate):
        """Example showing how UI files can be loaded using the same script
        when taking advantage of the Qt.py module and build-in methods
        from PySide/PySide2/PyQt4/PyQt5."""

        def __init__(self, parent=None, win_title=WTITLE, win_object=WOBJ):
            super(Boilerplate, self).__init__(
                parent, win_title=win_title, win_object=win_object
            )


# elif THREEDSMAX:
#     # https://forums.autodesk.com/t5/3ds-max-programming/3ds-max-2019-qt-dock-widget/td-p/8164550 # noqa
#     # https://help.autodesk.com/view/3DSMAX/2019/ENU/?guid=__py_ref_demo_py_side_tool_bar_q_widget_8py_example_html # noqa
#     pass
else:

    class Boilerplate(_Boilerplate):
        """Example showing how UI files can be loaded using the same script
        when taking advantage of the Qt.py module and build-in methods
        from PySide/PySide2/PyQt4/PyQt5."""

        def __init__(self, parent=None, win_title=WTITLE, win_object=WOBJ):
            super(Boilerplate, self).__init__(
                parent, win_title=win_title, win_object=win_object
            )


# ----------------------------------------------------------------------
# DCC application helper functions
# ----------------------------------------------------------------------
def _maya_delete_ui(window_title, window_object):
    """Delete existing UI in Maya"""
    if cmds.window(window_object, q=True, exists=True):
        cmds.deleteUI(window_object)  # Delete window
    if cmds.dockControl("MayaWindow|" + window_title, q=True, ex=True):
        cmds.deleteUI("MayaWindow|" + window_title)  # Delete docked window


def _maya_delete_workspace(window_object):
    """Delete existing workspace in Maya"""
    control = window_object + "WorkspaceControl"
    if cmds.workspaceControl(control, q=True, exists=True):
        cmds.workspaceControl(control, e=True, close=True)
        cmds.deleteUI(control, control=True)


def _maya_update_workspace(window_object):
    """Updates existing workspace in Maya"""
    control = window_object + "WorkspaceControl"
    # TODO make this argument controllable
    if cmds.workspaceControl(control, q=True, exists=True):
        cmds.workspaceControl(
            control,
            e=True,
            restore=True,
            retain=True,
            # # options below
            # dockToMainWindow=("left", -1),
            # tabToControl=("ChannelBoxLayerEditor", -1),
            # tabToControl=("Outliner", -1),
            tabToControl=("AttributeEditor", -1),
        )


def _maya_main_window():
    """Return Maya's main window"""
    for obj in QtWidgets.QApplication.topLevelWidgets():
        if obj.objectName() == "MayaWindow":
            return obj
    raise RuntimeError("Could not find MayaWindow instance")


def _nuke_delete_ui(window_object):
    """Delete existing UI in Nuke"""
    for obj in QtWidgets.QApplication.allWidgets():
        if obj.objectName() == window_object:
            obj.deleteLater()


def _nuke_main_window():
    """Returns Nuke's main window"""
    for obj in QtWidgets.QApplication.topLevelWidgets():
        if (
            obj.inherits("QMainWindow")
            and obj.metaObject().className() == "Foundry::UI::DockMainWindow"
        ):
            return obj
    else:
        raise RuntimeError("Could not find DockMainWindow instance")


def _nuke_set_zero_margins(widget_object):
    """Remove Nuke margins when docked UI

    .. _More info:
        https://gist.github.com/maty974/4739917
    """
    parentApp = QtWidgets.QApplication.allWidgets()
    parentWidgetList = []
    for parent in parentApp:
        for child in parent.children():
            if widget_object.__class__.__name__ == child.__class__.__name__:
                parentWidgetList.append(parent.parentWidget())
                try:
                    twoup = parent.parentWidget().parentWidget()
                    parentWidgetList.append(twoup)
                    threeup = twoup.parentWidget()
                    parentWidgetList.append(threeup)
                except AttributeError:
                    pass

                for sub in parentWidgetList:
                    if sub is not None:
                        for tinychild in sub.children():
                            try:
                                tinychild.setContentsMargins(0, 0, 0, 0)
                            except AttributeError:
                                pass


def _houdini_main_window():
    """Return Houdini's main window"""
    return hou.ui.mainQtWindow()


def _3dsmax_main_window():
    """Return 3dsmax's main window"""
    return MaxPlus.GetQMaxMainWindow()


# ----------------------------------------------------------------------
# Run functions
# ----------------------------------------------------------------------
class BoilerplateRunner:
    def __init__(self, guiClass=Boilerplate, win_title=WTITLE, win_object=WOBJ):  # noqa

        self.guiClass = guiClass
        self.window_title = win_title
        self.window_object = win_object
        self.boil = None

    def run_maya(self, **kwargs):
        """Run in Maya"""
        # Delete any existing existing UI
        _maya_delete_ui(self.window_title, self.window_object)
        # Delete any existing existing workspace
        _maya_delete_workspace(self.window_object)
        self.boil = self.guiClass(
            _maya_main_window(), self.window_title, self.window_object
        )

        # Makes Maya perform magic which makes the window stay
        # on top in OS X and Linux. As an added bonus, it'll
        # make Maya remember the window position
        self.boil.setProperty("saveWindowPref", True)

        if "dockable" in kwargs and kwargs["dockable"]:
            kwargs["allowed_areas"] = ["right", "left"]
        self.boil.show(**kwargs)
        if "dockable" in kwargs and kwargs["dockable"]:
            _maya_update_workspace(self.window_object)

    def run_nuke(self, **kwargs):
        """Run in Nuke

        Note:
            If you want the UI to always stay on top, replace:
            `boil.ui.setWindowFlags(QtCore.Qt.Tool)`
            with:
            `boil.ui.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)`

            If you want the UI to be modal:
            `boil.ui.setWindowModality(QtCore.Qt.WindowModal)`
        """
        _nuke_delete_ui(self.window_object)  # Delete any alrady existing UI
        if "dockable" in kwargs and kwargs["dockable"]:
            widgetname = ("{}.{}").format(
                self.guiClass.__module__, self.guiClass.__name__
            )
            panel = nukescripts.panels.registerWidgetAsPanel(
                widget=widgetname,  # module_name.Class_name
                name=self.window_title,
                id="uk.co.thefoundry." + self.window_title,
                create=True,
            )
            pane = nuke.getPaneFor("Properties.1")
            panel.addToPane(pane)
            self.boil = panel.customKnob.getObject().widget
            _nuke_set_zero_margins(self.boil)
        else:
            self.boil = self.guiClass(
                _nuke_main_window(), self.window_title, self.window_object
            )
            self.boil.setWindowFlags(QtCore.Qt.Tool)
            self.boil.show()  # Show the UI

    def run_houdini(self, **kwargs):
        """Run in Houdini"""
        self.boil = self.guiClass(
            _houdini_main_window(), self.window_title, self.window_object
        )
        self.boil.show()

    def run_3dsmax(self, **kwargs):
        """Run in 3dsmax"""
        # https://gist.github.com/mrabito/0f9d1f177a3bea94d33d35b476c88731
        # dockable?
        # https://help.autodesk.com/view/3DSMAX/2019/ENU/?guid=__py_ref_demo_py_side_tool_bar_q_widget_8py_example_html
        self.boil = self.guiClass(
            _3dsmax_main_window(), self.window_title, self.window_object
        )
        self.boil.show()

    def on_exit_unreal(self):
        app = QtWidgets.QApplication.instance()
        if app:
            app.store_window_geometry()
            app.quit()

    def run_unreal(self, **kwargs):
        # https://github.com/20tab/UnrealEnginePython
        # https://forums.unrealengine.com/development-discussion/python-scripting/1674204-dock-qtwidget-to-unreal
        # https://github.com/AlexQuevillon/UnrealPythonLibrary/blob/master/UnrealProject/UnrealPythonLibrary/Plugins/UnrealPythonLibraryPlugin/Content/Python/PythonLibraries/QtFunctions.py
        # https://forums.unrealengine.com/unreal-engine/unreal-studio/1526501-how-to-get-the-main-window-of-the-editor-to-parent-qt-or-pyside-application-to-it
        app = QtWidgets.QApplication.instance()

        if not app:
            # create the first instance
            app = QtWidgets.QApplication(sys.argv)
            app.aboutToQuit.connect(self.on_exit_unreal)

        atexit.register(self.on_exit_unreal)

        self.boil = None

        self.event_loop = QtCore.QEventLoop()
        self.boil = self.guiClass(None, self.window_title, self.window_object)
        mayapalette.set_maya_palette_with_tweaks(PALETTE_FILEPATH)
        unreal.parent_external_window_to_slate(self.boil.winId())
        self.boil.show()

    def on_exit_blender(self):
        """
        Close BlenderApplication instance on exit

        Returns: None

        """
        app = QtWidgets.QApplication.instance()
        if app:
            app.store_window_geometry()
            app.quit()

    def on_update_blender(self):
        # https://github.com/techartorg/bqt
        app = QtWidgets.QApplication.instance()
        if app.should_close:
            win = bpy.context.window_manager.windows[0]
            bpy.ops.wm.quit_blender({"window": win}, "INVOKE_DEFAULT")

    def run_blender(self, **kwargs):
        """Run in Blender"""
        # mix of code from
        # https://github.com/friedererdmann/blender_pyside2_example
        # and
        # https://github.com/techartorg/bqt

        # TODO add dockable?
        # https://github.com/cube-creative/guibedos/blob/master/guibedos/blender.py
        app = QtWidgets.QApplication.instance()

        if not app:
            # create the first instance
            app = QtWidgets.QApplication(sys.argv)
            # modal
            bpy.app.timers.register(self.on_update_blender, persistent=True)

        atexit.register(self.on_exit_blender)

        self.event_loop = QtCore.QEventLoop()
        self.boil = self.guiClass(None, self.window_title, self.window_object)
        mayapalette.set_maya_palette_with_tweaks(PALETTE_FILEPATH)
        self.boil.show()
        # non-modal:
        # app.exec_()

    def run_standalone(self, **kwargs):
        """Run standalone

        Note:
            Styling the UI with the Maya palette on OS X when using the
            PySide/PyQt4 bindings result in various issues, which is why
            it is disabled by default when you're running this combo.

        .. _Issue #9:
           https://github.com/fredrikaverpil/pyvfx-boilerplate/issues/9
        """
        extrawin = True
        app = QtWidgets.QApplication.instance()

        if not app:
            app = QtWidgets.QApplication(sys.argv)
            extrawin = False

        self.boil = self.guiClass(
            win_title=self.window_title, win_object=self.window_object
        )
        if not (platform.system() == "Darwin" and (Qt.IsPySide or Qt.IsPyQt4)):
            mayapalette.set_maya_palette_with_tweaks(PALETTE_FILEPATH)
        self.boil.show()  # Show the UI

        if not extrawin:
            sys.exit(app.exec_())

    def run_main(self, **kwargs):
        """Run appropriate gui"""
        if MAYA:
            self.run_maya(**kwargs)
        elif NUKE:
            self.run_nuke(**kwargs)
        elif HOUDINI:
            self.run_houdini(**kwargs)
        elif THREEDSMAX:
            self.run_3dsmax(**kwargs)
        elif BLENDER:
            self.run_blender(**kwargs)
        elif UNREAL:
            self.run_unreal(**kwargs)
        else:
            self.run_standalone(**kwargs)


================================================
FILE: src/pyvfx_boilerplate/cli.py
================================================
#!/usr/bin/env python
import os
import sys

# this is required to keep pyside2 seperate from maya and nuke's pyside2
# make sure this is added to any inherited program to work from cli
if "QT_PREFERRED_PATH" in os.environ:
    sys.path.append(os.environ["QT_PREFERRED_PATH"])

from pyvfx_boilerplate import menu  # isort:skip


def main():
    menu.activate()


if __name__ == "__main__":
    main()


================================================
FILE: src/pyvfx_boilerplate/init.py
================================================


================================================
FILE: src/pyvfx_boilerplate/mayapalette.py
================================================
import json

from Qt import QtGui, QtWidgets


def set_palette_from_dict(dct):
    """Set palette to current QApplication based on given dictionary"""
    groups = ["Disabled", "Active", "Inactive", "Normal"]
    roles = [
        "AlternateBase",
        "Background",
        "Base",
        "Button",
        "ButtonText",
        "BrightText",
        "Dark",
        "Foreground",
        "Highlight",
        "HighlightedText",
        "Light",
        "Link",
        "LinkVisited",
        "Mid",
        "Midlight",
        "Shadow",
        "ToolTipBase",
        "ToolTipText",
        "Text",
        "Window",
        "WindowText",
    ]
    palette = QtGui.QPalette()
    for role in roles:
        try:
            for group in groups:
                color = QtGui.QColor(dct["%s:%s" % (role, group)])
                qGrp = getattr(QtGui.QPalette, group)
                qRl = getattr(QtGui.QPalette, role)
                palette.setColor(qGrp, qRl, color)
        except:  # noqa
            print("Could not use: " + str(palette))
    try:
        QtWidgets.QApplication.setPalette(palette)
    except:  # noqa
        print("Could not set palette: " + str(palette))


def set_style():
    """Set style"""
    available_styles = QtWidgets.QStyleFactory.keys()
    if "Fusion" in available_styles:
        QtWidgets.QApplication.setStyle("Fusion")
    elif "Plastique" in available_styles:
        QtWidgets.QApplication.setStyle("Plastique")


def set_maya_tweaks():
    """Apply Maya-specific styling"""
    base_palette = QtWidgets.QApplication.palette()

    # Set custom colors
    LIGHT_COLOR = QtGui.QColor(100, 100, 100)
    MID_COLOR = QtGui.QColor(68, 68, 68)

    # Create a new palette
    tab_palette = QtGui.QPalette(base_palette)
    tab_palette.setBrush(QtGui.QPalette.Window, QtGui.QBrush(LIGHT_COLOR))
    tab_palette.setBrush(QtGui.QPalette.Button, QtGui.QBrush(MID_COLOR))

    # Define the widgets that needs tweaking
    widget_palettes = {}
    widget_palettes["QTabBar"] = tab_palette
    widget_palettes["QTabWidget"] = tab_palette

    # Set the new tweaked palette
    for name, palette in widget_palettes.items():
        QtWidgets.QApplication.setPalette(palette, name)


def read_json(filepath):
    """Read given JSON filepath into dictionary"""
    with open(filepath, "r") as data_file:
        data = json.load(data_file)
    return data


def set_maya_palette_with_tweaks(palette_filepath):
    """Apply styling to current QApplication"""
    data = read_json(palette_filepath)
    set_palette_from_dict(data)
    set_style()
    set_maya_tweaks()


================================================
FILE: src/pyvfx_boilerplate/menu.py
================================================
import pyvfx_boilerplate.boilerplate_ui as bpui

try:
    import maya.cmds as cmds
    import maya.mel as mel

    MAYA = True
except ImportError:
    MAYA = False

try:
    import nuke

    NUKE = True
except ImportError:
    NUKE = False

try:
    import hou  # noqa

    HOUDINI = True
except ImportError:
    HOUDINI = False

try:
    import MaxPlus

    THREEDSMAX = True
except ImportError:
    THREEDSMAX = False

try:
    import bpy

    BLENDER = True
except ImportError:
    BLENDER = False

try:
    import unreal  # noqa

    UNREAL = True
except ImportError:
    UNREAL = False


rootMenuName = "pyvfx"


def activate(dockable=False):
    bpr = bpui.BoilerplateRunner(bpui.Boilerplate)
    kwargs = {}
    kwargs["dockable"] = dockable
    bpr.run_main(**kwargs)


mcmd = "import pyvfx_boilerplate.menu\npyvfx_boilerplate.menu.activate({})"
if NUKE:
    m = nuke.menu("Nuke")
    menuname = "{}/boilerplate UI".format(rootMenuName)
    m.addCommand("{}/boilerplate UI".format(rootMenuName), mcmd.format(""))
    m.addCommand("{} dockable".format(menuname), mcmd.format("True"))

elif MAYA:
    MainMayaWindow = mel.eval("$temp = $gMainWindow")
    if not cmds.menu("pyvfxMenuItemRoot", exists=True):
        cmds.menu(
            "pyvfxMenuItemRoot",
            label=rootMenuName,
            parent=MainMayaWindow,
            tearOff=True,
            allowOptionBoxes=True,
        )

    cmds.menuItem(
        label="boilerplate UI",
        parent="pyvfxMenuItemRoot",
        ec=True,
        command=mcmd.format(""),
    )

    cmds.menuItem(
        label="boilerplate UI dockable",
        parent="pyvfxMenuItemRoot",
        ec=True,
        command=mcmd.format("True"),
    )

elif HOUDINI:
    print("add menu code here for Houdini")
    activate()

elif UNREAL:
    # http://golaem.com/content/doc/golaem-crowd-documentation/rendering-unreal-engine
    # https://github.com/AlexQuevillon/UnrealPythonLibrary/tree/master/UnrealProject/UnrealPythonLibrary
    print("add menu code here for Unreal")
    activate()

elif THREEDSMAX:

    def activate_dockable():
        activate(True)

    MaxPlus.MenuManager.UnregisterMenu(rootMenuName)
    mb = MaxPlus.MenuBuilder(rootMenuName)
    menuitem = "boilerplate UI"
    menuitemD = "{} dockable".format(menuitem)
    menulist = [
        (menuitem, menuitem, activate),
        (menuitemD, menuitemD, activate_dockable),
    ]
    for item in menulist:
        action = MaxPlus.ActionFactory.Create(item[0], item[1], item[2])
        mb.AddItem(action)
    menu = mb.Create(MaxPlus.MenuManager.GetMainMenu())

elif BLENDER:
    # a little of This
    # https://blender.stackexchange.com/questions/156652/topbar-ht-upper-bar-append-add-menus-in-two-places
    # and a little of this
    # https://blenderartists.org/t/creating-a-custom-menu-option/627316/4

    class PyvfxBoilerplateActivateOperator(bpy.types.Operator):
        """start the pyvfx_boilerplate ui"""

        bl_idname = "pyvfx_boilerplate_activate"
        bl_label = "boilerplate UI"

        def execute(self, context):
            activate()
            return {"FINISHED"}

    class TOPBAR_MT_pyvfx_menu(bpy.types.Menu):
        """create the pyvfx top menu and pyvfx menu item"""

        bl_label = rootMenuName

        def draw(self, context):
            """create the pyvfx menu item"""
            layout = self.layout
            layout.operator("pyvfx_boilerplate_activate")

        def menu_draw(self, context):
            """create the pyvfx top menu"""
            self.layout.menu("TOPBAR_MT_pyvfx_menu")

    def register():
        bpy.utils.register_class(PyvfxBoilerplateActivateOperator)
        bpy.utils.register_class(TOPBAR_MT_pyvfx_menu)
        bpy.types.TOPBAR_MT_editor_menus.append(TOPBAR_MT_pyvfx_menu.menu_draw)

    def unregister():
        bpy.utils.unregister_class(PyvfxBoilerplateActivateOperator)
        bpy.types.TOPBAR_MT_editor_menus.remove(TOPBAR_MT_pyvfx_menu.menu_draw)
        bpy.utils.unregister_class(TOPBAR_MT_pyvfx_menu)

    register()

else:
    activate()


================================================
FILE: src/pyvfx_boilerplate/resources/main_window.ui
================================================
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>405</width>
    <height>348</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QGridLayout" name="gridLayout">
    <item row="0" column="0">
     <layout class="QVBoxLayout" name="boilerVerticalLayout">
      <item>
       <widget class="QPushButton" name="boilerPushButton">
        <property name="text">
         <string>PushButton</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>


================================================
FILE: src/pyvfx_boilerplate/resources/module.ui
================================================
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
  <layout class="QGridLayout" name="gridLayout">
   <item row="0" column="0">
    <widget class="QFrame" name="frame">
     <property name="frameShape">
      <enum>QFrame::StyledPanel</enum>
     </property>
     <property name="frameShadow">
      <enum>QFrame::Raised</enum>
     </property>
     <layout class="QGridLayout" name="gridLayout_2">
      <item row="0" column="0">
       <widget class="QLabel" name="boilerLabel">
        <property name="text">
         <string>TextLabel</string>
        </property>
       </widget>
      </item>
     </layout>
    </widget>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>


================================================
FILE: src/pyvfx_boilerplate/resources/qpalette_maya2015.json
================================================
{
  "Foreground:Active": 4291348680,
  "HighlightedText:Active": 4294967295,
  "Mid:Active": 4281150765,
  "WindowText:Normal": 4291348680,
  "Window:Disabled": 4282664004,
  "AlternateBase:Inactive": 4281216558,
  "Midlight:Normal": 4282071867,
  "HighlightedText:Inactive": 4294967295,
  "Text:Disabled": 4285098345,
  "Midlight:Disabled": 4282071867,
  "Midlight:Inactive": 4282071867,
  "Highlight:Disabled": 4283856276,
  "Base:Disabled": 4280953386,
  "Window:Active": 4282664004,
  "Background:Inactive": 4282664004,
  "AlternateBase:Disabled": 4281216558,
  "Button:Inactive": 4284769380,
  "AlternateBase:Active": 4281216558,
  "LinkVisited:Active": 4283570315,
  "ToolTipBase:Inactive": 4294967260,
  "Dark:Inactive": 4280624421,
  "LinkVisited:Inactive": 4283570315,
  "Dark:Disabled": 4280624421,
  "Light:Inactive": 4284572001,
  "ButtonText:Inactive": 4291348680,
  "Dark:Active": 4280624421,
  "Light:Disabled": 4284572001,
  "Button:Disabled": 4283453520,
  "Shadow:Inactive": 4278190080,
  "ToolTipBase:Disabled": 4294967260,
  "BrightText:Inactive": 4280624421,
  "ToolTipBase:Active": 4294967260,
  "Highlight:Active": 4284976562,
  "BrightText:Disabled": 4294967295,
  "Highlight:Inactive": 4284976562,
  "ToolTipText:Active": 4278190080,
  "Light:Active": 4284572001,
  "Button:Active": 4284769380,
  "ButtonText:Disabled": 4286611584,
  "HighlightedText:Normal": 4294967295,
  "Base:Inactive": 4280953386,
  "BrightText:Active": 4280624421,
  "Highlight:Normal": 4284976562,
  "AlternateBase:Normal": 4281216558,
  "Dark:Normal": 4280624421,
  "Window:Inactive": 4282664004,
  "LinkVisited:Normal": 4283570315,
  "Link:Normal": 4278190318,
  "Base:Active": 4280953386,
  "Mid:Inactive": 4281150765,
  "Foreground:Disabled": 4286611584,
  "Text:Normal": 4291348680,
  "Link:Inactive": 4278190318,
  "ButtonText:Normal": 4291348680,
  "WindowText:Inactive": 4291348680,
  "ToolTipBase:Normal": 4294967260,
  "ToolTipText:Inactive": 4278190080,
  "WindowText:Disabled": 4286611584,
  "ToolTipText:Normal": 4278190080,
  "Midlight:Active": 4282071867,
  "ButtonText:Active": 4291348680,
  "BrightText:Normal": 4280624421,
  "Text:Active": 4291348680,
  "Mid:Normal": 4281150765,
  "WindowText:Active": 4291348680,
  "Base:Normal": 4280953386,
  "Background:Normal": 4282664004,
  "Mid:Disabled": 4281150765,
  "Background:Disabled": 4282664004,
  "Link:Disabled": 4278190318,
  "Window:Normal": 4282664004,
  "Shadow:Active": 4278190080,
  "Text:Inactive": 4291348680,
  "Button:Normal": 4284769380,
  "Light:Normal": 4284572001,
  "Link:Active": 4278190318,
  "Background:Active": 4282664004,
  "HighlightedText:Disabled": 4294967295,
  "Shadow:Disabled": 4278190080,
  "ToolTipText:Disabled": 4278190080,
  "Foreground:Normal": 4291348680,
  "LinkVisited:Disabled": 4283570315,
  "Shadow:Normal": 4278190080,
  "Foreground:Inactive": 4291348680
}

================================================
FILE: src/pyvfx_boilerplate/resources/qpalette_maya2016.json
================================================
{
  "Foreground:Active": 4290493371,
  "HighlightedText:Active": 4294967295,
  "Mid:Active": 4281150765,
  "WindowText:Normal": 4290493371,
  "Window:Disabled": 4282664004,
  "AlternateBase:Inactive": 4281216558,
  "Midlight:Normal": 4281808695,
  "HighlightedText:Inactive": 4294967295,
  "Text:Disabled": 4285098345,
  "Midlight:Disabled": 4281808695,
  "Midlight:Inactive": 4281808695,
  "Highlight:Disabled": 4283590260,
  "Base:Disabled": 4281019179,
  "Window:Active": 4282664004,
  "Background:Inactive": 4282664004,
  "AlternateBase:Disabled": 4281216558,
  "Button:Inactive": 4284308829,
  "AlternateBase:Active": 4281216558,
  "LinkVisited:Active": 4283570315,
  "ToolTipBase:Inactive": 4294967260,
  "Dark:Inactive": 4280624421,
  "LinkVisited:Inactive": 4283570315,
  "Dark:Disabled": 4280624421,
  "Light:Inactive": 4284572001,
  "ButtonText:Inactive": 4293848814,
  "Dark:Active": 4280624421,
  "Light:Disabled": 4284572001,
  "Button:Disabled": 4283124555,
  "Shadow:Inactive": 4278190080,
  "ToolTipBase:Disabled": 4294967260,
  "BrightText:Inactive": 4280624421,
  "ToolTipBase:Active": 4294967260,
  "Highlight:Active": 4283598246,
  "BrightText:Disabled": 4294967295,
  "Highlight:Inactive": 4283598246,
  "ToolTipText:Active": 4278190080,
  "Light:Active": 4284572001,
  "Button:Active": 4284308829,
  "ButtonText:Disabled": 4286611584,
  "HighlightedText:Normal": 4294967295,
  "Base:Inactive": 4281019179,
  "BrightText:Active": 4280624421,
  "Highlight:Normal": 4283598246,
  "AlternateBase:Normal": 4281216558,
  "Dark:Normal": 4280624421,
  "Window:Inactive": 4282664004,
  "LinkVisited:Normal": 4283570315,
  "Link:Normal": 4278190318,
  "Base:Active": 4281019179,
  "Mid:Inactive": 4281150765,
  "Foreground:Disabled": 4286611584,
  "Text:Normal": 4291348680,
  "Link:Inactive": 4278190318,
  "ButtonText:Normal": 4293848814,
  "WindowText:Inactive": 4290493371,
  "ToolTipBase:Normal": 4294967260,
  "ToolTipText:Inactive": 4278190080,
  "WindowText:Disabled": 4286611584,
  "ToolTipText:Normal": 4278190080,
  "Midlight:Active": 4281808695,
  "ButtonText:Active": 4293848814,
  "BrightText:Normal": 4280624421,
  "Text:Active": 4291348680,
  "Mid:Normal": 4281150765,
  "WindowText:Active": 4290493371,
  "Base:Normal": 4281019179,
  "Background:Normal": 4282664004,
  "Mid:Disabled": 4281150765,
  "Background:Disabled": 4282664004,
  "Link:Disabled": 4278190318,
  "Window:Normal": 4282664004,
  "Shadow:Active": 4278190080,
  "Text:Inactive": 4291348680,
  "Button:Normal": 4284308829,
  "Light:Normal": 4284572001,
  "Link:Active": 4278190318,
  "Background:Active": 4282664004,
  "HighlightedText:Disabled": 4294967295,
  "Shadow:Disabled": 4278190080,
  "ToolTipText:Disabled": 4278190080,
  "Foreground:Normal": 4290493371,
  "LinkVisited:Disabled": 4283570315,
  "Shadow:Normal": 4278190080,
  "Foreground:Inactive": 4290493371
}

================================================
FILE: src/pyvfx_boilerplate/utils/__init__.py
================================================


================================================
FILE: src/pyvfx_boilerplate/utils/get_set_palette_data_qt4.py
================================================
"""Set and get QPalette data from Maya

# Example: fetch palette data from Maya
data = getPaletteInfo()
print data
write_json(data)

# Example: read palette JSON file and set palette
data = read_json()
print data
setPaletteFromDict(data)
setStylePlastique()
setMayaTweaks()
"""

import json

from PySide import QtGui

STYLE = "plastique"
GROUPS = ["Disabled", "Active", "Inactive", "Normal"]
ROLES = [
    "AlternateBase",
    "Background",
    "Base",
    "Button",
    "ButtonText",
    "BrightText",
    "Dark",
    "Foreground",
    "Highlight",
    "HighlightedText",
    "Light",
    "Link",
    "LinkVisited",
    "Mid",
    "Midlight",
    "Shadow",
    "ToolTipBase",
    "ToolTipText",
    "Text",
    "Window",
    "WindowText",
]


def getPaletteInfo():
    palette = QtGui.QApplication.palette()

    # ColorGroups
    groups = []
    for name in dir(QtGui.QPalette):
        curr_pallet = getattr(QtGui.QPalette, name)
        if isinstance(curr_pallet, QtGui.QPalette.ColorGroup):
            if name != "All" and name != "NColorGroups" and name != "Current":
                print("ColorGroup: {}".format(name))
                groups.append(name)
    # ColorRoles
    roles = []
    for name in dir(QtGui.QPalette):
        if isinstance(getattr(QtGui.QPalette, name), QtGui.QPalette.ColorRole):
            if name != "NColorRoles" and name != "NoRole":
                print("ColorGroup: {}".format(name))
                roles.append(name)

    # build a dict with all the colors
    result = {}
    for role in ROLES:

        for group in GROUPS:
            qGrp = getattr(QtGui.QPalette, group)
            qRl = getattr(QtGui.QPalette, role)
            result["%s:%s" % (role, group)] = palette.color(qGrp, qRl).rgba()
    return result


def setPaletteFromDict(dct):
    palette = QtGui.QPalette()
    for role in ROLES:
        for group in GROUPS:
            color = QtGui.QColor(dct["%s:%s" % (role, group)])
            qGrp = getattr(QtGui.QPalette, group)
            qRl = getattr(QtGui.QPalette, role)
            palette.setColor(qGrp, qRl, color)
    QtGui.QApplication.setPalette(palette)


def setStylePlastique():
    QtGui.QApplication.setStyle(STYLE)


def setMayaTweaks():
    base_palette = QtGui.QApplication.palette()

    # Set custom colors
    LIGHT_COLOR = QtGui.QColor(100, 100, 100)
    MID_COLOR = QtGui.QColor(68, 68, 68)

    # Create a new palette
    tab_palette = QtGui.QPalette(base_palette)
    tab_palette.setBrush(QtGui.QPalette.Window, QtGui.QBrush(LIGHT_COLOR))
    tab_palette.setBrush(QtGui.QPalette.Button, QtGui.QBrush(MID_COLOR))

    # Define the widgets that needs tweaking
    widget_palettes = {}
    widget_palettes["QTabBar"] = tab_palette
    widget_palettes["QTabWidget"] = tab_palette

    # Set the new tweaked palette
    for name, palette in widget_palettes.items():
        QtGui.QApplication.setPalette(palette, name)


def write_json(data):
    with open("/Users/fredrik/Desktop/qpalette.json", "w") as outfile:
        json.dump(data, outfile)


def read_json():
    # read
    with open("/Users/fredrik/Desktop/qpalette.json", "r") as handle:
        data = json.load(handle)
    return data


================================================
FILE: src/pyvfx_boilerplate/utils/get_set_palette_data_qt5.py
================================================
"""Set and get QPalette data from Maya

# Example: fetch palette data from Maya
data = getPaletteInfo()
print data
write_json(data)

# Example: read palette JSON file and set palette
data = read_json()
print data
setPaletteFromDict(data)
setStylePlastique()
setMayaTweaks()
"""

import json

from PySide2 import QtGui

STYLE = "plastique"
GROUPS = ["Disabled", "Active", "Inactive", "Normal"]
ROLES = [
    "AlternateBase",
    "Background",
    "Base",
    "Button",
    "ButtonText",
    "BrightText",
    "Dark",
    "Foreground",
    "Highlight",
    "HighlightedText",
    "Light",
    "Link",
    "LinkVisited",
    "Mid",
    "Midlight",
    "Shadow",
    "ToolTipBase",
    "ToolTipText",
    "Text",
    "Window",
    "WindowText",
]


def getPaletteInfo():
    palette = QtGui.QGuiApplication.palette()

    # ColorGroups
    groups = []
    for name in dir(QtGui.QPalette):
        curr_pallet = getattr(QtGui.QPalette, name)
        if isinstance(curr_pallet, QtGui.QPalette.ColorGroup):
            if name != "All" and name != "NColorGroups" and name != "Current":
                print("ColorGroup: {}".format(name))
                groups.append(name)
    # ColorRoles
    roles = []
    for name in dir(QtGui.QPalette):
        if isinstance(getattr(QtGui.QPalette, name), QtGui.QPalette.ColorRole):
            if name != "NColorRoles" and name != "NoRole":
                print("ColorGroup: {}".format(name))
                roles.append(name)

    # build a dict with all the colors
    result = {}
    for role in ROLES:

        for group in GROUPS:
            qGrp = getattr(QtGui.QPalette, group)
            qRl = getattr(QtGui.QPalette, role)
            result["%s:%s" % (role, group)] = palette.color(qGrp, qRl).rgba()
    return result


def setPaletteFromDict(dct):
    palette = QtGui.QPalette()
    for role in ROLES:
        for group in GROUPS:
            color = QtGui.QColor(dct["%s:%s" % (role, group)])
            qGrp = getattr(QtGui.QPalette, group)
            qRl = getattr(QtGui.QPalette, role)
            palette.setColor(qGrp, qRl, color)
    QtGui.QApplication.setPalette(palette)


def setStylePlastique():
    QtGui.QApplication.setStyle(STYLE)


def setMayaTweaks():
    base_palette = QtGui.QApplication.palette()

    # Set custom colors
    LIGHT_COLOR = QtGui.QColor(100, 100, 100)
    MID_COLOR = QtGui.QColor(68, 68, 68)

    # Create a new palette
    tab_palette = QtGui.QPalette(base_palette)
    tab_palette.setBrush(QtGui.QPalette.Window, QtGui.QBrush(LIGHT_COLOR))
    tab_palette.setBrush(QtGui.QPalette.Button, QtGui.QBrush(MID_COLOR))

    # Define the widgets that needs tweaking
    widget_palettes = {}
    widget_palettes["QTabBar"] = tab_palette
    widget_palettes["QTabWidget"] = tab_palette

    # Set the new tweaked palette
    for name, palette in widget_palettes.items():
        QtGui.QApplication.setPalette(palette, name)


def write_json(data):
    with open("/Users/fredrik/Desktop/qpalette.json", "w") as outfile:
        json.dump(data, outfile)


def read_json():
    # read
    with open("/Users/fredrik/Desktop/qpalette.json", "r") as handle:
        data = json.load(handle)
    return data
Download .txt
gitextract_ynxoaokt/

├── .github/
│   └── workflows/
│       ├── pypi.yml
│       └── tests.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── setup.cfg
├── setup.py
└── src/
    └── pyvfx_boilerplate/
        ├── __init__.py
        ├── boilerplate_ui.py
        ├── cli.py
        ├── init.py
        ├── mayapalette.py
        ├── menu.py
        ├── resources/
        │   ├── main_window.ui
        │   ├── module.ui
        │   ├── qpalette_maya2015.json
        │   └── qpalette_maya2016.json
        └── utils/
            ├── __init__.py
            ├── get_set_palette_data_qt4.py
            └── get_set_palette_data_qt5.py
Download .txt
SYMBOL INDEX (57 symbols across 6 files)

FILE: src/pyvfx_boilerplate/boilerplate_ui.py
  class _Boilerplate (line 80) | class _Boilerplate(QtWidgets.QMainWindow):
    method __init__ (line 85) | def __init__(self, parent=None, win_title=WTITLE, win_object=WOBJ):
    method setupUi (line 104) | def setupUi(self):
    method say_hello (line 132) | def say_hello(self):
  class Boilerplate (line 142) | class Boilerplate(MayaQWidgetDockableMixin, _Boilerplate):
    method __init__ (line 147) | def __init__(self, parent=None, win_title=WTITLE, win_object=WOBJ):
    method __init__ (line 164) | def __init__(self, parent=None, win_title=WTITLE, win_object=WOBJ):
  class Boilerplate (line 159) | class Boilerplate(_Boilerplate):
    method __init__ (line 147) | def __init__(self, parent=None, win_title=WTITLE, win_object=WOBJ):
    method __init__ (line 164) | def __init__(self, parent=None, win_title=WTITLE, win_object=WOBJ):
  function _maya_delete_ui (line 173) | def _maya_delete_ui(window_title, window_object):
  function _maya_delete_workspace (line 181) | def _maya_delete_workspace(window_object):
  function _maya_update_workspace (line 189) | def _maya_update_workspace(window_object):
  function _maya_main_window (line 207) | def _maya_main_window():
  function _nuke_delete_ui (line 215) | def _nuke_delete_ui(window_object):
  function _nuke_main_window (line 222) | def _nuke_main_window():
  function _nuke_set_zero_margins (line 234) | def _nuke_set_zero_margins(widget_object):
  function _houdini_main_window (line 263) | def _houdini_main_window():
  function _3dsmax_main_window (line 268) | def _3dsmax_main_window():
  class BoilerplateRunner (line 276) | class BoilerplateRunner:
    method __init__ (line 277) | def __init__(self, guiClass=Boilerplate, win_title=WTITLE, win_object=...
    method run_maya (line 284) | def run_maya(self, **kwargs):
    method run_nuke (line 305) | def run_nuke(self, **kwargs):
    method run_houdini (line 339) | def run_houdini(self, **kwargs):
    method run_3dsmax (line 346) | def run_3dsmax(self, **kwargs):
    method on_exit_unreal (line 356) | def on_exit_unreal(self):
    method run_unreal (line 362) | def run_unreal(self, **kwargs):
    method on_exit_blender (line 384) | def on_exit_blender(self):
    method on_update_blender (line 396) | def on_update_blender(self):
    method run_blender (line 403) | def run_blender(self, **kwargs):
    method run_standalone (line 429) | def run_standalone(self, **kwargs):
    method run_main (line 457) | def run_main(self, **kwargs):

FILE: src/pyvfx_boilerplate/cli.py
  function main (line 13) | def main():

FILE: src/pyvfx_boilerplate/mayapalette.py
  function set_palette_from_dict (line 6) | def set_palette_from_dict(dct):
  function set_style (line 48) | def set_style():
  function set_maya_tweaks (line 57) | def set_maya_tweaks():
  function read_json (line 80) | def read_json(filepath):
  function set_maya_palette_with_tweaks (line 87) | def set_maya_palette_with_tweaks(palette_filepath):

FILE: src/pyvfx_boilerplate/menu.py
  function activate (line 50) | def activate(dockable=False):
  function activate_dockable (line 101) | def activate_dockable():
  class PyvfxBoilerplateActivateOperator (line 123) | class PyvfxBoilerplateActivateOperator(bpy.types.Operator):
    method execute (line 129) | def execute(self, context):
  class TOPBAR_MT_pyvfx_menu (line 133) | class TOPBAR_MT_pyvfx_menu(bpy.types.Menu):
    method draw (line 138) | def draw(self, context):
    method menu_draw (line 143) | def menu_draw(self, context):
  function register (line 147) | def register():
  function unregister (line 152) | def unregister():

FILE: src/pyvfx_boilerplate/utils/get_set_palette_data_qt4.py
  function getPaletteInfo (line 47) | def getPaletteInfo():
  function setPaletteFromDict (line 77) | def setPaletteFromDict(dct):
  function setStylePlastique (line 88) | def setStylePlastique():
  function setMayaTweaks (line 92) | def setMayaTweaks():
  function write_json (line 114) | def write_json(data):
  function read_json (line 119) | def read_json():

FILE: src/pyvfx_boilerplate/utils/get_set_palette_data_qt5.py
  function getPaletteInfo (line 47) | def getPaletteInfo():
  function setPaletteFromDict (line 77) | def setPaletteFromDict(dct):
  function setStylePlastique (line 88) | def setStylePlastique():
  function setMayaTweaks (line 92) | def setMayaTweaks():
  function write_json (line 114) | def write_json(data):
  function read_json (line 119) | def read_json():
Condensed preview — 21 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (50K chars).
[
  {
    "path": ".github/workflows/pypi.yml",
    "chars": 802,
    "preview": "# https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions\n# https://packaging."
  },
  {
    "path": ".github/workflows/tests.yml",
    "chars": 827,
    "preview": "# https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions\n\nname: Tests\n\non: [p"
  },
  {
    "path": ".gitignore",
    "chars": 61,
    "preview": "# Python\n*.pyc\n\n# Folders\n.eggs\n*.egg-info/\nbuild\ndist\nvenv\n\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 2470,
    "preview": "# Changelog\n\n### Version 3.3.0\n\n- adding Blender, 3DS Max, Houdini, and Unreal Engine support\n- see below for installing"
  },
  {
    "path": "LICENSE",
    "chars": 1083,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2016 Fredrik Averpil\n\nPermission is hereby granted, free of charge, to any person o"
  },
  {
    "path": "README.md",
    "chars": 2993,
    "preview": "# pyvfx-boilerplate\n\n![Tests](https://github.com/fredrikaverpil/pyvfx-boilerplate/workflows/Tests/badge.svg) ![PyPI](htt"
  },
  {
    "path": "setup.cfg",
    "chars": 25,
    "preview": "[bdist_wheel]\nuniversal=1"
  },
  {
    "path": "setup.py",
    "chars": 1106,
    "preview": "# https://docs.python.org/3/distutils/setupscript.html#additional-meta-data\n\nimport setuptools\n\nwith open(\"README.md\", \""
  },
  {
    "path": "src/pyvfx_boilerplate/__init__.py",
    "chars": 65,
    "preview": "__path__ = __import__(\"pkgutil\").extend_path(__path__, __name__)\n"
  },
  {
    "path": "src/pyvfx_boilerplate/boilerplate_ui.py",
    "chars": 15693,
    "preview": "\"\"\"This uses a Qt binding of \"any\" kind, thanks to the Qt.py module,\nto produce an UI. First, one .ui file is loaded and"
  },
  {
    "path": "src/pyvfx_boilerplate/cli.py",
    "chars": 400,
    "preview": "#!/usr/bin/env python\nimport os\nimport sys\n\n# this is required to keep pyside2 seperate from maya and nuke's pyside2\n# m"
  },
  {
    "path": "src/pyvfx_boilerplate/init.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/pyvfx_boilerplate/mayapalette.py",
    "chars": 2602,
    "preview": "import json\n\nfrom Qt import QtGui, QtWidgets\n\n\ndef set_palette_from_dict(dct):\n    \"\"\"Set palette to current QApplicatio"
  },
  {
    "path": "src/pyvfx_boilerplate/menu.py",
    "chars": 4058,
    "preview": "import pyvfx_boilerplate.boilerplate_ui as bpui\n\ntry:\n    import maya.cmds as cmds\n    import maya.mel as mel\n\n    MAYA "
  },
  {
    "path": "src/pyvfx_boilerplate/resources/main_window.ui",
    "chars": 877,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>MainWindow</class>\n <widget class=\"QMainWindow\" name=\""
  },
  {
    "path": "src/pyvfx_boilerplate/resources/module.ui",
    "chars": 974,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>Form</class>\n <widget class=\"QWidget\" name=\"Form\">\n  <"
  },
  {
    "path": "src/pyvfx_boilerplate/resources/qpalette_maya2015.json",
    "chars": 2866,
    "preview": "{\n  \"Foreground:Active\": 4291348680,\n  \"HighlightedText:Active\": 4294967295,\n  \"Mid:Active\": 4281150765,\n  \"WindowText:N"
  },
  {
    "path": "src/pyvfx_boilerplate/resources/qpalette_maya2016.json",
    "chars": 2866,
    "preview": "{\n  \"Foreground:Active\": 4290493371,\n  \"HighlightedText:Active\": 4294967295,\n  \"Mid:Active\": 4281150765,\n  \"WindowText:N"
  },
  {
    "path": "src/pyvfx_boilerplate/utils/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/pyvfx_boilerplate/utils/get_set_palette_data_qt4.py",
    "chars": 3302,
    "preview": "\"\"\"Set and get QPalette data from Maya\r\n\r\n# Example: fetch palette data from Maya\r\ndata = getPaletteInfo()\r\nprint data\r\n"
  },
  {
    "path": "src/pyvfx_boilerplate/utils/get_set_palette_data_qt5.py",
    "chars": 3306,
    "preview": "\"\"\"Set and get QPalette data from Maya\r\n\r\n# Example: fetch palette data from Maya\r\ndata = getPaletteInfo()\r\nprint data\r\n"
  }
]

About this extraction

This page contains the full source code of the fredrikaverpil/pyvfx-boilerplate GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 21 files (45.3 KB), approximately 12.7k tokens, and a symbol index with 57 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!