source: src/mlx/config.py@ 1109:986abbce32ef

python3
Last change on this file since 1109:986abbce32ef was 1098:cd3cf67ef749, checked in by István Váradi <ivaradi@…>, 16 months ago

The main window can be made resizable (re #369).

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