source: src/mlx/config.py@ 1097:f2ac841cffec

python3
Last change on this file since 1097:f2ac841cffec was 1097:f2ac841cffec, checked in by István Váradi <ivaradi@…>, 9 months ago

The browser cache can be cleared from the menu (re #368)

File size: 41.9 KB
Line 
1# -*- encoding: utf-8 -*-
2
3from . import const
4from .util import secondaryInstallation, utf2unicode
5
6import os
7import sys
8import traceback
9import configparser
10
11## @package mlx.config
12#
13# The handling of the configuration.
14#
15# The \ref Config class contains the main configuration and is capable of
16# loading and saving the configuration. It contains getters and setters for the
17# configuration options.
18#
19# Some parts of the configuration are not simple data items, like strings or
20# booleans, but more complicated data. These have their own class, like \ref
21# ApproachCallouts or \ref Checklist.
22
23#-------------------------------------------------------------------------------
24
25configPath = os.path.join(os.path.expanduser("~"),
26 "mlx.config" if os.name=="nt" else ".mlxrc") + \
27 ("-secondary" if secondaryInstallation else "")
28
29#-------------------------------------------------------------------------------
30
31if os.name=="nt":
32 _languageMap = { "en_GB" : "eng",
33 "hu_HU" : "hun" }
34
35#-------------------------------------------------------------------------------
36
37class Hotkey(object):
38 """A hotkey."""
39 def __init__(self, ctrl = False, shift = False, key = "0"):
40 """Construct the hotkey."""
41 self.ctrl = ctrl
42 self.shift = shift
43 self.key = key
44
45 def set(self, s):
46 """Set the hotkey from the given string."""
47 self.ctrl = "C" in s[:-1]
48 self.shift = "S" in s[:-1]
49 self.key = s[-1]
50
51 def __eq__(self, other):
52 """Check if the given hotkey is equal to the other one."""
53 return self.ctrl == other.ctrl and self.shift == other.shift and \
54 self.key == other.key
55
56 def __ne__(self, other):
57 """Check if the given hotkey is not equal to the other one."""
58 return not self==other
59
60 def __str__(self):
61 """Construct the hotkey to a string."""
62 s = ""
63 if self.ctrl: s += "C"
64 if self.shift: s += "S"
65 s += self.key
66 return s
67
68#-------------------------------------------------------------------------------
69
70class Checklist(object):
71 """A checklist for a certain aircraft type."""
72 # The name of the section of the checklists
73 SECTION="checklists"
74
75 @staticmethod
76 def fromConfig(config, aircraftType):
77 """Create a checklist for the given aircraft type from the given
78 config."""
79 baseName = "checklist." + const.icaoCodes[aircraftType] + "."
80 fileList = []
81 while True:
82 option = baseName + str(len(fileList))
83 if config.has_option(Checklist.SECTION, option):
84 fileList.append(config.get(Checklist.SECTION, option))
85 else:
86 break
87
88 return Checklist(fileList)
89
90 def __init__(self, fileList = None):
91 """Construct the check list with the given file list."""
92 self._fileList = [] if fileList is None else fileList[:]
93
94 def clone(self):
95 """Clone the checklist."""
96 return Checklist(self._fileList)
97
98 def toConfig(self, config, aircraftType):
99 """Add this checklist to the given config."""
100 baseName = "checklist." + const.icaoCodes[aircraftType] + "."
101 for index in range(0, len(self._fileList)):
102 option = baseName + str(index)
103 config.set(Checklist.SECTION, option,
104 self._fileList[index])
105
106 def __eq__(self, other):
107 """Determine if the checklist is equal to the given other one."""
108 return self._fileList == other._fileList
109
110 def __ne__(self, other):
111 """Determine if the checklist is not equal to the given other one."""
112 return not self==other
113
114 def __len__(self):
115 """Get the length of the file list."""
116 return len(self._fileList)
117
118 def __getitem__(self, index):
119 """Get the file with the given index."""
120 return self._fileList[index]
121
122 def __iter__(self):
123 """Iterate over the files."""
124 return iter(self._fileList)
125
126#-------------------------------------------------------------------------------
127
128class ApproachCallouts(object):
129 """The approach callouts for a certain aircraft type."""
130 # The name of the section of the approach callouts
131 SECTION="callouts"
132
133 @staticmethod
134 def fromConfig(config, aircraftType):
135 """Create a checklist for the given aircraft type from the given
136 config."""
137 baseName = "callouts." + const.icaoCodes[aircraftType] + "."
138 mapping = {}
139 while True:
140 option = baseName + str(len(mapping))
141 if config.has_option(ApproachCallouts.SECTION, option):
142 value = config.get(ApproachCallouts.SECTION, option)
143 (altitude, path) = value.split(",")
144 altitude = int(altitude.strip())
145 path = path.strip()
146 mapping[altitude] = path
147 else:
148 break
149
150 return ApproachCallouts(mapping)
151
152 def __init__(self, mapping = None):
153 """Construct the check list with the given mapping of altitudes to
154 files."""
155 self._mapping = {} if mapping is None else mapping.copy()
156
157 def clone(self):
158 """Clone the callout information."""
159 return ApproachCallouts(self._mapping)
160
161 def toConfig(self, config, aircraftType):
162 """Add this checklist to the given config."""
163 baseName = "callouts." + const.icaoCodes[aircraftType] + "."
164 index = 0
165 for (altitude, path) in self._mapping.items():
166 option = baseName + str(index)
167 config.set(ApproachCallouts.SECTION, option,
168 "%d, %s" % (altitude, path))
169 index += 1
170
171 def getAltitudes(self, descending = True):
172 """Get the altitudes in decreasing order by default."""
173 altitudes = list(self._mapping.keys())
174 altitudes.sort(reverse = descending)
175 return altitudes
176
177 def __bool__(self):
178 """Return if there is anything in the mapping."""
179 return not not self._mapping
180
181 def __eq__(self, other):
182 """Determine if the approach callout mapping is equal to the given
183 other one."""
184 return self._mapping == other._mapping
185
186 def __ne__(self, other):
187 """Determine if the approach callout mapping is not equal to the given
188 other one."""
189 return not self==other
190
191 def __len__(self):
192 """Get the number of elements in the mapping."""
193 return len(self._mapping)
194
195 def __getitem__(self, altitude):
196 """Get the file that is associated with the given altitude.
197
198 If no such file found, return None."""
199 return self._mapping[altitude] if altitude in self._mapping else None
200
201 def __iter__(self):
202 """Iterate over the pairs of altitudes and paths in decreasing order of
203 the altitude."""
204 altitudes = self.getAltitudes()
205
206 for altitude in altitudes:
207 yield (altitude, self._mapping[altitude])
208
209#-------------------------------------------------------------------------------
210
211class Config(object):
212 """Our configuration."""
213 DEFAULT_UPDATE_URL = "https://mlx.varadiistvan.hu/update"
214
215 _messageTypesSection = "messageTypes"
216
217 def __init__(self):
218 """Construct the configuration with default values."""
219
220 self._pilotID = ""
221 self._password = ""
222 self._rememberPassword = False
223
224 self._clearBrowserCacheOnStart = False
225 self._language = ""
226 self._hideMinimizedWindow = True
227 self._quitOnClose = False
228 self._onlineGateSystem = not secondaryInstallation
229 self._onlineACARS = not secondaryInstallation
230 self._flareTimeFromFS = False
231 self._syncFSTime = False
232 self._usingFS2Crew = False
233 self._iasSmoothingLength = -2
234 self._vsSmoothingLength = -2
235
236 self._useSimBrief = False
237 self._useInternalBrowserForSimBrief = False
238 self._simBriefUserName = ""
239 self._simBriefPassword = ""
240 self._rememberSimBriefPassword = False
241
242 self._pirepDirectory = None
243 self._pirepAutoSave = False
244
245 self._defaultMSFS = os.name=="nt"
246
247 self._enableSounds = not secondaryInstallation
248
249 self._pilotControlsSounds = True
250 self._pilotHotkey = Hotkey(ctrl = True, shift = False, key = "0")
251
252 self._taxiSoundOnPushback = False
253
254 self._enableApproachCallouts = False
255 self._speedbrakeAtTD = True
256
257 self._enableChecklists = False
258 self._checklistHotkey = Hotkey(ctrl = True, shift = True, key = "0")
259
260 self._autoUpdate = True
261 self._updateURL = Config.DEFAULT_UPDATE_URL
262
263 self._xplaneRemote = False
264 self._xplaneAddress = ""
265
266 self._messageTypeLevels = {}
267
268 self._checklists = {}
269 self._approachCallouts = {}
270 for aircraftType in const.aircraftTypes:
271 self._checklists[aircraftType] = Checklist()
272 self._approachCallouts[aircraftType] = ApproachCallouts()
273
274 self._modified = False
275
276 @property
277 def pilotID(self):
278 """Get the pilot ID."""
279 return self._pilotID
280
281 @pilotID.setter
282 def pilotID(self, pilotID):
283 """Set the pilot ID."""
284 if pilotID!=self._pilotID:
285 self._pilotID = pilotID
286 self._modified = True
287
288 @property
289 def password(self):
290 """Get the password."""
291 return self._password
292
293 @password.setter
294 def password(self, password):
295 """Set the password."""
296 if password!=self._password:
297 self._password = password
298 self._modified = True
299
300 @property
301 def rememberPassword(self):
302 """Get if we should remember the password."""
303 return self._rememberPassword
304
305 @rememberPassword.setter
306 def rememberPassword(self, rememberPassword):
307 """Set if we should remember the password."""
308 if rememberPassword!=self._rememberPassword:
309 self._rememberPassword = rememberPassword
310 self._modified = True
311
312 @property
313 def clearBrowseCacheOnStart(self):
314 """Get whether the browser cache should be cleared on start."""
315 return self._clearBrowserCacheOnStart
316
317 @clearBrowseCacheOnStart.setter
318 def clearBrowseCacheOnStart(self, clearBrowseCacheOnStart):
319 """Set whether the browser cache should be cleared on start."""
320 if clearBrowseCacheOnStart!=self._clearBrowserCacheOnStart:
321 self._clearBrowserCacheOnStart = clearBrowseCacheOnStart
322 self._modified = True
323
324 @property
325 def language(self):
326 """Get the language to use."""
327 return self._language
328
329 @language.setter
330 def language(self, language):
331 """Set the language to use."""
332 if language!=self._language:
333 self._language = language
334 self._modified = True
335
336 @property
337 def hideMinimizedWindow(self):
338 """Get whether a minimized window should be hidden."""
339 return self._hideMinimizedWindow
340
341 @hideMinimizedWindow.setter
342 def hideMinimizedWindow(self, hideMinimizedWindow):
343 """Set whether a minimized window should be hidden."""
344 if hideMinimizedWindow!=self._hideMinimizedWindow:
345 self._hideMinimizedWindow = hideMinimizedWindow
346 self._modified = True
347
348 @property
349 def quitOnClose(self):
350 """Get whether the application should quit when the close button is
351 clicked."""
352 return self._quitOnClose
353
354 @quitOnClose.setter
355 def quitOnClose(self, quitOnClose):
356 """Set whether the application should quit when the close button is
357 clicked."""
358 if quitOnClose!=self._quitOnClose:
359 self._quitOnClose = quitOnClose
360 self._modified = True
361
362 @property
363 def onlineGateSystem(self):
364 """Get whether the online gate system should be used."""
365 return self._onlineGateSystem
366
367 @onlineGateSystem.setter
368 def onlineGateSystem(self, onlineGateSystem):
369 """Set whether the online gate system should be used."""
370 if onlineGateSystem!=self._onlineGateSystem:
371 self._onlineGateSystem = onlineGateSystem
372 self._modified = True
373
374 @property
375 def onlineACARS(self):
376 """Get whether the online ACARS system should be used."""
377 return self._onlineACARS
378
379 @onlineACARS.setter
380 def onlineACARS(self, onlineACARS):
381 """Set whether the online ACARS system should be used."""
382 if onlineACARS!=self._onlineACARS:
383 self._onlineACARS = onlineACARS
384 self._modified = True
385
386 @property
387 def flareTimeFromFS(self):
388 """Get whether the flare time should be calculated from the time values
389 returned by the simulator."""
390 return self._flareTimeFromFS
391
392 @flareTimeFromFS.setter
393 def flareTimeFromFS(self, flareTimeFromFS):
394 """Set whether the flare time should be calculated from the time values
395 returned by the simulator."""
396 if flareTimeFromFS!=self._flareTimeFromFS:
397 self._flareTimeFromFS = flareTimeFromFS
398 self._modified = True
399
400 @property
401 def syncFSTime(self):
402 """Get whether the simulator's time should be synchronized with the
403 machine's clock."""
404 return self._syncFSTime
405
406 @syncFSTime.setter
407 def syncFSTime(self, syncFSTime):
408 """Set whether the simulator's time should be synchronized with the
409 machine's clock."""
410 if syncFSTime!=self._syncFSTime:
411 self._syncFSTime = syncFSTime
412 self._modified = True
413
414 @property
415 def usingFS2Crew(self):
416 """Get whether the FS2Crew addon is being used."""
417 return self._usingFS2Crew
418
419 @usingFS2Crew.setter
420 def usingFS2Crew(self, usingFS2Crew):
421 """Set whether the FS2Crew addon is being used."""
422 if usingFS2Crew!=self._usingFS2Crew:
423 self._usingFS2Crew = usingFS2Crew
424 self._modified = True
425
426 @property
427 def iasSmoothingLength(self):
428 """Get the number of samples over which the IAS is averaged for the
429 smoothed IAS calculation. It may be negative, in which case smoothing
430 is disabled, but we nevertheless store the number of seconds in case it
431 may become useful later."""
432 return self._iasSmoothingLength
433
434 @property
435 def realIASSmoothingLength(self):
436 """Get the real smoothing length of IAS."""
437 return max(self._iasSmoothingLength, 1)
438
439 @iasSmoothingLength.setter
440 def iasSmoothingLength(self, iasSmoothingLength):
441 """Set the number of samples over which the IAS is averaged for the
442 smoothed IAS calculation."""
443 if iasSmoothingLength!=self._iasSmoothingLength:
444 self._iasSmoothingLength = iasSmoothingLength
445 self._modified = True
446
447 @property
448 def vsSmoothingLength(self):
449 """Get the number of samples over which the VS is averaged for the
450 smoothed VS calculation. It may be negative, in which case smoothing
451 is disabled, but we nevertheless store the number of seconds in case it
452 may become useful later."""
453 return self._vsSmoothingLength
454
455 @property
456 def realVSSmoothingLength(self):
457 """Get the real smoothing length of VS."""
458 return max(self._vsSmoothingLength, 1)
459
460 @vsSmoothingLength.setter
461 def vsSmoothingLength(self, vsSmoothingLength):
462 """Set the number of samples over which the VS is averaged for the
463 smoothed VS calculation."""
464 if vsSmoothingLength!=self._vsSmoothingLength:
465 self._vsSmoothingLength = vsSmoothingLength
466 self._modified = True
467
468 @property
469 def useSimBrief(self):
470 """Check if SimBrief should be used."""
471 return self._useSimBrief
472
473 @useSimBrief.setter
474 def useSimBrief(self, useSimBrief):
475 """Check if SimBrief should be used."""
476 if self._useSimBrief != useSimBrief:
477 self._useSimBrief = useSimBrief
478 self._modified = True
479
480 @property
481 def useInternalBrowserForSimBrief(self):
482 """Get if we should use the internal browser to handle SimBrief."""
483 return self._useInternalBrowserForSimBrief
484
485 @useInternalBrowserForSimBrief.setter
486 def useInternalBrowserForSimBrief(self, useInternalBrowserForSimBrief):
487 """Set if we should use the internal browser to handle SimBrief."""
488 if useInternalBrowserForSimBrief!=self._useInternalBrowserForSimBrief:
489 self._useInternalBrowserForSimBrief = useInternalBrowserForSimBrief
490 self._modified = True
491
492 @property
493 def simBriefUserName(self):
494 """Get the SimBrief user name last used"""
495 return self._simBriefUserName
496
497 @simBriefUserName.setter
498 def simBriefUserName(self, simBriefUserName):
499 """Set the SimBrief user name to be used next."""
500 if self._simBriefUserName != simBriefUserName:
501 self._simBriefUserName = simBriefUserName
502 self._modified = True
503
504 @property
505 def simBriefPassword(self):
506 """Get the SimBrief password last used"""
507 return self._simBriefPassword
508
509 @simBriefPassword.setter
510 def simBriefPassword(self, simBriefPassword):
511 """Set the SimBrief password to be used next."""
512 if self._simBriefPassword != simBriefPassword:
513 self._simBriefPassword = simBriefPassword
514 self._modified = True
515
516 @property
517 def rememberSimBriefPassword(self):
518 """Get if we should remember the SimBrief password."""
519 return self._rememberSimBriefPassword
520
521 @rememberSimBriefPassword.setter
522 def rememberSimBriefPassword(self, rememberSimBriefPassword):
523 """Set if we should remember the SimBrief password."""
524 if rememberSimBriefPassword!=self._rememberSimBriefPassword:
525 self._rememberSimBriefPassword = rememberSimBriefPassword
526 self._modified = True
527
528 @property
529 def pirepDirectory(self):
530 """Get the directory offered by default when saving a PIREP."""
531 return self._pirepDirectory
532
533 @pirepDirectory.setter
534 def pirepDirectory(self, pirepDirectory):
535 """Get the directory offered by default when saving a PIREP."""
536 if pirepDirectory!=self._pirepDirectory and \
537 (pirepDirectory!="" or self._pirepDirectory is not None):
538 self._pirepDirectory = None if pirepDirectory=="" \
539 else pirepDirectory
540 if self._pirepDirectory is None:
541 self._pirepAutoSave = False
542 self._modified = True
543
544 @property
545 def pirepAutoSave(self):
546 """Get whether the PIREP should be saved automatically when it becomes
547 saveable."""
548 return self._pirepAutoSave
549
550 @pirepAutoSave.setter
551 def pirepAutoSave(self, pirepAutoSave):
552 """Set whether the PIREP should be saved automatically when it becomes
553 saveable."""
554 pirepAutoSave = pirepAutoSave and self._pirepDirectory is not None
555 if pirepAutoSave!=self._pirepAutoSave:
556 self._pirepAutoSave = pirepAutoSave
557 self._modified = True
558
559 @property
560 def defaultMSFS(self):
561 """Get if the default simulator type is MS FS."""
562 return self._defaultMSFS
563
564 @defaultMSFS.setter
565 def defaultMSFS(self, defaultMSFS):
566 """Set if the default simulator type is MS FS."""
567 if defaultMSFS!=self._defaultMSFS:
568 self._defaultMSFS = defaultMSFS
569 self._modified = True
570
571 def getMessageTypeLevel(self, messageType):
572 """Get the level for the given message type."""
573 return self._messageTypeLevels[messageType] \
574 if messageType in self._messageTypeLevels \
575 else const.MESSAGELEVEL_NONE
576
577 def isMessageTypeFS(self, messageType):
578 """Determine if the given message type is displayed in the
579 simulator."""
580 level = self.getMessageTypeLevel(messageType)
581 return level==const.MESSAGELEVEL_FS or \
582 level==const.MESSAGELEVEL_BOTH
583
584 def setMessageTypeLevel(self, messageType, level):
585 """Set the level of the given message type."""
586 if messageType not in self._messageTypeLevels or \
587 self._messageTypeLevels[messageType]!=level:
588 self._messageTypeLevels[messageType] = level
589 self._modified = True
590
591 @property
592 def enableSounds(self):
593 """Get whether background sounds are enabled."""
594 return self._enableSounds
595
596 @enableSounds.setter
597 def enableSounds(self, enableSounds):
598 """Set whether background sounds are enabled."""
599 if enableSounds!=self._enableSounds:
600 self._enableSounds = enableSounds
601 self._modified = True
602
603 @property
604 def pilotControlsSounds(self):
605 """Get whether the pilot controls the background sounds."""
606 return self._pilotControlsSounds
607
608 @pilotControlsSounds.setter
609 def pilotControlsSounds(self, pilotControlsSounds):
610 """Set whether the pilot controls the background sounds."""
611 if pilotControlsSounds!=self._pilotControlsSounds:
612 self._pilotControlsSounds = pilotControlsSounds
613 self._modified = True
614
615 @property
616 def pilotHotkey(self):
617 """Get the pilot's hotkey."""
618 return self._pilotHotkey
619
620 @pilotHotkey.setter
621 def pilotHotkey(self, pilotHotkey):
622 """Set the pilot's hotkey."""
623 if pilotHotkey!=self._pilotHotkey:
624 self._pilotHotkey = pilotHotkey
625 self._modified = True
626
627 @property
628 def taxiSoundOnPushback(self):
629 """Get whether the taxi sound should be played as soon as pushback starts."""
630 return self._taxiSoundOnPushback
631
632 @taxiSoundOnPushback.setter
633 def taxiSoundOnPushback(self, taxiSoundOnPushback):
634 """Set whether the taxi sound should be played as soon as pushback starts."""
635 if taxiSoundOnPushback!=self._taxiSoundOnPushback:
636 self._taxiSoundOnPushback = taxiSoundOnPushback
637 self._modified = True
638
639 @property
640 def enableApproachCallouts(self):
641 """Get whether the approach callouts should be played."""
642 return self._enableApproachCallouts
643
644 @enableApproachCallouts.setter
645 def enableApproachCallouts(self, enableApproachCallouts):
646 """Set whether the approach callouts should be played."""
647 if enableApproachCallouts!=self._enableApproachCallouts:
648 self._enableApproachCallouts = enableApproachCallouts
649 self._modified = True
650
651 @property
652 def speedbrakeAtTD(self):
653 """Get whether the speedbrake sounds should be played at touchdown."""
654 return self._speedbrakeAtTD
655
656 @speedbrakeAtTD.setter
657 def speedbrakeAtTD(self, speedbrakeAtTD):
658 """Set whether the speedbrake sounds should be played at touchdown."""
659 if speedbrakeAtTD!=self._speedbrakeAtTD:
660 self._speedbrakeAtTD = speedbrakeAtTD
661 self._modified = True
662
663 @property
664 def enableChecklists(self):
665 """Get whether aircraft-specific checklists should be played."""
666 return self._enableChecklists
667
668 @enableChecklists.setter
669 def enableChecklists(self, enableChecklists):
670 """Get whether aircraft-specific checklists should be played."""
671 if enableChecklists!=self._enableChecklists:
672 self._enableChecklists = enableChecklists
673 self._modified = True
674
675 @property
676 def checklistHotkey(self):
677 """Get the checklist hotkey."""
678 return self._checklistHotkey
679
680 @checklistHotkey.setter
681 def checklistHotkey(self, checklistHotkey):
682 """Set the checklist hotkey."""
683 if checklistHotkey!=self._checklistHotkey:
684 self._checklistHotkey = checklistHotkey
685 self._modified = True
686
687 @property
688 def autoUpdate(self):
689 """Get if an automatic update is needed."""
690 return self._autoUpdate
691
692 @autoUpdate.setter
693 def autoUpdate(self, autoUpdate):
694 """Set if an automatic update is needed."""
695 if autoUpdate!=self._autoUpdate:
696 self._autoUpdate = autoUpdate
697 self._modified = True
698
699 @property
700 def updateURL(self):
701 """Get the update URL."""
702 return self._updateURL
703
704 @updateURL.setter
705 def updateURL(self, updateURL):
706 """Set the update URL."""
707 if updateURL!=self._updateURL:
708 self._updateURL = updateURL
709 self._modified = True
710
711 @property
712 def xplaneRemote(self):
713 """Indicate if X-Plane should be accessed remotely."""
714 return self._xplaneRemote
715
716 @xplaneRemote.setter
717 def xplaneRemote(self, xplaneRemote):
718 """Set if X-Plane should be accessed remotely."""
719 if xplaneRemote!=self._xplaneRemote:
720 self._xplaneRemote = xplaneRemote
721 self._modified = True
722
723 @property
724 def xplaneAddress(self):
725 """Get the address of the machine running X-Plane"""
726 return self._xplaneAddress
727
728 @xplaneAddress.setter
729 def xplaneAddress(self, xplaneAddress):
730 """Set the address of the machine running X-Plane."""
731 if xplaneAddress!=self._xplaneAddress:
732 self._xplaneAddress = xplaneAddress
733 self._modified = True
734
735 def getChecklist(self, aircraftType):
736 """Get the checklist for the given aircraft type."""
737 return self._checklists[aircraftType]
738
739 def setChecklist(self, aircraftType, checklist):
740 """Set the checklist for the given aircraft type."""
741 if checklist!=self._checklists[aircraftType]:
742 self._checklists[aircraftType] = checklist.clone()
743 self._modified = True
744
745 def getApproachCallouts(self, aircraftType):
746 """Get the approach callouts for the given aircraft type."""
747 return self._approachCallouts[aircraftType]
748
749 def setApproachCallouts(self, aircraftType, approachCallouts):
750 """Set the approach callouts for the given aircraft type."""
751 if not approachCallouts==self._approachCallouts[aircraftType]:
752 self._approachCallouts[aircraftType] = approachCallouts.clone()
753 self._modified = True
754
755 def load(self):
756 """Load the configuration from its default location."""
757 try:
758 config = configparser.RawConfigParser()
759 config.read(configPath)
760 except:
761 traceback.print_exc()
762 return
763
764 self._pilotID = self._get(config, "login", "id", "")
765 self._password = self._get(config, "login", "password", "")
766 self._rememberPassword = self._getBoolean(config, "login",
767 "rememberPassword", False)
768
769 self._clearBrowserCacheOnStart = \
770 self._getBoolean(config, "general",
771 "clearBrowseCacheOnStart", False)
772
773 self._language = self._get(config, "general", "language", "")
774
775 self._hideMinimizedWindow = self._getBoolean(config, "general",
776 "hideMinimizedWindow",
777 True)
778 self._quitOnClose = self._getBoolean(config, "general",
779 "quitOnClose", False)
780
781 self._onlineGateSystem = self._getBoolean(config, "general",
782 "onlineGateSystem",
783 not secondaryInstallation)
784 self._onlineACARS = self._getBoolean(config, "general",
785 "onlineACARS",
786 not secondaryInstallation)
787 self._flareTimeFromFS = self._getBoolean(config, "general",
788 "flareTimeFromFS",
789 False)
790 self._syncFSTime = self._getBoolean(config, "general",
791 "syncFSTime",
792 False)
793 self._usingFS2Crew = self._getBoolean(config, "general",
794 "usingFS2Crew",
795 False)
796 self._iasSmoothingLength = int(self._get(config, "general",
797 "iasSmoothingLength",
798 -2))
799 self._vsSmoothingLength = int(self._get(config, "general",
800 "vsSmoothingLength",
801 -2))
802
803 self._useSimBrief = self._getBoolean(config, "simbrief",
804 "use", False)
805 self._useInternalBrowserForSimBrief = self._getBoolean(config, "simbrief",
806 "useInternalBrowser",
807 False)
808 self._simBriefUserName = self._get(config, "simbrief",
809 "username", "")
810 self._simBriefPassword = self._get(config, "simbrief",
811 "password", "")
812 self._rememberSimBriefPassword = self._getBoolean(config, "simbrief",
813 "rememberPassword",
814 False)
815
816 self._pirepDirectory = self._get(config, "general",
817 "pirepDirectory", None)
818
819 self._pirepAutoSave = self._getBoolean(config, "general",
820 "pirepAutoSave", False)
821 if self._pirepDirectory is None:
822 self._pirepAutoSave = False
823
824 self._messageTypeLevels = {}
825 for messageType in const.messageTypes:
826 self._messageTypeLevels[messageType] = \
827 self._getMessageTypeLevel(config, messageType)
828
829 self._enableSounds = self._getBoolean(config, "sounds", "enable",
830 not secondaryInstallation)
831 self._pilotControlsSounds = self._getBoolean(config, "sounds",
832 "pilotControls", True)
833 self._pilotHotkey.set(self._get(config, "sounds",
834 "pilotHotkey", "C0"))
835
836 self._taxiSoundOnPushback = \
837 self._getBoolean(config, "sounds", "taxiSoundOnPushback", False)
838 self._enableApproachCallouts = \
839 self._getBoolean(config, "sounds", "enableApproachCallouts", False)
840 self._speedbrakeAtTD = self._getBoolean(config, "sounds",
841 "speedbrakeAtTD", True)
842
843 self._enableChecklists = self._getBoolean(config, "sounds",
844 "enableChecklists", False)
845 self._checklistHotkey.set(self._get(config, "sounds",
846 "checklistHotkey", "CS0"))
847
848 self._autoUpdate = self._getBoolean(config, "update", "auto", True)
849 self._updateURL = self._get(config, "update", "url",
850 Config.DEFAULT_UPDATE_URL)
851
852 for aircraftType in const.aircraftTypes:
853 self._checklists[aircraftType] = \
854 Checklist.fromConfig(config, aircraftType)
855 self._approachCallouts[aircraftType] = \
856 ApproachCallouts.fromConfig(config, aircraftType)
857
858 self._defaultMSFS = self._getBoolean(config, "general",
859 "defaultMSFS", os.name=="nt")
860
861 self._xplaneRemote = self._getBoolean(config, "general",
862 "xplaneRemote", False)
863 self._xplaneAddress = self._get(config, "general",
864 "xplaneAddress", "")
865
866 self._modified = False
867
868 def save(self):
869 """Save the configuration file if it has been modified."""
870 if not self._modified:
871 return
872
873 config = configparser.RawConfigParser()
874
875 config.add_section("login")
876 config.set("login", "id", self._pilotID)
877 config.set("login", "password", self._password)
878 config.set("login", "rememberPassword",
879 "yes" if self._rememberPassword else "no")
880
881 config.add_section("general")
882 config.set("general", "clearBrowseCacheOnStart",
883 "yes" if self._clearBrowserCacheOnStart else "no")
884 if self._language:
885 config.set("general", "language", self._language)
886 config.set("general", "hideMinimizedWindow",
887 "yes" if self._hideMinimizedWindow else "no")
888 config.set("general", "quitOnClose",
889 "yes" if self._quitOnClose else "no")
890 config.set("general", "onlineGateSystem",
891 "yes" if self._onlineGateSystem else "no")
892 config.set("general", "onlineACARS",
893 "yes" if self._onlineACARS else "no")
894 config.set("general", "flareTimeFromFS",
895 "yes" if self._flareTimeFromFS else "no")
896 config.set("general", "syncFSTime",
897 "yes" if self._syncFSTime else "no")
898 config.set("general", "usingFS2Crew",
899 "yes" if self._usingFS2Crew else "no")
900 config.set("general", "iasSmoothingLength",
901 str(self._iasSmoothingLength))
902 config.set("general", "vsSmoothingLength",
903 str(self._vsSmoothingLength))
904
905 config.add_section("simbrief")
906 config.set("simbrief", "use",
907 "yes" if self._useSimBrief else "no")
908 config.set("simbrief", "useInternalBrowser",
909 "yes" if self._useInternalBrowserForSimBrief else "no")
910 config.set("simbrief", "username", self._simBriefUserName)
911 config.set("simbrief", "password", self._simBriefPassword)
912 config.set("simbrief", "rememberPassword",
913 "yes" if self._rememberSimBriefPassword else "no")
914
915 if self._pirepDirectory is not None:
916 config.set("general", "pirepDirectory", self._pirepDirectory)
917 config.set("general", "pirepAutoSave",
918 "yes" if self._pirepAutoSave else "no")
919
920 config.set("general", "defaultMSFS",
921 "yes" if self._defaultMSFS else "no")
922
923 config.set("general", "xplaneRemote",
924 "yes" if self._xplaneRemote else "no")
925 config.set("general", "xplaneAddress", self._xplaneAddress)
926
927 config.add_section(Config._messageTypesSection)
928 for messageType in const.messageTypes:
929 if messageType in self._messageTypeLevels:
930 option = self._getMessageTypeLevelOptionName(messageType)
931 level = self._messageTypeLevels[messageType]
932 config.set(Config._messageTypesSection, option,
933 const.messageLevel2string(level))
934
935 config.add_section("sounds")
936 config.set("sounds", "enable",
937 "yes" if self._enableSounds else "no")
938 config.set("sounds", "pilotControls",
939 "yes" if self._pilotControlsSounds else "no")
940 config.set("sounds", "pilotHotkey", str(self._pilotHotkey))
941 config.set("sounds", "taxiSoundOnPushback",
942 "yes" if self._taxiSoundOnPushback else "no")
943 config.set("sounds", "enableApproachCallouts",
944 "yes" if self._enableApproachCallouts else "no")
945 config.set("sounds", "speedbrakeAtTD",
946 "yes" if self._speedbrakeAtTD else "no")
947
948 config.set("sounds", "enableChecklists",
949 "yes" if self._enableChecklists else "no")
950 config.set("sounds", "checklistHotkey",
951 str(self._checklistHotkey))
952
953 config.add_section("update")
954 config.set("update", "auto",
955 "yes" if self._autoUpdate else "no")
956 config.set("update", "url", self._updateURL)
957
958 config.add_section(Checklist.SECTION)
959 config.add_section(ApproachCallouts.SECTION)
960 for aircraftType in const.aircraftTypes:
961 self._checklists[aircraftType].toConfig(config, aircraftType)
962 self._approachCallouts[aircraftType].toConfig(config, aircraftType)
963
964 try:
965 fd = os.open(configPath, os.O_CREAT|os.O_TRUNC|os.O_WRONLY,
966 0o600)
967 with os.fdopen(fd, "wt") as f:
968 config.write(f)
969 self._modified = False
970
971 print("Configuration saved:")
972 self.log()
973
974 except Exception as e:
975 print("Failed to update config: " + \
976 utf2unicode(str(e)), file=sys.stderr)
977
978 def _getBoolean(self, config, section, option, default):
979 """Get the given option as a boolean, if found in the given config,
980 otherwise the default."""
981 return config.getboolean(section, option) \
982 if config.has_option(section, option) \
983 else default
984
985 def _get(self, config, section, option, default):
986 """Get the given option as a string, if found in the given config,
987 otherwise the default."""
988 return config.get(section, option) \
989 if config.has_option(section, option) \
990 else default
991
992 def _getMessageTypeLevel(self, config, messageType):
993 """Get the message type level for the given message type."""
994 option = self._getMessageTypeLevelOptionName(messageType)
995 if config.has_option(Config._messageTypesSection, option):
996 value = config.get(Config._messageTypesSection, option)
997 return const.string2messageLevel(value)
998 elif secondaryInstallation:
999 return const.MESSAGELEVEL_NONE
1000 elif messageType in [const.MESSAGETYPE_LOGGER_ERROR,
1001 const.MESSAGETYPE_FAULT,
1002 const.MESSAGETYPE_NOGO,
1003 const.MESSAGETYPE_GATE_SYSTEM,
1004 const.MESSAGETYPE_HELP]:
1005 return const.MESSAGELEVEL_BOTH
1006 else:
1007 return const.MESSAGELEVEL_FS
1008
1009 def _getMessageTypeLevelOptionName(self, messageType):
1010 """Get the option name for the given message type level."""
1011 return const.messageType2string(messageType)
1012
1013 def setupLocale(self):
1014 """Setup the locale based on the language set.
1015
1016 Return True if a specific language was set, False otherwise."""
1017 import locale
1018 if self._language:
1019 print("Setting up locale for", self._language)
1020 os.environ["LANGUAGE"] = self._language
1021 langAndEncoding = self._language + "." + locale.getpreferredencoding()
1022 os.environ["LANG"] = langAndEncoding
1023 os.environ["LC_MESSAGES"] = langAndEncoding
1024 os.environ["LC_COLLATE"] = langAndEncoding
1025 os.environ["LC_CTYPE"] = langAndEncoding
1026 os.environ["LC_MONETARY"] = langAndEncoding
1027 os.environ["LC_NUMERIC"] = langAndEncoding
1028 os.environ["LC_TIME"] = langAndEncoding
1029 return True
1030 else:
1031 return False
1032
1033 def getLanguage(self):
1034 """Get the language to be used."""
1035 import locale
1036 if self._language:
1037 if os.name=="nt":
1038 if self._language in _languageMap:
1039 locale.setlocale(locale.LC_ALL, _languageMap[self._language])
1040 else:
1041 locale.setlocale(locale.LC_ALL, "")
1042 else:
1043 locale.setlocale(locale.LC_ALL, (self._language,
1044 locale.getpreferredencoding()))
1045 return self._language
1046 else:
1047 locale.setlocale(locale.LC_ALL, "")
1048 return locale.getdefaultlocale()[0]
1049
1050 def log(self):
1051 """Log the configuration by printing the values"""
1052 print(" pilot ID:", self._pilotID)
1053 print(" rememberPassword:", self._rememberPassword)
1054
1055 print(" clearBrowseCacheOnStart:", self._clearBrowserCacheOnStart)
1056 print(" language:", self._language)
1057
1058 print(" hideMinimizedWindow:", self._hideMinimizedWindow)
1059 print(" quitOnClose:", self._quitOnClose)
1060
1061 print(" onlineGateSystem:", self._onlineGateSystem)
1062 print(" onlineACARS:", self._onlineACARS)
1063
1064 print(" flareTimeFromFS:", self._flareTimeFromFS)
1065 print(" syncFSTime:", self._syncFSTime)
1066 print(" usingFS2Crew:", self._usingFS2Crew)
1067
1068 print(" iasSmoothingLength:", self._iasSmoothingLength)
1069 print(" vsSmoothingLength:", self._vsSmoothingLength)
1070
1071 print(" useSimBrief:", self._useSimBrief)
1072 print(" useInternalBrowserForSimBrief:", self._useInternalBrowserForSimBrief)
1073 print(" simBriefUserName:", self._simBriefUserName)
1074 print(" rememberSimBriefPassword:", self._rememberSimBriefPassword)
1075
1076 print(" pirepDirectory:", self._pirepDirectory)
1077 print(" pirepAutoSave:", self._pirepAutoSave)
1078
1079 print(" defaultMSFS:", self._defaultMSFS)
1080 print(" xplaneRemote:", self._xplaneRemote)
1081 print(" xplaneAddress:", self._xplaneAddress)
1082
1083 print(" enableSounds:", self._enableSounds)
1084
1085 print(" pilotControlsSounds:", self._pilotControlsSounds)
1086 print(" pilotHotkey:", str(self._pilotHotkey))
1087
1088 print(" taxiSoundOnPushback:", self._taxiSoundOnPushback)
1089
1090 print(" enableApproachCallouts:", self._enableApproachCallouts)
1091 print(" speedbrakeAtTD:", self._speedbrakeAtTD)
1092
1093 print(" enableChecklists:", self._enableChecklists)
1094 print(" checklistHotkey:", str(self._checklistHotkey))
1095
1096 print(" autoUpdate:", self._autoUpdate)
1097 print(" updateURL:", self._updateURL)
1098
1099 print(" messageTypeLevels:")
1100 for (type, level) in self._messageTypeLevels.items():
1101 print(" %s: %s" % (const.messageType2string(type),
1102 const.messageLevel2string(level)))
1103
1104 print(" checklists:")
1105 for (type, checklist) in self._checklists.items():
1106 print(" %s:" % (const.icaoCodes[type],))
1107 for path in checklist:
1108 print(" " + path)
1109
1110 print(" approachCallouts:")
1111 for (type, approachCallouts) in self._approachCallouts.items():
1112 print(" %s:" % (const.icaoCodes[type],))
1113 for (altitude, path) in approachCallouts:
1114 print(" %d: %s" % (altitude, path))
1115
1116#-------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.