You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
904 lines
43 KiB
Python
904 lines
43 KiB
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.
|
|
#
|
|
|
|
# PySide project and gem selector GUI
|
|
|
|
import os
|
|
import pathlib
|
|
import sys
|
|
import argparse
|
|
import json
|
|
import logging
|
|
import subprocess
|
|
from logging.handlers import RotatingFileHandler
|
|
from typing import List
|
|
from pyside import add_pyside_environment, is_pyside_ready, uninstall_env
|
|
|
|
engine_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', '..'))
|
|
sys.path.append(engine_path)
|
|
executable_path = ''
|
|
|
|
logger = logging.getLogger()
|
|
logger.setLevel(logging.INFO)
|
|
|
|
from o3de import add_gem_project, cmake, engine_template, manifest, register, remove_gem_project
|
|
|
|
o3de_folder = manifest.get_o3de_folder()
|
|
o3de_logs_folder = manifest.get_o3de_logs_folder()
|
|
project_manager_log_file_path = o3de_logs_folder / "project_manager.log"
|
|
log_file_handler = RotatingFileHandler(filename=project_manager_log_file_path, maxBytes=1024 * 1024, backupCount=1)
|
|
formatter = logging.Formatter('%(asctime)s | %(levelname)s : %(message)s')
|
|
log_file_handler.setFormatter(formatter)
|
|
logger.addHandler(log_file_handler)
|
|
|
|
logger.info("Starting Project Manager")
|
|
|
|
|
|
def initialize_pyside_from_parser():
|
|
# Parse arguments up top. We need to know the path to our binaries and QT libs in particular to load up
|
|
# PySide
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('--executable-path', required=True, help='Path to Executable to launch with project')
|
|
parser.add_argument('--binaries-path', default=None, help='Path to QT Binaries necessary for PySide. If not'
|
|
' provided executable_path folder is assumed')
|
|
parser.add_argument('--parent-pid', default=0, help='Process ID of launching process')
|
|
|
|
args = parser.parse_args()
|
|
|
|
logger.info(f"parent_pid is {args.parent_pid}")
|
|
global executable_path
|
|
executable_path = args.executable_path
|
|
binaries_path = args.binaries_path or os.path.dirname(executable_path)
|
|
|
|
# Initialize PySide before imports below. This adds both PySide python modules to the python system interpreter
|
|
# path and adds the necessary paths to binaries for the DLLs to be found and load their dependencies
|
|
add_pyside_environment(binaries_path)
|
|
|
|
|
|
if not is_pyside_ready():
|
|
initialize_pyside_from_parser()
|
|
|
|
try:
|
|
from PySide2.QtWidgets import QApplication, QDialogButtonBox, QPushButton, QComboBox, QMessageBox, QFileDialog
|
|
from PySide2.QtWidgets import QListView, QLabel
|
|
from PySide2.QtUiTools import QUiLoader
|
|
from PySide2.QtCore import QFile, QObject, Qt, Signal, Slot
|
|
from PySide2.QtGui import QIcon, QStandardItemModel, QStandardItem
|
|
except ImportError as e:
|
|
logger.error(f"Failed to import PySide2 with error {e}")
|
|
exit(-1)
|
|
|
|
logger.error(f"PySide2 imports successful")
|
|
|
|
|
|
class DialogLoggerSignaller(QObject):
|
|
send_to_dialog = Signal(str)
|
|
|
|
def __init__(self, dialog_logger):
|
|
super(DialogLoggerSignaller, self).__init__()
|
|
|
|
self.dialog_logger = dialog_logger
|
|
|
|
|
|
# Independent class to handle log forwarding. Logger and qt signals both use emit method.
|
|
# This class's job is to receive the logger record and then emit the formatted message through
|
|
# DialogLoggerSignaller which is what the ProjectDialog handler listens for
|
|
class DialogLogger(logging.Handler):
|
|
|
|
def __init__(self, log_dialog, log_level=logging.INFO, forward_log_level=logging.WARNING,
|
|
message_box_log_level=logging.ERROR):
|
|
super(DialogLogger, self).__init__()
|
|
|
|
self.log_dialog = log_dialog
|
|
self.log_level = log_level
|
|
self.forward_log_level = forward_log_level
|
|
self.message_box_log_level = message_box_log_level
|
|
self.log_records = []
|
|
self.formatter = logging.Formatter('%(levelname)s : %(message)s')
|
|
self.setFormatter(self.formatter)
|
|
self.signaller = DialogLoggerSignaller(self)
|
|
|
|
def emit(self, record):
|
|
self.log_records.append(record)
|
|
if record.levelno >= self.message_box_log_level:
|
|
QMessageBox.warning(None, record.levelname, record.message)
|
|
elif record.levelno >= self.forward_log_level:
|
|
self.signaller.send_to_dialog.emit(self.format(record))
|
|
|
|
|
|
class ProjectManagerDialog(QObject):
|
|
"""
|
|
Main project manager dialog is responsible for displaying the project selection list and output pane
|
|
"""
|
|
|
|
def __init__(self, parent=None):
|
|
super(ProjectManagerDialog, self).__init__(parent)
|
|
|
|
self.ui_path = (pathlib.Path(__file__).parent / 'ui').resolve()
|
|
self.home_folder = manifest.get_home_folder()
|
|
|
|
self.log_display = None
|
|
self.dialog_logger = DialogLogger(self)
|
|
logger.addHandler(self.dialog_logger)
|
|
logger.setLevel(logging.INFO)
|
|
|
|
self.dialog_logger.signaller.send_to_dialog.connect(self.handle_log_message)
|
|
self.mru_file_path = o3de_folder / 'mru.json'
|
|
|
|
self.create_from_template_ui_file_path = self.ui_path / 'create_from_template.ui'
|
|
self.create_gem_ui_file_path = self.ui_path / 'create_gem.ui'
|
|
self.create_project_ui_file_path = self.ui_path / 'create_project.ui'
|
|
self.manage_project_gem_targets_ui_file_path = self.ui_path / 'manage_gem_targets.ui'
|
|
self.project_manager_icon_file_path = self.ui_path / 'project_manager.ico'
|
|
self.project_manager_ui_file_path = self.ui_path / 'project_manager.ui'
|
|
|
|
self.project_manager_ui_file = QFile(self.project_manager_ui_file_path.as_posix())
|
|
self.project_manager_ui_file.open(QFile.ReadOnly)
|
|
|
|
loader = QUiLoader()
|
|
self.dialog = loader.load(self.project_manager_ui_file)
|
|
self.dialog.setWindowIcon(QIcon(self.project_manager_icon_file_path.as_posix()))
|
|
self.dialog.setFixedSize(self.dialog.size())
|
|
|
|
self.project_list_box = self.dialog.findChild(QComboBox, 'projectListBox')
|
|
self.refresh_project_list()
|
|
mru = self.get_mru_list()
|
|
if len(mru):
|
|
last_mru = pathlib.Path(mru[0]).resolve()
|
|
for this_slot in range(self.project_list_box.count()):
|
|
item_text = self.project_list_box.itemText(this_slot)
|
|
if last_mru.as_posix() in item_text:
|
|
self.project_list_box.setCurrentIndex(this_slot)
|
|
break
|
|
|
|
self.create_project_button = self.dialog.findChild(QPushButton, 'createProjectButton')
|
|
self.create_project_button.clicked.connect(self.create_project_handler)
|
|
self.create_gem_button = self.dialog.findChild(QPushButton, 'createGemButton')
|
|
self.create_gem_button.clicked.connect(self.create_gem_handler)
|
|
self.create_template_button = self.dialog.findChild(QPushButton, 'createTemplateButton')
|
|
self.create_template_button.clicked.connect(self.create_template_handler)
|
|
self.create_from_template_button = self.dialog.findChild(QPushButton, 'createFromTemplateButton')
|
|
self.create_from_template_button.clicked.connect(self.create_from_template_handler)
|
|
|
|
self.add_project_button = self.dialog.findChild(QPushButton, 'addProjectButton')
|
|
self.add_project_button.clicked.connect(self.add_project_handler)
|
|
self.add_gem_button = self.dialog.findChild(QPushButton, 'addGemButton')
|
|
self.add_gem_button.clicked.connect(self.add_gem_handler)
|
|
self.add_template_button = self.dialog.findChild(QPushButton, 'addTemplateButton')
|
|
self.add_template_button.clicked.connect(self.add_template_handler)
|
|
self.add_restricted_button = self.dialog.findChild(QPushButton, 'addRestrictedButton')
|
|
self.add_restricted_button.clicked.connect(self.add_restricted_handler)
|
|
|
|
self.remove_project_button = self.dialog.findChild(QPushButton, 'removeProjectButton')
|
|
self.remove_project_button.clicked.connect(self.remove_project_handler)
|
|
self.remove_gem_button = self.dialog.findChild(QPushButton, 'removeGemButton')
|
|
self.remove_gem_button.clicked.connect(self.remove_gem_handler)
|
|
self.remove_template_button = self.dialog.findChild(QPushButton, 'removeTemplateButton')
|
|
self.remove_template_button.clicked.connect(self.remove_template_handler)
|
|
self.remove_restricted_button = self.dialog.findChild(QPushButton, 'removeRestrictedButton')
|
|
self.remove_restricted_button.clicked.connect(self.remove_restricted_handler)
|
|
|
|
self.manage_runtime_project_gem_targets_button = self.dialog.findChild(QPushButton, 'manageRuntimeGemTargetsButton')
|
|
self.manage_runtime_project_gem_targets_button.clicked.connect(self.manage_runtime_project_gem_targets_handler)
|
|
self.manage_tool_project_gem_targets_button = self.dialog.findChild(QPushButton, 'manageToolGemTargetsButton')
|
|
self.manage_tool_project_gem_targets_button.clicked.connect(self.manage_tool_project_gem_targets_handler)
|
|
self.manage_server_project_gem_targets_button = self.dialog.findChild(QPushButton, 'manageServerGemTargetsButton')
|
|
self.manage_server_project_gem_targets_button.clicked.connect(self.manage_server_project_gem_targets_handler)
|
|
|
|
self.log_display = self.dialog.findChild(QLabel, 'logDisplay')
|
|
|
|
self.ok_cancel_button = self.dialog.findChild(QDialogButtonBox, 'okCancel')
|
|
self.ok_cancel_button.accepted.connect(self.accepted_handler)
|
|
|
|
self.dialog.show()
|
|
|
|
def refresh_project_list(self) -> None:
|
|
projects = manifest.get_all_projects()
|
|
self.project_list_box.clear()
|
|
for this_slot in range(len(projects)):
|
|
display_name = f'{os.path.basename(os.path.normpath(projects[this_slot]))} ({projects[this_slot]})'
|
|
self.project_list_box.addItem(display_name)
|
|
self.project_list_box.setItemData(self.project_list_box.count() - 1, projects[this_slot],
|
|
Qt.ToolTipRole)
|
|
|
|
def accepted_handler(self) -> None:
|
|
"""
|
|
Override for handling "Ok" on main project dialog to first check whether the user has selected a project and
|
|
prompt them to if not. If a project is selected will attempt to open it.
|
|
:return: None
|
|
"""
|
|
if not self.project_list_box.currentText():
|
|
msg_box = QMessageBox(parent=self.dialog)
|
|
msg_box.setWindowTitle("O3DE")
|
|
msg_box.setText("Please select a project")
|
|
msg_box.exec()
|
|
return
|
|
self.launch_with_project_path(self.get_selected_project_path())
|
|
|
|
def get_launch_project(self) -> str:
|
|
return os.path.normpath(self.get_selected_project_path())
|
|
|
|
def get_executable_launch_params(self) -> list:
|
|
"""
|
|
Retrieve the necessary launch parameters to make the subprocess launch call with - this is the path
|
|
to the executable such as the Editor and the path to the selected project
|
|
:return: list of params
|
|
"""
|
|
launch_params = [executable_path,
|
|
f'-regset="/Amazon/AzCore/Bootstrap/project_path={self.get_launch_project()}"']
|
|
return launch_params
|
|
|
|
def launch_with_project_path(self, project_path: str) -> None:
|
|
"""
|
|
Launch the desired application given the selected project
|
|
:param project_path: Path to currently selected project
|
|
:return: None
|
|
"""
|
|
logger.info(f'Attempting to open {project_path}')
|
|
self.update_mru_list(project_path)
|
|
launch_params = self.get_executable_launch_params()
|
|
logger.info(f'Launching with params {launch_params}')
|
|
subprocess.run(launch_params, env=uninstall_env())
|
|
|
|
def get_selected_project_path(self) -> str:
|
|
if self.project_list_box.currentIndex() == -1:
|
|
logger.warning("No project selected")
|
|
return ""
|
|
return self.project_list_box.itemData(self.project_list_box.currentIndex(), Qt.ToolTipRole)
|
|
|
|
def get_selected_project_name(self) -> str:
|
|
project_data = manifest.get_project_json_data(project_path=self.get_selected_project_path())
|
|
return project_data['project_name']
|
|
|
|
def create_project_handler(self):
|
|
"""
|
|
Opens the Create Project pane. Retrieves a list of available templates for display
|
|
:return: None
|
|
"""
|
|
loader = QUiLoader()
|
|
self.create_project_file = QFile(self.create_project_ui_file_path.as_posix())
|
|
|
|
if not self.create_project_file:
|
|
logger.error(f'Failed to create project UI file at {self.create_project_file}')
|
|
return
|
|
|
|
self.create_project_dialog = loader.load(self.create_project_file)
|
|
|
|
if not self.create_project_dialog:
|
|
logger.error(f'Failed to load create project dialog file at {self.create_project_file}')
|
|
return
|
|
|
|
self.create_project_ok_button = self.create_project_dialog.findChild(QDialogButtonBox, 'okCancel')
|
|
self.create_project_ok_button.accepted.connect(self.create_project_accepted_handler)
|
|
|
|
self.create_project_template_list = self.create_project_dialog.findChild(QListView, 'projectTemplates')
|
|
self.refresh_create_project_template_list()
|
|
|
|
self.create_project_dialog.exec()
|
|
|
|
def create_project_accepted_handler(self) -> None:
|
|
"""
|
|
Searches the available gems list for selected gems and attempts to add each one to the current project.
|
|
Updates UI after completion.
|
|
:return: None
|
|
"""
|
|
|
|
selected_item = self.create_project_template_list.selectionModel().currentIndex()
|
|
project_template_path = self.create_project_template_list.model().data(selected_item)
|
|
if not project_template_path:
|
|
return
|
|
|
|
folder_dialog = QFileDialog(self.dialog, "Select a Folder and Enter a New Project Name",
|
|
manifest.get_o3de_projects_folder().as_posix())
|
|
folder_dialog.setFileMode(QFileDialog.AnyFile)
|
|
folder_dialog.setOptions(QFileDialog.ShowDirsOnly)
|
|
project_count = 0
|
|
project_name = "MyNewProject"
|
|
while os.path.exists(os.path.join(engine_path, project_name)):
|
|
project_name = f"MyNewProject{project_count}"
|
|
project_count += 1
|
|
folder_dialog.selectFile(project_name)
|
|
project_folder = None
|
|
if folder_dialog.exec():
|
|
project_folder = folder_dialog.selectedFiles()
|
|
if project_folder:
|
|
if engine_template.create_project(project_path=project_folder[0],
|
|
template_path=project_template_path) == 0:
|
|
# Success
|
|
register.register(project_path=project_folder[0])
|
|
self.refresh_project_list()
|
|
msg_box = QMessageBox(parent=self.dialog)
|
|
msg_box.setWindowTitle("O3DE")
|
|
msg_box.setText(f"Project {project_folder[0]} created.")
|
|
msg_box.exec()
|
|
return
|
|
|
|
def create_gem_handler(self):
|
|
"""
|
|
Opens the Create Gem pane. Retrieves a list of available templates for display
|
|
:return: None
|
|
"""
|
|
loader = QUiLoader()
|
|
self.create_gem_file = QFile(self.create_gem_ui_file_path.as_posix())
|
|
|
|
if not self.create_gem_file:
|
|
logger.error(f'Failed to create gem UI file at {self.create_gem_file}')
|
|
return
|
|
|
|
self.create_gem_dialog = loader.load(self.create_gem_file)
|
|
|
|
if not self.create_gem_dialog:
|
|
logger.error(f'Failed to load create gem dialog file at {self.create_gem_file}')
|
|
return
|
|
|
|
self.create_gem_ok_button = self.create_gem_dialog.findChild(QDialogButtonBox, 'okCancel')
|
|
self.create_gem_ok_button.accepted.connect(self.create_gem_accepted_handler)
|
|
|
|
self.create_gem_template_list = self.create_gem_dialog.findChild(QListView, 'gemTemplates')
|
|
self.refresh_create_gem_template_list()
|
|
|
|
self.create_gem_dialog.exec()
|
|
|
|
def create_gem_accepted_handler(self) -> None:
|
|
"""
|
|
Searches the available gems list for selected gems and attempts to add each one to the current gem.
|
|
Updates UI after completion.
|
|
:return: None
|
|
"""
|
|
selected_item = self.create_gem_template_list.selectionModel().currentIndex()
|
|
gem_template_path = self.create_gem_template_list.model().data(selected_item)
|
|
if not gem_template_path:
|
|
return
|
|
|
|
folder_dialog = QFileDialog(self.dialog, "Select a Folder and Enter a New Gem Name",
|
|
manifest.get_o3de_gems_folder().as_posix())
|
|
folder_dialog.setFileMode(QFileDialog.AnyFile)
|
|
folder_dialog.setOptions(QFileDialog.ShowDirsOnly)
|
|
gem_count = 0
|
|
gem_name = "MyNewGem"
|
|
while os.path.exists(os.path.join(engine_path, gem_name)):
|
|
gem_name = f"MyNewGem{gem_count}"
|
|
gem_count += 1
|
|
folder_dialog.selectFile(gem_name)
|
|
gem_folder = None
|
|
if folder_dialog.exec():
|
|
gem_folder = folder_dialog.selectedFiles()
|
|
if gem_folder:
|
|
if engine_template.create_gem(gem_path=gem_folder[0],
|
|
template_path=gem_template_path) == 0:
|
|
# Success
|
|
register.register(gem_path=gem_folder[0])
|
|
msg_box = QMessageBox(parent=self.dialog)
|
|
msg_box.setWindowTitle("O3DE")
|
|
msg_box.setText(f"Gem {gem_folder[0]} created.")
|
|
msg_box.exec()
|
|
return
|
|
|
|
def create_template_handler(self):
|
|
"""
|
|
Opens a foldr select dialog and lets the user select the source folder they want to make a template
|
|
out of, then opens a second folder select dialog to get where they want to put the template and it name
|
|
:return: None
|
|
"""
|
|
|
|
source_folder = QFileDialog.getExistingDirectory(self.dialog,
|
|
"Select a Folder to make a template out of.",
|
|
manifest.get_o3de_folder().as_posix())
|
|
if not source_folder:
|
|
return
|
|
|
|
destination_template_folder_dialog = QFileDialog(self.dialog,
|
|
"Select where the template is to be created and named.",
|
|
manifest.get_o3de_templates_folder().as_posix())
|
|
destination_template_folder_dialog.setFileMode(QFileDialog.AnyFile)
|
|
destination_template_folder_dialog.setOptions(QFileDialog.ShowDirsOnly)
|
|
destination_folder = None
|
|
if destination_template_folder_dialog.exec():
|
|
destination_folder = destination_template_folder_dialog.selectedFiles()
|
|
if not destination_folder:
|
|
return
|
|
|
|
if engine_template.create_template(source_path=source_folder,
|
|
template_path=destination_folder[0]) == 0:
|
|
# Success
|
|
register.register(template_path=destination_folder[0])
|
|
msg_box = QMessageBox(parent=self.dialog)
|
|
msg_box.setWindowTitle("O3DE")
|
|
msg_box.setText(f"Template {destination_folder[0]} created.")
|
|
msg_box.exec()
|
|
return
|
|
|
|
def create_from_template_handler(self):
|
|
"""
|
|
Opens the Create from_template pane. Retrieves a list of available from_templates for display
|
|
:return: None
|
|
"""
|
|
loader = QUiLoader()
|
|
self.create_from_template_file = QFile(self.create_from_template_ui_file_path.as_posix())
|
|
|
|
if not self.create_from_template_file:
|
|
logger.error(f'Failed to create from_template UI file at {self.create_from_template_file}')
|
|
return
|
|
|
|
self.create_from_template_dialog = loader.load(self.create_from_template_file)
|
|
|
|
if not self.create_from_template_dialog:
|
|
logger.error(f'Failed to load create from_template dialog file at {self.create_from_template_file}')
|
|
return
|
|
|
|
self.create_from_template_ok_button = self.create_from_template_dialog.findChild(QDialogButtonBox, 'okCancel')
|
|
self.create_from_template_ok_button.accepted.connect(self.create_from_template_accepted_handler)
|
|
|
|
self.create_from_template_list = self.create_from_template_dialog.findChild(QListView, 'genericTemplates')
|
|
self.refresh_create_from_template_list()
|
|
|
|
self.create_from_template_dialog.exec()
|
|
|
|
def create_from_template_accepted_handler(self) -> None:
|
|
"""
|
|
Searches the available gems list for selected gems and attempts to add each one to the current gem.
|
|
Updates UI after completion.
|
|
:return: None
|
|
"""
|
|
create_gem_item = self.get_selected_gem_template()
|
|
if not create_gem_item:
|
|
return
|
|
|
|
folder_dialog = QFileDialog(self.dialog, "Select a Folder and Enter a New Gem Name",
|
|
manifest.get_o3de_gems_folder().as_posix())
|
|
folder_dialog.setFileMode(QFileDialog.AnyFile)
|
|
folder_dialog.setOptions(QFileDialog.ShowDirsOnly)
|
|
gem_count = 0
|
|
gem_name = "MyNewGem"
|
|
while os.path.exists(os.path.join(engine_path, gem_name)):
|
|
gem_name = f"MyNewGem{gem_count}"
|
|
gem_count += 1
|
|
folder_dialog.selectFile(gem_name)
|
|
gem_folder = None
|
|
if folder_dialog.exec():
|
|
gem_folder = folder_dialog.selectedFiles()
|
|
if gem_folder:
|
|
if engine_template.create_gem(gem_folder[0], create_gem_item[1]) == 0:
|
|
# Success
|
|
msg_box = QMessageBox(parent=self.dialog)
|
|
msg_box.setWindowTitle("O3DE")
|
|
msg_box.setText(f"gem {os.path.basename(os.path.normpath(gem_folder[0]))} created."
|
|
" Build your\nnew gem before hitting OK to launch.")
|
|
msg_box.exec()
|
|
return
|
|
|
|
def add_project_handler(self):
|
|
"""
|
|
Open a file search dialog looking for a folder which contains a valid project. If valid
|
|
will update the mru list with the new entry, if invalid will warn the user.
|
|
:return: None
|
|
"""
|
|
project_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Project Folder",
|
|
manifest.get_o3de_projects_folder().as_posix())
|
|
if project_folder:
|
|
if register.register(project_path=project_folder) == 0:
|
|
# Success
|
|
self.refresh_project_list()
|
|
|
|
msg_box = QMessageBox(parent=self.dialog)
|
|
msg_box.setWindowTitle("O3DE")
|
|
msg_box.setText(f"Added Project {project_folder}.")
|
|
msg_box.exec()
|
|
return
|
|
|
|
def add_gem_handler(self):
|
|
"""
|
|
Open a file search dialog looking for a folder which contains a gem. If valid
|
|
will update the mru list with the new entry, if invalid will warn the user.
|
|
:return: None
|
|
"""
|
|
gem_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Gem Folder",
|
|
manifest.get_o3de_gems_folder().as_posix())
|
|
if gem_folder:
|
|
if register.register(gem_path=gem_folder) == 0:
|
|
# Success
|
|
msg_box = QMessageBox(parent=self.dialog)
|
|
msg_box.setWindowTitle("O3DE")
|
|
msg_box.setText(f"Added Gem {gem_folder}.")
|
|
msg_box.exec()
|
|
return
|
|
|
|
def add_template_handler(self):
|
|
"""
|
|
Open a file search dialog looking for a folder which contains a valid template. If valid
|
|
will update the mru list with the new entry, if invalid will warn the user.
|
|
:return: None
|
|
"""
|
|
template_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Template Folder",
|
|
manifest.get_o3de_templates_folder().as_posix())
|
|
if template_folder:
|
|
if register.register(template_path=template_folder) == 0:
|
|
# Success
|
|
msg_box = QMessageBox(parent=self.dialog)
|
|
msg_box.setWindowTitle("O3DE")
|
|
msg_box.setText(f"Added Template {template_folder}.")
|
|
msg_box.exec()
|
|
return
|
|
|
|
def add_restricted_handler(self):
|
|
"""
|
|
Open a file search dialog looking for a folder which contains a valid template. If valid
|
|
will update the mru list with the new entry, if invalid will warn the user.
|
|
:return: None
|
|
"""
|
|
restricted_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Restricted Folder",
|
|
manifest.get_o3de_restricted_folder().as_posix())
|
|
if restricted_folder:
|
|
if register.register(restricted_path=restricted_folder) == 0:
|
|
# Success
|
|
msg_box = QMessageBox(parent=self.dialog)
|
|
msg_box.setWindowTitle("O3DE")
|
|
msg_box.setText(f"Added Restricted {restricted_folder}.")
|
|
msg_box.exec()
|
|
return
|
|
|
|
def remove_project_handler(self):
|
|
"""
|
|
Open a file search dialog looking for a folder which contains a valid project. If valid
|
|
will update the mru list with the new entry, if invalid will warn the user.
|
|
:return: None
|
|
"""
|
|
project_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Project Folder",
|
|
manifest.get_o3de_projects_folder().as_posix())
|
|
if project_folder:
|
|
if register.register(project_path=project_folder, remove=True) == 0:
|
|
# Success
|
|
self.refresh_project_list()
|
|
|
|
msg_box = QMessageBox(parent=self.dialog)
|
|
msg_box.setWindowTitle("O3DE")
|
|
msg_box.setText(f"Removed Project {project_folder}.")
|
|
msg_box.exec()
|
|
return
|
|
|
|
def remove_gem_handler(self):
|
|
"""
|
|
Open a file search dialog looking for a folder which contains a gem. If valid
|
|
will update the mru list with the new entry, if invalid will warn the user.
|
|
:return: None
|
|
"""
|
|
gem_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Gem Folder",
|
|
manifest.get_o3de_gems_folder().as_posix())
|
|
if gem_folder:
|
|
if register.register(gem_path=gem_folder, remove=True) == 0:
|
|
# Success
|
|
msg_box = QMessageBox(parent=self.dialog)
|
|
msg_box.setWindowTitle("O3DE")
|
|
msg_box.setText(f"Removed Gem {gem_folder}.")
|
|
msg_box.exec()
|
|
return
|
|
|
|
def remove_template_handler(self):
|
|
"""
|
|
Open a file search dialog looking for a folder which contains a valid template. If valid
|
|
will update the mru list with the new entry, if invalid will warn the user.
|
|
:return: None
|
|
"""
|
|
template_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Template Folder",
|
|
manifest.get_o3de_templates_folder().as_posix())
|
|
if template_folder:
|
|
if register.register(template_path=template_folder, remove=True) == 0:
|
|
# Success
|
|
msg_box = QMessageBox(parent=self.dialog)
|
|
msg_box.setWindowTitle("O3DE")
|
|
msg_box.setText(f"Removed Template {template_folder}.")
|
|
msg_box.exec()
|
|
return
|
|
|
|
def remove_restricted_handler(self):
|
|
"""
|
|
Open a file search dialog looking for a folder which contains a valid template. If valid
|
|
will update the mru list with the new entry, if invalid will warn the user.
|
|
:return: None
|
|
"""
|
|
restricted_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Restricted Folder",
|
|
manifest.get_o3de_restricted_folder().as_posix())
|
|
if restricted_folder:
|
|
if register.register(restricted_path=restricted_folder, remove=True) == 0:
|
|
# Success
|
|
msg_box = QMessageBox(parent=self.dialog)
|
|
msg_box.setWindowTitle("O3DE")
|
|
msg_box.setText(f"Removed Restricted {restricted_folder}.")
|
|
msg_box.exec()
|
|
return
|
|
|
|
def manage_runtime_project_gem_targets_handler(self):
|
|
"""
|
|
Opens the Gem management pane. Waits for the load thread to complete if still running and displays all
|
|
active gems for the current project as well as all available gems which aren't currently active.
|
|
:return: None
|
|
"""
|
|
|
|
if not self.get_selected_project_path():
|
|
msg_box = QMessageBox(parent=self.dialog)
|
|
msg_box.setWindowTitle("O3DE")
|
|
msg_box.setText("Please select a project")
|
|
msg_box.exec()
|
|
return
|
|
|
|
loader = QUiLoader()
|
|
self.manage_project_gem_targets_file = QFile(self.manage_project_gem_targets_ui_file_path.as_posix())
|
|
|
|
if not self.manage_project_gem_targets_file:
|
|
logger.error(f'Failed to load manage gem targets UI file at {self.manage_project_gem_targets_ui_file_path}')
|
|
return
|
|
|
|
self.manage_project_gem_targets_dialog = loader.load(self.manage_project_gem_targets_file)
|
|
|
|
if not self.manage_project_gem_targets_dialog:
|
|
logger.error(f'Failed to load gems dialog file at {self.manage_project_gem_targets_ui_file_path.as_posix()}')
|
|
return
|
|
|
|
self.manage_project_gem_targets_dialog.setWindowTitle(f"Manage Runtime Gem Targets for Project:"
|
|
f" {self.get_selected_project_name()}")
|
|
|
|
self.add_gem_button = self.manage_project_gem_targets_dialog.findChild(QPushButton, 'addGemTargetsButton')
|
|
self.add_gem_button.clicked.connect(self.add_runtime_project_gem_targets_handler)
|
|
|
|
self.available_gem_targets_list = self.manage_project_gem_targets_dialog.findChild(QListView,
|
|
'availableGemTargetsList')
|
|
self.refresh_runtime_project_gem_targets_available_list()
|
|
|
|
self.remove_project_gem_targets_button = self.manage_project_gem_targets_dialog.findChild(QPushButton,
|
|
'removeGemTargetsButton')
|
|
self.remove_project_gem_targets_button.clicked.connect(self.remove_runtime_project_gem_targets_handler)
|
|
|
|
self.enabled_gem_targets_list = self.manage_project_gem_targets_dialog.findChild(QListView,
|
|
'enabledGemTargetsList')
|
|
self.refresh_runtime_project_gem_targets_enabled_list()
|
|
|
|
self.manage_project_gem_targets_dialog.exec()
|
|
|
|
def manage_tool_project_gem_targets_handler(self):
|
|
"""
|
|
Opens the Gem management pane. Waits for the load thread to complete if still running and displays all
|
|
active gems for the current project as well as all available gems which aren't currently active.
|
|
:return: None
|
|
"""
|
|
|
|
if not self.get_selected_project_path():
|
|
msg_box = QMessageBox(parent=self.dialog)
|
|
msg_box.setWindowTitle("O3DE")
|
|
msg_box.setText("Please select a project")
|
|
msg_box.exec()
|
|
return
|
|
|
|
loader = QUiLoader()
|
|
self.manage_project_gem_targets_file = QFile(self.manage_project_gem_targets_ui_file_path.as_posix())
|
|
|
|
if not self.manage_project_gem_targets_file:
|
|
logger.error(f'Failed to load manage gem targets UI file at {self.manage_project_gem_targets_ui_file_path}')
|
|
return
|
|
|
|
self.manage_project_gem_targets_dialog = loader.load(self.manage_project_gem_targets_file)
|
|
|
|
if not self.manage_project_gem_targets_dialog:
|
|
logger.error(
|
|
f'Failed to load gems dialog file at {self.manage_project_gem_targets_ui_file_path.as_posix()}')
|
|
return
|
|
|
|
self.manage_project_gem_targets_dialog.setWindowTitle(f"Manage Tool Gem Targets for Project:"
|
|
f" {self.get_selected_project_name()}")
|
|
|
|
self.add_gem_button = self.manage_project_gem_targets_dialog.findChild(QPushButton, 'addGemTargetsButton')
|
|
self.add_gem_button.clicked.connect(self.add_tool_project_gem_targets_handler)
|
|
|
|
self.available_gem_targets_list = self.manage_project_gem_targets_dialog.findChild(QListView,
|
|
'availableGemTargetsList')
|
|
self.refresh_tool_project_gem_targets_available_list()
|
|
|
|
self.remove_project_gem_targets_button = self.manage_project_gem_targets_dialog.findChild(QPushButton,
|
|
'removeGemTargetsButton')
|
|
self.remove_project_gem_targets_button.clicked.connect(self.remove_tool_project_gem_targets_handler)
|
|
|
|
self.enabled_gem_targets_list = self.manage_project_gem_targets_dialog.findChild(QListView,
|
|
'enabledGemTargetsList')
|
|
self.refresh_tool_project_gem_targets_enabled_list()
|
|
|
|
self.manage_project_gem_targets_dialog.exec()
|
|
|
|
def manage_server_project_gem_targets_handler(self):
|
|
"""
|
|
Opens the Gem management pane. Waits for the load thread to complete if still running and displays all
|
|
active gems for the current project as well as all available gems which aren't currently active.
|
|
:return: None
|
|
"""
|
|
|
|
if not self.get_selected_project_path():
|
|
msg_box = QMessageBox(parent=self.dialog)
|
|
msg_box.setWindowTitle("O3DE")
|
|
msg_box.setText("Please select a project")
|
|
msg_box.exec()
|
|
return
|
|
|
|
loader = QUiLoader()
|
|
self.manage_project_gem_targets_file = QFile(self.manage_project_gem_targets_ui_file_path.as_posix())
|
|
|
|
if not self.manage_project_gem_targets_file:
|
|
logger.error(f'Failed to load manage gem targets UI file at {self.manage_project_gem_targets_ui_file_path}')
|
|
return
|
|
|
|
self.manage_project_gem_targets_dialog = loader.load(self.manage_project_gem_targets_file)
|
|
|
|
if not self.manage_project_gem_targets_dialog:
|
|
logger.error(
|
|
f'Failed to load gems dialog file at {self.manage_project_gem_targets_ui_file_path.as_posix()}')
|
|
return
|
|
|
|
self.manage_project_gem_targets_dialog.setWindowTitle(f"Manage Server Gem Targets for Project:"
|
|
f" {self.get_selected_project_name()}")
|
|
|
|
self.add_gem_button = self.manage_project_gem_targets_dialog.findChild(QPushButton, 'addGemTargetsButton')
|
|
self.add_gem_button.clicked.connect(self.add_server_project_gem_targets_handler)
|
|
|
|
self.available_gem_targets_list = self.manage_project_gem_targets_dialog.findChild(QListView,
|
|
'availableGemTargetsList')
|
|
self.refresh_server_project_gem_targets_available_list()
|
|
|
|
self.remove_project_gem_targets_button = self.manage_project_gem_targets_dialog.findChild(QPushButton,
|
|
'removeGemTargetsButton')
|
|
self.remove_project_gem_targets_button.clicked.connect(self.remove_server_project_gem_targets_handler)
|
|
|
|
self.enabled_gem_targets_list = self.manage_project_gem_targets_dialog.findChild(QListView,
|
|
'enabledGemTargetsList')
|
|
self.refresh_server_project_gem_targets_enabled_list()
|
|
|
|
self.manage_project_gem_targets_dialog.exec()
|
|
|
|
def manage_project_gem_targets_get_selected_available_gems(self) -> list:
|
|
selected_items = self.available_gem_targets_list.selectionModel().selectedRows()
|
|
return [(self.available_gem_targets_list.model().data(item)) for item in selected_items]
|
|
|
|
def manage_project_gem_targets_get_selected_enabled_gems(self) -> list:
|
|
selected_items = self.enabled_gem_targets_list.selectionModel().selectedRows()
|
|
return [(self.enabled_gem_targets_list.model().data(item)) for item in selected_items]
|
|
|
|
def add_project_gem_targets_handler(self) -> None:
|
|
gem_paths = manifest.get_all_gems()
|
|
for gem_target in self.manage_project_gem_targets_get_selected_available_gems():
|
|
for gem_path in gem_paths:
|
|
add_gem_project.add_gem_to_project(gem_path=gem_path,
|
|
project_path=self.get_selected_project_path())
|
|
self.refresh_runtime_project_gem_targets_available_list()
|
|
self.refresh_runtime_project_gem_targets_enabled_list()
|
|
return
|
|
self.refresh_runtime_project_gem_targets_available_list()
|
|
self.refresh_runtime_project_gem_targets_enabled_list()
|
|
|
|
def remove_project_gem_targets_handler(self):
|
|
gem_paths = manifest.get_all_gems()
|
|
for gem_target in self.manage_project_gem_targets_get_selected_enabled_gems():
|
|
for gem_path in gem_paths:
|
|
remove_gem_project.remove_gem_from_project(gem_path=gem_path,
|
|
project_path=self.get_selected_project_path())
|
|
self.refresh_runtime_project_gem_targets_available_list()
|
|
self.refresh_runtime_project_gem_targets_enabled_list()
|
|
return
|
|
self.refresh_runtime_project_gem_targets_available_list()
|
|
self.refresh_runtime_project_gem_targets_enabled_list()
|
|
|
|
def refresh_runtime_project_gem_targets_enabled_list(self) -> None:
|
|
enabled_project_gem_targets_model = QStandardItemModel()
|
|
enabled_project_gems = cmake.get_project_gems(project_path=self.get_selected_project_path())
|
|
for gem_target in sorted(enabled_project_gems):
|
|
model_item = QStandardItem(gem_target)
|
|
enabled_project_gem_targets_model.appendRow(model_item)
|
|
self.enabled_gem_targets_list.setModel(enabled_project_gem_targets_model)
|
|
|
|
|
|
def refresh_runtime_project_gem_targets_available_list(self) -> None:
|
|
available_project_gem_targets_model = QStandardItemModel()
|
|
enabled_project_gem_targets = cmake.get_project_gems(project_path=self.get_selected_project_path())
|
|
all_gem_targets = manifest.get_all_gems()
|
|
for gem_target in sorted(all_gem_targets):
|
|
if gem_target not in enabled_project_gem_targets:
|
|
model_item = QStandardItem(gem_target)
|
|
available_project_gem_targets_model.appendRow(model_item)
|
|
self.available_gem_targets_list.setModel(available_project_gem_targets_model)
|
|
|
|
|
|
def refresh_create_project_template_list(self) -> None:
|
|
self.create_project_template_model = QStandardItemModel()
|
|
for project_template_path in manifest.get_project_templates():
|
|
model_item = QStandardItem(project_template_path)
|
|
self.create_project_template_model.appendRow(model_item)
|
|
self.create_project_template_list.setModel(self.create_project_template_model)
|
|
|
|
def refresh_create_gem_template_list(self) -> None:
|
|
self.create_gem_template_model = QStandardItemModel()
|
|
for gem_template_path in manifest.get_gem_templates():
|
|
model_item = QStandardItem(gem_template_path)
|
|
self.create_gem_template_model.appendRow(model_item)
|
|
self.create_gem_template_list.setModel(self.create_gem_template_model)
|
|
|
|
def refresh_create_from_template_list(self) -> None:
|
|
self.create_from_template_model = QStandardItemModel()
|
|
for generic_template_path in manifest.get_generic_templates():
|
|
model_item = QStandardItem(generic_template_path)
|
|
self.create_from_template_model.appendRow(model_item)
|
|
self.create_from_template_list.setModel(self.create_from_template_model)
|
|
|
|
def update_mru_list(self, used_project: str) -> None:
|
|
"""
|
|
Promote a supplied project name to the "most recent" in a given MRU list.
|
|
:param used_project: path to project to promote
|
|
:param file_path: path to mru list file
|
|
:return: None
|
|
"""
|
|
used_project = os.path.normpath(used_project)
|
|
if not os.path.exists(os.path.dirname(self.mru_file_path)):
|
|
os.makedirs(os.path.dirname(self.mru_file_path), exist_ok=True)
|
|
mru_data = {}
|
|
try:
|
|
with open(self.mru_file_path, 'r') as mru_file:
|
|
mru_data = json.loads(mru_file.read())
|
|
except FileNotFoundError:
|
|
pass
|
|
except json.JSONDecodeError:
|
|
pass
|
|
|
|
recent_list = mru_data.get('Projects', [])
|
|
recent_list = [item for item in recent_list if item.get('Path') != used_project and
|
|
self.is_project_folder(item.get('Path'))]
|
|
|
|
new_list = [{'Path': used_project}]
|
|
new_list.extend(recent_list)
|
|
|
|
mru_data['Projects'] = new_list
|
|
try:
|
|
with open(self.mru_file_path, 'w') as mru_file:
|
|
mru_file.write(json.dumps(mru_data, indent=1))
|
|
except PermissionError as e:
|
|
logger.warning(f"Failed to write {self.mru_file_path} with error {e}")
|
|
|
|
def get_mru_list(self) -> List[str]:
|
|
"""
|
|
Retrieve the current MRU list. Does not perform validation that the projects still appear valid
|
|
:return: list of full path strings to project folders
|
|
"""
|
|
if not os.path.exists(os.path.dirname(self.mru_file_path)):
|
|
return []
|
|
try:
|
|
with open(self.mru_file_path, 'r') as mru_file:
|
|
mru_data = json.loads(mru_file.read())
|
|
except FileNotFoundError:
|
|
return []
|
|
except json.JSONDecodeError:
|
|
logger.error(f'MRU list at {self.mru_file_path} is not valid JSON')
|
|
return []
|
|
|
|
recent_list = mru_data.get('Projects', [])
|
|
return [item.get('Path') for item in recent_list if item.get('Path') is not None]
|
|
|
|
@Slot(str)
|
|
def handle_log_message(self, message: str) -> None:
|
|
"""
|
|
Signal handler for messages from the logger. Displays the most recent warning/error
|
|
:param message: formatted log message from DialogLoggerSignaller
|
|
:return:
|
|
"""
|
|
if not self.log_display:
|
|
return
|
|
self.log_display.setText(message)
|
|
self.log_display.setToolTip(message)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
dialog_app = QApplication(sys.argv)
|
|
my_dialog = ProjectManagerDialog()
|
|
dialog_app.exec_()
|
|
sys.exit(0)
|