from PandaObject import * from DirectGeometry import * import Task class PieMenu(NodePath, PandaObject): def __init__(self, visibleMenu, menuItems, action = None, fUpdateOnlyOnChange = 1): NodePath.__init__(self) # Create a toplevel node for aspect ratio scaling self.assign(hidden.attachNewNode('PieMenu')) self.dr = direct.dr # Attach the menu self.visibleMenu = visibleMenu # Try to flatten the visibleMenu (note, flattenStrong is too strong # for texture text self.visibleMenu.flattenMedium() self.visibleMenu.reparentTo(self) # Initialize instance variables self.menuItems = menuItems self.numItems = len(self.menuItems) self.degreesPerItem = 360.0/self.numItems self.itemOffset = self.degreesPerItem / 2.0 self.sfx = self.visibleMenu.getSx() self.sfz = self.visibleMenu.getSz() # Record target and action self.action = action self.initialState = None # Marking lines self.lines = LineNodePath(self.visibleMenu) self.lines.setColor(VBase4(1)) self.lines.setThickness(1) # Set flags self.fUpdateOnlyOnChange = fUpdateOnlyOnChange def performAction(self, value): if self.action: self.action(value) def removePieMenuTask(self): taskMgr.remove('pieMenuTask') self.reparentTo(hidden) self.lines.reset() def spawnPieMenuTask(self): # Make sure no errant tasks lying around taskMgr.remove('pieMenuTask') # Where did the user press the button? self.originX = self.dr.mouseX self.originY = self.dr.mouseY # Pop up menu self.reparentTo(render2d) self.setPos(self.originX,0.0,self.originY) # Compensate for window aspect ratio self.setScale(1.0, 1.0,1.0) # Start drawing the selection line self.lines.reset() self.lines.moveTo(0,0,0) self.lines.drawTo(0,0,0) self.lines.create() # Spawn task to update line and select new texture self.currItem = -1 taskMgr.add(self.pieMenuTask, 'pieMenuTask') def pieMenuTask(self,state): mouseX = self.dr.mouseX mouseY = self.dr.mouseY deltaX = mouseX - self.originX deltaY = mouseY - self.originY # Update the line self.lines.setVertex(1,(deltaX/self.sfx),0.0,(deltaY/self.sfz)) # How far from starting point has user moved the cursor? if ((abs(deltaX) < 0.1) and (abs(deltaY) < 0.1)): # In the center if self.fUpdateOnlyOnChange: # Only do this when things change if (self.currItem != -1): self.performAction(self.initialState) else: # Alway let use know mouse is in the center self.performAction(self.initialState) self.currItem = -1 else: # Outside of the center # Interacting with menu # subtract half a slice to effectively center item menuAngle = rad2Deg(math.atan2(deltaY, deltaX)) + self.itemOffset if menuAngle < 0.0: menuAngle = menuAngle + 360.0 menuAngle = menuAngle % 360.0 newItem = int(math.floor(menuAngle / self.degreesPerItem)) if self.fUpdateOnlyOnChange: if (self.currItem != newItem): self.performAction(self.menuItems[newItem]) else: self.performAction(self.menuItems[newItem]) self.currItem = newItem # Continue task return Task.cont def setInitialState(self,state): self.initialState = state def getInitialState(self): return self.initialState def setItemOffset(self,newOffset): self.itemOffset = newOffset def setUpdateOnlyOnChange(self,flag): self.fUpdateOnlyOnChange = flag class TextPieMenu(PieMenu): def __init__(self, textList, radius = 0.5, sf = 1, action = None, fUpdateOnlyOnChange = 1): numItems = len(textList) # Create top level node for new menu newMenu = hidden.attachNewNode('TextMenu') # Compute angle per item angle = deg2Rad(360.0/numItems) prop = base.win.getProperties() if prop.hasSize(): width = prop.getXSize() height = prop.getYSize() else: width = 640 height = 480 aspectRatio = width/float(height) # Add items from DirectGuiGlobals import getDefaultFont for i in range (numItems): # Create text node for each item if (textList[i] != None): tn = TextNode('TextItem') tn.setFont(getDefaultFont()) tn.setTransform(Mat4.scaleMat(0.07, 0.07, 0.07 * aspectRatio)) tn.setTextColor(0, 0, 0, 1) tn.setCardColor(1, 1, 1, 1) tn.setCardAsMargin(0.1, 0.1, 0.1, 0.1) tn.setText(str(textList[i])) # Reposition it card = tn.getCardTransformed() center = (card[1] - card[0], card[3] - card[2]) node = newMenu.attachNewNode(tn) node.setScale(sf) node.setPos(radius * math.cos(i * angle) - center[0], 0.0, ((radius * aspectRatio * math.sin(i * angle)) - center[1])) # Create and return a pie menu PieMenu.__init__(self, newMenu, textList, action = action, fUpdateOnlyOnChange = fUpdateOnlyOnChange) self.accept('mouse3', self.spawnPieMenuTask) self.accept('mouse3-up', self.removePieMenuTask) def destroy(self): self.ignore('mouse3') self.ignore('mouse3-up') self.removeNode()