Squashed commit of the following: [standardize_font_handling]

closes #60, closes #5, closes #68

The major functionality added here was proper use of types in the module, by importing after finalization.

commit 5009fa0fb9
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sun Apr 7 22:44:15 2024 -0400

    PyFont - use the new standard method for instancing

commit a19781b56a
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sun Apr 7 15:21:17 2024 -0400

    Many hours of pain & research behind this small commit. Safe object building by not messing with types before interpreter is fully initialized

commit 159658521c
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sun Mar 31 21:41:45 2024 -0400

    Font mostly working, just a few weird bugs with the types of the default items added to the module
This commit is contained in:
John McCardle 2024-04-07 22:51:31 -04:00
parent fbf263a038
commit 1a7186f745
8 changed files with 207 additions and 11 deletions

View File

@ -9,6 +9,11 @@ std::vector<sf::SoundBuffer> McRFPy_API::soundbuffers;
sf::Music McRFPy_API::music; sf::Music McRFPy_API::music;
sf::Sound McRFPy_API::sfx; sf::Sound McRFPy_API::sfx;
std::shared_ptr<PyFont> McRFPy_API::default_font;
std::shared_ptr<PyTexture> McRFPy_API::default_texture;
PyObject* McRFPy_API::mcrf_module;
static PyMethodDef mcrfpyMethods[] = { static PyMethodDef mcrfpyMethods[] = {
{"registerPyAction", McRFPy_API::_registerPyAction, METH_VARARGS, {"registerPyAction", McRFPy_API::_registerPyAction, METH_VARARGS,
"Register a callable Python object to correspond to an action string. (actionstr, callable)"}, "Register a callable Python object to correspond to an action string. (actionstr, callable)"},
@ -39,8 +44,15 @@ static PyMethodDef mcrfpyMethods[] = {
}; };
static PyModuleDef mcrfpyModule = { static PyModuleDef mcrfpyModule = {
PyModuleDef_HEAD_INIT, "mcrfpy", NULL, -1, mcrfpyMethods, PyModuleDef_HEAD_INIT, /* m_base - Always initialize this member to PyModuleDef_HEAD_INIT. */
NULL, NULL, NULL, NULL "mcrfpy", /* m_name */
NULL, /* m_doc - Docstring for the module; usually a docstring variable created with PyDoc_STRVAR is used. */
-1, /* m_size - Setting m_size to -1 means that the module does not support sub-interpreters, because it has global state. */
mcrfpyMethods, /* m_methods */
NULL, /* m_slots - An array of slot definitions ... When using single-phase initialization, m_slots must be NULL. */
NULL, /* traverseproc m_traverse - A traversal function to call during GC traversal of the module object */
NULL, /* inquiry m_clear - A clear function to call during GC clearing of the module object */
NULL /* freefunc m_free - A function to call during deallocation of the module object */
}; };
// Module initializer fn, passed to PyImport_AppendInittab // Module initializer fn, passed to PyImport_AppendInittab
@ -72,11 +84,19 @@ PyObject* PyInit_mcrfpy()
auto t = pytypes[i]; auto t = pytypes[i];
while (t != nullptr) while (t != nullptr)
{ {
PyType_Ready(t); /*std::cout << */ PyType_Ready(t); /*<< std::endl; */
PyModule_AddType(m, t); PyModule_AddType(m, t);
t = pytypes[i++]; t = pytypes[i++];
} }
// Add default_font and default_texture to module
McRFPy_API::default_font = std::make_shared<PyFont>("assets/JetbrainsMono.ttf");
McRFPy_API::default_texture = std::make_shared<PyTexture>("assets/kenney_tinydungeon.png", 16, 16);
//PyModule_AddObject(m, "default_font", McRFPy_API::default_font->pyObject());
//PyModule_AddObject(m, "default_texture", McRFPy_API::default_texture->pyObject());
PyModule_AddObject(m, "default_font", Py_None);
PyModule_AddObject(m, "default_texture", Py_None);
//McRFPy_API::mcrf_module = m;
return m; return m;
} }
@ -128,9 +148,13 @@ PyStatus init_python(const char *program_name)
#endif #endif
status = Py_InitializeFromConfig(&config); status = Py_InitializeFromConfig(&config);
PyConfig_Clear(&config);
return status; return status;
} }
/*
void McRFPy_API::setSpriteTexture(int ti) void McRFPy_API::setSpriteTexture(int ti)
{ {
int tx = ti % texture_width, ty = ti / texture_width; int tx = ti % texture_width, ty = ti / texture_width;
@ -139,6 +163,7 @@ void McRFPy_API::setSpriteTexture(int ti)
ty * texture_size, ty * texture_size,
texture_size, texture_size)); texture_size, texture_size));
} }
*/
// functionality // functionality
//void McRFPy_API:: //void McRFPy_API::
@ -155,6 +180,15 @@ void McRFPy_API::api_init() {
//texture_sprite_count = texture_width * texture_height; //texture_sprite_count = texture_width * texture_height;
//texture.setSmooth(false); //texture.setSmooth(false);
// Add default_font and default_texture to module
McRFPy_API::mcrf_module = PyImport_ImportModule("mcrfpy");
std::cout << PyUnicode_AsUTF8(PyObject_Repr(McRFPy_API::mcrf_module)) << std::endl;
//PyModule_AddObject(McRFPy_API::mcrf_module, "default_font", McRFPy_API::default_font->pyObject());
PyObject_SetAttrString(McRFPy_API::mcrf_module, "default_font", McRFPy_API::default_font->pyObject());
//PyModule_AddObject(McRFPy_API::mcrf_module, "default_texture", McRFPy_API::default_texture->pyObject());
PyObject_SetAttrString(McRFPy_API::mcrf_module, "default_texture", McRFPy_API::default_texture->pyObject());
//sprite.setTexture(texture); //sprite.setTexture(texture);
//sprite.setScale(sf::Vector2f(4.0f, 4.0f)); //sprite.setScale(sf::Vector2f(4.0f, 4.0f));
//setSpriteTexture(0); //setSpriteTexture(0);

View File

@ -3,6 +3,9 @@
#include "Python.h" #include "Python.h"
#include <list> #include <list>
#include "PyFont.h"
#include "PyTexture.h"
class GameEngine; // forward declared (circular members) class GameEngine; // forward declared (circular members)
class McRFPy_API class McRFPy_API
@ -14,10 +17,14 @@ private:
McRFPy_API(); McRFPy_API();
public: public:
inline static sf::Sprite sprite; static PyObject* mcrf_module;
inline static sf::Texture texture; static std::shared_ptr<PyFont> default_font;
static void setSpriteTexture(int); static std::shared_ptr<PyTexture> default_texture;
//inline static sf::Sprite sprite;
//inline static sf::Texture texture;
//static void setSpriteTexture(int);
inline static GameEngine* game; inline static GameEngine* game;
static void api_init(); static void api_init();
static void api_shutdown(); static void api_shutdown();

63
src/PyFont.cpp Normal file
View File

@ -0,0 +1,63 @@
#include "PyFont.h"
#include "McRFPy_API.h"
PyFont::PyFont(std::string filename)
: source(filename)
{
font = sf::Font();
font.loadFromFile(source);
}
PyObject* PyFont::pyObject()
{
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Font");
//PyObject* obj = PyType_GenericAlloc(&mcrfpydef::PyFontType, 0);
PyObject* obj = PyFont::pynew(type, Py_None, Py_None);
try {
((PyFontObject*)obj)->data = shared_from_this();
}
catch (std::bad_weak_ptr& e)
{
std::cout << "Bad weak ptr: shared_from_this() failed in PyFont::pyObject(); did you create a PyFont outside of std::make_shared? enjoy your segfault, soon!" << std::endl;
}
// TODO - shared_from_this will raise an exception if the object does not have a shared pointer. Constructor should be made private; write a factory function
return obj;
}
PyObject* PyFont::repr(PyObject* obj)
{
PyFontObject* self = (PyFontObject*)obj;
std::ostringstream ss;
if (!self->data)
{
ss << "<Font [invalid internal object]>";
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}
auto& pfont = *(self->data);
ss << "<Font (family=" << pfont.font.getInfo().family << ") source=`" << pfont.source << "`>";
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}
Py_hash_t PyFont::hash(PyObject* obj)
{
auto self = (PyFontObject*)obj;
return reinterpret_cast<Py_hash_t>(self->data.get());
}
int PyFont::init(PyFontObject* self, PyObject* args, PyObject* kwds)
{
static const char* keywords[] = { "filename", nullptr };
char* filename;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", const_cast<char**>(keywords), &filename))
return -1;
self->data = std::make_shared<PyFont>(filename);
return 0;
}
PyObject* PyFont::pynew(PyTypeObject* type, PyObject* args, PyObject* kwds)
{
return (PyObject*)type->tp_alloc(type, 0);
}

39
src/PyFont.h Normal file
View File

@ -0,0 +1,39 @@
#pragma once
#include "Common.h"
#include "Python.h"
class PyFont;
typedef struct {
PyObject_HEAD
std::shared_ptr<PyFont> data;
} PyFontObject;
class PyFont : public std::enable_shared_from_this<PyFont>
{
private:
std::string source;
public:
PyFont(std::string filename);
sf::Font font;
PyObject* pyObject();
static PyObject* repr(PyObject*);
static Py_hash_t hash(PyObject*);
static int init(PyFontObject*, PyObject*, PyObject*);
static PyObject* pynew(PyTypeObject* type, PyObject* args=NULL, PyObject* kwds=NULL);
};
namespace mcrfpydef {
static PyTypeObject PyFontType = {
.tp_name = "mcrfpy.Font",
.tp_basicsize = sizeof(PyFontObject),
.tp_itemsize = 0,
.tp_repr = PyFont::repr,
//.tp_hash = PyFont::hash,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = PyDoc_STR("SFML Font Object"),
//.tp_base = &PyBaseObject_Type,
.tp_init = (initproc)PyFont::init,
.tp_new = PyType_GenericNew, //PyFont::pynew,
};
}

View File

@ -1,5 +1,5 @@
#include "PyTexture.h" #include "PyTexture.h"
#include "McRFPy_API.h"
PyTexture::PyTexture(std::string filename, int sprite_w, int sprite_h) PyTexture::PyTexture(std::string filename, int sprite_w, int sprite_h)
: source(filename), sprite_width(sprite_w), sprite_height(sprite_h) : source(filename), sprite_width(sprite_w), sprite_height(sprite_h)
@ -28,9 +28,36 @@ sf::Sprite PyTexture::sprite(int index, sf::Vector2f pos, sf::Vector2f s)
PyObject* PyTexture::pyObject() PyObject* PyTexture::pyObject()
{ {
PyObject* obj = PyType_GenericAlloc(&mcrfpydef::PyTextureType, 0); // method 1: works but with type weirdness
//PyObject* obj = PyType_GenericAlloc(&mcrfpydef::PyTextureType, 0);
//Py_SET_TYPE(obj, &mcrfpydef::PyTextureType);
// method 2: does not work (segfault on use of the mcrfpy.Texture object)
//PyObject* obj = PyTexture::pynew(&mcrfpydef::PyTextureType, Py_None, Py_None);
// method 3: does not work (segfault on use of the mcrfpy.Texture object)
std::cout << "Find type" << std::endl;
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture");
//auto type = obj->ob_type;
//auto type = &mcrfpydef::PyTextureType;
//std::cout << "assigned value 0x" << std::hex << reinterpret_cast<long>(type) << std::endl;
//std::cout << "Found PyTextureType: " << PyUnicode_AsUTF8(PyObject_Repr((PyObject*)type)) << std::endl;
//std::cout << "PyTextureType metatype: " << PyUnicode_AsUTF8(PyObject_Repr((PyObject_Type((PyObject*)type)))) << std::endl;
//std::cout << "tp_alloc: 0x" << std::hex << reinterpret_cast <long>(type->tp_alloc) << std::endl <<
// "tp_new: 0x" << std::hex << reinterpret_cast<long>(type->tp_new) << std::endl;
//PyObject* obj = ((PyTypeObject*)type)->tp_new((PyTypeObject*)type, Py_None, Py_None);
PyObject* obj = PyTexture::pynew(type, Py_None, Py_None);
//Py_SET_TYPE(obj, type);
// method 4: call the type object?
std::cout << "Instantiated" << std::endl;
//Py_SET_TYPE(obj, &mcrfpydef::PyTextureType);
//PyObject_CallFunction(&mcrfpydef::PyTextureType,
try { try {
((PyTextureObject*)obj)->data = shared_from_this(); ((PyTextureObject*)obj)->data = shared_from_this();
std::cout << "Sideloaded texture: " << PyUnicode_AsUTF8(PyObject_Repr(obj)) << std::endl;
} }
catch (std::bad_weak_ptr& e) catch (std::bad_weak_ptr& e)
{ {
@ -40,6 +67,22 @@ PyObject* PyTexture::pyObject()
return obj; return obj;
} }
PyObject* PyTexture::repr(PyObject* obj)
{
PyTextureObject* self = (PyTextureObject*)obj;
std::ostringstream ss;
if (!self->data)
{
ss << "<Texture [invalid internal object]>";
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}
auto& ptex = *(self->data);
ss << "<Texture " << ptex.sheet_height << " rows, " << ptex.sheet_width << " columns; " << ptex.sprite_width << "x" << ptex.sprite_height << "px sprites. source='" << ptex.source << "'>";
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}
Py_hash_t PyTexture::hash(PyObject* obj) Py_hash_t PyTexture::hash(PyObject* obj)
{ {
auto self = (PyTextureObject*)obj; auto self = (PyTextureObject*)obj;

View File

@ -21,6 +21,7 @@ public:
sf::Sprite sprite(int index, sf::Vector2f pos = sf::Vector2f(0, 0), sf::Vector2f s = sf::Vector2f(1.0, 1.0)); sf::Sprite sprite(int index, sf::Vector2f pos = sf::Vector2f(0, 0), sf::Vector2f s = sf::Vector2f(1.0, 1.0));
PyObject* pyObject(); PyObject* pyObject();
static PyObject* repr(PyObject*);
static Py_hash_t hash(PyObject*); static Py_hash_t hash(PyObject*);
static int init(PyTextureObject*, PyObject*, PyObject*); static int init(PyTextureObject*, PyObject*, PyObject*);
static PyObject* pynew(PyTypeObject* type, PyObject* args=NULL, PyObject* kwds=NULL); static PyObject* pynew(PyTypeObject* type, PyObject* args=NULL, PyObject* kwds=NULL);
@ -31,10 +32,12 @@ namespace mcrfpydef {
.tp_name = "mcrfpy.Texture", .tp_name = "mcrfpy.Texture",
.tp_basicsize = sizeof(PyTextureObject), .tp_basicsize = sizeof(PyTextureObject),
.tp_itemsize = 0, .tp_itemsize = 0,
.tp_repr = PyTexture::repr,
.tp_hash = PyTexture::hash, .tp_hash = PyTexture::hash,
.tp_flags = Py_TPFLAGS_DEFAULT, .tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = PyDoc_STR("SFML Texture Object"), .tp_doc = PyDoc_STR("SFML Texture Object"),
//.tp_base = &PyBaseObject_Type,
.tp_init = (initproc)PyTexture::init, .tp_init = (initproc)PyTexture::init,
.tp_new = PyTexture::pynew, .tp_new = PyType_GenericNew, //PyTexture::pynew,
}; };
} }

View File

@ -13,6 +13,7 @@
#include "PyColor.h" #include "PyColor.h"
//#include "PyLinkedColor.h" //#include "PyLinkedColor.h"
#include "PyVector.h" #include "PyVector.h"
#include "PyFont.h"
enum PyObjectsEnum : int enum PyObjectsEnum : int
{ {
@ -409,7 +410,7 @@ static int PyUIDrawable_set_click(PyUIGridObject* self, PyObject* value, void* c
* Begin PyFontType defs * Begin PyFontType defs
* *
*/ */
/*
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
std::shared_ptr<sf::Font> data; std::shared_ptr<sf::Font> data;
@ -444,6 +445,7 @@ static int PyUIDrawable_set_click(PyUIGridObject* self, PyObject* value, void* c
return (PyObject*)self; return (PyObject*)self;
} }
}; };
*/
/* /*
* *
@ -792,13 +794,14 @@ static int PyUIDrawable_set_click(PyUIGridObject* self, PyObject* value, void* c
// //
// Set Font // Set Font
// //
std::cout << PyUnicode_AsUTF8(PyObject_Repr(font)) << std::endl;
if (font != NULL && !PyObject_IsInstance(font, (PyObject*)&PyFontType)){ if (font != NULL && !PyObject_IsInstance(font, (PyObject*)&PyFontType)){
PyErr_SetString(PyExc_TypeError, "font must be a mcrfpy.Font instance"); PyErr_SetString(PyExc_TypeError, "font must be a mcrfpy.Font instance");
return -1; return -1;
} else if (font != NULL) } else if (font != NULL)
{ {
auto font_obj = (PyFontObject*)font; auto font_obj = (PyFontObject*)font;
self->data->text.setFont(*font_obj->data); self->data->text.setFont(font_obj->data->font);
self->font = font; self->font = font;
Py_INCREF(font); Py_INCREF(font);
} else } else

View File

@ -2,6 +2,10 @@ import mcrfpy
font = mcrfpy.Font("assets/JetbrainsMono.ttf") font = mcrfpy.Font("assets/JetbrainsMono.ttf")
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16) texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
print("[game.py] Default texture:")
print(mcrfpy.default_texture)
print(type(mcrfpy.default_texture))
# build test widgets # build test widgets
mcrfpy.createScene("pytest") mcrfpy.createScene("pytest")