source: src/mlx/config.py@ 1094:a2a4b6462f53

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

The taxi sounds can be started on pushback (re #367)

File size: 41.0 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._language = ""
225 self._hideMinimizedWindow = True
226 self._quitOnClose = False
227 self._onlineGateSystem = not secondaryInstallation
228 self._onlineACARS = not secondaryInstallation
229 self._flareTimeFromFS = False
230 self._syncFSTime = False
231 self._usingFS2Crew = False
232 self._iasSmoothingLength = -2
233 self._vsSmoothingLength = -2
234
235 self._useSimBrief = False
236 self._useInternalBrowserForSimBrief = False
237 self._simBriefUserName = ""
238 self._simBriefPassword = ""
239 self._rememberSimBriefPassword = False
240
241 self._pirepDirectory = None
242 self._pirepAutoSave = False
243
244 self._defaultMSFS = os.name=="nt"
245
246 self._enableSounds = not secondaryInstallation
247
248 self._pilotControlsSounds = True
249 self._pilotHotkey = Hotkey(ctrl = True, shift = False, key = "0")
250
251 self._taxiSoundOnPushback = False
252
253 self._enableApproachCallouts = False
254 self._speedbrakeAtTD = True
255
256 self._enableChecklists = False
257 self._checklistHotkey = Hotkey(ctrl = True, shift = True, key = "0")
258
259 self._autoUpdate = True
260 self._updateURL = Config.DEFAULT_UPDATE_URL
261
262 self._xplaneRemote = False
263 self._xplaneAddress = ""
264
265 self._messageTypeLevels = {}
266
267 self._checklists = {}
268 self._approachCallouts = {}
269 for aircraftType in const.aircraftTypes:
270 self._checklists[aircraftType] = Checklist()
271 self._approachCallouts[aircraftType] = ApproachCallouts()
272
273 self._modified = False
274
275 @property
276 def pilotID(self):
277 """Get the pilot ID."""
278 return self._pilotID
279
280 @pilotID.setter
281 def pilotID(self, pilotID):
282 """Set the pilot ID."""
283 if pilotID!=self._pilotID:
284 self._pilotID = pilotID
285 self._modified = True
286
287 @property
288 def password(self):
289 """Get the password."""
290 return self._password
291
292 @password.setter
293 def password(self, password):
294 """Set the password."""
295 if password!=self._password:
296 self._password = password
297 self._modified = True
298
299 @property
300 def rememberPassword(self):
301 """Get if we should remember the password."""
302 return self._rememberPassword
303
304 @rememberPassword.setter
305 def rememberPassword(self, rememberPassword):
306 """Set if we should remember the password."""
307 if rememberPassword!=self._rememberPassword:
308 self._rememberPassword = rememberPassword
309 self._modified = True
310
311 @property
312 def language(self):
313 """Get the language to use."""
314 return self._language
315
316 @language.setter
317 def language(self, language):
318 """Set the language to use."""
319 if language!=self._language:
320 self._language = language
321 self._modified = True
322
323 @property
324 def hideMinimizedWindow(self):
325 """Get whether a minimized window should be hidden."""
326 return self._hideMinimizedWindow
327
328 @hideMinimizedWindow.setter
329 def hideMinimizedWindow(self, hideMinimizedWindow):
330 """Set whether a minimized window should be hidden."""
331 if hideMinimizedWindow!=self._hideMinimizedWindow:
332 self._hideMinimizedWindow = hideMinimizedWindow
333 self._modified = True
334
335 @property
336 def quitOnClose(self):
337 """Get whether the application should quit when the close button is
338 clicked."""
339 return self._quitOnClose
340
341 @quitOnClose.setter
342 def quitOnClose(self, quitOnClose):
343 """Set whether the application should quit when the close button is
344 clicked."""
345 if quitOnClose!=self._quitOnClose:
346 self._quitOnClose = quitOnClose
347 self._modified = True
348
349 @property
350 def onlineGateSystem(self):
351 """Get whether the online gate system should be used."""
352 return self._onlineGateSystem
353
354 @onlineGateSystem.setter
355 def onlineGateSystem(self, onlineGateSystem):
356 """Set whether the online gate system should be used."""
357 if onlineGateSystem!=self._onlineGateSystem:
358 self._onlineGateSystem = onlineGateSystem
359 self._modified = True
360
361 @property
362 def onlineACARS(self):
363 """Get whether the online ACARS system should be used."""
364 return self._onlineACARS
365
366 @onlineACARS.setter
367 def onlineACARS(self, onlineACARS):
368 """Set whether the online ACARS system should be used."""
369 if onlineACARS!=self._onlineACARS:
370 self._onlineACARS = onlineACARS
371 self._modified = True
372
373 @property
374 def flareTimeFromFS(self):
375 """Get whether the flare time should be calculated from the time values
376 returned by the simulator."""
377 return self._flareTimeFromFS
378
379 @flareTimeFromFS.setter
380 def flareTimeFromFS(self, flareTimeFromFS):
381 """Set whether the flare time should be calculated from the time values
382 returned by the simulator."""
383 if flareTimeFromFS!=self._flareTimeFromFS:
384 self._flareTimeFromFS = flareTimeFromFS
385 self._modified = True
386
387 @property
388 def syncFSTime(self):
389 """Get whether the simulator's time should be synchronized with the
390 machine's clock."""
391 return self._syncFSTime
392
393 @syncFSTime.setter
394 def syncFSTime(self, syncFSTime):
395 """Set whether the simulator's time should be synchronized with the
396 machine's clock."""
397 if syncFSTime!=self._syncFSTime:
398 self._syncFSTime = syncFSTime
399 self._modified = True
400
401 @property
402 def usingFS2Crew(self):
403 """Get whether the FS2Crew addon is being used."""
404 return self._usingFS2Crew
405
406 @usingFS2Crew.setter
407 def usingFS2Crew(self, usingFS2Crew):
408 """Set whether the FS2Crew addon is being used."""
409 if usingFS2Crew!=self._usingFS2Crew:
410 self._usingFS2Crew = usingFS2Crew
411 self._modified = True
412
413 @property
414 def iasSmoothingLength(self):
415 """Get the number of samples over which the IAS is averaged for the
416 smoothed IAS calculation. It may be negative, in which case smoothing
417 is disabled, but we nevertheless store the number of seconds in case it
418 may become useful later."""
419 return self._iasSmoothingLength
420
421 @property
422 def realIASSmoothingLength(self):
423 """Get the real smoothing length of IAS."""
424 return max(self._iasSmoothingLength, 1)
425
426 @iasSmoothingLength.setter
427 def iasSmoothingLength(self, iasSmoothingLength):
428 """Set the number of samples over which the IAS is averaged for the
429 smoothed IAS calculation."""
430 if iasSmoothingLength!=self._iasSmoothingLength:
431 self._iasSmoothingLength = iasSmoothingLength
432 self._modified = True
433
434 @property
435 def vsSmoothingLength(self):
436 """Get the number of samples over which the VS is averaged for the
437 smoothed VS calculation. It may be negative, in which case smoothing
438 is disabled, but we nevertheless store the number of seconds in case it
439 may become useful later."""
440 return self._vsSmoothingLength
441
442 @property
443 def realVSSmoothingLength(self):
444 """Get the real smoothing length of VS."""
445 return max(self._vsSmoothingLength, 1)
446
447 @vsSmoothingLength.setter
448 def vsSmoothingLength(self, vsSmoothingLength):
449 """Set the number of samples over which the VS is averaged for the
450 smoothed VS calculation."""
451 if vsSmoothingLength!=self._vsSmoothingLength:
452 self._vsSmoothingLength = vsSmoothingLength
453 self._modified = True
454
455 @property
456 def useSimBrief(self):
457 """Check if SimBrief should be used."""
458 return self._useSimBrief
459
460 @useSimBrief.setter
461 def useSimBrief(self, useSimBrief):
462 """Check if SimBrief should be used."""
463 if self._useSimBrief != useSimBrief:
464 self._useSimBrief = useSimBrief
465 self._modified = True
466
467 @property
468 def useInternalBrowserForSimBrief(self):
469 """Get if we should use the internal browser to handle SimBrief."""
470 return self._useInternalBrowserForSimBrief
471
472 @useInternalBrowserForSimBrief.setter
473 def useInternalBrowserForSimBrief(self, useInternalBrowserForSimBrief):
474 """Set if we should use the internal browser to handle SimBrief."""
475 if useInternalBrowserForSimBrief!=self._useInternalBrowserForSimBrief:
476 self._useInternalBrowserForSimBrief = useInternalBrowserForSimBrief
477 self._modified = True
478
479 @property
480 def simBriefUserName(self):
481 """Get the SimBrief user name last used"""
482 return self._simBriefUserName
483
484 @simBriefUserName.setter
485 def simBriefUserName(self, simBriefUserName):
486 """Set the SimBrief user name to be used next."""
487 if self._simBriefUserName != simBriefUserName:
488 self._simBriefUserName = simBriefUserName
489 self._modified = True
490
491 @property
492 def simBriefPassword(self):
493 """Get the SimBrief password last used"""
494 return self._simBriefPassword
495
496 @simBriefPassword.setter
497 def simBriefPassword(self, simBriefPassword):
498 """Set the SimBrief password to be used next."""
499 if self._simBriefPassword != simBriefPassword:
500 self._simBriefPassword = simBriefPassword
501 self._modified = True
502
503 @property
504 def rememberSimBriefPassword(self):
505 """Get if we should remember the SimBrief password."""
506 return self._rememberSimBriefPassword
507
508 @rememberSimBriefPassword.setter
509 def rememberSimBriefPassword(self, rememberSimBriefPassword):
510 """Set if we should remember the SimBrief password."""
511 if rememberSimBriefPassword!=self._rememberSimBriefPassword:
512 self._rememberSimBriefPassword = rememberSimBriefPassword
513 self._modified = True
514
515 @property
516 def pirepDirectory(self):
517 """Get the directory offered by default when saving a PIREP."""
518 return self._pirepDirectory
519
520 @pirepDirectory.setter
521 def pirepDirectory(self, pirepDirectory):
522 """Get the directory offered by default when saving a PIREP."""
523 if pirepDirectory!=self._pirepDirectory and \
524 (pirepDirectory!="" or self._pirepDirectory is not None):
525 self._pirepDirectory = None if pirepDirectory=="" \
526 else pirepDirectory
527 if self._pirepDirectory is None:
528 self._pirepAutoSave = False
529 self._modified = True
530
531 @property
532 def pirepAutoSave(self):
533 """Get whether the PIREP should be saved automatically when it becomes
534 saveable."""
535 return self._pirepAutoSave
536
537 @pirepAutoSave.setter
538 def pirepAutoSave(self, pirepAutoSave):
539 """Set whether the PIREP should be saved automatically when it becomes
540 saveable."""
541 pirepAutoSave = pirepAutoSave and self._pirepDirectory is not None
542 if pirepAutoSave!=self._pirepAutoSave:
543 self._pirepAutoSave = pirepAutoSave
544 self._modified = True
545
546 @property
547 def defaultMSFS(self):
548 """Get if the default simulator type is MS FS."""
549 return self._defaultMSFS
550
551 @defaultMSFS.setter
552 def defaultMSFS(self, defaultMSFS):
553 """Set if the default simulator type is MS FS."""
554 if defaultMSFS!=self._defaultMSFS:
555 self._defaultMSFS = defaultMSFS
556 self._modified = True
557
558 def getMessageTypeLevel(self, messageType):
559 """Get the level for the given message type."""
560 return self._messageTypeLevels[messageType] \
561 if messageType in self._messageTypeLevels \
562 else const.MESSAGELEVEL_NONE
563
564 def isMessageTypeFS(self, messageType):
565 """Determine if the given message type is displayed in the
566 simulator."""
567 level = self.getMessageTypeLevel(messageType)
568 return level==const.MESSAGELEVEL_FS or \
569 level==const.MESSAGELEVEL_BOTH
570
571 def setMessageTypeLevel(self, messageType, level):
572 """Set the level of the given message type."""
573 if messageType not in self._messageTypeLevels or \
574 self._messageTypeLevels[messageType]!=level:
575 self._messageTypeLevels[messageType] = level
576 self._modified = True
577
578 @property
579 def enableSounds(self):
580 """Get whether background sounds are enabled."""
581 return self._enableSounds
582
583 @enableSounds.setter
584 def enableSounds(self, enableSounds):
585 """Set whether background sounds are enabled."""
586 if enableSounds!=self._enableSounds:
587 self._enableSounds = enableSounds
588 self._modified = True
589
590 @property
591 def pilotControlsSounds(self):
592 """Get whether the pilot controls the background sounds."""
593 return self._pilotControlsSounds
594
595 @pilotControlsSounds.setter
596 def pilotControlsSounds(self, pilotControlsSounds):
597 """Set whether the pilot controls the background sounds."""
598 if pilotControlsSounds!=self._pilotControlsSounds:
599 self._pilotControlsSounds = pilotControlsSounds
600 self._modified = True
601
602 @property
603 def pilotHotkey(self):
604 """Get the pilot's hotkey."""
605 return self._pilotHotkey
606
607 @pilotHotkey.setter
608 def pilotHotkey(self, pilotHotkey):
609 """Set the pilot's hotkey."""
610 if pilotHotkey!=self._pilotHotkey:
611 self._pilotHotkey = pilotHotkey
612 self._modified = True
613
614 @property
615 def taxiSoundOnPushback(self):
616 """Get whether the taxi sound should be played as soon as pushback starts."""
617 return self._taxiSoundOnPushback
618
619 @taxiSoundOnPushback.setter
620 def taxiSoundOnPushback(self, taxiSoundOnPushback):
621 """Set whether the taxi sound should be played as soon as pushback starts."""
622 if taxiSoundOnPushback!=self._taxiSoundOnPushback:
623 self._taxiSoundOnPushback = taxiSoundOnPushback
624 self._modified = True
625
626 @property
627 def enableApproachCallouts(self):
628 """Get whether the approach callouts should be played."""
629 return self._enableApproachCallouts
630
631 @enableApproachCallouts.setter
632 def enableApproachCallouts(self, enableApproachCallouts):
633 """Set whether the approach callouts should be played."""
634 if enableApproachCallouts!=self._enableApproachCallouts:
635 self._enableApproachCallouts = enableApproachCallouts
636 self._modified = True
637
638 @property
639 def speedbrakeAtTD(self):
640 """Get whether the speedbrake sounds should be played at touchdown."""
641 return self._speedbrakeAtTD
642
643 @speedbrakeAtTD.setter
644 def speedbrakeAtTD(self, speedbrakeAtTD):
645 """Set whether the speedbrake sounds should be played at touchdown."""
646 if speedbrakeAtTD!=self._speedbrakeAtTD:
647 self._speedbrakeAtTD = speedbrakeAtTD
648 self._modified = True
649
650 @property
651 def enableChecklists(self):
652 """Get whether aircraft-specific checklists should be played."""
653 return self._enableChecklists
654
655 @enableChecklists.setter
656 def enableChecklists(self, enableChecklists):
657 """Get whether aircraft-specific checklists should be played."""
658 if enableChecklists!=self._enableChecklists:
659 self._enableChecklists = enableChecklists
660 self._modified = True
661
662 @property
663 def checklistHotkey(self):
664 """Get the checklist hotkey."""
665 return self._checklistHotkey
666
667 @checklistHotkey.setter
668 def checklistHotkey(self, checklistHotkey):
669 """Set the checklist hotkey."""
670 if checklistHotkey!=self._checklistHotkey:
671 self._checklistHotkey = checklistHotkey
672 self._modified = True
673
674 @property
675 def autoUpdate(self):
676 """Get if an automatic update is needed."""
677 return self._autoUpdate
678
679 @autoUpdate.setter
680 def autoUpdate(self, autoUpdate):
681 """Set if an automatic update is needed."""
682 if autoUpdate!=self._autoUpdate:
683 self._autoUpdate = autoUpdate
684 self._modified = True
685
686 @property
687 def updateURL(self):
688 """Get the update URL."""
689 return self._updateURL
690
691 @updateURL.setter
692 def updateURL(self, updateURL):
693 """Set the update URL."""
694 if updateURL!=self._updateURL:
695 self._updateURL = updateURL
696 self._modified = True
697
698 @property
699 def xplaneRemote(self):
700 """Indicate if X-Plane should be accessed remotely."""
701 return self._xplaneRemote
702
703 @xplaneRemote.setter
704 def xplaneRemote(self, xplaneRemote):
705 """Set if X-Plane should be accessed remotely."""
706 if xplaneRemote!=self._xplaneRemote:
707 self._xplaneRemote = xplaneRemote
708 self._modified = True
709
710 @property
711 def xplaneAddress(self):
712 """Get the address of the machine running X-Plane"""
713 return self._xplaneAddress
714
715 @xplaneAddress.setter
716 def xplaneAddress(self, xplaneAddress):
717 """Set the address of the machine running X-Plane."""
718 if xplaneAddress!=self._xplaneAddress:
719 self._xplaneAddress = xplaneAddress
720 self._modified = True
721
722 def getChecklist(self, aircraftType):
723 """Get the checklist for the given aircraft type."""
724 return self._checklists[aircraftType]
725
726 def setChecklist(self, aircraftType, checklist):
727 """Set the checklist for the given aircraft type."""
728 if checklist!=self._checklists[aircraftType]:
729 self._checklists[aircraftType] = checklist.clone()
730 self._modified = True
731
732 def getApproachCallouts(self, aircraftType):
733 """Get the approach callouts for the given aircraft type."""
734 return self._approachCallouts[aircraftType]
735
736 def setApproachCallouts(self, aircraftType, approachCallouts):
737 """Set the approach callouts for the given aircraft type."""
738 if not approachCallouts==self._approachCallouts[aircraftType]:
739 self._approachCallouts[aircraftType] = approachCallouts.clone()
740 self._modified = True
741
742 def load(self):
743 """Load the configuration from its default location."""
744 try:
745 config = configparser.RawConfigParser()
746 config.read(configPath)
747 except:
748 traceback.print_exc()
749 return
750
751 self._pilotID = self._get(config, "login", "id", "")
752 self._password = self._get(config, "login", "password", "")
753 self._rememberPassword = self._getBoolean(config, "login",
754 "rememberPassword", False)
755
756 self._language = self._get(config, "general", "language", "")
757
758 self._hideMinimizedWindow = self._getBoolean(config, "general",
759 "hideMinimizedWindow",
760 True)
761 self._quitOnClose = self._getBoolean(config, "general",
762 "quitOnClose", False)
763
764 self._onlineGateSystem = self._getBoolean(config, "general",
765 "onlineGateSystem",
766 not secondaryInstallation)
767 self._onlineACARS = self._getBoolean(config, "general",
768 "onlineACARS",
769 not secondaryInstallation)
770 self._flareTimeFromFS = self._getBoolean(config, "general",
771 "flareTimeFromFS",
772 False)
773 self._syncFSTime = self._getBoolean(config, "general",
774 "syncFSTime",
775 False)
776 self._usingFS2Crew = self._getBoolean(config, "general",
777 "usingFS2Crew",
778 False)
779 self._iasSmoothingLength = int(self._get(config, "general",
780 "iasSmoothingLength",
781 -2))
782 self._vsSmoothingLength = int(self._get(config, "general",
783 "vsSmoothingLength",
784 -2))
785
786 self._useSimBrief = self._getBoolean(config, "simbrief",
787 "use", False)
788 self._useInternalBrowserForSimBrief = self._getBoolean(config, "simbrief",
789 "useInternalBrowser",
790 False)
791 self._simBriefUserName = self._get(config, "simbrief",
792 "username", "")
793 self._simBriefPassword = self._get(config, "simbrief",
794 "password", "")
795 self._rememberSimBriefPassword = self._getBoolean(config, "simbrief",
796 "rememberPassword",
797 False)
798
799 self._pirepDirectory = self._get(config, "general",
800 "pirepDirectory", None)
801
802 self._pirepAutoSave = self._getBoolean(config, "general",
803 "pirepAutoSave", False)
804 if self._pirepDirectory is None:
805 self._pirepAutoSave = False
806
807 self._messageTypeLevels = {}
808 for messageType in const.messageTypes:
809 self._messageTypeLevels[messageType] = \
810 self._getMessageTypeLevel(config, messageType)
811
812 self._enableSounds = self._getBoolean(config, "sounds", "enable",
813 not secondaryInstallation)
814 self._pilotControlsSounds = self._getBoolean(config, "sounds",
815 "pilotControls", True)
816 self._pilotHotkey.set(self._get(config, "sounds",
817 "pilotHotkey", "C0"))
818
819 self._taxiSoundOnPushback = \
820 self._getBoolean(config, "sounds", "taxiSoundOnPushback", False)
821 self._enableApproachCallouts = \
822 self._getBoolean(config, "sounds", "enableApproachCallouts", False)
823 self._speedbrakeAtTD = self._getBoolean(config, "sounds",
824 "speedbrakeAtTD", True)
825
826 self._enableChecklists = self._getBoolean(config, "sounds",
827 "enableChecklists", False)
828 self._checklistHotkey.set(self._get(config, "sounds",
829 "checklistHotkey", "CS0"))
830
831 self._autoUpdate = self._getBoolean(config, "update", "auto", True)
832 self._updateURL = self._get(config, "update", "url",
833 Config.DEFAULT_UPDATE_URL)
834
835 for aircraftType in const.aircraftTypes:
836 self._checklists[aircraftType] = \
837 Checklist.fromConfig(config, aircraftType)
838 self._approachCallouts[aircraftType] = \
839 ApproachCallouts.fromConfig(config, aircraftType)
840
841 self._defaultMSFS = self._getBoolean(config, "general",
842 "defaultMSFS", os.name=="nt")
843
844 self._xplaneRemote = self._getBoolean(config, "general",
845 "xplaneRemote", False)
846 self._xplaneAddress = self._get(config, "general",
847 "xplaneAddress", "")
848
849 self._modified = False
850
851 def save(self):
852 """Save the configuration file if it has been modified."""
853 if not self._modified:
854 return
855
856 config = configparser.RawConfigParser()
857
858 config.add_section("login")
859 config.set("login", "id", self._pilotID)
860 config.set("login", "password", self._password)
861 config.set("login", "rememberPassword",
862 "yes" if self._rememberPassword else "no")
863
864 config.add_section("general")
865 if self._language:
866 config.set("general", "language", self._language)
867 config.set("general", "hideMinimizedWindow",
868 "yes" if self._hideMinimizedWindow else "no")
869 config.set("general", "quitOnClose",
870 "yes" if self._quitOnClose else "no")
871 config.set("general", "onlineGateSystem",
872 "yes" if self._onlineGateSystem else "no")
873 config.set("general", "onlineACARS",
874 "yes" if self._onlineACARS else "no")
875 config.set("general", "flareTimeFromFS",
876 "yes" if self._flareTimeFromFS else "no")
877 config.set("general", "syncFSTime",
878 "yes" if self._syncFSTime else "no")
879 config.set("general", "usingFS2Crew",
880 "yes" if self._usingFS2Crew else "no")
881 config.set("general", "iasSmoothingLength",
882 str(self._iasSmoothingLength))
883 config.set("general", "vsSmoothingLength",
884 str(self._vsSmoothingLength))
885
886 config.add_section("simbrief")
887 config.set("simbrief", "use",
888 "yes" if self._useSimBrief else "no")
889 config.set("simbrief", "useInternalBrowser",
890 "yes" if self._useInternalBrowserForSimBrief else "no")
891 config.set("simbrief", "username", self._simBriefUserName)
892 config.set("simbrief", "password", self._simBriefPassword)
893 config.set("simbrief", "rememberPassword",
894 "yes" if self._rememberSimBriefPassword else "no")
895
896 if self._pirepDirectory is not None:
897 config.set("general", "pirepDirectory", self._pirepDirectory)
898 config.set("general", "pirepAutoSave",
899 "yes" if self._pirepAutoSave else "no")
900
901 config.set("general", "defaultMSFS",
902 "yes" if self._defaultMSFS else "no")
903
904 config.set("general", "xplaneRemote",
905 "yes" if self._xplaneRemote else "no")
906 config.set("general", "xplaneAddress", self._xplaneAddress)
907
908 config.add_section(Config._messageTypesSection)
909 for messageType in const.messageTypes:
910 if messageType in self._messageTypeLevels:
911 option = self._getMessageTypeLevelOptionName(messageType)
912 level = self._messageTypeLevels[messageType]
913 config.set(Config._messageTypesSection, option,
914 const.messageLevel2string(level))
915
916 config.add_section("sounds")
917 config.set("sounds", "enable",
918 "yes" if self._enableSounds else "no")
919 config.set("sounds", "pilotControls",
920 "yes" if self._pilotControlsSounds else "no")
921 config.set("sounds", "pilotHotkey", str(self._pilotHotkey))
922 config.set("sounds", "taxiSoundOnPushback",
923 "yes" if self._taxiSoundOnPushback else "no")
924 config.set("sounds", "enableApproachCallouts",
925 "yes" if self._enableApproachCallouts else "no")
926 config.set("sounds", "speedbrakeAtTD",
927 "yes" if self._speedbrakeAtTD else "no")
928
929 config.set("sounds", "enableChecklists",
930 "yes" if self._enableChecklists else "no")
931 config.set("sounds", "checklistHotkey",
932 str(self._checklistHotkey))
933
934 config.add_section("update")
935 config.set("update", "auto",
936 "yes" if self._autoUpdate else "no")
937 config.set("update", "url", self._updateURL)
938
939 config.add_section(Checklist.SECTION)
940 config.add_section(ApproachCallouts.SECTION)
941 for aircraftType in const.aircraftTypes:
942 self._checklists[aircraftType].toConfig(config, aircraftType)
943 self._approachCallouts[aircraftType].toConfig(config, aircraftType)
944
945 try:
946 fd = os.open(configPath, os.O_CREAT|os.O_TRUNC|os.O_WRONLY,
947 0o600)
948 with os.fdopen(fd, "wt") as f:
949 config.write(f)
950 self._modified = False
951
952 print("Configuration saved:")
953 self.log()
954
955 except Exception as e:
956 print("Failed to update config: " + \
957 utf2unicode(str(e)), file=sys.stderr)
958
959 def _getBoolean(self, config, section, option, default):
960 """Get the given option as a boolean, if found in the given config,
961 otherwise the default."""
962 return config.getboolean(section, option) \
963 if config.has_option(section, option) \
964 else default
965
966 def _get(self, config, section, option, default):
967 """Get the given option as a string, if found in the given config,
968 otherwise the default."""
969 return config.get(section, option) \
970 if config.has_option(section, option) \
971 else default
972
973 def _getMessageTypeLevel(self, config, messageType):
974 """Get the message type level for the given message type."""
975 option = self._getMessageTypeLevelOptionName(messageType)
976 if config.has_option(Config._messageTypesSection, option):
977 value = config.get(Config._messageTypesSection, option)
978 return const.string2messageLevel(value)
979 elif secondaryInstallation:
980 return const.MESSAGELEVEL_NONE
981 elif messageType in [const.MESSAGETYPE_LOGGER_ERROR,
982 const.MESSAGETYPE_FAULT,
983 const.MESSAGETYPE_NOGO,
984 const.MESSAGETYPE_GATE_SYSTEM,
985 const.MESSAGETYPE_HELP]:
986 return const.MESSAGELEVEL_BOTH
987 else:
988 return const.MESSAGELEVEL_FS
989
990 def _getMessageTypeLevelOptionName(self, messageType):
991 """Get the option name for the given message type level."""
992 return const.messageType2string(messageType)
993
994 def setupLocale(self):
995 """Setup the locale based on the language set.
996
997 Return True if a specific language was set, False otherwise."""
998 import locale
999 if self._language:
1000 print("Setting up locale for", self._language)
1001 os.environ["LANGUAGE"] = self._language
1002 langAndEncoding = self._language + "." + locale.getpreferredencoding()
1003 os.environ["LANG"] = langAndEncoding
1004 os.environ["LC_MESSAGES"] = langAndEncoding
1005 os.environ["LC_COLLATE"] = langAndEncoding
1006 os.environ["LC_CTYPE"] = langAndEncoding
1007 os.environ["LC_MONETARY"] = langAndEncoding
1008 os.environ["LC_NUMERIC"] = langAndEncoding
1009 os.environ["LC_TIME"] = langAndEncoding
1010 return True
1011 else:
1012 return False
1013
1014 def getLanguage(self):
1015 """Get the language to be used."""
1016 import locale
1017 if self._language:
1018 if os.name=="nt":
1019 if self._language in _languageMap:
1020 locale.setlocale(locale.LC_ALL, _languageMap[self._language])
1021 else:
1022 locale.setlocale(locale.LC_ALL, "")
1023 else:
1024 locale.setlocale(locale.LC_ALL, (self._language,
1025 locale.getpreferredencoding()))
1026 return self._language
1027 else:
1028 locale.setlocale(locale.LC_ALL, "")
1029 return locale.getdefaultlocale()[0]
1030
1031 def log(self):
1032 """Log the configuration by printing the values"""
1033 print(" pilot ID:", self._pilotID)
1034 print(" rememberPassword:", self._rememberPassword)
1035
1036 print(" language:", self._language)
1037
1038 print(" hideMinimizedWindow:", self._hideMinimizedWindow)
1039 print(" quitOnClose:", self._quitOnClose)
1040
1041 print(" onlineGateSystem:", self._onlineGateSystem)
1042 print(" onlineACARS:", self._onlineACARS)
1043
1044 print(" flareTimeFromFS:", self._flareTimeFromFS)
1045 print(" syncFSTime:", self._syncFSTime)
1046 print(" usingFS2Crew:", self._usingFS2Crew)
1047
1048 print(" iasSmoothingLength:", self._iasSmoothingLength)
1049 print(" vsSmoothingLength:", self._vsSmoothingLength)
1050
1051 print(" useSimBrief:", self._useSimBrief)
1052 print(" useInternalBrowserForSimBrief:", self._useInternalBrowserForSimBrief)
1053 print(" simBriefUserName:", self._simBriefUserName)
1054 print(" rememberSimBriefPassword:", self._rememberSimBriefPassword)
1055
1056 print(" pirepDirectory:", self._pirepDirectory)
1057 print(" pirepAutoSave:", self._pirepAutoSave)
1058
1059 print(" defaultMSFS:", self._defaultMSFS)
1060 print(" xplaneRemote:", self._xplaneRemote)
1061 print(" xplaneAddress:", self._xplaneAddress)
1062
1063 print(" enableSounds:", self._enableSounds)
1064
1065 print(" pilotControlsSounds:", self._pilotControlsSounds)
1066 print(" pilotHotkey:", str(self._pilotHotkey))
1067
1068 print(" taxiSoundOnPushback:", self._taxiSoundOnPushback)
1069
1070 print(" enableApproachCallouts:", self._enableApproachCallouts)
1071 print(" speedbrakeAtTD:", self._speedbrakeAtTD)
1072
1073 print(" enableChecklists:", self._enableChecklists)
1074 print(" checklistHotkey:", str(self._checklistHotkey))
1075
1076 print(" autoUpdate:", self._autoUpdate)
1077 print(" updateURL:", self._updateURL)
1078
1079 print(" messageTypeLevels:")
1080 for (type, level) in self._messageTypeLevels.items():
1081 print(" %s: %s" % (const.messageType2string(type),
1082 const.messageLevel2string(level)))
1083
1084 print(" checklists:")
1085 for (type, checklist) in self._checklists.items():
1086 print(" %s:" % (const.icaoCodes[type],))
1087 for path in checklist:
1088 print(" " + path)
1089
1090 print(" approachCallouts:")
1091 for (type, approachCallouts) in self._approachCallouts.items():
1092 print(" %s:" % (const.icaoCodes[type],))
1093 for (altitude, path) in approachCallouts:
1094 print(" %d: %s" % (altitude, path))
1095
1096#-------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.