Possible Shadow Variables in REPL?

CircuitPython on hardware including Adafruit's boards, and CircuitPython libraries using Blinka on host computers.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
Draculen
 
Posts: 72
Joined: Mon Jan 24, 2022 11:14 pm

Possible Shadow Variables in REPL?

Post by Draculen »

I'm adding code to adafruits LED Animation sparkle so that it may support on_cycle_complete (play through once and move onto the next animation) However i'm running into an issue where Python seems to think i'm calling adafruits sequence.mpy code when that code is not even referenced/way out of scope. Instead I have local variables of the same name (the callback(self) seems to be where this is occuring but I am really confused as to why its happening.

the REPL says the following

File "adafruit_led_animation/sequence.py", line 197, in animate
File "/lib/adafruit_led_animation/group.py", line 156, in animate
File "adafruit_led_animation/animation/__init__.py", line 96, in animate
File "/lib/adafruit_led_animation/animation/sparkle.py", line 81, in on_cycle_complete
File "/lib/adafruit_led_animation/group.py", line 112, in _group_done
File "/lib/adafruit_led_animation/group.py", line 123, in on_cycle_complete
File "adafruit_led_animation/sequence.py", line 133, in _sequence_complete
File "adafruit_led_animation/sequence.py", line 160, in _advance
File "adafruit_led_animation/sequence.py", line 179, in next
File "adafruit_led_animation/sequence.py", line 129, in on_cycle_complete

I will post all the entire script just so Im not leaving anything out but the problem part seems to be
def on_cycle_complete in the sparkle animation. i've tried for hours to see what the issue is but so long as that callback is in sparkle it seems to run to sequence.py's version of it instead.

Code: Select all

import random
from adafruit_led_animation.animation import Animation
__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_LED_Animation.git"


