source: src/mlx/config.py@ 1059:07ffbc5c13f8

python3
Last change on this file since 1059:07ffbc5c13f8 was 1045:110ed385e042, checked in by István Váradi <ivaradi@…>, 2 years ago

Some update URLs are changed automatically to the current one (re #357)

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