Exemple #1
0
class Director(object):
	"""
	The central point of the application. It handles the main run loop, L{Scene} transitions, and propagating redraw events to the Scene's children. It is also the owner of the L{GestureDispatch}, L{ActionManager}, L{Scheduler}, and the L{GTKInterface}.

	Normally only one Director should exist per application.
	"""
	def __init__(self, windowSize=None):
		"""
		Initialization method. If windowSize is not defined, the default is C{Size(800,600)}.

		@param windowSize: The size of the window.
		@type windowSize: L{Size}
		"""
		if windowSize is None:
			self._windowSize = Size(800, 600)
		else:
			self._windowSize = windowSize
		self._gtkInterface = None	# set this up in setWindow()
		self._gestureDispatch = GestureDispatch()
		self._scheduler = Scheduler()
		self._actionManager = ActionManager(self._scheduler)

		self._isShowingFPS = False

		self._runningScene = None
		self._nextScene = None
		self._scenesStack = []

		self._oldFramerate = 1.0/60.0
		self._framerate = 1.0/60.0
		self._frames = 0
		self._isPaused = False
		self._dt = 0
		self._isNextDeltaTimeZero = False
		self._lastTimeStamp = 0
		self._accumDt = 0
		self._frames = 0
		self._displayedFramerate = 0
		self._isRecording = False
		self._backgroundColor = BlackColor()

#{ Accessor methods.
	def isShowingFPS(self):
		"""
		Whether or not the Director is displaying frames per second. Default is C{False}.

		@return: Whether or not the Director is displaying frames per second (FPS).
		@rtype: L{bool}
		"""
		return self._isShowingFPS

	def setShowingFPS(self, isShowingFPS):
		"""
		Sets whether or not the Director displays frames per second.

		@param isShowingFPS: Whether or not the Director displays frames per second.
		@type isShowingFPS: C{bool}
		"""
		self._isShowingFPS = isShowingFPS

	showingFPS = property(isShowingFPS, setShowingFPS, doc="Whether or not to display the framerate.")

	def getSize(self):
		"""
		Returns the size of the main application window.

		@return: Size of the main application window.
		@rtype: L{Size}
		"""
		return self._gtkInterface.getSize()

	size = property(getSize, doc="The size of the main application window.")

	def getGestureDispatch(self):
		"""
		Returns the L{GestureDispatch} for the application, which sends out notifications of L{GestureEvent}C{s} to L{GestureListener}C{s}.

		@return: The dispatch.
		@rtype: L{GestureDispatch}
		"""
		return self._gestureDispatch

	gestureDispatch = property(getGestureDispatch, doc="The application's GestureDispatch.")

	def getActionManager(self):
		"""
		Reutrns the L{ActionManager} for the application, which manages the L{Action}C{s}.

		@return: The manager.
		@rtype: L{ActionManager}
		"""
		return self._actionManager

	actionManager = property(getActionManager, doc="The application's ActionManager.")

	def getScheduler(self):
		"""
		Returns the L{Scheduler} for the application, which manages the L{Timer}C{s}.

		@return: The scheduler.
		@rtype: L{Scheduler}
		"""
		return self._scheduler

	scheduler = property(getScheduler, doc="The application's Scheduler.")

	def getBackgroundColor(self):
		return self._backgroundColor

	def setBackgroundColor(self, color):
		"""
		Sets the color for the background of the application.

		@param color: The color of the background.
		@type color: L{Color}
		"""
		if self._gtkInterface is not None:
			self._gtkInterface.setBackgroundColor(color)
		self._backgroundColor = color

	backgroundColor = property(getBackgroundColor, setBackgroundColor, doc="The application's background color.")

	def setWindow(self, window=None):
		"""
		Sets the main window for the application. If window is C{None}, a L{GTKWindow} is generated automatically.

		@param window: The main application window.
		@type window: L{AbstractWindow} (or C{None})
		"""
		if self._gtkInterface == None:
			self._gtkInterface = GTKInterface(self, window, self._windowSize) # if window is None, defaults to GTKWindow()
			self._gtkInterface.setBackgroundColor(self._backgroundColor)
		else:
			warnings.warn("Window is already set.")

	def getGTKLayout(self):
		"""
		Returns the main gtk.Layout to which cocosCairo draws. Use this method to perform PyGTK actions such as attaching C{gtk.Widgets} to the application. If the GTKInterface has not yet been initialized, then it will return C{None}.

		@return: The main gtk.Layout.
		@rtype: L{GTKLayout} (or C{None})
		"""
		if self._gtkInterface is not None:
			return self._gtkInterface.getGTKLayout()
		else:
			return None

	gtkLayout = property(getGTKLayout, doc="The application's main gtk.Layout.")
#}


#{ Scene methods.
	def getRunningScene(self):
		"""
		Returns the L{Scene} which is currently running or C{None} if there is not a scene currently running.

		@return: The running Scene.
		@rtype: L{Scene} (or C{None})
		"""
		return self._runningScene

	runningScene = property(getRunningScene, doc="The currently-running scene.")

	def runWithScene(self, scene):
		"""
		Starts the application with a L{Scene}. This starts the main run loop.

		@param scene: The opening scene.
		@type scene: L{Scene}
		"""

		if self._runningScene:
			warnings.warn("Scene is already running. Use replaceScene or pushScene instead.")
			return

		if self._gtkInterface is None:
			self.setWindow()

		self.pushScene(scene)
		self._startAnimation()

	def replaceScene(self, scene):
		"""
		Replaces the currently running L{Scene} with a new Scene.

		@param scene: The new Scene.
		@type scene: L{Scene}
		"""
		scene.setRect(Rect(Point(0,0), self._gtkInterface.getSize()))
		scene._setDirector(self)
		index = len(self._scenesStack)-1
		self._scenesStack[index] = scene
		self._nextScene = scene

	def pushScene(self, scene):
		"""
		Pushes a new L{Scene} onto the stack of Scenes.

		@param scene: The new Scene.
		@type scene: L{Scene}
		"""
		scene.setRect(Rect(Point(0,0), self._gtkInterface.getSize()))
		scene._setDirector(self)
		self._scenesStack.append(scene)
		self._nextScene = scene

	def popScene(self):
		"""
		Pops the most recently-pushed L{Scene} off the stack of Scenes.
		"""
		scene = self._scenesStack.pop()
		count = len(self._scenesStack)
		if count == 0:
			self.end()
		else:
			self._nextScene = self._scenesStack[count-1]
