Package fife :: Package extensions :: Module fife_settings
[hide private]
[frames] | no frames]

Source Code for Module fife.extensions.fife_settings

  1  # -*- coding: utf-8 -*- 
  2   
  3  # #################################################################### 
  4  #  Copyright (C) 2005-2019 by the FIFE team 
  5  #  http://www.fifengine.net 
  6  #  This file is part of FIFE. 
  7  # 
  8  #  FIFE is free software; you can redistribute it and/or 
  9  #  modify it under the terms of the GNU Lesser General Public 
 10  #  License as published by the Free Software Foundation; either 
 11  #  version 2.1 of the License, or (at your option) any later version. 
 12  # 
 13  #  This library is distributed in the hope that it will be useful, 
 14  #  but WITHOUT ANY WARRANTY; without even the implied warranty of 
 15  #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 16  #  Lesser General Public License for more details. 
 17  # 
 18  #  You should have received a copy of the GNU Lesser General Public 
 19  #  License along with this library; if not, write to the 
 20  #  Free Software Foundation, Inc., 
 21  #  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA 
 22  # #################################################################### 
 23   
 24  """ 
 25  Settings 
 26  ================================== 
 27   
 28  This module provides a nice framework for loading and saving game settings. 
 29  It is by no means complete but it does provide a good starting point. 
 30  """ 
 31   
 32  from future import standard_library 
 33  standard_library.install_aliases() 
 34  from builtins import str 
 35  from builtins import range 
 36  from builtins import object 
 37  import shutil 
 38  import os 
 39  from io import StringIO 
 40   
 41  from fife.extensions import fifelog 
 42  from fife.extensions.fife_utils import getUserDataDirectory 
 43  from fife.extensions.serializers.simplexml import SimpleXMLSerializer 
 44   
 45  FIFE_MODULE = "FIFE" 
 46   
