コード例 #1
0
ファイル: Slide.py プロジェクト: AlexRArmstrong/Optichart
class Slide(object):
	'''
	The text of a chart layed out and rendered.
	'''
	def __init__(self, chart_name = None):
		'''
		Initilize a Slide Class. Call Slide([chart-name]) to create a Slide
		object.  Optionally passing a fully pathed chart name.
		'''
		# These variables are created in Slide.
		# The chart object to display.
		self._chart = None
		self._chart_name = ''
		if chart_name:
			self.setChartName(chart_name)
		# The coordinates of pages breaks.
		self._pages = [[0, 0]]
		# TODO: def chrs. ???
		self._default_characters = []
		# The text of the chart layed out and rendered.
		self._surface = None
		
		# These variables must be set before calling slide.layout()
		# The width and height of the slide display surface.
		self._width = 0 # Pixels
		self._height = 0 # Pixels
		# Fonts.
		self._default_font = ''
		self._font_dir = ''
		# Factors for size calculations.
		self._dpi = 72 # dots/inch
		self._lane_length = 240 # inches
		
		
	def chart(self):
		'''
		Returns a chart object.
		'''
		return self._chart
	
	def chartName(self):
		'''
		Return the current chart name.
		'''
		return self._chart_name
	
	def setChartName(self, chart_name):
		'''
		Takes the path to a chart file and creates a chart object. Additionally
		sets the chart name variable.
		'''
		try:
			self._chart_name = chart_name
			self._chart = Chart(chart_name)
			return True
		except:
			return False
	
	def pageCoordinates(self):
		'''
		Return a list of the (x,y) coordinates of the page breakes.
		'''
		return self._pages
	
	def defaultCharacters(self):
		'''
		Return a list of the default characters coordinates and size information.
		The line scale factor (ie. 1.25 - the Snellen ratio divided out). Also 
		included is the width of a character and the space between characters on that line.
		Data Structure:
		list = [[x, y, line scale factor, character width, space width],
				[x, y, scale factor, chr width, space width]]
		'''
		return self._default_characters
	
	def surface(self):
		'''
		Returns the rendered surface.
		'''
		return self._surface
		# TODO: Maybe calling layout() should return the rendered surface?
	
	def slideWidth(self):
		'''
		Returns the slide width in pixels.
		'''
		return self._width
	
	def setSlideWidth(self, w):
		'''
		Set the width for the slide to w. This is the
		slide's display area in inches.  If using a custom dpi,
		Must call setDpi first - else will have the default 72 dpi.
		Returns True on succes, False otherwise.
		'''
		try:
			w_px = w * self._dpi
			self._width = int(w_px)
			return True
		except:
			return False
	
	def slideHeight(self):
		'''
		Returns the height of the slide in pixels.
		'''
		return self._height
	
	def setSlideHeight(self, h):
		'''
		Set the height of the slides display area to h.
		Takes the height in inches to set.
		Returns True on succes, False otherwise.
		'''
		try:
			h_px = h * self._dpi
			self._height = int(h_px)
			return True
		except:
			return False
	def dpi(self):
		'''
		Returns the slide's current dpi.
		'''
		return self._dpi
	
	def setDpi(self, dpi):
		'''
		Set the dpi for the slide.
		Returns True on succes, False otherwise.
		'''
		try:
			self._dpi = dpi
			return True
		except:
			return False
	
	def laneLength(self):
		'''
		Return the length of the lane for this slide.
		'''
		return self._lane_length
	
	def setLaneLength(self, length):
		'''
		Set the lane length for the slide.
		Returns True on succes, False otherwise.
		'''
		try:
			self._lane_length = int(length)
			return True
		except:
			return False
	
	def defaultFont(self):
		'''
		Return the default font used by this slide.
		'''
		return self._default_font
	
	def setDefaultFont(self, font_name):
		'''
		Sets the default font to use for this slide.
		Returns True on succes, False otherwise.
		'''
		try:
			self._default_font = font_name
			return True
		except:
			return False
	
	def fontDirectory(self):
		'''
		Returns the directory searched for fonts.
		'''
		return self._font_dir
	
	def setFontDirectory(self, font_dir):
		'''
		Sets the directory containing the fonts to 'font_dir'.
		Returns True on succes, False otherwise.
		'''
		try:
			self._font_dir = font_dir
			return True
		except:
			return False
	
	def fixFontName(self, font_name):
		'''
		Takes a font name and fixes the path so it can be used with pygame.
		Returns the full path name of the font.
		'''
		import os.path
		# First check to see if we have a font specified or should use default.
		if not font_name:
			return os.path.join(self._font_dir, self._default_font)
		elif os.path.isabs(font_name):
			return font_name
		elif os.path.exists(os.path.join(self._font_dir, font_name)):
			return os.path.join(self._font_dir, font_name)
		else:
			return os.path.join(self._font_dir, self._default_font)
	
	def calculateSize(self, lane_length, scale_factor, dpi):
		'''
		Calculates the vertical size for a letter with a given scale factor at
		a distance on a specified resoulation monitor.
		'''
		# TODO: Question? Should this be here or part of a line?
		vertical_inch_size = float(lane_length) * math.tan(math.radians(5.0 / 60.0)) * float(scale_factor)
		vertical_dpi_size = vertical_inch_size * dpi
		return vertical_dpi_size	
	
	def layout(self):
		'''
		Lays out the currently set chart.
		This needs to be called before trying to display the anything.
		Does display layout and renders the text onto a big surface.  Also
		checks for pages and default characters while doing this.
		'''
