Repository: psychopy/psychopy

Issue:
For visual.textStim, setOpacity() has no effect without setText()
For visual.textStim, using .setOpacity() has no effect on subsequent .draw(), unless .setText() is called between setOpacity() and draw(). Please note that opacity parameter (accessed by .opacity) does change, it is the stimulus appearance that doesn't change.

In the following example I am trying to present a sequence of letters in such a way that each letter fades in and out during a period of 60 screen flips.

``` python
from psychopy import core, visual, parallel
import numpy as np

win = visual.Window(size=(640,480), fullscr=False, allowGUI=True, units='pix')
myTextStim = visual.TextStim(win, font='Times New Roman', height=64, color='Black')

# generate some random letters
nletters = 10
randomAscii = np.random.random_integers(97,122, nletters)
letters = [chr(asc) for asc in randomAscii]

# let the opacity values change linearly from 0 to 1 to 0
n = np.arange(60)
opValues = np.bartlett(60)

# present letters one after another
for l in letters:

    myTextStim.setText(l)

    for nFrames in range(60):
        myTextStim.setOpacity(opValues[nFrames])
        # won't work without the line below
        #myTextStim.setText(myTextStim.text) 
        myTextStim.draw()
        win.flip()
```

I know that this issue has been reported before via mailing list (for earlier psychoPy versions), but using setText() after every opacity change is inconvenient because of the recent memory issues (#973 and related). Also, replacing .setText() with using ._pygletTextObj.text = ... (which was suggested to circumvent memory problem) does not make opacity change become visible.

In my experiment I am using multiple words, presented as hundreds of letters (equivalent to large nletters in the above example), and thus memory usage (so far I have watched it only via task manager) grows very rapidly, becoming an issue itself.



Discussion:
Thanks for reporting this. I see the same thing as you. Actually, I see it only after I revert a change I recently made to try to reduce the impact of the memory issue. (That change only sets the text if the text itself is new. However, the opacity is not considered there at all, and so the new version does does not even start to do the right thing. Drat.)

This behavior will not be easy to fix, because text rendering depends on pyglet. PsychoPy tries to set the opacity during OpenGL calls but it evidently has no effect. I assumed that it would have an effect, but apparently it all handled during .setText(). 

For your letter paradigm, you could draw and then capture an image of the letter via screenshot (or a small BufferImageStim), and then draw the image, while varying the opacity every frame. This is more hassle but should work.

This issue continues to be reported:

https://discourse.psychopy.org/t/display-response-recorded-message-after-specific-key-presses/14651
https://discourse.psychopy.org/t/opacity-of-text-stimuli-is-not-updating/11152
https://discourse.psychopy.org/t/setting-opacity-for-fade-effect/7334

I guess we need to know if it affects the new `TextBox2`, which will presumably take over in Builder text components, and if online text stimulus opacity is controlled correctly (presumably also switching to the PsychoJS equivalent of `TextBox2` though).
I'm attempting to fix this in #3189 as I'm changing how opacity is handled anyway, will mention this issue when I figure it out :) 

Resolving patch:
diff --git a/psychopy/visual/text.py b/psychopy/visual/text.py
--- a/psychopy/visual/text.py
+++ b/psychopy/visual/text.py
@@ -617,6 +617,15 @@ def _updateListNoShaders(self):
         GL.glEndList()
         self._needUpdate = False
 
+    @attributeSetter
+    def opacity(self, value):
+        BaseVisualStim.opacity.func(self, value)
+        self._setTextShaders()
+
+    def setOpacity(self, newOpacity, operation='', log=None):
+        BaseVisualStim.setOpacity(self, newOpacity, operation='', log=None)
+        self._setTextShaders()
+
     @attributeSetter
     def flipHoriz(self, value):
         """If set to True then the text will be flipped left-to-right.  The


Test patch: