changeset 567:9152ed2b5bb8

Created SimpleXMLSerializer which makes loading and saving variables to an XML file simple. In the process I removed the the XML code from the Settings class. It now uses SimpleXMLSerializer to load and save settings. I have also updated the RPG demo to use the SimpleXMLSerializer for loading and saving game specific data.
author prock@33b003aa-7bff-0310-803a-e67f0ece8222
date Mon, 28 Jun 2010 18:41:23 +0000
parents 90d369c788c0
children bfbf329e1da8
files demos/rpg/scripts/quests/questmanager.py demos/rpg/scripts/scene.py engine/python/fife/extensions/fife_settings.py
diffstat 3 files changed, 35 insertions(+), 214 deletions(-) [+]
line wrap: on
line diff
--- a/demos/rpg/scripts/quests/questmanager.py	Mon Jun 28 15:43:42 2010 +0000
+++ b/demos/rpg/scripts/quests/questmanager.py	Mon Jun 28 18:41:23 2010 +0000
@@ -26,7 +26,7 @@
 
 from fife import fife
 
-from fife.extensions.fife_settings import Setting
+from fife.extensions.serializers.simplexml import SimpleXMLSerializer
 from scripts.quests.basequest import Quest, ReturnItemQuest, QuestTypes
 from scripts.misc.serializer import Serializer
 
@@ -46,7 +46,7 @@
 	def deserialize(self, valuedict=None):
 		questfile = self._gamecontroller.settings.get("RPG", "QuestFile", "maps/quests.xml")
 		
-		self._questsettings = Setting(settings_file=questfile)
+		self._questsettings = SimpleXMLSerializer(questfile)
 		
 		for identifier in self._questsettings.get("QuestGivers", "list", []):
 			for quest in self._questsettings.get(identifier, "questlist", []):
--- a/demos/rpg/scripts/scene.py	Mon Jun 28 15:43:42 2010 +0000
+++ b/demos/rpg/scripts/scene.py	Mon Jun 28 18:41:23 2010 +0000
@@ -28,7 +28,7 @@
 from fife import fife
 from fife.extensions.loaders import loadMapFile
 from fife.extensions.loaders import loadImportFile
-from fife.extensions.fife_settings import Setting
+from fife.extensions.serializers.simplexml import SimpleXMLSerializer
 
 from scripts.actors.baseactor import Actor
 from scripts.actors.questgiver import QuestGiver
@@ -120,7 +120,7 @@
 		playerfilename = os.path.join("saves", "player_save.xml")
 		
 		if os.path.isfile(playerfilename):
-			player_settings = Setting(settings_file=playerfilename, copy_dist=False)
+			player_settings = SimpleXMLSerializer(playerfilename)
 			pvals = player_settings.get("player", "player", {})
 			self._player.deserialize(pvals)
 
@@ -142,8 +142,8 @@
 		modelfile = self._gamecontroller.settings.get("RPG", "AllObjectFile", "maps/allobjects.xml")
 		questfile = self._gamecontroller.settings.get("RPG", "QuestFile", "maps/quests.xml")
 		
-		self._objectsettings = Setting(settings_file=objectfile)
-		self._modelsettings = Setting(settings_file=modelfile)
+		self._objectsettings = SimpleXMLSerializer(objectfile)
+		self._modelsettings = SimpleXMLSerializer(modelfile)
 
 		for cam in self._map.getCameras():
 			self._cameras[cam.getId()] = cam
@@ -236,8 +236,8 @@
 	def serialize(self):
 		filename = os.path.join("saves", self._mapname + "_save.xml")
 		playerfilename = os.path.join("saves", "player_save.xml")
-		map_settings = Setting(settings_file=filename, copy_dist=False)
-		player_settings = Setting(settings_file=playerfilename, copy_dist=False)
+		map_settings = SimpleXMLSerializer(filename)
+		player_settings = SimpleXMLSerializer(playerfilename)
 		
 		objectlist = []
 		
@@ -251,8 +251,8 @@
 		pvals = self._player.serialize()
 		player_settings.set("player", "player", pvals)
 		
-		map_settings.saveSettings()
-		player_settings.saveSettings()
+		map_settings.save()
+		player_settings.save()
 		
 	def deserialize(self):
 		if self._mapname:
--- a/engine/python/fife/extensions/fife_settings.py	Mon Jun 28 15:43:42 2010 +0000
+++ b/engine/python/fife/extensions/fife_settings.py	Mon Jun 28 18:41:23 2010 +0000
@@ -38,12 +38,13 @@
 
 from fife.extensions import pychan
 from fife.extensions.fife_utils import getUserDataDirectory
+from fife.extensions.serializers.simplexml import SimpleXMLSerializer
+
 try:
 	import xml.etree.cElementTree as ET
 except:
 	import xml.etree.ElementTree as ET
 
-
 SETTINGS_GUI_XML="""\
 <Window name="Settings" title="Settings">
 	<Label text="Settings menu!" />
@@ -78,13 +79,6 @@
 </Window>
 """
 
