-
Notifications
You must be signed in to change notification settings - Fork 1
Contribution Notes: colours.c
This page details everything you must take into consideration before making changes to colours.c
. This will help reduce the number of bugs created by changes and make it easier to keep a track of what needs changing. Before making changes, make sure to read the PEP 7 Style Guide
You may want to create a new colour type or another custom type to use in the C class. These next steps detail the process to do so with an example of the ColourHSV
type.
- Create type
struct
and define what the object will contain - Create the function which creates a new instance of the type
- Create the function which initializes a new instance
- Define the
tp_slots
of the type - Add the class build code to the
PyMODINIT_FUNC
function
1. Create the custom type struct
, ie what the object will contain. Reference.
typedef struct {
PyObject_HEAD
int red, green, blue;
char colour[7];
} ColourHSV;
PyObject_HEAD is mandatory at the start of each object struct
The red, green, blue
variables are defined for all colour objects while the 7-length string colour
defines the actual colour itself in hex format. (Eg. "#ffffff"
for white). Do not initialize any values for variables here, this is just what an instance of the object contains.
The code ends with the name of the class, ColourHSV
.
2. Create the type's instance creation function. Reference.
static PyObject* ColourHSV_new(PyTypeObject *type, PyObject *args, PyObject*kwds) {
ColourHSV *self;
self = (ColourHSV *) type->tp_alloc(type, 0);
if (self != NULL) {
strncpy(self->colour, "#000000", 7);
self->red = 0;
self->green = 0;
self->blue = 0;
}
return (PyObject *) self;
}
This function will be called whenever someone creates a new instance of the ColourHSV
type. It starts out with the line of the arguments required for the function PyTypeObject *type, PyObject *args, PyObject*kwds
. This is standard for colour types.
Then, we allocate the object some memory,
self = (ColourHSV *) type->tp_alloc(type, 0);
and set the variable self
to its value. Checking if (self != NULL)
ensures that no error occurred after which we define the default values for the variables of the class. Finally, the code returns self
, essentially giving back the object.
3. Create the type's initialization creation function. Reference.
static int ColourHSV_init(ColourHSV *self, PyObject *args, PyObject *kwds) {
static char *kwlist[] = {"h", "s", "v", NULL};
int h = 0, s = 0, v = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iii", kwlist, &h, &s, &v))
return -1;
int red = 0, green = 0, blue = 0;
/* Code to define the values of red, green, & blue. It is the conversion from HSV to RGB here.*/
self->red = red;
self->green = green;
self->blue = blue;
sprintf(self->colour, "#%x%x%x", red, green, blue);
return 0;
}
This code is incredibly important so let's step through it carefully. It is called whenever someone creates a new ColourHSV
instance.
static char *kwlist[] = {"h", "s", "v", NULL};
int h = 0, s = 0, v = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iii", kwlist, &h, &s, &v))
return -1;
This part starts by defining what arguments the class is going to receive ({"h", "s", "v", NULL}
the NULL
is to terminate the array). Here it is the HSV values. Then, we parse the values and by saying "|iii"
it tells the API that the values for h
, s
, & v
are integers. If there is an error in this step, the program returns -1
.
Then it proceeds to convert the HSV values into RGB and stores them in 3 variables. The sprintf()
allows for the conversion of the RGB values into a hex string and the code returns a 0.
4. Define the type object. Reference.
This step defines what all fields are required for the colour object. Here is an example:
static PyTypeObject ColourHSVType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "goopylib.ColourHSV",
.tp_doc = "An object to represent goopylib colours using HSV values",
.tp_basicsize = sizeof(ColourHSV),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_new = PyType_GenericNew,
.tp_dealloc = (destructor) Colour_dealloc,
.tp_new = ColourHSV_new,
.tp_init = (initproc) ColourHSV_init,
.tp_members = Colour_members,
};
-
.tp_name
is the name of the type -
.tp_doc
is the docstring describing the type -
.tp_basicsize
just defines the size of the object, this issizeof(ColourHSV)
, the previously definedstruct
-
.tp_itemsize
is set to 0 to specify that instances of the object have a fixed length -
.tp_flags
is set to the generic, default flag -
.tp_new
points to the function we created which defined how to create a new instance -
.tp_init
points to the initialization function of the type previously created by us too -
.tp_members
this defines the attributes of the type, it is defined as:
static PyMemberDef Colour_members[] = {
{"red", T_INT, offsetof(Colour, red), 0,
"RGB red value of Colour"},
{"green", T_INT, offsetof(Colour, green), 0,
"RGB green value of Colour"},
{"blue", T_INT, offsetof(Colour, blue), 0,
"RGB blue value of Colour"},
{"colour", T_STRING, offsetof(Colour, colour), 0,
"Hexadecimal colour value of Colour"},
{NULL}
};
Every attribute here contains 5 elements: name, datatype, variable name, access flag, and its docstring. The {NULL}
at the end marks the end of all attributes.
The next step is to allow Python to build the extension module and the class along with it. Add this code to PyMODINIT_FUNC
:
PyObject *m;
if (PyType_Ready(&ColourHSVType) < 0)
return NULL;
Py_INCREF(&ColourHSVType);
m = PyModule_Create(&CColoursModule);
if (m == NULL)
return NULL;
if (PyModule_AddObject(m, "ColourHSV", (PyObject *) &ColourHSVType) < 0) {
Py_DECREF(&ColourHSVType);
Py_DECREF(m);
return NULL;}
Look at fields for more fields you can specify here.
Feel free to use this library in any of your projects and let me know about them!
Contact Author: bhavyemathur@gmail.com