I’ve checked the source code and there is no way to change nElements once it has been set, but you shouldn’t need to. Instead you can set nElements to the maximum number of stimuli that will be shown in any trial and then hide the unwanted ones on each trial, by setting their opacities to zero. The disp.opacities method is able to set the opacity values of individual stimuli if it supplied it with an Nx1 array, where N = nElements.
You can’t manipulate the textures of individual stimuli within the array, they must all be the same. However, you can manipulate the colours of individual stimuli using the disp.colors method, again by supplying it with an Nx1 array of RGB values. This will allow you to have your target one colour and your distractors another.
It is important to note that, in the ElementArrayStim component, textures and colours interact with one another, since both the texture variable and the colour variable specify a set of RGB values. I don’t think they are supposed to interact, since ‘blendmode = avg’ is said to be the default and this should mean that colours with an opacity of 1 should not mix (https://www.psychopy.org/general/rendering.html#blendmode), but trial and error shows that they do.
This interaction is complicated. But since your stimuli have a distinct foreground area and background area (ImageStim with a numpy array having unexpected results), the situation becomes straightforward, provided that you set the background RGB values for your texture to mid-grey (128, 128, 128) and the foreground RGB values for your texture to white (255,255,255). This is because a texture value of 128 interacts with any colour value to yield 128, while a texture value of 255 interacts with any colour value to yield the original colour value. So, when your texture is a white shape on a mid-grey background and you set the colour of a particular stimulus to blue, you’ll get a blue shape on a mid-grey background. And if you set the colour of another stimulus to red, you’ll get a red shape on a mid-grey background.
If you want a different background colour (say, black), then things get more complicated. It becomes necessary to tailor your background texture value to suit your colour value, or vice versa. For example, to give a blue stimulus a black background, the background texture would have to be set to yellow. Since PsychoPy’s ElementArrayStim component cannot set the texture to be different for different stimuli within an array, this means that different stimuli CANNOT have different foreground colours and a uniform background colour unless they are on a mid-grey background.
To sum up, you can have a fixed number of stimuli; and a single texture value, with a white foreground on a mid-grey background, which does not need to change. Each trial, you can set the opacity values of all your stimuli to 1 or 0 depending on whether you want them to be visible; and set the colour values of all your stimuli to the colours you want them to be. For example, if you had 4 stimuli:
disp.colors = [(1.0, -1.0, -1.0), (-1.0, 1.0, -1.0), (-1.0, -1.0, 1.0), (1.0, 1.0, -1.0)]
disp.opacities = [1.0, 1.0, 0.0, 0.0]
Note that these are setter methods and they require values to be set for all stimuli, i.e. you must supply an Nx1 array. The way they are named, it looks as if you are addressing the opacity and colour attributes directly and could therefore change a subset of elements in the array, but this is not the case.