#}


#{ Run methods.
	def end(self):
		"""
		Ends all animations, unregisters all L{GestureDispatch} listeners, and clears out the stack of L{Scene}C{s}. This does not end the program by itself, however.
		"""
		self._runningScene.onExit()
		self._runningScene.cleanup()
		self._scenesStack = []
		self._gestureDispatch.removeAllListeners()
		self._stopAnimation()

	def _setNextScene(self):
		"""
		Private method which handles setting the next L{Scene}. This should not normally be called manually.
		"""
		isTransitionRunning = isinstance(self._runningScene, AbstractTransition)
		isTransitionNext = isinstance(self._nextScene, AbstractTransition)
		if isTransitionNext is not True and self._runningScene is not None:
			self._runningScene.onExit()
		self._runningScene = self._nextScene
		self._nextScene = None
		if isTransitionRunning is not True:
			self._runningScene.onEnter()
			self._runningScene.onEnterFromFinishedTransition()

	def getFramerate(self):
		"""
		Returns the animation interval. Default is C{30.0} FPS.

		@return: Animation interval.
		@rtype: C{float}
		"""
		return self._framerate

	def setFramerate(self, framerate):
		"""
		Sets the animation interval, that is, the frames per second for the application. Note that this must be set before L{runWithScene} is called, otherwise this method will have no effect.

		@param framerate: The animation interval (FPS).
		@type framerate: C{float}
		"""
		self._framerate = framerate

	framerate = property(getFramerate, setFramerate, doc="The application's framerate.")

	def pause(self):
		"""
		Pauses the application.
		"""
		if self._isPaused:
			return
		self._oldFramerate = self._framerate
		self.setFramerate(1.0/4.0)
		self._isPaused = True

	def resume(self):
		"""
		Resumes the application.
		"""
		if not self._isPaused:
			return
		self.setFramerate(self._oldFramerate)
		self._isPaused = False
		self._dt = 0
#}


#{ Private methods.
	def _startAnimation(self):
		"""
		Private method that calls L{_preMainLoop} to begin the main loop.
		"""
		self._isRunning = True

		self._preMainLoop()
		self._gtkInterface.start()

	def _preMainLoop(self):
		"""
		Private method that sets up the L{_mainLoop} method to be called repeatedly.
		"""
		interval = self._framerate*1000
		interval = int(interval)
		gobject.timeout_add(interval, self._mainLoop)

	def _mainLoop(self):
		"""
		Private method which is called repeatedly to redraw the L{Node}C{s} and to update the L{Scheduler} with the time that has passed since the last loop.
		"""
		self._calculateDeltaTime()
		if not self._isPaused:
			if not self._isRecording:
				self._scheduler.tick(self._dt)
			else:
				self._scheduler.tick(self._framerate)
		if self._nextScene is not None:
			self._setNextScene()
		self._gtkInterface.redraw()	# This is not guaranteed to redraw within the same loop iteration as PyGTK accumulates redraw events before dispatching.
		if self._isShowingFPS is True:
			self._showFPS()
		if self._isRunning:
			return True
		else:
			return False

	def _calculateDeltaTime(self):
		"""
		Private method which calculates how much time has elapsed since the last loop iteration. This method should generally not be called manually.
		"""
		timestamp = time.time()	# now
		if self._isNextDeltaTimeZero:
			self._dt = 0
			self._isNextDeltaTimeZero = False
		#elif self._lastTimeStamp == 0.0:
		#	self._dt = 0
		else:
			self._dt = timestamp - self._lastTimeStamp
		self._lastTimeStamp = timestamp

	def _showFPS(self):
		self._frames += 1
		self._accumDt += self._dt
		if (self._accumDt > 0.1):
			self._displayedFramerate = self._frames/self._accumDt
			self._frames = 0
			self._accumDt = 0
		string = "%.1f" % self._displayedFramerate
		self._gtkInterface._layout.setFramerate(string)
		#self._fpsLabel.setText(string)

	def _stopAnimation(self):
		"""
		Private method which stops the main loop.
		"""
		self._isRunning = False
		self.stopRecording()
#}

#{ Recording methods.
	def takeScreenshot(self, imagePath):
		"""
		Takes a screenshot of the application. Note that PyGTK widgets will not be rendered to the image.

		@param imagePath: The name of the file to be saved.
		@type imagePath: C{string}
		"""
		self._gtkInterface._layout.takeScreenshot(imagePath)

	def startRecording(self, videoPath):
		"""
		Begins saving a sequence of image stills to be rendered to video.

		@param videoPath: The location where the video (and temporary image files) will be saved.
		@type videoPath: C{string}
		"""
		self._isRecording = True
		self._gtkInterface._layout.startRecording(videoPath)

	def stopRecording(self):
		"""
		Stops recording and, if FFmpeg is available, will automatically render the video. If there is already a movie file with the same name as the one given in L{startRecording}, that movie file will be deleted.
		"""
		self._isRecording = False
		self._gtkInterface._layout.stopRecording()