class InventorySlotButton(): def __init__(self, slotIndexX, slotIndexY, xCoord, yCoord, padding, x, y, inventorySlot, parentInventory, onClickFunction): self.xIndex = slotIndexX # The x index of the button in the inventory self.yIndex = slotIndexY # The y index of the button in the inventory self.button = None # The DirectButton self.inventorySlot = inventorySlot # The slot this button represents self.parentInventory = parentInventory # The region this slot belongs to self.LoadContent(x, y, xCoord, yCoord, padding, onClickFunction) def LoadContent(self, x, y, xCoord, yCoord, padding, onClickFunction): egg = 'inventorySlot' up = ''.join([egg, '-up']) over = ''.join([egg, '-over']) maps = loader.loadModel("Assets/Images/Inventory/%s" % ('inventorySlot')) self.button = DirectButton(geom = (maps.find('**/%s' % (up)), maps.find('**/%s' % (over)), maps.find('**/%s' % (over)), maps.find('**/%s' % (up))), command = onClickFunction, extraArgs = [self], pressEffect = 0, relief = None, rolloverSound = None, clickSound = None, scale = (128.0/1024.0, 1, 128.0/1024.0)) pos = Globals.ConvertFromImageAbsoluteToAspect2D(xCoord + x * padding, yCoord + y * padding, 1024) self.button.setPos(pos[0], 0, pos[1]) def ReparentTo(self, node): self.button.reparentTo(node) def GetPos(self): return self.button.getPos() def GetParentInventory(self): return self.parentInventory def GetInventorySlot(self): return self.inventorySlot def GetSlotIndices(self): return [self.xIndex, self.yIndex]
class PlacerTool3D(DirectFrame): ORIGINAL_SCALE = (1.0, 1.0, 1.0) MINIMIZED_SCALE = (0.85, 1.0, 0.15) ORIG_DRAG_BUTTON_POS = (0.37, 0.0, 0.37) MINI_DRAG_BUTTON_POS = (0.37, 0.0, 0.03) ORIG_MINI_BUTTON_POS = (0.29, 0.0, 0.37) MINI_MINI_BUTTON_POS = (0.29, 0.0, 0.03) ORIG_NAME_POS = (-0.39, 0.0, 0.27) MINI_NAME_POS = (-0.39, 0.0, 0.0) def __init__(self, target, increment=0.01, hprIncrement=1.0, parent=aspect2d, pos=(0.0, 0.0, 0.0)): DirectFrame.__init__(self, parent) self.target = target self.increment = increment self.minimized = False self.mainFrame = DirectFrame( parent=self, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=(1, 1, 0.75, 1), geom_scale=self.ORIGINAL_SCALE, pos=pos, ) # Arrow gui (preload) gui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_mainGui.bam') # Set Bins self.mainFrame.setBin('gui-popup', 0) # Name name = self.target.getName() self.nameLabel = TTLabel(self.mainFrame, text='Target: %s' % name, pos=self.ORIG_NAME_POS, text_align=TextNode.ALeft, text_wordwrap=13) # Pos pos = self.target.getPos() self.posLabel = TTLabel(self.mainFrame, text='Position: ', pos=(-0.39, 0.0, 0.055), text_align=TextNode.ALeft) self.xPosSpinner = PlacerToolSpinner(self.mainFrame, value=pos[0], pos=(-0.085, 0.0, 0.06), increment=increment, callback=self.handleXChange) self.yPosSpinner = PlacerToolSpinner(self.mainFrame, value=pos[1], pos=(0.1, 0.0, 0.06), increment=increment, callback=self.handleYChange) self.zPosSpinner = PlacerToolSpinner(self.mainFrame, value=pos[2], pos=(0.28, 0.0, 0.06), increment=increment, callback=self.handleZChange) # hpr hpr = self.target.getHpr() self.hprLabel = TTLabel(self.mainFrame, text='HPR: ', pos=(-0.39, 0.0, -0.19), text_align=TextNode.ALeft) self.hSpinner = PlacerToolSpinner(self.mainFrame, value=hpr[0], pos=(-0.085, 0.0, -0.195), increment=hprIncrement, callback=self.handleHChange) self.pSpinner = PlacerToolSpinner(self.mainFrame, value=hpr[1], pos=(0.1, 0.0, -0.195), increment=hprIncrement, callback=self.handlePChange) self.rSpinner = PlacerToolSpinner(self.mainFrame, value=hpr[2], pos=(0.28, 0.0, -0.195), increment=hprIncrement, callback=self.handleRChange) # scale scale = [round(s, 3) for s in self.target.getScale()] self.scaleLabel = TTLabel(self.mainFrame, text='Scale: ', pos=(-0.39, 0.0, -0.4), text_align=TextNode.ALeft) self.sxSpinner = PlacerToolSpinner(self.mainFrame, value=hpr[0], pos=(-0.085, 0.0, -0.4), increment=increment, callback=self.handleSxChange) self.sySpinner = PlacerToolSpinner(self.mainFrame, value=hpr[1], pos=(0.1, 0.0, -0.4), increment=increment, callback=self.handleSyChange) self.szSpinner = PlacerToolSpinner(self.mainFrame, value=hpr[2], pos=(0.28, 0.0, -0.4), increment=increment, callback=self.handleSzChange) gui.removeNode() gui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_nameShop') thumb = gui.find('**/tt_t_gui_mat_namePanelCircle') self.dragButton = DirectButton(self.mainFrame, relief=None, image=thumb, image_scale=(0.5, 0.5, 0.5), pos=self.ORIG_DRAG_BUTTON_POS) self.minimizeButton = DirectButton(self.mainFrame, relief=None, image=thumb, image_scale=(0.5, 0.5, 0.5), image_color=(0.0, 0.0, 0.65, 1.0), pos=self.ORIG_MINI_BUTTON_POS, command=self.toggleMinimize, extraArgs=[]) self.dragButton.bind(DGG.B1PRESS, self.onPress) if target is not None: self.setTarget(target) def destroy(self): self.target = None messenger.send('placer-destroyed', [self]) DirectFrame.destroy(self) def setTarget(self, target): self.target = target name = self.target.getName() scale = [round(s, 3) for s in self.target.getScale()] x, y, z = self.target.getPos() h, p, r = self.target.getHpr() sx, sy, sz = self.target.getScale() self.nameLabel['text'] = 'Target: %s' % name self.xPosSpinner.setValue(x) self.yPosSpinner.setValue(y) self.zPosSpinner.setValue(z) self.hSpinner.setValue(h) self.pSpinner.setValue(p) self.rSpinner.setValue(r) self.sxSpinner.setValue(sx) self.sySpinner.setValue(sy) self.szSpinner.setValue(sz) def handleXChange(self, value): self.changeTargetPos(0, value) def handleYChange(self, value): self.changeTargetPos(1, value) def handleZChange(self, value): self.changeTargetPos(2, value) def handleHChange(self, value): self.changeTargetHpr(0, value) def handlePChange(self, value): self.changeTargetHpr(1, value) def handleRChange(self, value): self.changeTargetHpr(2, value) def handleSxChange(self, value): self.changeTargetScale(0, value) def handleSyChange(self, value): self.changeTargetScale(1, value) def handleSzChange(self, value): self.changeTargetScale(2, value) def changeTargetPos(self, index, value): pos = self.target.getPos() pos[index] = float(value) self.target.setPos(pos) def changeTargetHpr(self, index, value): hpr = self.target.getHpr() hpr[index] = float(value) self.target.setHpr(hpr) def changeTargetScale(self, index, value): pos = self.target.getScale() pos[index] = float(value) self.target.setScale(pos) def toggleMinimize(self): if self.minimized: self.maximize() else: self.minimize() def minimize(self): self.minimized = True self.mainFrame['geom_scale'] = self.MINIMIZED_SCALE self.nameLabel.setPos(self.MINI_NAME_POS) self.dragButton.setPos(self.MINI_DRAG_BUTTON_POS) self.minimizeButton.setPos(self.MINI_MINI_BUTTON_POS) self.posLabel.hide() self.xPosSpinner.hide() self.yPosSpinner.hide() self.zPosSpinner.hide() self.hprLabel.hide() self.hSpinner.hide() self.pSpinner.hide() self.rSpinner.hide() self.scaleLabel.hide() self.setPos(0, 0, 0) def maximize(self): self.minimized = False self.mainFrame['geom_scale'] = self.ORIGINAL_SCALE self.nameLabel.setPos(self.ORIG_NAME_POS) self.dragButton.setPos(self.ORIG_DRAG_BUTTON_POS) self.minimizeButton.setPos(self.ORIG_MINI_BUTTON_POS) self.posLabel.show() self.xPosSpinner.show() self.yPosSpinner.show() self.zPosSpinner.show() self.hprLabel.show() self.hSpinner.show() self.pSpinner.show() self.rSpinner.show() self.scaleLabel.show() self.setPos(0, 0, 0) def onPress(self, e=None): self.accept('mouse1-up', self.onRelease) taskMgr.add(self.mouseMoverTask, '%s-mouseMoverTask' % self.id) def onRelease(self, e=None): self.ignore('mouse1-up') taskMgr.remove('%s-mouseMoverTask' % self.id) def mouseMoverTask(self, task): if base.mouseWatcherNode.hasMouse(): mpos = base.mouseWatcherNode.getMouse() buttonPos = self.dragButton.getPos() newPos = (mpos[0] - buttonPos[0] / 2 - 0.02, 0, mpos[1] - buttonPos[2]) self.setPos(render2d, newPos) return task.cont
class Mediator(Receiver,FocusObserver,NodePathWrapper): """The singleton mediatorobject mediates the interaction between the StoryMap objects, receiving notifications and calling methods on StoryMap objects. """ def __init__(self): # Create two story maps, 'Story Cards' which the user picks story cards # from, and 'My Story Map' in which the user constructs her story. self.storyCards = StoryMap(storyCardClass=FocusableChoosableStoryCard, title="Story Cards") self.storyCards.reparentTo(zcanvas.home) self.storyCards.setScale(0.02) self.storyCards.setPos(-.5,0,.8) self.storyCards.fill() self.storyCards.added_behaviour = 'disable' self.myStoryMap = StoryMap(storyCardClass=FocusableEditableStoryCard, title="My Story") self.myStoryMap.reparentTo(zcanvas.home) self.myStoryMap.setScale(0.02) self.myStoryMap.setPos(-.5,0,-.1) self.myStoryMap.added_behaviour = 'remove' #self.myStoryMap.keep_sorted = True #self.myStoryMap.np.showTightBounds() self.myStoryMap.auto_grow = True # Keyboard controls for saving, loading and exporting. #base.accept("f1",self.save) #base.accept("f2",self.load) #base.accept("f3",self.export) # Subscribe to some messages. self.acceptOnce('zoom done',zcanvas.message,["Right-click to zoom back out again."]) self.accept('add',self.add) self.accept('remove',self.remove) # Frame along the bottom for Save, Load and Quit buttons. self.bottom_np = aspect2d.attachNewNode('bottom frame') height = 0.15 self.bottom_np.setPos(-base.getAspectRatio(),0,-1-height) cm = CardMaker('bottom frame') cm.setFrame(0,2*base.getAspectRatio(),0,height) self.bottom_np.attachNewNode(cm.generate()) self.bottom_np.setTransparency(TransparencyAttrib.MAlpha) self.bottom_np.setColor(.1,.1,.1,.7) self.bottom_hbox = HBoxList(margin=1) self.bottom_hbox.reparentTo(self.bottom_np) self.bottom_hbox.setPos(0,0,height-0.03) self.bottom_hbox.setScale(.1) self.save_button = DirectButton(text="Save",command=self.save) b = Box() b.fill(self.save_button) self.bottom_hbox.append(b) self.load_button = DirectButton(text="Load",command=self.load) b = Box() b.fill(self.load_button) self.bottom_hbox.append(b) # Interval that slides the frame onto the screen. self.bottom_interval = LerpPosInterval( self.bottom_np, duration=1, pos=Point3(-base.getAspectRatio(),0,-1), startPos=Point3(-base.getAspectRatio(),0,-1-height), other=None, blendType='easeInOut', bakeInStart=1, fluid=0, name=None) self.bottom_reverse_interval = LerpPosInterval( self.bottom_np, duration=1, pos=Point3(-base.getAspectRatio(),0,-1-height), startPos=Point3(-base.getAspectRatio(),0,-1), other=None, blendType='easeInOut', bakeInStart=1, fluid=0, name=None) self.bottom_frame_is_active = False # Frame along the right for story cards. self.right_np = aspect2d.attachNewNode('right frame') width = 0.14*base.getAspectRatio() self.right_np.setPos(base.getAspectRatio()+width,0,1) cm = CardMaker('right frame') cm.setFrame(-width,0,-2,0) self.right_np.attachNewNode(cm.generate()) self.right_np.setTransparency(TransparencyAttrib.MAlpha) self.right_np.setColor(.1,.1,.1,.7) self.right_vbox = Stack() self.right_vbox.reparentTo(self.right_np) self.right_vbox.setPos(-width+0.035,0,-0.06) self.right_vbox.setScale(.02) # Interval that slides the frame onto the screen. self.right_interval = LerpPosInterval( self.right_np, duration=1, pos=Point3(base.getAspectRatio(),0,1), startPos=Point3(base.getAspectRatio()+width,0,1), other=None, blendType='easeInOut', bakeInStart=1, fluid=0, name=None) self.right_reverse_interval = LerpPosInterval( self.right_np, duration=1, pos=Point3(base.getAspectRatio()+width,0,1), startPos=Point3(base.getAspectRatio(),0,1), other=None, blendType='easeInOut', bakeInStart=1, fluid=0, name=None) self.right_frame_is_active = False # Task that watches for the mouse going to the screen edges and slides # the frames onscreen when it does. self.prev_x = None self.prev_y = None taskMgr.add(self.task,'Mediator mouse watcher task') NodePathWrapper.__init__(self) FocusObserver.__init__(self) def enterNone(self): """Viewport focus has changed to None.""" # Make the title of 'My Story Map' editable. self.myStoryMap.title['state'] = DGG.NORMAL def exitNone(self): """Undo any changes made by enterNone.""" self.myStoryMap.title['state'] = DGG.DISABLED def task(self,task): if base.mouseWatcherNode.hasMouse(): x=base.mouseWatcherNode.getMouseX() y=base.mouseWatcherNode.getMouseY() if y <= -0.87 and self.prev_y > -0.87: # The mouse has just moved into the bottom frame's area. self.activate_bottom_frame() elif y > -0.87 and self.prev_y <= -0.87: # The mouse has just moved out of the bottom frame's area. self.deactivate_bottom_frame() self.prev_y = y if x >= 0.8 and self.prev_x < 0.8: # The mouse has just moved into the right frame's area. self.activate_right_frame() elif x < 0.8 and self.prev_x >= 0.8: # The mouse has just moved out of the right frame's area. self.deactivate_right_frame() self.prev_x = x return task.cont def activate_bottom_frame(self): if not self.bottom_frame_is_active: self.bottom_interval.start() self.bottom_frame_is_active = True def deactivate_bottom_frame(self): if self.bottom_frame_is_active: self.bottom_reverse_interval.start() self.bottom_frame_is_active = False def activate_right_frame(self): if not self.right_frame_is_active: self.right_interval.start() self.right_frame_is_active = True def deactivate_right_frame(self): if self.right_frame_is_active: self.right_reverse_interval.start() self.right_frame_is_active = False def add(self,card): # The Add button was pressed on one of the StoryCards in self.storyCards for box in self.right_vbox: if box.contents is None: box.fill(card) # self.activate_right_frame() return zcanvas.message('The stack is full!\nDrag another card from the stack first.') def remove(self,editableCard): # The Remove button was pressed on one of the StoryCards in # self.myStoryMap editableCard.getPythonTag('box').empty() for choosableCard in self.storyCards.items(): if choosableCard.function == editableCard.function: choosableCard.enable() return # Implement the Originator interface of the memento design pattern. (For # saving and loading.) class Memento: """A passive class that stores the state of a Mediator object.""" def __init__(self, storycards, mystorymap, stack): # MediatorMemento just holds mementos for mediator's two StoryMap # objects. self.storycards = storycards self.mystorymap = mystorymap self.stack = stack def __str__(self): return self.mystorymap.__str__() def create_memento(self): """Return a memento object holding the current internal state of this object.""" return Mediator.Memento(self.storyCards.create_memento(), self.myStoryMap.create_memento(), self.right_vbox.create_memento()) def restore_memento(self,memento): """Restore the internal state of this object to that held by the given memento.""" self.storyCards.restore_memento(memento.storycards) self.myStoryMap.restore_memento(memento.mystorymap) self.right_vbox.restore_memento(memento.stack) # Implement the Caretaker interface of the memento design pattern. (For # saving and loading.) def save(self): """Save the current state of the application to file.""" memento = self.create_memento() import datetime f = open(str(datetime.datetime.now()).replace(' ','_')+'.saved_story','w') cPickle.dump(memento,f) f.close() zcanvas.message("Saved!") def _load(self,args): self.load_list.removeNode() f = open(args,'r') memento = cPickle.load(f) f.close() self.restore_memento(memento) taskMgr.doMethodLater(1, zcanvas.message, 'Welcome Message', extraArgs = ["Loaded!"]) def load(self): """Restore the current state of the application from file.""" dir = '.' ext = '.saved_story' saved_stories = [f for f in os.listdir(dir) if os.path.isfile(os.path.join(dir,f)) and f.endswith(ext)] saved_stories.sort() from direct.gui.DirectGui import DirectScrolledList,DirectButton labels = [] for saved_story in saved_stories: filename,ext = os.path.splitext(saved_story) l = DirectButton(text=filename, scale=0.05, command=self._load, extraArgs=[saved_story]) labels.append(l) self.load_list = DirectScrolledList( decButton_pos= (0.35, 0, 0.53), decButton_text = "/\\", decButton_text_scale = 0.04, decButton_borderWidth = (0.005, 0.005), incButton_pos= (0.35, 0, -0.02), incButton_text = "\\/", incButton_text_scale = 0.04, incButton_borderWidth = (0.005, 0.005), #frameSize = (0.0, 0.7, -0.05, 0.59), #frameColor = (1,0,0,0.5), pos = self.load_button.getPos(aspect2d), items = labels, numItemsVisible = 4, forceHeight = 0.11, itemFrame_frameSize = (-0.3, 0.3, -0.37, 0.11), itemFrame_pos = (0.35, 0, 0.4), ) def export(self): """Export the current story to a text file.""" memento = self.create_memento() try: f = open("story.txt", "w") try: f.write(memento.__str__()) finally: f.close() except IOError: print 'IOError while exporting story!'
class DirectWindow(DirectFrame): def __init__( self, pos=(-.5, .5), title='Title', curSize=(1, 1), maxSize=(1, 1), minSize=(.5, .5), backgroundColor=(1, 1, 1, 1), borderColor=(1, 1, 1, 1), titleColor=(1, 1, 1, 1), borderSize=0.04, titleSize=0.06, closeButton=False, windowParent=aspect2d, preserve=True, preserveWhole=True, ): self.preserve = preserve self.preserveWhole = preserveWhole self.windowParent = windowParent self.windowPos = pos DirectFrame.__init__( self, parent=windowParent, pos=(self.windowPos[0], 0, self.windowPos[1]), frameColor=(0, 0, 0, 0), frameTexture=loader.loadTexture(DIRECTORY + 'transparent.png')) self.setTransparency(True) # the title part of the window, drag around to move the window self.headerHeight = titleSize h = -self.headerHeight self.windowHeaderLeft = DirectButton( parent=self, frameTexture=DEFAULT_TITLE_GEOM_LEFT, frameSize=(-.5, .5, -.5, .5), borderWidth=(0, 0), relief=DGG.FLAT, frameColor=titleColor, ) self.windowHeaderCenter = DirectButton( parent=self, frameTexture=DEFAULT_TITLE_GEOM_CENTER, frameSize=(-.5, .5, -.5, .5), borderWidth=(0, 0), relief=DGG.FLAT, frameColor=titleColor, ) if closeButton: rightTitleGeom = DEFAULT_TITLE_GEOM_RIGHT_CLOSE command = self.destroy else: rightTitleGeom = DEFAULT_TITLE_GEOM_RIGHT command = None self.windowHeaderRight = DirectButton(parent=self, frameTexture=rightTitleGeom, frameSize=(-.5, .5, -.5, .5), borderWidth=(0, 0), relief=DGG.FLAT, frameColor=titleColor, command=command) self.windowHeaderLeft.setTransparency(True) self.windowHeaderCenter.setTransparency(True) self.windowHeaderRight.setTransparency(True) self.windowHeaderLeft.bind(DGG.B1PRESS, self.startWindowDrag) self.windowHeaderCenter.bind(DGG.B1PRESS, self.startWindowDrag) self.windowHeaderRight.bind(DGG.B1PRESS, self.startWindowDrag) # this is not handled correctly, if a window is dragged which has been # created before another it will not be released # check the bugfixed startWindowDrag function #self.windowHeader.bind(DGG.B1RELEASE,self.stopWindowDrag) text = TextNode('WindowTitleTextNode') text.setText(title) text.setAlign(TextNode.ACenter) text.setTextColor(0, 0, 0, 1) text.setShadow(0.05, 0.05) text.setShadowColor(1, 1, 1, 1) self.textNodePath = self.attachNewNode(text) self.textNodePath.setScale(self.headerHeight * 0.8) # the content part of the window, put stuff beneath # contentWindow.getCanvas() to put it into it self.maxVirtualSize = maxSize self.minVirtualSize = minSize self.resizeSize = borderSize self.contentWindow = DirectScrolledFrame( parent=self, pos=(0, 0, -self.headerHeight), canvasSize=(0, self.maxVirtualSize[0], 0, self.maxVirtualSize[1]), frameColor=( 0, 0, 0, 0), # defines the background color of the resize-button relief=DGG.FLAT, borderWidth=(0, 0), verticalScroll_frameSize=[0, self.resizeSize, 0, 1], horizontalScroll_frameSize=[0, 1, 0, self.resizeSize], # resize the scrollbar according to window size verticalScroll_resizeThumb=False, horizontalScroll_resizeThumb=False, # define the textures for the scrollbars verticalScroll_frameTexture=VERTICALSCROLL_FRAMETEXTURE, verticalScroll_incButton_frameTexture= VERTICALSCROLL_INCBUTTON_FRAMETEXTURE, verticalScroll_decButton_frameTexture= VERTICALSCROLL_DECBUTTON_FRAMETEXTURE, verticalScroll_thumb_frameTexture=VERTICALSCROLL_TUMB_FRAMETEXTURE, horizontalScroll_frameTexture=HORIZONTALSCROLL_FRAMETEXTURE, horizontalScroll_incButton_frameTexture= HORIZONTALSCROLL_INCBUTTON_FRAMETEXTURE, horizontalScroll_decButton_frameTexture= HORIZONTALSCROLL_DECBUTTON_FRAMETEXTURE, horizontalScroll_thumb_frameTexture= HORIZONTALSCROLL_TUMB_FRAMETEXTURE, # make all flat, so the texture is as we want it verticalScroll_relief=DGG.FLAT, verticalScroll_thumb_relief=DGG.FLAT, verticalScroll_decButton_relief=DGG.FLAT, verticalScroll_incButton_relief=DGG.FLAT, horizontalScroll_relief=DGG.FLAT, horizontalScroll_thumb_relief=DGG.FLAT, horizontalScroll_decButton_relief=DGG.FLAT, horizontalScroll_incButton_relief=DGG.FLAT, # colors verticalScroll_frameColor=borderColor, verticalScroll_incButton_frameColor=borderColor, verticalScroll_decButton_frameColor=borderColor, verticalScroll_thumb_frameColor=borderColor, horizontalScroll_frameColor=borderColor, horizontalScroll_incButton_frameColor=borderColor, horizontalScroll_decButton_frameColor=borderColor, horizontalScroll_thumb_frameColor=borderColor, ) self.contentWindow.setTransparency(True) # background color self.backgroundColor = DirectFrame( parent=self.contentWindow.getCanvas(), frameSize=(0, self.maxVirtualSize[0], 0, self.maxVirtualSize[1]), frameColor=backgroundColor, relief=DGG.FLAT, borderWidth=(.01, .01), ) self.backgroundColor.setTransparency(True) # Add a box self.box = boxes.VBox(parent=self.getCanvas()) # is needed for some nicer visuals of the resize button (background) self.windowResizeBackground = DirectButton( parent=self, frameSize=(-.5, .5, -.5, .5), borderWidth=(0, 0), scale=(self.resizeSize, 1, self.resizeSize), relief=DGG.FLAT, frameColor=backgroundColor, ) # the resize button of the window self.windowResize = DirectButton( parent=self, frameSize=(-.5, .5, -.5, .5), borderWidth=(0, 0), scale=(self.resizeSize, 1, self.resizeSize), relief=DGG.FLAT, frameTexture=DEFAULT_RESIZE_GEOM, frameColor=borderColor, ) self.windowResize.setTransparency(True) self.windowResize.bind(DGG.B1PRESS, self.startResizeDrag) self.windowResize.bind(DGG.B1RELEASE, self.stopResizeDrag) # offset then clicking on the resize button from the mouse to the resizebutton # position, required to calculate the position / scaling self.offset = None self.taskName = "resizeTask-%s" % str(hash(self)) # do sizing of the window (minimum) #self.resize( Vec3(0,0,0), Vec3(0,0,0) ) # maximum #self.resize( Vec3(100,0,-100), Vec3(0,0,0) ) self.resize(Vec3(curSize[0], 0, -curSize[1]), Vec3(0, 0, 0)) def getCanvas(self): return self.contentWindow.getCanvas() # dragging functions def startWindowDrag(self, param): self.wrtReparentTo(aspect2dMouseNode) self.ignoreAll() self.accept('mouse1-up', self.stopWindowDrag) def stopWindowDrag(self, param=None): # this is called 2 times (bug), so make sure it's not already parented to aspect2d if self.getParent() != self.windowParent: self.wrtReparentTo(self.windowParent) if self.preserve: if self.preserveWhole: if self.getZ() > 1: self.setZ(1) elif self.getZ() < -1 - self.getHeight(): self.setZ(-1 - self.getHeight()) if self.getX() > base.a2dRight - self.getWidth(): self.setX(base.a2dRight - self.getWidth()) elif self.getX() < base.a2dLeft: self.setX(base.a2dLeft) else: if self.getZ() > 1: self.setZ(1) elif self.getZ() < -1 + self.headerHeight: self.setZ(-1 + self.headerHeight) if self.getX() > base.a2dRight - self.headerHeight: self.setX(base.a2dRight - self.headerHeight) elif self.getX( ) < base.a2dLeft + self.headerHeight - self.getWidth(): self.setX(base.a2dLeft + self.headerHeight - self.getWidth()) #else: #Window moved beyond reach. Destroy window? # resize functions def resize(self, mPos, offset): mXPos = max(min(mPos.getX(), self.maxVirtualSize[0]), self.minVirtualSize[0]) mZPos = max(min(mPos.getZ(), -self.minVirtualSize[1]), -self.maxVirtualSize[1] - self.headerHeight) self.windowResize.setPos(mXPos - self.resizeSize / 2., 0, mZPos + self.resizeSize / 2.) self.windowResizeBackground.setPos(mXPos - self.resizeSize / 2., 0, mZPos + self.resizeSize / 2.) self['frameSize'] = (0, mXPos, 0, mZPos) self.windowHeaderLeft.setPos(self.headerHeight / 2., 0, -self.headerHeight / 2.) self.windowHeaderLeft.setScale(self.headerHeight, 1, self.headerHeight) self.windowHeaderCenter.setPos(mXPos / 2., 0, -self.headerHeight / 2.) self.windowHeaderCenter.setScale(mXPos - self.headerHeight * 2., 1, self.headerHeight) self.windowHeaderRight.setPos(mXPos - self.headerHeight / 2., 0, -self.headerHeight / 2.) self.windowHeaderRight.setScale(self.headerHeight, 1, self.headerHeight) self.contentWindow['frameSize'] = (0, mXPos, mZPos + self.headerHeight, 0) self.textNodePath.setPos(mXPos / 2., 0, -self.headerHeight / 3. * 2.) # show and hide that small background for the window sizer if mXPos == self.maxVirtualSize[0] and \ mZPos == -self.maxVirtualSize[1]-self.headerHeight: self.windowResizeBackground.hide() else: self.windowResizeBackground.show() def resizeTask(self, task=None): mPos = aspect2dMouseNode.getPos(self) + self.offset self.resize(mPos, self.offset) return task.cont def startResizeDrag(self, param): self.offset = self.windowResize.getPos(aspect2dMouseNode) taskMgr.remove(self.taskName) taskMgr.add(self.resizeTask, self.taskName) def stopResizeDrag(self, param): taskMgr.remove(self.taskName) # get the window to the front self.wrtReparentTo(self.windowParent) def addHorizontal(self, widgets): """ Accepts a list of directgui objects which are added to a horizontal box, which is then added to the vertical stack. """ hbox = boxes.HBox() for widget in widgets: hbox.pack(widget) self.box.pack(hbox) self.updateMaxSize() def addVertical(self, widgets): """ Accepts a list of directgui objects which are added to a vertical box, which is then added to the vertical stack. May cause funky layout results. """ #vbox = boxes.VBox() for widget in widgets: self.box.pack(widget) self.updateMaxSize() def add(self, widgets): """Shortcut function for addVertical""" self.addVertical(widgets) def updateMaxSize(self): """Updates the max canvas size to include all items packed. Window is resized to show all contents.""" bottomLeft, topRight = self.box.getTightBounds() self.maxVirtualSize = (topRight[0], -bottomLeft[2]) self.contentWindow['canvasSize'] = (0, self.maxVirtualSize[0], -self.maxVirtualSize[1], 0) self.backgroundColor['frameSize'] = (0, self.maxVirtualSize[0], -self.maxVirtualSize[1], 0) #perhaps this should be optional -- automatically resize for new elements self.reset() def reset(self): """Poorly named function that resizes window to fit all contents""" self.resize( Vec3(self.maxVirtualSize[0], 0, -self.maxVirtualSize[1] - self.headerHeight), Vec3(0, 0, 0))
class DirectWindow( DirectFrame ): def __init__( self, pos = ( -.5, .5), title = 'Title', curSize = ( 1, 1), maxSize = ( 1, 1 ), minSize = ( .5, .5 ), backgroundColor = ( 1, 1, 1, 1 ), borderColor = ( 1, 1, 1, 1 ), titleColor = ( 1, 1, 1, 1 ), borderSize = 0.04, titleSize = 0.06, closeButton = False, windowParent = aspect2d, preserve = True, preserveWhole = True, ): self.preserve = preserve self.preserveWhole = preserveWhole self.windowParent = windowParent self.windowPos = pos DirectFrame.__init__( self, parent = windowParent, pos = ( self.windowPos[0], 0, self.windowPos[1] ), frameColor = ( 0, 0, 0, 0 ), frameTexture = loader.loadTexture( DIRECTORY+'transparent.png' ) ) self.setTransparency(True) # the title part of the window, drag around to move the window self.headerHeight = titleSize h = -self.headerHeight self.windowHeaderLeft = DirectButton( parent = self, frameTexture = DEFAULT_TITLE_GEOM_LEFT, frameSize = ( -.5, .5, -.5, .5 ), borderWidth = ( 0, 0 ), relief = DGG.FLAT, frameColor = titleColor, ) self.windowHeaderCenter = DirectButton( parent = self, frameTexture = DEFAULT_TITLE_GEOM_CENTER, frameSize = ( -.5, .5, -.5, .5 ), borderWidth = ( 0, 0 ), relief = DGG.FLAT, frameColor = titleColor, ) if closeButton: rightTitleGeom = DEFAULT_TITLE_GEOM_RIGHT_CLOSE command = self.destroy else: rightTitleGeom = DEFAULT_TITLE_GEOM_RIGHT command = None self.windowHeaderRight = DirectButton( parent = self, frameTexture = rightTitleGeom, frameSize = ( -.5, .5, -.5, .5 ), borderWidth = ( 0, 0 ), relief = DGG.FLAT, frameColor = titleColor, command = command ) self.windowHeaderLeft.setTransparency(True) self.windowHeaderCenter.setTransparency(True) self.windowHeaderRight.setTransparency(True) self.windowHeaderLeft.bind( DGG.B1PRESS, self.startWindowDrag ) self.windowHeaderCenter.bind( DGG.B1PRESS, self.startWindowDrag ) self.windowHeaderRight.bind( DGG.B1PRESS, self.startWindowDrag ) # this is not handled correctly, if a window is dragged which has been # created before another it will not be released # check the bugfixed startWindowDrag function #self.windowHeader.bind(DGG.B1RELEASE,self.stopWindowDrag) text = TextNode('WindowTitleTextNode') text.setText(title) text.setAlign(TextNode.ACenter) text.setTextColor( 0, 0, 0, 1 ) text.setShadow(0.05, 0.05) text.setShadowColor( 1, 1, 1, 1 ) self.textNodePath = self.attachNewNode(text) self.textNodePath.setScale(self.headerHeight*0.8) # the content part of the window, put stuff beneath # contentWindow.getCanvas() to put it into it self.maxVirtualSize = maxSize self.minVirtualSize = minSize self.resizeSize = borderSize self.contentWindow = DirectScrolledFrame( parent = self, pos = ( 0, 0, -self.headerHeight ), canvasSize = ( 0, self.maxVirtualSize[0], 0, self.maxVirtualSize[1] ), frameColor = ( 0, 0, 0, 0), # defines the background color of the resize-button relief = DGG.FLAT, borderWidth = (0, 0), verticalScroll_frameSize = [0, self.resizeSize, 0, 1], horizontalScroll_frameSize = [0, 1, 0, self.resizeSize], # resize the scrollbar according to window size verticalScroll_resizeThumb = False, horizontalScroll_resizeThumb = False, # define the textures for the scrollbars verticalScroll_frameTexture = VERTICALSCROLL_FRAMETEXTURE, verticalScroll_incButton_frameTexture = VERTICALSCROLL_INCBUTTON_FRAMETEXTURE, verticalScroll_decButton_frameTexture = VERTICALSCROLL_DECBUTTON_FRAMETEXTURE, verticalScroll_thumb_frameTexture = VERTICALSCROLL_TUMB_FRAMETEXTURE, horizontalScroll_frameTexture = HORIZONTALSCROLL_FRAMETEXTURE, horizontalScroll_incButton_frameTexture = HORIZONTALSCROLL_INCBUTTON_FRAMETEXTURE, horizontalScroll_decButton_frameTexture = HORIZONTALSCROLL_DECBUTTON_FRAMETEXTURE, horizontalScroll_thumb_frameTexture = HORIZONTALSCROLL_TUMB_FRAMETEXTURE, # make all flat, so the texture is as we want it verticalScroll_relief = DGG.FLAT, verticalScroll_thumb_relief = DGG.FLAT, verticalScroll_decButton_relief = DGG.FLAT, verticalScroll_incButton_relief = DGG.FLAT, horizontalScroll_relief = DGG.FLAT, horizontalScroll_thumb_relief = DGG.FLAT, horizontalScroll_decButton_relief = DGG.FLAT, horizontalScroll_incButton_relief = DGG.FLAT, # colors verticalScroll_frameColor = borderColor, verticalScroll_incButton_frameColor = borderColor, verticalScroll_decButton_frameColor = borderColor, verticalScroll_thumb_frameColor = borderColor, horizontalScroll_frameColor = borderColor, horizontalScroll_incButton_frameColor = borderColor, horizontalScroll_decButton_frameColor = borderColor, horizontalScroll_thumb_frameColor = borderColor, ) self.contentWindow.setTransparency(True) # background color self.backgroundColor = DirectFrame( parent = self.contentWindow.getCanvas(), frameSize = ( 0, self.maxVirtualSize[0], 0, self.maxVirtualSize[1] ), frameColor = backgroundColor, relief = DGG.FLAT, borderWidth = ( .01, .01), ) self.backgroundColor.setTransparency(True) # Add a box self.box = boxes.VBox(parent = self.getCanvas()) # is needed for some nicer visuals of the resize button (background) self.windowResizeBackground = DirectButton( parent = self, frameSize = ( -.5, .5, -.5, .5 ), borderWidth = ( 0, 0 ), scale = ( self.resizeSize, 1, self.resizeSize ), relief = DGG.FLAT, frameColor = backgroundColor, ) # the resize button of the window self.windowResize = DirectButton( parent = self, frameSize = ( -.5, .5, -.5, .5 ), borderWidth = ( 0, 0 ), scale = ( self.resizeSize, 1, self.resizeSize ), relief = DGG.FLAT, frameTexture = DEFAULT_RESIZE_GEOM, frameColor = borderColor, ) self.windowResize.setTransparency(True) self.windowResize.bind(DGG.B1PRESS,self.startResizeDrag) self.windowResize.bind(DGG.B1RELEASE,self.stopResizeDrag) # offset then clicking on the resize button from the mouse to the resizebutton # position, required to calculate the position / scaling self.offset = None self.taskName = "resizeTask-%s" % str(hash(self)) # do sizing of the window (minimum) #self.resize( Vec3(0,0,0), Vec3(0,0,0) ) # maximum #self.resize( Vec3(100,0,-100), Vec3(0,0,0) ) self.resize( Vec3(curSize[0], 0, -curSize[1]), Vec3(0,0,0)) def getCanvas(self): return self.contentWindow.getCanvas() # dragging functions def startWindowDrag( self, param ): self.wrtReparentTo( aspect2dMouseNode ) self.ignoreAll() self.accept( 'mouse1-up', self.stopWindowDrag ) def stopWindowDrag( self, param=None ): # this is called 2 times (bug), so make sure it's not already parented to aspect2d if self.getParent() != self.windowParent: self.wrtReparentTo( self.windowParent ) if self.preserve: if self.preserveWhole: if self.getZ() > 1: self.setZ(1) elif self.getZ() < -1 - self.getHeight(): self.setZ(-1 - self.getHeight()) if self.getX() > base.a2dRight - self.getWidth(): self.setX(base.a2dRight - self.getWidth()) elif self.getX() < base.a2dLeft: self.setX(base.a2dLeft) else: if self.getZ() > 1: self.setZ(1) elif self.getZ() < -1 + self.headerHeight: self.setZ(-1 + self.headerHeight) if self.getX() > base.a2dRight - self.headerHeight: self.setX(base.a2dRight - self.headerHeight) elif self.getX() < base.a2dLeft + self.headerHeight - self.getWidth(): self.setX(base.a2dLeft + self.headerHeight - self.getWidth()) #else: #Window moved beyond reach. Destroy window? # resize functions def resize( self, mPos, offset ): mXPos = max( min( mPos.getX(), self.maxVirtualSize[0] ), self.minVirtualSize[0]) mZPos = max( min( mPos.getZ(), -self.minVirtualSize[1] ), -self.maxVirtualSize[1]-self.headerHeight) self.windowResize.setPos( mXPos-self.resizeSize/2., 0, mZPos+self.resizeSize/2. ) self.windowResizeBackground.setPos( mXPos-self.resizeSize/2., 0, mZPos+self.resizeSize/2. ) self['frameSize'] = (0, mXPos, 0, mZPos) self.windowHeaderLeft.setPos( self.headerHeight/2., 0, -self.headerHeight/2. ) self.windowHeaderLeft.setScale( self.headerHeight, 1, self.headerHeight ) self.windowHeaderCenter.setPos( mXPos/2., 0, -self.headerHeight/2. ) self.windowHeaderCenter.setScale( mXPos - self.headerHeight*2., 1, self.headerHeight ) self.windowHeaderRight.setPos( mXPos-self.headerHeight/2., 0, -self.headerHeight/2. ) self.windowHeaderRight.setScale( self.headerHeight, 1, self.headerHeight ) self.contentWindow['frameSize'] = ( 0, mXPos, mZPos+self.headerHeight, 0) self.textNodePath.setPos( mXPos/2., 0, -self.headerHeight/3.*2. ) # show and hide that small background for the window sizer if mXPos == self.maxVirtualSize[0] and \ mZPos == -self.maxVirtualSize[1]-self.headerHeight: self.windowResizeBackground.hide() else: self.windowResizeBackground.show() def resizeTask( self, task=None ): mPos = aspect2dMouseNode.getPos( self )+self.offset self.resize( mPos, self.offset ) return task.cont def startResizeDrag( self, param ): self.offset = self.windowResize.getPos( aspect2dMouseNode ) taskMgr.remove( self.taskName ) taskMgr.add( self.resizeTask, self.taskName ) def stopResizeDrag( self, param ): taskMgr.remove( self.taskName ) # get the window to the front self.wrtReparentTo( self.windowParent ) def addHorizontal(self, widgets): """ Accepts a list of directgui objects which are added to a horizontal box, which is then added to the vertical stack. """ hbox = boxes.HBox() for widget in widgets: hbox.pack(widget) self.box.pack(hbox) self.updateMaxSize() def addVertical(self, widgets): """ Accepts a list of directgui objects which are added to a vertical box, which is then added to the vertical stack. May cause funky layout results. """ #vbox = boxes.VBox() for widget in widgets: self.box.pack(widget) self.updateMaxSize() def add(self, widgets): """Shortcut function for addVertical""" self.addVertical(widgets) def updateMaxSize(self): """Updates the max canvas size to include all items packed. Window is resized to show all contents.""" bottomLeft, topRight = self.box.getTightBounds() self.maxVirtualSize = (topRight[0], -bottomLeft[2]) self.contentWindow['canvasSize'] = ( 0, self.maxVirtualSize[0], -self.maxVirtualSize[1], 0) self.backgroundColor['frameSize'] = ( 0, self.maxVirtualSize[0], -self.maxVirtualSize[1], 0 ) #perhaps this should be optional -- automatically resize for new elements self.reset() def reset(self): """Poorly named function that resizes window to fit all contents""" self.resize( Vec3(self.maxVirtualSize[0], 0, -self.maxVirtualSize[1]-self.headerHeight), Vec3(0,0,0))
class Mediator(Receiver, FocusObserver, NodePathWrapper): """The singleton mediatorobject mediates the interaction between the StoryMap objects, receiving notifications and calling methods on StoryMap objects. """ def __init__(self): # Create two story maps, 'Story Cards' which the user picks story cards # from, and 'My Story Map' in which the user constructs her story. self.storyCards = StoryMap(storyCardClass=FocusableChoosableStoryCard, title="Story Cards") self.storyCards.reparentTo(zcanvas.home) self.storyCards.setScale(0.02) self.storyCards.setPos(-.5, 0, .8) self.storyCards.fill() self.storyCards.added_behaviour = 'disable' self.myStoryMap = StoryMap(storyCardClass=FocusableEditableStoryCard, title="My Story") self.myStoryMap.reparentTo(zcanvas.home) self.myStoryMap.setScale(0.02) self.myStoryMap.setPos(-.5, 0, -.1) self.myStoryMap.added_behaviour = 'remove' #self.myStoryMap.keep_sorted = True #self.myStoryMap.np.showTightBounds() self.myStoryMap.auto_grow = True # Keyboard controls for saving, loading and exporting. #base.accept("f1",self.save) #base.accept("f2",self.load) #base.accept("f3",self.export) # Subscribe to some messages. self.acceptOnce('zoom done', zcanvas.message, ["Right-click to zoom back out again."]) self.accept('add', self.add) self.accept('remove', self.remove) # Frame along the bottom for Save, Load and Quit buttons. self.bottom_np = aspect2d.attachNewNode('bottom frame') height = 0.15 self.bottom_np.setPos(-base.getAspectRatio(), 0, -1 - height) cm = CardMaker('bottom frame') cm.setFrame(0, 2 * base.getAspectRatio(), 0, height) self.bottom_np.attachNewNode(cm.generate()) self.bottom_np.setTransparency(TransparencyAttrib.MAlpha) self.bottom_np.setColor(.1, .1, .1, .7) self.bottom_hbox = HBoxList(margin=1) self.bottom_hbox.reparentTo(self.bottom_np) self.bottom_hbox.setPos(0, 0, height - 0.03) self.bottom_hbox.setScale(.1) self.save_button = DirectButton(text="Save", command=self.save) b = Box() b.fill(self.save_button) self.bottom_hbox.append(b) self.load_button = DirectButton(text="Load", command=self.load) b = Box() b.fill(self.load_button) self.bottom_hbox.append(b) # Interval that slides the frame onto the screen. self.bottom_interval = LerpPosInterval( self.bottom_np, duration=1, pos=Point3(-base.getAspectRatio(), 0, -1), startPos=Point3(-base.getAspectRatio(), 0, -1 - height), other=None, blendType='easeInOut', bakeInStart=1, fluid=0, name=None) self.bottom_reverse_interval = LerpPosInterval( self.bottom_np, duration=1, pos=Point3(-base.getAspectRatio(), 0, -1 - height), startPos=Point3(-base.getAspectRatio(), 0, -1), other=None, blendType='easeInOut', bakeInStart=1, fluid=0, name=None) self.bottom_frame_is_active = False # Frame along the right for story cards. self.right_np = aspect2d.attachNewNode('right frame') width = 0.14 * base.getAspectRatio() self.right_np.setPos(base.getAspectRatio() + width, 0, 1) cm = CardMaker('right frame') cm.setFrame(-width, 0, -2, 0) self.right_np.attachNewNode(cm.generate()) self.right_np.setTransparency(TransparencyAttrib.MAlpha) self.right_np.setColor(.1, .1, .1, .7) self.right_vbox = Stack() self.right_vbox.reparentTo(self.right_np) self.right_vbox.setPos(-width + 0.035, 0, -0.06) self.right_vbox.setScale(.02) # Interval that slides the frame onto the screen. self.right_interval = LerpPosInterval( self.right_np, duration=1, pos=Point3(base.getAspectRatio(), 0, 1), startPos=Point3(base.getAspectRatio() + width, 0, 1), other=None, blendType='easeInOut', bakeInStart=1, fluid=0, name=None) self.right_reverse_interval = LerpPosInterval( self.right_np, duration=1, pos=Point3(base.getAspectRatio() + width, 0, 1), startPos=Point3(base.getAspectRatio(), 0, 1), other=None, blendType='easeInOut', bakeInStart=1, fluid=0, name=None) self.right_frame_is_active = False # Task that watches for the mouse going to the screen edges and slides # the frames onscreen when it does. self.prev_x = None self.prev_y = None taskMgr.add(self.task, 'Mediator mouse watcher task') NodePathWrapper.__init__(self) FocusObserver.__init__(self) def enterNone(self): """Viewport focus has changed to None.""" # Make the title of 'My Story Map' editable. self.myStoryMap.title['state'] = DGG.NORMAL def exitNone(self): """Undo any changes made by enterNone.""" self.myStoryMap.title['state'] = DGG.DISABLED def task(self, task): if base.mouseWatcherNode.hasMouse(): x = base.mouseWatcherNode.getMouseX() y = base.mouseWatcherNode.getMouseY() if y <= -0.87 and self.prev_y > -0.87: # The mouse has just moved into the bottom frame's area. self.activate_bottom_frame() elif y > -0.87 and self.prev_y <= -0.87: # The mouse has just moved out of the bottom frame's area. self.deactivate_bottom_frame() self.prev_y = y if x >= 0.8 and self.prev_x < 0.8: # The mouse has just moved into the right frame's area. self.activate_right_frame() elif x < 0.8 and self.prev_x >= 0.8: # The mouse has just moved out of the right frame's area. self.deactivate_right_frame() self.prev_x = x return task.cont def activate_bottom_frame(self): if not self.bottom_frame_is_active: self.bottom_interval.start() self.bottom_frame_is_active = True def deactivate_bottom_frame(self): if self.bottom_frame_is_active: self.bottom_reverse_interval.start() self.bottom_frame_is_active = False def activate_right_frame(self): if not self.right_frame_is_active: self.right_interval.start() self.right_frame_is_active = True def deactivate_right_frame(self): if self.right_frame_is_active: self.right_reverse_interval.start() self.right_frame_is_active = False def add(self, card): # The Add button was pressed on one of the StoryCards in self.storyCards for box in self.right_vbox: if box.contents is None: box.fill(card) # self.activate_right_frame() return zcanvas.message( 'The stack is full!\nDrag another card from the stack first.') def remove(self, editableCard): # The Remove button was pressed on one of the StoryCards in # self.myStoryMap editableCard.getPythonTag('box').empty() for choosableCard in self.storyCards.items(): if choosableCard.function == editableCard.function: choosableCard.enable() return # Implement the Originator interface of the memento design pattern. (For # saving and loading.) class Memento: """A passive class that stores the state of a Mediator object.""" def __init__(self, storycards, mystorymap, stack): # MediatorMemento just holds mementos for mediator's two StoryMap # objects. self.storycards = storycards self.mystorymap = mystorymap self.stack = stack def __str__(self): return self.mystorymap.__str__() def create_memento(self): """Return a memento object holding the current internal state of this object.""" return Mediator.Memento(self.storyCards.create_memento(), self.myStoryMap.create_memento(), self.right_vbox.create_memento()) def restore_memento(self, memento): """Restore the internal state of this object to that held by the given memento.""" self.storyCards.restore_memento(memento.storycards) self.myStoryMap.restore_memento(memento.mystorymap) self.right_vbox.restore_memento(memento.stack) # Implement the Caretaker interface of the memento design pattern. (For # saving and loading.) def save(self): """Save the current state of the application to file.""" memento = self.create_memento() import datetime f = open( str(datetime.datetime.now()).replace(' ', '_') + '.saved_story', 'w') cPickle.dump(memento, f) f.close() zcanvas.message("Saved!") def _load(self, args): self.load_list.removeNode() f = open(args, 'r') memento = cPickle.load(f) f.close() self.restore_memento(memento) taskMgr.doMethodLater(1, zcanvas.message, 'Welcome Message', extraArgs=["Loaded!"]) def load(self): """Restore the current state of the application from file.""" dir = '.' ext = '.saved_story' saved_stories = [ f for f in os.listdir(dir) if os.path.isfile(os.path.join(dir, f)) and f.endswith(ext) ] saved_stories.sort() from direct.gui.DirectGui import DirectScrolledList, DirectButton labels = [] for saved_story in saved_stories: filename, ext = os.path.splitext(saved_story) l = DirectButton(text=filename, scale=0.05, command=self._load, extraArgs=[saved_story]) labels.append(l) self.load_list = DirectScrolledList( decButton_pos=(0.35, 0, 0.53), decButton_text="/\\", decButton_text_scale=0.04, decButton_borderWidth=(0.005, 0.005), incButton_pos=(0.35, 0, -0.02), incButton_text="\\/", incButton_text_scale=0.04, incButton_borderWidth=(0.005, 0.005), #frameSize = (0.0, 0.7, -0.05, 0.59), #frameColor = (1,0,0,0.5), pos=self.load_button.getPos(aspect2d), items=labels, numItemsVisible=4, forceHeight=0.11, itemFrame_frameSize=(-0.3, 0.3, -0.37, 0.11), itemFrame_pos=(0.35, 0, 0.4), ) def export(self): """Export the current story to a text file.""" memento = self.create_memento() try: f = open("story.txt", "w") try: f.write(memento.__str__()) finally: f.close() except IOError: print 'IOError while exporting story!'
class DirectWindow( DirectFrame ): def __init__( self , pos = ( -.5, .5) , title = 'Title' , bgColor = (.5,.5,.5,1) , buttonColor = (1,1,1,1) #( .6, .6, .6, 1 ) #, minSize = ( .5, .5 ) #, maxSize = ( 1, 1 ) , minWindowSize = (0,0) , maxWindowSize = (10000,10000) , virtualSize = (1,1) , windowBorderTextureFiles = [ DEFAULT_TITLE_TEXTURE_LEFT , DEFAULT_TITLE_TEXTURE_CENTER , DEFAULT_TITLE_TEXTURE_RIGHT , DEFAULT_RESIZE_GEOM ] , windowBorderGeomFiles = [ DEFAULT_TITLE_GEOM_RIGHT ] , windowColors = [ ( 1, 1, 1, 1 ) , ( 1, 1, 1, 1 ) , ( 1, 1, 1, 1 ) , ( 1, 1, 1, 1 ) ] , borderSize = 0.01 , dragbarSize = 0.05 , parent=None): self.windowPos = pos self.minWindowSize = minWindowSize self.maxWindowSize = maxWindowSize self.virtualSize = virtualSize self.borderSize = borderSize self.dragbarSize = dragbarSize if parent is None: parent=aspect2d self.parent=parent self.previousSize = (10,10) self.collapsed = False # maybe we should check if aspect2d doesnt already contain the aspect2dMouseNode self.mouseNode = self.parent.attachNewNode( 'aspect2dMouseNode', sort = 999999 ) taskMgr.add( self.mouseNodeTask, 'mouseNodeTask' ) windowBorderTextures = list() for windowBorder in windowBorderTextureFiles: if windowBorder is not None: mdlFile = loader.loadTexture(windowBorder) windowBorderTextures.append(mdlFile) else: windowBorderTextures.append(None) windowBorderGeoms = list() for windowGeom in windowBorderGeomFiles: if windowGeom is not None: mdlFile = loader.loadModel(windowGeom) mdls = ( mdlFile.find('**/**-default'), mdlFile.find('**/**-click'), mdlFile.find('**/**-rollover'), mdlFile.find('**/**-disabled') ) windowBorderGeoms.append(mdls) else: windowBorderGeoms.append((None,None,None,None,),) # the main window we want to move around self.parentWindow = DirectFrame( parent=self.parent, pos=(self.windowPos[0], 0, self.windowPos[1]), #frameSize=# is defined in resize scale=(1, 1, -1), frameColor=bgColor, borderWidth=(0, 0), relief=DGG.FLAT, sortOrder=1, ) # header of the window (drag&drop with it) # the title part of the window, drag around to move the window self.headerParent = DirectButton( parent=self.parentWindow, pos=(0, 0, 0), #frameSize=# is defined in resize scale=(1, 1, self.dragbarSize), frameColor=(1, 1, 1, 1), borderWidth=(0, 0), relief=DGG.FLAT, ) self.headerParent.bind(DGG.B1PRESS,self.startWindowDrag) # images in the headerParent self.headerCenter = DirectFrame( parent=self.headerParent, pos=(0, 0, 1), #frameSize=# is defined in resize scale=(1,1,-1), frameColor=windowColors[1], frameTexture=windowBorderTextures[1], borderWidth=(0, 0), relief=DGG.FLAT, ) self.headerLeft = DirectFrame( parent=self.headerParent, pos=(0, 0, 1), frameSize=(0, self.dragbarSize, 0, 1), scale=(1,1,-1), frameColor=windowColors[0], frameTexture=windowBorderTextures[0], borderWidth=(0, 0), relief=DGG.FLAT, ) # collapse button self.headerRight = DirectButton( parent=self.headerParent, #pos=# is defined in resize frameSize=(0, self.dragbarSize, 0, 1), scale=(1,1,-1), frameColor=windowColors[2], #frameTexture=windowBorderTextures[2], borderWidth=(0, 0), relief=DGG.FLAT, command=self.toggleCollapsed, geom=windowBorderGeoms[0], geom_scale=(self.dragbarSize,1,1) ) # the resize button of the window self.resizeButton = DirectButton( parent=self.parentWindow, pos=(1-self.dragbarSize, 0, 1), frameSize=(0, 1, 0, 1), scale=(self.dragbarSize,1,-self.dragbarSize), frameColor=windowColors[3], frameTexture=windowBorderTextures[3], borderWidth=(0, 0), relief=DGG.FLAT, sortOrder=1, ) self.resizeButton.bind(DGG.B1PRESS,self.startResizeDrag) # text in the center of the window text = TextNode('WindowTitleTextNode') text.setText(title) text.setAlign(TextNode.ACenter) text.setTextColor( 0, 0, 0, 1 ) text.setShadow(0.05, 0.05) text.setShadowColor( 1, 1, 1, 1 ) self.textNodePath = self.headerCenter.attachNewNode(text) self.textNodePath.setPos(.5,0,.3) self.textNodePath.setScale(0.8*self.dragbarSize,1,0.8) if Y_INVERTED: scale = (1,1,-1) else: scale = (1,1,1) # the content part of the window, put stuff beneath # contentWindow.getCanvas() to put it into it self.contentWindow = DirectScrolledFrame( parent = self.parentWindow, #pos = # is defined in resize scale = scale, canvasSize = (0,self.virtualSize[0],0,self.virtualSize[1]), frameColor = buttonColor, relief = DGG.RAISED, borderWidth = (0,0), verticalScroll_frameSize = [0,self.dragbarSize,0,1], verticalScroll_frameTexture = loader.loadTexture( 'rightBorder.png' ), verticalScroll_incButton_frameTexture = loader.loadTexture( 'scrollDown.png' ), verticalScroll_decButton_frameTexture = loader.loadTexture( 'scrollDown.png' ), verticalScroll_thumb_frameTexture = loader.loadTexture( 'scrollBar.png' ), horizontalScroll_frameSize = [0,1,0,self.dragbarSize], horizontalScroll_frameTexture = loader.loadTexture( 'bottomBorder.png' ), horizontalScroll_incButton_frameTexture = loader.loadTexture( 'scrollDown.png' ), horizontalScroll_decButton_frameTexture = loader.loadTexture( 'scrollDown.png' ), horizontalScroll_thumb_frameTexture = loader.loadTexture( 'scrollBar.png' ), ) # child we attach should be inside the window DirectFrame.__init__( self, parent = self.contentWindow.getCanvas(), pos = (0,0,self.virtualSize[1]), scale = (1,1,1), frameSize = ( 0, self.virtualSize[0]+2*self.borderSize, 0, self.virtualSize[1] ), #frameColor = (0,0,0,1), relief = DGG.RIDGE, borderWidth = (0,0), ) self.initialiseoptions(DirectWindow) # offset then clicking on the resize button from the mouse to the resizebutton # position, required to calculate the position / scaling self.offset = None self.resizeButtonTaskName = "resizeTask-%s" % str(hash(self)) # do sizing of the window to virtualSize #self.resize( self.virtualSize[0]+2*self.borderSize # , self.virtualSize[1]+self.dragbarSize+2*self.borderSize ) self.resize(10,10) # a task that keeps a node at the position of the mouse-cursor def mouseNodeTask(self, task): if WindowManager.hasMouse(): x=WindowManager.getMouseX() y=WindowManager.getMouseY() # the mouse position is read relative to render2d, so set it accordingly self.mouseNode.setPos( render2d, x, 0, y ) return task.cont # dragging functions def startWindowDrag( self, param ): self.parentWindow.wrtReparentTo( self.mouseNode ) self.ignoreAll() self.accept( 'mouse1-up', self.stopWindowDrag ) def stopWindowDrag( self, param=None ): # this could be called even after the window has been destroyed #if self: # this is called 2 times (bug), so make sure it's not already parented to aspect2d if self.parentWindow.getParent() != self.parent: self.parentWindow.wrtReparentTo(self.parent) self.ignoreAll() # resize functions def startResizeDrag(self, param): self.offset = self.resizeButton.getPos(aspect2d) - self.mouseNode.getPos(aspect2d) taskMgr.remove( self.resizeButtonTaskName ) taskMgr.add( self.resizeButtonTask, self.resizeButtonTaskName ) self.accept( 'mouse1-up', self.stopResizeDrag,['x'] ) def resize(self,windowX,windowY): # limit max/min size of the window maxX = min(self.maxWindowSize[0], self.virtualSize[0]+2*self.borderSize) minX = max( self.dragbarSize*3, self.minWindowSize[0]) windowWidth = min( maxX, max( minX, windowX ) ) maxY = min( self.maxWindowSize[1], self.virtualSize[1]+self.dragbarSize+2*self.borderSize ) minY = max( self.dragbarSize*4, self.minWindowSize[1]) windowHeight = min( maxY, max( minY, windowY ) ) if self.collapsed: windowHeight = 2*self.dragbarSize+2*self.borderSize windowWidth = windowWidth self.contentWindow.hide() # store changed window width only self.previousSize = windowWidth, self.previousSize[1] else: self.contentWindow.show() self.previousSize = windowWidth, windowHeight # set the window size self.headerParent['frameSize'] = (0, windowWidth, 0, 1) self.headerCenter['frameSize'] = (0, windowWidth, 0, 1) self.parentWindow['frameSize'] = (0, windowWidth, 0, windowHeight) self.contentWindow['frameSize'] = (0, windowWidth-self.borderSize*2, 0, windowHeight-self.dragbarSize-2*self.borderSize) self.contentWindow.setPos(self.borderSize,0,windowHeight-self.borderSize) self.headerRight.setPos(windowWidth-self.dragbarSize, 0, 1) self.textNodePath.setPos(windowWidth/2.,0,.3) self.resizeButton.setPos(windowWidth-self.dragbarSize, 0, windowHeight) def resizeButtonTask(self, task=None): mPos = self.mouseNode.getPos(self.parentWindow) # max height, the smaller of (given maxWindowSize and real size of content and borders windowX = mPos.getX() + self.offset.getX() + self.dragbarSize windowY = mPos.getZ() - self.offset.getZ() self.resize(windowX,windowY) return task.cont def stopResizeDrag(self, param): taskMgr.remove( self.resizeButtonTaskName ) self.ignoreAll() # a bugfix for a wrong implementation def detachNode( self ): self.parentWindow.detachNode() #self. = None #DirectFrame.detachNode( self ) def removeNode( self ): self.parentWindow.removeNode() #DirectFrame.removeNode( self ) def toggleCollapsed(self,state=None): if state is None: state=not self.collapsed if state: self.collapse() else: self.uncollapse() def collapse(self): self.collapsed = True self.resize(*self.previousSize) def uncollapse(self): self.collapsed = False self.resize(*self.previousSize)