Forum Archive

Objc_util wants incorrect number of parameters

ellie_ff1493

I have been trying to make noise in Pythonista but am new to objc.
This code returns an error saying it needs 2 parameters but it needs 5 “initWithNoise_size_origin_sampleCount_seamless_”

The documentation says 5 doc and examples to

import objc_util
from objc_util import *

objc_util.load_framework("GameplayKit")
GKNoise = objc_util.ObjCClass("GKNoise")
GKNoiseMap = objc_util.ObjCClass("GKNoiseMap")
GKPerlinNoiseSource = objc_util.ObjCClass('GKPerlinNoiseSource')

noiseSource = GKPerlinNoiseSource.alloc().init()
noiseSource.frequency = 1
noiseSource.octaveCount = 6
noiseSource.lacunarity = 2
noiseSource.persistence = 0.5

noise = GKNoise.alloc().initWithNoiseSource(noiseSource)#make the noise algorithem 

print('initWithNoise_size_origin_sampleCount_seamless_' in dir(GKNoiseMap.alloc()))#is what im calling valid

newMap = GKNoiseMap.alloc().initWithNoise_size_origin_sampleCount_seamless_(noise, (10,10),(0,0),(10,10),True)#it wants 2 but needs 5 (i tryed two, it crashs)

row  = []
for x in range(10):
    col = []
    for y in range(10):

        col.append(newMap.interpolatedValueAtPosition_(position=(x/10,y/10)))
    row.append(col)

print(row)
print()


JonB

check GKNoiseMap.alloc().initWithNoise_size_origin_sampleCount_seamless_.encoding

or maybe

GKNoiseMap.noiseMapWithNoise_size_origin_sampleCount_seamless_.encoding

likely, pythonista got confused, and you will need to specify argtypes and restype in the call.

Also, the origin and size arguments do not take tuples, they take some sort of vector2d, which is probably going to be a ctypes.double*2, and you will have to construct such an array.

but one step at a time, post back on the encoding.

ellie_ff1493

thanks, will try that and get back to you if it works

ellie_ff1493

It’s actually looking quite hard, how do I set a type I don’t have a reference to, I looked in the docs and it’s in a framework called simd

JonB

it is probably an array of doubles. you need to print out the .encoding.

cvp

@ellie_ff1493 I tried, without success, this

import objc_util
from objc_util import *
import ctypes

objc_util.load_framework("GameplayKit")
GKNoise = objc_util.ObjCClass("GKNoise")
GKNoiseMap = objc_util.ObjCClass("GKNoiseMap")
GKPerlinNoiseSource = objc_util.ObjCClass('GKPerlinNoiseSource')

class vector_double2 (ctypes.Structure):
    _fields_ = [('x', ctypes.c_float), ('y', ctypes.c_float)]

class vector_int2 (ctypes.Structure):
    _fields_ = [('x', ctypes.c_int), ('y', ctypes.c_int)]

noiseSource = GKPerlinNoiseSource.alloc().init()
noiseSource.frequency = 1
noiseSource.octaveCount = 6
noiseSource.lacunarity = 2
noiseSource.persistence = 0.5

noise = GKNoise.alloc().initWithNoiseSource(noiseSource)#make the noise algorithem 

print('initWithNoise_size_origin_sampleCount_seamless_' in dir(GKNoiseMap.alloc()))#is what im calling valid


newMap = GKNoiseMap.alloc().initWithNoise_(noise)
# only to know parameters types
print(dir(newMap))
print(newMap.size())
print(dir(newMap.origin()))
print(newMap.sampleCount())
print(newMap.isSeamless())


size = vector_double2(10.0,10.0)
origin = vector_double2(0.0,0.0)
#origin = newMap.origin()
#print(dir(origin))
#print(type(origin))
sampleCount = vector_int2(10,10)

newMap = GKNoiseMap.alloc().initWithNoise_size_origin_sampleCount_seamless_(noise, size, origin, sampleCount, True, restype=GKNoiseMap, argtypes=[GKNoise, ctypes.POINTER(vector_double2), ctypes.POINTER(vector_double2),  ctypes.POINTER(vector_int2), c_bool])


row  = []
for x in range(10):
    col = []
    for y in range(10):

        col.append(newMap.interpolatedValueAtPosition_(position=(x/10,y/10)))
    row.append(col)

print(row)
print()

I got error

