"""A DirectRadioButton is a type of button that, similar to a DirectCheckButton, has a separate indicator and can be toggled between two states. However, only one DirectRadioButton in a group can be enabled at a particular time.""" __all__ = ['DirectRadioButton'] from panda3d.core import * from . import DirectGuiGlobals as DGG from .DirectButton import * from .DirectLabel import * class DirectRadioButton(DirectButton): """ DirectRadioButton(parent) - Create a DirectGuiWidget which responds to mouse clicks by setting given value to given variable and execute a callback function (passing that state through) if defined """ def __init__(self, parent = None, **kw): # Inherits from DirectButton # A Direct Frame can have: # - A background texture (pass in path to image, or Texture Card) # - A midground geometry item (pass in geometry) # - A foreground text Node (pass in text string or Onscreen Text) # For a direct button: # Each button has 4 states (ready, press, rollover, disabled) # The same image/geom/text can be used for all four states or each # state can have a different text/geom/image # State transitions happen automatically based upon mouse interaction # Responds to click event and calls command if None self.colors = None optiondefs = ( ('indicatorValue', 0, self.setIndicatorValue), # variable is a list whose value will be set by this radio button ('variable', [], None), # value is the value to be set when this radio button is selected ('value', [], None), # others is a list of other radio buttons sharing same variable ('others', [], None), # boxBorder defines the space created around the check box ('boxBorder', 0, None), # boxPlacement maps left, above, right, below ('boxPlacement', 'left', None), # boxGeom defines geom to indicate current radio button is selected or not ('boxGeom', None, None), ('boxGeomColor', None, None), ('boxGeomScale', 1.0, None), ('boxImage', None, None), ('boxImageScale', 1.0, None), ('boxImageColor', VBase4(1, 1, 1, 1), None), ('boxRelief', None, None), ) # Merge keyword options with default options self.defineoptions(kw, optiondefs) # Initialize superclasses DirectButton.__init__(self, parent) self.indicator = self.createcomponent("indicator", (), None, DirectLabel, (self,), numStates = 2, image = self['boxImage'], image_scale = self['boxImageScale'], image_color = self['boxImageColor'], geom = self['boxGeom'], geom_scale = self['boxGeomScale'], geom_color = self['boxGeomColor'], state = 'disabled', text = ('X', 'X'), relief = self['boxRelief'], ) # Call option initialization functions self.initialiseoptions(DirectRadioButton) # After initialization with X giving it the correct size, put back space if self['boxGeom'] is None: if not 'boxRelief' in kw and self['boxImage'] is None: self.indicator['relief'] = DGG.SUNKEN self.indicator['text'] = (' ', '*') self.indicator['text_pos'] = (0, -.25) else: self.indicator['text'] = (' ', ' ') if self['boxGeomColor'] != None and self['boxGeom'] != None: self.colors = [VBase4(1, 1, 1, 0), self['boxGeomColor']] self.component('indicator')['geom_color'] = VBase4(1, 1, 1, 0) needToCheck = True if len(self['value']) == len(self['variable']) != 0: for i in range(len(self['value'])): if self['variable'][i] != self['value'][i]: needToCheck = False break if needToCheck: self.check() # Override the resetFrameSize of DirectGuiWidget inorder to provide space for label def resetFrameSize(self): self.setFrameSize(fClearFrame = 1) def setFrameSize(self, fClearFrame = 0): if self['frameSize']: # Use user specified bounds self.bounds = self['frameSize'] frameType = self.frameStyle[0].getType() ibw = self.indicator['borderWidth'] else: # Use ready state to compute bounds frameType = self.frameStyle[0].getType() if fClearFrame and (frameType != PGFrameStyle.TNone): self.frameStyle[0].setType(PGFrameStyle.TNone) self.guiItem.setFrameStyle(0, self.frameStyle[0]) # To force an update of the button self.guiItem.getStateDef(0) # Clear out frame before computing bounds self.getBounds() # Restore frame style if necessary if (frameType != PGFrameStyle.TNone): self.frameStyle[0].setType(frameType) self.guiItem.setFrameStyle(0, self.frameStyle[0]) # Ok, they didn't set specific bounds, # let's add room for the label indicator # get the difference in height ibw = self.indicator['borderWidth'] indicatorWidth = (self.indicator.getWidth() + (2*ibw[0])) indicatorHeight = (self.indicator.getHeight() + (2*ibw[1])) diff = (indicatorHeight + (2*self['boxBorder']) - (self.bounds[3] - self.bounds[2])) # If background is smaller then indicator, enlarge background if diff > 0: if self['boxPlacement'] == 'left': #left self.bounds[0] += -(indicatorWidth + (2*self['boxBorder'])) self.bounds[3] += diff/2 self.bounds[2] -= diff/2 elif self['boxPlacement'] == 'below': #below self.bounds[2] += -(indicatorHeight+(2*self['boxBorder'])) elif self['boxPlacement'] == 'right': #right self.bounds[1] += indicatorWidth + (2*self['boxBorder']) self.bounds[3] += diff/2 self.bounds[2] -= diff/2 else: #above self.bounds[3] += indicatorHeight + (2*self['boxBorder']) # Else make space on correct side for indicator else: if self['boxPlacement'] == 'left': #left self.bounds[0] += -(indicatorWidth + (2*self['boxBorder'])) elif self['boxPlacement'] == 'below': #below self.bounds[2] += -(indicatorHeight + (2*self['boxBorder'])) elif self['boxPlacement'] == 'right': #right self.bounds[1] += indicatorWidth + (2*self['boxBorder']) else: #above self.bounds[3] += indicatorHeight + (2*self['boxBorder']) # Set frame to new dimensions if ((frameType != PGFrameStyle.TNone) and (frameType != PGFrameStyle.TFlat)): bw = self['borderWidth'] else: bw = (0, 0) # Set frame to new dimensions self.guiItem.setFrame( self.bounds[0] - bw[0], self.bounds[1] + bw[0], self.bounds[2] - bw[1], self.bounds[3] + bw[1]) # If they didn't specify a position, put it in the center of new area if not self.indicator['pos']: bbounds = self.bounds lbounds = self.indicator.bounds newpos = [0, 0, 0] if self['boxPlacement'] == 'left': #left newpos[0] += bbounds[0]-lbounds[0] + self['boxBorder'] + ibw[0] dropValue = (bbounds[3]-bbounds[2]-lbounds[3]+lbounds[2])/2 + self['boxBorder'] newpos[2] += (bbounds[3]-lbounds[3] + self['boxBorder'] - dropValue) elif self['boxPlacement'] == 'right': #right newpos[0] += bbounds[1]-lbounds[1] - self['boxBorder'] - ibw[0] dropValue = (bbounds[3]-bbounds[2]-lbounds[3]+lbounds[2])/2 + self['boxBorder'] newpos[2] += (bbounds[3]-lbounds[3] + self['boxBorder'] - dropValue) elif self['boxPlacement'] == 'above': #above newpos[2] += bbounds[3]-lbounds[3] - self['boxBorder'] - ibw[1] else: #below newpos[2] += bbounds[2]-lbounds[2] + self['boxBorder'] + ibw[1] self.indicator.setPos(newpos[0], newpos[1], newpos[2]) def commandFunc(self, event): if len(self['value']) == len(self['variable']) != 0: for i in range(len(self['value'])): self['variable'][i] = self['value'][i] self.check() def check(self): self['indicatorValue'] = 1 self.setIndicatorValue() for other in self['others']: if other != self: other.uncheck() if self['command']: # Pass any extra args to command self['command'](*self['extraArgs']) def setOthers(self, others): self['others'] = others def uncheck(self): self['indicatorValue'] = 0 if self.colors != None: self.component('indicator')['geom_color'] = self.colors[self['indicatorValue']] def setIndicatorValue(self): self.component('indicator').guiItem.setState(self['indicatorValue']) if self.colors != None: self.component('indicator')['geom_color'] = self.colors[self['indicatorValue']]