Having trouble accessing object properties

I have made a custom ToggleButton class by subclassing the DirectFrame class and adding a toggle function. And then I have a buttons array which includes all the buttons.

class DirectToggle(DirectFrame):
    def __init__(self, parent=None, **kw):
        optiondefs = (
            ('numStates', 2, None),
            ('state', DGG.NORMAL, None),
            ('relief', DGG.RAISED, None),
            ('invertedFrames', (1,), None),
            ('pressEffect', 1, DGG.INITOPT),
        self.defineoptions(kw, optiondefs)

        DirectFrame.__init__(self, parent)

        pressEffectNP = None
        if self['pressEffect']:
            pressEffectNP = self.stateNodePath[1].attachNewNode('pressEffect', 1)
            self.stateNodePath[1] = pressEffectNP


        if pressEffectNP:
            bounds = self.getBounds()
            centerX = (bounds[0] + bounds[1]) / 2
            centerY = (bounds[2] + bounds[3]) / 2

            mat = Mat4.translateMat(-centerX, 0, -centerY) * \
                  Mat4.scaleMat(0.98) * \
                  Mat4.translateMat(centerX, 0, centerY)

        self.toggle = 'up'
        def foggle(*kw):
            global buttons
            for x in self.getParent().getChildren():
            print(self['relief'])                  #works fine
            print(buttons[0]['relief'])       #returns a 'TypeError: 'panda3d.core.NodePath' object is not subscriptable' error
            if self.toggle == 'up':
                self['relief'] = 3
                self.toggle = 'down'
                self['relief'] = 2
                self.toggle = 'up'

        self.bind(DGG.B1PRESS, foggle)

When I try access the buttons ‘relief’ state through self, it works fine, but when I try to access it through the buttons array, I get an error. They both point to the same object yet the latter doesn’t work. From my understanding, it is because I’m not pointing to the direct object. How can I fix this?

In short, GUI items aren’t stored in the node-hierarchy as DirectGUI objects. Those aren’t even available in their nodes (which will be PGItems), I believe.

What I suggest is that, rather than searching for them when a button is pressed, you store your buttons when you create them as DirectToggles. At that point you should still have access to them in their DirectGUI form, and should thus be able to keep references to those.

Quite how you do this might depend on why you want to access the other buttons. If you want them to toggle each other, like radio buttons, then you might look at how DirectRadioButton does it–in short, it provides a method that allows the developer to list the “other” buttons for a given radio button.

I’ve stored the buttons as soon as DirectToggle is initialised as you suggested and it works! Once again, thank you for sharing your knowledge :slight_smile:
And yes I do want to make them radio buttons, DirectRadioButton does something similar to what I intend to do just in a much cleaner way.

It’s my pleasure, and I’m glad if I’ve been of service. :slight_smile:

If I may ask, why not use DirectRadioButton? I think that you can pass it a list of objects that represent the “pressed” and “unpressed” states (I think that you might even be able to pass it small DirectFrames for this purpose).

I guess I could’ve done it that way and it might have been cleaner, it’s working as it is right now so that’s sufficient.

I’ve found that self.toggle is completely useless :rofl:. You can use self[‘relief’] to check if it is pressed or not instead.

Fair enough! If what you have now serves your purposes and works well, then you may as well use it, I do feel!

Yup! You could perhaps add a convenience method to your class that makes for slightly more-readable code, that checks the “relief” keyword in the background.

Do you mean binding the ‘relief’ property?

Just a method that accesses the “relief” property–as you have been doing. Something like this:

class DirectToggle(DirectFrame):
    # __init__, other methods, etc. ...

    def isPressed(self):
        # If the relief is "sunken", then it's pressed; 
        #  otherwise, it's not.
        return self["relief"] == DGG.SUNKEN

self.myToggle = DirectToggle(<parameters here>)

# Elsewhere again...
if self.myToggle.isPressed():
    # Do stuff if the toggle is pressed...

It’s a little neater, a little more convenient (I feel), and perhaps a little less error-prone than repeating “if self[‘relief’] == DGG.SUNKEN” in various places in your code, I feel.

Hmm I guess that is another way I could do it, if I what I have right now doesn’t work out I’ll make sure to implement your concept :slight_smile:

This is also a possibility:

    def pressed(self):
        return self["relief"] == DGG.SUNKEN

Then to check if it is pressed:

if self.myToggle.pressed:
    # Do stuff

It arguably looks a little cleaner.

That’s fair–I’ll admit that I wasn’t aware of the “property” tag! :slight_smile: