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 import shutil
33 import os
34 from StringIO import StringIO
35
36 from fife.extensions import fifelog
37 from fife.extensions import pychan
38 from fife.extensions.fife_utils import getUserDataDirectory
39 from fife.extensions.serializers.simplexml import SimpleXMLSerializer
40
41 SETTINGS_GUI_XML="""\
42 <Window name="Settings" title="Settings">
43 <Label text="Settings menu!" />
44 <HBox>
45 <VBox>
46 <Label text="Resolution:" />
47 <Label text="Renderer:" />
48 <Label text="Light Model:" />
49 </VBox>
50 <VBox min_size="120,60">
51 <DropDown name="screen_resolution" min_size="120,0" />
52 <DropDown name="render_backend" min_size="120,0" />
53 <DropDown name="lighting_model" min_size="120,0" />
54 </VBox>
55 </HBox>
56 <CheckBox name="enable_fullscreen" text="Use the full screen mode" />
57 <CheckBox name="enable_sound" text="Enable sound" />
58 <HBox>
59 <Spacer />
60 <Button name="cancelButton" text="Cancel" />
61 <Button name="okButton" text="Ok" />
62 <Button name="defaultButton" text="Defaults" />
63 </HBox>
64 </Window>
65 """
66
67 CHANGES_REQUIRE_RESTART="""\
68 <Window title="Changes require restart">
69 <Label text="Some of your changes require you to restart." />
70 <HBox>
71 <Spacer />
72 <Button name="closeButton" text="Ok" />
73 </HBox>
74 </Window>
75 """
76
77 FIFE_MODULE = "FIFE"
78
80 """
81 This class manages loading and saving of game settings.
82
83 Usage::
84 from fife.extensions.fife_settings import Setting
85 settings = Setting(app_name="myapp")
86 screen_width = settings.get("FIFE", "ScreenWidth", 1024)
87 screen_height = settings.get("FIFE", "ScreenHeight", 768)
88 """
89
90 - def __init__(self, app_name="", settings_file="", default_settings_file= "settings-dist.xml", settings_gui_xml="", changes_gui_xml="", copy_dist=True, serializer=None):
91 """
92 Initializes the Setting object.
93
94 @param app_name: The applications name. If this parameter is provided
95 alone it will try to read the settings file from the users home directory.
96 In windows this will be something like: C:\Documents and Settings\user\Application Data\fife
97 @type app_name: C{string}
98 @param settings_file: The name of the settings file. If this parameter is
99 provided it will look for the setting file as you specify it, first looking
100 in the working directory. It will NOT look in the users home directory.
101 @type settings_file: C{string}
102 @param default_settings_file: The name of the default settings file. If the settings_file
103 does not exist this file will be copied into the place of the settings_file. This file
104 must exist in the root directory of your project!
105 @type default_settings_file: C{string}
106 @param settings_gui_xml: If you specify this parameter you can customize the look
107 of the settings dialog box.
108 @param copy_dist: Copies the default settings file to the settings_file location. If
109 this is False it will create a new empty setting file.
110 @param serializer: Overrides the default XML serializer
111 @type serializer: C{SimpleSerializer}
112
113 """
114 self._app_name = app_name
115 self._settings_file = settings_file
116 self._default_settings_file = default_settings_file
117 self._settings_gui_xml = settings_gui_xml
118 self._changes_gui_xml = changes_gui_xml
119 self.OptionsDlg = None
120
121
122 self._entries = {}
123
124 if self._settings_file == "":
125 self._settings_file = "settings.xml"
126 self._appdata = getUserDataDirectory("fife", self._app_name)
127 else:
128 self._appdata = os.path.dirname(self._settings_file)
129 self._settings_file = os.path.basename(self._settings_file)
130
131
132 if self._settings_gui_xml == "":
133 self._settings_gui_xml = SETTINGS_GUI_XML
134
135 if self._changes_gui_xml == "":
136 self._changes_gui_xml = CHANGES_REQUIRE_RESTART
137
138
139 if not os.path.exists(os.path.join(self._appdata, self._settings_file)):
140 if os.path.exists(self._default_settings_file) and copy_dist:
141 shutil.copyfile(self._default_settings_file, os.path.join(self._appdata, self._settings_file))
142
143
144 self._validSetting = {}
145 self._validSetting['FIFE'] = {
146 'FullScreen':[True,False], 'PychanDebug':[True,False]
147 , 'ProfilingOn':[True,False], 'SDLRemoveFakeAlpha':[0,1], 'GLCompressImages':[False,True], 'GLUseFramebuffer':[False,True], 'GLUseNPOT':[False,True],
148 'RenderBackend':['OpenGL','SDL', 'OpenGLe'],
149 'ScreenResolution':['640x480', '800x600', '1024x600', '1024x768', '1280x768',
150 '1280x800', '1280x960', '1280x1024', '1366x768', '1440x900',
151 '1600x900', '1600x1200', '1680x1050', '1920x1080', '1920x1200'],
152 'BitsPerPixel':[0,16,24,32],
153 'InitialVolume':[0.0,10.0], 'WindowTitle':"", 'WindowIcon':"", 'Font':"",
154 'FontGlyphs':"", 'DefaultFontSize':"", 'Lighting':[0,1],
155 'ColorKeyEnabled':[True,False], 'ColorKey':['a','b','c'], 'VideoDriver':"",
156 'PlaySounds':[True,False], 'LogToFile':[0,1],
157 'LogToPrompt':[0,1],'UsePsyco':[True,False], 'LogLevelFilter':[0,1,2,3],
158 'LogModules':['all', 'controller','script','video','audio','loaders','vfs','pool','view','model','metamodel','event_channel','xml'],
159 'FrameLimitEnabled':[True,False], 'FrameLimit':[0], 'MouseSensitivity':[0.0], 'MouseAcceleration':[True,False]
160 }
161
162 glyphDft = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,!?-+/():;%&`'*#=[]\\\""
163
164
165 self._defaultSetting = {}
166 self._defaultSetting['FIFE'] = {
167 'FullScreen':False, 'PychanDebug':False
168 , 'ProfilingOn':False, 'SDLRemoveFakeAlpha':0, 'GLCompressImages':False, 'GLUseFramebuffer':True, 'GLUseNPOT':True,
169 'RenderBackend':'OpenGL', 'ScreenResolution':"1024x768", 'BitsPerPixel':0,
170 'InitialVolume':5.0, 'WindowTitle':"", 'WindowIcon':"", 'Font':"",
171 'FontGlyphs':glyphDft, 'DefaultFontSize':12, 'Lighting':0,
172 'ColorKeyEnabled':False, 'ColorKey':[255,0,255], 'VideoDriver':"",
173 'PlaySounds':True, 'LogToFile':0,
174 'LogToPrompt':0,'UsePsyco':False,'LogLevelFilter':[0],
175 'LogModules':['controller','script'],
176 'FrameLimitEnabled':False, 'FrameLimit':60,
177 'MouseSensitivity':0.0,
178 'MouseAcceleration':False
179 }
180
181
182 self._readSettingsCompleted = {}
183
184
185 self._settingsFromFile = {}
186
187
188
189 self._logger = None
190
191
192 self._resolutions = self._validSetting['FIFE']['ScreenResolution']
193 self._renderbackends = self._validSetting['FIFE']['RenderBackend']
194 self._lightingmodels = self._validSetting['FIFE']['Lighting']
195
196
197 self._gui_style = "default"
198
199
200 if serializer:
201 self._serializer = serializer
202 else:
203 self._serializer = SimpleXMLSerializer()
204
205 self.initSerializer()
206
207
208 self._allModules = self._serializer.getModuleName()
209
210 for module in self._allModules:
211 self._readSettingsCompleted[module] = False
212
213 self._initDefaultSettingEntries()
214
215
216
217
218
219
221 if validSettings:
222 self._validSetting[module] = settings
223 else:
224 self._defaultSetting[module] = settings
225
226
232
233
235 if validSetting:
236 return self._validSetting[module]
237 else:
238 return self._defaultSetting[module]
239
240
242 if validSetting:
243 return self._validSetting[module][name]
244 else:
245 return self._defaultSetting[module][name]
246
247
253
255 self._serializer.load(os.path.join(self._appdata, self._settings_file))
256
258 """Initializes the default fife setting entries. Not to be called from
259 outside this class."""
260 self.createAndAddEntry(FIFE_MODULE, "PlaySounds", "enable_sound",
261 requiresrestart=True)
262 self.createAndAddEntry(FIFE_MODULE, "FullScreen", "enable_fullscreen",
263 requiresrestart=True)
264 self.createAndAddEntry(FIFE_MODULE, "ScreenResolution", "screen_resolution", initialdata = self._resolutions,
265 requiresrestart=True)
266 self.createAndAddEntry(FIFE_MODULE, "RenderBackend", "render_backend", initialdata = self._renderbackends,
267 requiresrestart=True)
268 self.createAndAddEntry(FIFE_MODULE, "Lighting", "lighting_model", initialdata = self._lightingmodels,
269 requiresrestart=True)
270
271 - def createAndAddEntry(self, module, name, widgetname, applyfunction=None, initialdata=None, requiresrestart=False):
272 """"
273 @param module: The Setting module this Entry belongs to
274 @type module: C{String}
275 @param name: The Setting's name
276 @type name: C{String}
277 @param widgetname: The name of the widget that is used to change this
278 setting
279 @type widgetname: C{String}
280 @param applyfunction: function that makes the changes when the Setting is
281 saved
282 @type applyfunction: C{function}
283 @param initialdata: If the widget supports the setInitialData() function
284 this can be used to set the initial data
285 @type initialdata: C{String} or C{Boolean}
286 @param requiresrestart: Whether or not the changing of this setting
287 requires a restart
288 @type requiresrestart: C{Boolean}
289 """
290 entry = SettingEntry(module, name, widgetname, applyfunction, initialdata, requiresrestart)
291 self.addEntry(entry)
292
293 - def addEntry(self, entry):
294 """Adds a new C{SettingEntry} to the Settting
295 @param entry: A new SettingEntry that is to be added
296 @type entry: C{SettingEntry}
297 """
298 if entry.module not in self._entries:
299 self._entries[entry.module] = {}
300 self._entries[entry.module][entry.name] = entry
301
302 """
303 # Make sure the new entry is available
304 if self.get(entry.module, entry.name) is None:
305 print "Updating", self._settings_file, "to the default, it is missing the entry:"\
306 , entry.name ,"for module", entry.module
307
308 #self.setDefaults()
309 if self.get(entry.module, entry.name) is None:
310 print "WARNING:", entry.module, ":", entry.name, "still not found!"
311 """
312
314 """ Writes the settings to the settings file
315
316 @param filename: Specifies the file to save the settings to. If it is not specified
317 the original settings file is used.
318 @type filename: C{string}
319 """
320 if self._serializer:
321 if filename == "":
322 self._serializer.save(os.path.join(self._appdata, self._settings_file))
323 else:
324 self._serializer.save(filename)
325
326
328 if self._serializer:
329
330 self._logger = logger
331 modules = self._serializer.getModuleName()
332 self._settingsFromFile[module] = self._serializer.getAllSettings(module)
333
334 if self._logger:
335 self._logger.log_log("Loading Settings From File ...")
336
337 if self._settingsFromFile[module] is not None:
338 self._readSettingsCompleted[module] = True
339
340
341 if module is not "FIFE":
342 return self._settingsFromFile[module]
343 """
344 Now we have all the settings we needed. We have to validate the settings. Applicable for module
345 FIFE only
346 """
347 for name in self._settingsFromFile[module]:
348
349
350 if name in self._validSetting[module]:
351
352 e_value = self._settingsFromFile[module][name]
353
354 if name == "InitialVolume":
355 if e_value >= self._validSetting[module][name][0] and e_value <= self._validSetting[module][name][1]:
356 self._settingsFromFile[module][name] = e_value
357 else:
358 if self._logger:
359 self._logger.log_log("InitalVolume must have a value between 0.0 and 10.0")
360
361 elif name == "ColorKey":
362 e_value = e_value.split(',')
363 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):
364 self._settingsFromFile[name] = [int(e_value[0]),int(e_value[1]),int(e_value[2])];
365
366 else:
367 if self._logger:
368 self._logger.log_log("ColorKey values must be within 0 and 255. Setting to Default Value.")
369
370 elif name == "ScreenResolution":
371 temp = e_value.split('x')
372 if len(temp) == 2:
373 self._settingsFromFile[module][name]=e_value
374 else:
375 if self._logger:
376 self._logger.log_log("Invalid Screen Resolution value. We expect two integer seperted by x")
377
378 elif len(self._validSetting[module][name]) == 0:
379 self._settingsFromFile[module][name] = e_value
380
381 elif name == "LogModules":
382 for checking_element in e_value:
383 module_valid = False
384 for base_element in self._validSetting[module][name]:
385
386
387 if checking_element == base_element:
388 module_valid = True
389 already_in = False
390 for element in self._settingsFromFile[module][name]:
391 if element == checking_element:
392 already_in = True
393 if already_in == False:
394 self._settingsFromFile[module][name].append(checking_element)
395 if module_valid == False:
396 if self._logger:
397 self._logger.log_log(checking_element +" is not a valid logModule")
398 elif name == "FrameLimit":
399 if e_value > 0:
400 self._settingsFromFile[module][name] = e_value
401 else:
402 if self._logger:
403 self._logger.log_log(e_value + " is not a valid FrameLimit setting. You must specify a positive integer!")
404 elif name == "MouseSensitivity":
405 self._settingsFromFile[module][name] = e_value
406 elif name == "MouseAcceleration":
407 self._settingsFromFile[module][name] = e_value
408 else:
409
410 if isinstance(self._settingsFromFile[module][name],list) == True or isinstance(self._settingsFromFile[module][name],dict) == True:
411 valid = False
412 for value in self._validSetting[module][name]:
413 if value == e_value:
414 valid = True
415 self._settingsFromFile[module][name] = e_value;
416 if valid == False:
417 if self._logger:
418 self._logger.log_log("Setting " + name + " got invalid value. Setting to Default.")
419 else: self._settingsFromFile[module][name] = e_value
420
421
422 else:
423 if self._logger:
424 self._logger.log_log("Setting "+ name + " is unknown")
425
426 if self._logger:
427 self._logger.log_log("Settings Loaded ...")
428
429 """
430 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
431 not present in the settings.xml file. For this, we should give them the default Values that are in defaultSetting.
432 """
433
434 for name in self._defaultSetting[module]:
435 if name not in self._settingsFromFile[module]:
436 self._settingsFromFile[module][name] = self._defaultSetting[module][name]
437
438 return self._settingsFromFile[module]
439
440 else:
441 return None
442
443 - def get(self, module, name, defaultValue=None):
444 """ Gets the value of a specified setting
445
446 @param module: Name of the module to get the setting from
447 @param name: Setting name
448 @param defaultValue: Specifies the default value to return if the setting is not found
449 @type defaultValue: C{str} or C{unicode} or C{int} or C{float} or C{bool} or C{list} or C{dict}
450 """
451
452 if self._serializer:
453 if module is "FIFE":
454
455 if self._readSettingsCompleted[module] is not True:
456 value = self._serializer.get(module, name, defaultValue)
457
458 if value is not None:
459 return value
460 else:
461 if name in self._defaultSetting[module]:
462 return self._defaultSetting[module][name]
463 else:
464 raise Exception(str(name) + ' is neither in settings.xml nor it has a default value set')
465 else:
466 if name in self._settingsFromFile[module]:
467 return self._settingsFromFile[module][name]
468 else:
469 raise Exception(str(name) + ' is neither in settings.xml nor it has a default value set')
470 else:
471 return self._serializer.get(module, name, defaultValue)
472 else:
473 """
474 serializer not set, reading from default value
475 """
476 if name in self._defaultSetting:
477 return self._defaultSetting[module][name]
478 else:
479 raise Exception(str(name) + ' is neither in settings.xml nor it has a default value set')
480
481 - def set(self, module, name, value, extra_attrs={}):
482 """
483 Sets a setting to specified value.
484
485 @param module: Module where the setting should be set
486 @param name: Name of setting
487 @param value: Value to assign to setting
488 @type value: C{str} or C{unicode} or C{int} or C{float} or C{bool} or C{list} or C{dict}
489 @param extra_attrs: Extra attributes to be stored in the XML-file
490 @type extra_attrs: C{dict}
491 """
492
493
494 if module in self._settingsFromFile:
495 self._settingsFromFile[module][name] = value
496 else:
497 self._settingsFromFile[module] = { name: value }
498
499 if self._serializer:
500 self._serializer.set(module, name, value, extra_attrs)
501
503 """ Set a custom gui style used for the option dialog.
504 @param style: Pychan style to be used
505 @type style: C{string}
506 """
507 self._gui_style = style
508
510 """
511 Opens the options dialog box. Usually you would bind this to a button.
512 """
513 self.changesRequireRestart = False
514 self.isSetToDefault = False
515 if not self.OptionsDlg:
516 self.loadSettingsDialog()
517 self.fillWidgets()
518 self.OptionsDlg.show()
519
521 """
522 Load up the settings xml and return the widget.
523 """
524 self.OptionsDlg = self._loadWidget(self._settings_gui_xml)
525 self.OptionsDlg.stylize(self._gui_style)
526 self.OptionsDlg.mapEvents({
527 'okButton' : self.applySettings,
528 'cancelButton' : self.OptionsDlg.hide,
529 'defaultButton' : self.setDefaults
530 })
531 return self.OptionsDlg
532
539
562
591
592
594 """Shows a dialog that informes the user that a restart is required
595 to perform the changes."""
596 RestartDlg = self._loadWidget(self._changes_gui_xml)
597 RestartDlg.stylize(self._gui_style)
598 RestartDlg.mapEvents({ 'closeButton' : RestartDlg.hide })
599 RestartDlg.show()
600
601
603 """
604 A list of valid default screen resolutions. This should be called once
605 right after you instantiate Settings.
606
607 Valid screen resolutions must be strings in the form of: WIDTHxHEIGHT
608
609 Example:
610 settings.setAvailableScreenResolutions(["800x600", "1024x768"])
611 """
612 self._resolutions = reslist
613
615 """
616 Overwrites the setting file with the default settings file.
617 """
618 shutil.copyfile(self._default_settings_file, os.path.join(self._appdata, self._settings_file))
619 self.changesRequireRestart = True
620 self.initSerializer()
621
622
623
624 if self.OptionsDlg:
625 self.fillWidgets()
626
629
632
634 return self._serializer
635
636 entries = property(_getEntries, _setEntries)
637 serializer = property(_getSerializer)
638
639 -class SettingEntry(object):
640
641 - def __init__(self, module, name, widgetname, applyfunction=None, initialdata=None, requiresrestart=False):
642 """
643 @param module: The Setting module this Entry belongs to
644 @type module: C{String}
645 @param name: The Setting's name
646 @type name: C{String}
647 @param widgetname: The name of the widget that is used to change this
648 setting
649 @type widgetname: C{String}
650 @param applyfunction: function that makes the changes when the Setting is
651 saved
652 @type applyfunction: C{function}
653 @param initialdata: If the widget supports the setInitialData() function
654 this can be used to set the initial data
655 @type initialdata: C{String} or C{Boolean}
656 @param requiresrestart: Whether or not the changing of this setting
657 requires a restart
658 @type requiresrestart: C{Boolean}
659 """
660 self._module = module
661 self._name = name
662 self._settingwidgetname = widgetname
663 self._requiresrestart = requiresrestart
664 self._initialdata = initialdata
665 self._applyfunction = applyfunction
666
672
673 - def onApply(self, data):
674 """Implement actions that need to be taken when the setting is changed
675 here.
676 """
677 if self._applyfunction is not None:
678 self._applyfunction(data)
679
680 - def _getModule(self):
682
683 - def _setModule(self, module):
684 self._module = module
685
686 - def _getName(self):
688
689 - def _setName(self, name):
691
693 return self._settingwidgetname
694
697
699 return self._requiresrestart
700
701 - def _setRequiresRestart(self, requiresrestart):
702 self._requiresrestart = requiresrestart
703
704 - def _getInitialData(self):
705 return self._initialdata
706
707 - def _setInitialData(self, initialdata):
708 self._initialdata = initialdata
709
711 return self._applyfunction
712
713 - def _setApplyFunction(self, applyfunction):
714 self._applyfunction = applyfunction
715
716 module = property(_getModule, _setModule)
717 name = property(_getName, _setName)
718 settingwidgetname = property(_getSettingWidgetName, _setSettingWidgetName)
719 requiresrestart = property(_getRequiresRestart, _setRequiresRestart)
720 initialdata = property(_getInitialData, _setInitialData)
721 applyfunction = property(_getApplyFunction, _setApplyFunction)
722
724 return "SettingEntry: " + self.name + " Module: " + self.module + " Widget: " + \
725 self.settingwidgetname + " requiresrestart: " + str(self.requiresrestart) + \
726 " initialdata: " + str(self.initialdata)
727