DirectCheckButton.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. """A DirectCheckButton is a type of button that toggles between two states
  2. when clicked. It also has a separate indicator that can be modified
  3. separately.
  4. See the :ref:`directcheckbutton` page in the programming manual for a more
  5. in-depth explanation and an example of how to use this class.
  6. """
  7. __all__ = ['DirectCheckButton']
  8. from panda3d.core import PGFrameStyle, VBase4
  9. from .DirectButton import DirectButton
  10. from .DirectLabel import DirectLabel
  11. class DirectCheckButton(DirectButton):
  12. """
  13. DirectCheckButton(parent) - Create a DirectGuiWidget which responds
  14. to mouse clicks by setting a state of on or off and execute a callback
  15. function (passing that state through) if defined
  16. """
  17. def __init__(self, parent = None, **kw):
  18. # Inherits from DirectButton
  19. # A Direct Frame can have:
  20. # - A background texture (pass in path to image, or Texture Card)
  21. # - A midground geometry item (pass in geometry)
  22. # - A foreground text Node (pass in text string or Onscreen Text)
  23. # For a direct button:
  24. # Each button has 4 states (ready, press, rollover, disabled)
  25. # The same image/geom/text can be used for all four states or each
  26. # state can have a different text/geom/image
  27. # State transitions happen automatically based upon mouse interaction
  28. # Responds to click event and calls command if None
  29. self.colors = None
  30. optiondefs = (
  31. ('indicatorValue', 0, self.setIndicatorValue),
  32. # boxBorder defines the space created around the check box
  33. ('boxBorder', 0, None),
  34. # boxPlacement maps left, above, right, below
  35. ('boxPlacement', 'left', None),
  36. ('boxImage', None, None),
  37. ('boxImageScale', 1, None),
  38. ('boxImageColor', None, None),
  39. ('boxRelief', 'sunken', None),
  40. )
  41. # Merge keyword options with default options
  42. self.defineoptions(kw, optiondefs)
  43. # Initialize superclasses
  44. DirectButton.__init__(self, parent)
  45. self.indicator = self.createcomponent("indicator", (), None,
  46. DirectLabel, (self,),
  47. numStates = 2,
  48. image = self['boxImage'],
  49. image_scale = self['boxImageScale'],
  50. image_color = self['boxImageColor'],
  51. state = 'disabled',
  52. text = ('X', 'X'),
  53. relief = self['boxRelief'],
  54. )
  55. # Call option initialization functions
  56. self.initialiseoptions(DirectCheckButton)
  57. # After initialization with X giving it the correct size, put back space
  58. if self['boxImage'] is None:
  59. self.indicator['text'] = (' ', '*')
  60. self.indicator['text_pos'] = (0, -.2)
  61. else:
  62. self.indicator['text'] = (' ', ' ')
  63. if self['boxImageColor'] is not None and self['boxImage'] is not None:
  64. self.colors = [VBase4(0, 0, 0, 0), self['boxImageColor']]
  65. self.component('indicator')['image_color'] = VBase4(0, 0, 0, 0)
  66. # Override the resetFrameSize of DirectGuiWidget inorder to provide space for label
  67. def resetFrameSize(self):
  68. self.setFrameSize(fClearFrame = 1)
  69. def setFrameSize(self, fClearFrame = 0):
  70. if self['frameSize']:
  71. # Use user specified bounds
  72. self.bounds = self['frameSize']
  73. frameType = self.frameStyle[0].getType()
  74. ibw = self.indicator['borderWidth']
  75. else:
  76. # Use ready state to compute bounds
  77. frameType = self.frameStyle[0].getType()
  78. if fClearFrame and frameType != PGFrameStyle.TNone:
  79. self.frameStyle[0].setType(PGFrameStyle.TNone)
  80. self.guiItem.setFrameStyle(0, self.frameStyle[0])
  81. # To force an update of the button
  82. self.guiItem.getStateDef(0)
  83. # Clear out frame before computing bounds
  84. self.getBounds()
  85. # Restore frame style if necessary
  86. if frameType != PGFrameStyle.TNone:
  87. self.frameStyle[0].setType(frameType)
  88. self.guiItem.setFrameStyle(0, self.frameStyle[0])
  89. # Ok, they didn't set specific bounds,
  90. # let's add room for the label indicator
  91. # get the difference in height
  92. ibw = self.indicator['borderWidth']
  93. indicatorWidth = (self.indicator.getWidth() + (2*ibw[0]))
  94. indicatorHeight = (self.indicator.getHeight() + (2*ibw[1]))
  95. diff = (indicatorHeight + (2*self['boxBorder']) -
  96. (self.bounds[3] - self.bounds[2]))
  97. # If background is smaller then indicator, enlarge background
  98. if diff > 0:
  99. if self['boxPlacement'] == 'left': #left
  100. self.bounds[0] += -(indicatorWidth + (2*self['boxBorder']))
  101. self.bounds[3] += diff/2
  102. self.bounds[2] -= diff/2
  103. elif self['boxPlacement'] == 'below': #below
  104. self.bounds[2] += -(indicatorHeight+(2*self['boxBorder']))
  105. elif self['boxPlacement'] == 'right': #right
  106. self.bounds[1] += indicatorWidth + (2*self['boxBorder'])
  107. self.bounds[3] += diff/2
  108. self.bounds[2] -= diff/2
  109. else: #above
  110. self.bounds[3] += indicatorHeight + (2*self['boxBorder'])
  111. # Else make space on correct side for indicator
  112. else:
  113. if self['boxPlacement'] == 'left': #left
  114. self.bounds[0] += -(indicatorWidth + (2*self['boxBorder']))
  115. elif self['boxPlacement'] == 'below': #below
  116. self.bounds[2] += -(indicatorHeight + (2*self['boxBorder']))
  117. elif self['boxPlacement'] == 'right': #right
  118. self.bounds[1] += indicatorWidth + (2*self['boxBorder'])
  119. else: #above
  120. self.bounds[3] += indicatorHeight + (2*self['boxBorder'])
  121. # Set frame to new dimensions
  122. if ((frameType != PGFrameStyle.TNone) and
  123. (frameType != PGFrameStyle.TFlat)):
  124. bw = self['borderWidth']
  125. else:
  126. bw = (0, 0)
  127. # Set frame to new dimensions
  128. self.guiItem.setFrame(
  129. self.bounds[0] - bw[0],
  130. self.bounds[1] + bw[0],
  131. self.bounds[2] - bw[1],
  132. self.bounds[3] + bw[1])
  133. # If they didn't specify a position, put it in the center of new area
  134. if not self.indicator['pos']:
  135. bbounds = self.bounds
  136. lbounds = self.indicator.bounds
  137. newpos = [0, 0, 0]
  138. if self['boxPlacement'] == 'left': #left
  139. newpos[0] += bbounds[0]-lbounds[0] + self['boxBorder'] + ibw[0]
  140. dropValue = (bbounds[3]-bbounds[2]-lbounds[3]+lbounds[2])/2 + self['boxBorder']
  141. newpos[2] += (bbounds[3]-lbounds[3] + self['boxBorder'] -
  142. dropValue)
  143. elif self['boxPlacement'] == 'right': #right
  144. newpos[0] += bbounds[1]-lbounds[1] - self['boxBorder'] - ibw[0]
  145. dropValue = (bbounds[3]-bbounds[2]-lbounds[3]+lbounds[2])/2 + self['boxBorder']
  146. newpos[2] += (bbounds[3]-lbounds[3] + self['boxBorder']
  147. - dropValue)
  148. elif self['boxPlacement'] == 'above': #above
  149. newpos[2] += bbounds[3]-lbounds[3] - self['boxBorder'] - ibw[1]
  150. else: #below
  151. newpos[2] += bbounds[2]-lbounds[2] + self['boxBorder'] + ibw[1]
  152. self.indicator.setPos(newpos[0], newpos[1], newpos[2])
  153. def commandFunc(self, event):
  154. self['indicatorValue'] = 1 - self['indicatorValue']
  155. if self.colors is not None:
  156. self.component('indicator')['image_color'] = self.colors[self['indicatorValue']]
  157. if self['command']:
  158. # Pass any extra args to command
  159. self['command'](*[self['indicatorValue']] + self['extraArgs'])
  160. def setIndicatorValue(self):
  161. self.component('indicator').guiItem.setState(self['indicatorValue'])
  162. if self.colors is not None:
  163. self.component('indicator')['image_color'] = self.colors[self['indicatorValue']]