Package horizons :: Package gui :: Package modules :: Module settings
[hide private]
[frames] | no frames]

Source Code for Module horizons.gui.modules.settings

  1  # ################################################### 
  2  # Copyright (C) 2008-2017 The Unknown Horizons Team 
  3  # team@unknown-horizons.org 
  4  # This file is part of Unknown Horizons. 
  5  # 
  6  # Unknown Horizons is free software; you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation; either version 2 of the License, or 
  9  # (at your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, 
 12  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 14  # GNU General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program; if not, write to the 
 18  # Free Software Foundation, Inc., 
 19  # 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA 
 20  # ################################################### 
 21   
 22  import os 
 23  import sys 
 24   
 25  from fife import fife 
 26   
 27  import horizons.globals 
 28  from horizons.constants import LANGUAGENAMES, SETTINGS 
 29  from horizons.gui.modules.hotkeys_settings import HotkeyConfiguration 
 30  from horizons.gui.modules.loadingscreen import QUOTES_SETTINGS 
 31  from horizons.gui.widgets.pickbeltwidget import PickBeltWidget 
 32  from horizons.gui.windows import Window 
 33  from horizons.i18n import ( 
 34          change_language, find_available_languages, get_language_translation_stats, gettext as T, 
 35          gettext_lazy as LazyT) 
 36  from horizons.network.networkinterface import NetworkInterface 
 37  from horizons.util.python import parse_port 
 38  from horizons.util.python.callback import Callback 
 39   
 40   
