1""" The task of this module is to provide easy saving/loading of configurations 2 It also supports gconf like connection, so you get notices when a property 3 has changed. """ 4import sys 5import os 6import atexit 7import builtins 8import locale 9from configparser import RawConfigParser 10 11from pychess import MSYS2 12from pychess.Utils.const import FISCHERRANDOMCHESS, LOSERSCHESS, COUNT_OF_SOUNDS, SOUND_MUTE 13from pychess.System.Log import log 14from pychess.System.prefix import addDataPrefix, addUserConfigPrefix, getDataPrefix 15 16section = "General" 17configParser = RawConfigParser(default_section=section) 18 19for sect in ("FICS", "ICC"): 20 if not configParser.has_section(sect): 21 configParser.add_section(sect) 22 23path = addUserConfigPrefix("config") 24encoding = locale.getpreferredencoding() 25if os.path.isfile(path): 26 with open(path, encoding=encoding) as fh: 27 configParser.read_file(fh) 28 29 30def save_config(path=path, encoding=encoding): 31 with open(path, "w", encoding=encoding) as fh: 32 configParser.write(fh) 33 34 35atexit.register(save_config) 36 37if sys.platform == "win32": 38 username = os.environ["USERNAME"] 39else: 40 from os import getuid 41 from pwd import getpwuid 42 userdata = getpwuid(getuid()) 43 realname = userdata.pw_gecos.split(",")[0] 44 if realname: 45 username = realname 46 else: 47 username = userdata.pw_name 48 49if getattr(sys, 'frozen', False) and not MSYS2: 50 # pyinstaller specific! 51 if hasattr(sys, "_MEIPASS"): 52 base_path = sys._MEIPASS 53 else: 54 base_path = os.path.dirname(sys.executable) 55 default_book_path = os.path.join(base_path, "pychess_book.bin") 56else: 57 default_book_path = os.path.join(addDataPrefix("pychess_book.bin")) 58 59no_gettext = False 60 61 62idkeyfuncs = {} 63conid = 0 64 65 66def notify_add(key, func, *args, section=section): 67 """The signature for func must be self, client, *args, **kwargs""" 68 assert isinstance(key, str) 69 global conid 70 idkeyfuncs[conid] = (key, func, args, section) 71 conid += 1 72 return conid - 1 73 74 75def notify_remove(conid): 76 del idkeyfuncs[conid] 77 78 79if "_" not in builtins.__dir__(): 80 def _(text): 81 return text 82 83 84DEFAULTS = { 85 "General": { 86 "firstName": username, 87 "secondName": _("Guest"), 88 "showEmt": False, 89 "showEval": False, 90 "showBlunder": True, 91 "hideTabs": False, 92 "closeAll": False, 93 "faceToFace": False, 94 "scoreLinearScale": False, 95 "showCaptured": False, 96 "figuresInNotation": False, 97 "moveAnimation": False, 98 "noAnimation": False, 99 "autoPromote": False, 100 "autoRotate": False, 101 "showFICSgameno": False, 102 "fullAnimation": True, 103 "showCords": True, 104 "drawGrid": True, 105 "activateSupportAlgorithm": False, 106 "board_frame": 1, 107 "board_style": 1, 108 "pieceTheme": "Chessicons", 109 "darkcolour": "", 110 "lightcolour": "", 111 "movetextFont": "FreeSerif Regular 12", 112 "autoSave": True, 113 "autoSavePath": os.path.expanduser("~"), 114 "autoSaveFormat": "pychess", 115 "saveEmt": False, 116 "saveEval": False, 117 "saveRatingChange": False, 118 "indentPgn": False, 119 "saveOwnGames": True, 120 "dont_show_externals_at_startup": False, 121 "max_analysis_spin": 3, 122 "variation_threshold_spin": 50, 123 "fromCurrent": True, 124 "shouldWhite": True, 125 "shouldBlack": True, 126 "ThreatPV": False, 127 "infinite_analysis": False, 128 "opening_check": False, 129 "opening_file_entry": default_book_path, 130 "book_depth_max": 13, 131 "book_exact_match": True, 132 "endgame_check": False, 133 "egtb_path": os.path.join(getDataPrefix()), 134 "online_egtb_check": True, 135 "autoCallFlag": True, 136 "hint_mode": False, 137 "spy_mode": False, 138 "ana_combobox": 0, 139 "analyzer_check": True, 140 "inv_ana_combobox": 0, 141 "inv_analyzer_check": False, 142 "newgametasker_playercombo": 0, 143 "ics_combo": 0, 144 "autoLogin": False, 145 "standard_toggle": True, 146 "blitz_toggle": True, 147 "lightning_toggle": True, 148 "variant_toggle": True, 149 "registered_toggle": True, 150 "guest_toggle": True, 151 "computer_toggle": True, 152 "titled_toggle": True, 153 "numberOfFingers": 0, 154 "numberOfTimesLoggedInAsRegisteredUser": 0, 155 "lastdifference-1": -1, 156 "lastdifference-2": -1, 157 "lastdifference-3": -1, 158 "standard_toggle1": True, 159 "blitz_toggle1": True, 160 "lightning_toggle1": True, 161 "variant_toggle1": True, 162 "computer_toggle1": True, 163 "categorycombo": 0, 164 "learncombo0": 0, 165 "learncombo1": 0, 166 "learncombo2": 0, 167 "learncombo3": 0, 168 "welcome_image": addDataPrefix("glade/background.jpg"), 169 "alarm_spin": 15, 170 "show_tip_at_startup": True, 171 "tips_seed": 0, 172 "tips_index": 0, 173 "dont_show_externals_at_startup": False, 174 "download_timestamp": False, 175 "download_chess_db": False, 176 "download_scoutfish": False, 177 "ngvariant1": FISCHERRANDOMCHESS, 178 "ngvariant2": LOSERSCHESS, 179 "useSounds": True, 180 "max_log_files": 10, 181 "show_sidepanels": True, 182 "chat_paned_position": 100, 183 "notimeRadio": 0, 184 "blitzRadio": 0, 185 "ngblitz min": 5, 186 "ngblitz gain": 0, 187 "ngblitz moves": 0, 188 "rapidRadio": 0, 189 "ngrapid min": 15, 190 "ngrapid gain": 5, 191 "ngrapid moves": 0, 192 "normalRadio": 0, 193 "ngnormal min": 45, 194 "ngnormal gain": 15, 195 "ngnormal moves": 0, 196 "classicalRadio": 0, 197 "playNormalRadio": 0, 198 "playVariant1Radio": 0, 199 "playVariant2Radio": 0, 200 "ngclassical min": 3, 201 "ngclassical gain": 0, 202 "ngclassical moves": 40, 203 "whitePlayerCombobox": 0, 204 "blackPlayerCombobox": 0, 205 "skillSlider1": 20, 206 "skillSlider2": 20, 207 "taskerSkillSlider": 20, 208 "seek1Radio": 0, 209 "seek2Radio": 0, 210 "seek3Radio": 0, 211 "challenge1Radio": 0, 212 "challenge2Radio": 0, 213 "challenge3Radio": 0, 214 }, 215 "FICS": { 216 "timesealCheck": True, 217 "hostEntry": "freechess.org", 218 "usernameEntry": "", 219 "passwordEntry": "", 220 "asGuestCheck": True, 221 }, 222 "ICC": { 223 "timesealCheck": True, 224 "hostEntry": "chessclub.com", 225 "usernameEntry": "", 226 "passwordEntry": "", 227 "asGuestCheck": True, 228 }, 229} 230 231for i in range(COUNT_OF_SOUNDS): 232 DEFAULTS["General"]["soundcombo%d" % i] = SOUND_MUTE 233 DEFAULTS["General"]["sounduri%d" % i] = "" 234 235 236def get(key, section=section): 237 try: 238 default = DEFAULTS[section][key] 239 except KeyError: 240 # window attributes has no default values 241 # print("!!! conf get() KeyError: %s %s" % (section, key)) 242 default = None 243 244 try: 245 value = configParser.getint(section, key, fallback=default) 246 # print("... conf get %s %s: %s" % (section, key, value)) 247 return value 248 except ValueError: 249 pass 250 251 try: 252 value = configParser.getboolean(section, key, fallback=default) 253 # print("... conf get %s %s: %s" % (section, key, value)) 254 return value 255 except ValueError: 256 pass 257 258 try: 259 value = configParser.getfloat(section, key, fallback=default) 260 # print("... conf get %s %s: %s" % (section, key, value)) 261 return value 262 except ValueError: 263 pass 264 265 value = configParser.get(section, key, fallback=default) 266 # print("... conf get %s %s: %s" % (section, key, value)) 267 return value 268 269 270def set(key, value, section=section): 271 # print("---conf set()", section, key, value) 272 try: 273 configParser.set(section, key, str(value)) 274 with open(path, "w") as fh: 275 configParser.write(fh) 276 except Exception as err: 277 log.error( 278 "Unable to save configuration '%s'='%s' because of error: %s %s" % 279 (repr(key), repr(value), err.__class__.__name__, ", ".join( 280 str(a) for a in err.args))) 281 for key_, func, args, section_ in idkeyfuncs.values(): 282 if key_ == key and section_ == section: 283 func(None, *args) 284 285 286def hasKey(key, section=section): 287 return configParser.has_option(section, key) 288