-EMPTY_SETTINGS="""\
-<?xml version='1.0' encoding='UTF-8'?>
-<Settings>
-
-</Settings>
-"""
-
 FIFE_MODULE = "FIFE"
 
 class Setting(object):
@@ -123,6 +117,8 @@
 
 		# Holds SettingEntries
 		self._entries = {}
+		
+		self._xmlserializer = None
 
 		if self._settings_file == "":
 			self._settings_file = "settings.xml"
@@ -142,10 +138,6 @@
 		if not os.path.exists(os.path.join(self._appdata, self._settings_file)):
 			if os.path.exists('settings-dist.xml') and copy_dist:
 				shutil.copyfile('settings-dist.xml', os.path.join(self._appdata, self._settings_file))
-			else:
-				#no settings file found
-				tree = ET.parse(StringIO(EMPTY_SETTINGS))
-				tree.write(os.path.join(self._appdata, self._settings_file), 'UTF-8')
 
 		#default settings
 		self._resolutions = ['640x480', '800x600', '1024x768', '1280x800', '1440x900']
@@ -154,9 +146,9 @@
 		#Used to stylize the options gui
 		self._gui_style = "default"
 
+		#Initialize the XML serializer
 		self.loadSettings()
 
-
 		self._initDefaultSettingEntries()
 
 	def _initDefaultSettingEntries(self):
@@ -212,51 +204,12 @@
 			print "It's probably missing in settings-dist.xml as well!"
 
 	def loadSettings(self):
-		self._tree = ET.parse(os.path.join(self._appdata, self._settings_file))
-
-		self._root_element = self._tree.getroot()
-		self.validateTree()
-
-	def setGuiStyle(self, style):
-		""" Set a custom gui style used for the option dialog.
-		@param style: Pychan style to be used
-		@type style: C{string}
-		"""
-		self._gui_style = style
+		self._xmlserializer = SimpleXMLSerializer(os.path.join(self._appdata, self._settings_file))
 
-	def validateTree(self):
-		""" Iterates the settings tree and prints warning when an invalid tag is found """
-		for c in self._root_element.getchildren():
-			if c.tag != "Module":
-				print "Invalid tag in settings.xml. Expected Module, got: ", c.tag
-			elif c.get("name", "") == "":
-				print "Invalid tag in settings.xml. Module name is empty."
-			else:
-				for e in c.getchildren():
-					if e.tag != "Setting":
-						print "Invalid tag in settings.xml in module: ",c.tag,
-						print ". Expected Setting, got: ", e.tag
-					elif c.get("name", "") == "":
-						print "Invalid tag in settings.xml in module: ",c.tag,
-						print ". Setting name is empty", e.tag
-
-	def getModuleTree(self, module):
-		"""
-		Returns a module element from the settings tree. If no module with the specified
-		name exists, a new element will be created.
-
-		@param module: The module to get from the settings tree
-		@type module: C{string}
-		"""
-		if not isinstance(module, str) and not isinstance(module, unicode):
-			raise AttributeError("Settings:getModuleTree: Invalid type for module argument.")
-
-		for c in self._root_element.getchildren():
-			if c.tag == "Module" and c.get("name", "") == module:
-				return c
-
-		# Create module
-		return ET.SubElement(self._root_element, "Module", {"name":module})
+	def saveSettings(self):
+		""" Writes the settings to the settings file """
+		if self._xmlserializer:
+			self._xmlserializer.save()
 
 	def get(self, module, name, defaultValue=None):
 		""" Gets the value of a specified setting
@@ -266,55 +219,11 @@
 		@param defaultValue: Specifies the default value to return if the setting is not found
 		@type defaultValue: C{str} or C{unicode} or C{int} or C{float} or C{bool} or C{list} or C{dict}
 		"""
-		if not isinstance(name, str) and not isinstance(name, unicode):
-			raise AttributeError("Settings:get: Invalid type for name argument.")
-
-		moduleTree = self.getModuleTree(module)
-		element = None
-		for e in moduleTree.getchildren():
-			if e.tag == "Setting" and e.get("name", "") == name:
-				element = e
-				break
+		if self._xmlserializer:
+			return self._xmlserializer.get(module, name, defaultValue)
 		else:
-			return defaultValue
-
-		e_value = element.text
-		e_strip = element.get("strip", "1").strip().lower()
-		e_type	= str(element.get("type", "str")).strip()
-
-		if e_value is None:
-			return defaultValue
-
-		# Strip value
-		if e_strip == "" or e_strip == "false" or e_strip == "no" or e_strip == "0":
-			e_strip = False
-		else: e_strip = True
-
-		if e_type == "str" or e_type == "unicode":
-			if e_strip: e_value = e_value.strip()
-		else:
-			e_value = e_value.strip()
-
-		# Return value
-		if e_type == 'int':
-			return int(e_value)
-		elif e_type == 'float':
-			return float(e_value)
-		elif e_type == 'bool':
-			e_value = e_value.lower()
-			if e_value == "" or e_value == "false" or e_value == "no" or e_value == "0":
-				return False
-			else:
-				return True
-		elif e_type == 'str':
-			return str(e_value)
-		elif e_type == 'unicode':
-			return unicode(e_value)
-		elif e_type == 'list':
-			return self._deserializeList(e_value)
-		elif e_type == 'dict':
-			return self._deserializeDict(e_value)
-
+			return None
+	
 	def set(self, module, name, value, extra_attrs={}):
 		"""
 		Sets a setting to specified value.
@@ -326,103 +235,16 @@
 		@param extra_attrs: Extra attributes to be stored in the XML-file
 		@type extra_attrs: C{dict}
 		"""
