1# Copyright (C) 2005 Jeremy S. Sanders 2# Email: Jeremy Sanders <jeremy@jeremysanders.net> 3# 4# This program is free software; you can redistribute it and/or modify 5# it under the terms of the GNU General Public License as published by 6# the Free Software Foundation; either version 2 of the License, or 7# (at your option) any later version. 8# 9# This program is distributed in the hope that it will be useful, 10# but WITHOUT ANY WARRANTY; without even the implied warranty of 11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12# GNU General Public License for more details. 13# 14# You should have received a copy of the GNU General Public License along 15# with this program; if not, write to the Free Software Foundation, Inc., 16# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17############################################################################### 18 19"""A database for default values of settings.""" 20 21from __future__ import division, print_function 22import sys 23import datetime 24 25import numpy as N 26from .. import qtall as qt 27 28def _(text, disambiguation=None, context="Preferences"): 29 """Translate text.""" 30 return qt.QCoreApplication.translate(context, text, disambiguation) 31 32# default values to some settings in case the user does not have these 33defaultValues = { 34 # export options 35 'export_DPI': 100, 36 'export_DPI_PDF': 150, 37 'export_DPI_SVG': 96, 38 'export_color': True, 39 'export_antialias': True, 40 'export_quality': 85, 41 'export_background': '#ffffff00', 42 'export_SVG_text_as_text': False, 43 44 # plot options 45 'plot_updatepolicy': -1, # update on document changed 46 'plot_antialias': True, 47 'plot_numthreads': 2, 48 49 # recent files list 50 'main_recentfiles': [], 51 52 # default stylesheet 53 'stylesheet_default': '', 54 # default custom definitons 55 'custom_default': '', 56 57 # colors (isdefault, 'notdefaultcolor') 58 'color_page': (True, 'white'), 59 'color_error': (True, 'red'), 60 'color_command': (True, 'blue'), 61 'color_cntrlline': (True, 'blue'), 62 'color_cntrlcorner': (True, 'black'), 63 64 # document theme 65 'colortheme_default': 'default-latest', 66 67 # further ui options 68 'toolbar_size': 24, 69 70 # if set to true, do UI formatting in US/English 71 'ui_english': False, 72 73 # use cwd as starting directory 74 'dirname_usecwd': False, 75 76 # use document directory for export 77 'dirname_export_location': 'doc', 78 79 # export templates 80 'export_template_single': '%DOCNAME%', 81 'export_template_multi': '%DOCNAME%_%PAGE00%', 82 83 # add import paths 84 'docfile_addimportpaths': True, 85 86 # ask tutorial before? 87 'ask_tutorial': False, 88 89 # log picked points to clipboard or to console 90 'picker_to_clipboard': False, 91 'picker_to_console': True, 92 'picker_sig_figs': 5, 93 94 # add these directories to the python path (colon-separated) 95 'external_pythonpath': '', 96 97 # location of ghostscript (or empty to search) 98 'external_ghostscript': '', 99 100 # translation file to load 101 'translation_file': '', 102 103 # user has disabled version update checks 104 # (packagers: please don't disable here, see disableVersionChecks in 105 # veusz/utils/version.py) 106 'vercheck_disabled': False, 107 'vercheck_asked_user': False, 108 'vercheck_last_done': (2000,1,1), 109 'vercheck_latest': '', 110 111 # whether to send feedback about usage 112 # (packagers, please don't disable here but in veusz/setting/feedback.py) 113 'feedback_disabled': False, 114 'feedback_asked_user': False, 115 116 # locations considered secure to load 117 'secure_dirs': [], 118 'secure_unsaved': True, 119} 120 121class _SettingDB(object): 122 """A class which provides access to a persistant settings database. 123 124 Items are accesses as a dict, with items as key=value 125 """ 126 127 # list of colors 128 colors = ('page', 'error', 'command', 'cntrlline', 'cntrlcorner') 129 # default colors if isdefault is set in the setting 130 color_defaults = { 131 'page': 'LightBase', 132 'error': 'red', 133 'command': 'blue', 134 'cntrlline': 'blue', 135 'cntrlcorner': 'black', 136 } 137 138 def __init__(self): 139 """Initialise the object, reading the settings.""" 140 141 # This domain name is fictional! 142 self.domain = 'veusz.org' 143 self.product = 'veusz' 144 self.database = {} 145 self.sepchars = "%%%" 146 147 # read settings using QSettings 148 self.readSettings() 149 150 def color(self, name): 151 """Get a color setting as a QColor.""" 152 153 val = self.database['color_' + name] 154 if val[0]: 155 default = self.color_defaults[name] 156 if default == 'LightBase': 157 base = qt.qApp.palette().color(qt.QPalette.Base) 158 if base.value() < 127: 159 base = qt.QColor(qt.Qt.white) 160 return base 161 162 return qt.QColor(default) 163 else: 164 return qt.QColor(val[1]) 165 166 def readSettings(self): 167 """Read the settings using QSettings. 168 169 Entries have / replaced with set of characters self.sepchars 170 This is because it greatly simplifies the logic as QSettings 171 has special meaning for / 172 173 The only issues are that the key may be larger than 255 characters 174 We should probably check for this 175 """ 176 177 s = qt.QSettings(self.domain, self.product) 178 179 for key in s.childKeys(): 180 val = s.value(key) 181 realkey = key.replace(self.sepchars, '/') 182 183 try: 184 self.database[realkey] = eval(val) 185 except: 186 print('Error interpreting item "%s" in ' 187 'settings file' % realkey, file=sys.stderr) 188 189 # set any defaults which haven't been set 190 for key in defaultValues: 191 if key not in self.database: 192 self.database[key] = defaultValues[key] 193 194 # keep install date for reminders, etc 195 if 'install_date' not in self.database: 196 today = datetime.date.today() 197 self.database['install_date'] = (today.year, today.month, today.day) 198 199 def writeSettings(self): 200 """Write the settings using QSettings. 201 202 This is called by the mainwindow on close 203 """ 204 205 s = qt.QSettings(self.domain, self.product) 206 207 # write each entry, keeping track of which ones haven't been written 208 cleankeys = [] 209 for key in self.database: 210 cleankey = key.replace('/', self.sepchars) 211 cleankeys.append(cleankey) 212 213 s.setValue(cleankey, repr(self.database[key])) 214 215 # now remove all the values which have been removed 216 for key in list(s.childKeys()): 217 if key not in cleankeys: 218 s.remove(key) 219 220 def get(self, key, defaultval=None): 221 """Return key if it is in database, else defaultval.""" 222 return self.database.get(key, defaultval) 223 224 def __getitem__(self, key): 225 """Get the item from the database.""" 226 return self.database[key] 227 228 def __setitem__(self, key, value): 229 """Set the value in the database.""" 230 self.database[key] = value 231 232 def __delitem__(self, key): 233 """Remove the key from the database.""" 234 del self.database[key] 235 236 def __contains__(self, key): 237 """Is the key in the database.""" 238 return key in self.database 239 240# create the SettingDB singleton 241settingdb = _SettingDB() 242 243# a normal dict for non-persistent settings 244transient_settings = { 245 # disable safety checks on evaluated code 246 'unsafe_mode': False, 247} 248 249def updateUILocale(): 250 """Update locale to one given in preferences.""" 251 global uilocale 252 253 if settingdb['ui_english']: 254 uilocale = qt.QLocale.c() 255 else: 256 uilocale = qt.QLocale.system() 257 uilocale.setNumberOptions(qt.QLocale.OmitGroupSeparator) 258 259 qt.QLocale.setDefault(uilocale) 260 261def ui_floattostring(f, maxdp=14): 262 """Convert float to string with more precision.""" 263 if not N.isfinite(f): 264 if N.isnan(f): 265 return 'nan' 266 if f < 0: 267 return '-inf' 268 return 'inf' 269 elif 1e-4 <= abs(f) <= 1e5 or f == 0: 270 s = ('%.'+str(maxdp)+'g') % f 271 # strip excess zeros to right 272 if s.find('.') >= 0: 273 s = s.rstrip('0').rstrip('.') 274 else: 275 s = ('%.'+str(maxdp)+'e') % f 276 # split into mantissa/exponent and strip extra zeros, etc 277 mant, expon = s.split('e') 278 mant = mant.rstrip('0').rstrip('.') 279 expon = int(expon) 280 s = '%se%i' % (mant, expon) 281 # make decimal point correct for local 282 s = s.replace('.', uilocale.decimalPoint()) 283 return s 284 285def ui_stringtofloat(s): 286 """Convert string to float, allowing for decimal point in different 287 locale.""" 288 s = s.replace(uilocale.decimalPoint(), '.') 289 return float(s) 290 291updateUILocale() 292