1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
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
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
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
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
143 self._readSettingsCompleted = {}
144
145
146 self._settingsFromFile = {}
147
148
149
150 self._logger = None
151
152
153 self._resolutions = self._validSetting['FIFE']['ScreenResolution']
154 self._renderbackends = self._validSetting['FIFE']['RenderBackend']
155 self._lightingmodels = self._validSetting['FIFE']['Lighting']
156
157
158 self._gui_style = "default"
159
160
161 if serializer:
162 self._serializer = serializer
163 else:
164 self._serializer = SimpleXMLSerializer()
165
166 self.initSerializer()
167
168
169
170 if "FIFE" not in self._serializer.getModuleNameList():
171 self.setDefaults()
172
173
174 self._allModules = self._serializer.getModuleNameList()
175
176 for module in self._allModules:
177 self._readSettingsCompleted[module] = False
178
179 self._initDefaultSettingEntries()
180
181
182
183
184
185
187 if validSetting:
188 self._validSetting[module] = settings
189 else:
190 self._defaultSetting[module] = settings
191
192
198
199
201 if validSetting:
202 return self._validSetting[module]
203 else:
204 return self._defaultSetting[module]
205
206
208 if validSetting:
209 return self._validSetting[module][name]
210 else:
211 return self._defaultSetting[module][name]
212
213
219
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
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
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
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
309
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
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
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
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
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
492 if module in self._settingsFromFile:
493 del self._settingsFromFile[module][name]
494
495 if self._serializer:
496 self._serializer.remove(module, name)
497
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
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
520
523
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):
563
564 - def _setModule(self, module):
565 self._module = module
566
567 - def _getName(self):
569
570 - def _setName(self, name):
572
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
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
598 return "SettingEntry: " + self.name + " Module: " + self.module + \
599 " requiresrestart: " + str(self.requiresrestart) + \
600 " initialdata: " + str(self.initialdata)
601