-		if not isinstance(name, str) and not isinstance(name, unicode):
-			raise AttributeError("Settings:set: Invalid type for name argument.")
-
-		moduleTree = self.getModuleTree(module)
-		e_type = "str"
+		if self._xmlserializer:
+			self._xmlserializer.set(module, name, value, extra_attrs)
 
-		if isinstance(value, bool): # This must be before int
-			e_type = "bool"
-			value = str(value)
-		elif isinstance(value, int):
-			e_type = "int"
-			value = str(value)
-		elif isinstance(value, float):
-			e_type = "float"
-			value = str(value)
-		elif isinstance(value, unicode):
-			e_type = "unicode"
-			value = unicode(value)
-		elif isinstance(value, list):
-			e_type = "list"
-			value = self._serializeList(value)
-		elif isinstance(value, dict):
-			e_type = "dict"
-			value = self._serializeDict(value)
-		else:
-			e_type = "str"
-			value = str(value)
-
-		for e in moduleTree.getchildren():
-			if e.tag != "Setting": continue
-			if e.get("name", "") == name:
-				e.text = value
-				break
-		else:
-			attrs = {"name":name, "type":e_type}
-			for k in extra_attrs:
-				if k not in attrs:
-					attrs[k] = extra_args[k]
-			elm = ET.SubElement(moduleTree, "Setting", attrs)
-			elm.text = value
-
-	def saveSettings(self):
-		""" Writes the settings to the settings file """
-		self._indent(self._root_element)
-		self._tree.write(os.path.join(self._appdata, self._settings_file), 'UTF-8')
-
-	def _indent(self, elem, level=0):
+	def setGuiStyle(self, style):
+		""" Set a custom gui style used for the option dialog.
+		@param style: Pychan style to be used
+		@type style: C{string}
 		"""
-		Adds whitespace, so the resulting XML-file is properly indented.
-		Shamelessly stolen from http://effbot.org/zone/element-lib.htm
-		"""
-		i = "\n" + level*"  "
-		if len(elem):
-			if not elem.text or not elem.text.strip():
-				elem.text = i + "  "
-			if not elem.tail or not elem.tail.strip():
-				elem.tail = i
-			for elem in elem:
-				self._indent(elem, level+1)
-			if not elem.tail or not elem.tail.strip():
-				elem.tail = i
-		else:
-			if level and (not elem.tail or not elem.tail.strip()):
-				elem.tail = i
-
-	# FIXME:
-	# These serialization functions are not reliable at all
-	# This will only serialize the first level of a dict or list
-	# It will not check the types nor the content for conflicts.
-	# Perhaps we should add a small serialization library?
-	def _serializeList(self, list):
-		""" Serializes a list, so it can be stored in a text file """
-		return " ; ".join(list)
-
-	def _deserializeList(self, string):
-		""" Deserializes a list back into a list object """
-		return string.split(" ; ")
-
-	def _serializeDict(self, dict):
-		""" Serializes a list, so it can be stored in a text file """
-		serial = ""
-		for key in dict:
-			value = dict[key]
-			if serial != "": serial += " ; "
-			serial += str(key)+" : "+str(value)
-
-		return serial
-
-	def _deserializeDict(self, serial):
-		""" Deserializes a list back into a dict object """
-		dict = {}
-		items = serial.split(" ; ")
-		for i in items:
-			kv_pair = i.split(" : ")
-			dict[kv_pair[0]] = kv_pair[1]
-		return dict
-
+		self._gui_style = style
+		
 	def onOptionsPress(self):
 		"""
 		Opens the options dialog box.  Usually you would bind this to a button.
@@ -447,7 +269,6 @@
 		else:
 			return pychan.loadXML(StringIO(dialog))
 
-
 	def fillWidgets(self):
 		for module in self._entries.itervalues():
 			for entry in module.itervalues():
@@ -518,10 +339,10 @@
 		shutil.copyfile('settings-dist.xml', os.path.join(self._appdata, self._settings_file))
 		self.changesRequireRestart = True
 		self.loadSettings()
-		self._showChangeRequireRestartDialog()
+		#self._showChangeRequireRestartDialog()
 
-		if self.OptionsDlg:
-			self.OptionsDlg.hide()
+		#if self.OptionsDlg:
+		#	self.OptionsDlg.hide()
 
 	def _getEntries(self):
 		return self._entries