11 char _pythonBuffer[1024] =
"";
12 char _updateBuffer[4096] =
"";
14 std::string _outputText;
15 std::string _updateOutputText;
17 py::object _stdout_buffer;
25 enum class ScriptType {
30 ScriptType currentScriptType = ScriptType::OneOff;
32 console_state state = console_state::Collapsed;
33 console_state last_state = console_state::Collapsed;
37 float intervalSec = 1.0f;
38 double lastExecTime = 0.0;
39 bool useInterval =
false;
40 bool updatePaused =
true;
42 bool inputActive =
false;
44 bool readyToClear =
false;
45 bool readyToExecute =
false;
57 py::gil_scoped_acquire gil;
60 readyToExecute =
false;
68 double now = ImGui::GetTime();
71 currentScriptType == ScriptType::OnUpdate
72 && now - lastExecTime >= intervalSec) {
73 runUpdateScript(deltaTime);
77 sys = py::module_::import(
"sys");
78 _stdout_buffer = sys.attr(
"stdout");
79 _stdout_buffer = sys.attr(
"stdout");
82 std::string GetComponentName() {
83 return "PythonCodeComponent";
86 void showGUI(std::vector<BaseComponent*> otherComponents = {}) {
87 ImGui::Text(
"Script Type:");
89 if (ImGui::RadioButton(
"One-Off", currentScriptType == ScriptType::OneOff)) {
90 currentScriptType = ScriptType::OneOff;
93 if (ImGui::RadioButton(
"On Update", currentScriptType == ScriptType::OnUpdate)) {
94 currentScriptType = ScriptType::OnUpdate;
98 bool childActive = ImGui::BeginChild(
"Python Interpreter");
100 ImGuiChildFlags flags = ImGuiChildFlags_ResizeY;
101 bool nestedChildActive = ImGui::BeginChild(
"Python Input", ImVec2(0.0f, 300.0f), flags);
102 if (nestedChildActive) {
103 ImGui::Text(
"Python Script");
104 if (currentScriptType == ScriptType::OneOff) {
105 ImGui::Text(
"One-Off Script (Execute on demand)");
108 ImGui::Text(
"Update Script (Execute every frame)");
109 ImGui::Checkbox(
"Pause Update", &updatePaused);
111 float originalScale = ImGui::GetFont()->Scale;
112 ImGui::GetFont()->Scale = 1.5f;
113 ImGui::PushFont(ImGui::GetFont());
115 if (currentScriptType == ScriptType::OneOff) {
116 ImGui::InputTextMultiline(
"##pythonInput",
117 _pythonBuffer, IM_ARRAYSIZE(_pythonBuffer),
118 ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 4 * 1.5f));
121 ImGui::InputTextMultiline(
"##updateInput",
122 _updateBuffer, IM_ARRAYSIZE(_updateBuffer),
123 ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 4 * 1.5f));
126 inputActive = ImGui::IsItemActive() || ImGui::IsItemFocused();
129 ImGui::GetFont()->Scale = originalScale;
134 if (currentScriptType == ScriptType::OneOff) {
135 if (ImGui::Button(
"Run")) {
136 readyToExecute =
true;
139 if (ImGui::Button(
"Clear Output")) {
144 ImGui::Text(
"Auto-run settings:");
145 ImGui::Checkbox(
"Use Interval", &useInterval);
147 ImGui::InputFloat(
"Interval (s)", &intervalSec, 0.1f, 1.0f,
"%.2f");
151 ImGui::TextDisabled(
"(Runs every frame)");
154 if (ImGui::Button(
"Clear Output")) {
158 nestedChildActive = ImGui::BeginChild(
"Python Output");
159 if (nestedChildActive) {
160 ImGui::Text(
"Output:");
161 bool nestedNestedChildActive = ImGui::BeginChild(
"OutputChild", ImVec2(0.0f, 100.0f),
true);
162 if (nestedNestedChildActive)
164 if (currentScriptType == ScriptType::OneOff) {
165 ImGui::TextWrapped(
"%s", _outputText.c_str());
168 ImGui::TextWrapped(
"%s", _updateOutputText.c_str());
180 _stdout_buffer.attr(
"truncate")(0);
181 _stdout_buffer.attr(
"seek")(0);
183 catch (
const std::exception& e) {
184 std::cerr <<
"Failed to clear StringIO: " << e.what() << std::endl;
192 py::exec(_pythonBuffer);
193 py::object output = _stdout_buffer.attr(
"getvalue")();
194 _outputText = output.cast<std::string>();
197 catch (
const std::exception& e) {
198 _outputText = std::string(
"Python error: ") + e.what();
202 void runUpdateScript(
float deltaTime) {
205 py::globals()[
"deltaTime"] = deltaTime;
207 py::exec(_updateBuffer);
209 py::object output = _stdout_buffer.attr(
"getvalue")();
210 _updateOutputText = output.cast<std::string>();
213 catch (
const std::exception& e) {
214 _updateOutputText = std::string(
"Python error: ") + e.what();