Skip to content

Commit

Permalink
Updated c++ and python shape examples after feedback.
Browse files Browse the repository at this point in the history
Signed-off-by: Chris Galvan <[email protected]>
  • Loading branch information
cgalvan committed Nov 3, 2021
1 parent 148c0f7 commit 4cda972
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ namespace ShapeExample
void ShapeExampleEditorSystemComponent::NotifyRegisterViews()
{
AzToolsFramework::ViewPaneOptions options;
options.paneRect = QRect(100, 100, 500, 350);
options.paneRect = QRect(100, 100, 500, 500);
options.showOnToolsToolbar = true;
options.toolbarIcon = ":/ShapeExample/toolbar_icon.svg";

Expand Down
71 changes: 59 additions & 12 deletions cpp_gems/ShapeExample/Code/Source/ShapeExampleWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@
*
*/

#include <AzCore/Component/TransformBus.h>
#include <AzCore/Utils/Utils.h>

#include <AzToolsFramework/API/EntityCompositionRequestBus.h>
#include <AzToolsFramework/Component/EditorComponentAPIBus.h>
#include <AzToolsFramework/Entity/EditorEntityAPIBus.h>

#include <QCheckBox>
#include <QComboBox>
#include <QDoubleValidator>
#include <QFormLayout>
#include <QGridLayout>
#include <QIcon>
#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>
Expand All @@ -30,16 +34,31 @@ namespace ShapeExample
setWindowTitle(QObject::tr("ShapeExample"));

QVBoxLayout* mainLayout = new QVBoxLayout(this);
mainLayout->setSpacing(20);

QWidget* entityNameWidget = new QWidget(this);
// Introduction text explaining the example
QLabel* introText = new QLabel(QObject::tr("Welcome to the Python Shape Example tool. This tool demonstrates an example of creating an entity with a shape component in the editor. It also has other functional samples for you to play with too."), this);
introText->setWordWrap(true);
mainLayout->addWidget(introText);

// Show link to the actual tutorial for this example
QLabel* tutorialLink = new QLabel(QObject::tr("You can learn how to build this example on <a href=\"https://o3de.org/docs/learning-guide/tutorials/custom-tools/shape-example-cpp/\">O3DE Learn.</a>"), this);
tutorialLink->setTextFormat(Qt::RichText);
tutorialLink->setOpenExternalLinks(true);
mainLayout->addWidget(tutorialLink);

QGroupBox* entityNameWidget = new QGroupBox(QObject::tr("Name your entity (Line Edit)"), this);
QFormLayout* formLayout = new QFormLayout();

// Line edit to (optionally) rename the entity that gets created
m_nameInput = new QLineEdit(this);
m_nameInput->setPlaceholderText(QObject::tr("Set custom Entity name here..."));
m_nameInput->setPlaceholderText(QObject::tr("Optional"));
m_nameInput->setClearButtonEnabled(true);

// Check box used to toggle appending a suffix of the shape name when renaming the entity
m_addShapeNameSuffix = new QCheckBox(this);
m_addShapeNameSuffix->setDisabled(true);
m_addShapeNameSuffix->setToolTip("e.g. Entity2_BoxShape");

// Example of listening to signals using a slot as the handler
QObject::connect(m_nameInput, &QLineEdit::textChanged, this, &ShapeExampleWidget::OnNameInputTextChanged);
Expand All @@ -50,7 +69,29 @@ namespace ShapeExample
entityNameWidget->setLayout(formLayout);
mainLayout->addWidget(entityNameWidget);

QWidget* shapeButtons = new QWidget(this);
QGroupBox* comboBoxGroup = new QGroupBox("Choose your scale (Combo Box)", this);
QVBoxLayout* comboBoxLayout = new QVBoxLayout();

// Combo box with preset scales to be set on the created entity
// The user can also manually enter a scale into the combo box,
// which is validated to constrain to a double value between 0 and 100 with 3 decimals allowed
QStringList scaleValues = {
"1.0",
"1.5",
"2.0",
"5.0",
"10.0"
};
m_scaleInput = new QComboBox(this);
m_scaleInput->setEditable(true);
m_scaleInput->setValidator(new QDoubleValidator(0.0, 100.0, 3, this));
m_scaleInput->addItems(scaleValues);
comboBoxLayout->addWidget(m_scaleInput);

comboBoxGroup->setLayout(comboBoxLayout);
mainLayout->addWidget(comboBoxGroup);

QGroupBox* shapeButtons = new QGroupBox(QObject::tr("Choose your shape (Button)"), this);
QGridLayout* gridLayout = new QGridLayout();

// We want to find every component that provides the ShapeService
Expand Down Expand Up @@ -82,14 +123,8 @@ namespace ShapeExample
AZStd::string name = componentNames[i];
AZ::TypeId typeId = typeIds[i];

// Find the icon registered for this component by its type id
AZStd::string editorIconPath;
AzToolsFramework::EditorRequestBus::BroadcastResult(editorIconPath, &AzToolsFramework::EditorRequests::GetComponentTypeEditorIcon, typeId);
QString iconPath = QString::fromUtf8(editorIconPath.c_str());

// Create a button with the shape components name and icon
QPushButton* shapeButton = new QPushButton(QIcon(iconPath), QString::fromUtf8(name.c_str()), this);
shapeButton->setMinimumHeight(40);
// Create a button with the shape components name
QPushButton* shapeButton = new QPushButton(QString::fromUtf8(name.c_str()), this);

// Example of listening to signals using a lambda as the handler
QObject::connect(shapeButton, &QPushButton::clicked, this, [this, typeId]() {
Expand All @@ -105,6 +140,10 @@ namespace ShapeExample
shapeButtons->setLayout(gridLayout);
mainLayout->addWidget(shapeButtons);

QLabel* warningLabel = new QLabel(QObject::tr("<i>(Make sure a level is loaded before choosing your shape)</i>"), this);
warningLabel->setTextFormat(Qt::RichText);
mainLayout->addWidget(warningLabel);

// Add stretch at bottom of the layout to fill any expanded space larger than what is needed,
// so that if our tool is resized large our content will stay together
mainLayout->addStretch();
Expand Down Expand Up @@ -147,6 +186,14 @@ namespace ShapeExample
EditorEntityAPIBus::Event(newEntityId, &EditorEntityAPIRequests::SetName, entityName.toUtf8().constData());
}

// Set the scale on our new entity
bool validFloat = false;
float scale = m_scaleInput->currentText().toFloat(&validFloat);
if (validFloat)
{
AZ::TransformBus::Event(newEntityId, &AZ::TransformInterface::SetLocalUniformScale, scale);
}

// Add the corresponding shape component for the button we pressed to the newly created entity
EditorComponentAPIBus::Broadcast(&EditorComponentAPIRequests::AddComponentsOfType, newEntityId, AZ::ComponentTypeList{ typeId });
}
Expand Down
2 changes: 2 additions & 0 deletions cpp_gems/ShapeExample/Code/Source/ShapeExampleWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#endif

class QCheckBox;
class QComboBox;
class QLineEdit;

namespace ShapeExample
Expand All @@ -32,6 +33,7 @@ namespace ShapeExample
private:
QLineEdit* m_nameInput = nullptr;
QCheckBox* m_addShapeNameSuffix = nullptr;
QComboBox* m_scaleInput = nullptr;

void CreateEntityWithShapeComponent(const AZ::TypeId& typeId);
};
Expand Down
2 changes: 2 additions & 0 deletions py_gems/PyShapeExample/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
__pycache__/
*.pyc
Binary file not shown.
78 changes: 67 additions & 11 deletions py_gems/PyShapeExample/Editor/Scripts/pyshapeexample_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@
Generated from O3DE PythonToolGem Template"""

import azlmbr.bus as bus
import azlmbr.components as components
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.math as math
from PySide2.QtGui import QIcon
from PySide2.QtWidgets import QCheckBox, QDialog, QFormLayout, QGridLayout, QLineEdit, QPushButton, QVBoxLayout, QWidget

from PySide2.QtCore import Qt
from PySide2.QtGui import QDoubleValidator
from PySide2.QtWidgets import QCheckBox, QComboBox, QDialog, QFormLayout, QGridLayout, QGroupBox, QLabel, QLineEdit, QPushButton, QVBoxLayout, QWidget

class PyShapeExampleDialog(QDialog):
def on_name_input_text_changed(self, text):
Expand Down Expand Up @@ -41,23 +44,45 @@ def create_entity_with_shape_component(self, type_id):
# Set new name on the entity we created
editor.EditorEntityAPIBus(bus.Event, "SetName", new_entity_id, entity_name)

# Set the scale on our new entity
try:
scale_text = self.scale_combobox.currentText()
components.TransformBus(bus.Event, "SetLocalUniformScale", new_entity_id, float(scale_text))
except:
pass

# Add the corresponding shape component for the button we pressed to the newly created entity
editor.EditorComponentAPIBus(bus.Broadcast, "AddComponentsOfType", new_entity_id, [type_id])

def __init__(self, parent=None):
super(PyShapeExampleDialog, self).__init__(parent)

main_layout = QVBoxLayout()
main_layout.setSpacing(20)

# Introduction text explaining the example
intro_text = QLabel("Welcome to the Python Shape Example tool. This tool demonstrates an example of creating an entity with a shape component in the editor. It also has other functional samples for you to play with too.", self)
intro_text.setWordWrap(True)
main_layout.addWidget(intro_text)

# Show link to the actual tutorial for this example
tutorial_link = QLabel('You can learn how to build this example on <a href="https://o3de.org/docs/learning-guide/tutorials/custom-tools/shape-example-py/">O3DE Learn.</a>', self)
tutorial_link.setTextFormat(Qt.RichText)
tutorial_link.setOpenExternalLinks(True)
main_layout.addWidget(tutorial_link)

entity_name_widget = QWidget(self)
entity_name_widget = QGroupBox("Name your entity (Line Edit)", self)
form_layout = QFormLayout()

# Line edit to (optionally) rename the entity that gets created
self.name_input = QLineEdit(self)
self.name_input.setPlaceholderText("Set custom Entity name here...")
self.name_input.setPlaceholderText("Optional")
self.name_input.setClearButtonEnabled(True)

# Check box used to toggle appending a suffix of the shape name when renaming the entity
self.add_shape_name_suffix = QCheckBox(self)
self.add_shape_name_suffix.setDisabled(True)
self.add_shape_name_suffix.setToolTip("e.g. Entity2_BoxShape")

# Example of listening to signals using a slot as the handler
self.name_input.textChanged.connect(self.on_name_input_text_changed)
Expand All @@ -68,7 +93,29 @@ def __init__(self, parent=None):
entity_name_widget.setLayout(form_layout)
main_layout.addWidget(entity_name_widget)

shape_buttons = QWidget(self)
combobox_group = QGroupBox("Choose your scale (Combo Box)", self)
combobox_layout = QVBoxLayout()

# Combo box with preset scales to be set on the created entity
# The user can also manually enter a scale into the combo box,
# which is validated to constrain to a double value between 0 and 100 with 3 decimals allowed
scale_values = [
"1.0",
"1.5",
"2.0",
"5.0",
"10.0"
]
self.scale_combobox = QComboBox(self)
self.scale_combobox.setEditable(True)
self.scale_combobox.setValidator(QDoubleValidator(0.0, 100.0, 3, self))
self.scale_combobox.addItems(scale_values)
combobox_layout.addWidget(self.scale_combobox)

combobox_group.setLayout(combobox_layout)
main_layout.addWidget(combobox_group)

shape_buttons = QGroupBox("Choose your shape (Button)", self)
grid_layout = QGridLayout()

# We want to find every component that provides the ShapeService
Expand All @@ -84,12 +131,8 @@ def __init__(self, parent=None):
for i, name in enumerate(component_names):
type_id = type_ids[i]

# Find the icon registered for this component by its type id
icon_path = editor.EditorRequestBus(bus.Broadcast, 'GetComponentTypeEditorIcon', type_id)

# Create a button with the shape components name and icon
shape_button = QPushButton(QIcon(icon_path), name, self)
shape_button.setMinimumHeight(40)
# Create a button with the shape components name
shape_button = QPushButton(name, self)

# Example of listening to signals using a lambda as the handler
#shape_button.clicked.connect(lambda type_id: self.create_entity_with_shape_component(type_id))
Expand All @@ -103,8 +146,21 @@ def __init__(self, parent=None):
shape_buttons.setLayout(grid_layout)
main_layout.addWidget(shape_buttons)

warning_label = QLabel("<i>(Make sure a level is loaded before choosing your shape)</i>", self)
warning_label.setTextFormat(Qt.RichText)
main_layout.addWidget(warning_label)

# Add stretch at bottom of the layout to fill any expanded space larger than what is needed,
# so that if our tool is resized large our content will stay together
main_layout.addStretch()

self.setLayout(main_layout)


if __name__ == "__main__":
# Create a new instance of the tool if launched from the Python Scripts window,
# which allows for quick iteration without having to close/re-launch the Editor
test_dialog = PyShapeExampleDialog()
test_dialog.setWindowTitle("Shape Example (Python)")
test_dialog.show()
test_dialog.adjustSize()

0 comments on commit 4cda972

Please sign in to comment.