47 -class Setting(object):
48 """ 49 This class manages loading and saving of game settings. 50 51 Usage:: 52 from fife.extensions.fife_settings import Setting 53 settings = Setting(app_name="myapp") 54 screen_width = settings.get("FIFE", "ScreenWidth", 1024) 55 screen_height = settings.get("FIFE", "ScreenHeight", 768) 56 """ 57
58 - def __init__(self, app_name="", settings_file="", default_settings_file= "settings-dist.xml", copy_dist=True, serializer=None):
59 r""" 60 Initializes the Setting object. 61 62 @param app_name: The applications name. If this parameter is provided 63 alone it will try to read the settings file from the users home directory. 64 In windows this will be something like: C:\Documents and Settings\user\Application Data\fife 65 @type app_name: C{string} 66 @param settings_file: The name of the settings file. If this parameter is 67 provided it will look for the setting file as you specify it, first looking 68 in the working directory. It will NOT look in the users home directory. 69 @type settings_file: C{string} 70 @param default_settings_file: The name of the default settings file. If the settings_file 71 does not exist this file will be copied into the place of the settings_file. This file 72 must exist in the root directory of your project! 73 @type default_settings_file: C{string} 74 @param copy_dist: Copies the default settings file to the settings_file location. If 75 this is False it will create a new empty setting file. 76 @param serializer: Overrides the default XML serializer 77 @type serializer: C{SimpleSerializer} 78 79 """ 80 self._app_name = app_name 81 self._settings_file = settings_file 82 self._default_settings_file = default_settings_file 83 84 # Holds SettingEntries 85 self._entries = {} 86 87 if self._settings_file == "": 88 self._settings_file = "settings.xml" 89 self._appdata = getUserDataDirectory("fife", self._app_name) 90 else: 91 self._appdata = os.path.dirname(self._settings_file) 92 self._settings_file = os.path.basename(self._settings_file) 93 94 if not os.path.exists(os.path.join(self._appdata, self._settings_file)): 95 if os.path.exists(self._default_settings_file) and copy_dist: 96 shutil.copyfile(self._default_settings_file, os.path.join(self._appdata, self._settings_file)) 97 98 # valid values possible for the engineSettings 99 self._validSetting = {} 100 self._validSetting['FIFE'] = { 101 'FullScreen':[True,False], 'RefreshRate':[0,200], 'Display':[0,9], 'VSync':[True,False], 'PychanDebug':[True,False] 102 , 'ProfilingOn':[True,False], 'SDLRemoveFakeAlpha':[True,False], 'GLCompressImages':[False,True], 'GLUseFramebuffer':[False,True], 'GLUseNPOT':[False,True], 103 'GLUseMipmapping':[False,True], 'GLTextureFiltering':['None', 'Bilinear', 'Trilinear', 'Anisotropic'], 'GLUseMonochrome':[False,True], 104 'GLUseDepthBuffer':[False,True], 'GLAlphaTestValue':[0.0,1.0], 105 'RenderBackend':['OpenGL', 'SDL'], 106 'ScreenResolution':['640x480', '800x600', '1024x600', '1024x768', '1280x768', 107 '1280x800', '1280x960', '1280x1024', '1366x768', '1440x900', 108 '1600x900', '1600x1200', '1680x1050', '1920x1080', '1920x1200'], 109 'BitsPerPixel':[0,16,24,32], 110 'InitialVolume':[0.0,10.0], 'WindowTitle':"", 'WindowIcon':"", 'Font':"", 111 'FontGlyphs':"", 'DefaultFontSize':"", 'Lighting':[0,1], 112 'ColorKeyEnabled':[True,False], 'ColorKey':['a','b','c'], 'VideoDriver':"", 'RenderDriver':"", 113 'PlaySounds':[True,False], 'LogToFile':[True,False], 114 'LogToPrompt':[True,False], 'LogLevelFilter':[0,1,2,3], 115 'LogModules':['all', 'controller','script','video','audio','loaders','vfs','pool','view','model','metamodel','event_channel','xml'], 116 'FrameLimitEnabled':[True,False], 'FrameLimit':[0], 'MouseSensitivity':[0.0], 'MouseAcceleration':[True,False], 117 'NativeImageCursor':[True,False], 'JoystickSupport':[True, False] 118 } 119 120 glyphDft = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,!?-+/():;%&`'*#=[]\\\"" 121 122 # we at this point assume default values are final values for engineSettings 123 self._defaultSetting = {} 124 self._defaultSetting['FIFE'] = { 125 'FullScreen':False, 'RefreshRate':60, 'Display':0, 'VSync':False, 'PychanDebug':False, 126 'ProfilingOn':False, 'SDLRemoveFakeAlpha':False, 'GLCompressImages':False, 'GLUseFramebuffer':True, 'GLUseNPOT':True, 127 'GLUseMipmapping':False, 'GLTextureFiltering':'None', 'GLUseMonochrome':False, 'GLUseDepthBuffer':False, 'GLAlphaTestValue':0.3, 128 'RenderBackend':'OpenGL', 'ScreenResolution':"1024x768", 'BitsPerPixel':0, 129 'InitialVolume':5.0, 'WindowTitle':"", 'WindowIcon':"", 'Font':"", 130 'FontGlyphs':glyphDft, 'DefaultFontSize':12, 'Lighting':0, 131 'ColorKeyEnabled':False, 'ColorKey':[255,0,255], 'VideoDriver':"", 'RenderDriver':"", 132 'PlaySounds':True, 'LogToFile':False, 133 'LogToPrompt':False,'LogLevelFilter':0, 134 'LogModules':['controller','script'], 135 'FrameLimitEnabled':False, 'FrameLimit':60, 136 'MouseSensitivity':0.0, 137 'MouseAcceleration':False, 138 'NativeImageCursor':False, 139 'JoystickSupport':False 140 } 141 142 # has the settings file been read 143 self._readSettingsCompleted = {} 144 145 # the global dictionary from which we will read after self._readSettingsCompleted is True 146 self._settingsFromFile = {} 147 148 # the logger needed to write in log file. It will be initialized in this file when self.getSettings() 149 # method is called by logger 150 self._logger = None 151 152 #default settings 153 self._resolutions = self._validSetting['FIFE']['ScreenResolution'] 154 self._renderbackends = self._validSetting['FIFE']['RenderBackend'] 155 self._lightingmodels = self._validSetting['FIFE']['Lighting'] 156 157 #Used to stylize the options gui 158 self._gui_style = "default" 159 160 #Initialize the serializer 161 if serializer: 162 self._serializer = serializer 163 else: 164 self._serializer = SimpleXMLSerializer() 165 166 self.initSerializer() 167 168 # if there's no FIFE module assume the settings file is broken 169 # and replace with default settings file 170 if "FIFE" not in self._serializer.getModuleNameList(): 171 self.setDefaults() 172 173 # Get all modules and initialize reading of them from xml file as false 174 self._allModules = self._serializer.getModuleNameList() 175 # print("All Module Names:",self._allModules) 176 for module in self._allModules: 177 self._readSettingsCompleted[module] = False 178 179 self._initDefaultSettingEntries()
180 181 #self.setOneSetting('FIFE','Font','fonts/FreeSans.ttf',False) 182 183 #print self.getSettingsFromFile('unknownhorizons') 184 185 # set all Settings in either validSetting or defaultSetting
186 - def setAllSettings(self,module,settings,validSetting = True):
187 if validSetting: 188 self._validSetting[module] = settings 189 else: 190 self._defaultSetting[module] = settings
191 192 # set an entry in the validSetting or defaultSetting dictionary
193 - def setOneSetting(self,module,name,value,validSetting = True):
194 if validSetting: 195 self._validSetting[module][name] = value 196 else: 197 self._defaultSetting[module][name] = value
198 199 # get all the Settings(either validSetting or defaultSetting)
200 - def getAllSettings(self,module,validSetting = True):
201 if validSetting: 202 return self._validSetting[module] 203 else: 204 return self._defaultSetting[module]
205 206 # get an entry from either validSetting or defaultSetting
207 - def getOneSetting(self,module,name,validSetting = True):
208 if validSetting: 209 return self._validSetting[module][name] 210 else: 211 return self._defaultSetting[module][name]
212 213 # sets valid resolution options in the settings->Resolution
214 - def setValidResolutions(self, options):
215 if options: 216 self._resolutions = options 217 self.createAndAddEntry(FIFE_MODULE, "ScreenResolution", initialdata = self._resolutions, 218 requiresrestart=True)
219
220 - def initSerializer(self):
221 self._serializer.load(os.path.join(self._appdata, self._settings_file))
222
224 """Initializes the default fife setting entries. Not to be called from 225 outside this class.""" 226 self.createAndAddEntry(FIFE_MODULE, "PlaySounds", requiresrestart=True) 227 228 self.createAndAddEntry(FIFE_MODULE, "FullScreen", requiresrestart=True) 229 230 self.createAndAddEntry(FIFE_MODULE, "ScreenResolution", initialdata = self._resolutions, requiresrestart=True) 231 232 self.createAndAddEntry(FIFE_MODULE, "RenderBackend", initialdata = self._renderbackends, requiresrestart=True)
233
234 - def createAndAddEntry(self, module, name, applyfunction=None, initialdata=None, requiresrestart=False):
235 """" 236 @param module: The Setting module this Entry belongs to 237 @type module: C{String} 238 @param name: The Setting's name 239 @type name: C{String} 240 @param applyfunction: function that makes the changes when the Setting is 241 saved 242 @type applyfunction: C{function} 243 @param initialdata: If the widget supports the setInitialData() function 244 this can be used to set the initial data 245 @type initialdata: C{String} or C{Boolean} 246 @param requiresrestart: Whether or not the changing of this setting 247 requires a restart 248 @type requiresrestart: C{Boolean} 249 """ 250 entry = SettingEntry(module, name, applyfunction, initialdata, requiresrestart) 251 self.addEntry(entry)
252
253 - def addEntry(self, entry):
254 """Adds a new C{SettingEntry} to the Settting 255 @param entry: A new SettingEntry that is to be added 256 @type entry: C{SettingEntry} 257 """ 258 if entry.module not in self._entries: 259 self._entries[entry.module] = {} 260 self._entries[entry.module][entry.name] = entry 261 262 """ 263 # Make sure the new entry is available 264 if self.get(entry.module, entry.name) is None: 265 print "Updating", self._settings_file, "to the default, it is missing the entry:"\ 266 , entry.name ,"for module", entry.module 267 268 #self.setDefaults() 269 if self.get(entry.module, entry.name) is None: 270 print "WARNING:", entry.module, ":", entry.name, "still not found!" 271 """
272
273 - def saveSettings(self, filename=""):
274 """ Writes the settings to the settings file 275 276 @param filename: Specifies the file to save the settings to. If it is not specified 277 the original settings file is used. 278 @type filename: C{string} 279 """ 280 if self._serializer: 281 if filename == "": 282 self._serializer.save(os.path.join(self._appdata, self._settings_file)) 283 else: 284 self._serializer.save(filename)
285 286 # get all the settings of a module name module
287 - def getSettingsFromFile(self, module, logger=None):
288 if self._serializer: 289 290 self._logger = logger 291 modules = self._serializer.getModuleNameList() 292 self._settingsFromFile[module] = self._serializer.getAllSettings(module) 293 294 if self._logger: 295 self._logger.log_log("Loading Settings From File ...") 296 297 if self._settingsFromFile[module] is not None: 298 self._readSettingsCompleted[module] = True 299 300 # we need validation for the module FIFE only 301 if module != "FIFE": 302 return self._settingsFromFile[module] 303 """ 304 Now we have all the settings we needed. We have to validate the settings. Applicable for module 305 FIFE only 306 """ 307 for name in self._settingsFromFile[module]: 308 # if the setting name is known, so that it is 309 # both in self._settingsFromFile and validSetting 310 if name in self._validSetting[module]: 311 312 e_value = self._settingsFromFile[module][name] 313 314 if name == "InitialVolume": 315 if e_value >= self._validSetting[module][name][0] and e_value <= self._validSetting[module][name][1]: 316 self._settingsFromFile[module][name] = e_value 317 else: 318 if self._logger: 319 self._logger.log_log("InitalVolume must have a value between 0.0 and 10.0") 320 321 elif name == "GLAlphaTestValue": 322 if e_value >= self._validSetting[module][name][0] and e_value <= self._validSetting[module][name][1]: 323 self._settingsFromFile[module][name] = e_value 324 else: 325 if self._logger: 326 self._logger.log_log("GLAlphaTestValue must have a value between 0.0 and 1.0") 327 328 elif name == "ColorKey": 329 e_value = e_value.split(',') 330 if int(e_value[0]) in range(0,256) and int(e_value[1]) in range(0,256) and int(e_value[2]) in range(0,256): 331 self._settingsFromFile[name] = [int(e_value[0]),int(e_value[1]),int(e_value[2])]; 332 333 else: 334 if self._logger: 335 self._logger.log_log("ColorKey values must be within 0 and 255. Setting to Default Value.") 336 337 elif name == "ScreenResolution": 338 temp = e_value.split('x') 339 if len(temp) == 2: 340 self._settingsFromFile[module][name]=e_value 341 else: 342 if self._logger: 343 self._logger.log_log("Invalid Screen Resolution value. We expect two integer separated by x") 344 345 elif len(self._validSetting[module][name]) == 0: 346 self._settingsFromFile[module][name] = e_value 347 348 elif name == "LogModules": 349 for checking_element in e_value: 350 module_valid = False 351 for base_element in self._validSetting[module][name]: 352 # checking_element is valid 353 354 if checking_element == base_element: 355 module_valid = True 356 already_in = False 357 for element in self._settingsFromFile[module][name]: 358 if element == checking_element: 359 already_in = True 360 if already_in == False: 361 self._settingsFromFile[module][name].append(checking_element) 362 if module_valid == False: 363 if self._logger: 364 self._logger.log_log(checking_element +" is not a valid logModule") 365 elif name == "FrameLimit": 366 if e_value > 0: 367 self._settingsFromFile[module][name] = e_value 368 else: 369 if self._logger: 370 self._logger.log_log(e_value + " is not a valid FrameLimit setting. You must specify a positive integer!") 371 elif name == "MouseSensitivity": 372 self._settingsFromFile[module][name] = e_value 373 elif name == "MouseAcceleration": 374 self._settingsFromFile[module][name] = e_value 375 elif name == "NativeImageCursor": 376 self._settingsFromFile[module][name] = e_value 377 elif name == "JoystickSupport": 378 self._settingsFromFile[module][name] = e_value 379 380 elif name in ("SDLRemoveFakeAlpha", "LogToPrompt", "LogToFile"): 381 if type(e_value) == int: 382 try: 383 e_value = (False, True)[e_value] 384 except IndexError: 385 self._logger.log_warn("Invalid int-value for %s. Defaulted to False!"%name) 386 e_value = False 387 self._logger.log_warn("Use of type int for %s is deprecated. Use bool instead!"%name) 388 self._settingsFromFile[module][name] = e_value 389 390 else: 391 392 if isinstance(self._settingsFromFile[module][name],list) == True or isinstance(self._settingsFromFile[module][name],dict) == True: 393 valid = False 394 for value in self._validSetting[module][name]: 395 if value == e_value: 396 valid = True 397 self._settingsFromFile[module][name] = e_value; 398 if valid == False: 399 if self._logger: 400 self._logger.log_log("Setting " + name + " got invalid value. Setting to Default.") 401 else: self._settingsFromFile[module][name] = e_value 402 403 # name is unknown 404 else: 405 if self._logger: 406 self._logger.log_log("Setting "+ name + " is unknown") 407 408 if self._logger: 409 self._logger.log_log("Settings Loaded ...") 410 411 """ 412 Upto this point we have validated all the settings that are in settings.xml file. But, what if a setting is valid and still it is 413 not present in the settings.xml file. For this, we should give them the default Values that are in defaultSetting. 414 """ 415 416 for name in self._defaultSetting[module]: 417 if name not in self._settingsFromFile[module]: 418 self._settingsFromFile[module][name] = self._defaultSetting[module][name] 419 420 return self._settingsFromFile[module] 421 422 else: 423 return None
424
425 - def get(self, module, name, defaultValue=None):
426 """ Gets the value of a specified setting 427 428 @param module: Name of the module to get the setting from 429 @param name: Setting name 430 @param defaultValue: Specifies the default value to return if the setting is not found 431 @type defaultValue: C{str} or C{unicode} or C{int} or C{float} or C{bool} or C{list} or C{dict} 432 """ 433 434 if self._serializer: 435 if module == "FIFE": 436 # check whether getAllSettings has been called already 437 if self._readSettingsCompleted[module] is not True: 438 value = self._serializer.get(module, name, defaultValue) 439 440 if value is not None: 441 return value 442 else: 443 if name in self._defaultSetting[module]: 444 return self._defaultSetting[module][name] 445 else: 446 raise Exception(str(name) + ' is neither in settings.xml nor it has a default value set') 447 else: 448 if name in self._settingsFromFile[module]: 449 return self._settingsFromFile[module][name] 450 else: 451 raise Exception(str(name) + ' is neither in settings.xml nor it has a default value set') 452 else: 453 return self._serializer.get(module, name, defaultValue) 454 else: 455 """ 456 serializer not set, reading from default value 457 """ 458 if name in self._defaultSetting: 459 return self._defaultSetting[module][name] 460 else: 461 raise Exception(str(name) + ' is neither in settings.xml nor it has a default value set')
462
463 - def set(self, module, name, value, extra_attrs={}):
464 """ 465 Sets a setting to specified value. 466 467 @param module: Module where the setting should be set 468 @param name: Name of setting 469 @param value: Value to assign to setting 470 @type value: C{str} or C{unicode} or C{int} or C{float} or C{bool} or C{list} or C{dict} 471 @param extra_attrs: Extra attributes to be stored in the XML-file 472 @type extra_attrs: C{dict} 473 """ 474 475 #update the setting cache 476 if module in self._settingsFromFile: 477 self._settingsFromFile[module][name] = value 478 else: 479 self._settingsFromFile[module] = { name: value } 480 481 if self._serializer: 482 self._serializer.set(module, name, value, extra_attrs)
483
484 - def remove(self, module, name):
485 """ 486 Removes a variable 487 488 @param module: Module where the variable should be set 489 @param name: Name of the variable 490 """ 491 #update the setting cache 492 if module in self._settingsFromFile: 493 del self._settingsFromFile[module][name] 494 495 if self._serializer: 496 self._serializer.remove(module, name)
497
498 - def setAvailableScreenResolutions(self, reslist):
499 """ 500 A list of valid default screen resolutions. This should be called once 501 right after you instantiate Settings. 502 503 Valid screen resolutions must be strings in the form of: WIDTHxHEIGHT 504 505 Example: 506 settings.setAvailableScreenResolutions(["800x600", "1024x768"]) 507 """ 508 self._resolutions = reslist
509
510 - def setDefaults(self):
511 """ 512 Overwrites the setting file with the default settings file. 513 """ 514 shutil.copyfile(self._default_settings_file, os.path.join(self._appdata, self._settings_file)) 515 self.changesRequireRestart = True 516 self.initSerializer()
517
518 - def _getEntries(self):
519 return self._entries
520
521 - def _setEntries(self, entries):
522 self._entries = entries
523
524 - def _getSerializer(self):
525 return self._serializer
526 527 entries = property(_getEntries, _setEntries) 528 serializer = property(_getSerializer)
529
530 -class SettingEntry(object):
531
532 - def __init__(self, module, name, applyfunction=None, initialdata=None, requiresrestart=False):
533 """ 534 @param module: The Setting module this Entry belongs to 535 @type module: C{String} 536 @param name: The Setting's name 537 @type name: C{String} 538 @param applyfunction: function that makes the changes when the Setting is 539 saved 540 @type applyfunction: C{function} 541 @param initialdata: If the widget supports the setInitialData() function 542 this can be used to set the initial data 543 @type initialdata: C{String} or C{Boolean} 544 @param requiresrestart: Whether or not the changing of this setting 545 requires a restart 546 @type requiresrestart: C{Boolean} 547 """ 548 self._module = module 549 self._name = name 550 self._requiresrestart = requiresrestart 551 self._initialdata = initialdata 552 self._applyfunction = applyfunction
553
554 - def onApply(self, data):
555 """Implement actions that need to be taken when the setting is changed 556 here. 557 """ 558 if self._applyfunction is not None: 559 self._applyfunction(data)
560
561 - def _getModule(self):
562 return self._module
563
564 - def _setModule(self, module):
565 self._module = module
566
567 - def _getName(self):
568 return self._name
569
570 - def _setName(self, name):
571 self._name = name
572
573 - def _getRequiresRestart(self):
574 return self._requiresrestart
575
576 - def _setRequiresRestart(self, requiresrestart):
577 self._requiresrestart = requiresrestart
578
579 - def _getInitialData(self):
580 return self._initialdata
581
582 - def _setInitialData(self, initialdata):
583 self._initialdata = initialdata
584
585 - def _getApplyFunction(self):
586 return self._applyfunction
587
588 - def _setApplyFunction(self, applyfunction):
589 self._applyfunction = applyfunction
590 591 module = property(_getModule, _setModule) 592 name = property(_getName, _setName) 593 requiresrestart = property(_getRequiresRestart, _setRequiresRestart) 594 initialdata = property(_getInitialData, _setInitialData) 595 applyfunction = property(_getApplyFunction, _setApplyFunction) 596
597 - def __str__(self):
598 return "SettingEntry: " + self.name + " Module: " + self.module + \ 599 " requiresrestart: " + str(self.requiresrestart) + \ 600 " initialdata: " + str(self.initialdata)
601