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
self.initialiseoptions(DirectToggle)
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)
pressEffectNP.setMat(mat)
self.toggle = 'up'
def foggle(*kw):
global buttons
for x in self.getParent().getChildren():
buttons.append(x)
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'
else:
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
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.
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).
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.
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
#Elsewhere...
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.