41 -class Setting:
42 - def __init__(self, module, name, widget_name, initial_data=None, restart=False, 43 callback=None, on_change=None):
44 self.module = module 45 self.name = name 46 self.widget_name = widget_name 47 self.initial_data = initial_data 48 self.restart = restart 49 self.callback = callback 50 self.on_change = on_change
51 52
53 -class SettingsDialog(PickBeltWidget, Window):
54 """Widget for Options dialog with pickbelt style pages""" 55 56 widget_xml = 'settings.xml' 57 sections = ( 58 ('graphics_settings', LazyT('Graphics')), 59 ('hotkeys_settings', LazyT('Hotkeys')), 60 ('game_settings', LazyT('Game')), 61 ) 62
63 - def __init__(self, windows):
64 Window.__init__(self, windows) 65 PickBeltWidget.__init__(self) 66 67 self._settings = horizons.globals.fife._setting 68 69 self.widget.mapEvents({ 70 'okButton': self.apply_settings, 71 'defaultButton': self.set_defaults, 72 'cancelButton': self._windows.close, 73 })
74
75 - def _init_settings(self):
76 """Init the settings with the stored values.""" 77 languages = list(find_available_languages().keys()) 78 language_names = [LANGUAGENAMES[x] for x in sorted(languages)] 79 80 fps = {0: LazyT("Disabled"), 30: 30, 45: 45, 60: 60, 90: 90, 120: 120} 81 82 FIFE = SETTINGS.FIFE_MODULE 83 UH = SETTINGS.UH_MODULE 84 85 def get_resolutions(): 86 return get_screen_resolutions(self._settings.get(FIFE, 'ScreenResolution'))
87 88 self._options = [ 89 # Graphics/Sound/Input 90 Setting(FIFE, 'ScreenResolution', 'screen_resolution', get_resolutions, restart=True), 91 Setting(FIFE, 'FullScreen', 'enable_fullscreen', restart=True), 92 Setting(FIFE, 'FrameLimit', 'fps_rate', fps, restart=True, callback=self._apply_FrameLimit), 93 94 Setting(UH, 'VolumeMusic', 'volume_music', callback=self._apply_VolumeMusic, 95 on_change=self._on_slider_changed), 96 Setting(UH, 'VolumeEffects', 'volume_effects', callback=self._apply_VolumeEffects, 97 on_change=self._on_slider_changed), 98 Setting(FIFE, 'PlaySounds', 'enable_sound', callback=self._apply_PlaySounds), 99 Setting(UH, 'EdgeScrolling', 'edgescrolling'), 100 Setting(UH, 'CursorCenteredZoom', 'cursor_centered_zoom'), 101 Setting(UH, 'MiddleMousePan', 'middle_mouse_pan'), 102 Setting(FIFE, 'MouseSensitivity', 'mousesensitivity', restart=True, 103 on_change=self._on_slider_changed), 104 105 # Game 106 Setting(UH, 'AutosaveInterval', 'autosaveinterval', on_change=self._on_slider_changed), 107 Setting(UH, 'AutosaveMaxCount', 'autosavemaxcount', on_change=self._on_slider_changed), 108 Setting(UH, 'QuicksaveMaxCount', 'quicksavemaxcount', on_change=self._on_slider_changed), 109 Setting(UH, 'Language', 'uni_language', language_names, 110 callback=self._apply_Language, on_change=self._on_Language_changed), 111 112 Setting(UH, 'UninterruptedBuilding', 'uninterrupted_building'), 113 Setting(UH, 'AutoUnload', 'auto_unload'), 114 Setting(UH, 'DebugLog', 'debug_log', callback=self._apply_DebugLog), 115 Setting(UH, 'ShowResourceIcons', 'show_resource_icons'), 116 Setting(UH, 'ScrollSpeed', 'scrollspeed', on_change=self._on_slider_changed), 117 Setting(UH, 'QuotesType', 'quotestype', QUOTES_SETTINGS), 118 Setting(UH, 'NetworkPort', 'network_port', callback=self._apply_NetworkPort), 119 ] 120 121 self._fill_widgets() 122 123 # key configuration 124 self.hotkey_interface = HotkeyConfiguration() 125 number = self.sections.index(('hotkeys_settings', T('Hotkeys'))) 126 self.page_widgets[number].removeAllChildren() 127 self.page_widgets[number].addChild(self.hotkey_interface.widget)
128
129 - def show(self):
130 self._init_settings() 131 self.widget.show()
132
133 - def hide(self):
134 self.widget.hide()
135
136 - def restart_promt(self):
137 headline = T("Restart required") 138 message = T("Some of your changes require a restart of Unknown Horizons. Do you want to restart Unknown Horizons now?") 139 if self._windows.open_popup(headline, message, show_cancel_button=True): 140 return True
141
142 - def set_defaults(self):
143 title = T("Restore default settings") 144 msg = T("Restoring the default settings will delete all changes to the settings you made so far.") + \ 145 " " + T("Do you want to continue?") 146 147 if self._windows.open_popup(title, msg, show_cancel_button=True): 148 self.hotkey_interface.reset_to_default() 149 self._settings.set_defaults() 150 self.restart_prompt() 151 self._windows.close()
152
153 - def apply_settings(self):
154 restart_required = False 155 156 for entry in self._options: 157 widget = self.widget.findChild(name=entry.widget_name) 158 new_value = widget.getData() 159 160 if callable(entry.initial_data): 161 initial_data = entry.initial_data() 162 else: 163 initial_data = entry.initial_data 164 165 if isinstance(initial_data, list): 166 new_value = initial_data[new_value] 167 elif isinstance(initial_data, dict): 168 new_value = list(initial_data.keys())[new_value] 169 170 old_value = self._settings.get(entry.module, entry.name) 171 172 # Store new setting 173 self._settings.set(entry.module, entry.name, new_value) 174 175 # If setting changed, allow applying of new value and sanity checks 176 if new_value != old_value: 177 if entry.restart: 178 restart_required = True 179 180 if entry.callback: 181 entry.callback(old_value, new_value) 182 183 self.hotkey_interface.save_settings() 184 self._settings.apply() 185 self._settings.save() 186 187 if restart_required: 188 if self.restart_promt(): 189 horizons.globals.fife.engine.destroy() 190 os.execv(sys.executable, [sys.executable] + sys.argv) 191 192 self._windows.close()
193
194 - def _fill_widgets(self):
195 for entry in self._options: 196 value = self._settings.get(entry.module, entry.name) 197 widget = self.widget.findChild(name=entry.widget_name) 198 199 if entry.initial_data: 200 if callable(entry.initial_data): 201 initial_data = entry.initial_data() 202 else: 203 initial_data = entry.initial_data 204 205 if isinstance(initial_data, dict): 206 widget.setInitialData(list(initial_data.values())) 207 value = list(initial_data.keys()).index(value) 208 elif isinstance(initial_data, list): 209 widget.setInitialData(initial_data) 210 value = initial_data.index(value) 211 else: 212 widget.setInitialData(initial_data) 213 214 widget.setData(value) 215 216 if entry.on_change: 217 cb = Callback(entry.on_change, widget) 218 cb() 219 widget.capture(cb)
220
221 - def _on_slider_changed(self, widget):
222 """Callback for updating value label of a slider after dragging it. 223 224 As the numeric values under the hood often do not represent mental 225 models of what the setting achieves very well, depending on the 226 setting in question we display a modified value, sometimes with 227 a '%' suffix.""" 228 value_label = self.widget.findChild(name=widget.name + '_value') 229 value = { 230 'volume_music': lambda x: '{:d}%'.format(int(500 * x)), 231 'volume_effects': lambda x: '{:d}%'.format(int(200 * x)), 232 'mousesensitivity': lambda x: '{:.1f}x'.format(10 * x), 233 'autosaveinterval': lambda x: '{:.1f}'.format(x), 234 'autosavemaxcount': lambda x: '{:d}'.format(int(x)), 235 'quicksavemaxcount': lambda x: '{:d}'.format(int(x)), 236 'scrollspeed': lambda x: '{:.1f}'.format(x), 237 }[widget.name](widget.value) 238 value_label.text = value
239 240 # callbacks for changes of settings 241
242 - def _apply_PlaySounds(self, old, new):
243 horizons.globals.fife.sound.setup_sound()
244
245 - def _apply_VolumeMusic(self, old, new):
246 horizons.globals.fife.sound.set_volume_bgmusic(new)
247
248 - def _apply_VolumeEffects(self, old, new):
249 horizons.globals.fife.sound.set_volume_effects(new)
250
251 - def _apply_FrameLimit(self, old, new):
252 # handling value 0 for framelimit to disable limiter 253 if new == 0: 254 self._settings.set(SETTINGS.FIFE_MODULE, 'FrameLimitEnabled', False) 255 else: 256 self._settings.set(SETTINGS.FIFE_MODULE, 'FrameLimitEnabled', True)
257
258 - def _apply_NetworkPort(self, old, new):
259 """Sets a new value for client network port""" 260 # port is saved as string due to pychan limitations 261 try: 262 # 0 is not a valid port, but a valid value here (used for default) 263 parse_port(new) 264 except ValueError: 265 headline = T("Invalid network port") 266 descr = T("The port you specified is not valid. It must be a number between 1 and 65535.") 267 advice = T("Please check the port you entered and make sure it is in the specified range.") 268 self._windows.open_error_popup(headline, descr, advice) 269 # reset value and reshow settings dlg 270 self._settings.set(SETTINGS.UH_MODULE, 'NetworkPort', "0") 271 else: 272 # port is valid 273 try: 274 if NetworkInterface() is None: 275 NetworkInterface.create_instance() 276 NetworkInterface().network_data_changed() 277 except Exception as e: 278 headline = T("Failed to apply new network settings.") 279 descr = T("Network features could not be initialized with the current configuration.") 280 advice = T("Check the settings you specified in the network section.") 281 if 0 < parse_port(new) < 1024: 282 #i18n This is advice for players seeing a network error with the current config 283 advice += " " + \ 284 T("Low port numbers sometimes require special access privileges, try 0 or a number greater than 1024.") 285 details = str(e) 286 self._windows.open_error_popup(headline, descr, advice, details)
287
288 - def _apply_Language(self, old, new):
289 language = LANGUAGENAMES.get_by_value(new) 290 change_language(language)
291
292 - def _on_Language_changed(self, widget):
293 value = widget.items[widget.getData()] 294 language_code = LANGUAGENAMES.get_by_value(value) 295 296 status_label = self.widget.findChild(name='language_translation_status') 297 if not language_code or language_code == 'en': 298 status_label.text = '' 299 else: 300 value = get_language_translation_stats(language_code) 301 if value: 302 status_label.text = T('Translation {percentage}% completed').format(percentage=value) 303 else: 304 status_label.text = ''
305
306 - def _apply_DebugLog(self, old, new):
307 horizons.main.set_debug_log(new)
308 309
310 -def get_screen_resolutions(selected_default):
311 """Create an instance of fife.DeviceCaps and compile a list of possible resolutions. 312 313 NOTE: This call only works if the engine is inited. 314 """ 315 possible_resolutions = {selected_default} 316 317 MIN_X = 800 318 MIN_Y = 600 319 320 devicecaps = fife.DeviceCaps() 321 devicecaps.fillDeviceCaps() 322 323 for screenmode in devicecaps.getSupportedScreenModes(): 324 x = screenmode.getWidth() 325 y = screenmode.getHeight() 326 if x < MIN_X or y < MIN_Y: 327 continue 328 res = str(x) + 'x' + str(y) 329 possible_resolutions.add(res) 330 331 by_width = lambda res: int(res.split('x')[0]) 332 return sorted(possible_resolutions, key=by_width)
333