Traceback (most recent call last):
  File "/private/var/mobile/Containers/Shared/AppGroup/668A7D98-7216-47ED-917D-AA0B6173167E/Pythonista3/Documents/test4.py", line 43, in <module>
    newMap = GKNoiseMap.alloc().initWithNoise_size_origin_sampleCount_seamless_(noise, size, origin, sampleCount, True, restype=GKNoiseMap, argtypes=[GKNoise, ctypes.POINTER(vector_double2), c_void_p,  ctypes.POINTER(vector_int2), c_bool])
  File "/var/containers/Bundle/Application/FED7F8B2-4F86-4833-BCFB-C2F8803CD0F1/Pythonista3.app/Frameworks/Py3Kit.framework/pylib/site-packages/objc_util.py", line 892, in __call__
    objc_msgSend.argtypes = argtypes
TypeError: item 3 in _argtypes_ has no from_param method

JonB

can someone please print the .encoding attribute of the function you want to call?

objc is self documenting, for the most part we don't have to guess what it wants.

cvp

@JonB

print(GKNoiseMap.alloc().initWithNoise_size_origin_sampleCount_seamless_.encoding)

Gives

b'@68@0:8@16244056B64'
JonB

For your argtypes, first should be c_void_p, not GKNoise (that is what the error is saying. there are two hidden argtypes that get added, so your first is really the third... i think, i forget if you have to add the secret ones)

next, the signature calls for vectors, not pointers to the vectors. so remove POINTER. then your third arg is wrong... restype also should be c_void_p.

try this:

argtypes=[c_void_p, vector_double2, vector_double2, vector_int2, c_bool], restype=c_void_p

you might need to add two c_void_p's at the start --i forget if objc_util does that for you (for the hidden _self and selector arguments to objc_msgSend)

cvp

@JonB Thanks as usual, how can I find an explanation of .encoding codes?
@ellie_ff1493 this works

import objc_util
from objc_util import *
import ctypes

objc_util.load_framework("GameplayKit")
GKNoise = objc_util.ObjCClass("GKNoise")
GKNoiseMap = objc_util.ObjCClass("GKNoiseMap")
GKPerlinNoiseSource = objc_util.ObjCClass('GKPerlinNoiseSource')

class vector_double2 (ctypes.Structure):
    _fields_ = [('x', ctypes.c_float), ('y', ctypes.c_float)]

class vector_int2 (ctypes.Structure):
    _fields_ = [('x', ctypes.c_int), ('y', ctypes.c_int)]

noiseSource = GKPerlinNoiseSource.alloc().init()
noiseSource.frequency = 1
noiseSource.octaveCount = 6
noiseSource.lacunarity = 2
noiseSource.persistence = 0.5

noise = GKNoise.alloc().initWithNoiseSource(noiseSource)#make the noise algorithem 

size = vector_double2(10.0,10.0)
origin = vector_double2(0.0,0.0)
sampleCount = vector_int2(10,10)

newMap = GKNoiseMap.alloc().initWithNoise_size_origin_sampleCount_seamless_(noise, size, origin, sampleCount, True, argtypes=[c_void_p, vector_double2, vector_double2, vector_int2, c_bool], restype=c_void_p)



row  = []
for x in range(10):
    col = []
    for y in range(10):

        col.append(newMap.interpolatedValueAtPosition_(position=(x/10,y/10)))
    row.append(col)

print(row)
print()

JonB

see https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html

the encodings you get from introspection also have bytesize numbers after each character, which you can ignore.

cvp

@JonB Thanks

simon_hibbs

@cvp I tried the code you posted above, but it doesn't work for me. I just get an array in which all the values are -1.0 and can't seem to get anything else out of the noise map.

I am finding using objc_util frustrating. Under what circumstances do I need to use the .alloc() method on an object? To be fair the Apple documentation on these classes is terrible. Putting all the documentation on a class and it's methods and properties on a single page in a consistent format can't be all that hard.

JonB

There is a difference between primitive ctypes structures and objc objects. If you are creating an objc object directly from a ObjCClass, with an init method, you need an alloc:

E.g.

ObjCClass('SomeClass').alloc().initWithParameter_(value)

Some classes provide ways of directly giving you an object, or giving you a pointer to a shared object. So

ObjCClass('SomeClass').someClassWithParameter_(param)

Or

ObjCClass('SomeClass').sharedSomeClass()

Also, .new() is short for .alloc().init().

So, if you are calling an init method you must use alloc. If not, not.

cvp

@simon_hibbs the intent of my code was only to help @ellie_ff1493, I did not try to understand the code it-self, sorry. Perhaps he could help you

To check if a class needs alloc() or not, I first do

Print(dir(ObjCClass('SomeClass')))

and I see if alloc is in the list or not, then

Print(dir(ObjCClass('SomeClass').alloc()))
simon_hibbs

@cvp Ok, thanks. very helpful.

mikael

Thank you all, this thread has been so very useful.