In-work: Python segfaults when adding new objects to module

This commit is contained in:
John McCardle 2023-08-25 21:57:42 -04:00
parent a455c44b34
commit ac0ec4bb71
10 changed files with 529 additions and 16 deletions

View File

@ -4,16 +4,20 @@
#include "ActionCode.h"
#include "McRFPy_API.h"
#include "PythonScene.h"
#include "UITestScene.h"
#include "Resources.h"
GameEngine::GameEngine()
{
font.loadFromFile("./assets/JetbrainsMono.ttf");
Resources::font.loadFromFile("./assets/JetbrainsMono.ttf");
Resources::game = this;
window.create(sf::VideoMode(1024, 768), "McRogueFace - r/RoguelikeDev Tutorial Run");
visible = window.getDefaultView();
window.setFramerateLimit(30);
scene = "menu";
scene = "uitest";
//std::cout << "Constructing MenuScene" << std::endl;
scenes["menu"] = new MenuScene(this);
scenes["uitest"] = new UITestScene(this);
//std::cout << "Constructed MenuScene" <<std::endl;
//scenes["play"] = new UITestScene(this);
//api = new McRFPy_API(this);
@ -21,10 +25,10 @@ GameEngine::GameEngine()
McRFPy_API::game = this;
McRFPy_API::api_init();
McRFPy_API::executePyString("import mcrfpy");
McRFPy_API::executePyString("from UIMenu import *");
McRFPy_API::executePyString("from Grid import *");
//McRFPy_API::executePyString("from UIMenu import *");
//McRFPy_API::executePyString("from Grid import *");
scenes["py"] = new PythonScene(this, "TestScene");
//scenes["py"] = new PythonScene(this, "TestScene");
IndexSprite::game = this;
@ -35,7 +39,7 @@ Scene* GameEngine::currentScene() { return scenes[scene]; }
void GameEngine::changeScene(std::string s) { std::cout << "Current scene is now '" << s << "'\n"; scene = s; }
void GameEngine::quit() { running = false; }
void GameEngine::setPause(bool p) { paused = p; }
sf::Font & GameEngine::getFont() { return font; }
sf::Font & GameEngine::getFont() { /*return font; */ return Resources::font; }
sf::RenderWindow & GameEngine::getWindow() { return window; }
void GameEngine::run()

View File

@ -2,6 +2,7 @@
#include "platform.h"
#include "GameEngine.h"
#include "Grid.h"
#include "UI.h"
// static class members...?
std::map<std::string, UIMenu*> McRFPy_API::menus;
@ -104,23 +105,127 @@ static PyModuleDef mcrfpyModule = {
NULL, NULL, NULL, NULL
};
//
// Point class dump
//
// Point class example
class Point
{
public:
float x, y;
float magnitude() {
return std::sqrt(x*x + y*y);
}
};
static PyObject* PyPoint_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
{
Point* self;
self = (Point*)type->tp_alloc(type, 0);
if (self != nullptr)
{
self->x = 0.0f;
self->y = 0.0f;
}
return (PyObject*)self;
}
// Method to initialize the Point object
static int PyPoint_init(Point* self, PyObject* args, PyObject* kwds)
{
static const char* keywords[] = { "x", "y", nullptr };
float x = 0.0f, y = 0.0f;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ff", const_cast<char**>(keywords), &x, &y))
{
return -1;
}
self->x = x;
self->y = y;
return 0;
}
// Method to calculate the magnitude of the Point
static PyObject* PyPoint_magnitude(Point* self)
{
float mag = self->magnitude();
return PyFloat_FromDouble(mag);
}
static PyMethodDef PyPoint_methods[] = {
{"magnitude", (PyCFunction)PyPoint_magnitude, METH_NOARGS,
"Vector length, or distance from origin."},
{NULL, NULL, 0, NULL}
};
static PyMemberDef PyPoint_members[] = {
{"x", T_FLOAT, offsetof(Point, x), 0},
{"y", T_FLOAT, offsetof(Point, y), 0},
{NULL}
};
static PyTypeObject PyPointType = {
.tp_name = "mcrfpy.Point",
.tp_basicsize = sizeof(Point),
//.tp_itemsize = 0,
//.tp_dealloc = NULL,
//.tp_repr = NULL,
//.tp_hash = NULL,
//.tp_iter
//.tp_iternext
.tp_flags = Py_TPFLAGS_DEFAULT,
//.tp_doc = PyDoc_STR("Custom point object. (x, y)"),
//.tp_methods = PyPoint_methods,
.tp_members = PyPoint_members,
//.tp_init = (initproc)PyPoint_init,
//.tp_new = PyPoint_new, //PyType_GenericNew ?
};
//
// End Dump
//
// Module initializer fn, passed to PyImport_AppendInittab
PyObject* PyInit_mcrfpy()
{
PyObject* m = PyModule_Create(&mcrfpyModule);
if (m == NULL) return NULL;
/*
// add C++ classes exposed to Python here
Py_INCREF(&PyUIMenuType);
if (PyModule_AddObject(m, "Menu", (PyObject *) & PyUIMenuType) < 0)
if (m == NULL)
{
Py_DECREF(&PyUIMenuType);
std::cout << "ohno, module didn't ):\n";
return NULL;
}
/*
// This code runs, but Python segfaults when accessing the UIFrame type.
std::cout << "Adding UIFrame object to module\n";
Py_INCREF(&mcrfpydef::PyUIFrameType);
if (PyModule_AddObject(m, "UIFrame", (PyObject *) & mcrfpydef::PyUIFrameType) < 0)
{
std::cout << "Failed to add UIFrame object\n";
Py_DECREF(&mcrfpydef::PyUIFrameType);
//return NULL;
}
std::cout << "Returning module\n";
*/
Py_INCREF(&PyPointType);
if (PyModule_AddObject(m, "Point", (PyObject *) &PyPointType) < 0) {
std::cout << "ohno, couldn't add\n";
Py_DECREF(&PyPointType);
Py_DECREF(m);
return NULL;
}
return m;
}
@ -190,7 +295,7 @@ void McRFPy_API::setSpriteTexture(int ti)
void McRFPy_API::api_init() {
// build API exposure before python initialization
PyImport_AppendInittab("mcrfpy", PyInit_mcrfpy);
PyImport_AppendInittab("mcrfpy", &PyInit_mcrfpy);
// use full path version of argv[0] from OS to init python
init_python(narrow_string(executable_filename()).c_str());

6
src/Resources.cpp Normal file
View File

@ -0,0 +1,6 @@
#include "Resources.h"
// Resources class members memory allocation
sf::Font Resources::font;
GameEngine* Resources::game;

10
src/Resources.h Normal file
View File

@ -0,0 +1,10 @@
#include "Common.h"
class GameEngine; // forward declared
class Resources
{
public:
static sf::Font font;
static GameEngine* game;
};

35
src/UI.cpp Normal file
View File

@ -0,0 +1,35 @@
#include "UI.h"
#include "Resources.h"
#include "GameEngine.h"
void UIDrawable::render()
{
//std::cout << "Rendering base UIDrawable\n";
render(sf::Vector2f());
}
void UIFrame::render(sf::Vector2f offset)
{
//std::cout << "Rendering UIFrame w/ offset\n";
box.move(offset);
Resources::game->getWindow().draw(box);
box.move(-offset);
for (auto drawable : children) {
drawable->render(offset + box.getPosition());
}
}
void UICaption::render(sf::Vector2f offset)
{
//std::cout << "Rendering Caption with offset\n";
text.move(offset);
Resources::game->getWindow().draw(text);
text.move(-offset);
}
void UISprite::render(sf::Vector2f offset)
{
sprite.move(offset);
Resources::game->getWindow().draw(sprite);
sprite.move(-offset);
}

236
src/UI.h Normal file
View File

@ -0,0 +1,236 @@
#include "Common.h"
#include "Python.h"
#include "structmember.h"
class UIDrawable
{
public:
UIDrawable* parent;
void render();
virtual void render(sf::Vector2f) = 0;
//virtual sf::Rect<int> aabb(); // not sure I care about this yet
//virtual sf::Vector2i position();
bool handle_event(/* ??? click, scroll, keystroke*/);
std::string action;
};
class UIFrame: public UIDrawable
{
public:
sf::RectangleShape box;
std::vector<UIDrawable*> children;
void render(sf::Vector2f) override final;
};
class UICaption: public UIDrawable
{
public:
sf::Text text;
void render(sf::Vector2f) override final;
};
class UISprite: public UIDrawable
{
public:
void render(sf::Vector2f) override final;
int texture_index, sprite_index;
float scale;
sf::Sprite sprite;
};
namespace mcrfpydef {
static PyObject* PyUIFrame_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
{
std::cout << "New called\n";
UIFrame* self;
self = (UIFrame*)type->tp_alloc(type, 0);
if (self != nullptr)
{
}
return (PyObject*)self;
}
static int PyUIFrame_init(UIFrame* self, PyObject* args, PyObject* kwds)
{
std::cout << "Init called\n";
static const char* keywords[] = { "x", "y", nullptr }; // TODO: keywords and other python objects to configure box (sf::RectangleShape)
float x = 0.0f, y = 0.0f;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ff", const_cast<char**>(keywords), &x, &y))
{
return -1;
}
//self->x = x;
//self->y = y;
return 0;
}
static PyObject* PyUIFrame_repr(UIFrame* self)
{
std::ostringstream ss;
ss << "<UIFrame ()>";
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}
static PyObject* PyUIFrame_setSize(UIFrame* self, PyObject* args)
{
float w, h;
if (!PyArg_ParseTuple(args, "ff", &w, &h)) return (PyObject*)-1;
self->box.setSize(sf::Vector2f(w, h));
Py_INCREF(Py_None);
//return PyFloat_FromDouble(mag);
return Py_None;
}
static PyMethodDef PyUIFrame_methods[] = {
{"set_size", (PyCFunction)PyUIFrame_setSize, METH_VARARGS,
"Set the width and height of the UIFrame's visible box"},
{NULL, NULL, 0, NULL}
};
/*
static PyMemberDef PyUIFrame_members[] = {
{"box", T_OBJECT, offsetof(UIFrame, box), 0},
{NULL}
};
*/
static PyTypeObject PyUIFrameType = {
//PyVarObject_HEAD_INIT(NULL, 0)
//.tp_base = NULL,
.tp_name = "mcrfpy.UIFrame",
.tp_basicsize = sizeof(UIFrame),
.tp_itemsize = 0,
.tp_dealloc = [](PyObject* obj) { Py_TYPE(obj)->tp_free(obj); },
//.tp_repr = (reprfunc)PyUIFrame_repr,
//.tp_hash = NULL,
//.tp_iter
//.tp_iternext
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = PyDoc_STR("Custom UIFrame object"),
//.tp_methods = PyUIFrame_methods,
//.tp_members = PyUIFrame_members,
.tp_init = (initproc)PyUIFrame_init,
.tp_new = PyUIFrame_new, //PyType_GenericNew ?
};
// module
/*
static PyModuleDef ui_module = {
PyModuleDef_HEAD_INIT,
.m_name = "ui",
.m_size = -1,
};
PyMODINIT_FUNC PyInit_my_module(void) {
PyObject* module = PyModule_Create(&my_module);
if (module == NULL) {
return NULL;
}
if (PyType_Ready(&MyType_Type) < 0) {
return NULL;
}
Py_INCREF(&MyType_Type);
PyModule_AddObject(module, "UIFrame", (PyObject*)&MyType_Type);
return module;
}
*/
// Point class example
class Point
{
public:
float x, y;
float magnitude() {
return std::sqrt(x*x + y*y);
}
};
static PyObject* PyPoint_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
{
Point* self;
self = (Point*)type->tp_alloc(type, 0);
if (self != nullptr)
{
self->x = 0.0f;
self->y = 0.0f;
}
return (PyObject*)self;
}
// Method to initialize the Point object
static int PyPoint_init(Point* self, PyObject* args, PyObject* kwds)
{
static const char* keywords[] = { "x", "y", nullptr };
float x = 0.0f, y = 0.0f;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ff", const_cast<char**>(keywords), &x, &y))
{
return -1;
}
self->x = x;
self->y = y;
return 0;
}
// Method to calculate the magnitude of the Point
static PyObject* PyPoint_magnitude(Point* self)
{
float mag = self->magnitude();
return PyFloat_FromDouble(mag);
}
static PyMethodDef PyPoint_methods[] = {
{"magnitude", (PyCFunction)PyPoint_magnitude, METH_NOARGS,
"Vector length, or distance from origin."},
{NULL, NULL, 0, NULL}
};
static PyMemberDef PyPoint_members[] = {
{"x", T_FLOAT, offsetof(Point, x), 0},
{"y", T_FLOAT, offsetof(Point, y), 0},
{NULL}
};
static PyTypeObject PyPointType = {
.tp_name = "mcrfpy.Point",
.tp_basicsize = sizeof(Point),
//.tp_itemsize = 0,
//.tp_dealloc = NULL,
//.tp_repr = NULL,
//.tp_hash = NULL,
//.tp_iter
//.tp_iternext
.tp_flags = Py_TPFLAGS_DEFAULT,
//.tp_doc = PyDoc_STR("Custom point object. (x, y)"),
//.tp_methods = PyPoint_methods,
//.tp_members = PyPoint_members,
//.tp_init = (initproc)PyPoint_init,
//.tp_new = PyPoint_new, //PyType_GenericNew ?
};
/*
static PyModuleDef PyPointModule = {
PyModuleDef_HEAD_INIT,
.m_name = "point",
.m_doc = "Custom point module.",
.m_size = -1,
//.m_methods = PyPoint_methods,
};
*/
}

View File

@ -1,5 +1,6 @@
#include "UIMenu.h"
#include "Common.h"
#include "Resources.h"
UIMenu::UIMenu(sf::Font & _font)
: font(_font)
@ -10,6 +11,14 @@ UIMenu::UIMenu(sf::Font & _font)
box.setFillColor(sf::Color(0,0,255));
}
UIMenu::UIMenu()
: font(Resources::font)
{
box.setSize(sf::Vector2f(300, 400));
box.setPosition(sf::Vector2f(300, 250));
box.setFillColor(sf::Color(0,0,255));
}
void UIMenu::render(sf::RenderWindow & window)
{
window.draw(box);

View File

@ -10,9 +10,13 @@ public:
//UIMenu() {};
sf::Font & font;
UIMenu(sf::Font & _font);
UIMenu();
std::vector<sf::Text> captions;
std::vector<Button> buttons;
std::vector<IndexSprite> sprites;
/* idea: */ //std::vector<UIDrawable> children; // on the UIBox class?
sf::RectangleShape box;
bool visible = false;
int next_text = 10;

83
src/UITestScene.cpp Normal file
View File

@ -0,0 +1,83 @@
#include "UITestScene.h"
#include "ActionCode.h"
UITestScene::UITestScene(GameEngine* g) : Scene(g)
{
text.setFont(game->getFont());
text.setString("Test Scene for UI elements");
text.setCharacterSize(24);
//registerAction(ActionCode::KEY + sf::Keyboard::Space, "start_game");
//registerAction(ActionCode::KEY + sf::Keyboard::Up, "up");
//registerAction(ActionCode::KEY + sf::Keyboard::Down, "down");
// Create a UI element or three?
e1 = UIFrame();
e1.box.setPosition(100, 150);
e1.box.setSize(sf::Vector2f(400,400));
e1.box.setFillColor(sf::Color(255, 0, 0));
e1a = UIFrame();
e1a.box.setPosition(50, 50);
e1a.box.setSize(sf::Vector2f(200,200));
e1a.box.setFillColor(sf::Color(0, 255, 0));
e1.children.push_back(&e1a);
e1aa = UIFrame();
e1aa.box.setPosition(5, 5);
e1aa.box.setSize(sf::Vector2f(100,100));
e1aa.box.setFillColor(sf::Color(0, 0, 255));
e1a.children.push_back(&e1aa);
e2 = UICaption();
e2.text.setFont(game->getFont());
e2.text.setString("Hello World.");
//e2.text.setColor(sf::Color(255, 255, 255));
e2.text.setPosition(50, 250);
ui_elements.push_back(&e1);
ui_elements.push_back(&e2);
}
void UITestScene::update()
{
//std::cout << "MenuScene update" << std::endl;
}
void UITestScene::doAction(std::string name, std::string type)
{
//std::cout << "MenuScene doAction: " << name << ", " << type << std::endl;
//if (name.compare("start_game") == 0 and type.compare("start") == 0)
if(ACTION("start_game", "start"))
game->changeScene("py");
/*
else if(ACTIONONCE("up"))
game->getWindow().setSize(sf::Vector2u(1280, 800));
else if(ACTIONONCE("down"))
game->getWindow().setSize(sf::Vector2u(1024, 768));
*/
}
void UITestScene::sRender()
{
game->getWindow().clear();
game->getWindow().draw(text);
// draw all UI elements
for (auto e: ui_elements)
{
//std::cout << "Rendering element\n";
e->render();
}
//e1.render(sf::Vector2f(0, 0));
//e1.render(sf::Vector2f(-100, -100));
game->getWindow().display();
McRFPy_API::REPL();
}

21
src/UITestScene.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include "Common.h"
#include "Scene.h"
#include "GameEngine.h"
#include <list>
#include "UI.h"
class UITestScene: public Scene
{
sf::Text text;
UIFrame e1, e1a, e1aa;
UICaption e2;
std::vector<UIDrawable*> ui_elements;
public:
UITestScene(GameEngine*);
void update() override final;
void doAction(std::string, std::string) override final;
void sRender() override final;
};