#		# Clear per slide/chart variables.
		self._pages = [[0, 0]]
		self._default_characters = []
		self._surface = None
		
		all_rendered_lines = []
		all_lines = self._chart.lines()
		page_numbers = self._chart.pages()
		slide_width = self._width # the width of the slide display area from config file.
		lane_length = self._lane_length
		dpi = self._dpi
		
		for line_no, current_line in enumerate(all_lines):
			line_font = current_line.font()
			full_font_name = self.fixFontName(line_font)
			num_sections = len(current_line.sections())
			all_section_widths = current_line.columnSizes()
			sect_x = 0
			sect_y = 0
			all_rendered_sections = []
			line_height = 0
			default_chr_position = 0
			for i, each_section in enumerate(current_line.sections()):
				# Figure the current section width.
				section_width = slide_width * all_section_widths[i] / 100
				# Get the scaling factor and calculate the size in pixels
				scale_factor = each_section.scaleFactor()
				line_size = self.calculateSize(lane_length, scale_factor, dpi)
				# Create a font.
				section_font = pygame.font.Font(full_font_name, int(line_size))
				# Get the text for this section.
				text = each_section.text()
				# Calculate the spacing of the letters for this section.
				num_chrs = len(text)
				text_width, text_height = section_font.size(text)
				chr_width = text_width / num_chrs
				space_width = (section_width - text_width) / (num_chrs + 1.0)
				# Create a surface for this section.
				section_surface = pygame.Surface([section_width, text_height])
				section_surface.fill(WHITE)
				# Keep track of how big the line is - different sections will change this.
				if text_height > line_height:
					line_height = text_height
				# Render each character onto a surface at the correctly spaced position.
				x_pos = 0
				y_pos = 0
				x_pos += space_width
				for each_chr in text:
					chr_surface = section_font.render(each_chr, True, BLACK, NOTBLACK)
					chr_surface.set_colorkey(NOTBLACK)
					chr_position = [x_pos, y_pos]
					section_surface.blit(chr_surface, chr_position)
					# Check for default character position.
					if default_chr_position == current_line.defaultCharacterPosition():
						self._default_characters.append([x_pos, y_pos, scale_factor, chr_width, space_width])
					x_pos = x_pos + chr_width + space_width
					default_chr_position += 1
				all_rendered_sections.append(section_surface)
			# Now blit all the section surfaces onto a line surface.
			line_surface = pygame.Surface([slide_width, line_height])
			line_surface.fill(WHITE)
			for i, each_sect_surf in enumerate(all_rendered_sections):
				# Get the correct width for this section.
				section_width = slide_width * all_section_widths[i] / 100
				sect_y = (line_surface.get_height() - each_sect_surf.get_height()) / 2.0
				section_position = [sect_x, sect_y]
				line_surface.blit(each_sect_surf, section_position)
				sect_x += section_width
			all_rendered_lines.append(line_surface)
		
		# Find the total height of the chart.
		total_chart_heigh_px = 0
		total_chart_width_px = 0
		num_lines = range(len(all_rendered_lines))
		for i in num_lines:
			cur_ren_line = all_rendered_lines[i]
			ln_width, ln_height = cur_ren_line.get_size()
			# Calculate the space between each line - can vary per line.
			scale_factor = all_lines[i].lineSpaceingScaleFactor()
			line_spaceing = self.calculateSize(lane_length, scale_factor, dpi)
			total_chart_heigh_px = total_chart_heigh_px + ln_height + line_spaceing
				
		# Make a big surface to hold all the lines.
		#total_size = [total_chart_width_px, total_chart_heigh_px]
		total_size = [slide_width, total_chart_heigh_px]
		self._surface = pygame.Surface(total_size)
		self._surface.fill(WHITE)
		
		# Now render all the text to the big surface.
		position = [slide_width / 2, 0]
		for line_no, each_line in enumerate(all_rendered_lines):
			x_r, y_r = each_line.get_size()
			position[0] = position[0] - (x_r / 2)
			self._surface.blit(each_line, position)
			# Y for def chrs.
			self._default_characters[line_no][1] = position[1]
			# Figure out the coordinates for the page breaks.
			if line_no in page_numbers:
				x = 0
				y = position[1]
				pg_coords = [x, y]
				self._pages.append(pg_coords)
			# Find the line spaceing for this line.
			scale_factor = all_lines[line_no].lineSpaceingScaleFactor()
			line_spaceing = self.calculateSize(lane_length, scale_factor, dpi)
			# Incriment the position for the next line.
			position[0] = slide_width / 2
			position[1] += y_r + line_spaceing