class Sparkle(Animation):

    # pylint: disable=too-many-arguments
    def __init__(
        self, pixel_object, speed, color, num_sparkles=1, name=None, mask=None
    ):
        if len(pixel_object) < 2:
            raise ValueError("Sparkle needs at least 2 pixels")
        if mask:
            self._mask = mask
        else:
            self._mask = []
        if len(self._mask) >= len(pixel_object):
            raise ValueError("Sparkle mask should be smaller than number pixel array")
        self._half_color = color
        self._dim_color = color
        self._sparkle_color = color
        self._num_sparkles = num_sparkles
        self._num_pixels = len(pixel_object)
        self._pixels = []
        # self.add_cycle_complete_receiver(animation, callback)

        super().__init__(pixel_object, speed, color, name=name)

    on_cycle_complete_supported = True

    def on_cycle_complete(self):
        self.cycle_count += 1
        print("cycle complete called")
        if self.cycle_count % self.notify_cycles == 0:
            for callback in self._also_notify:
                callback(self) #This is the part that seems to create a domino effect of tracebacks/references out of scope
    def _set_color(self, color):
        half_color = tuple(color[rgb] // 4 for rgb in range(len(color)))
        dim_color = tuple(color[rgb] // 10 for rgb in range(len(color)))
        for pixel in range(  # pylint: disable=consider-using-enumerate
            len(self.pixel_object)
        ):
            if self.pixel_object[pixel] == self._half_color:
                self.pixel_object[pixel] = half_color
            elif self.pixel_object[pixel] == self._dim_color:
                self.pixel_object[pixel] = dim_color
        self._half_color = half_color
        self._dim_color = dim_color
        self._sparkle_color = color

    def _random_in_mask(self):
        if len(self._mask) == 0:
            return random.randint(0, (len(self.pixel_object) - 1))
        return self._mask[random.randint(0, (len(self._mask) - 1))]

    def draw(self):
        self._pixels = [self._random_in_mask() for _ in range(self._num_sparkles)]
        for pixel in self._pixels:
            self.pixel_object[pixel] = self._sparkle_color

        if self.draw_count % len(self.pixel_object) == 0:
            print("animation has reached end of pixel array")
            self.cycle_complete = True

    def after_draw(self):
        self.show()
        for pixel in self._pixels:
            self.pixel_object[pixel % self._num_pixels] = self._half_color
            if (pixel + 1) % self._num_pixels in self._mask:
                self.pixel_object[(pixel + 1) % self._num_pixels] = self._dim_color

    def reset(self):
        print("diminishing returns")

User avatar
neradoc
 
Posts: 542
Joined: Wed Apr 27, 2016 2:38 pm

Re: Possible Shadow Variables in REPL?

Post by neradoc »

Hi, can you show the test code that you use ?
And the full stacktrace of the error ? There is no error name in what you posted (or starting point).

The following code on a Circuit Playground Bluefruit, using your sparkle library, seems to run without error:

Code: Select all

import board
import neopixel

from sparkle import Sparkle

pixel_pin = board.NEOPIXEL
pixel_num = 10

pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.5, auto_write=False)
sparkle = Sparkle(pixels, speed=0.1, color=0x00FFFF)

def receiver(sender):
	print(sender)

sparkle.add_cycle_complete_receiver(receiver)

while True:
	sparkle.animate()

User avatar
Draculen
 
Posts: 72
Joined: Mon Jan 24, 2022 11:14 pm

Re: Possible Shadow Variables in REPL?

Post by Draculen »

When the animation of my sparkle code in code.py reaches the end of the LED array is when I get the error from my original post, the error is TypeError: AnimationSequence is not a call-able object

Here is the code thats running ( I tried to trim down everything that shouldn't matter just to make it less confusing)

Code: Select all

import board
import neopixel
import time
from adafruit_led_animation.animation.comet import Comet
from adafruit_led_animation.animation.chase import Chase
from adafruit_led_animation.sequence import AnimationSequence
from adafruit_led_animation.color import CYAN, TEAL
from adafruit_led_animation.group import AnimationGroup
from adafruit_led_animation import animation
# from adafruit_led_animation import color
from adafruit_led_animation.animation.sparkle import Sparkle

# from adafruit_led_animation import helper
from adafruit_led_animation.helper import PixelMap
from adafruit_led_animation.helper import PixelSubset

pixel_pin = board.A5
pixel_pinL = board.A2
pixel_pinBR = board.D10
pixel_pinBL = board.D12
pixel_num = 103
pixels = neopixel.NeoPixel(
    pixel_pin, pixel_num, brightness=0.5, auto_write=False, pixel_order=(1, 0, 2, 3)
)
pixil = neopixel.NeoPixel(
    pixel_pinL, pixel_num, brightness=0.5, auto_write=False, pixel_order=(1, 0, 2, 3)
)
blPixel = neopixel.NeoPixel(
    pixel_pinBL, pixel_num, brightness=0.5, auto_write=False, pixel_order=(1, 0, 2, 3)
)
brPixel = neopixel.NeoPixel(
    pixel_pinBR, pixel_num, brightness=0.5, auto_write=False, pixel_order=(1, 0, 2, 3)
)


sped = 0.04

speeed=0.05
advance_on_cycle_complete=True


animations = AnimationSequence(

    AnimationGroup(
        Comet(pixels, speed=0.08, color=CYAN),
        Comet(pixil, speed=0.08, color=CYAN),
        Comet(brPixel, speed=0.08, color=CYAN),
        Comet(blPixel, speed=0.08, color=CYAN),
        # sync=True,
        Sparkle(pixels, speed=0.08, color=TEAL, num_sparkles=10),
        Sparkle(pixil, speed=0.08, color=TEAL, num_sparkles=10),
        Sparkle(brPixel, speed=0.08, color=TEAL, num_sparkles=10),
        Sparkle(blPixel, speed=0.08, color=TEAL, num_sparkles=10),
    ),

    #advance_interval=3.0,
    auto_clear=True,  # this provides an interesting effect  when false
    auto_reset=True,

)
animations.add_cycle_complete_receiver(animations)
animations.advance_on_cycle_complete=True
while True:

    animations.animate()


User avatar
neradoc
 
Posts: 542
Joined: Wed Apr 27, 2016 2:38 pm

Re: Possible Shadow Variables in REPL?

Post by neradoc »

add_cycle_complete_receiver takes a function as argument, to setup the callback.
You are passing it the animations variable with is an instance of AnimationSequence, hence the error: AnimationSequence is not a call-able object.

What are you actually trying to do ?
What exactly do you want to happen at the end of a cycle ?

User avatar
Draculen
 
Posts: 72
Joined: Mon Jan 24, 2022 11:14 pm

Re: Possible Shadow Variables in REPL?

Post by Draculen »

Interesting!

Essentially I was trying to add support to the sparkle animation so that I can use Comet and sparkle as an on_cycle_complete sequence. Each animation plays to the end of the LED arrays length and then moves on to the next animation in the animation group

I've been just super confused because I was referencing how other animations are using on cycle complete and could not figure out what I was missing =/

User avatar
neradoc
 
Posts: 542
Joined: Wed Apr 27, 2016 2:38 pm

Re: Possible Shadow Variables in REPL?

Post by neradoc »

I note in your code that there is only one animation in your sequence, so advance_on_cycle_complete does nothing. Animation groups are animations that play simultaneously, animation sequences are for animations that play one after the other.

You want something like that I think:

Code: Select all

group1 = AnimationGroup(
    Comet(pixels, speed=0.08, color=CYAN),
    Comet(pixil, speed=0.08, color=CYAN),
    Comet(brPixel, speed=0.08, color=CYAN),
    Comet(blPixel, speed=0.08, color=CYAN),
)

group2 = AnimationGroup(
    Sparkle(pixels, speed=0.08, color=TEAL, num_sparkles=10),
    Sparkle(pixil, speed=0.08, color=TEAL, num_sparkles=10),
    Sparkle(brPixel, speed=0.08, color=TEAL, num_sparkles=10),
    Sparkle(blPixel, speed=0.08, color=TEAL, num_sparkles=10),
)

animations = AnimationSequence(
    group1,
    group2,
    #advance_interval=3.0,
    auto_clear=True,
    auto_reset=True,

)
animations.advance_on_cycle_complete=True

while True:
    animations.animate()


User avatar
Draculen
 
Posts: 72
Joined: Mon Jan 24, 2022 11:14 pm

Re: Possible Shadow Variables in REPL?

Post by Draculen »

Normally there is alot more animation groups going on at the same time but I didn't want to make my post more convoluted then it needed to be just for an easier read. So just to clarify- the way the receiver is being called in code.py is conflicting with sparkle.pys callback? Sorry I'm way more used to c# and c++ so with python having no public and private variables it's been a wild ride

User avatar
Draculen
 
Posts: 72
Joined: Mon Jan 24, 2022 11:14 pm

Re: Possible Shadow Variables in REPL?

Post by Draculen »

If it makes it easier, this is the code in its entirety (I apologize in advance for how cluttered it looks, its 400 Pixels split into PixelSubsets)

Code: Select all

import board
import neopixel
import time
from adafruit_led_animation.animation.comet import Comet
from adafruit_led_animation.animation.chase import Chase
from adafruit_led_animation.sequence import AnimationSequence
from adafruit_led_animation.color import CYAN, TEAL
from adafruit_led_animation.group import AnimationGroup
from adafruit_led_animation import animation
# from adafruit_led_animation import color
from adafruit_led_animation.animation.sparkle import Sparkle

# from adafruit_led_animation import helper
from adafruit_led_animation.helper import PixelMap
from adafruit_led_animation.helper import PixelSubset

pixel_pin = board.A5
pixel_pinL = board.A2
pixel_pinBR = board.D10
pixel_pinBL = board.D12
pixel_num = 103
pixels = neopixel.NeoPixel(
    pixel_pin, pixel_num, brightness=0.5, auto_write=False, pixel_order=(1, 0, 2, 3)
)
pixil = neopixel.NeoPixel(
    pixel_pinL, pixel_num, brightness=0.5, auto_write=False, pixel_order=(1, 0, 2, 3)
)
blPixel = neopixel.NeoPixel(
    pixel_pinBL, pixel_num, brightness=0.5, auto_write=False, pixel_order=(1, 0, 2, 3)
)
brPixel = neopixel.NeoPixel(
    pixel_pinBR, pixel_num, brightness=0.5, auto_write=False, pixel_order=(1, 0, 2, 3)
)

#teal and cyan can be stored locally as a definition or just a locally declared variable(not adafruits library)

# leds_range0 = PixelMap(pixels, range(0, 16), individual_pixels=True)
# leds_range1 = PixelMap(pixil, range(0, 17), individual_pixels=True)


#backside neopixels Lower part
B_first_L = PixelSubset(blPixel, 0, 17)
B_first_R = PixelSubset(brPixel, 0, 17)
B_R_crest = PixelSubset(blPixel, 17, 27)
B_L_crest = PixelSubset(brPixel, 17, 27)
B_stem_R = PixelSubset(blPixel, 29, 50)
#crestL = PixelSubset(pixels, 17, 50)
B_stem_L = PixelSubset(brPixel, 27, 50)
#time.sleep(1000)
B_rem_R = PixelSubset(blPixel, 50, 103)
B_rem_L = PixelSubset(brPixel, 50, 103)

#front side neopixels higher part left and right
B_crestR2 = PixelSubset(blPixel, 17, 29)
B_stem_R2 = PixelSubset(brPixel, 29, 52)
B_crestL2 = PixelSubset(blPixel, 16, 29)
B_stem_L2 = PixelSubset(brPixel, 29, 50)
B_rem_R2 = PixelSubset(blPixel, 52, 103)
B_rem_L2 = PixelSubset(brPixel, 50, 103)

B_chaseStem_L = PixelSubset(blPixel, 0, 48)
B_chaseStem_L2 = PixelSubset(blPixel, 33, 42)
B_chaseStem_R = PixelSubset(brPixel, 0, 49)
B_chaseStem_R2 = PixelSubset(brPixel, 33, 41)
B_chaseStem_CL = PixelSubset(blPixel, 41, 48)
B_chaseStemCR = PixelSubset(brPixel, 42, 49)




#frontside neopixels Lower part
A_first_L = PixelSubset(pixels, 0, 16)
A_first_R = PixelSubset(pixil, 0, 16)
A_R_crest = PixelSubset(pixil, 16, 26)
A_L_crest = PixelSubset(pixels, 16, 26)
A_stem_R = PixelSubset(pixil, 27, 48)
#crestL = PixelSubset(pixels, 17, 50)
A_stem_L = PixelSubset(pixels, 27, 49)
A_rem_R = PixelSubset(pixil, 50, 103)
A_rem_L = PixelSubset(pixels, 50, 103)

A_crestR2 = PixelSubset(pixil, 17, 29)
A_stem_R2 = PixelSubset(pixil, 29, 52)
A_crestL2 = PixelSubset(pixels, 16, 29)
A_stem_L2 = PixelSubset(pixels, 29, 50)
A_rem_R2 = PixelSubset(pixil, 52, 103)
A_rem_L2 = PixelSubset(pixels, 50, 103)

chaseStem_L = PixelSubset(pixels, 0, 49)
chaseStem_R = PixelSubset(pixil, 0, 49)


teal = (0, 255, 120)
cyan = (0, 255, 255)
white = (255, 255, 255)

sped = 0.04

speeed=0.05
advance_on_cycle_complete=True


animations = AnimationSequence(
    AnimationGroup(

  #  Chase(A_first_L, speeed, size=4, spacing=8, color=TEAL),
#Chase(A_first_R, speeed, size=5, spacing=8, color=TEAL),
    Chase(chaseStem_L, speeed, size=4, spacing=8, color=TEAL),
Chase(chaseStem_R, speeed, size=5, spacing=8, color=TEAL),

        #Comet(A_first_L, sped, tail_length=8, color=TEAL),
       # Comet(A_first_R, sped, tail_length=8, color=TEAL),

Chase(B_chaseStem_L, speeed, size=4, spacing=8, color=TEAL),
Chase(B_chaseStem_R, speeed, size=5, spacing=8, color=TEAL),

    #Chase(A_rem_R,  speeed, size=4, spacing=8, color=TEAL), # this is new
        Chase(A_rem_L, speeed, size=4, spacing=8, color=TEAL),
Chase(A_rem_R, speeed, size=4, spacing=8, color=TEAL),
        Chase(B_rem_R,  speeed, size=4, spacing=8, color=TEAL),
        Chase(B_rem_L,  speeed, size=4, spacing=8, color=TEAL),

#advance_on_cycle_complete=True doesnt work
        # other side
        #Comet(B_first_L, sped, tail_length=8, color=TEAL),
        #Comet(B_first_R, sped, tail_length=8, color=TEAL),
        #convert all to be chase besides the top


    ),
    AnimationGroup(

        #Chase(first_L, 0.05, size=6, spacing=10, color=teal),
        #Chase(first_R, 0.05, size=6, spacing=10, color=teal),
        Comet(A_R_crest, sped, tail_length=5, color=TEAL),
        Comet(A_L_crest, sped, tail_length=5, color=TEAL),


        Comet(B_R_crest, sped, tail_length=5, color=TEAL),
        Comet(B_L_crest, sped, tail_length=5, color=TEAL),


    ),
   AnimationGroup(

        #Chase(first_L, 0.05, size=6, spacing=10, color=teal),
        #Chase(first_R, 0.05, size=6, spacing=10, color=teal),

        Comet(A_stem_L, sped, tail_length=5, color=TEAL),
        Comet(A_stem_R, sped, tail_length=5, color=TEAL),

        Comet(B_stem_L, sped, tail_length=5, color=TEAL),
        Comet(B_stem_R, sped, tail_length=5, color=TEAL),

    ),
    AnimationGroup(

        Comet(A_rem_R, sped, tail_length=5, color=TEAL), # this is new
        Comet(A_rem_L, sped, tail_length=5, color=TEAL),

        Comet(B_rem_R, sped, tail_length=15, color=TEAL),
        Comet(B_rem_L, sped, tail_length=15, color=TEAL),

    ),
    AnimationGroup(
        Comet(pixels, speed=0.08, color=CYAN),
        Comet(pixil, speed=0.08, color=CYAN),
        Comet(brPixel, speed=0.08, color=CYAN),
        Comet(blPixel, speed=0.08, color=CYAN),
        # sync=True,
        Sparkle(pixels, speed=0.08, color=TEAL, num_sparkles=10),
        Sparkle(pixil, speed=0.08, color=TEAL, num_sparkles=10),
        Sparkle(brPixel, speed=0.08, color=TEAL, num_sparkles=10),
        Sparkle(blPixel, speed=0.08, color=TEAL, num_sparkles=10),
    ),

    #advance_interval=3.0,
    auto_clear=True,  # this provides an interesting effect  when false
    auto_reset=True,

)
animations.add_cycle_complete_receiver(animations)
animations.advance_on_cycle_complete=True
while True:

    animations.animate()

I think I see the issue you were refering to, animations.add_cycle_complete_receiver is pointing to itself (animations) and I believe sequence.py adds its own receiver when on_cycle_complete is flagged as true. The code above worked perfectly fine for the Comet animation but as soon as I started restructuring sparkle.py; thats when I started receiving the call-able error (it was even calling that TypeError when the Sparkle animation was not being called within the AnimationGroup). Any insight at all on how to make this make sense is ineffably appreciated

User avatar
neradoc
 
Posts: 542
Joined: Wed Apr 27, 2016 2:38 pm

Re: Possible Shadow Variables in REPL?

Post by neradoc »

add_cycle_complete_receiver takes a function as argument, not an animation, it is a function that is called when the cycle is complete. That's its only purpose. If you don't have a callback to call, you don't use it.

https://docs.circuitpython.org/projects ... e_receiver

If all you want is to enable advance on cycle complete, the changes you made to the Sparkle class should already take care of it: setting on_cycle_complete_supported = True and setting self.cycle_complete at the end of a cycle. You also shouldn't need to implement on_cycle_complete(self) since it's inherited from Animation, so you can remove that too.

https://github.com/adafruit/Adafruit_Ci ... #L192-L201

User avatar
Draculen
 
Posts: 72
Joined: Mon Jan 24, 2022 11:14 pm

Re: Possible Shadow Variables in REPL?

Post by Draculen »

Just arranged the code as you said and cried tears of joy. It works perfectly!!!!

I sincerely can't thank you enough for your insight!

Here is the final product (the theater chase animation will be replaced with more comets now that cycle complete works
It's 400 neopixels (200 on each side of the blade) which will be covered with turquoise light diffusing foam.
https://drive.google.com/file/d/1IXxf3s ... p=drivesdk

Locked
Please be positive and constructive with your questions and comments.

Return to “Adafruit CircuitPython”