Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
Trouble with OpenGL/Ctypes and Pythonista
-
Greetings,
I am having a heck of a time trying to get a simple OpenGL example to run....this is an example that I have used on the Mac and Raspberry Pi that just draws a simple triangle. I started with example code from @omz then got stuck on the function glShaderSource. My code is on GitHub and it fails on line 86 with the error message "ctypes.ArgumentError: argument 3: <class 'TypeError'>: wrong type".Any help you can provide would be much appreciated. I am using Python 2.7 with the latest version of Pythonista. Thanks!
-
You have commented out the lines that set the argtypes. It is expecting a c_void_p (by default, i think), you are giving it a character array.
-
I think if
argtypes
are not set,ctypes
guesses the type based on what arguments you give. For example anint
becomes ac_int
. The defaultargtypes
guesses are often incorrect, so it's a good idea to set theargtypes
explicitly. If you do that and you get aTypeError
, that means you're not passing an object of the right type.In this case here, it looks like you're overthinking things a little when creating the char array :) In your code you write
char_arr = (c_char_p * len(shader_source))
.c_char_p
stands forchar *
, so in this case you're creating an array ofchar *
s withlen(shader_source)
elements. If you want to create a fixed-length char array, you should usec_char
as the element type (e. g.c_char * len(shader_source)
). But in this case I think you don't need to do any manual conversion at all -ctypes
can automatically convert a Pythonbytes
object to ac_char_p
, so if you set the function'sargtypes
correctly, you can pass it thebytes
object directly. -
Thanks JonB uncommenting argtypes and giving it a c_void_p it is now working....I have a different error but at least I got past that line. Thank you!
-
Thank you both @dgelessus and @JonB !! ..... I use to be good at C but Python has spoiled me :-). Without the OpenGLES-Pythonista project I am not even sure I would of gotten this far. What confuses me is I can't just pass glShaderSource a c-string with the shader code it is an array of string pointers, OpenGL ES function definition
But your answer(s) at least got me further down the road and I will continue to explore.
-
Ah, if the third parameter really is a
char **
, that's a different story - I don't know anything about OpenGL and its APIs, so I assumed the double pointer was by accident.In that case, first of all you should set the
argtypes
appropriately, it will make calling the function much cleaner:c.glShaderSource.restype = None c.glShaderSource.argtypes = [GLuint, GLsizei, POINTER(c_char_p), POINTER(GLint)]
Now you can call the function like this:
c.glShaderSource(shader, 1, byref(cast(source, c_char_p)), None)
Since the
argtypes
are set, the integer arguments don't need to be manually wrapped in the required types.byref
behaves like the&
operator in C, it returns the address of the given object (which in this case creates achar **
). Thecast
toc_char_p
is necessary becausebyref
doesn't perform any automatic conversions on its argument.The
length
argument isNone
(CNULL
), which according to the documentation means that allsource
strings are zero-terminated (which is the case for all C strings created byctypes
). -
@dgelessus thanks again!!!! That is much more elegant than the code I was using. I have now carefully gone through all the argtypes with the OpenGL function I was going to use and made sure they are correct. I have updated the code on github. Thanks!