3
0

ui.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. #
  2. # Copyright (c) Contributors to the Open 3D Engine Project.
  3. # For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. #
  5. # SPDX-License-Identifier: Apache-2.0 OR MIT
  6. #
  7. #
  8. # -------------------------------------------------------------------------
  9. """! brief
  10. UI methods for DccScriptingInterface Editor framework
  11. :file: DccScriptingInterface\\editor\\scripts\\ui.py
  12. """
  13. # standard imports
  14. import os
  15. import subprocess
  16. from pathlib import Path
  17. import logging as _logging
  18. #PySide2 imports
  19. from PySide2 import QtWidgets
  20. from PySide2.QtWidgets import QMenuBar, QMenu, QAction
  21. from PySide2 import QtGui
  22. from PySide2.QtCore import Slot, QObject, QUrl
  23. from shiboken2 import wrapInstance, getCppPointer
  24. # -------------------------------------------------------------------------
  25. # -------------------------------------------------------------------------
  26. # global scope
  27. from DccScriptingInterface.Editor.Scripts import _PACKAGENAME
  28. _MODULENAME = f'{_PACKAGENAME}.ui'
  29. _LOGGER = _logging.getLogger(_MODULENAME)
  30. _LOGGER.debug('Initializing: {0}.'.format({_MODULENAME}))
  31. # this accesses common global state, e.g. DCCSI_GDEBUG (is True or False)
  32. from DccScriptingInterface.globals import *
  33. import DccScriptingInterface.config as dccsi_core_config
  34. _settings_core = dccsi_core_config.get_config_settings(enable_o3de_python=True,
  35. enable_o3de_pyside2=False,
  36. set_env=True)
  37. import DccScriptingInterface.azpy.shared.ui.samples
  38. from DccScriptingInterface.azpy.shared.ui.samples import SampleUI
  39. # O3DE imports
  40. import azlmbr
  41. import azlmbr.bus
  42. import azlmbr.paths
  43. import azlmbr.action
  44. # -------------------------------------------------------------------------
  45. # - slot ------------------------------------------------------------------
  46. # as the list of slots/actions grows, refactor into sub-modules
  47. @Slot()
  48. def click_action_sampleui():
  49. """! Creates a standalone sample ui with button, this is provided for
  50. Technicl Artists learning, as one of the purposes of the dccsi is
  51. onboarding TAs to the editor extensibility experience.
  52. :return: returns the created ui
  53. """
  54. _LOGGER.debug(f'Clicked: click_action_sampleui')
  55. # import additional O3DE QtForPython Gem modules
  56. import az_qt_helpers
  57. ui = SampleUI(parent=az_qt_helpers.get_editor_main_window(),
  58. title='Dccsi: SampleUI')
  59. ui.show()
  60. return
  61. # - hook ------------------------------------------------------------------
  62. def hook_register_action_sampleui(parameters):
  63. _LOGGER.debug(f'Registered: hook_register_action_sampleui')
  64. action_properties = azlmbr.action.ActionProperties()
  65. action_properties.name = 'SampleUI'
  66. action_properties.description = "Open an Example SampleUI Dialog"
  67. action_properties.category = "Python"
  68. azlmbr.action.ActionManagerPythonRequestBus(azlmbr.bus.Broadcast,
  69. 'RegisterAction',
  70. 'o3de.context.editor.mainwindow',
  71. 'o3de.action.python.dccsi.examples.sampleui',
  72. action_properties,
  73. click_action_sampleui)
  74. # -------------------------------------------------------------------------
  75. # -------------------------------------------------------------------------
  76. def add_action_OLD(parent: QMenu,
  77. title: str = "SampleUI",
  78. action_slot = click_action_sampleui) -> QAction:
  79. """! adds an action to the parent QMenu
  80. :param parent_menu: the parent Qmenu to add an action to
  81. :param title: The UI text str for the menu action
  82. :param action_slot: @Slot decorated method, see click_sampleui()
  83. :return: returns the created action
  84. """
  85. _LOGGER.debug(f"Creating '{title}' action for menu '{parent.title()}'")
  86. action = None
  87. action_list = parent.findChildren(QAction)
  88. for a in action_list:
  89. if a.text() == f"&{title}":
  90. action = a
  91. if not action:
  92. action = parent.addAction(f"&{title}")
  93. # click_sampleui signal
  94. action.triggered.connect(action_slot)
  95. return action
  96. # -------------------------------------------------------------------------
  97. # -------------------------------------------------------------------------
  98. def create_menu_OLD(parent: QMenu, title: str = 'StudioTools') -> QMenu:
  99. """! Creates a 'Studio Tools' menu for the DCCsi functionality
  100. :param parent: The parent QMenu (or QMenuBar)
  101. :param : The UI text str for the submenu
  102. :return: returns the created submenu
  103. """
  104. _LOGGER.debug(f"Creating a dccsi menu: '{title}'")
  105. dccsi_menu = None
  106. menu_list = parent.findChildren(QMenu)
  107. for m in menu_list:
  108. if m.title() == f"&{title}":
  109. dccsi_menu = m
  110. if not dccsi_menu:
  111. # create our own dccsi menu
  112. dccsi_menu = parent.addMenu(f"&{title}")
  113. return dccsi_menu
  114. # -------------------------------------------------------------------------
  115. # -------------------------------------------------------------------------
  116. def start_service(py_file: Path = None,
  117. debug: bool = DCCSI_GDEBUG) -> subprocess:
  118. """! Common method to start the external application start script
  119. :param : The UI text str for the submenu
  120. :return: returns the created submenu
  121. """
  122. _LOGGER.debug(f'Starting Service: {py_file.parent.name}')
  123. cmd = [str(_settings_core.DCCSI_PY_BASE), str(py_file)]
  124. # there are at least three ways to go about this ...
  125. # the first, is to call the function
  126. # this doesn't work well with O3DE, because the environ is propogated
  127. # and o3de python and Qt both interfer with wing boot and operation
  128. # try:
  129. # #wing_proc = wing_start.call()
  130. # wing_proc = wing_start.popen()
  131. # except Exception as e:
  132. # _LOGGER.error(f'{e} , traceback =', exc_info=True)
  133. # return None
  134. # # this sort of works, seems to stall the editor until wing closes.
  135. # the second, we could try to execute another way ...
  136. # probably the same results as above, it's also probably not safe
  137. # py_file = Path(wing_config.settings.PATH_DCCSI_TOOLS_IDE_WING, 'start.py').resolve()
  138. # try:
  139. # wing_proc = exec(open(f"{py_file.as_posix()}").read())
  140. # except Exception as e:
  141. # _LOGGER.error(f'{e} , traceback =', exc_info=True)
  142. # return None
  143. # tries to execute but fails to do so correctly
  144. if debug:
  145. # This approach will block the editor but can return a callstack
  146. # tThis is valuable to debug boot issues with start.py code itself
  147. # You must manually enable this flag near beginning of this module
  148. p = subprocess.Popen(cmd,
  149. stdout=subprocess.PIPE,
  150. stderr=subprocess.PIPE)
  151. out, err = p.communicate()
  152. _LOGGER.debug(f'out: {str(out)}')
  153. _LOGGER.error(f'err: {str(err)}')
  154. _LOGGER.debug(f'returncode: {str(p.returncode)}')
  155. _LOGGER.debug('EXIT')
  156. else:
  157. # This is non-blocking, but if it fails can be difficult to debug
  158. p = subprocess.Popen(cmd)
  159. _LOGGER.debug('pid', p.pid)
  160. _LOGGER.debug('EXIT')
  161. return p
  162. # -------------------------------------------------------------------------