Integrating up through commit 90f050496

This commit is contained in:
alexpete
2021-04-07 14:03:29 -07:00
parent 8f2ed080a9
commit c2cbd430fe
2694 changed files with 285622 additions and 176874 deletions
@@ -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']
@@ -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()
@@ -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")