Integrating up through commit 90f050496
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
|
||||
import os
|
||||
|
||||
from azpy import env_bool
|
||||
from azpy.env_bool import env_bool
|
||||
from azpy.constants import ENVAR_DCCSI_GDEBUG
|
||||
from azpy.constants import ENVAR_DCCSI_DEV_MODE
|
||||
|
||||
|
||||
@@ -67,19 +67,12 @@ _DCCSI_DEV_MODE = env_bool.env_bool(constants.ENVAR_DCCSI_DEV_MODE, False)
|
||||
|
||||
# for py2.7 (Maya) we provide this, so we must assume some bootstrapping
|
||||
# has occured, see DccScriptingInterface\\config.py (_DCCSI_PYTHON_LIB_PATH)
|
||||
if sys.version_info.major >= 3:
|
||||
import pathlib
|
||||
else: # py2.x
|
||||
import pathlib2 as pathlib # python 2 backport
|
||||
# its mkdir() function supposedly supports exist_ok
|
||||
# ^ but in practice still seems to bark
|
||||
# TypeError: mkdir() got an unexpected keyword argument 'exist_ok'
|
||||
|
||||
|
||||
import pathlib
|
||||
from pathlib import Path
|
||||
if _G_DEBUG:
|
||||
print('DCCsi debug breadcrumb, pathlib is: {}'.format(pathlib))
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
# to be continued...
|
||||
|
||||
# get/set the project name
|
||||
@@ -95,7 +88,7 @@ _LY_PROJECT_TAG = os.getenv(constants.ENVAR_LY_PROJECT,
|
||||
_DCCSI_LOG_PATH = Path(os.getenv(constants.ENVAR_DCCSI_LOG_PATH,
|
||||
Path(_LY_DEV,
|
||||
_LY_PROJECT_TAG,
|
||||
'Cache',
|
||||
'Cache',
|
||||
'pc', 'user', 'log', 'logs')))
|
||||
|
||||
|
||||
@@ -105,7 +98,7 @@ for handler in _logging.root.handlers[:]:
|
||||
# very basic root logger for early debugging, flip to while 1:
|
||||
while 0:
|
||||
_logging.basicConfig(level=_logging.DEBUG,
|
||||
format=constants.FRMT_LOG_LONG,
|
||||
format=constants.FRMT_LOG_LONG,
|
||||
datefmt='%m-%d %H:%M')
|
||||
|
||||
_logging.debug('azpy.rootlogger> root logger set up for debugging') # root logger
|
||||
@@ -190,7 +183,7 @@ def initialize_logger(name,
|
||||
_logger.debug("Folder is already there")
|
||||
else:
|
||||
_logger.debug("Folder was created")
|
||||
|
||||
|
||||
_log_filepath = Path(_DCCSI_LOG_PATH, '{}.log'.format(name))
|
||||
try:
|
||||
_log_filepath.touch(mode=0o666, exist_ok=True)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
import os
|
||||
|
||||
from azpy import env_bool
|
||||
from azpy.env_bool import env_bool
|
||||
from azpy.constants import ENVAR_DCCSI_GDEBUG
|
||||
from azpy.constants import ENVAR_DCCSI_DEV_MODE
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ _LOGGER = _logging.getLogger(_PACKAGENAME)
|
||||
_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME}))
|
||||
|
||||
__all__ = ['get_os', 'return_stub', 'get_stub_check_path',
|
||||
'get_dccsi_config']
|
||||
'get_dccsi_config', 'get_current_project']
|
||||
|
||||
# note: this module should reamin py2.7 compatible (Maya) so no f'strings
|
||||
# -------------------------------------------------------------------------
|
||||
@@ -49,7 +49,7 @@ def get_os():
|
||||
message = str("DCCsi.azpy.config_utils.py: "
|
||||
"Unexpectedly executing on operating system '{}'"
|
||||
"".format(sys.platform))
|
||||
|
||||
|
||||
raise RuntimeError(message)
|
||||
return os_folder
|
||||
# -------------------------------------------------------------------------
|
||||
@@ -72,7 +72,7 @@ def return_stub_dir(stub_file='dccsi_stub'):
|
||||
'({}) in a walk-up from currnet path'
|
||||
''.format(stub_file))
|
||||
break
|
||||
|
||||
|
||||
_dir_to_last_file = path
|
||||
|
||||
return _dir_to_last_file
|
||||
@@ -109,7 +109,7 @@ def get_stub_check_path(in_path=os.getcwd(), check_stub='engineroot.txt'):
|
||||
# settings.setenv() # doing this will add the additional DYNACONF_ envars
|
||||
def get_dccsi_config(dccsi_dirpath=return_stub_dir()):
|
||||
"""Convenience method to set and retreive settings directly from module."""
|
||||
|
||||
|
||||
# we can go ahead and just make sure the the DCCsi env is set
|
||||
# config is SO generic this ensures we are importing a specific one
|
||||
_module_tag = "dccsi.config"
|
||||
@@ -123,18 +123,36 @@ def get_dccsi_config(dccsi_dirpath=return_stub_dir()):
|
||||
str(_dccsi_path.resolve()))
|
||||
_dccsi_config = importlib.util.module_from_spec(_spec_dccsi_config)
|
||||
_spec_dccsi_config.loader.exec_module(_dccsi_config)
|
||||
|
||||
|
||||
_LOGGER.debug('Executed config: {}'.format(_spec_dccsi_config))
|
||||
else: # py2.x
|
||||
import imp
|
||||
_dccsi_config = imp.load_source(_module_tag, str(_dccsi_path.resolve()))
|
||||
_LOGGER.debug('Imported config: {}'.format(_spec_dccsi_config))
|
||||
return _dccsi_config
|
||||
|
||||
|
||||
else:
|
||||
return None
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def get_current_project(dev_folder=get_stub_check_path()):
|
||||
"""Uses regex in lumberyard Dev\\bootstrap.cfg to retreive project tag str"""
|
||||
boostrap_filepath = Path(dev_folder, "bootstrap.cfg")
|
||||
if boostrap_filepath.exists():
|
||||
bootstrap = open(str(boostrap_filepath), "r")
|
||||
regex_str = r"^project_path\s*=\s*(.*)"
|
||||
game_project_regex = re.compile(regex_str)
|
||||
for line in bootstrap:
|
||||
game_folder_match = game_project_regex.match(line)
|
||||
if game_folder_match:
|
||||
_LOGGER.debug('Project is: {}'.format(game_folder_match.group(1)))
|
||||
return game_folder_match.group(1)
|
||||
return None
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def bootstrap_dccsi_py_libs(dccsi_dirpath=return_stub_dir()):
|
||||
"""Builds and adds local site dir libs based on py version"""
|
||||
@@ -166,16 +184,18 @@ if __name__ == '__main__':
|
||||
_LOGGER.info("# {0} #".format('-' * 72))
|
||||
|
||||
_LOGGER.info('Current Work dir: {0}'.format(os.getcwd()))
|
||||
|
||||
|
||||
_LOGGER.info('OS: {}'.format(get_os()))
|
||||
|
||||
_LOGGER.info('DCCSIG_PATH: {}'.format(return_stub_dir('dccsi_stub')))
|
||||
|
||||
|
||||
_config = get_dccsi_config()
|
||||
_LOGGER.info('DCCSI_CONFIG_PATH: {}'.format(_config))
|
||||
|
||||
_LOGGER.info('LY_DEV: {}'.format(get_stub_check_path('engineroot.txt')))
|
||||
|
||||
_LOGGER.info('LY_PROJECT: {}'.format(get_current_project(get_stub_check_path('engineroot.txt'))))
|
||||
|
||||
_LOGGER.info('DCCSI_PYTHON_LIB_PATH: {}'.format(bootstrap_dccsi_py_libs(return_stub_dir('dccsi_stub'))))
|
||||
|
||||
# custom prompt
|
||||
|
||||
@@ -27,7 +27,7 @@ import os
|
||||
import sys
|
||||
import site
|
||||
import logging as _logging
|
||||
|
||||
|
||||
# for this module to perform standalone
|
||||
# we need to set up basic access to the DCCsi
|
||||
_MODULE_PATH = os.path.realpath(__file__) # To Do: what if frozen?
|
||||
@@ -56,12 +56,15 @@ FRMT_LOG_SHRT = "[%(asctime)s][%(name)s][%(levelname)s] >> %(message)s"
|
||||
_G_DEBUG = env_bool.env_bool(ENVAR_DCCSI_GDEBUG, False)
|
||||
_DCCSI_DEV_MODE = env_bool.env_bool(ENVAR_DCCSI_DEV_MODE, False)
|
||||
|
||||
for handler in _logging.root.handlers[:]:
|
||||
_logging.root.removeHandler(handler)
|
||||
|
||||
_PACKAGENAME = 'azpy.constants'
|
||||
|
||||
_log_level = int(20)
|
||||
_LOG_LEVEL = int(20)
|
||||
if _G_DEBUG:
|
||||
_log_level = int(10)
|
||||
_logging.basicConfig(level=_log_level,
|
||||
_LOG_LEVEL = int(10)
|
||||
_logging.basicConfig(level=_LOG_LEVEL,
|
||||
format=FRMT_LOG_LONG,
|
||||
datefmt='%m-%d %H:%M')
|
||||
_LOGGER = _logging.getLogger(_PACKAGENAME)
|
||||
@@ -85,7 +88,7 @@ TAG_MOCK_PROJECT = str('MockProject')
|
||||
TAG_DIR_LY_DEV = str('dev')
|
||||
TAG_DIR_DCCSI_AZPY = str('azpy')
|
||||
TAG_DIR_DCCSI_SDK = str('SDK')
|
||||
TAG_DIR_LY_BUILD = str('windows_vs2019')
|
||||
TAG_DIR_LY_BUILD = str('build')
|
||||
TAG_QT_PLUGIN_PATH = str('QT_PLUGIN_PATH')
|
||||
|
||||
# filesystem markers, stub file names.
|
||||
@@ -255,7 +258,7 @@ PATH_DEFAULT_WINGHOME = str('{0}{1}{2}.{3}'
|
||||
TAG_DEFAULT_WING_MINOR_VER))
|
||||
|
||||
PATH_SAT_INSTALL_PATH = str('{0}\\{1}\\{2}\\{3}\\{4}'
|
||||
''.format(PATH_PROGRAMFILES_X64,
|
||||
''.format(PATH_PROGRAMFILES_X64,
|
||||
'Allegorithmic',
|
||||
'Substance Automation Toolkit',
|
||||
'Python API',
|
||||
@@ -273,7 +276,7 @@ if __name__ == '__main__':
|
||||
_G_DEBUG = True
|
||||
_DCCSI_DEV_MODE = True
|
||||
_LOGGER.setLevel(_logging.DEBUG) # force debugging
|
||||
|
||||
|
||||
# this is a top level module and to reduce cyclical azpy imports
|
||||
# it only has a basic logger configured, add log to console
|
||||
_handler = _logging.StreamHandler(sys.stdout)
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
# coding:utf-8
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
# -- This line is 75 characters -------------------------------------------
|
||||
"""azpy.dev.ide.__init__"""
|
||||
|
||||
from azpy.env_bool import env_bool
|
||||
from azpy.constants import ENVAR_DCCSI_GDEBUG
|
||||
from azpy.constants import ENVAR_DCCSI_DEV_MODE
|
||||
|
||||
# global space
|
||||
_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False)
|
||||
_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False)
|
||||
|
||||
_PACKAGENAME = 'azpy.dev.ide'
|
||||
|
||||
from azpy import initialize_logger
|
||||
_LOGGER = initialize_logger(_PACKAGENAME)
|
||||
_LOGGER.debug('Invoking __init__.py for {0}.'.format({_PACKAGENAME}))
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
__all__ = []
|
||||
|
||||
try:
|
||||
import wingapi
|
||||
__all__ = init_wing(__all__)
|
||||
except:
|
||||
pass
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def init_wing(_all):
|
||||
"""If the wingapi is required for a package/module to import,
|
||||
then it should be initialized and added here so general imports
|
||||
don't fail"""
|
||||
|
||||
# Make sure we can import the native apis
|
||||
import wingapi # this will fail if we can't
|
||||
|
||||
_all.append('wing')
|
||||
# add others
|
||||
|
||||
# Importing additional local packages/modules
|
||||
return _all
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def init_all(_all):
|
||||
"""If the wingapi is required for a package/module to import,
|
||||
then it should be initialized and added here so general imports
|
||||
don't fail"""
|
||||
|
||||
# Make sure we can import the native apis
|
||||
import wingapi # this will fail if we can't
|
||||
|
||||
_all.append('wing')
|
||||
# add others
|
||||
|
||||
# Importing additional local packages/modules
|
||||
return _all
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def import_all(_all=__all__):
|
||||
"""this will test imports of __all__
|
||||
can be run before or after init() to test"""
|
||||
from azpy import test_imports
|
||||
_LOGGER.debug('Testing Imports from {0}'.format(_PACKAGENAME))
|
||||
test_imports(_all,
|
||||
_pkg=_PACKAGENAME,
|
||||
_logger=_LOGGER)
|
||||
return _all
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
if _DCCSI_DEV_MODE:
|
||||
# If in dev mode this will test imports of __all__
|
||||
import_all(__all__)
|
||||
# -------------------------------------------------------------------------
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
# coding:utf-8
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
# -- This line is 75 characters -------------------------------------------
|
||||
# send each line to maya with > send_py_cmd_to_maya
|
||||
print('Hello World: Command received from WingIDE')
|
||||
import maya.cmds as cmds
|
||||
foo = cmds.polySphere(n='DemoSphere', radius=1.0)
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# more complex example
|
||||
import maya.cmds as cmds
|
||||
import random
|
||||
import time
|
||||
|
||||
name = 'DemoCube'
|
||||
size = random.uniform(0.5, 2.0)
|
||||
variation = random.uniform(1.5, 5.0)
|
||||
amount = random.randint(9, 21)
|
||||
|
||||
# remove previous
|
||||
obj_list = cmds.ls('{}*'.format(name))
|
||||
if len(obj_list) > 0:
|
||||
cmds.delete(obj_list)
|
||||
|
||||
for i in range(0, amount - 1):
|
||||
|
||||
depth_rand = random.uniform(size, size * variation)
|
||||
|
||||
tegel = cmds.polyCube(name='{}#'.format(name),
|
||||
w=size, h=size, d=depth_rand)
|
||||
cmds.move(size * i, 0, 5)
|
||||
i += 1
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
import maya.cmds as cmds
|
||||
import random
|
||||
import time
|
||||
|
||||
name = 'DemoCube'
|
||||
size = random.uniform(0.5, 2.0)
|
||||
variation = random.uniform(1.5, 5.0)
|
||||
amount = random.randint(9, 21)
|
||||
|
||||
def make_some_wonky_cubes(name=, size, variation, amount):
|
||||
# remove previous
|
||||
obj_list = cmds.ls('{}*'.format(name))
|
||||
if len(obj_list) > 0:
|
||||
cmds.delete(obj_list)
|
||||
|
||||
for i in range(0, amount - 1):
|
||||
|
||||
depth_rand = random.uniform(size, size * variation)
|
||||
|
||||
tegel = cmds.polyCube(name='{}#'.format(name),
|
||||
w=size, h=size, d=depth_rand)
|
||||
cmds.move(size * i, 0, 5)
|
||||
i += 1
|
||||
return
|
||||
|
||||
foo = make_some_wonky_cubes()
|
||||
time.sleep(10)
|
||||
foo = make_some_wonky_cubes()
|
||||
|
||||
while 1:
|
||||
foo = make_some_wonky_cubes()
|
||||
time.sleep(5)
|
||||
foo = make_some_wonky_cubes(size=0.5, amount=20)
|
||||
time.sleep(5)
|
||||
break
|
||||
@@ -0,0 +1,2 @@
|
||||
*.pyo
|
||||
*.pyc
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
# coding:utf-8
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
# -- This line is 75 characters -------------------------------------------
|
||||
"""azpy.dev.ide.wing.__init__"""
|
||||
|
||||
__all__ = ['hot_keys', 'test']
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+355
@@ -0,0 +1,355 @@
|
||||
# coding:utf-8
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
# inspiration: http://www.emeraldartist.com/blog/2012/10/11/remotely-sending-code-to-maya-from-wing
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
""" Module to remotely send code to maya. Inside of WingIDE prefs, you will
|
||||
need to add the parent dire of this module to 'IDE Extension Scripting>Search Path'
|
||||
Additionally, you will need set up custom key bindings 'User Interface>Keyboard'
|
||||
|
||||
"""
|
||||
# -- This line is 75 characters -------------------------------------------
|
||||
|
||||
# standard imports
|
||||
import socket
|
||||
import random
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import logging as _logging
|
||||
|
||||
# wing ide
|
||||
import wingapi
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
MODULENAME = 'azpy.dev.ide.wing.hot_keys'
|
||||
_LOGGER = _logging.getLogger(MODULENAME)
|
||||
|
||||
## extend logger
|
||||
#_handler = _logging.StreamHandler(sys.stdout)
|
||||
#_handler.setLevel(_logging.DEBUG)
|
||||
#FRMT_LOG_LONG = "[%(name)s][%(levelname)s] >> %(message)s (%(asctime)s; %(filename)s:%(lineno)d)"
|
||||
#_formatter = _logging.Formatter(FRMT_LOG_LONG)
|
||||
#_handler.setFormatter(_formatter)
|
||||
#_LOGGER.addHandler(_handler)
|
||||
#_LOGGER.debug('Loading: {0}.'.format({MODULENAME}))
|
||||
|
||||
_LOCAL_HOST = socket.gethostbyname(socket.gethostname())
|
||||
_LOGGER.info('local_host: {}'.format(_LOCAL_HOST))
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def display_text(test_str):
|
||||
"""Displays text in a WingIDE pop-up dialog"""
|
||||
app = wingapi.gApplication
|
||||
v = "Product info is: " + str(app.GetProductInfo())
|
||||
v += "\nAnd you typed: %s" % test_str
|
||||
wingapi.gApplication.ShowMessageDialog("Test Message", v)
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def get_wing_text(): # no hotkey
|
||||
"""
|
||||
Return the text currently selected in wing
|
||||
"""
|
||||
editor = wingapi.gApplication.GetActiveEditor()
|
||||
if editor is None:
|
||||
return None
|
||||
else:
|
||||
current_doc = editor.GetDocument()
|
||||
start, end = editor.GetSelection()
|
||||
text_block = current_doc.GetCharRange(start, end)
|
||||
_LOGGER.debug('selected text is: {}'.format(text_block))
|
||||
return text_block
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def display_wing_text(): # Ctrl+Shift+D
|
||||
text_block = get_wing_text()
|
||||
display_text(text_block)
|
||||
return text_block
|
||||
|
||||
display_wing_text.contexts = [
|
||||
wingapi.kContextNewMenu("DCCsi Scripts"),
|
||||
wingapi.kContextEditor(),
|
||||
]
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def get_stub_check_path(in_path=__file__, check_stub='engineroot.txt'):
|
||||
'''
|
||||
Returns the branch root directory of the dev\\'engineroot.txt'
|
||||
(... or you can pass it another known stub)
|
||||
|
||||
so we can safely build relative filepaths within that branch.
|
||||
|
||||
If the stub is not found, it returns None
|
||||
'''
|
||||
path = os.path.abspath(os.path.join(os.path.dirname(in_path), ".."))
|
||||
_LOGGER.info('parent dir: {}'.format(path))
|
||||
|
||||
while 1:
|
||||
test_path = os.path.join(path, check_stub)
|
||||
|
||||
if os.path.isfile(test_path):
|
||||
return os.path.abspath(os.path.join(os.path.dirname(test_path)))
|
||||
|
||||
else:
|
||||
path, tail = (os.path.abspath(os.path.join(os.path.dirname(test_path), "..")),
|
||||
os.path.basename(test_path))
|
||||
|
||||
if (len(tail) == 0):
|
||||
return None
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# globals
|
||||
|
||||
_LY_DEV = get_stub_check_path()
|
||||
_LOGGER.info('_LY_DEV: {}'.format(_LY_DEV))
|
||||
|
||||
_PROJ_CACHE = os.path.join(_LY_DEV, 'cache', 'DCCsi', 'wing')
|
||||
_LOGGER.info('_PROJ_CACHE: {}'.format(_PROJ_CACHE))
|
||||
|
||||
if not os.path.exists(_PROJ_CACHE):
|
||||
os.makedirs(_PROJ_CACHE)
|
||||
_LOGGER.info('SUCCESS creating: {}'.format(_PROJ_CACHE))
|
||||
else:
|
||||
_LOGGER.info('_PROJ_CACHE already exists: {}'.format(_PROJ_CACHE))
|
||||
|
||||
# makedirs(_PROJ_CACHE)
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def create_client_socket(local_host=_LOCAL_HOST,command_port=6000):
|
||||
"""create a client (wing) socket connection to maya (server, commandPort)"""
|
||||
for res in socket.getaddrinfo(local_host, command_port,
|
||||
socket.AF_UNSPEC, socket.SOCK_STREAM,0, socket.AI_PASSIVE):
|
||||
af, socktype, proto, canonname, sa = res
|
||||
try:
|
||||
mSocket = socket.socket(af, socktype, proto)
|
||||
except socket.error as e:
|
||||
mSocket = None
|
||||
continue
|
||||
try:
|
||||
# Make our socket --> Maya connection:
|
||||
mSocket.connect(sa)
|
||||
except socket.error as e:
|
||||
mSocket.close()
|
||||
mSocket = None
|
||||
continue
|
||||
break
|
||||
|
||||
if not mSocket:
|
||||
raise RuntimeError("Unable to initialise client socket.")
|
||||
|
||||
return mSocket
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def send_selection_to_maya(language='python',
|
||||
local_host=_LOCAL_HOST,
|
||||
command_port=6000):
|
||||
"""Basic method to connect to Maya and send selected code over.
|
||||
chunks can be large, which makes socket programming cumbersome.
|
||||
This module stashes the selection in a temp .txt file in the cache.
|
||||
Then send maya a command for a specific module, which will then
|
||||
read that file and execute the code line-by-line, allowing for
|
||||
arbirtarily large selections that might otherwise overrun buffer"""
|
||||
|
||||
port_name = str('{0}:{1}'.format(local_host, command_port))
|
||||
_LOGGER.info('port_name: {}'.format(port_name))
|
||||
|
||||
if language != "mel" and language != "python":
|
||||
raise ValueError("Expecting either 'mel' or 'python'")
|
||||
|
||||
# Save the text to a temp file.
|
||||
# If mel, make sure it end with a semicolon
|
||||
selected_text = get_wing_text()
|
||||
if language == 'mel':
|
||||
if not selected_text.endswith(';'):
|
||||
selected_text += ';'
|
||||
|
||||
# This saves a temp file on Windows
|
||||
# Mac\Linux support may need updating
|
||||
temp_file_name = 'tmp_wing_data.txt'
|
||||
|
||||
temp_file_path = os.path.join(_PROJ_CACHE, temp_file_name)
|
||||
temp_file_path = os.path.abspath(temp_file_path)
|
||||
temp_file = temp_file_path.replace("\\", "/") # maya is linux paths?
|
||||
_LOGGER.debug('temp_file_path is: {}'.format(temp_file_path))
|
||||
|
||||
if os.access(temp_file, os.F_OK):
|
||||
# open and print the file in Maya:
|
||||
f=open(temp_file_path, "w")
|
||||
f.write(selected_text)
|
||||
f.close()
|
||||
else:
|
||||
_LOGGER.info("No temp file exists: {}".format(temp_file))
|
||||
file=open(temp_file, "w")
|
||||
if os.path.isfile(temp_file):
|
||||
_LOGGER.info('Created the file, please try again')
|
||||
else:
|
||||
_LOGGER.info('File not created')
|
||||
|
||||
# Create the socket that will connect to Maya, Opening a socket can vary from
|
||||
mSocket = create_client_socket(local_host, command_port)
|
||||
|
||||
if mSocket:
|
||||
# Now ping Maya over the command-port
|
||||
message = ("import azpy.maya.utils.execute_wing_code;"
|
||||
"azpy.maya.utils.execute_wing_code.main('{}')".format(language))
|
||||
|
||||
if language == 'mel':
|
||||
message = 'python({})'.format(message) # wrap in in mel python cmd
|
||||
try:
|
||||
# Send our code to Maya:
|
||||
mSocket.send(message.encode('ascii'))
|
||||
time.sleep(1)
|
||||
client_response = str(mSocket.recv(4096)).encode("utf-8") # receive the result info
|
||||
# time.sleep(0.25)
|
||||
# next command
|
||||
except Exception as e:
|
||||
_LOGGER.error("Sending command to Maya failed: {}".format(e))
|
||||
|
||||
_LOGGER.info("salt:{0}:: sent: {1}".format(str(random.randint(1, 9999)), message))
|
||||
_LOGGER.info("The result is: {}".format(client_response))
|
||||
|
||||
mSocket.close()
|
||||
else:
|
||||
_LOGGER.error('No client socket, mSocket is: {}'.format(mSocket))
|
||||
|
||||
return
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def send_command_to_maya(language='python',
|
||||
local_host=_LOCAL_HOST,
|
||||
command_port=6000):
|
||||
"""Basic method to connect to Maya and send a single smaller command directly"""
|
||||
|
||||
port_name = str('{0}:{1}'.format(local_host, command_port))
|
||||
_LOGGER.info('port_name: {}'.format(port_name))
|
||||
|
||||
if language != "mel" and language != "python":
|
||||
raise ValueError("Expecting either 'mel' or 'python'")
|
||||
|
||||
# Save the text to a temp file.
|
||||
# If mel, make sure it end with a semicolon
|
||||
selected_text = get_wing_text()
|
||||
if language == 'mel':
|
||||
if not selected_text.endswith(';'):
|
||||
selected_text += ';'
|
||||
|
||||
# Create the socket that will connect to Maya, Opening a socket can vary from
|
||||
mSocket = create_client_socket(local_host, command_port)
|
||||
_LOGGER.info('mSocket is: {}'.format(mSocket))
|
||||
|
||||
if mSocket:
|
||||
# Now ping Maya over the command-port
|
||||
message = str(selected_text)
|
||||
if language == 'mel':
|
||||
message = 'python({})'.format(message) # wrap in in mel python cmd
|
||||
# Now ping Maya over the command-port
|
||||
try:
|
||||
if language == 'mel':
|
||||
message = 'python({})'.format(message)
|
||||
|
||||
# to do: the buffer default I think is 4096
|
||||
# long selections are going to fail (not sure how)
|
||||
mSocket.send(message.encode('ascii'))
|
||||
time.sleep(1)
|
||||
client_response = str(mSocket.recv(4096)).encode("utf-8") # receive the result info
|
||||
# time.sleep(0.25)
|
||||
# next command
|
||||
except Exception as e:
|
||||
_LOGGER.error("Sending command to Maya failed: {}".format(e))
|
||||
|
||||
_LOGGER.info("salt:{0}:: sent: {1}".format(str(random.randint(1, 9999)), message))
|
||||
_LOGGER.info("The result is: {}".format(client_response))
|
||||
|
||||
mSocket.close()
|
||||
else:
|
||||
_LOGGER.error('No client socket, mSocket is: {}'.format(mSocket))
|
||||
|
||||
return
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def send_py_cmd_to_maya():
|
||||
"""Send the selected Python command to Maya"""
|
||||
send_command_to_maya() # default language is 'python'
|
||||
|
||||
send_py_cmd_to_maya.contexts = [
|
||||
wingapi.kContextNewMenu("DCCsi Scripts"),
|
||||
wingapi.kContextEditor(),
|
||||
]
|
||||
|
||||
def send_mel_cmd_to_maya():
|
||||
"""Send the selected code to Maya as mel"""
|
||||
send_command_to_maya('mel')
|
||||
|
||||
send_mel_cmd_to_maya.contexts = [
|
||||
wingapi.kContextNewMenu("DCCsi Scripts"),
|
||||
wingapi.kContextEditor(),
|
||||
]
|
||||
|
||||
def python_selection_to_maya():
|
||||
"""Send the selected Python code to Maya"""
|
||||
send_selection_to_maya() # default language is 'python'
|
||||
|
||||
python_selection_to_maya.contexts = [
|
||||
wingapi.kContextNewMenu("DCCsi Scripts"),
|
||||
wingapi.kContextEditor(),
|
||||
]
|
||||
|
||||
def mel_selection_to_maya():
|
||||
"""Send the selected code to Maya as mel"""
|
||||
send_selection_to_maya('mel')
|
||||
|
||||
mel_selection_to_maya.contexts = [
|
||||
wingapi.kContextNewMenu("DCCsi Scripts"),
|
||||
wingapi.kContextEditor(),
|
||||
]
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
###########################################################################
|
||||
# Main Code Block, runs this script as main (testing)
|
||||
# -------------------------------------------------------------------------
|
||||
if __name__ == '__main__':
|
||||
# there are not really tests to run here due to this being a list of
|
||||
# constants for shared use.
|
||||
_G_DEBUG = True
|
||||
_DCCSI_DEV_MODE = True
|
||||
_LOGGER.setLevel(_logging.DEBUG) # force debugging
|
||||
|
||||
foo = get_wing_text()
|
||||
|
||||
# send each line to maya with > send_py_cmd_to_maya
|
||||
print('Hello World: Command received from WingIDE')
|
||||
#import maya.cmds as cmds
|
||||
#foo = cmds.polySphere()
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
This particular sub-package is devoted to WingIDE
|
||||
|
||||
It is mainly wingide specific extensions
|
||||
|
||||
Some notes ...
|
||||
|
||||
There are a couple of projects set up for Lumberyard python development with WingIDE
|
||||
|
||||
The FIRST is the DCCsi:
|
||||
dev\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\Solutions\.wing\DCCsi_7x.wpr
|
||||
|
||||
This provides devs a project to directly work on the DCCsi itself
|
||||
|
||||
You can launch wing and directly load this project via the following .bat file:
|
||||
dev\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\Launchers\Windows\Launch_WingIDE-7-1.bat
|
||||
|
||||
Note: the data-driven env hooks for the DCCsi are in the Env.bat:
|
||||
dev\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\Launchers\Windows\Env.bat
|
||||
^ when you launch wing via the .bat file it bootstraps that env first
|
||||
|
||||
Additionally, that same env is being transitioned to a python implementation using dynaconf (WIP):
|
||||
dev\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\.env
|
||||
dev\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\settings.json
|
||||
dev\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\config.py
|
||||
^ this last file is the root dynaconf config for the DCCsi
|
||||
This will also allow us to have per-tool, per-project, per-dcc app env extensions and settings in a more nested way
|
||||
|
||||
The SECOND is the AtomTechArt Lumberyard project: dev\AtomTechArt
|
||||
dev\AtomTechArt\DCCsi\envs\AtomTechArt\AtomTechArt.wpr
|
||||
|
||||
Note: this is set up as a venv based on the lumberyard python intstall
|
||||
and thus provides a sandbox silo to develop outside of the lumberyard python, and outside of the DCCsi
|
||||
|
||||
This .bat will launch wing and directly load this project:
|
||||
dev\AtomTechArt\DCCsi\Launch_AtomTechArt_WingIDE-7-1.bat
|
||||
|
||||
WingIDE auto-complete with Lumberyard:
|
||||
Lumberyard when built will generate .pyi files for source analysis, inspection and auto-complete
|
||||
On a per-project basis they are generated in the cache like:
|
||||
dev\Cache\AtomTechArt\pc\user\python_symbols\azlmbr
|
||||
|
||||
Note: to get this to work unfortunatley each user must configure the wingide prefs to include this path
|
||||
(there is no shared project / data-driven way that I know of to set this up otherwise)
|
||||
|
||||
Note: lumberyard is not currently generating __init__.pyi files in that package structure:
|
||||
https://jira.agscollab.com/browse/SPEC-3315
|
||||
|
||||
The workaround is to create them yourself (they can be empty) and needs to be in the root of each package folder, like this:
|
||||
dev\Cache\AtomTechArt\pc\user\python_symbols\azlmbr\__init__.pyi
|
||||
dev\Cache\AtomTechArt\pc\user\python_symbols\azlmbr\materialeditor\__init__.pyi
|
||||
ETC...
|
||||
|
||||
Then to enable do the following in WingIDE
|
||||
|
||||
1. (Dialog) WingIDE > Edit > Peferences
|
||||
2. (Section) Category > Source Analysis > Advanced
|
||||
3. (Add Path) In the area labeled "Interface File Path", insert a new path and point it to your cache:
|
||||
Example (mine): g:\depot\JG_PC1_spectrAtom\dev\Cache\AtomTechArt\pc\user\python_symbols
|
||||
|
||||
You might need to reboot wing. Then you should have auto-complete for the lumberyard api
|
||||
> import azlmbr
|
||||
|
||||
Note: the entirety of azlmbr api does not generate .pyi files currently,
|
||||
all of the "Behaviour Context" based classes do
|
||||
non-BC modules such as azlmbr.paths currently do not
|
||||
https://jira.agscollab.com/browse/SPEC-3316
|
||||
@@ -0,0 +1,83 @@
|
||||
# coding:utf-8
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
"""This is a module to test script extensions for WingIDE
|
||||
reference: https://wingware.com/doc/scripting/example
|
||||
|
||||
note: there are important instructions in that doc for
|
||||
setting up your project files with wingapi auto-complete, etc.
|
||||
|
||||
We added C:\Program Files (x86)\Wing Pro 7.1\src to the
|
||||
PYTHONPATH via env.bat and dynaconf config instead so it is
|
||||
part of the inhereted environment."""
|
||||
|
||||
import sys
|
||||
import logging as _logging
|
||||
import wingapi
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
_MODULENAME = 'azpy.dev.ide.wing.test'
|
||||
_LOGGER = _logging.getLogger(_MODULENAME)
|
||||
_handler = _logging.StreamHandler(sys.stdout)
|
||||
_handler.setLevel(_logging.DEBUG)
|
||||
FRMT_LOG_LONG = "[%(name)s][%(levelname)s] >> %(message)s (%(asctime)s; %(filename)s:%(lineno)d)"
|
||||
_formatter = _logging.Formatter(FRMT_LOG_LONG)
|
||||
_handler.setFormatter(_formatter)
|
||||
_LOGGER.addHandler(_handler)
|
||||
_LOGGER.debug('Loading: {0}.'.format({_MODULENAME}))
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def dccsi_test_script(test_str):
|
||||
"""Simple test command for WingIDE
|
||||
|
||||
to run in wing: Edit > Command by Name
|
||||
^ opens a commanline at bottom of IDE
|
||||
|
||||
type: test-script (then return)
|
||||
^ commandline now takes entering a Test Str
|
||||
|
||||
Test Str: Booyah
|
||||
^ a Pop-up dialog will display in wing
|
||||
|
||||
"""
|
||||
app = wingapi.gApplication
|
||||
v = "Product info is: " + str(app.GetProductInfo())
|
||||
v += "\nAnd you typed: %s" % test_str
|
||||
wingapi.gApplication.ShowMessageDialog("Test Message", v)
|
||||
|
||||
#dccsi_test_script.contexts = [wingapi.kContextNewMenu("Scripts")]
|
||||
|
||||
# this will add to a menu in WingIDE
|
||||
dccsi_test_script.contexts = [
|
||||
wingapi.kContextNewMenu("DCCsi Scripts"),
|
||||
wingapi.kContextEditor(),
|
||||
]
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# bind a hotkey inside Wing that will execute our newly installed Module
|
||||
# Inside Wing, choose Edit -> Preferences, and on the left, under User Interface, choose Keyboard
|
||||
# In the center right of the Keyboard section is where you can add "Ccustom Key Bindings" combinations to execute code
|
||||
# For this test example, I have bound Ctrl+Alt+Shift+T as my key combination for executing dccsi_test_script
|
||||
|
||||
###########################################################################
|
||||
# Main Code Block, runs this script as main (testing)
|
||||
# -------------------------------------------------------------------------
|
||||
if __name__ == '__main__':
|
||||
# there are not really tests to run here due to this being a list of
|
||||
# constants for shared use.
|
||||
_G_DEBUG = True
|
||||
_DCCSI_DEV_MODE = True
|
||||
_LOGGER.setLevel(_logging.DEBUG) # force debugging
|
||||
|
||||
foo = dccsi_test_script("This is a test")
|
||||
@@ -0,0 +1,16 @@
|
||||
# coding:utf-8
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
# -- This line is 75 characters -------------------------------------------
|
||||
|
||||
# define api package for each IDE supported
|
||||
__all__ = ['check']
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
# coding:utf-8
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
# -- This line is 75 characters -------------------------------------------
|
||||
|
||||
# define api package for each IDE supported
|
||||
__all__ = ['running_state', 'maya_app']
|
||||
|
||||
# maya_app, named such to avoid namespace collisions with maya dcc app api
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
# coding:utf-8
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
# -- This line is 75 characters -------------------------------------------
|
||||
# -- Standard Python modules --
|
||||
import sys
|
||||
import os
|
||||
import inspect
|
||||
import logging as _logging
|
||||
|
||||
# -- External Python modules --
|
||||
# none
|
||||
|
||||
# -- Extension Modules --
|
||||
import azpy
|
||||
from azpy.env_bool import env_bool
|
||||
from azpy.constants import ENVAR_DCCSI_GDEBUG
|
||||
from azpy.constants import ENVAR_DCCSI_DEV_MODE
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# -- Global Definitions --
|
||||
_DCCSI_DCC_APP = None
|
||||
|
||||
# set up global space, logging etc.
|
||||
_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False)
|
||||
_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False)
|
||||
|
||||
_MODULENAME = 'azpy.dev.utils.check.maya_app'
|
||||
_LOGGER = _logging.getLogger(_MODULENAME)
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
###########################################################################
|
||||
## These mini-functions need to be defined, before they are called
|
||||
# -------------------------------------------------------------------------
|
||||
# run this, if we are in Maya
|
||||
def set_dcc_app(dcc_app='maya'):
|
||||
"""
|
||||
azpy.dev.utils.check.maya.set_dcc_app()
|
||||
this will set global _DCCSI_DCC_APP = 'maya'
|
||||
and os.environ["DCCSI_DCC_APP"] = 'maya'
|
||||
"""
|
||||
_DCCSI_DCC_APP = dcc_app
|
||||
|
||||
_LOGGER.info('Setting DCCSI_DCC_APP to: {0}'.format(dcc_app))
|
||||
|
||||
return _DCCSI_DCC_APP
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def clear_dcc_app(dcc_app=False):
|
||||
"""
|
||||
azpy.dev.utils.check.maya.set_dcc_app()
|
||||
this will set global _DCCSI_DCC_APP = False
|
||||
and os.environ["DCCSI_DCC_APP"] = False
|
||||
"""
|
||||
_DCCSI_DCC_APP = dcc_app
|
||||
|
||||
_LOGGER.info('Setting DCCSI_DCC_APP to: {0}'.format(dcc_app))
|
||||
|
||||
return _DCCSI_DCC_APP
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def validate_state(DCCSI_DCC_APP=_DCCSI_DCC_APP):
|
||||
'''
|
||||
This will detect if we are running in Maya or not,
|
||||
then will call either, set_dcc_app('maya') or clear_dcc_app(dcc_app=False)
|
||||
'''
|
||||
|
||||
if _G_DEBUG:
|
||||
_LOGGER.debug(autolog())
|
||||
|
||||
try:
|
||||
import maya.cmds as cmds
|
||||
DCCSI_DCC_APP = set_dcc_app('maya')
|
||||
except ImportError as e:
|
||||
_LOGGER.warning('Can not perform: import maya.cmds as cmds')
|
||||
DCCSI_DCC_APP = clear_dcc_app()
|
||||
else:
|
||||
try:
|
||||
if cmds.about(batch=True):
|
||||
DCCSI_DCC_APP = set_dcc_app('maya')
|
||||
except AttributeError as e:
|
||||
_LOGGER.warning("maya.cmds module isn't fully loaded/populated, "
|
||||
"(cmds populates only in batch, maya.standalone, or maya GUI)")
|
||||
# NO Maya
|
||||
DCCSI_DCC_APP=clear_dcc_app()
|
||||
|
||||
return DCCSI_DCC_APP
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def autolog():
|
||||
'''Automatically log the current function details.'''
|
||||
# Get the previous frame in the stack, otherwise it would
|
||||
# be this function!!!
|
||||
func = inspect.currentframe().f_back.f_back.f_code
|
||||
# Dump the message + the name of this function to the log.
|
||||
output = ('{module} AUTOLOG:\r'
|
||||
'Called from::\n{0}():\r'
|
||||
'In file: {1},\r'
|
||||
'At line: {2}\n'
|
||||
''.format(func.co_name,
|
||||
func.co_filename,
|
||||
func.co_firstlineno,
|
||||
module=_MODULENAME))
|
||||
return output
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# run the check on import
|
||||
_DCCSI_DCC_APP = validate_state()
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
###########################################################################
|
||||
# Main Code Block, runs this script as main (testing)
|
||||
# -------------------------------------------------------------------------
|
||||
if __name__ == '__main__':
|
||||
# there are not really tests to run here due to this being a list of
|
||||
# constants for shared use.
|
||||
_G_DEBUG = True
|
||||
_DCCSI_DEV_MODE = True
|
||||
_LOGGER.setLevel(_logging.DEBUG) # force debugging
|
||||
|
||||
## reduce cyclical azpy imports
|
||||
## it only has a basic logger configured, add log to console
|
||||
#_handler = _logging.StreamHandler(sys.stdout)
|
||||
#_handler.setLevel(_logging.DEBUG)
|
||||
#FRMT_LOG_LONG = "[%(name)s][%(levelname)s] >> %(message)s (%(asctime)s; %(filename)s:%(lineno)d)"
|
||||
#_formatter = _logging.Formatter(FRMT_LOG_LONG)
|
||||
#_handler.setFormatter(_formatter)
|
||||
#_LOGGER.addHandler(_handler)
|
||||
#_LOGGER.debug('Loading: {0}.'.format({_MODULENAME}))
|
||||
|
||||
# happy print
|
||||
from azpy.constants import STR_CROSSBAR
|
||||
_LOGGER.info(STR_CROSSBAR)
|
||||
_LOGGER.info('{} ... Running script as __main__'.format(_MODULENAME))
|
||||
_LOGGER.info(STR_CROSSBAR)
|
||||
|
||||
_DCCSI_DCC_APP = validate_state()
|
||||
_LOGGER.info('Is Maya Running? _DCCSI_DCC_APP = {}'.format(_DCCSI_DCC_APP))
|
||||
+257
@@ -0,0 +1,257 @@
|
||||
# coding:utf-8
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
# -- This line is 75 characters -------------------------------------------
|
||||
# -- Standard Python modules --
|
||||
import sys
|
||||
import os
|
||||
import logging as _logging
|
||||
|
||||
# -- External Python modules --
|
||||
# none
|
||||
|
||||
# -- Extension Modules --
|
||||
# none (yet)
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# -- Global Definitions --
|
||||
_DCCSI_DCC_APP = None
|
||||
|
||||
_MODULENAME = 'azpy.dev.utils.check.running_state'
|
||||
_LOGGER = _logging.getLogger(_MODULENAME)
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# First Class
|
||||
class CheckRunningState(object):
|
||||
"""
|
||||
< To Do: document Class >
|
||||
"""
|
||||
|
||||
# Class Variables
|
||||
DCCSI_DCC_APP = None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
'''
|
||||
CheckRunningState Class Initialization
|
||||
|
||||
< To Do: Need to document >
|
||||
|
||||
Input Attributes:
|
||||
-----------------
|
||||
self. -> SCALAR: Description.
|
||||
Default =
|
||||
|
||||
Keyword Arguments:
|
||||
------------------
|
||||
self. -> STRING: Description.
|
||||
Default =
|
||||
self. -> OBJECT: Description.
|
||||
Default =
|
||||
|
||||
Additional Attributes:
|
||||
----------------------
|
||||
self. -> BOOLEAN: Description.
|
||||
Default =
|
||||
|
||||
Documentation last updated: Month. Day, Year - Author
|
||||
'''
|
||||
|
||||
# -- Default Values --
|
||||
# top level storage for whether or not we are running
|
||||
# in a DCC tool interpreter
|
||||
self._dcc_py = False
|
||||
|
||||
# basic python info
|
||||
# if these can't run, we are in a bad state anyway
|
||||
self._python = sys.version
|
||||
self._py_version_info = sys.version_info
|
||||
|
||||
# -- Input Checks --
|
||||
self.check_known()
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
# --method-------------------------------------------------------------
|
||||
def check_known(self):
|
||||
# -- init --
|
||||
# first let's check if any of these DCC apps are running
|
||||
# 0 - maya first
|
||||
CheckRunningState.DCCSI_DCC_APP = self.maya_running()
|
||||
|
||||
# 1 - then max
|
||||
if not CheckRunningState.DCCSI_DCC_APP:
|
||||
CheckRunningState.DCCSI_DCC_APP = self.max_running()
|
||||
else:
|
||||
_LOGGER.warning('DCCSI_DCC_APP is already set: {}'.format(CheckRunningState.DCCSI_DCC_APP))
|
||||
|
||||
# 2 - then blender
|
||||
if not CheckRunningState.DCCSI_DCC_APP:
|
||||
CheckRunningState.DCCSI_DCC_APP = self.blender_running()
|
||||
else:
|
||||
_LOGGER.warning('DCCSI_DCC_APP is already set: {}'.format(CheckRunningState.DCCSI_DCC_APP))
|
||||
|
||||
# store checks for DCC info
|
||||
if CheckRunningState.DCCSI_DCC_APP:
|
||||
self.dcc_py = True
|
||||
|
||||
# store check for is maya running headless
|
||||
if CheckRunningState.DCCSI_DCC_APP == 'maya':
|
||||
self.maya_headless = self.is_maya_headless()
|
||||
|
||||
# set a envar other modules can easily check
|
||||
if CheckRunningState.DCCSI_DCC_APP:
|
||||
os.environ['DCCSI_DCC_APP'] = CheckRunningState.DCCSI_DCC_APP
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
|
||||
#--properties----------------------------------------------------------
|
||||
@property
|
||||
def python(self):
|
||||
return self._python
|
||||
|
||||
@python.setter
|
||||
def python(self, value):
|
||||
self._python = value
|
||||
return self._python
|
||||
|
||||
@property
|
||||
def py_version_info(self):
|
||||
return self._py_version_info
|
||||
|
||||
@py_version_info.setter
|
||||
def py_version_info(self, value):
|
||||
self._py_version_info = value
|
||||
return self._py_version_info
|
||||
|
||||
@property
|
||||
def dcc_app(self):
|
||||
return self._dcc_py
|
||||
|
||||
@dcc_app.setter
|
||||
def dcc_app(self, value):
|
||||
self._dcc_py = value
|
||||
return self._dcc_py
|
||||
|
||||
@property
|
||||
def maya_headless(self):
|
||||
return self._maya_headless
|
||||
|
||||
@maya_headless.setter
|
||||
def maya_headless(self, value):
|
||||
self._maya_headless = value
|
||||
return self._maya_headless
|
||||
|
||||
# template property
|
||||
# @property
|
||||
# def foo(self):
|
||||
# return self._foo
|
||||
|
||||
# @foo.setter
|
||||
# def foo(self, value):
|
||||
#self._foo = value
|
||||
# return self._foo
|
||||
# --properties----------------------------------------------------------
|
||||
|
||||
# --method-------------------------------------------------------------
|
||||
def maya_running(self):
|
||||
"""< To Do: Need to document >"""
|
||||
try:
|
||||
import azpy.dev.utils.check.maya_app as check_dcc
|
||||
DCCSI_DCC_APP = check_dcc.validate_state()
|
||||
except ImportError as e:
|
||||
_LOGGER.info('Not Implemented: azpy.dev.utils.check.maya_app')
|
||||
if DCCSI_DCC_APP:
|
||||
CheckRunningState.DCCSI_DCC_APP = check_dcc.validate_state()
|
||||
os.environ["DCCSI_DCC_APP"] = str(DCCSI_DCC_APP)
|
||||
return CheckRunningState.DCCSI_DCC_APP
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
# --method-------------------------------------------------------------
|
||||
def is_maya_headless(self):
|
||||
"""< To Do: Need to document >"""
|
||||
if self.maya_app:
|
||||
import maya.cmds as mc
|
||||
try:
|
||||
if mc.about(batch=True):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except Exception as e:
|
||||
# cmds module isn't fully loaded/populated
|
||||
# (which only happens in batch, maya.standalone, or maya GUI)
|
||||
# no maya
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
# --method-------------------------------------------------------------
|
||||
|
||||
|
||||
# --method-------------------------------------------------------------
|
||||
def max_running(self):
|
||||
"""
|
||||
< To Do: implement >
|
||||
"""
|
||||
try:
|
||||
import azpy.dev.utils.check.max_app as check_dcc
|
||||
CheckRunningState.DCCSI_DCC_APP = check_dcc.validate_state()
|
||||
except ImportError as e:
|
||||
_LOGGER.info('Not Implemented: azpy.dev.utils.check.max')
|
||||
if CheckRunningState.DCCSI_DCC_APP:
|
||||
CheckRunningState.DCCSI_DCC_APP = check_dcc.validate_state()
|
||||
os.environ["DCCSI_DCC_APP"] = str(CheckRunningState.DCCSI_DCC_APP)
|
||||
return CheckRunningState.DCCSI_DCC_APP
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
# --method-------------------------------------------------------------
|
||||
def blender_running(self):
|
||||
"""
|
||||
< To Do: implement >
|
||||
"""
|
||||
try:
|
||||
import azpy.dev.utils.check.blender_app as check_dcc
|
||||
CheckRunningState.DCCSI_DCC_APP = check_dcc.validate_state()
|
||||
except ImportError as e:
|
||||
_LOGGER.info('Not Implemented: azpy.dev.utils.check.blender')
|
||||
if CheckRunningState.DCCSI_DCC_APP:
|
||||
CheckRunningState.DCCSI_DCC_APP = check_dcc.validate_state()
|
||||
os.environ["DCCSI_DCC_APP"] = str(CheckRunningState.DCCSI_DCC_APP)
|
||||
return CheckRunningState.DCCSI_DCC_APP
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
#==========================================================================
|
||||
# Class Test
|
||||
#==========================================================================
|
||||
if __name__ == '__main__':
|
||||
_G_DEBUG = True
|
||||
_DCCSI_DEV_MODE = True
|
||||
_LOGGER.setLevel(_logging.DEBUG) # force debugging
|
||||
|
||||
# -- Extend Logger
|
||||
#_handler = _logging.StreamHandler(sys.stdout)
|
||||
# _handler.setLevel(_logging.DEBUG)
|
||||
#FRMT_LOG_LONG = "[%(name)s][%(levelname)s] >> %(message)s (%(asctime)s; %(filename)s:%(lineno)d)"
|
||||
#_formatter = _logging.Formatter(FRMT_LOG_LONG)
|
||||
# _handler.setFormatter(_formatter)
|
||||
# _LOGGER.addHandler(_handler)
|
||||
#_LOGGER.debug('Loading: {0}.'.format({_MODULENAME}))
|
||||
|
||||
# happy print
|
||||
from azpy.constants import STR_CROSSBAR
|
||||
_LOGGER.info(STR_CROSSBAR)
|
||||
_LOGGER.info('{} ... Running script as __main__'.format(_MODULENAME))
|
||||
_LOGGER.info(STR_CROSSBAR)
|
||||
|
||||
foo = CheckRunningState()
|
||||
_LOGGER.info('DCCSI_DCC_APP: {}'.format(foo.DCCSI_DCC_APP))
|
||||
@@ -12,8 +12,6 @@
|
||||
#
|
||||
# -- This line is 75 characters -------------------------------------------
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
'''
|
||||
Module: <DCCsi>\azpy\shared\common\base_env.py
|
||||
|
||||
@@ -27,21 +25,15 @@ Allowing those str('tag') to easily be changed in a single location.
|
||||
|
||||
If they are paths for code acess we assume they were put on the sys path.
|
||||
'''
|
||||
# -------------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
__author__ = 'HogJonny'
|
||||
__project__ = 'DccScriptingInterface'
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# built in's
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import logging as _logging
|
||||
from collections import OrderedDict
|
||||
|
||||
# 3rd Party
|
||||
from box import Box
|
||||
from pathlib import Path
|
||||
|
||||
# Lumberyard extensions
|
||||
@@ -51,27 +43,29 @@ from azpy.shared.common.core_utils import get_stub_check_path
|
||||
from azpy.shared.common.envar_utils import get_envar_default
|
||||
from azpy.shared.common.envar_utils import set_envar_defaults
|
||||
from azpy.shared.common.envar_utils import Validate_Envar
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
from azpy import env_bool
|
||||
from azpy.env_bool import env_bool
|
||||
from azpy.constants import ENVAR_DCCSI_GDEBUG
|
||||
from azpy.constants import ENVAR_DCCSI_DEV_MODE
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
_PACKAGENAME = 'azpy.env_base'
|
||||
|
||||
_logging.basicConfig(level=_logging.INFO,
|
||||
format=FRMT_LOG_LONG,
|
||||
datefmt='%m-%d %H:%M')
|
||||
_LOGGER = _logging.getLogger(_PACKAGENAME)
|
||||
_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME}))
|
||||
|
||||
# global space
|
||||
_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False)
|
||||
_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False)
|
||||
|
||||
_PACKAGENAME = __name__
|
||||
if _PACKAGENAME is '__main__':
|
||||
_PACKAGENAME = 'azpy.env_base'
|
||||
|
||||
_LOGGER = _logging.getLogger(_PACKAGENAME)
|
||||
_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME}))
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# set up base totally non-functional defauls (denoted with $<ENVAR>)
|
||||
# if something hasn't been set, it will stay '$<envar>'
|
||||
_BASE_ENVVAR_DICT = Box(ordered_box=True)
|
||||
_BASE_ENVVAR_DICT = OrderedDict()
|
||||
|
||||
# project tag
|
||||
_BASE_ENVVAR_DICT[ENVAR_LY_PROJECT] = '${0}'.format(ENVAR_LY_PROJECT)
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ All Lumberyard render related packages/modules should live here."""
|
||||
|
||||
import os
|
||||
|
||||
from azpy import env_bool
|
||||
from azpy.env_bool import env_bool
|
||||
from azpy.constants import ENVAR_DCCSI_GDEBUG
|
||||
from azpy.constants import ENVAR_DCCSI_DEV_MODE
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
import os
|
||||
|
||||
from azpy import env_bool
|
||||
from azpy.env_bool import env_bool
|
||||
from azpy.constants import ENVAR_DCCSI_GDEBUG
|
||||
from azpy.constants import ENVAR_DCCSI_DEV_MODE
|
||||
|
||||
|
||||
@@ -15,14 +15,10 @@
|
||||
# importing all of the modules
|
||||
"""azpy.maya.__init__"""
|
||||
|
||||
import os
|
||||
|
||||
from azpy import env_bool
|
||||
from azpy.constants import ENVAR_DCCSI_GDEBUG
|
||||
from azpy.env_bool import env_bool
|
||||
from azpy.constants import ENVAR_DCCSI_DEV_MODE
|
||||
|
||||
# global space
|
||||
_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False)
|
||||
_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False)
|
||||
|
||||
_PACKAGENAME = __name__
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
|
||||
import os
|
||||
|
||||
from azpy import env_bool
|
||||
from azpy.env_bool import env_bool
|
||||
from azpy.constants import ENVAR_DCCSI_GDEBUG
|
||||
from azpy.constants import ENVAR_DCCSI_DEV_MODE
|
||||
|
||||
|
||||
+12
-14
@@ -9,8 +9,6 @@
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
# -- This line is 75 characters -------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------
|
||||
@@ -26,7 +24,7 @@ Module Documentation:
|
||||
.. module:: event_callback_handler
|
||||
:synopsis: Simple event based callback_event handler
|
||||
using maya.api.OpenMaya (api2)
|
||||
|
||||
|
||||
.. :note: nothing mundane to declare
|
||||
.. :attention: callbacks should be uninstalled on exit
|
||||
.. :warning: maya may crash on exit if callbacks are not uninstalled
|
||||
@@ -55,7 +53,7 @@ Module Documentation:
|
||||
SceneOpened
|
||||
PostSceneRead
|
||||
workspaceChanged
|
||||
|
||||
|
||||
.. moduleauthor:: Amazon Lumberyard
|
||||
"""
|
||||
|
||||
@@ -96,10 +94,10 @@ _LOGGER.debug('Invoking:: {0}.'.format({_PACKAGENAME}))
|
||||
class EventCallbackHandler(object):
|
||||
"""
|
||||
A simple Maya event based callback_event handler class
|
||||
|
||||
|
||||
:ivar callback_event: stores event type trigger for a maya callback_event
|
||||
:vartype event: for example, 'NameChanged'
|
||||
|
||||
|
||||
:ivar this_function: stores this_function to call when callback_event is triggered
|
||||
:vartype this_function: for example,
|
||||
cb = EventCallbackHandler(callback_event='NameChanged',
|
||||
@@ -116,12 +114,12 @@ class EventCallbackHandler(object):
|
||||
self._callback_id = None
|
||||
# state tracker
|
||||
self._message_id_set = None
|
||||
|
||||
|
||||
# the callback_event event trigger
|
||||
self._callback_event = callback_event
|
||||
# the thing to do on callback_event
|
||||
self._function = this_function
|
||||
|
||||
|
||||
if install:
|
||||
self.install()
|
||||
|
||||
@@ -129,7 +127,7 @@ class EventCallbackHandler(object):
|
||||
@property
|
||||
def callback_id(self):
|
||||
return self._callback_id
|
||||
|
||||
|
||||
@property
|
||||
def callback_event(self):
|
||||
return self._callback_event
|
||||
@@ -137,15 +135,15 @@ class EventCallbackHandler(object):
|
||||
@property
|
||||
def this_function(self):
|
||||
return self._this_function
|
||||
|
||||
|
||||
# --method-------------------------------------------------------------
|
||||
def install(self):
|
||||
"""
|
||||
installs this callback_event for event, which makes it active
|
||||
"""
|
||||
|
||||
|
||||
add_event_method = openmaya.MEventMessage.addEventCallback
|
||||
|
||||
|
||||
# when called, check if it's already installed
|
||||
if self._callback_id:
|
||||
_LOGGER.warning("EventCallback::{0}:{1}, is already installed"
|
||||
@@ -177,7 +175,7 @@ class EventCallbackHandler(object):
|
||||
"""
|
||||
|
||||
remove_event_callback = openmaya.MEventMessage.removeCallback
|
||||
|
||||
|
||||
if self._callback_id:
|
||||
try:
|
||||
remove_event_callback(self._callback_id)
|
||||
@@ -197,7 +195,7 @@ class EventCallbackHandler(object):
|
||||
"".format(self._callback_event,
|
||||
self._function.__name__))
|
||||
return False
|
||||
|
||||
|
||||
# --method-------------------------------------------------------------
|
||||
def __del__(self):
|
||||
"""
|
||||
|
||||
+38
-38
@@ -21,47 +21,47 @@
|
||||
"""
|
||||
.. module:: node_message_callback_handler
|
||||
:synopsis: this module contains code related to mNodeName message based callbacks in Maya
|
||||
|
||||
|
||||
.. moduleauthor:: Amazon Lumberyard
|
||||
|
||||
|
||||
.. :note: nothing mundane to declare
|
||||
.. :attention: callbacks should be uninstalled on exit
|
||||
.. :warning: maya may crash on exit if callbacks are not uninstalled
|
||||
|
||||
.. Usage:
|
||||
< To Do >
|
||||
< To Do >
|
||||
|
||||
.. Version:
|
||||
0.1.0 | prototype
|
||||
|
||||
.. History:
|
||||
< To Do >
|
||||
|
||||
|
||||
.. Reference:
|
||||
MNodeMessage
|
||||
This class is used to register callbacks for dependency mNodeName messages of specific dependency nodes.
|
||||
http://download.autodesk.com/us/maya/2011help/API/class_m_node_message.html
|
||||
|
||||
|
||||
There are 4 add thisCallback methods which will add callbacks for the following types of messages:
|
||||
Attribute Changed
|
||||
Attribute Added or Removed
|
||||
Node Dirty
|
||||
Name Changed
|
||||
|
||||
- Attribute Changed
|
||||
- Attribute Added or Removed
|
||||
- Node Dirty
|
||||
- Name Changed
|
||||
|
||||
If we import OpenMaya,
|
||||
import maya.api.OpenMaya as om
|
||||
from maya.api.OpenMaya import MNodeMessage as mNM
|
||||
|
||||
|
||||
The valid callbacks for usage are:
|
||||
mNM.addAttributeChangedCallback
|
||||
mNM.addAttributeAddedOrRemovedCallback
|
||||
mNM.addNodeDirtyCallback
|
||||
mNM.addNodeDirtyPlugCallback
|
||||
mNM.addNameChangedCallback
|
||||
mNM.addNodeAboutToDeleteCallback
|
||||
mNM.addNodePreRemovalCallback
|
||||
mNM.addNodeDestroyedCallback
|
||||
mNM.addKeyableChangeOverride
|
||||
mNM.addAttributeChangedCallback
|
||||
mNM.addAttributeAddedOrRemovedCallback
|
||||
mNM.addNodeDirtyCallback
|
||||
mNM.addNodeDirtyPlugCallback
|
||||
mNM.addNameChangedCallback
|
||||
mNM.addNodeAboutToDeleteCallback
|
||||
mNM.addNodePreRemovalCallback
|
||||
mNM.addNodeDestroyedCallback
|
||||
mNM.addKeyableChangeOverride
|
||||
"""
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
@@ -117,12 +117,12 @@ class NodeMessageCallbackHandler(object):
|
||||
self._callback_type_id = None
|
||||
# state tracker
|
||||
self._message_id_set = None
|
||||
|
||||
|
||||
# the this_callback event trigger
|
||||
self._this_callback = this_callback
|
||||
# the thing to do on this_callback
|
||||
self._function = this_function
|
||||
|
||||
|
||||
# this handlers object mNodeName
|
||||
# passing in a null MObject (ie, without a name as an argument)
|
||||
# registers the this_callback to get all name changes in the scene
|
||||
@@ -132,18 +132,18 @@ class NodeMessageCallbackHandler(object):
|
||||
self._m_object = om.MObject()
|
||||
else:
|
||||
self._m_object = om.MObject(mNodeName)
|
||||
|
||||
|
||||
# install / activate this callback
|
||||
if install:
|
||||
self.install()
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
#--properties----------------------------------------------------------
|
||||
@property
|
||||
def callback_id(self):
|
||||
return self._callback_type_id
|
||||
|
||||
|
||||
@property
|
||||
def this_callback(self):
|
||||
return self._this_callback
|
||||
@@ -152,8 +152,8 @@ class NodeMessageCallbackHandler(object):
|
||||
def this_function(self):
|
||||
return self._this_function
|
||||
#--properties----------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
# --method-------------------------------------------------------------
|
||||
def install(self):
|
||||
"""
|
||||
@@ -210,8 +210,8 @@ class NodeMessageCallbackHandler(object):
|
||||
self._function.__name__))
|
||||
return False
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
# --method-------------------------------------------------------------
|
||||
def __del__(self):
|
||||
"""
|
||||
@@ -234,7 +234,7 @@ def testNameChanged(*args):
|
||||
mNode = None
|
||||
_LOGGER.debug('\t~ no node')
|
||||
_LOGGER.debug('\t~ warning: {0}'.format(e))
|
||||
|
||||
|
||||
# get old name
|
||||
try:
|
||||
oldName = args[1]
|
||||
@@ -242,7 +242,7 @@ def testNameChanged(*args):
|
||||
oldName = None
|
||||
_LOGGER.debug('\t~ no oldName')
|
||||
_LOGGER.debug('\t~ warning: {0}'.format(e))
|
||||
|
||||
|
||||
# convert the MObject to a dep mNode
|
||||
try:
|
||||
depNode = om.MFnDependencyNode(mNode)
|
||||
@@ -250,9 +250,9 @@ def testNameChanged(*args):
|
||||
depNode = None
|
||||
_LOGGER.debug('\t~ no depNode')
|
||||
_LOGGER.debug('\t~ warning: {0}'.format(e))
|
||||
|
||||
|
||||
if oldName == (u""): oldName = 'null'
|
||||
|
||||
|
||||
# get node type
|
||||
try:
|
||||
nodeType = depNode.typeName()
|
||||
@@ -260,7 +260,7 @@ def testNameChanged(*args):
|
||||
nodeType = None
|
||||
_LOGGER.debug('\t~ no nodeType')
|
||||
_LOGGER.debug('\t~ warning: {0}'.format(e))
|
||||
|
||||
|
||||
# get node name
|
||||
try:
|
||||
nodeName = depNode.name()
|
||||
@@ -273,7 +273,7 @@ def testNameChanged(*args):
|
||||
_LOGGER.debug('newName: {0}'.format(nodeName))
|
||||
_LOGGER.debug('oldName: {0}'.format(oldName))
|
||||
_LOGGER.debug('nodeType: {0}'.format(nodeType))
|
||||
|
||||
|
||||
return depNode
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@@ -282,8 +282,8 @@ def testNameChanged(*args):
|
||||
# Run as LICENSE
|
||||
#==========================================================================
|
||||
if __name__ == '__main__':
|
||||
|
||||
|
||||
name_changed_callback = om.MNodeMessage.addNameChangedCallback
|
||||
ncbh = NodeMessageCallbackHandler(name_changed_callback,
|
||||
testNameChanged)
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
|
||||
import os
|
||||
|
||||
from azpy import env_bool
|
||||
from azpy.env_bool import env_bool
|
||||
from azpy.constants import ENVAR_DCCSI_GDEBUG
|
||||
from azpy.constants import ENVAR_DCCSI_DEV_MODE
|
||||
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
|
||||
mport os
|
||||
|
||||
from azpy import env_bool
|
||||
from azpy.env_bool import env_bool
|
||||
from azpy.constants import ENVAR_DCCSI_GDEBUG
|
||||
from azpy.constants import ENVAR_DCCSI_DEV_MODE
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# coding:utf-8
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
# -- This line is 75 characters -------------------------------------------
|
||||
|
||||
# define api package for each IDE supported
|
||||
__all__ = ['simple_command_port', 'execute_wing_code', 'wing_to_maya']
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
# coding:utf-8
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
# -- This line is 75 characters -------------------------------------------
|
||||
import os
|
||||
import socket
|
||||
import logging as _logging
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def get_stub_check_path(in_path=__file__, check_stub='engineroot.txt'):
|
||||
'''
|
||||
Returns the branch root directory of the dev\\'engineroot.txt'
|
||||
(... or you can pass it another known stub)
|
||||
|
||||
so we can safely build relative filepaths within that branch.
|
||||
|
||||
If the stub is not found, it returns None
|
||||
'''
|
||||
path = os.path.abspath(os.path.join(os.path.dirname(in_path), ".."))
|
||||
_LOGGER.info('parent dir: {}'.format(path))
|
||||
|
||||
while 1:
|
||||
test_path = os.path.join(path, check_stub)
|
||||
|
||||
if os.path.isfile(test_path):
|
||||
return os.path.abspath(os.path.join(os.path.dirname(test_path)))
|
||||
|
||||
else:
|
||||
path, tail = (os.path.abspath(os.path.join(os.path.dirname(test_path), "..")),
|
||||
os.path.basename(test_path))
|
||||
|
||||
if (len(tail) == 0):
|
||||
return None
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# -- Global Definitions --
|
||||
_MODULENAME = 'azpy.maya.utils.execute_wing_code'
|
||||
_LOGGER = _logging.getLogger(_MODULENAME)
|
||||
|
||||
_LY_DEV = get_stub_check_path()
|
||||
_LOGGER.info('_LY_DEV: {}'.format(_LY_DEV))
|
||||
|
||||
_PROJ_CACHE = os.path.join(_LY_DEV, 'cache', 'DCCsi', 'wing')
|
||||
_LOGGER.info('_PROJ_CACHE: {}'.format(_PROJ_CACHE))
|
||||
|
||||
_LOCAL_HOST = socket.gethostbyname(socket.gethostname())
|
||||
_LOGGER.info('local_host: {}'.format(_LOCAL_HOST))
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
###########################################################################
|
||||
# --main code block--------------------------------------------------------
|
||||
def main(code_type='python'):
|
||||
"""
|
||||
Evaluate the temp file on disk, made by Wing, in Maya.
|
||||
|
||||
code_type : string : Supports either 'python' or 'mel'
|
||||
|
||||
"""
|
||||
temp_file_name = 'tmp_wing_data.txt'
|
||||
|
||||
temp_file_path = os.path.join(_PROJ_CACHE, temp_file_name)
|
||||
temp_file_path = os.path.abspath(temp_file_path)
|
||||
temp_file = temp_file_path.replace("\\", "/") # maya is linux paths?
|
||||
_LOGGER.debug('temp_file_path is: {}'.format(temp_file_path))
|
||||
|
||||
if os.access(temp_file, os.F_OK):
|
||||
# open and print the file in Maya:
|
||||
f = open(temp_file, "r")
|
||||
lines = f.readlines()
|
||||
for line in lines:
|
||||
print(line.rstrip())
|
||||
f.close()
|
||||
|
||||
if code_type == "python":
|
||||
# execute the file contents in Maya:
|
||||
f = open(temp_file, "r")
|
||||
# (1) doesn't work?
|
||||
#exec(f, __main__.__dict__, __main__.__dict__)
|
||||
# (2) works is series of single expressions
|
||||
#for line in lines:
|
||||
#exec(line.rstrip())
|
||||
# f.close()
|
||||
# (3) this seems to work much better
|
||||
temp_code_file_name = 'temp_code.py'
|
||||
temp_code_file = os.path.join(_PROJ_CACHE, temp_code_file_name)
|
||||
temp_code_file = os.path.abspath(temp_code_file)
|
||||
temp_code = temp_file_path.replace("\\", "/") # maya is linux paths?
|
||||
code = compile(f.read(), temp_code, 'exec')
|
||||
_LOGGER.debug(type(code))
|
||||
exec(code)
|
||||
|
||||
elif code_type == "mel":
|
||||
mel_cmd = "source '{}'".format(temp_file)
|
||||
# This causes the "// Result: " line to show up in the Script Editor:
|
||||
om.MGlobal.executeCommand(mel_cmd, True, True)
|
||||
else:
|
||||
_LOGGER.warning("No temp file exists: {}".format(temp_file))
|
||||
file=open(temp_file, "w")
|
||||
file.write("test file write")
|
||||
if os.path.isfile(temp_file):
|
||||
_LOGGER.info('Created the temp file, please try again!')
|
||||
else:
|
||||
_LOGGER.error('File not created: {}'.format(temp_file))
|
||||
|
||||
return
|
||||
# -------------------------------------------------------------------------
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
# coding:utf-8
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
# -- This line is 75 characters -------------------------------------------
|
||||
# -- Standard Python modules --
|
||||
import sys
|
||||
import os
|
||||
import socket
|
||||
import site
|
||||
import time
|
||||
import logging as _logging
|
||||
|
||||
# -- External Python modules --
|
||||
# none
|
||||
|
||||
# -- Extension Modules --
|
||||
# none (yet)
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# -- Global Definitions --
|
||||
_MODULENAME = 'azpy.maya.utils.simple_command_port'
|
||||
_LOGGER = _logging.getLogger(_MODULENAME)
|
||||
|
||||
_LOCAL_HOST = socket.gethostbyname(socket.gethostname())
|
||||
_LOGGER.info('local_host: {}'.format(_LOCAL_HOST))
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
class SimpleCommandPort:
|
||||
"""
|
||||
Simple maya command port Object Class.
|
||||
"""
|
||||
#----------------------------------------------------------------------
|
||||
#def __new__(self):
|
||||
#try:
|
||||
#self.port_name
|
||||
#return self
|
||||
#except NameError as e:
|
||||
#self.port_name = None
|
||||
#self.logger.error("Specify a port like: '127.0.0.1:6000'")
|
||||
#return
|
||||
#return
|
||||
|
||||
# -- Constructor ------------------------------------------------------
|
||||
def __init__(self,
|
||||
local_host=_LOCAL_HOST,
|
||||
comman_port=6000,
|
||||
logger=_LOGGER,
|
||||
source_type='python',
|
||||
echo_output=True,
|
||||
noreturn=False,
|
||||
*args, **kwargs):
|
||||
'''
|
||||
SimpleCommandPort Class Initialization
|
||||
|
||||
< To Do: Need to document >
|
||||
|
||||
Input Attributes:
|
||||
-----------------
|
||||
self. SCALAR: Description.
|
||||
Default =
|
||||
|
||||
Keyword Arguments:
|
||||
------------------
|
||||
self. STRING: Description.
|
||||
Default =
|
||||
self. OBJECT: Description.
|
||||
Default =
|
||||
|
||||
Additional Attributes:
|
||||
----------------------
|
||||
self. BOOLEAN: Description.
|
||||
Default =
|
||||
|
||||
Documentation last updated: Month. Day, Year - Author
|
||||
'''
|
||||
|
||||
# -- Default Values --
|
||||
self._port_name = '0.0.0.0:0000'
|
||||
self._port = None
|
||||
self._logger = None
|
||||
self._echo_output = echo_output
|
||||
self._noreturn = noreturn
|
||||
self._source_type = source_type
|
||||
|
||||
if logger != None:
|
||||
self._logger = logger
|
||||
|
||||
# -- Input Checks --
|
||||
if local_host != None and local_host != '0.0.0.0':
|
||||
self._port_name = str('{0}:{1}'.format(local_host, comman_port))
|
||||
else:
|
||||
self.logger.error("Specify a port: SimpleCommandPort('127.0.0.1','6000')")
|
||||
|
||||
## -- init --
|
||||
#try:
|
||||
#self.port = self.cmdPortOpen()
|
||||
#except Exception as e:
|
||||
#self.port = None
|
||||
#if self.logger:
|
||||
#self.logger.autolog(e, level='error')
|
||||
#else:
|
||||
#self.logIt(e, level='error')
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
#--properties----------------------------------------------------------
|
||||
@property
|
||||
def port_name(self):
|
||||
return self._port_name
|
||||
|
||||
@port_name.setter
|
||||
def port_name(self, value):
|
||||
self._port_name = value
|
||||
return self._port_name
|
||||
|
||||
@port_name.getter
|
||||
def port_name(self):
|
||||
return self._port_name
|
||||
|
||||
@property
|
||||
def port(self):
|
||||
return self._port
|
||||
|
||||
@port.setter
|
||||
def port(self, value):
|
||||
self._port = value
|
||||
return self._port
|
||||
|
||||
@property
|
||||
def logger(self):
|
||||
return self._logger
|
||||
|
||||
@logger.setter
|
||||
def logger(self, value):
|
||||
self._logger = value
|
||||
return self._logger
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
# --method-------------------------------------------------------------
|
||||
def open(self):
|
||||
from azpy.dev.utils.check.maya_app import validate_state
|
||||
if validate_state():
|
||||
if self.port_name != None and self.port_name != '0.0.0.0:0000':
|
||||
import maya.cmds as cmds
|
||||
try:
|
||||
self.logger.info('Opening the cmd port: {0}'
|
||||
''.format(self.port_name))
|
||||
except Exception as e:
|
||||
self.logger.error('{0}'.format(e))
|
||||
|
||||
try:
|
||||
# to do: get mel working
|
||||
self.port = cmds.commandPort(name=self.port_name,
|
||||
echoOutput=self._echo_output,
|
||||
sourceType =self._source_type,
|
||||
noreturn=self._noreturn)
|
||||
self.logger.info('{0}:: Open!'.format(self.port_name))
|
||||
time.sleep(0.25)
|
||||
return True, self.port_name
|
||||
except Exception as e:
|
||||
self.logger.error('{0}'.format(e))
|
||||
_LOGGER.info(cmds.commandPort(self.port_name, q=True))
|
||||
self.port_name='ERROR'
|
||||
return False, self.port_name
|
||||
else:
|
||||
self.logger.warning('Can not use a port: {}'.format(self.port_name))
|
||||
else:
|
||||
self.logger.warning('Did not perform port open, Not running Maya!')
|
||||
return False
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
# --method-------------------------------------------------------------
|
||||
def close(self):
|
||||
|
||||
self.logger.info('Closing the port: {0}'.format(self.port_name))
|
||||
|
||||
try:
|
||||
import maya.cmds as cmds
|
||||
cmds.commandPort(name=self.port_name, close=True, echoOutput=self._echo_output)
|
||||
self.port = None
|
||||
self.logger.info('{0}:: Port Closed!'.format(self.port_name))
|
||||
except Exception as e:
|
||||
self.logger.error('{0}'.format(e))
|
||||
self.port_name = 'ERROR'
|
||||
return False
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
return True
|
||||
#----------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
###########################################################################
|
||||
# Main Code Block, runs this script as main (testing)
|
||||
# -------------------------------------------------------------------------
|
||||
if __name__ == '__main__':
|
||||
_G_DEBUG = True
|
||||
_DCCSI_DEV_MODE = True
|
||||
_LOGGER.setLevel(_logging.DEBUG) # force debugging
|
||||
|
||||
# -- Extend Logger
|
||||
#_handler = _logging.StreamHandler(sys.stdout)
|
||||
# _handler.setLevel(_logging.DEBUG)
|
||||
#FRMT_LOG_LONG = "[%(name)s][%(levelname)s] >> %(message)s (%(asctime)s; %(filename)s:%(lineno)d)"
|
||||
#_formatter = _logging.Formatter(FRMT_LOG_LONG)
|
||||
# _handler.setFormatter(_formatter)
|
||||
# _LOGGER.addHandler(_handler)
|
||||
#_LOGGER.debug('Loading: {0}.'.format({_MODULENAME}))
|
||||
|
||||
# happy print
|
||||
from azpy.constants import STR_CROSSBAR
|
||||
_LOGGER.info(STR_CROSSBAR)
|
||||
_LOGGER.info('{} ... Running script as __main__'.format(_MODULENAME))
|
||||
_LOGGER.info(STR_CROSSBAR)
|
||||
|
||||
# should throw an error message
|
||||
foo_port = SimpleCommandPort()
|
||||
|
||||
# should attemp to open the port, should warn as defualt port is '0.0.0.0:0000'
|
||||
foo_port.open()
|
||||
|
||||
# should return a port object
|
||||
foo_port = SimpleCommandPort('127.0.0.1:6000')
|
||||
|
||||
# should attemp to open the port, which should warn because only works in Maya
|
||||
foo_port.open()
|
||||
|
||||
_LOGGER.info('Port Name: {}'.format(foo_port.port_name))
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
# coding:utf-8
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
# -- This line is 75 characters -------------------------------------------
|
||||
# -- Standard Python modules --
|
||||
import sys
|
||||
import os
|
||||
import site
|
||||
import socket
|
||||
import time
|
||||
import logging as _logging
|
||||
|
||||
# -- External Python modules --
|
||||
# none
|
||||
|
||||
# -- Extension Modules --
|
||||
from simple_command_port import SimpleCommandPort
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# -- Global Definitions --
|
||||
_MODULENAME = 'azpy.maya.utils.wing_to_maya'
|
||||
_LOGGER = _logging.getLogger(_MODULENAME)
|
||||
|
||||
_LOCAL_HOST = socket.gethostbyname(socket.gethostname())
|
||||
_LOGGER.info('local_host: {}'.format(_LOCAL_HOST))
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def start_wing_to_maya(local_host=_LOCAL_HOST,
|
||||
comman_port=6000,
|
||||
logger=_LOGGER,
|
||||
*args, **kwargs):
|
||||
"""
|
||||
imports the module
|
||||
opens a port to run python code from wingIDE directly
|
||||
"""
|
||||
|
||||
if logger != None:
|
||||
_LOGGER = _LOGGER
|
||||
|
||||
try:
|
||||
import maya.cmds as cmds
|
||||
except ImportError as e:
|
||||
_LOGGER.error('Could not perform: {}'.format(e))
|
||||
raise e
|
||||
|
||||
try:
|
||||
port
|
||||
except NameError:
|
||||
port = None
|
||||
|
||||
port_name = str('{0}:{1}'.format(local_host, comman_port))
|
||||
|
||||
# should only be getting the port passed in
|
||||
_LOGGER.info('Attempting to open port:: {0}'.format(port_name))
|
||||
port_object = SimpleCommandPort(port_name)
|
||||
|
||||
try:
|
||||
port = port_object.open()
|
||||
except Exception as e:
|
||||
_LOGGER.error('Could not open port: {e}'.format(e))
|
||||
raise e
|
||||
|
||||
if not port:
|
||||
return port
|
||||
else:
|
||||
try:
|
||||
import execute_wing_code
|
||||
except Exception as e:
|
||||
_LOGGER.error('Could not execute code: {}'.format(e))
|
||||
return 'Error'
|
||||
|
||||
time.sleep(0.25)
|
||||
_LOGGER.info('WingIDE: Python >> to >> Maya, is started.')
|
||||
|
||||
try:
|
||||
test_port = cmds.commandPort(port_name, q=True)
|
||||
except Exception as e:
|
||||
_LOGGER.autolog(e, level='error')
|
||||
return 'Error'
|
||||
|
||||
if test_port:
|
||||
message = 'That port is already open. '
|
||||
message += 'Attempting to close old port, so I can re-establish it...'
|
||||
_LOGGER.info(message)
|
||||
try:
|
||||
cmds.commandPort(name=port_name, close=True, echoOutput=True)
|
||||
except Exception as e:
|
||||
_LOGGER.error(''.format(e))
|
||||
|
||||
time.sleep(0.25)
|
||||
try:
|
||||
test_port = cmds.commandPort(port_name, q=True)
|
||||
except Exception as e:
|
||||
_LOGGER.error('Could not query port: {}'.format(e))
|
||||
|
||||
if test_port == False:
|
||||
_LOGGER.info('Port closed! Re-opening ...')
|
||||
try:
|
||||
port = SimpleCommandPort(port_name)
|
||||
except Exception as e:
|
||||
_LOGGER.error('Could not create port object: {}'.format(e))
|
||||
|
||||
time.sleep(0.25)
|
||||
try:
|
||||
test_port = cmds.commandPort(port_name, q=True)
|
||||
_LOGGER.info('The posrt is: {}'.format(test_port))
|
||||
except Exception as e:
|
||||
_LOGGER.error('Could not query port: {}'.format(e))
|
||||
|
||||
elif test_port == False:
|
||||
_LOGGER.info('The port does not exist ... attempting to open it again now')
|
||||
try:
|
||||
port = SimpleCommandPort(port_name)
|
||||
except Exception as e:
|
||||
_LOGGER.error('Could not create port: {}'.format(e))
|
||||
|
||||
time.sleep(0.25)
|
||||
|
||||
try:
|
||||
test_port = cmds.commandPort(port_name, q=True)
|
||||
_LOGGER.info('Port is: {}'.format(test_port))
|
||||
except Exception as e:
|
||||
_LOGGER.error('Could not create port: {}'.format(e))
|
||||
|
||||
return port
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def start_wing_to_maya_menu():
|
||||
"""
|
||||
Simple hook to call the function from a menu item,
|
||||
using the default port name defined
|
||||
"""
|
||||
port = object() # init a dummy object
|
||||
|
||||
# default name ... name is first arg, or a kwarg
|
||||
portName, kwargs = setSynthArgKwarg(port, argPosIndex=0, argTag='portName',
|
||||
inArgs=args, inKwargs=kwargs,
|
||||
defaultValue="127.0.0.1:6000")
|
||||
|
||||
port = start_wing_to_maya(local_host=_LOCAL_HOST, comman_port=6000)
|
||||
return
|
||||
# -------------------------------------------------------------------------
|
||||
@@ -19,8 +19,7 @@ All Atom render related packages/modules should live here."""
|
||||
|
||||
import os
|
||||
|
||||
import azpy
|
||||
from azpy import env_bool
|
||||
from azpy.env_bool import env_bool
|
||||
from azpy.constants import ENVAR_DCCSI_GDEBUG
|
||||
from azpy.constants import ENVAR_DCCSI_DEV_MODE
|
||||
|
||||
|
||||
@@ -21,19 +21,8 @@ import logging as _logging
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# global space debug flag
|
||||
# not using azpy.constants here to help avoid cyclical imports lower
|
||||
from azpy.env_bool import env_bool
|
||||
|
||||
# have to avoid importing these from constants
|
||||
# because we can end up with cyclical import issues
|
||||
# need to figure out a better solution later so we don't duplicate everywhere
|
||||
ENVAR_DCCSI_GDEBUG = str('DCCSI_GDEBUG')
|
||||
ENVAR_DCCSI_DEV_MODE = str('DCCSI_DEV_MODE')
|
||||
|
||||
# global space
|
||||
_G_DEBUG = os.getenv(ENVAR_DCCSI_GDEBUG, False)
|
||||
_DCCSI_DEV_MODE = os.getenv(ENVAR_DCCSI_DEV_MODE, False)
|
||||
# global space debug flag, no fancy stuff here we use in bootstrap
|
||||
_G_DEBUG = False # manually enable to debug this file
|
||||
|
||||
_PACKAGENAME = __name__
|
||||
if _PACKAGENAME is '__main__':
|
||||
@@ -59,8 +48,9 @@ def return_stub(stub):
|
||||
if (len(tail) == 0):
|
||||
path = ""
|
||||
if _G_DEBUG:
|
||||
print('~ Debug Message: I was not able to find the '
|
||||
'path to that file (stub) in a walk-up from currnet path')
|
||||
_LOGGER.debug('~ Debug Message: I was not able to find the '
|
||||
'path to that file (stub) in a walk-up '
|
||||
'from currnet path')
|
||||
break
|
||||
_dir_to_last_file = path
|
||||
|
||||
@@ -76,13 +66,13 @@ if __name__ == '__main__':
|
||||
# constants for shared use.
|
||||
|
||||
# happy print
|
||||
print("# {0} #".format('-' * 72))
|
||||
print('~ find_stub.py ... Running script as __main__')
|
||||
print("# {0} #\r".format('-' * 72))
|
||||
_LOGGER.info("# {0} #".format('-' * 72))
|
||||
_LOGGER.info('~ find_stub.py ... Running script as __main__')
|
||||
_LOGGER.info("# {0} #\r".format('-' * 72))
|
||||
|
||||
print('~ Current Work dir: {0}'.format(os.getcwd()))
|
||||
_LOGGER.info('~ Current Work dir: {0}'.format(os.getcwd()))
|
||||
|
||||
print('~ Dev\: {0}'.format(return_stub('engineroot.txt')))
|
||||
_LOGGER.info('~ Dev\: {0}'.format(return_stub('engineroot.txt')))
|
||||
|
||||
# custom prompt
|
||||
sys.ps1 = "[azpy]>>"
|
||||
|
||||
@@ -11,13 +11,12 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
# -- This line is 75 characters -------------------------------------------
|
||||
# The __init__.py files help guide import statements without automatically
|
||||
# importing all of the modules
|
||||
|
||||
"""azpy.shared.__init__"""
|
||||
|
||||
import os
|
||||
|
||||
from azpy import env_bool
|
||||
from azpy.env_bool import env_bool
|
||||
from azpy.constants import ENVAR_DCCSI_GDEBUG
|
||||
from azpy.constants import ENVAR_DCCSI_DEV_MODE
|
||||
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"ordered_box": true,
|
||||
"COMPANY": "Amazon.Lumberyard",
|
||||
"LY_PROJECT": "DccScriptingInterface",
|
||||
"LY_DEV": "G:\\depot\\JG_PC1_spectrAtom\\dev",
|
||||
"LY_BUILD_DIR_NAME": "windows_vs2019",
|
||||
"LY_BUILD_PATH": "G:\\depot\\JG_PC1_spectrAtom\\dev\\windows_vs2019",
|
||||
"QT_PLUGIN_PATH": "G:\\depot\\JG_PC1_spectrAtom\\dev\\bin\\profile\\EditorPlugins",
|
||||
"LY_PROJECT_PATH": "G:\\depot\\JG_PC1_spectrAtom\\dev\\Gems\\AtomLyIntegration\\TechnicalArt\\DccScriptingInterface",
|
||||
"DCCSIG_PATH": "G:\\depot\\JG_PC1_spectrAtom\\dev\\Gems\\AtomLyIntegration\\TechnicalArt\\DccScriptingInterface",
|
||||
"DCCSI_AZPY_PATH": "G:\\depot\\JG_PC1_spectrAtom\\dev\\Gems\\AtomLyIntegration\\TechnicalArt\\DccScriptingInterface\\azpy",
|
||||
"DCCSI_SDK_PATH": "G:\\depot\\JG_PC1_spectrAtom\\dev\\Gems\\AtomLyIntegration\\TechnicalArt\\DccScriptingInterface\\SDK",
|
||||
"DCCSI_WING_VERSION_MAJOR": "7",
|
||||
"DCCSI_WING_VERSION_MINOR": "1",
|
||||
"WINGHOME": "C:\\Program Files (x86)\\Wing Pro 7.1",
|
||||
"DCCSI_PY_DEFAULT": "G:\\depot\\JG_PC1_spectrAtom\\dev\\Tools\\Python\\3.7.5\\windows\\python.exe"
|
||||
}
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
|
||||
import os
|
||||
|
||||
from azpy import env_bool
|
||||
from azpy.env_bool import env_bool
|
||||
from azpy.constants import ENVAR_DCCSI_GDEBUG
|
||||
from azpy.constants import ENVAR_DCCSI_DEV_MODE
|
||||
|
||||
|
||||
+19
-27
@@ -53,8 +53,8 @@ import site
|
||||
import fnmatch
|
||||
|
||||
# 3rd Party
|
||||
from unipath import Path
|
||||
from progress.spinner import Spinner
|
||||
from pathlib import Path
|
||||
# from progress.spinner import Spinner # deprecate use (or refactor)
|
||||
|
||||
# Lumberyard extensions
|
||||
from azpy.constants import *
|
||||
@@ -64,7 +64,7 @@ from azpy import initialize_logger
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# global space debug flag
|
||||
from azpy import env_bool
|
||||
from azpy.env_bool import env_bool
|
||||
from azpy.constants import ENVAR_DCCSI_GDEBUG
|
||||
from azpy.constants import ENVAR_DCCSI_DEV_MODE
|
||||
|
||||
@@ -85,12 +85,9 @@ _LOGGER.debug('Invoking __init__.py for {0}.'.format({_PACKAGENAME}))
|
||||
# --------------------------------------------------------------------------
|
||||
def gather_paths_of_type_from_dir(in_path=str('c:\\'),
|
||||
extension=str('*.py'),
|
||||
return_path_list=list(),
|
||||
use_spinner=False):
|
||||
return_path_list=list()):
|
||||
'''Walks from in_path and returns list of directories that contain the
|
||||
file type matching the extension'''
|
||||
if use_spinner:
|
||||
spinner = Spinner('Finding: {0}\r'.format(extension))
|
||||
|
||||
# recursive function for finding paths
|
||||
dir_contents = os.listdir(in_path)
|
||||
@@ -111,9 +108,6 @@ def gather_paths_of_type_from_dir(in_path=str('c:\\'),
|
||||
if found:
|
||||
return_path_list.append(dir_trim_following_slash(to_unix_path(os.path.abspath(in_path))))
|
||||
|
||||
if use_spinner:
|
||||
spinner.next()
|
||||
|
||||
complete = True
|
||||
|
||||
return return_path_list
|
||||
@@ -136,7 +130,7 @@ def dir_trim_following_slash(current_path_str):
|
||||
def to_unix_path(current_path_str):
|
||||
'''converts path string to use unix slashes'''
|
||||
_LOGGER.debug('to_unix_path({0})'.format(current_path_str))
|
||||
safe_path = current_path_str.replace('\\', '/')
|
||||
safe_path = str(current_path_str).replace('\\', '/')
|
||||
return safe_path
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
@@ -158,7 +152,7 @@ def module_path():
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
def get_stub_check_path(in_path, checkStub='engineroot.txt'):
|
||||
def get_stub_check_path(in_path, check_stub='engineroot.txt'):
|
||||
'''
|
||||
Returns the branch root directory of the dev\'engineroot.txt'
|
||||
(... or you can pass it another known stub)
|
||||
@@ -167,15 +161,13 @@ def get_stub_check_path(in_path, checkStub='engineroot.txt'):
|
||||
|
||||
If the stub is not found, it returns None
|
||||
'''
|
||||
from unipath import Path
|
||||
|
||||
path = Path(in_path).absolute()
|
||||
|
||||
while 1:
|
||||
testPath = Path(path, checkStub)
|
||||
test_path = Path(path, check_stub)
|
||||
|
||||
if testPath.isfile():
|
||||
return Path(testPath)
|
||||
if test_path.is_file():
|
||||
return Path(test_path)
|
||||
|
||||
else:
|
||||
path, tail = (path.parent, path.name)
|
||||
@@ -188,15 +180,16 @@ def get_stub_check_path(in_path, checkStub='engineroot.txt'):
|
||||
# -------------------------------------------------------------------------
|
||||
def reorder_sys_paths(known_sys_paths):
|
||||
"""Reorders new directories to the front"""
|
||||
new_sys_path = []
|
||||
sys_paths = list(sys.path)
|
||||
new_sys_paths = []
|
||||
|
||||
for item in list(sys.path):
|
||||
for item in sys_paths:
|
||||
item = Path(item)
|
||||
if item.lower() not in known_sys_paths:
|
||||
new_sys_path.append(item)
|
||||
sys.path.remove(item)
|
||||
if str(item).lower() not in known_sys_paths:
|
||||
new_sys_paths.append(item)
|
||||
sys_paths.remove(str(item))
|
||||
|
||||
sys.path[:0] = new_sys_path
|
||||
sys.path[:0] = new_sys_paths
|
||||
|
||||
known_sys_paths = site._init_pathinfo()
|
||||
return known_sys_paths
|
||||
@@ -386,13 +379,11 @@ def walk_up_dir(in_path, dir_tag='foo'):
|
||||
|
||||
returns None if the directory named dir_tag is not found
|
||||
'''
|
||||
from unipath import Path
|
||||
|
||||
path = Path(Path(__file__).absolute())
|
||||
path = Path(__file__).absolute()
|
||||
while 1:
|
||||
# hmmm, will this break on unix paths?
|
||||
# what about case sensitivity?
|
||||
dir_base_name = Path(path.norm_case()).name()
|
||||
dir_base_name = path.norm_case().name()
|
||||
if (dir_base_name == dir_tag):
|
||||
break
|
||||
path, tail = (path.parent(), path.name())
|
||||
@@ -406,6 +397,7 @@ def walk_up_dir(in_path, dir_tag='foo'):
|
||||
# --------------------------------------------------------------------------
|
||||
def return_stub(stub):
|
||||
'''Take a file name (stub) and returns the directory of the file (stub)'''
|
||||
# to do: refactor to pathlib.Path
|
||||
from unipath import Path
|
||||
|
||||
dir_last_file = None
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ from azpy.constants import *
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
from azpy import env_bool
|
||||
from azpy.env_bool import env_bool
|
||||
from azpy.constants import ENVAR_DCCSI_GDEBUG
|
||||
from azpy.constants import ENVAR_DCCSI_DEV_MODE
|
||||
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# The __init__.py files help guide import statements without automatically
|
||||
# importing all of the modules
|
||||
"""azpy.shared.ui.__init__"""
|
||||
|
||||
import os
|
||||
import logging
|
||||
import logging.config
|
||||
|
||||
# global space debug flag
|
||||
_G_DEBUG = os.getenv('DCCSI_GDEBUG', False)
|
||||
|
||||
# global space debug flag
|
||||
_DCCSI_DEV_MODE = os.getenv('DCCSI_DEV_MODE', False)
|
||||
|
||||
if _DCCSI_DEV_MODE:
|
||||
_PACKAGENAME = __name__
|
||||
if _PACKAGENAME is '__main__':
|
||||
_PACKAGENAME = 'noodely'
|
||||
|
||||
_PKG_PARENT_PATH = str('azpy.shared')
|
||||
_PKG_PATH = str('{0}.{1}'.format(_PKG_PARENT_PATH, _PACKAGENAME))
|
||||
_LOGGER = logging.getLogger(_PACKAGENAME)
|
||||
_LOGGER.debug('Invoking __init__.py for {0}.'.format({_PKG_PATH}))
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
#
|
||||
__all__ = ['config', 'find_arg', 'master', 'node', 'synth',
|
||||
'synth_arg_kwarg', 'test_foo']
|
||||
#
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
del _LOGGER
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------
|
||||
# find_arg.py
|
||||
# A simple function for arg, kwarg retrieval
|
||||
# version: 0.1
|
||||
# maintenance: Gallowj
|
||||
# -------------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------
|
||||
"""Module docstring: A simple function for retrieval of arg, kwarg"""
|
||||
__author__ = 'HogJonny'
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
def find_arg(argPosIndex=None, argTag=None, removeKwarg=None,
|
||||
inArgs=None, inKwargs=None, defaultValue=None):
|
||||
"""
|
||||
# finds and returns an arg...
|
||||
# if a positional index is given argPosIndex=0, it checks args first
|
||||
# if a argTag is given, it checks kwargs
|
||||
# If removeKwarg=True, it will remove the found arg from kwargs
|
||||
# * I actually want/need to do this often
|
||||
#
|
||||
# a set kwarg will ALWAYS take precident over
|
||||
# positional arg!!!
|
||||
#
|
||||
# return outArg, args, kwargs <-- get back modified kwargs!
|
||||
#
|
||||
# proper usage:
|
||||
#
|
||||
# foundArg, args, kwargs = find_arg(0, 'name',)
|
||||
"""
|
||||
if argPosIndex != None:
|
||||
if not isinstance(argPosIndex, int):
|
||||
raise TypeError('argPosIndex: accepts a index integer!\r'
|
||||
'got: {0}'.format(argPosIndex))
|
||||
|
||||
# positional args ... check the position
|
||||
if len(inArgs) > 0:
|
||||
try:
|
||||
foundArg = inArgs[argPosIndex]
|
||||
except:
|
||||
pass
|
||||
|
||||
# check kwargs ... a set kwarg will ALWAYS take precident over
|
||||
# positional arg!!!
|
||||
try:
|
||||
foundArg
|
||||
except:
|
||||
foundArg = inKwargs.get(argTag, defaultValue) # defaults to None
|
||||
|
||||
if removeKwarg:
|
||||
if argTag in inKwargs:
|
||||
del inKwargs[argTag]
|
||||
|
||||
# if we didn't find the arg/kwarg, the defualt return will be None
|
||||
return foundArg, inKwargs
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
###########################################################################
|
||||
# --call block-------------------------------------------------------------
|
||||
if __name__ == "__main__":
|
||||
print ("# ----------------------------------------------------------------------- #\r")
|
||||
print ('~ find_arg.py ... Running script as __main__')
|
||||
print ("# ----------------------------------------------------------------------- #\r")
|
||||
|
||||
_G_DEBUG = True
|
||||
|
||||
from test_foo import Foo
|
||||
|
||||
#######################################################################
|
||||
# Node Class
|
||||
# ---------------------------------------------------------------------
|
||||
class TestNode(Foo):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__()
|
||||
self._name, kwargs = find_arg(argTag='foo', removeKwarg=True,
|
||||
inArgs=args, inKwargs=kwargs)
|
||||
self._name, kwargs = find_arg(argPosIndex=0, argTag='name',
|
||||
removeKwarg=True,
|
||||
inArgs=args, inKwargs=kwargs) # <-- first positional OR kwarg
|
||||
self._parent, kwargs = find_arg(argPosIndex=1, argTag='parent',
|
||||
removeKwarg=True,
|
||||
inArgs=args, inKwargs=kwargs) # <-- second positional OR kwarg
|
||||
|
||||
self._kwargsDict = {}
|
||||
|
||||
# arbitrary argument properties
|
||||
# checking **kwargs, any kwargs left
|
||||
# will be used to synthesize a property
|
||||
for key, value in kwargs.items():
|
||||
self._kwargsDict[key] = value
|
||||
# synthesize(self, '{0}'.format(key), value) <-- I have a method,
|
||||
# which synthesizes properties... with gettr, settr, etc.
|
||||
if _G_DEBUG:
|
||||
print("{0}:{1}".format(key, value))
|
||||
|
||||
# representation
|
||||
def __repr__(self):
|
||||
return '{0}({1})\r'.format(self.__class__.__name__, self.__dict__)
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
testNode = TestNode('foo')
|
||||
|
||||
testNode2 = TestNode(name='fooey', parent=testNode)
|
||||
|
||||
testNode3 = TestNode('kablooey', testNode2, goober='dufus')
|
||||
|
||||
print ('testNode2, name: {0}, parent: {1}'.format(testNode2._name, testNode2._parent))
|
||||
print (testNode3)
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
###########################################################################
|
||||
# HELPER method functions
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
def istext(filename):
|
||||
"""
|
||||
A guess if a file is text or binary
|
||||
"""
|
||||
s = open(filename).read(512)
|
||||
text_characters = "".join(map(chr, range(32, 127)) + list("\n\r\t\b"))
|
||||
_null_trans = string.maketrans("", "")
|
||||
if not s:
|
||||
# Empty files are considered text
|
||||
return True
|
||||
if "\0" in s:
|
||||
# Files with null bytes are likely binary
|
||||
return False
|
||||
# Get the non-text characters (maps a character to itself then
|
||||
# use the 'remove' option to get rid of the text characters.)
|
||||
t = s.translate(_null_trans, text_characters)
|
||||
# If more than 30% non-text characters, then
|
||||
# this is considered a binary file
|
||||
if float(len(t)) / float(len(s)) > 0.30:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def display_cached_value(cache, cache_key):
|
||||
try:
|
||||
cached_value = cache[cache_key]
|
||||
print("{0}={1}".format(cache_key, cached_value))
|
||||
except KeyError:
|
||||
print("{0}=Not in cache".format(cache_key))
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------
|
||||
# master.py
|
||||
# Allows for project based setup to be used with noodly
|
||||
# version: 0.1
|
||||
# author: Gallowj
|
||||
# -------------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------
|
||||
import os
|
||||
|
||||
from unipath import Path
|
||||
|
||||
_G_DEFAULT_PROJECT_DIR = os.getcwd()
|
||||
_G_MASTER_ROOT_NODE = None
|
||||
|
||||
|
||||
@property
|
||||
def _G_DEFAULT_PROJECT_DIR(value):
|
||||
_G_DEFAULT_PROJECT_DIR = value
|
||||
return _G_DEFAULT_PROJECT_DIR
|
||||
|
||||
|
||||
def set_PROJECT_DIR(value):
|
||||
"""Sets and returns _G_DEFAULT_PROJECT_DIR"""
|
||||
global _G_DEFAULT_PROJECT_DIR
|
||||
_G_DEFAULT_PROJECT_DIR = Path(value).expand()
|
||||
return Path(_G_DEFAULT_PROJECT_DIR)
|
||||
|
||||
|
||||
@property
|
||||
def _G_MASTER_ROOT_NODE(value):
|
||||
_G_MASTER_ROOT_NODE = value
|
||||
return _G_MASTER_ROOT_NODE
|
||||
|
||||
|
||||
def set_MASTER_ROOT_NODE(value):
|
||||
"""Sets and returns _G_MASTER_ROOT_NODE"""
|
||||
global _G_MASTER_ROOT_NODE
|
||||
if not isinstance(inNode, ProjectRootNode):
|
||||
raise TypeError('self._projectRootNode is: {0}\r'
|
||||
'A _projectRootNode, needs to be properly set\r'
|
||||
'So that we can acces:\r'
|
||||
'\tself._projectRootNode._sourceRoot\r'
|
||||
'\tself._projectRootNode._overrideRoot\r'
|
||||
'Use self.assignProjectRootNode(<projectRootNode>)\r'
|
||||
''.format(type(inNode)))
|
||||
|
||||
_G_MASTER_ROOT_NODE = inNode
|
||||
return _G_MASTER_ROOT_NODE
|
||||
|
||||
|
||||
###########################################################################
|
||||
# tests(), code block for testing module
|
||||
# -------------------------------------------------------------------------
|
||||
def tests():
|
||||
set_PROJECT_DIR(os.getcwd())
|
||||
print(_G_DEFAULT_PROJECT_DIR)
|
||||
print(_G_DEFAULT_PROJECT_DIR.parent)
|
||||
print(_G_DEFAULT_PROJECT_DIR.components())
|
||||
|
||||
# NOT implemented yet
|
||||
# set_PROJECT_DIR
|
||||
|
||||
return
|
||||
|
||||
|
||||
###########################################################################
|
||||
# --call block-------------------------------------------------------------
|
||||
if __name__ == "__main__":
|
||||
print ("# ----------------------------------------------------------------------- #")
|
||||
print ('~ noodly.master ... Running script as __main__')
|
||||
print ("# ----------------------------------------------------------------------- #\r")
|
||||
|
||||
# run simple tests
|
||||
tests()
|
||||
|
||||
#_G_DEFAULT_PROJECT_DIR = Path(os.getcwd())
|
||||
print(_G_DEFAULT_PROJECT_DIR.components())
|
||||
+623
@@ -0,0 +1,623 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# this has to be at the beginning
|
||||
from __future__ import division
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------
|
||||
# node.py
|
||||
# simple base Node Class, for tool creation.
|
||||
# version: 0.1
|
||||
# author: Gallowj
|
||||
# -------------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------
|
||||
"""
|
||||
Module docstring:
|
||||
A Simple Node Base Class Module, for creating basic nodes within a hierarchy.
|
||||
"""
|
||||
|
||||
__author__ = 'HogJonny'
|
||||
|
||||
_G_DEBUG = True # global state for debugging
|
||||
_G_SETTINGS = None # global Settings storage
|
||||
_G_LOG = None # global LOGger storage
|
||||
|
||||
_G_MASTER_NODE = None # We intend to init a master node, unless provided
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# built-ins
|
||||
import os
|
||||
import copy
|
||||
import traceback
|
||||
import string
|
||||
import logging
|
||||
|
||||
# using hashids to generate unique name identifiers for nodes
|
||||
import hashids
|
||||
import cachetools
|
||||
from sched import scheduler
|
||||
|
||||
# local imports
|
||||
from LyPy.si_shared.noodly.helpers import display_cached_value
|
||||
from LyPy.si_shared.noodly.find_arg import find_arg
|
||||
from LyPy.si_shared.noodly.synth import synthesize
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# quick test code (remove later)
|
||||
from hashids import Hashids
|
||||
hashids = Hashids(min_length=16, salt='DCCsi')
|
||||
if _G_DEBUG:
|
||||
print (hashids.encrypt(193487)) # test hash
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# set up logger
|
||||
_G_LOGGER = logging.getLogger(__name__)
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Use unicode strings
|
||||
_base = str # Python 3 str (=unicode), or Python 2 bytes.
|
||||
if os.path.supports_unicode_filenames:
|
||||
try:
|
||||
_base = unicode # Python 2 unicode.
|
||||
except NameError:
|
||||
pass
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
###########################################################################
|
||||
# HELPER method functions
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
def return_node_from_hashid(hashid):
|
||||
if not isinstance(hashid, str):
|
||||
raise TypeError("{0},{1}: Accepts hashids as str types!\r"
|
||||
"Input hashid:{2}\r"
|
||||
"".format('noodly',
|
||||
'return_node_from_hashid(hashid)',
|
||||
type(hashid)))
|
||||
|
||||
temp_node = Node(temp_node=True).get_sibling_node_from_hashid('{0}'.format(hashid))
|
||||
|
||||
return temp_node
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
class Node(object):
|
||||
"""Class constructor: makes a node."""
|
||||
|
||||
# share the debug state
|
||||
_DEBUG = _G_DEBUG
|
||||
|
||||
# logger
|
||||
_LOGGER = _G_LOGGER
|
||||
|
||||
# class header
|
||||
message_header = 'noodly, Node(): Message'
|
||||
|
||||
# class variable
|
||||
_cls_node_count = 0
|
||||
_cls_node_list = []
|
||||
_cls_node_dict = {}
|
||||
|
||||
# --BASE-METHODS-------------------------------------------------------
|
||||
# --constructor-
|
||||
def __init__(self, node_name=None, parent_node=None, *args, **kwargs):
|
||||
|
||||
self._logger = Node._LOGGER
|
||||
|
||||
self._node_type = self.__class__.__name__
|
||||
|
||||
# a dict to store properties/attrs
|
||||
# in the event an object is re-built / re-init
|
||||
# it is important to store anything here that needs retention
|
||||
self._kwargs_dict = {}
|
||||
self._children = []
|
||||
|
||||
# private local access to the cls_node_list
|
||||
self._cls_node_list = Node._cls_node_list
|
||||
|
||||
# -- secret keyword -----------------------------------------------
|
||||
self._temp_node = False
|
||||
temp_node, kwargs = find_arg(argPosIndex=None, argTag='temp_node',
|
||||
removeKwarg=True, inArgs=args,
|
||||
inKwargs=kwargs) # <-- kwarg only
|
||||
self._temp_node = temp_node
|
||||
if self._temp_node:
|
||||
self._kwargs_dict['temp_node'] = self._temp_node
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
# -- store message header -----------------------------------------
|
||||
# setup the .message_header <-- kwarg only
|
||||
message_header, kwargs = find_arg(argPosIndex=None, argTag='message_header',
|
||||
removeKwarg=True, inArgs=args, inKwargs=kwargs,
|
||||
defaultValue=('{0}(), Message'
|
||||
.format(self._node_type)))
|
||||
self._message_header = message_header
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
# -- hashid -------------------------------------------------------
|
||||
self._name_is_uni_hashid = False
|
||||
self._node_class_index = len(Node._cls_node_list)
|
||||
if Node._DEBUG:
|
||||
print ('__init__.node_class_index: {0}'.format(self._node_class_index))
|
||||
self._uni_hashid = hashids.encrypt(self._node_class_index)
|
||||
if Node._DEBUG:
|
||||
print ('__init__.uni_hashid: {0}'.format(self._uni_hashid))
|
||||
|
||||
# update class dict
|
||||
if not self._temp_node:
|
||||
Node._cls_node_dict[self._uni_hashid] = self
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
# -- store the node name ------------------------------------------
|
||||
self._node_name = node_name
|
||||
if (self._node_class_index == 0 and self._node_name == None):
|
||||
self._node_name = 'MASTER'
|
||||
elif (self._node_class_index > 0 and self._node_name == None):
|
||||
# set a default node_name if none, based on the unihashid
|
||||
if not self._name_is_uni_hashid:
|
||||
self._node_name = self._uni_hashid
|
||||
self._name_is_uni_hashid = True
|
||||
if Node._DEBUG:
|
||||
print ('__init__.node_name: {0}'.format(self._node_name))
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
# -- node parent_node --------------------------------------------------
|
||||
# set up the parent_node property
|
||||
self._parent_node = parent_node
|
||||
|
||||
if self._parent_node != None:
|
||||
# add this node, to the parent_nodes list of children
|
||||
try:
|
||||
self._parent_node.add_child(self)
|
||||
except:
|
||||
pass # <-- parent_node object passed is NOT a noodly.node?
|
||||
|
||||
# Update class variables
|
||||
Node.cls_node_count_up(self)
|
||||
Node.cls_node_list_append(self)
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# arbitrary argument properties
|
||||
# check postions *args and **kwargs
|
||||
# any kwargs left will be used to synthesize a property
|
||||
try:
|
||||
# checking import due to the way the code is structured
|
||||
# the code was passing even if module was not imported
|
||||
synthesize
|
||||
synthExists = True
|
||||
except Exception as e:
|
||||
print(e)
|
||||
raise e
|
||||
|
||||
for key, value in kwargs.items():
|
||||
self._kwargs_dict[key] = value
|
||||
try:
|
||||
synthesize(self, key, value)
|
||||
except Exception as e: # <-- maybe it can't synthesize?
|
||||
# in which case fall back to setting the property
|
||||
print(e)
|
||||
code = compile(r'self._{0}={1}'.format(key, value), 'synthProp', 'exec')
|
||||
pass
|
||||
if Node._DEBUG:
|
||||
print("{0}:{1}".format(key, value))
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
# if temp node, adjust the class counter
|
||||
if temp_node:
|
||||
self.cls_node_count_down()
|
||||
self.cls_node_list_remove()
|
||||
|
||||
# -- properties ------------------------------------------------------------
|
||||
@property
|
||||
def logger(self):
|
||||
return self._logger
|
||||
|
||||
@logger.setter
|
||||
def logger(self, logger):
|
||||
self._logger = logger
|
||||
return self._logger
|
||||
|
||||
@logger.getter
|
||||
def logger(self):
|
||||
return self._logger
|
||||
|
||||
@property
|
||||
def kwargs_dict(self):
|
||||
return self._kwargs_dict
|
||||
|
||||
@kwargs_dict.getter
|
||||
def kwargs_dict(self):
|
||||
return self._kwargs_dict
|
||||
|
||||
@property
|
||||
def message_header(self):
|
||||
return self._message_header
|
||||
|
||||
@message_header.setter
|
||||
def message_header(self, message_header):
|
||||
self._message_header = message_header
|
||||
return self._message_header
|
||||
|
||||
@property
|
||||
def node_type(self):
|
||||
return self._node_type
|
||||
|
||||
@node_type.setter
|
||||
def node_type(self, node_type):
|
||||
self._node_type = node_type
|
||||
return self._node_type
|
||||
|
||||
@node_type.getter
|
||||
def node_type(self):
|
||||
return self._node_type
|
||||
|
||||
@property
|
||||
def temp_node(self):
|
||||
return self._temp_node
|
||||
|
||||
@temp_node.setter
|
||||
def temp_node(self, temp_node):
|
||||
self._temp_node = temp_node
|
||||
return self._temp_node
|
||||
|
||||
@temp_node.getter
|
||||
def temp_node(self):
|
||||
return self._temp_node
|
||||
|
||||
@property
|
||||
def node_name(self):
|
||||
return self._node_name
|
||||
|
||||
@node_name.setter
|
||||
def node_name(self, nameStr):
|
||||
if nameStr != None:
|
||||
if not isinstance(nameStr, str):
|
||||
raise TypeError("{0}, {1}: Accepts str types!"
|
||||
"".format(self.__class__.__name__,
|
||||
self._node_name.__name__))
|
||||
try:
|
||||
self._node_name = nameStr
|
||||
except:
|
||||
synthesize(self, '_node_name', None)
|
||||
|
||||
return self._node_name
|
||||
|
||||
@node_name.getter
|
||||
def node_name(self):
|
||||
return self._node_name
|
||||
|
||||
@property
|
||||
def name_is_uni_hashid(self):
|
||||
return self._name_is_uni_hashid
|
||||
|
||||
@node_type.setter
|
||||
def name_is_uni_hashid(self, value):
|
||||
self._name_is_uni_hashid = value
|
||||
return self._name_is_uni_hashid
|
||||
|
||||
@node_type.getter
|
||||
def name_is_uni_hashid(self):
|
||||
return self._name_is_uni_hashid
|
||||
|
||||
@property
|
||||
def node_class_index(self):
|
||||
return self._node_class_index
|
||||
|
||||
@property
|
||||
def uni_hashid(self):
|
||||
return self._uni_hashid
|
||||
|
||||
@property
|
||||
def cls_node_dict(self):
|
||||
return Node._cls_node_dict
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# --method-set---------------------------------------------------------
|
||||
def cls_node_count_up(self):
|
||||
Node._cls_node_count += 1
|
||||
return Node._cls_node_count
|
||||
|
||||
def cls_node_count_down(self):
|
||||
Node._cls_node_count -= 1
|
||||
return Node._cls_node_count
|
||||
|
||||
def cls_node_list_append(self):
|
||||
Node._cls_node_list.append(self)
|
||||
return Node._cls_node_list
|
||||
|
||||
def cls_node_list_remove(self):
|
||||
Node._cls_node_list.remove(self)
|
||||
return Node._cls_node_list
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
# --method-set---------------------------------------------------------
|
||||
@property
|
||||
def parent_node(self):
|
||||
return self._parent_node
|
||||
|
||||
@parent_node.setter
|
||||
def parent_node(self, parent_node):
|
||||
self._parent_node = parent_node
|
||||
return self._parent_node
|
||||
|
||||
@parent_node.getter
|
||||
def parent_node(self):
|
||||
return self._parent_node
|
||||
|
||||
def add_child(self, child):
|
||||
self._children.append(child)
|
||||
|
||||
# --method--
|
||||
def remove_child(self, child):
|
||||
self._children.remove(child)
|
||||
|
||||
@property
|
||||
def children(self):
|
||||
return self._children
|
||||
|
||||
def child(self, row):
|
||||
return self._children[row]
|
||||
|
||||
def child_count(self):
|
||||
return len(self._children)
|
||||
|
||||
def row(self):
|
||||
if self._parent_node != None:
|
||||
return self._parent_node._children.index(self)
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
# --method-------------------------------------------------------------
|
||||
def get_sibling_node_from_hashid(self, hashid):
|
||||
if not isinstance(hashid, str):
|
||||
raise TypeError("{0}.{1}: Accepts hashids as str types!"
|
||||
"".format(self.__class__.__name__,
|
||||
'get_sibling_node_from_hashid(hashid)'))
|
||||
|
||||
if hashid in self.cls_node_dict.keys():
|
||||
return self.cls_node_dict[hashid]
|
||||
else:
|
||||
return None
|
||||
|
||||
# --method-------------------------------------------------------------
|
||||
def clear_node_dep(self):
|
||||
self.clear_children
|
||||
self.clear_node_list()
|
||||
self.clear_node_count()
|
||||
return self
|
||||
|
||||
def clear_node_list(self):
|
||||
Node._cls_node_list = []
|
||||
return Node._cls_node_list
|
||||
|
||||
def clear_node_count(self):
|
||||
Node._cls_node_count = 0
|
||||
return Node._cls_node_count
|
||||
|
||||
def clear_children(self):
|
||||
self._children = []
|
||||
return self._children
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
@cachetools.cached(cachetools.LFUCache(maxsize=2048))
|
||||
def cache_node(self):
|
||||
return self
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
# --method-------------------------------------------------------------
|
||||
def hierarchy(self, tab_level=-1):
|
||||
|
||||
output = ''
|
||||
tab_level += 1
|
||||
|
||||
for i in range(tab_level):
|
||||
output += '\t'
|
||||
|
||||
output += ('{tab}/------node_name:: "{0}"\n'
|
||||
'{1} |type:: {2}\n'
|
||||
'{1} |_uni_hashid:: "{3}"\r'
|
||||
''.format(self._node_name,
|
||||
'\t' * tab_level,
|
||||
self._node_type,
|
||||
self._uni_hashid,
|
||||
tab=tab_level))
|
||||
|
||||
# TO DO:: object hierarchy "'hips'|'rightLeg'|'etc'"
|
||||
|
||||
for child in self._children:
|
||||
output += child.hierarchy(tab_level)
|
||||
|
||||
tab_level -= 1
|
||||
# output += '\n'
|
||||
|
||||
return output
|
||||
|
||||
def log_hierarchy(self):
|
||||
# Not implemented
|
||||
self._logger.autolog(self.hierarchy(),
|
||||
"return_node_from_hashid('{0}').log_hierarchy()"
|
||||
"".format(self._uni_hashid()))
|
||||
return
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
# --method-------------------------------------------------------------
|
||||
# representation
|
||||
def __str__(self):
|
||||
'''Returns a nice string representation of the object.'''
|
||||
|
||||
# TO DO: need to improve this
|
||||
if self.node_name == None or self.name_is_uni_hashid == True:
|
||||
# open parenthesis only
|
||||
output = ("{0}(node_name='{1}'"
|
||||
"".format(self.__class__.__name__,
|
||||
self.uni_hashid))
|
||||
else:
|
||||
output = ("{0}(node_name='{1}'"
|
||||
"".format(self.__class__.__name__,
|
||||
self.node_name))
|
||||
|
||||
if self.parent_node != None:
|
||||
output += (", parent_node=return_node_from_hashid('{0}')"
|
||||
"".format(self.parent_node.uni_hashid))
|
||||
|
||||
if len(self.kwargs_dict) > 0:
|
||||
for key, value in self.kwargs_dict.items():
|
||||
if not isinstance(value, str):
|
||||
output += (", {0}={1}".format(key, value))
|
||||
else: # if a str add the extra quotes
|
||||
output += (", {0}='{1}'".format(key, value))
|
||||
|
||||
# add the close parenthesis
|
||||
output += ')'
|
||||
|
||||
if self.name_is_uni_hashid == True or self.temp_node:
|
||||
output = ("return_node_from_hashid('{0}')"
|
||||
"".format(self.node_name))
|
||||
|
||||
# Node(self, node_name, parent_node=None, path='')
|
||||
return output
|
||||
|
||||
# representation
|
||||
def __repr__(self):
|
||||
return '{0}({1})\r'.format(self.__class__.__name__, self.__dict__)
|
||||
# --Class End--------------------------------------------------------------
|
||||
|
||||
|
||||
class ClassProperty(property):
|
||||
"""Decorator"""
|
||||
|
||||
def __get__(self, cls, owner):
|
||||
return self.fget.__get__(None, owner)()
|
||||
# --Class End--------------------------------------------------------------
|
||||
|
||||
|
||||
###########################################################################
|
||||
# tests(), code block for testing module
|
||||
# -------------------------------------------------------------------------
|
||||
def tests():
|
||||
default_node = Node()
|
||||
print(str(default_node))
|
||||
print(repr(default_node))
|
||||
print(default_node.uni_hashid)
|
||||
print(default_node.kwargs_dict)
|
||||
print(default_node.message_header)
|
||||
print(default_node.node_name)
|
||||
print(default_node.parent_node)
|
||||
print(default_node.node_class_index)
|
||||
# print(default_node.cls_node_dict)
|
||||
|
||||
# temp_node = Node()
|
||||
temp_node = Node(temp_node=True)
|
||||
print(temp_node)
|
||||
|
||||
test_default_node = Node(temp_node=True).get_sibling_node_from_hashid('kxYLm0XQeXJ7jWaP')
|
||||
print(test_default_node)
|
||||
|
||||
another_test = return_node_from_hashid('kxYLm0XQeXJ7jWaP')
|
||||
print(another_test)
|
||||
|
||||
second_node = Node(node_name='foo', parent_node=default_node)
|
||||
print(str(second_node))
|
||||
print(second_node.uni_hashid)
|
||||
print(second_node.kwargs_dict)
|
||||
print(second_node.message_header)
|
||||
print(second_node.node_name)
|
||||
print(second_node.parent_node.node_name)
|
||||
print(second_node.node_class_index)
|
||||
# print(second_node.cls_node_dict)
|
||||
|
||||
second_child = Node(node_name='fooey', parent_node=another_test)
|
||||
print(str(second_child))
|
||||
print(second_child.uni_hashid)
|
||||
print(second_child.kwargs_dict)
|
||||
print(second_child.message_header)
|
||||
print(second_child.node_name)
|
||||
print(second_child.parent_node.node_name)
|
||||
print(second_child.node_class_index)
|
||||
# print(second_child.cls_node_dict)
|
||||
|
||||
third_node = Node(node_name='kablooey', parent_node=second_node,
|
||||
message_header='Node(): CUSTOM Message')
|
||||
print(str(third_node))
|
||||
print(third_node.uni_hashid)
|
||||
print(third_node.kwargs_dict)
|
||||
print(third_node.message_header)
|
||||
print(third_node.node_name)
|
||||
print(third_node.parent_node.node_name)
|
||||
print(third_node.node_class_index)
|
||||
|
||||
fourth_node = Node(parent_node=second_node,
|
||||
message_header='Node(): CUSTOM Message')
|
||||
print(str(fourth_node))
|
||||
print(fourth_node.uni_hashid)
|
||||
print(fourth_node.kwargs_dict)
|
||||
print(fourth_node.message_header)
|
||||
print(fourth_node.node_name)
|
||||
print(fourth_node.parent_node.node_name)
|
||||
print(fourth_node.node_class_index)
|
||||
|
||||
kwarg_test_child = Node(node_name='kwargChild', parent_node=default_node, garble=1001001) # custom kwarg
|
||||
print(str(kwarg_test_child))
|
||||
print(kwarg_test_child.uni_hashid)
|
||||
print(kwarg_test_child.node_name)
|
||||
print(kwarg_test_child.parent_node.node_name)
|
||||
print(kwarg_test_child.kwargs_dict)
|
||||
# check the custom arg/property garble
|
||||
print(kwarg_test_child.garble)
|
||||
|
||||
# check the node hierarchy
|
||||
print(default_node.hierarchy())
|
||||
|
||||
# retreive a node from it's known hashid
|
||||
master_node = return_node_from_hashid('kxYLm0XQeXJ7jWaP')
|
||||
print(master_node.uni_hashid) # verify hasid
|
||||
# should return the same node as default_node
|
||||
print(master_node.node_name) # should be 'MASTER'
|
||||
return
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
def cache_tests():
|
||||
cache = cachetools.LFUCache(maxsize=128)
|
||||
runner = scheduler()
|
||||
cache["HogJonny"] = 1001001
|
||||
runner.enter(2, 1, display_cached_value,
|
||||
kwargs={'cache': cache, 'cache_key': 'HogJonny'})
|
||||
runner.enter(6, 1, display_cached_value,
|
||||
kwargs={'cache': cache, 'cache_key': 'HogJonny'})
|
||||
runner.run()
|
||||
return
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
def main():
|
||||
return
|
||||
# - END, main() --
|
||||
|
||||
|
||||
###########################################################################
|
||||
# --call block-------------------------------------------------------------
|
||||
if __name__ == "__main__":
|
||||
print ("# ----------------------------------------------------------------------- #")
|
||||
print ('~ noodly.Node ... Running script as __main__')
|
||||
print ("# ----------------------------------------------------------------------- #\r")
|
||||
|
||||
# run simple tests
|
||||
tests()
|
||||
|
||||
cache_tests()
|
||||
+407
@@ -0,0 +1,407 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# this has to be at the beginning
|
||||
from __future__ import division
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------
|
||||
# pathnode.py
|
||||
# simple path objecy based Node Class, for tool creation.
|
||||
# version: 0.1
|
||||
# author: Gallowj
|
||||
# -------------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------
|
||||
"""
|
||||
Module docstring:
|
||||
A simple path objecy based Node Class, for creating path hierarchies.
|
||||
"""
|
||||
__author__ = 'HogJonny'
|
||||
|
||||
_G_DEBUG = True # global state for debugging
|
||||
_G_SETTINGS = None # global Settings storage
|
||||
_G_LOG = None # global LOGger storage
|
||||
|
||||
_G_MASTER_NODE = None # We intend to init a master node
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# built-ins
|
||||
import os
|
||||
import copy
|
||||
import subprocess
|
||||
import traceback
|
||||
import string
|
||||
import logging
|
||||
from unipath import Path, AbstractPath
|
||||
|
||||
# local ly imports
|
||||
from LyPy.si_shared.noodly.helpers import istext
|
||||
from LyPy.si_shared.noodly.find_arg import find_arg
|
||||
from LyPy.si_shared.noodly.synth import synthesize
|
||||
from LyPy.si_shared.noodly.node import Node
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Use unicode strings
|
||||
_base = str # Python 3 str (=unicode), or Python 2 bytes.
|
||||
if os.path.supports_unicode_filenames:
|
||||
try:
|
||||
_base = unicode # Python 2 unicode.
|
||||
except NameError:
|
||||
pass
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# set up logger
|
||||
_G_LOGGER = logging.getLogger(__name__)
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
class PathNode(Node):
|
||||
"""doc string"""
|
||||
|
||||
# share the debug state
|
||||
_DEBUG = _G_DEBUG
|
||||
|
||||
# logger
|
||||
_LOGGER = _G_LOGGER
|
||||
|
||||
# class header
|
||||
_message_header = 'noodly, PathNode(): Message'
|
||||
|
||||
# App Launcher paths...
|
||||
try:
|
||||
_maya_exe_path = Path(os.environ['MAYAPY'])
|
||||
except:
|
||||
_maya_exe_path = Path(r"C:\Program Files\Autodesk\Maya2019\bin\maya.exe")
|
||||
try:
|
||||
_notepad_exe_path = Path(os.environ['DEFAULT_TXT_EXE'])
|
||||
except:
|
||||
_notepad_exe_path = Path(r"C:\Program Files (x86)\Notepad++\notepad++.exe")
|
||||
|
||||
# --BASE-METHODS-------------------------------------------------------
|
||||
def __new__(cls, path="", root_path=None, *args, **kwargs):
|
||||
'''docstring'''
|
||||
# if not isinstance(path, str) and not isinstance(path, Path):
|
||||
# raise TypeError("{0}, {1}: Accepts paths as str or Path() types!\r"
|
||||
# "Input data is:{2}\r"
|
||||
# "".format('noodly, PathNode',
|
||||
# 'PathNode(filename)', type(path)))
|
||||
#
|
||||
self = super(PathNode, cls).__new__(cls)
|
||||
return self
|
||||
|
||||
# --constructor--------------------------------------------------------
|
||||
def __init__(self, path="", root_path=None, parent_is_root=None,
|
||||
name_is_path=None, *args, **kwargs):
|
||||
|
||||
self._logger = Node._LOGGER
|
||||
|
||||
self._node_type = self.__class__.__name__
|
||||
|
||||
# a dict to store properties/attrs
|
||||
# in the event an object is re-built / re-init
|
||||
# it is important to store anything here that needs retention
|
||||
self._kwargs_dict = {}
|
||||
|
||||
# -- secret keyword -----------------------------------------------
|
||||
self._temp_node = False
|
||||
temp_node, kwargs = find_arg(argPosIndex=None, argTag='temp_node',
|
||||
removeKwarg=True, inArgs=args,
|
||||
inKwargs=kwargs) # <-- kwarg only
|
||||
|
||||
self._temp_node = temp_node
|
||||
if self._temp_node:
|
||||
self.k_wargs_dict['temp_node'] = self._temp_node
|
||||
|
||||
# -- Node class args/kwargs ---------------------------------------
|
||||
node_name, kwargs = find_arg(argPosIndex=2, argTag='node_name',
|
||||
removeKwarg=True, inArgs=args,
|
||||
inKwargs=kwargs) # <-- third arg, kwarg
|
||||
|
||||
parent_node, kwargs = find_arg(argPosIndex=3, argTag='parent_node',
|
||||
removeKwarg=True, inArgs=args,
|
||||
inKwargs=kwargs) # <-- fourth arg, kwarg
|
||||
|
||||
self._root_path = root_path
|
||||
|
||||
self._parent_is_root = parent_is_root
|
||||
if self._parent_is_root != None:
|
||||
self._kwargs_dict['parent_is_root'] = self.parent_is_root
|
||||
|
||||
if parent_is_root: # <-- do it
|
||||
self._root_path = parent_node
|
||||
|
||||
# make sure the path is a Path
|
||||
self._path = path
|
||||
if not isinstance(self._path, Path):
|
||||
try:
|
||||
self._path = Path(path)
|
||||
except:
|
||||
self._path = Path() # empty path object fallback
|
||||
|
||||
self._name_is_path = name_is_path
|
||||
if self._name_is_path:
|
||||
self._kwargs_dict['name_is_path'] = self._name_is_path
|
||||
|
||||
# this might only work if the file actually exists
|
||||
self._node_name = node_name
|
||||
if self._name_is_path:
|
||||
if self._path.name != None or self._path.name != '':
|
||||
self._node_name = str(self._path.name)
|
||||
|
||||
# Path.__init__(self)
|
||||
super(PathNode, self).__init__(self._node_name, parent_node,
|
||||
temp_node=temp_node,
|
||||
*args, **kwargs)
|
||||
|
||||
# -- properties -------------------------------------------------------
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
return self._path
|
||||
|
||||
@path.setter
|
||||
def path(self, path):
|
||||
self._path = path
|
||||
return self._path
|
||||
|
||||
@path.getter
|
||||
def path(self):
|
||||
return self._path
|
||||
|
||||
@property
|
||||
def root_path(self):
|
||||
return self._root_path
|
||||
|
||||
@root_path.setter
|
||||
def root_path(self, root_path):
|
||||
self._root_path = root_path
|
||||
return self._root_path
|
||||
|
||||
@root_path.getter
|
||||
def root_path(self):
|
||||
return self._root_path
|
||||
|
||||
@property
|
||||
def parent_is_root(self):
|
||||
return self._parent_is_root
|
||||
|
||||
@parent_is_root.setter
|
||||
def parent_is_root(self, parent_is_root):
|
||||
self._parent_is_root = parent_is_root
|
||||
return self._parent_is_root
|
||||
|
||||
@parent_is_root.getter
|
||||
def parent_is_root(self):
|
||||
return self._parent_is_root
|
||||
|
||||
@property
|
||||
def name_is_path(self):
|
||||
return self._name_is_path
|
||||
|
||||
# @name_is_path.setter
|
||||
# def name_is_path(self, name_is_path):
|
||||
# self._name_is_path = name_is_path
|
||||
# return self._name_is_path
|
||||
|
||||
@name_is_path.getter
|
||||
def name_is_path(self):
|
||||
return self._name_is_path
|
||||
|
||||
# --method-------------------------------------------------------------
|
||||
def set_file_path(self, path):
|
||||
if not isinstance(path, Path):
|
||||
try:
|
||||
path = Path(path)
|
||||
except:
|
||||
raise TypeError("must be Path compatible")
|
||||
|
||||
# retreive a copy of the old _kwargs dict
|
||||
_kwargs_dict_copy = copy.copy(self._kwargs_dict)
|
||||
_name_is_uni_hashid = copy.copy(self.name_is_uni_hashid)
|
||||
|
||||
# create a new me (self), with new value
|
||||
# attempt to keep existing attrs/settings
|
||||
self = PathNode(path=path,
|
||||
root_path=self.root_path,
|
||||
parent_is_root=self.parent_is_root,
|
||||
name_is_path=self.name_is_path,
|
||||
temp_node=self.temp_node,
|
||||
node_name=self.node_name,
|
||||
parent_node=self.parent_node,
|
||||
name_is_uni_hashid=self.name_is_uni_hashid)
|
||||
|
||||
# now we need to restore any custom properties on the replacement object
|
||||
for key, value in _kwargs_dict_copy.items():
|
||||
self._kwargs_dict[key] = value
|
||||
try:
|
||||
synthesize(self, '{0}'.format(key), value)
|
||||
except:
|
||||
code = compile('self._{0} = {1}'.format(key, value), 'synthProp', 'exec')
|
||||
if Node._DEBUG:
|
||||
self.logger.error('can not set: self._{0} = {1}'.format(key, value))
|
||||
|
||||
# replace myself in the class nodeDict, based on my unihashid
|
||||
self.cls_node_dict[self.uni_hashid] = self
|
||||
|
||||
# return the new version of myself
|
||||
return self.cls_node_dict[self.uni_hashid]
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
# --method-------------------------------------------------------------
|
||||
def start_file(self, filepath=None):
|
||||
'''opens the file in the prefered os editor for the filetype'''
|
||||
if filepath == None:
|
||||
filepath = self.path
|
||||
|
||||
if not isinstance(filepath, Path): # <-any subclass of Path works?
|
||||
filepath = Path(filepath)
|
||||
|
||||
self.logger.debug('starting file: {0}'.format(filepath))
|
||||
try:
|
||||
os.startfile(filepath)
|
||||
except IOError as e:
|
||||
self.logger.error(e)
|
||||
|
||||
return filepath
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
# --method-------------------------------------------------------------
|
||||
def explore_file(self, filepath=None):
|
||||
if filepath == None:
|
||||
filepath = self.path
|
||||
|
||||
if not isinstance(filepath, Path):
|
||||
filepath = Path(filepath)
|
||||
|
||||
self.logger.debug('exploring file: {0}'.format(filepath))
|
||||
if filepath.exists():
|
||||
try:
|
||||
subprocess.Popen(r'explorer /select,"{0}"'.format(filepath))
|
||||
except IOError as e:
|
||||
self.logger.error(e)
|
||||
else:
|
||||
self.logger.error('file does not exist: {0}'.format(filepath))
|
||||
|
||||
return filepath
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
# --method-------------------------------------------------------------
|
||||
def hierarchy(self, tabLevel=-1):
|
||||
|
||||
output = ''
|
||||
if isinstance(self, RootNode):
|
||||
if gDebug:
|
||||
func = inspect.currentframe().f_back.f_code
|
||||
output += ('{0}Called from:\n'
|
||||
'{0}{1}\n'.format('\t' * (tabLevel + 1), func))
|
||||
|
||||
tabLevel += 1
|
||||
|
||||
for i in range(tabLevel):
|
||||
output += '\t'
|
||||
|
||||
output += ('{tab}/------ nodeName:: "{0}"\n'
|
||||
'{1} |typeInfo:: {2}\n'
|
||||
'{1} |_uniHashid:: "{3}"\r'
|
||||
'{1} |path:: "{4}"\n'
|
||||
'{1} |get_root():: "{5}"\n'
|
||||
'{1} |getPathFromRoot():: "{6}"\n'
|
||||
''.format(self.getNodeName(),
|
||||
'\t' * tabLevel,
|
||||
self.get_typeInfo(),
|
||||
self.get_uniHashid(),
|
||||
self,
|
||||
self.get_root(),
|
||||
self.getPathFromRoot(),
|
||||
tab=tabLevel))
|
||||
|
||||
for child in self._children:
|
||||
output += child.hierarchy(tabLevel)
|
||||
|
||||
tabLevel -= 1
|
||||
|
||||
return output
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
# --Class End--------------------------------------------------------------
|
||||
|
||||
|
||||
###########################################################################
|
||||
# tests(), code block for testing module
|
||||
# -------------------------------------------------------------------------
|
||||
def tests():
|
||||
from node import Node
|
||||
default_node = Node() # result: Node(node_name='MASTER')
|
||||
print(default_node)
|
||||
|
||||
first_child = PathNode(path=None, node_name='first_child', parent_node=default_node)
|
||||
print(first_child)
|
||||
# result: PathNode(temp_node=True, parent_node=Node(node_name='MASTER')).siblingNodeFromHashid('WNPZoKBVpXV16QLz')
|
||||
# first_child.nodeType
|
||||
# first_child.parent_node
|
||||
# first_child.node_name
|
||||
|
||||
try:
|
||||
# PathNode requires a arg 'path' input (should be a path str)
|
||||
fubar_path_node = PathNode() # <-- this should fail
|
||||
print (fubar_path_node)
|
||||
except Exception as err:
|
||||
print ('\r{0}'.format(err))
|
||||
print (traceback.format_exc())
|
||||
|
||||
foo = PathNode(r'\foo\fooey\kablooey', node_name='foo',
|
||||
parent_node=default_node)
|
||||
print(foo)
|
||||
|
||||
testes = Path(r'/foo/fooey/kablooey')
|
||||
|
||||
print(foo.path.exists())
|
||||
print(foo.path.parent)
|
||||
print(foo.path.norm_case())
|
||||
print(foo.path.absolute())
|
||||
|
||||
fooey = PathNode(None, parent_node=foo)
|
||||
print(fooey)
|
||||
|
||||
kablooey = PathNode(r'\foo\fooey\kablooey',
|
||||
parent_node=default_node,
|
||||
name_is_path=True)
|
||||
print(kablooey)
|
||||
|
||||
kablooey = kablooey.set_file_path(r'c:\mytemp\fubar.txt')
|
||||
print(kablooey)
|
||||
kablooey.start_file()
|
||||
kablooey.explore_file()
|
||||
|
||||
return
|
||||
# - END, tests() ----------------------------------------------------------
|
||||
|
||||
|
||||
def main():
|
||||
pass
|
||||
return
|
||||
# - END, main() -----------------------------------------------------------
|
||||
|
||||
|
||||
###########################################################################
|
||||
# --call block-------------------------------------------------------------
|
||||
if __name__ == "__main__":
|
||||
print ("# ----------------------------------------------------------------------- #")
|
||||
print ('~ noodly.PathNode ... Running script as __main__')
|
||||
print ("# ----------------------------------------------------------------------- #\r")
|
||||
|
||||
# run simple tests
|
||||
tests()
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# synth.py
|
||||
# Convenience module for a standardized attr interface for classes/objects.
|
||||
# version: 0.1
|
||||
# date: 11/14/2013
|
||||
# author: jGalloway
|
||||
# -------------------------------------------------------------------------
|
||||
__author__ = 'HogJonny'
|
||||
# -------------------------------------------------------------------------
|
||||
"""
|
||||
This module contains the function
|
||||
|
||||
.synthesize(inst, name, value, readonly=False)
|
||||
|
||||
It is useful in object oriented class attributes, providing a stardard
|
||||
interface for creating properties of classes.
|
||||
|
||||
Can be called within an objects Class, or an instance of an object can be
|
||||
passed into the function to have the properties added. The property will
|
||||
be added and set/get/del attr interface will be created.
|
||||
"""
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
|
||||
def synthesize(inst, name, value, readonly=False):
|
||||
"""
|
||||
Convenience method to create getters, setters and a property for the
|
||||
instance. Should the instance already have the getters or setters
|
||||
defined this won't add them and the property will reference the already
|
||||
defined getters and setters Should be called from within __init__.
|
||||
|
||||
Creates [name], _[name], get[Name], set[Name], del[Name], and on inst.
|
||||
|
||||
:param inst: An instance of the class to add the methods to.
|
||||
:param name: Base name to build function names and storage variable.
|
||||
:param value: Initial state of the created variable.
|
||||
|
||||
"""
|
||||
cls = type(inst)
|
||||
storageName = '_{0}'.format(name)
|
||||
getterName = 'get{0}{1}'.format(name[0].capitalize(), name[1:])
|
||||
setterName = 'set{0}{1}'.format(name[0].capitalize(), name[1:])
|
||||
deleterName = 'del{0}{1}'.format(name[0].capitalize(), name[1:])
|
||||
|
||||
setattr(inst, storageName, value)
|
||||
|
||||
# We always define the getter
|
||||
def buildCustomGetter(self):
|
||||
return getattr(self, storageName)
|
||||
|
||||
# Add the Getter
|
||||
if not hasattr(inst, getterName):
|
||||
setattr(cls, getterName, buildCustomGetter)
|
||||
|
||||
# Handle Read Only
|
||||
if readonly:
|
||||
if not hasattr(inst, name):
|
||||
setattr(cls, name,
|
||||
property(fget=getattr(cls, getterName, None) or buildCustomGetter,
|
||||
fdel=getattr(cls, getterName, None)))
|
||||
else:
|
||||
# We only define the setter if we arn't read only
|
||||
def buildCustomSetter(self, state):
|
||||
setattr(self, storageName, state)
|
||||
if not hasattr(inst, setterName):
|
||||
setattr(cls, setterName, buildCustomSetter)
|
||||
member = None
|
||||
if hasattr(cls, name):
|
||||
# we need to try to update the property fget, fset, fdel
|
||||
# incase the class has defined its own custom functions
|
||||
member = getattr(cls, name)
|
||||
if not isinstance(member, property):
|
||||
raise ValueError('Member "{0}" for class "{1}" exists and is not a property.'
|
||||
''.format(name, cls.__name__))
|
||||
|
||||
# If the class has the property or not we still try to set it
|
||||
setattr(cls, name,
|
||||
property(fget=getattr(member, 'fget', None)
|
||||
or getattr(cls, getterName, None)
|
||||
or buildCustomGetter,
|
||||
fset=getattr(member, 'fset', None)
|
||||
or getattr(cls, setterName, None)
|
||||
or buildCustomSetter,
|
||||
fdel=getattr(member, 'fdel', None)
|
||||
or getattr(cls, getterName, None)))
|
||||
|
||||
return getattr(inst, name)
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
|
||||
###########################################################################
|
||||
# Main Code Block, will run the tool
|
||||
# -------------------------------------------------------------------------
|
||||
if __name__ == '__main__':
|
||||
"""Self Testing"""
|
||||
|
||||
from test_foo import Foo
|
||||
|
||||
# create an object from Foo Class
|
||||
myFoo = Foo()
|
||||
|
||||
pass
|
||||
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# synth_arg_kwarg.py
|
||||
# Convenience module for a standardized attr interface for classes/objects.
|
||||
# -------------------------------------------------------------------------
|
||||
__author__ = 'HogJonny'
|
||||
# -------------------------------------------------------------------------
|
||||
from find_arg import find_arg
|
||||
from synth import synthesize
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
def setSynthArgKwarg(inst, argPosIndex=None, argTag=None, defaultValue=None,
|
||||
inArgs=None, inKwargs=None, removeKwarg=True,
|
||||
setAnyway=True):
|
||||
"""
|
||||
Uses find_arg and sets a property on a object.
|
||||
|
||||
Special args:
|
||||
setAnyway <-- if the object has the property already, set it
|
||||
|
||||
If the arg/property doesn't exist we synthesize it
|
||||
"""
|
||||
|
||||
foundArg = None
|
||||
argValueDict = {}
|
||||
|
||||
# find the argument, or set to default value
|
||||
foundArg, inKwargs = find_arg(argPosIndex, argTag, removeKwarg,
|
||||
inArgs, inKwargs,
|
||||
defaultValue)
|
||||
|
||||
if foundArg:
|
||||
argTag = foundArg
|
||||
|
||||
# single arg first
|
||||
# make sure the object doesn't arealdy have this property
|
||||
try:
|
||||
hasattr(inst, argTag) # check if property exists
|
||||
if setAnyway:
|
||||
try:
|
||||
setattr(inst, argTag, defaultValue) # try to set
|
||||
except Exception as e:
|
||||
raise e
|
||||
except:
|
||||
pass
|
||||
|
||||
# make it a synthetic property
|
||||
if argTag:
|
||||
try:
|
||||
argValue = synthesize(inst, argTag, defaultValue)
|
||||
argValueDict[argTag] = argValue
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
# multiple and/or remaining kwards next
|
||||
if inKwargs:
|
||||
if len(inKwargs) > 0:
|
||||
for k, v in inKwargs.items():
|
||||
try:
|
||||
hasattr(inst, k) # check if property exists
|
||||
if setAnyway:
|
||||
try:
|
||||
setattr(inst, k, v) # try to set
|
||||
except Exception as e:
|
||||
raise e
|
||||
except:
|
||||
pass
|
||||
|
||||
if k:
|
||||
try:
|
||||
argValue = synthesize(inst, k, v)
|
||||
argValueDict[k] = argValue
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
return argValueDict
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
|
||||
###########################################################################
|
||||
# Main Code Block, will run the tool
|
||||
# -------------------------------------------------------------------------
|
||||
if __name__ == '__main__':
|
||||
|
||||
from test_foo import Foo
|
||||
|
||||
# define a arg/property tag we know doesn't exist
|
||||
synthArgTag = 'syntheticArg'
|
||||
|
||||
# create a test object
|
||||
print('~ creating the test foo object...')
|
||||
myFoo = Foo()
|
||||
|
||||
print('~ Starting - single synthetic arg test...')
|
||||
# find and set existing, or create and set
|
||||
argValueDict = setSynthArgKwarg(myFoo,
|
||||
argTag=synthArgTag,
|
||||
defaultValue='kablooey')
|
||||
|
||||
# what was returned
|
||||
print('~ single value returned...')
|
||||
for k, v in argValueDict.items():
|
||||
print("Arg '{0}':'{1}'".format(k, v))
|
||||
|
||||
# attempt to access the new synthetic property directly
|
||||
print('~ direct property access test...')
|
||||
try:
|
||||
myFoo.syntheticArg
|
||||
print('myFoo.{0}: {1}'.format(synthArgTag, myFoo.syntheticArg))
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
# can we create a bunch of kwargs?
|
||||
print('~ Starting - multiple synthetic kwarg test...')
|
||||
newKwargs = {'fooey': 'chop suey', 'success': True}
|
||||
|
||||
# find and set existing, or create and set
|
||||
argValueDict = setSynthArgKwarg(myFoo,
|
||||
inKwargs=newKwargs,
|
||||
defaultValue='kablooey')
|
||||
|
||||
# what was returned
|
||||
print('~ multiple values returned...')
|
||||
for k, v in argValueDict.items():
|
||||
print("Arg '{0}':'{1}'".format(k, v))
|
||||
|
||||
print('~ multiple direct property access test...')
|
||||
try:
|
||||
myFoo.fooey
|
||||
print('myFoo.{0}: {1}'.format('fooey', myFoo.fooey))
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
try:
|
||||
myFoo.success
|
||||
print('myFoo.{0}: {1}'.format('success', myFoo.success))
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
print('~ Starting - known failure test...')
|
||||
try:
|
||||
myFoo.knownBad
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print('Test failed as expected!!!')
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# test_foo.py
|
||||
# just a dumb test object
|
||||
# -------------------------------------------------------------------------
|
||||
__author__ = 'HogJonny'
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
from synth import synthesize
|
||||
|
||||
|
||||
class Foo(object):
|
||||
"""
|
||||
This is a Class, it creates a Foo object... which does nothing really
|
||||
"""
|
||||
|
||||
__propertyTag = 'fooProperty'
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
def __init__(self, name='Foo', value='defaultValue', *args, **kwargs):
|
||||
'''Class __init__'''
|
||||
synthesize(self, 'name', name)
|
||||
synthesize(self, Foo.__propertyTag, value)
|
||||
|
||||
synthesize(self, 'test', 'testValue')
|
||||
|
||||
self.testDump = object.__getattribute__(self, 'test')
|
||||
|
||||
# This calls a class method
|
||||
self.methodA()
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
def methodA(self):
|
||||
'''Class synthesized property methods self-tests'''
|
||||
|
||||
"""test getters"""
|
||||
print ('{0}.fooProperty is: {1}'
|
||||
''.format(self.getName(), self.getFooProperty()))
|
||||
|
||||
"""test property retreival"""
|
||||
print ('{0}.testDump is: {1}'
|
||||
''.format(self.name, self.testDump))
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
|
||||
###########################################################################
|
||||
# Main Code Block, will run the tool
|
||||
# -------------------------------------------------------------------------
|
||||
if __name__ == '__main__':
|
||||
# create an object from Foo Class
|
||||
myFoo = Foo()
|
||||
+3
-4
@@ -10,14 +10,13 @@
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
# -- This line is 75 characters -------------------------------------------
|
||||
# The __init__.py files help guide import statements without automatically
|
||||
# importing all of the modules
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
"""azpy.shared.ui.__init__"""
|
||||
|
||||
import os
|
||||
|
||||
from azpy import env_bool
|
||||
from azpy.env_bool import env_bool
|
||||
from azpy.constants import ENVAR_DCCSI_GDEBUG
|
||||
from azpy.constants import ENVAR_DCCSI_DEV_MODE
|
||||
|
||||
|
||||
+355
@@ -0,0 +1,355 @@
|
||||
# coding:utf-8
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
from __future__ import unicode_literals
|
||||
# from builtins import str
|
||||
|
||||
# built in's
|
||||
import os
|
||||
import sys
|
||||
import uuid
|
||||
import weakref
|
||||
import logging as _logging
|
||||
|
||||
# 3rd Party
|
||||
from unipath import Path
|
||||
|
||||
# azpy extensions
|
||||
import azpy.config_utils
|
||||
_config = azpy.config_utils.get_dccsi_config()
|
||||
settings = _config.get_config_settings(setup_ly_pyside=True)
|
||||
|
||||
import PySide2.QtWidgets as QtWidgets
|
||||
import PySide2.QtCore as QtCore
|
||||
from shiboken2 import wrapInstance
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# global space debug flag
|
||||
_G_DEBUG = settings.DCCSI_GDEBUG
|
||||
|
||||
# global space debug flag
|
||||
_DCCSI_DEV_MODE = settings.DCCSI_DEV_MODE
|
||||
|
||||
# global maya state (if we are running in maya with gui)
|
||||
# or another dcc tool with pyside2
|
||||
# TODO implement that check
|
||||
_G_PYSIDE2_DCC = None
|
||||
|
||||
_MODULE_PATH = Path(__file__)
|
||||
|
||||
_MODULENAME = 'azpy.shared.ui.azpy_base_widget'
|
||||
_LOGGER = _logging.getLogger(_MODULENAME)
|
||||
_LOGGER.debug('Something invoked :: {0}.'.format(_MODULENAME))
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
class DccWidget(object):
|
||||
"""This is an experimental Class to make a widget compatible with
|
||||
a number of PySide2/Python compatible DCC Tools like Maya and Houdini"""
|
||||
|
||||
_LABEL_NAME = 'no name window' # Window display name
|
||||
_instances = list()
|
||||
|
||||
# --constructor--------------------------------------------------------
|
||||
def __init__(self, parent=None, dcc_gui=_G_PYSIDE2_DCC, *args, **kwargs):
|
||||
|
||||
# False or None, 'Maya', 'Houdini' ... or another pysdie2 dcc
|
||||
self._dcc_gui = dcc_gui
|
||||
|
||||
self._name = '{0}_{1}'.format(self.__class__.__name__, uuid.uuid4())
|
||||
|
||||
self._parent = parent
|
||||
self._parent = self._base_setup()
|
||||
|
||||
# Init all baseclasses (including QWidget) of the main class
|
||||
try:
|
||||
super().__init__(*args, **kwargs)
|
||||
except Exception as Err:
|
||||
print(Err)
|
||||
|
||||
self.__class__._instances.append(weakref.proxy(self))
|
||||
|
||||
if isinstance(self, QtWidgets.QWidget):
|
||||
self.setParent(self._parent)
|
||||
|
||||
# camel case because this is a Qt|Pyside2 widget method
|
||||
if self.objectName() == '':
|
||||
# Set a unique object name string so Maya can easily look it up
|
||||
self.setObjectName('{0}_{1}'.format(self._name))
|
||||
|
||||
# -- properties -------------------------------------------------------
|
||||
@property
|
||||
def dcc_gui(self):
|
||||
return self._dcc_gui
|
||||
|
||||
@dcc_gui.setter
|
||||
def dcc_gui(self, type):
|
||||
self._dcc_gui = type
|
||||
return self._dcc_gui
|
||||
|
||||
@dcc_gui.getter
|
||||
def dcc_gui(self):
|
||||
return self._dcc_gui
|
||||
|
||||
@property
|
||||
def parent(self):
|
||||
return self._parent
|
||||
|
||||
@parent.setter
|
||||
def parent(self, type):
|
||||
self._parent = type
|
||||
return self._parent
|
||||
|
||||
@parent.getter
|
||||
def parent(self):
|
||||
return self._parent
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
def _base_setup(self, *args, **kwargs):
|
||||
'''TODO'''
|
||||
# if there is not a parent, we want a mainwindow to parent to
|
||||
if self.parent == None:
|
||||
self.parent = self._make_standalone_mainwindow()
|
||||
return self.parent
|
||||
|
||||
def _make_standalone_mainwindow(self):
|
||||
'''Make a standalone mainwindow parent to existing Qapp
|
||||
We parent so that this Qwidget will not be auto-destroyed or garbage
|
||||
collected if the instance variable goes out of scope.
|
||||
|
||||
If a Qapp doesn't exist, start one.
|
||||
'''
|
||||
original_parent = self._parent
|
||||
new_parent = None
|
||||
|
||||
if self.dcc_gui:
|
||||
if self.dcc_gui == 'Maya':
|
||||
# import the Maya ui class
|
||||
import OpenMaya as omui
|
||||
# Parent under the main Maya window
|
||||
mainWindowPtr = omui.MQtUtil.mainWindow()
|
||||
new_parent = wrapInstance(long(mainWindowPtr), QMainWindow)
|
||||
elif self.dcc_gui == 'Houdini':
|
||||
pass # not implemented
|
||||
else:
|
||||
from azpy.shared.ui.templates import TemplateMainWindow
|
||||
new_parent = TemplateMainWindow()
|
||||
|
||||
return new_parent
|
||||
|
||||
def objectName(self):
|
||||
# stand in for Qt call on mixed objects, thus camelCase
|
||||
return None
|
||||
|
||||
def show(self):
|
||||
# stand in for Qt call on mixed objects, thus camelCase
|
||||
return None
|
||||
|
||||
def thing(self):
|
||||
# Make this widget appear as a standalone window even though it is parented
|
||||
if isinstance(self, QtWidgets.QDockWidget):
|
||||
self.setWindowFlags(QtCore.Qt.Dialog | QtCore.Qt.FramelessWindowHint)
|
||||
else:
|
||||
try:
|
||||
self.setWindowFlags(QtCore.Qt.Window)
|
||||
except:
|
||||
pass
|
||||
|
||||
# Delete the parent QDockWidget if applicable
|
||||
if isinstance(original_parent, QtWidgets.QDockWidget):
|
||||
original_parent.close()
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
class BaseQwidgetAzpy(object):
|
||||
"""Inheret from to handle common base functionality for Qt Widgets
|
||||
Parents to a standalone Qapp and mainwindow if not explicitly provided
|
||||
Place this before the Qt Widget Class in inheretance order"""
|
||||
|
||||
_LABEL_NAME = 'no name window' # Window display name
|
||||
_instances = list()
|
||||
|
||||
_ORG_TAG = 'Amazon_Lumberyard'
|
||||
_APP_TAG = 'DCCsi'
|
||||
|
||||
# --constructor--------------------------------------------------------
|
||||
def __init__(self,
|
||||
parent=None,
|
||||
logger=None,
|
||||
qapp=None,
|
||||
window_title=_LABEL_NAME,
|
||||
*args, **kwargs):
|
||||
"""To DO"""
|
||||
self._name_uuid = '{0}_{1}'.format(self.__class__.__name__, uuid.uuid4())
|
||||
|
||||
self._logger = logger
|
||||
if not self._logger:
|
||||
self._logger = _logging.getLogger(self._name_uuid)
|
||||
|
||||
self._parent = parent
|
||||
|
||||
self._qapp = qapp or QtWidgets.QApplication.instance()
|
||||
if self._qapp == None:
|
||||
self.logger.debug('No QApplication has been instantiated')
|
||||
self._qapp = self._base_setup(window_title)
|
||||
|
||||
# Init all baseclasses (including QWidget) of the main class
|
||||
try:
|
||||
super(BaseQwidgetAzpy, self).__init__(*args, **kwargs)
|
||||
except Exception as Err:
|
||||
print(Err)
|
||||
|
||||
self.__class__._instances.append(weakref.proxy(self))
|
||||
|
||||
if isinstance(self, QtWidgets.QWidget):
|
||||
self.setParent(self._parent)
|
||||
|
||||
# camel case because this is a Qt|Pyside2 widget method
|
||||
if self.objectName() == '':
|
||||
# Set a unique object name string so Maya can easily look it up
|
||||
self.setObjectName(self._name)
|
||||
|
||||
# -- properties -------------------------------------------------------
|
||||
@property
|
||||
def parent(self):
|
||||
return self._parent
|
||||
|
||||
@parent.setter
|
||||
def parent(self, type):
|
||||
self._parent = type
|
||||
return self._parent
|
||||
|
||||
@parent.getter
|
||||
def parent(self):
|
||||
return self._parent
|
||||
|
||||
@property
|
||||
def logger(self):
|
||||
return self._logger
|
||||
|
||||
@logger.setter
|
||||
def logger(self, logger):
|
||||
self._logger = logger
|
||||
return self._logger
|
||||
|
||||
@logger.getter
|
||||
def logger(self):
|
||||
return self._logger
|
||||
|
||||
@property
|
||||
def qapp(self):
|
||||
return self._qapp
|
||||
|
||||
@qapp.setter
|
||||
def qapp(self, qapp):
|
||||
self._qapp = qapp
|
||||
return self._qapp
|
||||
|
||||
@qapp.getter
|
||||
def qapp(self):
|
||||
return self._qapp
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def objectName(self):
|
||||
# stand in for Qt call on mixed objects, thus camelCase
|
||||
return self._name_uuid
|
||||
|
||||
def show(self):
|
||||
# stand in for Qt call on mixed objects, thus camelCase
|
||||
return None
|
||||
|
||||
def _base_setup(self, *args, **kwargs):
|
||||
'''TODO'''
|
||||
# if there is not a parent, we want a mainwindow to parent to
|
||||
if self.parent == None:
|
||||
try:
|
||||
self.parent = self._make_standalone_app(self.objectName())
|
||||
except:
|
||||
self.parent = self._make_standalone_app(self._name_uuid)
|
||||
return self.parent
|
||||
|
||||
def _make_standalone_app(self, name):
|
||||
useGUI = not '-no-gui' in sys.argv
|
||||
self.qapp = QtWidgets.QApplication(sys.argv) if useGUI else QtWidgets.QCoreApplication(sys.argv)
|
||||
self.qapp.setOrganizationName(self.__class__._ORG_TAG)
|
||||
self.qapp.setApplicationName('{app}:{tool}'.format(app=self.__class__._APP_TAG,
|
||||
tool=name))
|
||||
return self.qapp
|
||||
|
||||
@QtCore.Slot()
|
||||
def closeEvent(self, *args, **kwargs):
|
||||
"""Event which is run when window closes"""
|
||||
self.logger.debug("Method: {0}.{1}".format(self.__class__.__name__, 'closeEvent'))
|
||||
self.logger.debug("Closing: {0}".format(self.objectName()))
|
||||
self.__class__._instances.remove(self)
|
||||
self.qapp.instance().quit
|
||||
self.qapp.exit()
|
||||
# ----------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestWidget(BaseQwidgetAzpy, QtWidgets.QPushButton):
|
||||
def __init__(self, parent=None, *args, **kwargs):
|
||||
|
||||
# Init all baseclasses (including QWidget) of the main class
|
||||
try:
|
||||
super().__init__(*args, **kwargs)
|
||||
except Exception as Err:
|
||||
print(Err)
|
||||
|
||||
try:
|
||||
self.logger.debug('{0}'.format(self.parent))
|
||||
except Exception as Err:
|
||||
print(Err)
|
||||
|
||||
# self.setParent(parent)
|
||||
|
||||
self.setText('Push Me')
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
"""Run this file as main"""
|
||||
import sys
|
||||
|
||||
_TEST_APP_NAME = '{0}-{1}'.format(_MODULENAME, 'TEST')
|
||||
|
||||
_LOGGER = azpy.initialize_logger(_TEST_APP_NAME,
|
||||
log_to_file=True,
|
||||
default_log_level=_logging.DEBUG)
|
||||
|
||||
from azpy.constants import STR_CROSSBAR
|
||||
_LOGGER.info(STR_CROSSBAR)
|
||||
_LOGGER.info("{0} :: if __name__ == '__main__':".format(_MODULENAME))
|
||||
_LOGGER.info(STR_CROSSBAR)
|
||||
|
||||
|
||||
# test raw BaseWidget
|
||||
_TEST_BASE_WIDGET = BaseQwidgetAzpy()
|
||||
_LOGGER.info(_TEST_BASE_WIDGET.objectName())
|
||||
_TEST_BASE_WIDGET.show() # this should do nothing,it is a dummy call
|
||||
# this call is replaced with version the QWidget
|
||||
_TEST_BASE_WIDGET.closeEvent() # mimic Qt close
|
||||
_TEST_BASE_WIDGET = None
|
||||
|
||||
# NEED TO DELETE ^ Makes a Qapp
|
||||
|
||||
_TEST_WIDGET = TestWidget()
|
||||
_LOGGER.info(_TEST_WIDGET.objectName())
|
||||
_TEST_WIDGET.show()
|
||||
|
||||
del _LOGGER
|
||||
sys.exit(_TEST_WIDGET.qapp.exec_())
|
||||
# -------------------------------------------------------------------------
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
# coding:utf-8
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
from __future__ import unicode_literals
|
||||
# from builtins import str
|
||||
|
||||
# built in's
|
||||
import os
|
||||
# from io import StringIO # for handling unicode strings
|
||||
|
||||
# azpy
|
||||
from azpy import initialize_logger
|
||||
|
||||
# 3rd Party
|
||||
from unipath import Path
|
||||
import PySide2.QtCore as QtCore
|
||||
import PySide2.QtWidgets as QtWidgets
|
||||
import PySide2.QtGui as QtGui
|
||||
# -------------------------------------------------------------------------
|
||||
# global space debug flag
|
||||
_G_DEBUG = os.getenv('DCCSI_GDEBUG', False)
|
||||
|
||||
# global space debug flag
|
||||
_DCCSI_DEV_MODE = os.getenv('DCCSI_DEV_MODE', False)
|
||||
|
||||
_MODULE_PATH = Path(__file__)
|
||||
|
||||
_ORG_TAG = 'Amazon_Lumberyard'
|
||||
_APP_TAG = 'DCCsi'
|
||||
_TOOL_TAG = 'azpy.shared.ui.custom_treemodel'
|
||||
_TYPE_TAG = 'test'
|
||||
|
||||
_MODULENAME = __name__
|
||||
if _MODULENAME is '__main__':
|
||||
_MODULENAME = _TOOL_TAG
|
||||
|
||||
_UI_FILE = Path(_MODULE_PATH.parent, 'resources', 'example.ui')
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
###########################################################################
|
||||
## CustomTreeModel, Class
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
class CustomFileTreeModel(QtCore.QAbstractItemModel):
|
||||
"""
|
||||
Creates a customized model subclassed from, QAbstractItemModel
|
||||
Compatible with a TreeView
|
||||
"""
|
||||
|
||||
# --constructor--------------------------------------------------------
|
||||
def __init__(self, parent=None, *args, **kwargs):
|
||||
'''
|
||||
Constructor, INPUTS: Node, QObject
|
||||
'''
|
||||
super(CustomFileTreeModel, self).__init__(parent=parent, *args, **kwargs)
|
||||
|
||||
# can use these later (maybe?)
|
||||
# easily extended later, TODO implement as property, add append
|
||||
self._file_ext_list = ['.sbs', '.sbsar']
|
||||
|
||||
# we assume this is a file tree, we need a root path (lmbr project?)
|
||||
self._root_filepath = root_filepath
|
||||
# TODO: need to make sure we are getting path objects
|
||||
|
||||
# build the root node
|
||||
self._root_node = self.buildRootNode('root', None, self._rootFilePath)
|
||||
|
||||
# master selection
|
||||
self._masterSelection = masterSelection
|
||||
|
||||
# build the master selection node
|
||||
self._masterNode = self.buildMasterNode('master', None, self._masterSelection, self._rootNode.path())
|
||||
|
||||
# want to store off a couple lists in the model, for retreival later
|
||||
self._nodeList = None
|
||||
self._depNodesList = None
|
||||
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
# coding:utf-8
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
"""help_menu.py: Setup a standard Help item in the menubar for PySide2 GUIs"""
|
||||
|
||||
# built in's
|
||||
import os
|
||||
import logging
|
||||
|
||||
# azpy
|
||||
from azpy import initialize_logger
|
||||
# import azpy.shared.ui.qt_settings as qt_settings
|
||||
|
||||
# 3rd Party
|
||||
from unipath import Path
|
||||
import PySide2.QtCore as QtCore
|
||||
import PySide2.QtGui as QtGui
|
||||
import PySide2.QtWidgets as QtWidgets
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# global space debug flag
|
||||
_G_DEBUG = os.getenv('DCCSI_GDEBUG', False)
|
||||
|
||||
# global space developer mode flag
|
||||
_DCCSI_DEV_MODE = os.getenv('DCCSI_DEV_MODE', False)
|
||||
|
||||
_MODULE_PATH = Path(__file__)
|
||||
|
||||
_ORG_TAG = 'Amazon_Lumberyard'
|
||||
_APP_TAG = 'DCCsi'
|
||||
_TOOL_TAG = 'azpy.shared.ui.help_menu'
|
||||
_TYPE_TAG = 'test'
|
||||
|
||||
_MODULENAME = __name__
|
||||
if _MODULENAME is '__main__':
|
||||
_MODULENAME = _TOOL_TAG
|
||||
|
||||
_LOGGER = logging.getLogger(_MODULENAME)
|
||||
_LOGGER.debug('Something invoked :: {0}.'.format({_MODULENAME}))
|
||||
|
||||
# TODO: implement this
|
||||
# checks the run configuration to determine if we are running in maya
|
||||
_G_MAYA = False
|
||||
try:
|
||||
from azpy.config.maya import _G_MAYA
|
||||
if _G_MAYA:
|
||||
import maya.cmds as mc
|
||||
except:
|
||||
pass
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
class HelpMenu():
|
||||
"""
|
||||
Setup a standard Help item in the menubar for PySide2 GUIs
|
||||
|
||||
INPUTS:
|
||||
main_window = the class instance of the QMainWindow
|
||||
tool_label = the menu label for the tool's help item
|
||||
tool_help_page = the http:// path to the specific bpTool help page
|
||||
|
||||
Here's an example
|
||||
# self.help_menu = azpy.shared.ui.help_menu.setup(self, 'Help...', 'https://some.site.com/azpy')
|
||||
|
||||
"""
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
def __init__(self, main_window, tool_label, tool_help_page):
|
||||
"""Constructor"""
|
||||
|
||||
self.main_window = main_window
|
||||
# store mainwindow menubar, we can attach the Help menu to this
|
||||
self.menubar = self.main_window.menuBar()
|
||||
self.tool_label = tool_label
|
||||
self.tool_help_page = tool_help_page
|
||||
|
||||
self.help_menu = QtWidgets.QMenu(self.menubar)
|
||||
self.help_menu.setObjectName("help_menu")
|
||||
self.help_menu.setTitle("Help")
|
||||
|
||||
self.generic_tool_help_setup()
|
||||
self.specific_tool_help_setup()
|
||||
self.tool_bug_report_setup()
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def specific_tool_help_setup(self):
|
||||
""""""
|
||||
self.tool_action_help = QtWidgets.QAction(self.main_window)
|
||||
self.tool_action_help.setObjectName("tool_action_help")
|
||||
self.help_menu.addAction(self.tool_action_help)
|
||||
self.menubar.addAction(self.help_menu.menuAction())
|
||||
self.tool_action_help.setText(self.tool_label)
|
||||
self.main_window.connect(self.tool_action_help, QtCore.SIGNAL("triggered()"), self.tool_help_display)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
def generic_tool_help_setup(self):
|
||||
""""""
|
||||
self.azpy_tool_action_help = QtWidgets.QAction(self.main_window)
|
||||
self.azpy_tool_action_help.setObjectName("azpy_tool_action_help")
|
||||
self.help_menu.addAction(self.azpy_tool_action_help)
|
||||
self.menubar.addAction(self.help_menu.menuAction())
|
||||
self.azpy_tool_action_help.setText("DCCsi help...")
|
||||
self.main_window.connect(self.azpy_tool_action_help, QtCore.SIGNAL("triggered()"), self.azpy_tool_help_display)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
def tool_bug_report_setup(self):
|
||||
""""""
|
||||
self.tool_action_bug_report = QtWidgets.QAction(self.main_window)
|
||||
self.tool_action_bug_report.setObjectName("tool_action_bug_report")
|
||||
self.help_menu.addAction(self.tool_action_bug_report)
|
||||
self.menubar.addAction(self.help_menu.menuAction())
|
||||
self.tool_action_bug_report.setText("Report a Tool Bug...")
|
||||
self.main_window.connect(self.tool_action_bug_report, QtCore.SIGNAL("triggered()"), self.bug_report_display)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def tool_help_display(self):
|
||||
""""""
|
||||
if _G_MAYA:
|
||||
mc.showHelp(self.tool_help_page, absolute=True)
|
||||
else:
|
||||
_LOGGER.debug('This command, {0}: currently only works when running in Maya.'.format('tool_help_display'))
|
||||
pass
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
def azpy_tool_help_display(self):
|
||||
""""""
|
||||
if _G_MAYA:
|
||||
mc.showHelp('https://some.site.com/azpy/maya_tools/', absolute=True)
|
||||
else:
|
||||
_LOGGER.debug('This command, {0}: currently only works when running in Maya.'.format('azpy_tool_help_display'))
|
||||
pass
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
def bug_report_display(self):
|
||||
""""""
|
||||
if _G_MAYA:
|
||||
mc.showHelp('https://some.site.com/azpy/report_bug', absolute=True)
|
||||
else:
|
||||
_LOGGER.debug('This command, {0}: currently only works when running in Maya.'.format('bug_report_display'))
|
||||
pass
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestMainWindow(QtWidgets.QMainWindow):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
self.setWindowTitle('PySide2-HelpMenu-Test')
|
||||
|
||||
# Setup Help Menu
|
||||
self.help_menu = HelpMenu(self, 'PySide2-Test Help...', 'http://dccSI.com/NewTool')
|
||||
|
||||
# main widget
|
||||
self.main_widget = QtWidgets.QWidget(self)
|
||||
self.setCentralWidget(self.main_widget)
|
||||
|
||||
# layout initialize
|
||||
self.global_layout = QtWidgets.QVBoxLayout()
|
||||
self.main_widget.setLayout(self.global_layout)
|
||||
|
||||
# Add Widgets
|
||||
self.spinbox = QtWidgets.QSpinBox()
|
||||
self.spinbox.setValue(30)
|
||||
layout = QtWidgets.QFormLayout()
|
||||
layout.addRow('Parameter', self.spinbox)
|
||||
self.button = QtWidgets.QPushButton('Execute')
|
||||
|
||||
# global layout setting
|
||||
self.global_layout.addLayout(layout)
|
||||
self.global_layout.addWidget(self.button)
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""Event which is run when window closes"""
|
||||
|
||||
_LOGGER.debug("Exiting: {0}".format(_TOOL_TAG))
|
||||
# ----------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
"""Run this file as main"""
|
||||
import sys
|
||||
|
||||
_LOGGER.debug("{0} :: if __name__ == '__main__':".format(_TOOL_TAG))
|
||||
_LOGGER.debug("Starting App: {0} ...".format(_TOOL_TAG))
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
mainWin = TestMainWindow()
|
||||
mainWin.show()
|
||||
|
||||
del _LOGGER
|
||||
sys.exit(app.exec_())
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
# coding:utf-8
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
# -------------------------------------------------------------------------
|
||||
from __future__ import unicode_literals
|
||||
# from builtins import str
|
||||
|
||||
# built in's
|
||||
import sys
|
||||
import os
|
||||
import uuid
|
||||
|
||||
# azpy
|
||||
from azpy import initialize_logger
|
||||
from azpy.shared.ui.base_widget import BaseQwidgetAzpy
|
||||
|
||||
# 3rd Party
|
||||
from unipath import Path
|
||||
import PySide2.QtCore as QtCore
|
||||
import PySide2.QtWidgets as QtWidgets
|
||||
|
||||
from PySide2.QtCore import QProcess, Signal, Slot, QTextCodec
|
||||
from PySide2.QtGui import QTextCursor
|
||||
from PySide2.QtWidgets import QPlainTextEdit
|
||||
from PySide2.QtCore import QTimer
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# global space debug flag
|
||||
_G_DEBUG = os.getenv('DCCSI_GDEBUG', False)
|
||||
|
||||
# global space debug flag
|
||||
_DCCSI_DEV_MODE = os.getenv('DCCSI_DEV_MODE', False)
|
||||
|
||||
_MODULE_PATH = Path(__file__)
|
||||
|
||||
_ORG_TAG = 'Amazon_Lumberyard'
|
||||
_APP_TAG = 'DCCsi'
|
||||
_TOOL_TAG = 'azpy.shared.ui.pyside2_qtextedit_stdout'
|
||||
_TYPE_TAG = 'test'
|
||||
|
||||
_MODULENAME = __name__
|
||||
if _MODULENAME is '__main__':
|
||||
_MODULENAME = _TOOL_TAG
|
||||
|
||||
if _G_DEBUG:
|
||||
_LOGGER = initialize_logger(_MODULENAME, log_to_file=True)
|
||||
_LOGGER.debug('Something invoked :: {0}.'.format({_MODULENAME}))
|
||||
else:
|
||||
_LOGGER = initialize_logger(_MODULENAME)
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
class ProcessOutputReader(QProcess):
|
||||
produce_output = Signal(str)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
|
||||
# merge stderr channel into stdout channel
|
||||
self.setProcessChannelMode(QProcess.MergedChannels)
|
||||
# prepare decoding process' output to Unicode
|
||||
self._codec = QTextCodec.codecForLocale()
|
||||
self._decoder_stdout = self._codec.makeDecoder()
|
||||
# only necessary when stderr channel isn't merged into stdout:
|
||||
# self._decoder_stderr = codec.makeDecoder()
|
||||
|
||||
self.readyReadStandardOutput.connect(self._ready_read_standard_output)
|
||||
# only necessary when stderr channel isn't merged into stdout:
|
||||
# self.readyReadStandardError.connect(self._ready_read_standard_error)
|
||||
|
||||
@Slot()
|
||||
def _ready_read_standard_output(self):
|
||||
raw_bytes = self.readAllStandardOutput()
|
||||
text = self._decoder_stdout.toUnicode(raw_bytes)
|
||||
self.produce_output.emit(text)
|
||||
|
||||
# only necessary when stderr channel isn't merged into stdout:
|
||||
# @Slot()
|
||||
# def _ready_read_standard_error(self):
|
||||
# raw_bytes = self.readAllStandardError()
|
||||
# text = self._decoder_stderr.toUnicode(raw_bytes)
|
||||
# self.produce_output.emit(text)
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
|
||||
class MyConsole(BaseQwidgetAzpy, QPlainTextEdit):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
|
||||
# Set a unique object name string so Maya can easily look it up
|
||||
self.setObjectName('{0}_{1}'.format(self.__class__.__name__,
|
||||
uuid.uuid4()))
|
||||
|
||||
self.setReadOnly(True)
|
||||
self.setMaximumBlockCount(10000) # limit console to 10000 lines
|
||||
|
||||
self._cursor_output = self.textCursor()
|
||||
|
||||
@Slot(str)
|
||||
def append_output(self, text):
|
||||
self._cursor_output.insertText(text)
|
||||
self.scroll_to_last_line()
|
||||
|
||||
def scroll_to_last_line(self):
|
||||
cursor = self.textCursor()
|
||||
cursor.movePosition(QTextCursor.End)
|
||||
cursor.movePosition(QTextCursor.Up if cursor.atBlockStart() else
|
||||
QTextCursor.StartOfLine)
|
||||
self.setTextCursor(cursor)
|
||||
|
||||
def output_text(self, text):
|
||||
self._cursor_output.insertText(text)
|
||||
self.scroll_to_last_line()
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
"""Run this file as main"""
|
||||
import sys
|
||||
|
||||
_ORG_TAG = 'Amazon_Lumberyard'
|
||||
_APP_TAG = 'DCCsi'
|
||||
_TOOL_TAG = 'azpy.shared.ui.pyside2_qtextedit_stdout'
|
||||
_TYPE_TAG = 'test'
|
||||
|
||||
if _G_DEBUG:
|
||||
_LOGGER = initialize_logger('{0}-TEST'.format(_TOOL_TAG), log_to_file=True)
|
||||
_LOGGER.debug('Something invoked :: {0}.'.format({_MODULENAME}))
|
||||
|
||||
_LOGGER.debug("{0} :: if __name__ == '__main__':".format(_TOOL_TAG))
|
||||
_LOGGER.debug("Starting App:{0} TEST ...".format(_TOOL_TAG))
|
||||
|
||||
# # create the application instance
|
||||
# _APP = QtWidgets.QApplication(sys.argv)
|
||||
# _APP.setOrganizationName(_ORG_TAG)
|
||||
# _APP.setApplicationName('{app}:{tool}'.format(app=_APP_TAG, tool=_TOOL_TAG))
|
||||
|
||||
# create a console and connect the process output reader to it
|
||||
_CONSOLE = MyConsole()
|
||||
|
||||
# create a process output reader
|
||||
_READER = ProcessOutputReader(parent=_CONSOLE)
|
||||
_READER.produce_output.connect(_CONSOLE.append_output)
|
||||
|
||||
# start something and log (including to console)
|
||||
# this starts a test app
|
||||
_TEST_PY_FILE = Path(_MODULE_PATH.parent, 'pyside2_ui_utils.py')
|
||||
_READER.start('python', ['-u', _TEST_PY_FILE]) # start the process
|
||||
|
||||
# after that starts, this will show the console
|
||||
# LY_QSS = Path(_MODULE_PATH.parent, 'resources', 'stylesheets', 'LYstyle.qss')
|
||||
_DARK_STYLE = Path(_MODULE_PATH.parent, 'resources', 'qdarkstyle', 'style.qss')
|
||||
_CONSOLE.qapp.setStyleSheet(_DARK_STYLE.read_file())
|
||||
_CONSOLE.show() # make the console visible
|
||||
|
||||
_LOGGER.debug(_CONSOLE.objectName())
|
||||
|
||||
_TIMER = QTimer()
|
||||
_TIMER.timeout.connect(lambda: None)
|
||||
_TIMER.start(100)
|
||||
|
||||
del _LOGGER
|
||||
sys.exit(_CONSOLE.qapp.exec_())
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
# coding:utf-8
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
# -------------------------------------------------------------------------
|
||||
#
|
||||
from __future__ import unicode_literals
|
||||
# from builtins import str
|
||||
|
||||
# built in's
|
||||
import os
|
||||
import site
|
||||
import uuid
|
||||
import logging as _logging
|
||||
import xml.etree.ElementTree as xml # Qt .ui files are xml
|
||||
from io import StringIO # for handling unicode strings
|
||||
|
||||
# azpy extensions
|
||||
import azpy.config_utils
|
||||
_config = azpy.config_utils.get_dccsi_config()
|
||||
# ^ this is effectively an import and retreive of <dccsi>\config.py
|
||||
# and init's access to Qt/Pyside2
|
||||
# init lumberyard Qy/PySide2 access
|
||||
|
||||
# now default settings are extended with PySide2
|
||||
# this is an alternative to "from dynaconf import settings" with Qt
|
||||
settings = _config.get_config_settings(setup_ly_pyside=True)
|
||||
|
||||
# 3rd Party (we may or do provide)
|
||||
from unipath import Path
|
||||
|
||||
# now we can import lumberyards PySide2
|
||||
import PySide2.QtCore as QtCore
|
||||
import PySide2.QtWidgets as QtWidgets
|
||||
import PySide2.QtGui as QtGui
|
||||
from PySide2.QtWidgets import QApplication, QSizePolicy
|
||||
import PySide2.QtUiTools as QtUiTools
|
||||
|
||||
# special case for import pyside2uic
|
||||
site.addsitedir(settings.DCCSI_PYSIDE2_TOOLS)
|
||||
import pyside2uic
|
||||
|
||||
# azpy
|
||||
import azpy.shared.ui.qt_settings as qt_settings
|
||||
import azpy.shared.ui.help_menu as help_menu
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# global space debug flag
|
||||
_G_DEBUG = settings.DCCSI_GDEBUG
|
||||
|
||||
# global space debug flag
|
||||
_DCCSI_DEV_MODE = settings.DCCSI_DEV_MODE
|
||||
|
||||
_MODULE_PATH = Path(__file__)
|
||||
|
||||
_ORG_TAG = 'Amazon_Lumberyard'
|
||||
_APP_TAG = 'DCCsi'
|
||||
_TOOL_TAG = 'azpy.shared.ui.pyside2_ui_utils'
|
||||
_TYPE_TAG = 'test'
|
||||
|
||||
_MODULENAME = _TOOL_TAG
|
||||
_LOGGER = _logging.getLogger(_MODULENAME)
|
||||
_LOGGER.debug('Something invoked :: {0}.'.format(_MODULENAME))
|
||||
|
||||
_UI_FILE = Path(_MODULE_PATH.parent, 'resources', 'example.ui')
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
class UiLoader(QtUiTools.QUiLoader):
|
||||
def __init__(self, base_instance):
|
||||
super(UiLoader, self).__init__(base_instance)
|
||||
self._base_instance = base_instance
|
||||
|
||||
def createWidget(self, classname, parent=None, name=""):
|
||||
widget = super(UiLoader, self).createWidget(
|
||||
classname, parent, name)
|
||||
|
||||
if parent is None:
|
||||
return self._base_instance
|
||||
else:
|
||||
setattr(self._base_instance, name, widget)
|
||||
return widget
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
class UiWidget(QtWidgets.QWidget):
|
||||
def __init__(self, ui_file=_UI_FILE, parent=None):
|
||||
super().__init__(parent)
|
||||
loader = UiLoader(parent)
|
||||
file = QFile(ui_file)
|
||||
file.open(QFile.ReadOnly)
|
||||
loader.load(file, self)
|
||||
file.close()
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
def from_ui_generate_form_and_base_class(filename, return_output=False):
|
||||
"""Parse a Qt Designer .ui file and return Pyside2 Form and Base Class
|
||||
Usage:
|
||||
import azpy.shared.ui as azpyui
|
||||
form_class, base_class = azpyui.from_ui_generate_form_and_class(r'C:\my\filepath\tool.ui')
|
||||
"""
|
||||
ui_file = Path(filename)
|
||||
output = ''
|
||||
parsed_xml = None
|
||||
try:
|
||||
ui_file.exists()
|
||||
except FileNotFoundError as error:
|
||||
output += 'File does not exist: {0}/r'.format(error)
|
||||
if _G_DEBUG:
|
||||
print(error)
|
||||
if return_output:
|
||||
return False, output
|
||||
else:
|
||||
return False
|
||||
|
||||
try:
|
||||
ui_file.ext == 'ui'
|
||||
except IOError as error:
|
||||
output += 'Not a Qt Designer .ui file: {0}/r'.format(error)
|
||||
if return_output:
|
||||
return False, output
|
||||
else:
|
||||
return False
|
||||
|
||||
parsed_xml = xml.parse(ui_file)
|
||||
form_class = parsed_xml.find('class').text
|
||||
widget_class = parsed_xml.find('widget').get('class')
|
||||
|
||||
with open(ui_file, 'r') as ui_file:
|
||||
stream = StringIO() # create a file io stream
|
||||
frame = {}
|
||||
|
||||
_uic_compiler_path = Path(settings.DCCSI_PYSIDE2_TOOLS)
|
||||
site.addsitedir(_uic_compiler_path)
|
||||
|
||||
import pyside2uic
|
||||
|
||||
# compile the .ui file as a .pyc represented in steam
|
||||
pyside2uic.compileUi(ui_file, stream, indent=4)
|
||||
# compile the .pyc bytecode from stream
|
||||
pyc = compile(stream.getvalue(), '', 'exec')
|
||||
# execute the .pyc bytecode
|
||||
exec (pyc, frame)
|
||||
|
||||
# Retreive the form_class and base_class based on type in designer .ui (xml)
|
||||
form_class = frame['Ui_{0}'.format(form_class)]
|
||||
base_class = eval('QtWidgets.{0}'.format(widget_class))
|
||||
|
||||
ui_file.close()
|
||||
|
||||
if return_output:
|
||||
return form_class, base_class, output
|
||||
else:
|
||||
return form_class, base_class
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
"""Run this file as main"""
|
||||
import sys
|
||||
|
||||
_LOGGER = azpy.initialize_logger('{0}-TEST'.format(_TOOL_TAG), log_to_file=True)
|
||||
_LOGGER.debug('Something invoked :: {0}.'.format({_MODULENAME}))
|
||||
|
||||
_LOGGER.info("{0} :: if __name__ == '__main__':".format(_TOOL_TAG))
|
||||
_LOGGER.info("Starting App:{0} TEST ...".format(_TOOL_TAG))
|
||||
|
||||
_FORM_CLASS, _BASE_CLASS = from_ui_generate_form_and_base_class(_UI_FILE)
|
||||
|
||||
_LOGGER.info(_FORM_CLASS)
|
||||
_LOGGER.info(_BASE_CLASS)
|
||||
|
||||
from azpy.shared.ui.templates import TemplateMainWindow
|
||||
|
||||
_MAIN_WINDOW = TemplateMainWindow(logger=_LOGGER)
|
||||
_MAIN_WINDOW.show()
|
||||
|
||||
del _LOGGER
|
||||
sys.exit(_MAIN_WINDOW.qapp.exec_())
|
||||
# -------------------------------------------------------------------------
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
# coding:utf-8
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
"""qt_settings.py: Manages a QSettings for a tool"""
|
||||
|
||||
# built in's
|
||||
import os
|
||||
import logging as _logging
|
||||
|
||||
# 3rd Party (we may provide)
|
||||
from unipath import Path
|
||||
from dynaconf import settings
|
||||
|
||||
# azpy extensions
|
||||
import azpy.config_utils
|
||||
_config = azpy.config_utils.get_dccsi_config()
|
||||
# ^ this is effectively an import and retreive of <dccsi>\config.py
|
||||
# init lumberyard Qy/PySide2 access
|
||||
_config.init_ly_pyside(settings.LY_DEV)
|
||||
|
||||
# now we can import lumberyards PySide2
|
||||
import PySide2.QtCore as QtCore
|
||||
import PySide2.QtWidgets as QtWidgets
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# global space debug flag
|
||||
_G_DEBUG = settings.DCCSI_GDEBUG
|
||||
|
||||
# global space debug flag
|
||||
_DCCSI_DEV_MODE = settings.DCCSI_DEV_MODE
|
||||
|
||||
_MODULE_PATH = Path(__file__)
|
||||
|
||||
_ORG_TAG = 'Amazon_Lumberyard'
|
||||
_APP_TAG = 'DCCsi'
|
||||
_TOOL_TAG = 'azpy.shared.ui.qt_settings'
|
||||
_TYPE_TAG = 'test'
|
||||
|
||||
_MODULENAME = _TOOL_TAG
|
||||
_LOGGER = _logging.getLogger(_MODULENAME)
|
||||
_LOGGER.debug('Something invoked :: {0}.'.format(_MODULENAME))
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def createSettings(org='Amazon_Lumberyard', app='DCCsi',
|
||||
tool='azpy', type='default'):
|
||||
"""Sets up a settings .ini
|
||||
|
||||
Returns a QSettings instance"""
|
||||
|
||||
settings_folder = '{org}//{app}'.format(org=org, app=app)
|
||||
settings_name = '{tool}-{type}'.format(tool=tool, type=type)
|
||||
|
||||
settings = QtCore.QSettings(QtCore.QSettings.IniFormat,
|
||||
QtCore.QSettings.UserScope,
|
||||
settings_folder, settings_name)
|
||||
|
||||
return settings
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
"""Run this file as main"""
|
||||
import sys
|
||||
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
app.setOrganizationName(_ORG_TAG)
|
||||
app.setApplicationName('{app}:{tool}'.format(app=_APP_TAG, tool=_TOOL_TAG))
|
||||
|
||||
test_esttings = createSettings(_ORG_TAG, _APP_TAG, _TOOL_TAG, _TYPE_TAG)
|
||||
_LOGGER.info(test_esttings)
|
||||
_LOGGER.info(test_esttings.fileName())
|
||||
+680
@@ -0,0 +1,680 @@
|
||||
# coding:utf-8
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
from __future__ import unicode_literals
|
||||
# from builtins import str
|
||||
|
||||
# built in's
|
||||
import os
|
||||
import sys
|
||||
import logging as _logging
|
||||
import uuid
|
||||
import xml.etree.ElementTree as xml # Qt .ui files are xml
|
||||
from io import StringIO # for handling unicode strings
|
||||
|
||||
# 3rd Party (we may or do provide)
|
||||
from unipath import Path
|
||||
|
||||
# azpy extensions
|
||||
import azpy.config_utils
|
||||
_config = azpy.config_utils.get_dccsi_config()
|
||||
# ^ this is effectively an import and retreive of <dccsi>\config.py
|
||||
# and init's access to Qt/Pyside2
|
||||
# init lumberyard Qy/PySide2 access
|
||||
|
||||
# now default settings are extended with PySide2
|
||||
# this is an alternative to "from dynaconf import settings" with Qt
|
||||
settings = _config.get_config_settings(setup_ly_pyside=True)
|
||||
|
||||
# now we can import lumberyards PySide2
|
||||
import azpy.shared.ui.qt_settings as qt_settings
|
||||
import azpy.shared.ui.help_menu as help_menu
|
||||
import azpy.shared.ui.pyside2_ui_utils as ui_utils
|
||||
|
||||
import pyside2uic
|
||||
import PySide2.QtCore as QtCore
|
||||
import PySide2.QtWidgets as QtWidgets
|
||||
import PySide2.QtGui as QtGui
|
||||
import PySide2.QtUiTools as QtUiTools
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# global space debug flag
|
||||
_G_DEBUG = settings.DCCSI_GDEBUG
|
||||
|
||||
# global space debug flag
|
||||
_DCCSI_DEV_MODE = settings.DCCSI_DEV_MODE
|
||||
|
||||
_MODULE_PATH = Path(__file__)
|
||||
|
||||
_MODULENAME = 'azpy.shared.ui.teamplates'
|
||||
_LOGGER = _logging.getLogger(_MODULENAME)
|
||||
_LOGGER.debug('Something invoked :: {0}.'.format(_MODULENAME))
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# example .ui used as default (for tests, etc.)
|
||||
_UI_FILE = Path(_MODULE_PATH.parent, 'resources', 'example.ui')
|
||||
# Hmmm.... should be a better way to handle this?
|
||||
_FORM_CLASS, _BASE_CLASS = ui_utils.from_ui_generate_form_and_base_class(_UI_FILE)
|
||||
# looks like either we aren't compiling it or it's not provided in the current
|
||||
# version of Qt we use (check mack Qt5.15)
|
||||
# https://doc-snapshots.qt.io/qtforpython-5.15/PySide2/QtUiTools/ls.loadUiType.html)
|
||||
#_FORM_CLASS, _BASE_CLASS = QtUiTools.loadUiType(_UI_FILE)
|
||||
# print(QtUiTools.QUiLoader) <-- this one works differently but maybe that pattern is better?
|
||||
|
||||
# default dark styling for standalone apps
|
||||
_DARK_STYLE = Path(_MODULE_PATH.parent, 'resources', 'qdarkstyle', 'style.qss')
|
||||
# ^ lumberyard style doesn't work for all widgets, so this can be direcly applied
|
||||
# to sidgets that look funny, they won't be a perfect match but also won't look odd
|
||||
# To Do: make optional and/or force only when standalone
|
||||
# adopt the Qt app style by default (use lumberyards style when it's the parent)
|
||||
# include a standlaone lumberyard-like styling also
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# https://doc.qt.io/qt-5/designer-using-a-ui-file-python.html
|
||||
# pattern #1
|
||||
|
||||
# pyside2-uic.exe form.ui > form.py
|
||||
# results might be something like this:
|
||||
|
||||
#from ui_form import Ui_Form
|
||||
# class UicWidget(QtWidgets.QWidget):
|
||||
#def __init__(self, parent=None):
|
||||
#super(Window, self).__init__(parent)
|
||||
#self.m_ui = Ui_Form()
|
||||
#self.m_ui.setupUi(self)
|
||||
|
||||
# pattern #2
|
||||
# that could also be represented like?
|
||||
|
||||
# class SomeWidget(Ui_Form, QtWidgets.QWidget):
|
||||
#def __init__(self, parent=None):
|
||||
#super(Window, self).__init__(parent)
|
||||
# self.setupUi(self)
|
||||
|
||||
# pattern 3, use QUiLoader
|
||||
# you need to know the main widget class (QtWidgets.QWidget), load .ui form within class
|
||||
#from PySide2.QtUiTools import QUiLoader
|
||||
#from PySide2 import QtWidgets
|
||||
#from PySide2.QtCore import QFile
|
||||
|
||||
#class MyForm(QtWidgets.QWidget):
|
||||
#def __init__(self, parent=None):
|
||||
#QtWidgets.QWidget.__init__(self, parent)
|
||||
|
||||
## read/load my .ui file
|
||||
#file = QFile("form.ui")
|
||||
#file.open(QFile.ReadOnly)
|
||||
#self.loader = QUiLoader()
|
||||
#self.my_widget = self.loader.load(file, self)
|
||||
#file.close()
|
||||
|
||||
## setup my own layout
|
||||
#layout = QtWidgets.QVBoxLayout()
|
||||
#layout.addWidget(self.my_widget)
|
||||
#self.setLayout(layout)
|
||||
|
||||
#if __name__ == '__main__':
|
||||
#app = QtWidgets.QApplication(sys.argv)
|
||||
#myapp = MyForm()
|
||||
#myapp.show()
|
||||
#sys.exit(app.exec_())
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# pattern 4 (I like this)
|
||||
#_UI_FILE = Path(_MODULE_PATH.parent, 'resources', 'example.ui')
|
||||
#_FORM_CLASS, _BASE_CLASS = ui_utils.from_ui_generate_form_and_base_class(_UI_FILE)
|
||||
|
||||
# here is basically what that returns ...
|
||||
#parsed_xml = xml.parse(ui_file)
|
||||
# form_class = parsed_xml.find('class').text # --> <class 'Ui_Form'>
|
||||
# widget_class = parsed_xml.find('widget').get('class') # --> <class 'PySide2.QtWidgets.QWidget'>
|
||||
|
||||
#class MyToolWidget(TemplateToolWidget):
|
||||
#def __init__(self, parent, *args, **kwargs):
|
||||
#super().__init__(parent, *args, **kwargs)
|
||||
|
||||
#my_tool = MyToolWidget()
|
||||
|
||||
|
||||
class TemplateToolWidget(_FORM_CLASS, _BASE_CLASS):
|
||||
def __init__(self, parent, logger=None, *args, **kwargs):
|
||||
'''A custom tool window with a demo set of template ui functionality'''
|
||||
|
||||
super().__init__(parent, *args, **kwargs)
|
||||
|
||||
self._logger = logger
|
||||
if not self._logger:
|
||||
try:
|
||||
self._logger = self.parent().logger
|
||||
except:
|
||||
self._logger = azpy.initialize_logger(self.objectName())
|
||||
|
||||
self.logger.debug("Method: {0}.{1}".format(__class__, '__init__'))
|
||||
|
||||
# lets hope the parent is a mainwindow?
|
||||
self.mainwindow = parent
|
||||
|
||||
self._project_directory = Path('C:\Lumberyard', 'Dev', 'Gems',
|
||||
'DccScriptingInterface', 'MockProject')
|
||||
|
||||
# uic adds a function to our class called setupUi,
|
||||
# calling this creates all the widgets from the .ui file
|
||||
self.setupUi(self)
|
||||
|
||||
# this one is local to this class
|
||||
self.setup_ui()
|
||||
|
||||
# Connect the interface controls
|
||||
self.connect_interface()
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# -- properties --------------------------------------------------------
|
||||
@property
|
||||
def logger(self):
|
||||
return self._logger
|
||||
|
||||
@logger.setter
|
||||
def logger(self, logger):
|
||||
self._logger = logger
|
||||
return self._logger
|
||||
|
||||
@logger.getter
|
||||
def logger(self):
|
||||
return self._logger
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def setup_ui(self):
|
||||
"""TODO: Doc String"""
|
||||
# override this method to inject your own ui widgets amd layout
|
||||
|
||||
self.logger.debug("Method: {0}.{1}".format(__class__, 'setup_ui'))
|
||||
|
||||
self.helpMenu = help_menu.HelpMenu(self.mainwindow, 'TemplateToolTool Help...', 'http://dccSI.com/NewTool')
|
||||
|
||||
_DARK_STYLE = Path(_MODULE_PATH.parent, 'resources', 'qdarkstyle', 'style.qss')
|
||||
|
||||
try:
|
||||
self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
|
||||
except Exception as e:
|
||||
self.logger.warning('warning! : {}'.format(e))
|
||||
|
||||
# override some of widgets to a better dark style
|
||||
self.WatchdogList_treeView.setStyleSheet(_DARK_STYLE.read_file())
|
||||
self.WatchdogList_treeView.setMinimumHeight(250)
|
||||
self.output_console_textEdit.setStyleSheet(_DARK_STYLE.read_file())
|
||||
self.output_console_textEdit.setMinimumHeight(250)
|
||||
pass
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def set_defaults(self):
|
||||
"""TODO: Doc String"""
|
||||
self.logger.debug("Method: {0}.{1}".format(__class__, 'set_defaults'))
|
||||
pass
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def connect_interface(self):
|
||||
"""TODO: Doc String"""
|
||||
self.logger.debug("Method: {0}.{1}".format(__class__, 'connect_interface'))
|
||||
# Connect widgets to methods
|
||||
# QtCore.QObject.connect(self.renameButton, QtCore.SIGNAL("clicked()"), self.NewCommand)
|
||||
pass
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def new_command(self):
|
||||
"""TODO: Doc String"""
|
||||
self.logger.debug("Method: {0}.{1}".format(__class__, 'new_command'))
|
||||
pass
|
||||
# ----------------------------------------------------------------------
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
class TemplateMainWindow(QtWidgets.QMainWindow):
|
||||
"""TODO"""
|
||||
|
||||
_ORG_TAG = 'Amazon_Lumberyard'
|
||||
_APP_TAG = 'DCCsi'
|
||||
|
||||
def __init__(self,
|
||||
parent=None,
|
||||
logger=None,
|
||||
app=None,
|
||||
custom_tool_widget=None,
|
||||
window_title='TemplateToolWidget',
|
||||
app_icon="icon.png",
|
||||
*args, **kwargs):
|
||||
"""TODO"""
|
||||
|
||||
# secret demo
|
||||
demo = kwargs.pop('demo', False)
|
||||
|
||||
self._logger = logger
|
||||
self._qapp = None
|
||||
self._name_uuid = '{0}_{1}'.format(self.__class__.__name__, uuid.uuid4())
|
||||
|
||||
if not self._logger:
|
||||
self._logger = azpy.initialize_logger(self._name_uuid)
|
||||
# self._logger = initialize_logger()
|
||||
|
||||
self._qapp = app or QtWidgets.QApplication.instance()
|
||||
if self._qapp is not None:
|
||||
pass
|
||||
else:
|
||||
# self.logger.debug('No QApplication has been instantiated')
|
||||
self._qapp = self._make_standalone_app(window_title)
|
||||
|
||||
super(TemplateMainWindow, self).__init__(parent=parent, *args, **kwargs)
|
||||
|
||||
# camel case because this is a Qt|Pyside2 widget method
|
||||
if self.objectName() == '':
|
||||
# Set a unique object name string so Maya can easily look it up
|
||||
self.setObjectName(self._name_uuid)
|
||||
|
||||
self.window_title = window_title
|
||||
if self.window_title == '':
|
||||
self.window_title = self.objectName()
|
||||
self.setWindowTitle(self.window_title)
|
||||
|
||||
self._style_sheet = None
|
||||
if not self._style_sheet:
|
||||
self._style_sheet = Path(_MODULE_PATH.parent, 'resources',
|
||||
'stylesheets', 'LYstyle.qss')
|
||||
self.setStyleSheet(self._style_sheet.read_file())
|
||||
|
||||
self.app_icon = app_icon
|
||||
|
||||
self._custom_widget = custom_tool_widget
|
||||
if self._custom_widget:
|
||||
self._custom_widget.parent(self)
|
||||
|
||||
self.setup_ui()
|
||||
if demo:
|
||||
custom_tool_widget = TemplateToolWidget(self)
|
||||
|
||||
self.add_custom_widget(custom_tool_widget)
|
||||
|
||||
# Setup the settings .ini
|
||||
_TYPE_TAG = 'TEMPLATE'
|
||||
self.settings = qt_settings.createSettings(TemplateMainWindow._ORG_TAG,
|
||||
TemplateMainWindow._APP_TAG,
|
||||
self.window_title, _TYPE_TAG)
|
||||
|
||||
# Read the saved settings
|
||||
self.read_settings()
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
def _make_standalone_app(self, name):
|
||||
useGUI = not '-no-gui' in sys.argv
|
||||
self.qapp = QtWidgets.QApplication(sys.argv) if useGUI else QtWidgets.QCoreApplication(sys.argv)
|
||||
self.qapp.setOrganizationName(TemplateMainWindow._ORG_TAG)
|
||||
self.qapp.setApplicationName('{app}:{tool}'.format(app=TemplateMainWindow._APP_TAG,
|
||||
tool=name))
|
||||
return self.qapp
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# -- properties --------------------------------------------------------
|
||||
@property
|
||||
def logger(self):
|
||||
return self._logger
|
||||
|
||||
@logger.setter
|
||||
def logger(self, logger):
|
||||
self._logger = logger
|
||||
return self._logger
|
||||
|
||||
@logger.getter
|
||||
def logger(self):
|
||||
return self._logger
|
||||
|
||||
@property
|
||||
def custom_widget(self):
|
||||
return self._lcustom_widget
|
||||
|
||||
@custom_widget.setter
|
||||
def custom_widget(self, custom_widget):
|
||||
self._custom_widget = custom_widget
|
||||
return self._custom_widget
|
||||
|
||||
@custom_widget.getter
|
||||
def custom_widget(self):
|
||||
return self._custom_widget
|
||||
|
||||
@property
|
||||
def qapp(self):
|
||||
return self._qapp
|
||||
|
||||
@qapp.setter
|
||||
def qapp(self, qapp):
|
||||
self._qapp = qapp
|
||||
return self._qapp
|
||||
|
||||
@qapp.getter
|
||||
def qapp(self):
|
||||
return self._qapp
|
||||
|
||||
@property
|
||||
def app_icon(self):
|
||||
return self._app_icon
|
||||
|
||||
@app_icon.setter
|
||||
def app_icon(self, icon):
|
||||
self._app_icon = QtGui.QIcon(icon)
|
||||
self.setWindowIcon(self._app_icon)
|
||||
return self._app_icon
|
||||
|
||||
@app_icon.getter
|
||||
def app_icon(self):
|
||||
return self._app_icon
|
||||
|
||||
def setIcon(self, icon):
|
||||
self.app_icon = icon
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def setup_ui(self, *args, **kwargs):
|
||||
# self.toolbar = QtWidgets.QToolBar()
|
||||
# self.addToolBar(self.toolbar)
|
||||
self.logger.debug("Method: {0}.{1}".format(__class__, 'setup_ui'))
|
||||
self.setIcon = self.app_icon
|
||||
|
||||
# Exit QAction on hotkey
|
||||
exit_tag = "Exit"
|
||||
exit_action = QtWidgets.QAction(exit_tag, self)
|
||||
exit_action.setShortcut("Ctrl+Q")
|
||||
exit_action.triggered.connect(self.close)
|
||||
|
||||
# basic menubar File > Exit event
|
||||
self.menu_bar = self.menuBar() # type: QMenuBar
|
||||
file_menu = self.menu_bar.addMenu("File") # type: QMenu
|
||||
file_menu.addAction(exit_tag, self.close)
|
||||
|
||||
# main widget
|
||||
self.central_widget = QtWidgets.QWidget(self)
|
||||
self.setCentralWidget(self.central_widget)
|
||||
|
||||
# layout initialize
|
||||
self.global_layout = QtWidgets.QVBoxLayout(self.central_widget)
|
||||
self.global_layout.setContentsMargins(8, 8, 8, 8)
|
||||
self.central_widget.setLayout(self.global_layout)
|
||||
|
||||
self.createStatusBar()
|
||||
|
||||
# TODO: create a progress bar
|
||||
# https://codeloop.org/how-to-create-progressbar-in-pyside2/amp/
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def add_custom_widget(self, custom_tool_widget=None):
|
||||
# Add our TemplateToolWidget
|
||||
if custom_tool_widget:
|
||||
self.custom_widget = custom_tool_widget
|
||||
self.custom_widget.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
|
||||
layout = QtWidgets.QFormLayout()
|
||||
self.global_layout.addLayout(layout)
|
||||
self.global_layout.addWidget(self.custom_widget)
|
||||
# else: # demo test tool
|
||||
# self.custom_widget = TemplateToolWidget(self)
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def createStatusBar(self):
|
||||
self.logger.debug("Method: {0}.{1}".format(__class__, 'createStatusBar'))
|
||||
self.myStatus = QtWidgets.QStatusBar()
|
||||
self.myStatus.showMessage("{0}: Ready".format(self.window_title), 3000)
|
||||
self.setStatusBar(self.myStatus)
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
@QtCore.Slot()
|
||||
def closeEvent(self, event=None, *args, **kwargs):
|
||||
"""Event which is run when window closes"""
|
||||
self.logger.debug("Method: {0}.{1}".format(__class__, 'closeEvent'))
|
||||
|
||||
close = QtWidgets.QMessageBox()
|
||||
result = close.question(self,
|
||||
"Confirm Exit...",
|
||||
"Are you sure you want to exit ?",
|
||||
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
|
||||
|
||||
if result == QtWidgets.QMessageBox.Yes:
|
||||
if event:
|
||||
event.accept()
|
||||
self.logger.debug("Closing: {0}".format(self.objectName()))
|
||||
self.write_settings()
|
||||
self.qapp.instance().quit
|
||||
self.qapp.exit()
|
||||
else:
|
||||
if event:
|
||||
event.ignore()
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def write_settings(self):
|
||||
"""Writes out the windows settings"""
|
||||
self.logger.debug("Method: {0}.{1}".format(__class__, 'write_settings'))
|
||||
# main window
|
||||
# saves window size and position
|
||||
self.settings.setValue("geometry", self.saveGeometry())
|
||||
# saves the state of window's toolbars and dockWidgets
|
||||
self.settings.setValue("windowState", self.saveState())
|
||||
# widgets, save a setting to persist
|
||||
# self.settings.setValue("textFilePath", self.textFileLineEdit.text())
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def read_settings(self):
|
||||
"""Reads in the windows settings"""
|
||||
self.logger.debug("Method: {0}.{1}".format(__class__, 'read_settings'))
|
||||
# main window
|
||||
self.restoreGeometry(self.settings.value("geometry")) # restores window size and position
|
||||
self.restoreState(self.settings.value("windowState")) # restores the state of window's toolbars and dockWidgets
|
||||
# widgets, restore something from settings
|
||||
# if self.settings.value("textFilePath"):
|
||||
# textFile = self.qt_settings.value("textFilePath",).toString()
|
||||
# self.textFileLineEdit.setText(textFile)
|
||||
# ----------------------------------------------------------------------
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
class TestToolWidget(TemplateToolWidget):
|
||||
def __init__(self, parent, *args, **kwargs):
|
||||
'''A custom window with a demo set of ui widgets'''
|
||||
|
||||
super(TestToolWidget, self).__init__(parent=parent, *args, **kwargs)
|
||||
|
||||
self.logger.debug("Method: {0}.{1}".format(__class__, '__init__'))
|
||||
|
||||
# this one is local to this class
|
||||
self.extend_ui()
|
||||
|
||||
self._test_property = 'Property Not set'
|
||||
self.logger.debug("self._test_property: {0}".format(self._test_property))
|
||||
|
||||
self.class_tests()
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# -- properties --------------------------------------------------------
|
||||
@property
|
||||
def test_property(self):
|
||||
return self._test_property
|
||||
|
||||
@test_property.setter
|
||||
def test_property(self, test_property):
|
||||
self._test_property = test_property
|
||||
return self._test_property
|
||||
|
||||
@test_property.getter
|
||||
def test_property(self):
|
||||
return self._test_property
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def class_tests(self):
|
||||
"""TODO: Doc String"""
|
||||
self.logger.debug("Method: {0}.{1}".format(__class__, 'class_tests'))
|
||||
self.test_property = 'TEST PROPERTY'
|
||||
self.logger.debug("self._test_property: {0}".format(self.test_property))
|
||||
return
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def setup_ui(self):
|
||||
"""TODO: Doc String"""
|
||||
self.logger.debug("Method: {0}.{1}".format(__class__, 'setup_ui'))
|
||||
return
|
||||
|
||||
def extend_ui(self):
|
||||
"""TODO: Doc String"""
|
||||
self.logger.debug("Method: {0}.{1}".format(__class__, 'extend_ui'))
|
||||
|
||||
# this would be local to this class, runs outside _init__
|
||||
#
|
||||
# self.setup_ui()
|
||||
|
||||
# Connect the interface controls
|
||||
# self.connect_interface()
|
||||
|
||||
self._watchdog_list = QtGui.QStandardItemModel(parent=self)
|
||||
|
||||
self._project_directory = Path('C:\Lumberyard', 'Dev', 'Gems',
|
||||
'DccScriptingInterface', 'MockProject')
|
||||
|
||||
# we want 2 columns
|
||||
self._watchdog_list.setColumnCount(2)
|
||||
|
||||
# set up a temp watch dog folder watch list (as a model)
|
||||
self._watchdog_list.setHorizontalHeaderLabels(['Path', 'State'])
|
||||
|
||||
_DARK_STYLE = Path(_MODULE_PATH.parent, 'resources', 'qdarkstyle', 'style.qss')
|
||||
|
||||
# override some of widgets to a better dark style
|
||||
self.WatchdogList_treeView.setStyleSheet(_DARK_STYLE.read_file())
|
||||
self.WatchdogList_treeView.setMinimumHeight(250)
|
||||
self.WatchdogList_treeView.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
||||
self.WatchdogList_treeView.setModel(self._watchdog_list)
|
||||
self.WatchdogList_treeView.setUniformRowHeights(True)
|
||||
|
||||
# populate treeView data
|
||||
parent_item = self._watchdog_list.invisibleRootItem()
|
||||
first_row = QtGui.QStandardItem(self._project_directory)
|
||||
self._watchdog_list.appendRow(first_row)
|
||||
# span container columns
|
||||
self.WatchdogList_treeView.setFirstColumnSpanned(0, self.WatchdogList_treeView.rootIndex(), True)
|
||||
|
||||
TemplateMainWindow = QtGui.QStandardItem('\\Assets\\some\\path')
|
||||
self._watchdog_list.appendRow(TemplateMainWindow)
|
||||
first_row_state = QtGui.QStandardItem('{0}'.format('Active'))
|
||||
# TemplateMainWindow.appendRow(first_row_state)
|
||||
|
||||
second_row = QtGui.QStandardItem('\\Assets\\another\\path')
|
||||
self._watchdog_list.appendRow(second_row)
|
||||
second_row_state = QtGui.QStandardItem('{0}'.format('Active'))
|
||||
# second_row.appendRow(second_row_state)
|
||||
|
||||
first_row.appendRow([TemplateMainWindow, second_row])
|
||||
# span container columns
|
||||
self.WatchdogList_treeView.setFirstColumnSpanned(0, self.WatchdogList_treeView.rootIndex(), True)
|
||||
|
||||
self.output_console_textEdit.setStyleSheet(_DARK_STYLE.read_file())
|
||||
self.output_console_textEdit.setMinimumHeight(250)
|
||||
|
||||
# TODO: create a progress bar
|
||||
# https://codeloop.org/how-to-create-progressbar-in-pyside2/amp/
|
||||
pass
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def set_defaults(self):
|
||||
"""TODO: Doc String"""
|
||||
self.logger.debug("Method: {0}.{1}".format(__class__, 'extend_ui'))
|
||||
pass
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def connect_interface(self):
|
||||
"""TODO: Doc String"""
|
||||
self.logger.debug("Method: {0}.{1}".format(__class__, 'connect_interface'))
|
||||
# Connect widgets to methods
|
||||
# QtCore.QObject.connect(self.renameButton, QtCore.SIGNAL("clicked()"), self.NewCommand)
|
||||
pass
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def new_command(self):
|
||||
"""TODO: Doc String"""
|
||||
self.logger.debug("Method: {0}.{1}".format(__class__, 'new_command'))
|
||||
pass
|
||||
# ----------------------------------------------------------------------
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
if __name__ == '__main__':
|
||||
"""Run this file as main"""
|
||||
import sys
|
||||
|
||||
_TEST_APP_NAME = '{0}-{1}'.format(_MODULENAME, 'TEST')
|
||||
|
||||
_LOGGER = azpy.initialize_logger(_TEST_APP_NAME,
|
||||
log_to_file=True,
|
||||
default_log_level=_logging.DEBUG)
|
||||
|
||||
from azpy.constants import STR_CROSSBAR
|
||||
_LOGGER.info(STR_CROSSBAR)
|
||||
_LOGGER.info("{0} :: if __name__ == '__main__':".format(_MODULENAME))
|
||||
_LOGGER.info(STR_CROSSBAR)
|
||||
|
||||
# test from_ui_generate_form_and_base_class
|
||||
_FORM_CLASS, _BASE_CLASS = ui_utils.from_ui_generate_form_and_base_class(_UI_FILE)
|
||||
|
||||
_LOGGER.info(_FORM_CLASS)
|
||||
_LOGGER.info(_BASE_CLASS)
|
||||
|
||||
_LOGGER.info("Starting App: {0} ...".format(_TEST_APP_NAME))
|
||||
|
||||
while 0:
|
||||
# Test 1, MainWindow with no widget (just the frame)
|
||||
_MAINWINDOW = TemplateMainWindow(logger=_LOGGER)
|
||||
_LOGGER.info(_MAINWINDOW.objectName())
|
||||
_LOGGER.info(_MAINWINDOW.settings)
|
||||
_LOGGER.info(_MAINWINDOW.settings.fileName())
|
||||
_MAINWINDOW.setWindowTitle(_TEST_APP_NAME)
|
||||
_MAINWINDOW.show()
|
||||
break
|
||||
|
||||
while 0:
|
||||
# test 2, demo forces a template widget to be created internally
|
||||
_MAINWINDOW = TemplateMainWindow(logger=_LOGGER,
|
||||
demo=True) #<-- template demo
|
||||
_LOGGER.info(_MAINWINDOW.objectName())
|
||||
_LOGGER.info(_MAINWINDOW.settings)
|
||||
_LOGGER.info(_MAINWINDOW.settings.fileName())
|
||||
_MAINWINDOW.setWindowTitle(_TEST_APP_NAME)
|
||||
_MAINWINDOW.show()
|
||||
break
|
||||
|
||||
while 1:
|
||||
# test 3, add a custom tool widget
|
||||
_MAINWINDOW = TemplateMainWindow(logger=_LOGGER)
|
||||
_CUSTOM_WIDGET = TemplateToolWidget(parent=_MAINWINDOW)
|
||||
_MAINWINDOW.add_custom_widget(_CUSTOM_WIDGET)
|
||||
_LOGGER.info(_MAINWINDOW.objectName())
|
||||
_LOGGER.info(_MAINWINDOW.settings)
|
||||
_LOGGER.info(_MAINWINDOW.settings.fileName())
|
||||
_MAINWINDOW.setWindowTitle(_TEST_APP_NAME)
|
||||
_MAINWINDOW.show()
|
||||
_MAINWINDOW.close() # test the close function
|
||||
break
|
||||
|
||||
del _LOGGER
|
||||
sys.exit(_MAINWINDOW.qapp.exec_())
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
|
||||
import os
|
||||
|
||||
from azpy import env_bool
|
||||
from azpy.env_bool import env_bool
|
||||
from azpy.constants import ENVAR_DCCSI_GDEBUG
|
||||
from azpy.constants import ENVAR_DCCSI_DEV_MODE
|
||||
|
||||
|
||||
@@ -0,0 +1,736 @@
|
||||
# coding:utf-8
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
# -- This line is 75 characters -------------------------------------------
|
||||
from __future__ import unicode_literals
|
||||
|
||||
"""
|
||||
Module Description:
|
||||
DccScriptingInterface\\azpy\\shared\\synthetic_env.py
|
||||
|
||||
Stands up a synthetic version of the environment in:
|
||||
DccScriptingInterface\\Launchers\\windows\\Env.bat
|
||||
|
||||
DccScriptingInterface a.k.a. DCCsi
|
||||
|
||||
This module (softly) assumes that the cwd is the DCCsi
|
||||
AND that you can get access to imports from azpy
|
||||
|
||||
The configures a synthetic version of the default DCCsi env,
|
||||
which includes additional code access paths and some other settings.
|
||||
|
||||
It does not assume that the external environment is configured.
|
||||
It provides best guess defaults for the environment (when they are not set.)
|
||||
It will not overwrite the envars that are configured.
|
||||
|
||||
To do this, it uses the pattern:
|
||||
_SOME_ENVAR = os.getenv('ENVAR_TAG', <default>)
|
||||
|
||||
The environment is conveniently packed into a dictionary,
|
||||
this allows the dictionar to be imported into another module,
|
||||
the import will resolve and standup the synthetic environment.
|
||||
|
||||
Configures several useful environment config settings and paths,
|
||||
[key] : [value]
|
||||
|
||||
# this is the required base environment
|
||||
LY_PROJECT : name of project (project directory)
|
||||
LY_DEV : path to Lumberyard \dev root
|
||||
LY_PROJECT_PATH : path to project dir
|
||||
DCCSIG_PATH : path to the DCCsi Gem root
|
||||
DCCSI_AZPY_PATH * : path to azpy Python API (code)
|
||||
DCCSI_SDK_PATH : path to associated (non-api code) DCC SDK
|
||||
|
||||
# nice to haves in base env to define core support
|
||||
DCCSI_GDEBUG : sets global debug prints
|
||||
DCCSI_DEV_MODE : extends support like early debugger attachment
|
||||
DCCSI_GDEBUGGER : default debugger (WING)
|
||||
|
||||
DCCsi has been (experimentally) tested with : PY27, PY37
|
||||
- py27 for DCC tools like Maya
|
||||
- py37 for Lumberyard
|
||||
- it probably will work with 3.6 (substance)
|
||||
|
||||
:: Default version py37 has a launcher
|
||||
(activates the env, starts py interpreter)
|
||||
set DCCSI_PY_BASE=%DCCSI_PYTHON_INSTALL%\python.exe
|
||||
|
||||
:: shared location for 64bit python 3.7 BASE location
|
||||
set DCCSI_PY_DCCSI=%DCCSI_LAUNCHERS_PATH%\Launch_pyBASE.bat
|
||||
|
||||
:: Override DCCSI_PY_DCCSI to set a defualt version for the DCCsi
|
||||
|
||||
:: ide and debugger plugs
|
||||
:: for instance with WingIDE this defines using the default py interpreter
|
||||
${DCCSI_PY_DEFAULT}
|
||||
|
||||
::Other python runtimes can be defined, for py27 we use this
|
||||
:: shared location for 64bit DCCSI_PY_MAYA python 2.7 DEV location
|
||||
set DCCSI_PY_MAYA=%MAYA_LOCATION%\bin\mayapy.exe
|
||||
:: wingIDE can use more then one defined/managed interpreters
|
||||
:: allowing you to _G_DEBUG code in multiple runtimes in one session
|
||||
${DCCSI_PY_MAYA}
|
||||
|
||||
# related to the WING as the default DCCSI_GDEBUGGER
|
||||
DCCSI_WING_VERSION_MAJOR : the major version of Wing IDE (default IDE)
|
||||
DCCSI_WING_VERSION_MINOR : minor version #
|
||||
WINGHOME : path for wing (for debug) integration
|
||||
|
||||
NOTES:
|
||||
The default IDE and debugger is WING primarily because of author preference.
|
||||
Other options are available to configure out-of-the-box (PyCharm)
|
||||
"""
|
||||
# -------------------------------------------------------------------------
|
||||
# built in's
|
||||
import os
|
||||
import sys
|
||||
import site
|
||||
import re
|
||||
#import inspect
|
||||
import json
|
||||
|
||||
import logging as _logging
|
||||
from collections import OrderedDict
|
||||
|
||||
# Lumberyard and Atom standalone should be launched in py3.7+
|
||||
# because of use of pathlib (unless externally provided in py2.7)
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
os.environ['PYTHONINSPECT'] = 'True'
|
||||
|
||||
_MODULE_PATH = os.path.realpath(__file__) # To Do: what if frozen?
|
||||
_DCCSIG_PATH = os.path.normpath(os.path.join(_MODULE_PATH, '../..'))
|
||||
_DCCSIG_PATH = os.getenv('DCCSIG_PATH', _DCCSIG_PATH)
|
||||
site.addsitedir(_DCCSIG_PATH)
|
||||
print(_DCCSIG_PATH)
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Lumberyard extensions
|
||||
from pathlib import Path
|
||||
|
||||
# set up global space, logging etc.
|
||||
import azpy
|
||||
from azpy.env_bool import env_bool
|
||||
from azpy.constants import ENVAR_DCCSI_GDEBUG
|
||||
from azpy.constants import ENVAR_DCCSI_DEV_MODE
|
||||
|
||||
_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False)
|
||||
_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False)
|
||||
|
||||
_PACKAGENAME = 'DCCsi.azpy.sunthetic_env'
|
||||
|
||||
_log_level = int(20)
|
||||
if _G_DEBUG:
|
||||
_log_level = int(10)
|
||||
_LOGGER = azpy.initialize_logger(_PACKAGENAME,
|
||||
log_to_file=True,
|
||||
default_log_level=_log_level)
|
||||
|
||||
_LOGGER.debug('Starting up: {0}.'.format({_PACKAGENAME}))
|
||||
_LOGGER.debug('_DCCSIG_PATH: {}'.format(_DCCSIG_PATH))
|
||||
_LOGGER.debug('_G_DEBUG: {}'.format(_G_DEBUG))
|
||||
_LOGGER.debug('_DCCSI_DEV_MODE: {}'.format(_DCCSI_DEV_MODE))
|
||||
|
||||
if _DCCSI_DEV_MODE:
|
||||
from azpy.test.entry_test import connect_wing
|
||||
foo = connect_wing()
|
||||
|
||||
# we can go ahead and just make sure the the DCCsi env is set
|
||||
# config is SO generic this ensures we are importing a specific one
|
||||
_spec_dccsi_config = importlib.util.spec_from_file_location("dccsi.config",
|
||||
Path(_DCCSIG_PATH,
|
||||
"config.py"))
|
||||
_dccsi_config = importlib.util.module_from_spec(_spec_dccsi_config)
|
||||
_spec_dccsi_config.loader.exec_module(_dccsi_config)
|
||||
|
||||
settings = _dccsi_config.get_config_settings()
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# Lumberyard extensions
|
||||
from azpy.constants import *
|
||||
from azpy.shared.common.core_utils import walk_up_dir
|
||||
from azpy.shared.common.core_utils import get_stub_check_path
|
||||
|
||||
_DCCSI_PYTHON_LIB_PATH = os.getenv(ENVAR_DCCSI_PYTHON_LIB_PATH,
|
||||
PATH_DCCSI_PYTHON_LIB_PATH)
|
||||
_LOGGER.debug('Dccsi Lib Path: {0}'.format(_DCCSI_PYTHON_LIB_PATH))
|
||||
|
||||
if os.path.exists(_DCCSI_PYTHON_LIB_PATH):
|
||||
site.addsitedir(_DCCSI_PYTHON_LIB_PATH) # add access
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# post-bootstrap global space
|
||||
_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False)
|
||||
_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False)
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
FRMT_LOG_LONG = ("[%(name)s][%(levelname)s] >> "
|
||||
"%(message)s (%(asctime)s; %(filename)s:%(lineno)d)")
|
||||
_PACKAGENAME = 'azpy.synthetic_env'
|
||||
|
||||
_logging.basicConfig(level=_logging.INFO,
|
||||
format=FRMT_LOG_LONG,
|
||||
datefmt='%m-%d %H:%M')
|
||||
_LOGGER = _logging.getLogger(_PACKAGENAME)
|
||||
|
||||
_log_level = int(10)
|
||||
console_handler = _logging.StreamHandler(sys.stdout)
|
||||
console_handler.setLevel(_log_level)
|
||||
formatter = _logging.Formatter(FRMT_LOG_LONG)
|
||||
console_handler.setFormatter(formatter)
|
||||
_LOGGER.addHandler(console_handler)
|
||||
_LOGGER.setLevel(_log_level)
|
||||
|
||||
_LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME}))
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# This module is semi-standalone (no azpy YET) to avoid circular imports
|
||||
# To Do: figure out how to bets NOPT dup this methods (there are also moudles)
|
||||
# we want to run this potentially with no preformed env
|
||||
# and thus we may not have full acess to the DCCsi and azpy package
|
||||
# -------------------------------------------------------------------------
|
||||
def get_current_project(dev_folder):
|
||||
boostrap_filepath = Path(dev_folder, "bootstrap.cfg")
|
||||
bootstrap = open(boostrap_filepath, "r")
|
||||
game_project_regex = re.compile("^sys_game_folder\s*=\s*(.*)")
|
||||
for line in bootstrap:
|
||||
game_folder_match = game_project_regex.match(line)
|
||||
if game_folder_match:
|
||||
return game_folder_match.group(1)
|
||||
return None
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# TO DO: Move to a util package or module
|
||||
def return_stub(stub='dccsi_stub'):
|
||||
'''Take a file name (stub) and returns the directory of the file (stub)'''
|
||||
_dir_to_last_file = None
|
||||
if _dir_to_last_file is None:
|
||||
path = Path(__file__).absolute()
|
||||
while 1:
|
||||
path, tail = Path(path).split()
|
||||
if Path(path, stub).is_file():
|
||||
break
|
||||
if (len(tail) == 0):
|
||||
path = ""
|
||||
if _G_DEBUG:
|
||||
_LOGGER.debug('~Not able to find the path to that file '
|
||||
'(stub) in a walk-up from currnet path.')
|
||||
break
|
||||
_dir_to_last_file = path
|
||||
|
||||
return _dir_to_last_file
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def get_stub_check_path(in_path, check_stub='engineroot.txt'):
|
||||
'''
|
||||
Returns the branch root directory of the dev\'engineroot.txt'
|
||||
(... or you can pass it another known stub)
|
||||
|
||||
so we can safely build relative filepaths within that branch.
|
||||
|
||||
If the stub is not found, it returns None
|
||||
'''
|
||||
path = Path(in_path).absolute()
|
||||
|
||||
while 1:
|
||||
test_path = Path(path, check_stub)
|
||||
|
||||
if test_path.is_file():
|
||||
return Path(test_path)
|
||||
|
||||
else:
|
||||
path, tail = (path.parent, path.name)
|
||||
|
||||
if (len(tail) == 0):
|
||||
return None
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# TO DO: Move to a util package or module
|
||||
def resolve_envar_path(envar='LY_DEV',
|
||||
start_path=__file__,
|
||||
check_stub='engineroot.txt',
|
||||
dir_name='dev',
|
||||
return_posix_str=False):
|
||||
"""This is meant to resolve a '\dev' path for LY
|
||||
|
||||
First we validate the start_path (or we can't walk)
|
||||
|
||||
Then we walk up the startPath looking for stub
|
||||
engineroot.txt is the default file marker for lumberyard
|
||||
|
||||
That is a pretty safe indicator that we found the right '\dev'
|
||||
|
||||
Second it checks if the env var 'LY_DEV' is set, use that instead!
|
||||
|
||||
"""
|
||||
|
||||
fallback = None
|
||||
|
||||
# amke sure the start_path exists, otherwise we have nothing to walk up
|
||||
try:
|
||||
start_path = Path(start_path)
|
||||
start_path.exists()
|
||||
if start_path.is_dir():
|
||||
fallback = start_path
|
||||
elif start_path.is_file():
|
||||
fallback = start_path.parent
|
||||
except Exception as e:
|
||||
_LOGGER.info('Does NOT exist, no valid path: {0}'.format(start_path))
|
||||
raise EnvironmentError
|
||||
|
||||
# generate a fallback based on finding know stub
|
||||
# known stub is probably more reliable then dir name
|
||||
|
||||
fallback_stub = get_stub_check_path(fallback, check_stub)
|
||||
# there is alos a chance this comes back None
|
||||
|
||||
# cast from is_file to directory
|
||||
if fallback_stub and fallback_stub.is_file():
|
||||
fallback = fallback_stub.parent
|
||||
|
||||
# check if it's set in env and fetch, or set to fallback
|
||||
envar_path = Path(os.getenv(envar, fallback))
|
||||
try:
|
||||
envar_path.exists()
|
||||
envar_path.is_dir()
|
||||
envar_path.name.lower == dir_name
|
||||
except Exception as e:
|
||||
_LOGGER.info('Not a lumbertard \dev path: {0}'.format(e))
|
||||
raise EnvironmentError
|
||||
|
||||
# last resort see if you can walk up to the dir name
|
||||
if not envar_path:
|
||||
envar_path = walk_up_dir(start_path, dir_name)
|
||||
|
||||
if envar_path and Path(envar_path).exists():
|
||||
# box module can't serialize WindowsPath objects
|
||||
# so Path object being stashed in this dict need .resolve() or .as_posix()
|
||||
if return_posix_str:
|
||||
return Path(envar_path).as_posix()
|
||||
else:
|
||||
return Path(envar_path)
|
||||
else:
|
||||
return None
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# setting up storage dict, only using a Box (super dict) instead
|
||||
_SYNTH_ENV_DICT = OrderedDict()
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# --- Build Defaults ------------------------------------------------------
|
||||
def stash_env(_SYNTH_ENV_DICT = OrderedDict()):
|
||||
"""This block attempts to fetch or derive best guess fallback"""
|
||||
|
||||
# basic default fall backs (in case no environment exists)
|
||||
# this first set is just the expected base_env
|
||||
|
||||
_LOGGER.info(STR_CROSSBAR)
|
||||
_LOGGER.info('~ stash_env(), setting module fallbacks')
|
||||
|
||||
# box module can't serialize WindowsPath objects
|
||||
# so Path object being stashed in this dict need .resolve() or .as_posix()
|
||||
_THIS_MODULE_PATH = Path(__file__).as_posix()
|
||||
|
||||
# company name from env or default
|
||||
# TAG_DEFAULT_COMPANY = str('Amazon.Lumberyard')
|
||||
_G_COMPANY_NAME = os.getenv(ENVAR_COMPANY,
|
||||
TAG_DEFAULT_COMPANY)
|
||||
|
||||
# we want to pack these all into a easy access dict
|
||||
_SYNTH_ENV_DICT[ENVAR_COMPANY] = _G_COMPANY_NAME
|
||||
|
||||
# <ly>\dev Lumberyard ROOT PATH
|
||||
# someone decided to use this as a root stub (for similar reasons in C++?)
|
||||
# STUB_LY_DEV = str('engineroot.txt')
|
||||
# I don't own \dev so I didn't want to check in anything new there
|
||||
_LY_DEV = resolve_envar_path(ENVAR_LY_DEV, # envar
|
||||
_THIS_MODULE_PATH, # search path
|
||||
STUB_LY_DEV, # stub
|
||||
TAG_DIR_LY_DEV) # dir
|
||||
|
||||
_SYNTH_ENV_DICT[ENVAR_LY_DEV] = _LY_DEV.as_posix()
|
||||
|
||||
# project name is a string, it should be project dir name
|
||||
# for siloed testing and a purely synthetc env (nothing previously set)
|
||||
# the default should probably be the 'DccScriptingInterface' (DCCsi)
|
||||
# we also have a 'MockProject' sanndbox/silo, but we want to reseve that
|
||||
# for testing overrides of the default synthetic env
|
||||
|
||||
# we can do two things here,
|
||||
# first we can try to fetch from the env os.getenv('LY_PROJECT')
|
||||
# If comes back None, allows you to specify a default fallback
|
||||
# changed to just make the fallback what is set in boostrap
|
||||
# so now it's less of a fallnack and more correct if not
|
||||
# explicitly set
|
||||
_LY_PROJECT = os.getenv(ENVAR_LY_PROJECT,
|
||||
get_current_project(_LY_DEV))
|
||||
_SYNTH_ENV_DICT[ENVAR_LY_PROJECT] = _LY_PROJECT
|
||||
|
||||
_LY_BUILD_DIR_NAME = os.getenv(ENVAR_LY_BUILD_DIR_NAME,
|
||||
TAG_DIR_LY_BUILD)
|
||||
_SYNTH_ENV_DICT[ENVAR_LY_BUILD_DIR_NAME] = _LY_BUILD_DIR_NAME
|
||||
|
||||
# pattern for the above is (and will be repeated)
|
||||
# _SOME_ENVAR = resolve_envar_path('ENVAR',
|
||||
# 'path\\to\\start\\search',
|
||||
# 'knownStubFileName',
|
||||
# 'knownDirName')
|
||||
# checks env for ENVAR first,
|
||||
# then walks up search path looking for stub (fallback 0),
|
||||
# then walks up niavely looking for a dirName (fallback 1)
|
||||
|
||||
# other paths that don't have a stub to search for as a fallback
|
||||
# can be stashed in this manner
|
||||
|
||||
# variable containers for all of the default/base env config dccsi entry
|
||||
# setting most basic defaults, not using an module import dependancies
|
||||
# we can't know where the user installed or set up any of these,
|
||||
# so we guess based on how I set up the original dev environment
|
||||
|
||||
# -- envar --
|
||||
_LY_BUILD_PATH = Path(os.getenv(ENVAR_LY_BUILD_PATH,
|
||||
PATH_LY_BUILD_PATH))
|
||||
_SYNTH_ENV_DICT[ENVAR_LY_BUILD_PATH] = _LY_BUILD_PATH.as_posix()
|
||||
|
||||
# -- envar --
|
||||
_LY_BIN_PATH = Path(os.getenv(ENVAR_LY_BIN_PATH,
|
||||
PATH_LY_BIN_PATH))
|
||||
# some of these need hard checks
|
||||
if not _LY_BIN_PATH.exists():
|
||||
raise Exception('LY_BIN_PATH does NOT exist: {0}'.format(_LY_BIN_PATH))
|
||||
else:
|
||||
_SYNTH_ENV_DICT[ENVAR_LY_BIN_PATH] = _LY_BIN_PATH.as_posix()
|
||||
# adding to sys.path apparently doesn't work for .dll locations like Qt
|
||||
os.environ['PATH'] = _LY_BIN_PATH.as_posix() + os.pathsep + os.environ['PATH']
|
||||
|
||||
# -- envar --
|
||||
# if that stub marker doesn't exist assume DCCsi path (fallback 1)
|
||||
_LY_PROJECT_PATH = Path(os.getenv(ENVAR_LY_PROJECT_PATH,
|
||||
Path(_LY_DEV, _LY_PROJECT)))
|
||||
_SYNTH_ENV_DICT[ENVAR_LY_PROJECT_PATH] = _LY_PROJECT_PATH.as_posix()
|
||||
|
||||
# -- envar --
|
||||
_DCCSIG_PATH = resolve_envar_path(ENVAR_DCCSIG_PATH, # envar
|
||||
_THIS_MODULE_PATH, # search path
|
||||
STUB_LY_ROOT_DCCSI, # stub name
|
||||
TAG_DEFAULT_PROJECT) # dir
|
||||
_SYNTH_ENV_DICT[ENVAR_DCCSIG_PATH] = _DCCSIG_PATH.as_posix()
|
||||
|
||||
# -- envar --
|
||||
_AZPY_PATH = Path(os.getenv(ENVAR_DCCSI_AZPY_PATH,
|
||||
Path(_DCCSIG_PATH, TAG_DIR_DCCSI_AZPY)))
|
||||
_SYNTH_ENV_DICT[ENVAR_DCCSI_AZPY_PATH] = _AZPY_PATH.as_posix()
|
||||
|
||||
# -- envar --
|
||||
_DCCSI_SDK_PATH = Path(os.getenv(ENVAR_DCCSI_SDK_PATH,
|
||||
Path(_DCCSIG_PATH, TAG_DIR_DCCSI_SDK)))
|
||||
_SYNTH_ENV_DICT[ENVAR_DCCSI_SDK_PATH] = _DCCSI_SDK_PATH.as_posix()
|
||||
|
||||
# -- envar --
|
||||
# external dccsi site-packages
|
||||
_DCCSI_PYTHON_LIB_PATH = Path(os.getenv(ENVAR_DCCSI_PYTHON_LIB_PATH,
|
||||
PATH_DCCSI_PYTHON_LIB_PATH))
|
||||
_SYNTH_ENV_DICT[ENVAR_DCCSI_PYTHON_LIB_PATH] = _DCCSI_PYTHON_LIB_PATH.as_posix()
|
||||
|
||||
# -- envar --
|
||||
# extend to py36 (conda env) and interpreter (wrapped as a .bat file)
|
||||
_DEFAULT_PY_PATH = Path(_DCCSIG_PATH, TAG_DEFAULT_PY)
|
||||
_DEFAULT_PY_PATH = Path(os.getenv(ENVAR_DCCSI_PY_DEFAULT,
|
||||
_DEFAULT_PY_PATH))
|
||||
_SYNTH_ENV_DICT[ENVAR_DCCSI_PY_DEFAULT] = _DEFAULT_PY_PATH.as_posix()
|
||||
|
||||
# -- envar --
|
||||
# wing ide vars
|
||||
_WINGHOME_DEFAULT_PATH = Path(os.getenv(ENVAR_WINGHOME,
|
||||
PATH_DEFAULT_WINGHOME))
|
||||
_SYNTH_ENV_DICT[ENVAR_WINGHOME] = _WINGHOME_DEFAULT_PATH.as_posix()
|
||||
|
||||
# -- done --
|
||||
return _SYNTH_ENV_DICT
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def init_ly_pyside(env_dict=_SYNTH_ENV_DICT):
|
||||
"""sets access to lumberyards Qt dlls and PySide"""
|
||||
|
||||
# -- envar --
|
||||
_QTFORPYTHON_PATH = Path(os.getenv(ENVAR_QTFORPYTHON_PATH,
|
||||
PATH_QTFORPYTHON_PATH))
|
||||
# some of these need hard checks
|
||||
if not _QTFORPYTHON_PATH.exists():
|
||||
raise Exception('QTFORPYTHON_PATH does NOT exist: {0}'.format(_QTFORPYTHON_PATH))
|
||||
else:
|
||||
_SYNTH_ENV_DICT[ENVAR_QTFORPYTHON_PATH] = _QTFORPYTHON_PATH.as_posix()
|
||||
# ^ some of these should be put on sys.path and/or PYTHONPATH or PATH
|
||||
#os.environ['PATH'] = _QTFORPYTHON_PATH.as_posix() + os.pathsep + os.environ['PATH']
|
||||
site.addsitedir(_QTFORPYTHON_PATH.as_posix()) # PYTHONPATH
|
||||
|
||||
# -- envar --
|
||||
_QT_PLUGIN_PATH = Path(os.getenv(ENVAR_QT_PLUGIN_PATH,
|
||||
PATH_QT_PLUGIN_PATH))
|
||||
# some of these need hard checks
|
||||
if not _QT_PLUGIN_PATH.exists():
|
||||
raise Exception('QT_PLUGIN_PATH does NOT exist: {0}'.format(_QT_PLUGIN_PATH))
|
||||
else:
|
||||
_SYNTH_ENV_DICT[ENVAR_QT_PLUGIN_PATH] = _QT_PLUGIN_PATH.as_posix()
|
||||
# https://stackoverflow.com/questions/214852/python-module-dlls
|
||||
os.environ['PATH'] = _QT_PLUGIN_PATH.as_posix() + os.pathsep + os.environ['PATH']
|
||||
|
||||
|
||||
|
||||
QTFORPYTHON_PATH = Path.joinpath(LY_DEV,
|
||||
'Gems',
|
||||
'QtForPython',
|
||||
'3rdParty',
|
||||
'pyside2',
|
||||
'windows',
|
||||
'release').resolve()
|
||||
os.environ["DYNACONF_QTFORPYTHON_PATH"] = str(QTFORPYTHON_PATH)
|
||||
os.environ["QTFORPYTHON_PATH"] = str(QTFORPYTHON_PATH)
|
||||
sys.path.insert(1, str(QTFORPYTHON_PATH))
|
||||
site.addsitedir(str(QTFORPYTHON_PATH))
|
||||
|
||||
LY_BIN_PATH = Path.joinpath(LY_DEV,
|
||||
'windows_vs2019',
|
||||
'bin',
|
||||
'profile').resolve()
|
||||
os.environ["DYNACONF_LY_BIN_PATH"] = str(LY_BIN_PATH)
|
||||
os.environ["LY_BIN_PATH"] = str(LY_BIN_PATH)
|
||||
site.addsitedir(str(LY_BIN_PATH))
|
||||
sys.path.insert(1, str(LY_BIN_PATH))
|
||||
|
||||
QT_PLUGIN_PATH = Path.joinpath(LY_BIN_PATH,
|
||||
'EditorPlugins').resolve()
|
||||
os.environ["DYNACONF_QT_PLUGIN_PATH"] = str(QT_PLUGIN_PATH)
|
||||
os.environ["QT_PLUGIN_PATH"] = str(QT_PLUGIN_PATH)
|
||||
site.addsitedir(str(QT_PLUGIN_PATH))
|
||||
sys.path.insert(1, str(QT_PLUGIN_PATH))
|
||||
|
||||
QT_QPA_PLATFORM_PLUGIN_PATH = Path.joinpath(QT_PLUGIN_PATH,
|
||||
'platforms').resolve()
|
||||
os.environ["DYNACONF_QT_QPA_PLATFORM_PLUGIN_PATH"] = str(QT_QPA_PLATFORM_PLUGIN_PATH)
|
||||
os.environ["QT_QPA_PLATFORM_PLUGIN_PATH"] = str(QT_QPA_PLATFORM_PLUGIN_PATH)
|
||||
site.addsitedir(str(QT_QPA_PLATFORM_PLUGIN_PATH))
|
||||
sys.path.insert(1, str(QT_QPA_PLATFORM_PLUGIN_PATH))
|
||||
|
||||
# add Qt binaries to the Windows path to handle findings DLL file dependencies
|
||||
if sys.platform.startswith('win'):
|
||||
path = os.environ['PATH']
|
||||
newPath = ''
|
||||
newPath += str(LY_BIN_PATH) + os.pathsep
|
||||
newPath += str(Path.joinpath(QTFORPYTHON_PATH,
|
||||
'shiboken2').resolve()) + os.pathsep
|
||||
newPath += str(Path.joinpath(QTFORPYTHON_PATH,
|
||||
'PySide2').resolve()) + os.pathsep
|
||||
newPath += path
|
||||
os.environ['PATH']=newPath
|
||||
_LOGGER.debug('PySide2 bootstrapped PATH for Windows.')
|
||||
|
||||
try:
|
||||
import PySide2
|
||||
_LOGGER.debug('DCCsi, config.py: SUCCESS: import PySide2')
|
||||
_LOGGER.debug(PySide2)
|
||||
status = True
|
||||
except ImportError as e:
|
||||
_LOGGER.debug('DCCsi, config.py: FAILURE: import PySide2')
|
||||
status = False
|
||||
raise(e)
|
||||
|
||||
try:
|
||||
import shiboken2
|
||||
_LOGGER.debug('DCCsi, config.py: SUCCESS: import shiboken2')
|
||||
_LOGGER.debug(shiboken2)
|
||||
status = True
|
||||
except ImportError as e:
|
||||
_LOGGER.debug('DCCsi, config.py: FAILURE: import shiboken2')
|
||||
status = False
|
||||
raise(e)
|
||||
|
||||
return status
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# py 2 and 3 compatible iter
|
||||
def get_items(dict_object):
|
||||
for key in dict_object:
|
||||
yield key, dict_object[key]
|
||||
|
||||
def set_env(dict_object):
|
||||
for key, value in get_items(dict_object):
|
||||
try:
|
||||
os.environ[key] = value
|
||||
except EnvironmentError as e:
|
||||
_LOGGER.error('ERROR: {e}')
|
||||
return dict_object
|
||||
|
||||
# will trigger on any import
|
||||
# suggested use: from synthetic_env import _SYNTH_ENV_DICT
|
||||
#_SYNTH_ENV_DICT = set_env(_SYNTH_ENV_DICT)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def test_Qt():
|
||||
try:
|
||||
import PySide2
|
||||
print('PySide2: {0}'.format(Path(PySide2.__file__).as_posix()))
|
||||
# builtins.ImportError: DLL load failed: The specified procedure could not be found.
|
||||
from PySide2 import QtCore
|
||||
from PySide2 import QtWidgets
|
||||
except IOError as e:
|
||||
print('ERROR: {0}'.format(e))
|
||||
raise e
|
||||
|
||||
try:
|
||||
qapp = QtWidgets.QApplication([])
|
||||
except:
|
||||
# already exists
|
||||
qapp = QtWidgets.QApplication.instance()
|
||||
|
||||
try:
|
||||
buttonFlags = QtWidgets.QMessageBox.information(QtWidgets.QApplication.activeWindow(), 'title', 'ok')
|
||||
qapp.instance().quit
|
||||
qapp.exit()
|
||||
except Exception as e:
|
||||
print('ERROR: {0}'.format(e))
|
||||
raise e
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
def main(argv, env_dict_object, debug=False, devmode=False):
|
||||
import getopt
|
||||
try:
|
||||
opts, args = getopt.getopt(argv, "hvt:", ["verbose=", "test="])
|
||||
except getopt.GetoptError:
|
||||
# not logging, print to cmd line console
|
||||
print('synthetic_env.py -v <print_dict> -t <run_test>')
|
||||
sys.exit(2)
|
||||
|
||||
for opt, arg in opts:
|
||||
if opt == '-h':
|
||||
print('synthetic_env.py -v <print_dict> -t <run test>')
|
||||
sys.exit()
|
||||
|
||||
elif opt in ("-t", "--test"):
|
||||
debug = True
|
||||
devmode = True
|
||||
test()
|
||||
|
||||
elif opt in ("-v", "--verbose"):
|
||||
try:
|
||||
from box import Box
|
||||
except ImportError as e:
|
||||
print('ERROR: {0}'.format(e))
|
||||
raise e
|
||||
try:
|
||||
env_dict_object = Box(env_dict_object)
|
||||
print(str(env_dict_object.to_json(sort_keys=False,
|
||||
indent=4)))
|
||||
except Exception as e:
|
||||
print('ERROR: {0}'.format(e))
|
||||
raise e
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
###########################################################################
|
||||
# Main Code Block, runs this script as main (testing)
|
||||
# -------------------------------------------------------------------------
|
||||
if __name__ == '__main__':
|
||||
# run simple tests?
|
||||
_G_DEBUG = True
|
||||
_DCCSI_DEV_MODE = True
|
||||
|
||||
if _DCCSI_DEV_MODE:
|
||||
try:
|
||||
import azpy.test.entry_test
|
||||
print('SUCCESS: import azpy.test.entry_test')
|
||||
azpy.test.entry_test.main(verbose=True, connectDebugger=True)
|
||||
except ImportError as e:
|
||||
print('ERROR: {0}'.format(e))
|
||||
raise e
|
||||
|
||||
# init, stash and then activate
|
||||
_SYNTH_ENV_DICT = OrderedDict()
|
||||
_SYNTH_ENV_DICT = stash_env(_SYNTH_ENV_DICT)
|
||||
_SYNTH_ENV_DICT = set_env(_SYNTH_ENV_DICT)
|
||||
|
||||
main(sys.argv[1:], _SYNTH_ENV_DICT, _G_DEBUG, _DCCSI_DEV_MODE)
|
||||
|
||||
if _G_DEBUG:
|
||||
|
||||
tempBoxJsonFilePath = Path(_SYNTH_ENV_DICT['DCCSIG_PATH'], '.temp')
|
||||
tempBoxJsonFilePath = Path(tempBoxJsonFilePath, 'boxDumpTest.json')
|
||||
_LOGGER.info(f'tempBoxJsonFilePath: {tempBoxJsonFilePath}')
|
||||
|
||||
try:
|
||||
tempBoxJsonFilePath.mkdir(parents=True, exist_ok=True)
|
||||
tempBoxJsonFilePath.touch(mode=0o777, exist_ok=True)
|
||||
except Exception as e:
|
||||
_LOGGER.info(e)
|
||||
|
||||
_LOGGER.info('~ writting with Box.to_json')
|
||||
try:
|
||||
from box import Box
|
||||
_SYNTH_ENV_DICT = Box(_SYNTH_ENV_DICT)
|
||||
_LOGGER.info(type(_SYNTH_ENV_DICT))
|
||||
except Exception as e:
|
||||
_LOGGER.info(e)
|
||||
|
||||
# -- BOX STORE ------
|
||||
_LOGGER.info(str(_SYNTH_ENV_DICT.to_json(sort_keys=False,
|
||||
indent=4)))
|
||||
try:
|
||||
#_SYNTH_ENV_DICT.to_json(filename=None, encoding='utf-8', errors='strict')
|
||||
#from os import fspath
|
||||
#tempBoxJsonFilePath = fspath(tempBoxJsonFilePath)
|
||||
_SYNTH_ENV_DICT.to_json(filename=tempBoxJsonFilePath.as_posix(),
|
||||
sort_keys=False,
|
||||
indent=4)
|
||||
_LOGGER.info('~ Box.to_json SUCCESS')
|
||||
except Exception as e:
|
||||
_LOGGER.info(e)
|
||||
# if this raises an exception related to WindowsPath
|
||||
# that a path obj was stashed, use .as_posix() when stashing
|
||||
raise e
|
||||
|
||||
_LOGGER.info('listing envar keys: {0}'.format(_SYNTH_ENV_DICT.keys()))
|
||||
|
||||
# -- BOX READ ------
|
||||
_LOGGER.info('~ read Box.from_json')
|
||||
|
||||
parseJsonBox = Box.from_json(filename=tempBoxJsonFilePath,
|
||||
encoding="utf-8",
|
||||
errors="strict",
|
||||
object_pairs_hook=OrderedDict)
|
||||
|
||||
_LOGGER.info('~ pretty print parsed Box.from_json')
|
||||
|
||||
_LOGGER.info(json.dumps(parseJsonBox, indent=4, sort_keys=False, ensure_ascii=False))
|
||||
|
||||
# also run the Qt/PySide2 test
|
||||
test_Qt()
|
||||
|
||||
del _LOGGER
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
import os
|
||||
|
||||
from azpy import env_bool
|
||||
from azpy.env_bool import env_bool
|
||||
from azpy.constants import ENVAR_DCCSI_GDEBUG
|
||||
from azpy.constants import ENVAR_DCCSI_DEV_MODE
|
||||
|
||||
|
||||
Reference in New Issue
Block a user