source: src/mlx/gui/gui.py@ 1164:92e6925b39b8

python3
Last change on this file since 1164:92e6925b39b8 was 1164:92e6925b39b8, checked in by István Váradi <ivaradi@…>, 3 weeks ago

Long debug logs are attached as a file to the bug tickets

File size: 74.4 KB
Line 
1# -*- coding: utf-8 -*-
2
3from .statusicon import StatusIcon
4from .statusbar import Statusbar
5from .info import FlightInfo
6from .update import Updater
7from mlx.gui.common import *
8from mlx.gui.flight import Wizard
9from mlx.gui.monitor import MonitorWindow
10from mlx.gui.weighthelp import WeightHelp
11from mlx.gui.gates import FleetGateStatus
12from mlx.gui.prefs import Preferences
13from mlx.gui.checklist import ChecklistEditor
14from mlx.gui.callouts import ApproachCalloutsEditor
15from mlx.gui.flightlist import AcceptedFlightsWindow
16from mlx.gui.pirep import PIREPViewer, PIREPEditor
17from mlx.gui.bugreport import BugReportDialog
18from mlx.gui.acars import ACARS
19from mlx.gui.timetable import TimetableWindow
20from . import cef
21
22import mlx.const as const
23import mlx.fs as fs
24import mlx.flight as flight
25import mlx.logger as logger
26import mlx.acft as acft
27import mlx.web as web
28import mlx.singleton as singleton
29import mlx.airports as airports
30from mlx.i18n import xstr, getLanguage
31from mlx.pirep import PIREP
32
33import time
34import threading
35import sys
36import datetime
37import webbrowser
38
39#------------------------------------------------------------------------------
40
41## @package mlx.gui.gui
42#
43# The main GUI class.
44#
45# The \ref GUI class is the main class of the GUI. It is a connection listener,
46# and aggregates all the windows, the menu, etc. It maintains the connection to
47# the simulator as well as the flight object.
48
49#------------------------------------------------------------------------------
50
51class GUI(fs.ConnectionListener):
52 """The main GUI class."""
53 _authors = [ ("Váradi", "István", "prog_test"),
54 ("Galyassy", "Tamás", "negotiation"),
55 ("Kurják", "Ákos", "test"),
56 ("Nagy", "Dániel", "test"),
57 ("Radó", "Iván", "test"),
58 ("Petrovszki", "Gábor", "test"),
59 ("Serfőző", "Tamás", "test"),
60 ("Szebenyi", "Bálint", "test"),
61 ("Zsebényi-Loksa", "Gergely", "test") ]
62
63 _maxInlineDebugLogLines = 200
64
65 def __init__(self, programDirectory, config):
66 """Construct the GUI."""
67 GObject.threads_init()
68
69 GLib.set_prgname("mlx")
70 Gdk.set_program_class("MAVA-Logger-X")
71
72 self._programDirectory = programDirectory
73 self.config = config
74 self._connecting = False
75 self._reconnecting = False
76 self._connected = False
77 self._logger = logger.Logger(self)
78 self._flight = None
79 self._simulator = None
80 self._fsType = None
81 self._monitoring = False
82
83 self._fleet = None
84
85 self._fleetCallback = None
86
87 self._updatePlaneCallback = None
88 self._updatePlaneTailNumber = None
89 self._updatePlaneStatus = None
90 self._updatePlaneGateNumber = None
91
92 self._stdioLock = threading.Lock()
93 self._stdioText = ""
94 self._stdioStartingLine = True
95
96 self._sendPIREPCallback = None
97 self._sendBugReportCallback = None
98
99 self._credentialsCondition = threading.Condition()
100 self._credentialsAvailable = False
101 self._credentialsUserName = None
102 self._credentialsPassword = None
103
104 self._bookFlightsUserCallback = None
105 self._bookFlightsBusyCallback = None
106
107 self.webHandler = web.Handler(config, self._getCredentialsCallback)
108 self.webHandler.start()
109
110 self.toRestart = False
111
112 @property
113 def programDirectory(self):
114 """Get the program directory."""
115 return self._programDirectory
116
117 def build(self, iconDirectory):
118 """Build the GUI."""
119
120 self._mainWindow = window = Gtk.Window()
121 if os.name!="nt":
122 window.set_visual(window.get_screen().lookup_visual(0x21))
123 window.set_title(WINDOW_TITLE_BASE)
124 window.set_icon_from_file(os.path.join(iconDirectory, "logo.ico"))
125 window.set_resizable(self.config.mainWindowResizable)
126 window.connect("delete-event", self.deleteMainWindow)
127 window.connect("window-state-event", self._handleMainWindowState)
128 if os.name=="nt":
129 window.connect("leave-notify-event", self._handleLeaveNotify)
130 accelGroup = Gtk.AccelGroup()
131 window.add_accel_group(accelGroup)
132 window.realize()
133
134 mainVBox = Gtk.VBox()
135 window.add(mainVBox)
136
137 self._preferences = Preferences(self)
138 self._timetableWindow = TimetableWindow(self)
139 self._timetableWindow.connect("delete-event", self._hideTimetableWindow)
140 self._flightsWindow = AcceptedFlightsWindow(self)
141 self._flightsWindow.connect("delete-event", self._hideFlightsWindow)
142 self._checklistEditor = ChecklistEditor(self)
143 self._approachCalloutsEditor = ApproachCalloutsEditor(self)
144 self._bugReportDialog = BugReportDialog(self)
145
146 menuBar = self._buildMenuBar(accelGroup)
147 mainVBox.pack_start(menuBar, False, False, 0)
148
149 self._notebook = Gtk.Notebook()
150 mainVBox.pack_start(self._notebook, True, True, 4)
151
152 self._wizard = Wizard(self)
153 label = Gtk.Label(xstr("tab_flight"))
154 label.set_use_underline(True)
155 label.set_tooltip_text(xstr("tab_flight_tooltip"))
156 self._notebook.append_page(self._wizard, label)
157
158 self._flightInfo = FlightInfo(self)
159 label = Gtk.Label(xstr("tab_flight_info"))
160 label.set_use_underline(True)
161 label.set_tooltip_text(xstr("tab_flight_info_tooltip"))
162 self._notebook.append_page(self._flightInfo, label)
163 self._flightInfo.disable()
164
165 self._weightHelp = WeightHelp(self)
166 label = Gtk.Label(xstr("tab_weight_help"))
167 label.set_use_underline(True)
168 label.set_tooltip_text(xstr("tab_weight_help_tooltip"))
169 self._notebook.append_page(self._weightHelp, label)
170
171 (logWidget, self._logView) = self._buildLogWidget()
172 addFaultTag(self._logView.get_buffer())
173 label = Gtk.Label(xstr("tab_log"))
174 label.set_use_underline(True)
175 label.set_tooltip_text(xstr("tab_log_tooltip"))
176 self._notebook.append_page(logWidget, label)
177
178 self._fleetGateStatus = FleetGateStatus(self)
179 label = Gtk.Label(xstr("tab_gates"))
180 label.set_use_underline(True)
181 label.set_tooltip_text(xstr("tab_gates_tooltip"))
182 self._notebook.append_page(self._fleetGateStatus, label)
183
184 self._acars = ACARS(self)
185 label = Gtk.Label("ACARS")
186 label.set_use_underline(True)
187 self._notebook.append_page(self._acars, label)
188
189 (self._debugLogWidget, self._debugLogView) = self._buildLogWidget()
190 self._debugLogWidget.show_all()
191
192 mainVBox.pack_start(Gtk.HSeparator(), False, False, 0)
193
194 self._statusbar = Statusbar(iconDirectory)
195 mainVBox.pack_start(self._statusbar, False, False, 0)
196
197 self._notebook.connect("switch-page", self._notebookPageSwitch)
198
199 self._monitorWindow = MonitorWindow(self, iconDirectory)
200 self._monitorWindow.add_accel_group(accelGroup)
201 self._monitorWindowX = None
202 self._monitorWindowY = None
203 self._selfToggling = False
204
205 self._pirepViewer = PIREPViewer(self)
206 self._messagedPIREPViewer = PIREPViewer(self, showMessages = True)
207
208 self._pirepEditor = PIREPEditor(self)
209
210 window.show_all()
211
212 self._wizard.grabDefault()
213 self._weightHelp.reset()
214 self._weightHelp.disable()
215
216 self._statusIcon = StatusIcon(iconDirectory, self)
217
218 self._busyCursor = Gdk.Cursor(Gdk.CursorType.WATCH)
219
220 self._loadPIREPDialog = None
221 self._lastLoadedPIREP = None
222
223 self._hotkeySetID = None
224 self._pilotHotkeyIndex = None
225 self._checklistHotkeyIndex = None
226
227 self._aboutDialog = None
228
229 @property
230 def mainWindow(self):
231 """Get the main window of the GUI."""
232 return self._mainWindow
233
234 @property
235 def logger(self):
236 """Get the logger used by us."""
237 return self._logger
238
239 @property
240 def simulator(self):
241 """Get the simulator used by us."""
242 return self._simulator
243
244 @property
245 def flight(self):
246 """Get the flight being performed."""
247 return self._flight
248
249 @property
250 def fsType(self):
251 """Get the flight simulator type."""
252 return self._fsType
253
254 @property
255 def entranceExam(self):
256 """Get whether an entrance exam is about to be taken."""
257 return self._wizard.entranceExam
258
259 @property
260 def loggedIn(self):
261 """Indicate if the user has logged in properly."""
262 return self._wizard.loggedIn
263
264 @property
265 def loginResult(self):
266 """Get the result of the login."""
267 return self._wizard.loginResult
268
269 @property
270 def bookedFlight(self):
271 """Get the booked flight selected, if any."""
272 return self._wizard.bookedFlight
273
274 @property
275 def numCockpitCrew(self):
276 """Get the number of cockpit crew members."""
277 return self._wizard.numCockpitCrew
278
279 @property
280 def numCabinCrew(self):
281 """Get the number of cabin crew members."""
282 return self._wizard.numCabinCrew
283
284 @property
285 def numPassengers(self):
286 """Get the number of passengers."""
287 return self._wizard.numPassengers
288
289 @property
290 def numChildren(self):
291 """Get the number of child passengers."""
292 return self._wizard.numChildren
293
294 @property
295 def numInfants(self):
296 """Get the number of infant passengers."""
297 return self._wizard.numInfants
298
299 @property
300 def bagWeight(self):
301 """Get the bag weight."""
302 return self._wizard.bagWeight
303
304 @property
305 def cargoWeight(self):
306 """Get the cargo weight."""
307 return self._wizard.cargoWeight
308
309 @property
310 def mailWeight(self):
311 """Get the mail weight."""
312 return self._wizard.mailWeight
313
314 @property
315 def zfw(self):
316 """Get Zero-Fuel Weight calculated for the current flight."""
317 return self._wizard.zfw
318
319 @property
320 def filedCruiseAltitude(self):
321 """Get cruise altitude filed for the current flight."""
322 return self._wizard.filedCruiseAltitude
323
324 @property
325 def cruiseAltitude(self):
326 """Get cruise altitude set for the current flight."""
327 return self._wizard.cruiseAltitude
328
329 @property
330 def loggableCruiseAltitude(self):
331 """Get the cruise altitude that can be logged."""
332 return self._wizard.loggableCruiseAltitude
333
334 @property
335 def route(self):
336 """Get the flight route."""
337 return self._wizard.route
338
339 @property
340 def departureMETAR(self):
341 """Get the METAR of the deprature airport."""
342 return self._wizard.departureMETAR
343
344 @property
345 def arrivalMETAR(self):
346 """Get the METAR of the deprature airport."""
347 return self._wizard.arrivalMETAR
348
349 @property
350 def departureRunway(self):
351 """Get the name of the departure runway."""
352 return self._wizard.departureRunway
353
354 @property
355 def sid(self):
356 """Get the SID."""
357 return self._wizard.sid
358
359 @property
360 def v1(self):
361 """Get the V1 speed calculated for the flight."""
362 return self._wizard.v1
363
364 @property
365 def vr(self):
366 """Get the Vr speed calculated for the flight."""
367 return self._wizard.vr
368
369 @property
370 def v2(self):
371 """Get the V2 speed calculated for the flight."""
372 return self._wizard.v2
373
374 @property
375 def derate(self):
376 """Get the derate value calculated for the flight."""
377 return self._wizard.derate
378
379 @property
380 def takeoffAntiIceOn(self):
381 """Get whether the anti-ice system was on during take-off."""
382 return self._wizard.takeoffAntiIceOn
383
384 @takeoffAntiIceOn.setter
385 def takeoffAntiIceOn(self, value):
386 """Set the anti-ice on indicator."""
387 GObject.idle_add(self._setTakeoffAntiIceOn, value)
388
389 @property
390 def rtoIndicated(self):
391 """Get whether the pilot has indicated than an RTO has occured."""
392 return self._wizard.rtoIndicated
393
394 @property
395 def arrivalRunway(self):
396 """Get the arrival runway."""
397 return self._wizard.arrivalRunway
398
399 @property
400 def star(self):
401 """Get the STAR."""
402 return self._wizard.star
403
404 @property
405 def transition(self):
406 """Get the transition."""
407 return self._wizard.transition
408
409 @property
410 def approachType(self):
411 """Get the approach type."""
412 return self._wizard.approachType
413
414 @property
415 def vref(self):
416 """Get the Vref speed calculated for the flight."""
417 return self._wizard.vref
418
419 @property
420 def landingAntiIceOn(self):
421 """Get whether the anti-ice system was on during landing."""
422 return self._wizard.landingAntiIceOn
423
424 @landingAntiIceOn.setter
425 def landingAntiIceOn(self, value):
426 """Set the anti-ice on indicator."""
427 GObject.idle_add(self._setLandingAntiIceOn, value)
428
429 @property
430 def flightType(self):
431 """Get the flight type."""
432 return self._wizard.flightType
433
434 @property
435 def online(self):
436 """Get whether the flight was online or not."""
437 return self._wizard.online
438
439 @property
440 def comments(self):
441 """Get the comments."""
442 return self._flightInfo.comments
443
444 @property
445 def hasComments(self):
446 """Indicate whether there is a comment."""
447 return self._flightInfo.hasComments
448
449 @property
450 def flightDefects(self):
451 """Get the flight defects."""
452 return self._flightInfo.faultsAndExplanations
453
454 @property
455 def delayCodes(self):
456 """Get the delay codes."""
457 return self._flightInfo.delayCodes
458
459 @property
460 def hasDelayCode(self):
461 """Determine if there is at least one delay code selected."""
462 return self._flightInfo.hasDelayCode
463
464 @property
465 def faultsFullyExplained(self):
466 """Determine if all the faults have been fully explained by the
467 user."""
468 return self._flightInfo.faultsFullyExplained
469
470 @property
471 def backgroundColour(self):
472 """Get the background colour of the main window."""
473 return self._mainWindow.get_style_context().\
474 get_background_color(Gtk.StateFlags.NORMAL)
475
476 def run(self):
477 """Run the GUI."""
478 if self.config.autoUpdate:
479 self._updater = Updater(self,
480 self._programDirectory,
481 self.config.updateURL,
482 self._mainWindow)
483 self._updater.start()
484 else:
485 self.updateDone()
486
487 if self.config.clearBrowseCacheOnStart:
488 cef.clearCache()
489 self.config.clearBrowseCacheOnStart = False
490
491 singleton.raiseCallback = self.raiseCallback
492 Gtk.main()
493 if os.name != "nt":
494 cef.messageLoop()
495
496 singleton.raiseCallback = None
497
498 self._wizard.finalizeCEF()
499 cef.finalizeSimBrief()
500 self._acars.stop()
501
502 cef.finalize()
503
504 self._disconnect()
505
506 def updateDone(self):
507 """Called when the update is done (and there is no need to restart)."""
508 GObject.idle_add(self._updateDone)
509
510 def connected(self, fsType, descriptor):
511 """Called when we have connected to the simulator."""
512 self._connected = True
513 self._logger.untimedMessage("MLX %s connected to the simulator %s" % \
514 (const.VERSION, descriptor))
515 fs.sendMessage(const.MESSAGETYPE_INFORMATION,
516 "Welcome to MAVA Logger X " + const.VERSION)
517 GObject.idle_add(self._handleConnected, fsType, descriptor)
518
519 def _handleConnected(self, fsType, descriptor):
520 """Called when the connection to the simulator has succeeded."""
521 self._statusbar.updateConnection(self._connecting, self._connected)
522 self.endBusy()
523 if not self._reconnecting:
524 self._wizard.connected(fsType, descriptor)
525 self._reconnecting = False
526 self._fsType = fsType
527 self._listenHotkeys()
528
529 def connectionFailed(self):
530 """Called when the connection failed."""
531 self._logger.untimedMessage("Connection to the simulator failed")
532 GObject.idle_add(self._connectionFailed)
533
534 def _connectionFailed(self):
535 """Called when the connection failed."""
536 self.endBusy()
537 self._statusbar.updateConnection(self._connecting, self._connected)
538
539 dialog = Gtk.MessageDialog(parent = self._mainWindow,
540 type = Gtk.MessageType.ERROR,
541 message_format = xstr("conn_failed"))
542
543 dialog.set_title(WINDOW_TITLE_BASE)
544 dialog.format_secondary_markup(xstr("conn_failed_sec"))
545
546 dialog.add_button(xstr("button_cancel"), 0)
547 dialog.add_button(xstr("button_tryagain"), 1)
548 dialog.set_default_response(1)
549
550 result = dialog.run()
551 dialog.hide()
552 if result == 1:
553 self.beginBusy(xstr("connect_busy"))
554 self._simulator.reconnect()
555 else:
556 self.reset()
557
558 def disconnected(self):
559 """Called when we have disconnected from the simulator."""
560 self._connected = False
561 self._logger.untimedMessage("Disconnected from the simulator")
562 if self._flight is not None:
563 self._flight.disconnected()
564
565 GObject.idle_add(self._disconnected)
566
567 def _disconnected(self):
568 """Called when we have disconnected from the simulator unexpectedly."""
569 self._statusbar.updateConnection(self._connecting, self._connected)
570
571 dialog = Gtk.MessageDialog(type = Gtk.MessageType.ERROR,
572 message_format = xstr("conn_broken"),
573 parent = self._mainWindow)
574 dialog.set_title(WINDOW_TITLE_BASE)
575 dialog.format_secondary_markup(xstr("conn_broken_sec"))
576
577 dialog.add_button(xstr("button_cancel"), 0)
578 dialog.add_button(xstr("button_reconnect"), 1)
579 dialog.set_default_response(1)
580
581 result = dialog.run()
582 dialog.hide()
583 if result == 1:
584 self.beginBusy(xstr("connect_busy"))
585 self._reconnecting = True
586 self._simulator.reconnect()
587 else:
588 self.reset()
589
590 def enableFlightInfo(self, aircraftType):
591 """Enable the flight info tab."""
592 self._flightInfo.enable(aircraftType)
593
594 def bookFlights(self, callback, flightIDs, date, tailNumber,
595 busyCallback = None):
596 """Initiate the booking of flights with the given timetable IDs and
597 other data"""
598 self._bookFlightsUserCallback = callback
599 self._bookFlightsBusyCallback = busyCallback
600
601 self.beginBusy(xstr("bookflights_busy"))
602 if busyCallback is not None:
603 busyCallback(True)
604
605 self.webHandler.bookFlights(self._bookFlightsCallback,
606 flightIDs, date, tailNumber)
607
608 def _bookFlightsCallback(self, returned, result):
609 """Called when the booking of flights has finished."""
610 GObject.idle_add(self._handleBookFlightsResult, returned, result)
611
612 def _handleBookFlightsResult(self, returned, result):
613 """Called when the booking of flights is done.
614
615 If it was successful, the booked flights are added to the list of the
616 flight selector."""
617 if self._bookFlightsBusyCallback is not None:
618 self._bookFlightsBusyCallback(False)
619 self.endBusy()
620
621 if returned:
622 for bookedFlight in result.bookedFlights:
623 self._wizard.addFlight(bookedFlight)
624
625 self._bookFlightsUserCallback(returned, result)
626
627 def cancelFlight(self):
628 """Cancel the current file, if the user confirms it."""
629 dialog = Gtk.MessageDialog(parent = self._mainWindow,
630 type = Gtk.MessageType.QUESTION,
631 message_format = xstr("cancelFlight_question"))
632
633 dialog.add_button(xstr("button_no"), Gtk.ResponseType.NO)
634 dialog.add_button(xstr("button_yes"), Gtk.ResponseType.YES)
635
636 dialog.set_title(WINDOW_TITLE_BASE)
637 result = dialog.run()
638 dialog.hide()
639
640 if result==Gtk.ResponseType.YES:
641 self.reset()
642
643 def reset(self):
644 """Reset the GUI."""
645 self._disconnect()
646
647 self._simulator = None
648
649 self._flightInfo.reset()
650 self._flightInfo.disable()
651 self.resetFlightStatus()
652
653 self._weightHelp.reset()
654 self._weightHelp.disable()
655 self._notebook.set_current_page(0)
656
657 self._logView.get_buffer().set_text("")
658
659 if self.loggedIn:
660 self._wizard.cancelFlight(self._handleReloadResult)
661 else:
662 self._wizard.reset(None)
663
664 def _handleReloadResult(self, returned, result):
665 """Handle the result of the reloading of the flights."""
666 self._wizard.reset(result if returned and result.loggedIn else None)
667
668 def _disconnect(self, closingMessage = None, duration = 3):
669 """Disconnect from the simulator if connected."""
670 self.stopMonitoring()
671 self._clearHotkeys()
672
673 if self._connected:
674 if closingMessage is None:
675 self._flight.simulator.disconnect()
676 else:
677 fs.sendMessage(const.MESSAGETYPE_ENVIRONMENT,
678 closingMessage, duration,
679 disconnect = True)
680 self._connected = False
681
682 self._connecting = False
683 self._reconnecting = False
684 self._statusbar.updateConnection(False, False)
685 self._weightHelp.disable()
686
687 return True
688
689 def insertFlightLogLine(self, index, timestampString, text, isFault):
690 """Insert the flight log line with the given data."""
691 GObject.idle_add(self._insertFlightLogLine, index,
692 formatFlightLogLine(timestampString, text),
693 isFault)
694
695 def _insertFlightLogLine(self, index, line, isFault):
696 """Perform the real insertion.
697
698 To be called from the event loop."""
699 buffer = self._logView.get_buffer()
700 lineIter = buffer.get_iter_at_line(index)
701 insertTextBuffer(buffer, lineIter, line, isFault = isFault)
702 self._logView.scroll_mark_onscreen(buffer.get_insert())
703
704 def removeFlightLogLine(self, index):
705 """Remove the flight log line with the given index."""
706 GObject.idle_add(self._removeFlightLogLine, index)
707
708 def addFault(self, id, timestampString, text):
709 """Add a fault to the list of faults."""
710 faultText = formatFlightLogLine(timestampString, text).strip()
711 GObject.idle_add(self._flightInfo.addFault, id, faultText)
712
713 def updateFault(self, id, timestampString, text):
714 """Update a fault in the list of faults."""
715 faultText = formatFlightLogLine(timestampString, text).strip()
716 GObject.idle_add(self._flightInfo.updateFault, id, faultText)
717
718 def clearFault(self, id):
719 """Clear a fault in the list of faults."""
720 GObject.idle_add(self._flightInfo.clearFault, id)
721
722 def _removeFlightLogLine(self, index):
723 """Perform the real removal."""
724 buffer = self._logView.get_buffer()
725 startIter = buffer.get_iter_at_line(index)
726 endIter = buffer.get_iter_at_line(index+1)
727 buffer.delete(startIter, endIter)
728 self._logView.scroll_mark_onscreen(buffer.get_insert())
729
730 def check(self, flight, aircraft, logger, oldState, state):
731 """Update the data."""
732 GObject.idle_add(self._monitorWindow.setData, state)
733 GObject.idle_add(self._statusbar.updateTime, state.timestamp)
734
735 def resetFlightStatus(self):
736 """Reset the status of the flight."""
737 self._statusbar.resetFlightStatus()
738 self._statusbar.updateTime()
739 self._statusIcon.resetFlightStatus()
740
741 def setStage(self, stage):
742 """Set the stage of the flight."""
743 GObject.idle_add(self._setStage, stage)
744
745 def _setStage(self, stage):
746 """Set the stage of the flight."""
747 self._statusbar.setStage(stage)
748 self._statusIcon.setStage(stage)
749 self._wizard.setStage(stage)
750 if stage==const.STAGE_END:
751 welcomeMessage = \
752 airports.getWelcomeMessage(self.bookedFlight.arrivalICAO)
753 self._disconnect(closingMessage =
754 "Flight plan closed. " + welcomeMessage,
755 duration = 5)
756
757 def setRating(self, rating):
758 """Set the rating of the flight."""
759 GObject.idle_add(self._setRating, rating)
760
761 def _setRating(self, rating):
762 """Set the rating of the flight."""
763 self._statusbar.setRating(rating)
764 self._statusIcon.setRating(rating)
765
766 def setNoGo(self, reason):
767 """Set the rating of the flight to No-Go with the given reason."""
768 GObject.idle_add(self._setNoGo, reason)
769
770 def _setNoGo(self, reason):
771 """Set the rating of the flight."""
772 self._statusbar.setNoGo(reason)
773 self._statusIcon.setNoGo(reason)
774
775 def _handleMainWindowState(self, window, event):
776 """Hande a change in the state of the window"""
777 iconified = Gdk.WindowState.ICONIFIED
778
779 if (event.changed_mask&Gdk.WindowState.WITHDRAWN)!=0:
780 if (event.new_window_state&Gdk.WindowState.WITHDRAWN)!=0:
781 self._statusIcon.mainWindowHidden()
782 else:
783 self._statusIcon.mainWindowShown()
784
785 if (event.changed_mask&Gdk.WindowState.ICONIFIED)!=0 and \
786 (event.new_window_state&Gdk.WindowState.ICONIFIED)==0:
787 self._mainWindow.present()
788
789 def _handleLeaveNotify(self, widget, event):
790 """Handle the leave-notify event.
791
792 Here we reset the focus to the main window as CEF might have acquired
793 it earlier."""
794 self._mainWindow.get_window().focus(0)
795
796 def raiseCallback(self):
797 """Callback for the singleton handling code."""
798 GObject.idle_add(self.raiseMainWindow)
799
800 def raiseMainWindow(self):
801 """Show the main window if invisible, and raise it."""
802 if not self._mainWindow.get_visible():
803 self.showMainWindow()
804 self._mainWindow.present()
805
806 def deleteMainWindow(self, window, event):
807 """Handle the delete event for the main window."""
808 if self.config.quitOnClose:
809 self._quit()
810 else:
811 self.hideMainWindow()
812 return True
813
814 def hideMainWindow(self, savePosition = True):
815 """Hide the main window and save its position."""
816 if savePosition:
817 (self._mainWindowX, self._mainWindowY) = \
818 self._mainWindow.get_window().get_root_origin()
819 else:
820 self._mainWindowX = self._mainWindowY = None
821 self._mainWindow.hide()
822 return True
823
824 def showMainWindow(self):
825 """Show the main window at its former position."""
826 if self._mainWindowX is not None and self._mainWindowY is not None:
827 self._mainWindow.move(self._mainWindowX, self._mainWindowY)
828
829 self._mainWindow.show()
830 self._mainWindow.deiconify()
831
832 def toggleMainWindow(self):
833 """Toggle the main window."""
834 if self._mainWindow.get_visible():
835 self.hideMainWindow()
836 else:
837 self.showMainWindow()
838
839 def hideMonitorWindow(self, savePosition = True):
840 """Hide the monitor window."""
841 if savePosition:
842 (self._monitorWindowX, self._monitorWindowY) = \
843 self._monitorWindow.get_window().get_root_origin()
844 else:
845 self._monitorWindowX = self._monitorWindowY = None
846 self._monitorWindow.hide()
847 self._statusIcon.monitorWindowHidden()
848 if self._showMonitorMenuItem.get_active():
849 self._selfToggling = True
850 self._showMonitorMenuItem.set_active(False)
851 return True
852
853 def showMonitorWindow(self):
854 """Show the monitor window."""
855 if self._monitorWindowX is not None and self._monitorWindowY is not None:
856 self._monitorWindow.move(self._monitorWindowX, self._monitorWindowY)
857 self._monitorWindow.show_all()
858 self._statusIcon.monitorWindowShown()
859 if not self._showMonitorMenuItem.get_active():
860 self._selfToggling = True
861 self._showMonitorMenuItem.set_active(True)
862
863 def _toggleMonitorWindow(self, menuItem):
864 if self._selfToggling:
865 self._selfToggling = False
866 elif self._monitorWindow.get_visible():
867 self.hideMonitorWindow()
868 else:
869 self.showMonitorWindow()
870
871 def restart(self, clearCEFCache = False):
872 """Quit and restart the application."""
873 self.toRestart = True
874 self.config.clearBrowseCacheOnStart = clearCEFCache
875 self._quit(force = True)
876
877 def flushStdIO(self):
878 """Flush any text to the standard error that could not be logged."""
879 if self._stdioText and sys.__stderr__ is not None:
880 sys.__stderr__.write(self._stdioText)
881
882 def writeStdIO(self, text):
883 """Write the given text into standard I/O log."""
884 with self._stdioLock:
885 self._stdioText += text
886
887 GObject.idle_add(self._writeStdIO)
888
889 def beginBusy(self, message):
890 """Begin a period of background processing."""
891 self._wizard.set_sensitive(False)
892 self._weightHelp.set_sensitive(False)
893 self._mainWindow.get_window().set_cursor(self._busyCursor)
894 self._statusbar.updateBusyState(message)
895
896 def updateBusyState(self, message):
897 """Update the busy state."""
898 self._statusbar.updateBusyState(message)
899
900 def endBusy(self):
901 """End a period of background processing."""
902 self._mainWindow.get_window().set_cursor(None)
903 self._weightHelp.set_sensitive(True)
904 self._wizard.set_sensitive(True)
905 self._statusbar.updateBusyState(None)
906
907 def initializeWeightHelp(self):
908 """Initialize the weight help tab."""
909 self._weightHelp.reset()
910 self._weightHelp.enable()
911
912 def getFleetAsync(self, callback = None, force = None):
913 """Get the fleet asynchronously."""
914 GObject.idle_add(self.getFleet, callback, force)
915
916 def getFleet(self, callback = None, force = False, busyCallback = None):
917 """Get the fleet.
918
919 If force is False, and we already have a fleet retrieved,
920 that one will be used."""
921 if self._fleet is None or force:
922 self._fleetCallback = callback
923 self._fleetBusyCallback = busyCallback
924 if busyCallback is not None:
925 busyCallback(True)
926 self.beginBusy(xstr("fleet_busy"))
927 self.webHandler.getFleet(self._fleetResultCallback)
928 else:
929 callback(self._fleet)
930
931 def commentsChanged(self):
932 """Indicate that the comments have changed."""
933 self._wizard.commentsChanged()
934
935 def delayCodesChanged(self):
936 """Called when the delay codes have changed."""
937 self._wizard.delayCodesChanged()
938
939 def faultExplanationsChanged(self):
940 """Called when the status of the explanations of the faults have
941 changed."""
942 self._wizard.faultExplanationsChanged()
943
944 def updateRTO(self, inLoop = False):
945 """Indicate that the RTO state should be updated."""
946 if inLoop:
947 self._wizard.updateRTO()
948 else:
949 GObject.idle_add(self.updateRTO, True)
950
951 def rtoToggled(self, indicated):
952 """Called when the user has toggled the RTO checkbox."""
953 self._flight.rtoToggled(indicated)
954
955 def _fleetResultCallback(self, returned, result):
956 """Called when the fleet has been queried."""
957 GObject.idle_add(self._handleFleetResult, returned, result)
958
959 def _handleFleetResult(self, returned, result):
960 """Handle the fleet result."""
961 self.endBusy()
962 if self._fleetBusyCallback is not None:
963 self._fleetBusyCallback(False)
964 if returned:
965 self._fleet = result.fleet
966 else:
967 self._fleet = None
968
969 dialog = Gtk.MessageDialog(parent = self.mainWindow,
970 type = Gtk.MessageType.ERROR,
971 message_format = xstr("fleet_failed"))
972 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
973 dialog.set_title(WINDOW_TITLE_BASE)
974 dialog.run()
975 dialog.hide()
976
977 callback = self._fleetCallback
978 self._fleetCallback = None
979 self._fleetBusyCallback = None
980 if callback is not None:
981 callback(self._fleet)
982 self._fleetGateStatus.handleFleet(self._fleet)
983
984 def updatePlane(self, tailNumber, status,
985 gateNumber = None, callback = None):
986 """Update the status of the given plane."""
987 self.beginBusy(xstr("fleet_update_busy"))
988
989 self._updatePlaneCallback = callback
990
991 self._updatePlaneTailNumber = tailNumber
992 self._updatePlaneStatus = status
993 self._updatePlaneGateNumber = gateNumber
994
995 self.webHandler.updatePlane(self._updatePlaneResultCallback,
996 tailNumber, status, gateNumber)
997
998 def _updatePlaneResultCallback(self, returned, result):
999 """Called when the status of a plane has been updated."""
1000 GObject.idle_add(self._handleUpdatePlaneResult, returned, result)
1001
1002 def _handleUpdatePlaneResult(self, returned, result):
1003 """Handle the plane update result."""
1004 self.endBusy()
1005 if returned:
1006 success = result.success
1007 if success:
1008 if self._fleet is not None:
1009 self._fleet.updatePlane(self._updatePlaneTailNumber,
1010 self._updatePlaneStatus,
1011 self._updatePlaneGateNumber)
1012 self._fleetGateStatus.handleFleet(self._fleet)
1013 else:
1014 dialog = Gtk.MessageDialog(parent = self.mainWindow,
1015 type = Gtk.MessageType.ERROR,
1016 message_format = xstr("fleet_update_failed"))
1017 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.ACCEPT)
1018 dialog.set_title(WINDOW_TITLE_BASE)
1019 dialog.run()
1020 dialog.hide()
1021
1022 success = None
1023
1024 callback = self._updatePlaneCallback
1025 self._updatePlaneCallback = None
1026 if callback is not None:
1027 callback(success)
1028
1029 def _writeStdIO(self):
1030 """Perform the real writing."""
1031 with self._stdioLock:
1032 text = self._stdioText
1033 self._stdioText = ""
1034 if not text: return
1035
1036 lines = text.splitlines()
1037 if text[-1]=="\n":
1038 text = ""
1039 else:
1040 text = lines[-1]
1041 lines = lines[:-1]
1042
1043 now = datetime.datetime.now()
1044 timeStr = "%02d:%02d:%02d: " % (now.hour, now.minute, now.second)
1045
1046 for line in lines:
1047 #print >> sys.__stdout__, line
1048 if self._stdioStartingLine:
1049 self._writeLog(timeStr, self._debugLogView)
1050 self._writeLog(line + "\n", self._debugLogView)
1051 self._stdioStartingLine = True
1052
1053 if text:
1054 #print >> sys.__stdout__, text,
1055 if self._stdioStartingLine:
1056 self._writeLog(timeStr, self._debugLogView)
1057 self._writeLog(text, self._debugLogView)
1058 self._stdioStartingLine = False
1059
1060 def connectSimulator(self, bookedFlight, simulatorType):
1061 """Connect to the simulator for the first time."""
1062 self._logger.reset()
1063
1064 self._flight = flight.Flight(self._logger, self)
1065 self._flight.flareTimeFromFS = self.config.flareTimeFromFS
1066 self._flight.aircraftType = bookedFlight.aircraftType
1067 self._flight.aircraft = acft.Aircraft.create(self._flight, bookedFlight)
1068 self._flight.aircraft._checkers.append(self)
1069
1070 self._flight.departureGateIsTaxiThrough = self._wizard.isDepartureGateTaxiThrough
1071 print("The departure gate is '%s', and it is %staxi-through" %
1072 (self._wizard._departureGate,
1073 "" if self._flight.departureGateIsTaxiThrough else "not "))
1074
1075 if self._simulator is None:
1076 self._simulator = fs.createSimulator(simulatorType, self)
1077 fs.setupMessageSending(self.config, self._simulator)
1078 self._setupTimeSync()
1079
1080 self._flight.simulator = self._simulator
1081
1082 self.beginBusy(xstr("connect_busy"))
1083 self._statusbar.updateConnection(self._connecting, self._connected)
1084
1085 self._connecting = True
1086 self._simulator.connect(self._flight.aircraft)
1087
1088 def startMonitoring(self):
1089 """Start monitoring."""
1090 if not self._monitoring:
1091 self.simulator.startMonitoring()
1092 self._monitoring = True
1093
1094 def stopMonitoring(self):
1095 """Stop monitoring."""
1096 if self._monitoring:
1097 self.simulator.stopMonitoring()
1098 self._monitoring = False
1099
1100 def cruiseLevelChanged(self):
1101 """Called when the cruise level is changed in the flight wizard."""
1102 if self._flight is not None:
1103 return self._flight.cruiseLevelChanged()
1104 else:
1105 return False
1106
1107 def _buildMenuBar(self, accelGroup):
1108 """Build the main menu bar."""
1109 menuBar = Gtk.MenuBar()
1110
1111 fileMenuItem = Gtk.MenuItem(xstr("menu_file"))
1112 fileMenu = Gtk.Menu()
1113 fileMenuItem.set_submenu(fileMenu)
1114 menuBar.append(fileMenuItem)
1115
1116 loadPIREPMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_OPEN)
1117 loadPIREPMenuItem.set_use_stock(True)
1118 loadPIREPMenuItem.set_label(xstr("menu_file_loadPIREP"))
1119 loadPIREPMenuItem.add_accelerator("activate", accelGroup,
1120 ord(xstr("menu_file_loadPIREP_key")),
1121 Gdk.ModifierType.CONTROL_MASK,
1122 Gtk.AccelFlags.VISIBLE)
1123 loadPIREPMenuItem.connect("activate", self._loadPIREP)
1124 fileMenu.append(loadPIREPMenuItem)
1125
1126 fileMenu.append(Gtk.SeparatorMenuItem())
1127
1128 quitMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_QUIT)
1129 quitMenuItem.set_use_stock(True)
1130 quitMenuItem.set_label(xstr("menu_file_quit"))
1131 quitMenuItem.add_accelerator("activate", accelGroup,
1132 ord(xstr("menu_file_quit_key")),
1133 Gdk.ModifierType.CONTROL_MASK,
1134 Gtk.AccelFlags.VISIBLE)
1135 quitMenuItem.connect("activate", self._quit)
1136 fileMenu.append(quitMenuItem)
1137
1138 toolsMenuItem = Gtk.MenuItem(xstr("menu_tools"))
1139 toolsMenu = Gtk.Menu()
1140 toolsMenuItem.set_submenu(toolsMenu)
1141 menuBar.append(toolsMenuItem)
1142
1143 self._timetableMenuItem = timetableMenuItem = \
1144 Gtk.ImageMenuItem(Gtk.STOCK_INDENT)
1145 timetableMenuItem.set_use_stock(True)
1146 timetableMenuItem.set_label(xstr("menu_tools_timetable"))
1147 timetableMenuItem.add_accelerator("activate", accelGroup,
1148 ord(xstr("menu_tools_timetable_key")),
1149 Gdk.ModifierType.CONTROL_MASK,
1150 Gtk.AccelFlags.VISIBLE)
1151 timetableMenuItem.connect("activate", self.showTimetable)
1152 self._timetableMenuItem.set_sensitive(False)
1153 toolsMenu.append(timetableMenuItem)
1154
1155 self._flightsMenuItem = flightsMenuItem = \
1156 Gtk.ImageMenuItem(Gtk.STOCK_SPELL_CHECK)
1157 flightsMenuItem.set_use_stock(True)
1158 flightsMenuItem.set_label(xstr("menu_tools_flights"))
1159 flightsMenuItem.add_accelerator("activate", accelGroup,
1160 ord(xstr("menu_tools_flights_key")),
1161 Gdk.ModifierType.CONTROL_MASK,
1162 Gtk.AccelFlags.VISIBLE)
1163 flightsMenuItem.connect("activate", self.showFlights)
1164 self._flightsMenuItem.set_sensitive(False)
1165 toolsMenu.append(flightsMenuItem)
1166
1167 checklistMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_APPLY)
1168 checklistMenuItem.set_use_stock(True)
1169 checklistMenuItem.set_label(xstr("menu_tools_chklst"))
1170 checklistMenuItem.add_accelerator("activate", accelGroup,
1171 ord(xstr("menu_tools_chklst_key")),
1172 Gdk.ModifierType.CONTROL_MASK,
1173 Gtk.AccelFlags.VISIBLE)
1174 checklistMenuItem.connect("activate", self._editChecklist)
1175 toolsMenu.append(checklistMenuItem)
1176
1177 approachCalloutsMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_EDIT)
1178 approachCalloutsMenuItem.set_use_stock(True)
1179 approachCalloutsMenuItem.set_label(xstr("menu_tools_callouts"))
1180 approachCalloutsMenuItem.add_accelerator("activate", accelGroup,
1181 ord(xstr("menu_tools_callouts_key")),
1182 Gdk.ModifierType.CONTROL_MASK,
1183 Gtk.AccelFlags.VISIBLE)
1184 approachCalloutsMenuItem.connect("activate", self._editApproachCallouts)
1185 toolsMenu.append(approachCalloutsMenuItem)
1186
1187 prefsMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_PREFERENCES)
1188 prefsMenuItem.set_use_stock(True)
1189 prefsMenuItem.set_label(xstr("menu_tools_prefs"))
1190 prefsMenuItem.add_accelerator("activate", accelGroup,
1191 ord(xstr("menu_tools_prefs_key")),
1192 Gdk.ModifierType.CONTROL_MASK,
1193 Gtk.AccelFlags.VISIBLE)
1194 prefsMenuItem.connect("activate", self._editPreferences)
1195 toolsMenu.append(prefsMenuItem)
1196
1197 toolsMenu.append(Gtk.SeparatorMenuItem())
1198
1199 clearCEFCacheMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_DISCARD)
1200 clearCEFCacheMenuItem.set_use_stock(True)
1201 clearCEFCacheMenuItem.set_label(xstr("menu_tools_clear_cef_cache"))
1202 clearCEFCacheMenuItem.connect("activate", self._clearCEFCache)
1203 toolsMenu.append(clearCEFCacheMenuItem)
1204
1205 toolsMenu.append(Gtk.SeparatorMenuItem())
1206
1207 bugReportMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_PASTE)
1208 bugReportMenuItem.set_use_stock(True)
1209 bugReportMenuItem.set_label(xstr("menu_tools_bugreport"))
1210 bugReportMenuItem.add_accelerator("activate", accelGroup,
1211 ord(xstr("menu_tools_bugreport_key")),
1212 Gdk.ModifierType.CONTROL_MASK,
1213 Gtk.AccelFlags.VISIBLE)
1214 bugReportMenuItem.connect("activate", self._reportBug)
1215 toolsMenu.append(bugReportMenuItem)
1216
1217 viewMenuItem = Gtk.MenuItem(xstr("menu_view"))
1218 viewMenu = Gtk.Menu()
1219 viewMenuItem.set_submenu(viewMenu)
1220 menuBar.append(viewMenuItem)
1221
1222 self._showMonitorMenuItem = Gtk.CheckMenuItem()
1223 self._showMonitorMenuItem.set_label(xstr("menu_view_monitor"))
1224 self._showMonitorMenuItem.set_use_underline(True)
1225 self._showMonitorMenuItem.set_active(False)
1226 self._showMonitorMenuItem.add_accelerator("activate", accelGroup,
1227 ord(xstr("menu_view_monitor_key")),
1228 Gdk.ModifierType.CONTROL_MASK,
1229 Gtk.AccelFlags.VISIBLE)
1230 self._showMonitorMenuItem.connect("toggled", self._toggleMonitorWindow)
1231 viewMenu.append(self._showMonitorMenuItem)
1232
1233 showDebugMenuItem = Gtk.CheckMenuItem()
1234 showDebugMenuItem.set_label(xstr("menu_view_debug"))
1235 showDebugMenuItem.set_use_underline(True)
1236 showDebugMenuItem.set_active(False)
1237 showDebugMenuItem.add_accelerator("activate", accelGroup,
1238 ord(xstr("menu_view_debug_key")),
1239 Gdk.ModifierType.CONTROL_MASK,
1240 Gtk.AccelFlags.VISIBLE)
1241 showDebugMenuItem.connect("toggled", self._toggleDebugLog)
1242 viewMenu.append(showDebugMenuItem)
1243
1244 helpMenuItem = Gtk.MenuItem(xstr("menu_help"))
1245 helpMenu = Gtk.Menu()
1246 helpMenuItem.set_submenu(helpMenu)
1247 menuBar.append(helpMenuItem)
1248
1249 manualMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_HELP)
1250 manualMenuItem.set_use_stock(True)
1251 manualMenuItem.set_label(xstr("menu_help_manual"))
1252 manualMenuItem.add_accelerator("activate", accelGroup,
1253 ord(xstr("menu_help_manual_key")),
1254 Gdk.ModifierType.CONTROL_MASK,
1255 Gtk.AccelFlags.VISIBLE)
1256 manualMenuItem.connect("activate", self._showManual)
1257 helpMenu.append(manualMenuItem)
1258
1259 helpMenu.append(Gtk.SeparatorMenuItem())
1260
1261 aboutMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_ABOUT)
1262 aboutMenuItem.set_use_stock(True)
1263 aboutMenuItem.set_label(xstr("menu_help_about"))
1264 aboutMenuItem.add_accelerator("activate", accelGroup,
1265 ord(xstr("menu_help_about_key")),
1266 Gdk.ModifierType.CONTROL_MASK,
1267 Gtk.AccelFlags.VISIBLE)
1268 aboutMenuItem.connect("activate", self._showAbout)
1269 helpMenu.append(aboutMenuItem)
1270
1271 return menuBar
1272
1273 def _toggleDebugLog(self, menuItem):
1274 """Toggle the debug log."""
1275 if menuItem.get_active():
1276 label = Gtk.Label(xstr("tab_debug_log"))
1277 label.set_use_underline(True)
1278 label.set_tooltip_text(xstr("tab_debug_log_tooltip"))
1279 self._debugLogPage = self._notebook.append_page(self._debugLogWidget, label)
1280 self._notebook.set_current_page(self._debugLogPage)
1281 else:
1282 self._notebook.remove_page(self._debugLogPage)
1283
1284 def _buildLogWidget(self):
1285 """Build the widget for the log."""
1286 alignment = Gtk.Alignment(xscale = 1.0, yscale = 1.0)
1287
1288 alignment.set_padding(padding_top = 8, padding_bottom = 8,
1289 padding_left = 16, padding_right = 16)
1290
1291 logScroller = Gtk.ScrolledWindow()
1292 # FIXME: these should be constants in common
1293 logScroller.set_policy(Gtk.PolicyType.AUTOMATIC,
1294 Gtk.PolicyType.AUTOMATIC)
1295 logScroller.set_shadow_type(Gtk.ShadowType.IN)
1296 logView = Gtk.TextView()
1297 logView.set_editable(False)
1298 logView.set_cursor_visible(False)
1299 logScroller.add(logView)
1300
1301 logBox = Gtk.VBox()
1302 logBox.pack_start(logScroller, True, True, 0)
1303 logBox.set_size_request(-1, 200)
1304
1305 alignment.add(logBox)
1306
1307 return (alignment, logView)
1308
1309 def _writeLog(self, msg, logView, isFault = False):
1310 """Write the given message to the log."""
1311 buffer = logView.get_buffer()
1312 appendTextBuffer(buffer, msg, isFault = isFault)
1313 logView.scroll_mark_onscreen(buffer.get_insert())
1314
1315 def _quit(self, what = None, force = False):
1316 """Quit from the application."""
1317 if force:
1318 result=Gtk.ResponseType.YES
1319 else:
1320 dialog = Gtk.MessageDialog(parent = self._mainWindow,
1321 type = Gtk.MessageType.QUESTION,
1322 message_format = xstr("quit_question"))
1323
1324 dialog.add_button(xstr("button_no"), Gtk.ResponseType.NO)
1325 dialog.add_button(xstr("button_yes"), Gtk.ResponseType.YES)
1326
1327 dialog.set_title(WINDOW_TITLE_BASE)
1328 result = dialog.run()
1329 dialog.hide()
1330
1331 if result==Gtk.ResponseType.YES:
1332 self._statusIcon.destroy()
1333 if os.name=="nt":
1334 return Gtk.main_quit()
1335 else:
1336 cef.quitMessageLoop()
1337 return True
1338
1339 def _notebookPageSwitch(self, notebook, page, page_num):
1340 """Called when the current page of the notebook has changed."""
1341 if page_num==0:
1342 GObject.idle_add(self._wizard.grabDefault)
1343 else:
1344 self._mainWindow.set_default(None)
1345
1346 def loginSuccessful(self):
1347 """Called when the login is successful."""
1348 self._flightsMenuItem.set_sensitive(True)
1349 self._timetableMenuItem.set_sensitive(True)
1350
1351 def isWizardActive(self):
1352 """Determine if the flight wizard is active."""
1353 return self._notebook.get_current_page()==0
1354
1355 def showTimetable(self, menuItem = None):
1356 """Callback for showing the timetable."""
1357 if self._timetableWindow.hasFlightPairs:
1358 self._timetableWindow.show_all()
1359 else:
1360 date = datetime.date.today()
1361 self._timetableWindow.setTypes(self.loginResult.types)
1362 self._timetableWindow.setDate(date)
1363 self.updateTimeTable(date)
1364 self.beginBusy(xstr("timetable_query_busy"))
1365
1366 def updateTimeTable(self, date):
1367 """Update the time table for the given date."""
1368 self.beginBusy(xstr("timetable_query_busy"))
1369 self._timetableWindow.set_sensitive(False)
1370 window = self._timetableWindow.get_window()
1371 if window is not None:
1372 window.set_cursor(self._busyCursor)
1373 self.webHandler.getTimetable(self._timetableCallback, date,
1374 self.loginResult.types)
1375
1376 def _timetableCallback(self, returned, result):
1377 """Called when the timetable has been received."""
1378 GObject.idle_add(self._handleTimetable, returned, result)
1379
1380 def _handleTimetable(self, returned, result):
1381 """Handle the result of the query for the timetable."""
1382 self.endBusy()
1383 window = self._timetableWindow.get_window()
1384 if window is not None:
1385 window.set_cursor(None)
1386 self._timetableWindow.set_sensitive(True)
1387 if returned:
1388 self._timetableWindow.setFlightPairs(result.flightPairs)
1389 self._timetableWindow.show_all()
1390 else:
1391 dialog = Gtk.MessageDialog(parent = self.mainWindow,
1392 type = Gtk.MessageType.ERROR,
1393 message_format = xstr("timetable_failed"))
1394 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1395 dialog.set_title(WINDOW_TITLE_BASE)
1396 dialog.run()
1397 dialog.hide()
1398 self._timetableWindow.clear()
1399
1400 def showFlights(self, menuItem):
1401 """Callback for showing the flight list."""
1402 if self._flightsWindow.hasFlights:
1403 self._flightsWindow.show_all()
1404 else:
1405 self.beginBusy(xstr("acceptedflt_query_busy"))
1406 self.webHandler.getAcceptedFlights(self._acceptedFlightsCallback)
1407
1408 def _acceptedFlightsCallback(self, returned, result):
1409 """Called when the accepted flights have been received."""
1410 GObject.idle_add(self._handleAcceptedFlights, returned, result)
1411
1412 def _handleAcceptedFlights(self, returned, result):
1413 """Handle the result of the query for accepted flights."""
1414 self.endBusy()
1415 if returned:
1416 self._flightsWindow.clear()
1417 for flight in result.flights:
1418 self._flightsWindow.addFlight(flight)
1419 self._flightsWindow.show_all()
1420 else:
1421 dialog = Gtk.MessageDialog(parent = self.mainWindow,
1422 type = Gtk.MessageType.ERROR,
1423 message_format = xstr("acceptedflt_failed"))
1424 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1425 dialog.set_title(WINDOW_TITLE_BASE)
1426 dialog.run()
1427 dialog.hide()
1428
1429 def _hideTimetableWindow(self, window, event):
1430 """Hide the window of the timetable."""
1431 self._timetableWindow.hide()
1432 return True
1433
1434 def _hideFlightsWindow(self, window, event):
1435 """Hide the window of the accepted flights."""
1436 self._flightsWindow.hide()
1437 return True
1438
1439 def _editChecklist(self, menuItem):
1440 """Callback for editing the checklists."""
1441 self._checklistEditor.run()
1442
1443 def _editApproachCallouts(self, menuItem):
1444 """Callback for editing the approach callouts."""
1445 self._approachCalloutsEditor.run()
1446
1447 def _editPreferences(self, menuItem):
1448 """Callback for editing the preferences."""
1449 self._clearHotkeys()
1450 self._preferences.run(self.config)
1451 self._mainWindow.set_resizable(self.config.mainWindowResizable)
1452 self._setupTimeSync()
1453 self._listenHotkeys()
1454
1455 def _clearCEFCache(self, menuItem):
1456 """Callback for clearing the CEF cache."""
1457 if askYesNo(xstr("clear_cef_cache_confirmation"),
1458 parent = self._mainWindow):
1459 self.restart(clearCEFCache = True)
1460
1461 def _reportBug(self, menuItem):
1462 """Callback for reporting a bug."""
1463 self._bugReportDialog.run()
1464
1465 def _setupTimeSync(self):
1466 """Enable or disable the simulator time synchronization based on the
1467 configuration."""
1468 simulator = self._simulator
1469 if simulator is not None:
1470 if self.config.syncFSTime:
1471 simulator.enableTimeSync()
1472 else:
1473 simulator.disableTimeSync()
1474
1475 def viewPIREP(self, pirep):
1476 """Display the PIREP viewer window with the given PIREP."""
1477 self._pirepViewer.setPIREP(pirep)
1478 self._pirepViewer.show_all()
1479 self._pirepViewer.run()
1480 self._pirepViewer.hide()
1481
1482 def viewMessagedPIREP(self, pirep):
1483 """Display the PIREP viewer window with the given PIREP containing
1484 messages as well."""
1485 self._messagedPIREPViewer.setPIREP(pirep)
1486 self._messagedPIREPViewer.show_all()
1487 self._messagedPIREPViewer.run()
1488 self._messagedPIREPViewer.hide()
1489
1490 def editPIREP(self, pirep):
1491 """Display the PIREP editor window and allow editing the PIREP."""
1492 self._pirepEditor.setPIREP(pirep)
1493 self._pirepEditor.show_all()
1494 if self._pirepEditor.run()==Gtk.ResponseType.OK:
1495 self.beginBusy(xstr("pirepEdit_save_busy"))
1496 self.webHandler.sendPIREP(self._pirepUpdatedCallback, pirep,
1497 update = True)
1498 else:
1499 self._pirepEditor.hide()
1500
1501 def _pirepUpdatedCallback(self, returned, result):
1502 """Callback for the PIREP updating result."""
1503 GObject.idle_add(self._handlePIREPUpdated, returned, result)
1504
1505 def _handlePIREPUpdated(self, returned, result):
1506 """Callback for the PIREP updating result."""
1507 self.endBusy()
1508 secondaryMarkup = None
1509 type = Gtk.MessageType.ERROR
1510 if returned:
1511 if result.success:
1512 type = None
1513 elif result.alreadyFlown:
1514 messageFormat = xstr("sendPIREP_already")
1515 secondaryMarkup = xstr("sendPIREP_already_sec")
1516 elif result.notAvailable:
1517 messageFormat = xstr("sendPIREP_notavail")
1518 else:
1519 messageFormat = xstr("sendPIREP_unknown")
1520 secondaryMarkup = xstr("sendPIREP_unknown_sec")
1521 else:
1522 print("PIREP sending failed", result)
1523 messageFormat = xstr("sendPIREP_failed")
1524 secondaryMarkup = xstr("sendPIREP_failed_sec")
1525
1526 if type is not None:
1527 dialog = Gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
1528 type = type, message_format = messageFormat)
1529 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1530 dialog.set_title(WINDOW_TITLE_BASE)
1531 if secondaryMarkup is not None:
1532 dialog.format_secondary_markup(secondaryMarkup)
1533
1534 dialog.run()
1535 dialog.hide()
1536
1537 self._pirepEditor.hide()
1538
1539 def _loadPIREP(self, menuItem):
1540 """Load a PIREP for sending."""
1541 dialog = self._getLoadPirepDialog()
1542
1543 if self._lastLoadedPIREP:
1544 dialog.set_current_folder(os.path.dirname(self._lastLoadedPIREP))
1545 else:
1546 pirepDirectory = self.config.pirepDirectory
1547 if pirepDirectory is not None:
1548 dialog.set_current_folder(pirepDirectory)
1549
1550 result = dialog.run()
1551 dialog.hide()
1552
1553 if result==Gtk.ResponseType.OK:
1554 self._lastLoadedPIREP = dialog.get_filename()
1555
1556 pirep = PIREP.load(self._lastLoadedPIREP)
1557 if pirep is None:
1558 dialog = Gtk.MessageDialog(parent = self._mainWindow,
1559 type = Gtk.MessageType.ERROR,
1560 message_format = xstr("loadPIREP_failed"))
1561 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1562 dialog.set_title(WINDOW_TITLE_BASE)
1563 dialog.format_secondary_markup(xstr("loadPIREP_failed_sec"))
1564 dialog.run()
1565 dialog.hide()
1566 else:
1567 dialog = self._getSendLoadedDialog(pirep)
1568 dialog.show_all()
1569 while True:
1570 result = dialog.run()
1571
1572 if result==Gtk.ResponseType.OK:
1573 self.sendPIREP(pirep)
1574 elif result==1:
1575 self.viewPIREP(pirep)
1576 else:
1577 break
1578
1579 dialog.hide()
1580
1581 def _getLoadPirepDialog(self):
1582 """Get the PIREP loading file chooser dialog.
1583
1584 If it is not created yet, it will be created."""
1585 if self._loadPIREPDialog is None:
1586 dialog = Gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
1587 xstr("loadPIREP_browser_title"),
1588 action = Gtk.FileChooserAction.OPEN,
1589 buttons = (Gtk.STOCK_CANCEL,
1590 Gtk.ResponseType.CANCEL,
1591 Gtk.STOCK_OK, Gtk.ResponseType.OK),
1592 parent = self._mainWindow)
1593 dialog.set_modal(True)
1594
1595
1596 filter = Gtk.FileFilter()
1597 filter.set_name(xstr("file_filter_pireps"))
1598 filter.add_pattern("*.pirep")
1599 dialog.add_filter(filter)
1600
1601 filter = Gtk.FileFilter()
1602 filter.set_name(xstr("file_filter_all"))
1603 filter.add_pattern("*.*")
1604 dialog.add_filter(filter)
1605
1606 self._loadPIREPDialog = dialog
1607
1608 return self._loadPIREPDialog
1609
1610 def _getSendLoadedDialog(self, pirep):
1611 """Get a dialog displaying the main information of the flight from the
1612 PIREP and providing Cancel and Send buttons."""
1613 dialog = Gtk.Dialog(title = WINDOW_TITLE_BASE + " - " +
1614 xstr("loadPIREP_send_title"),
1615 parent = self._mainWindow,
1616 flags = Gtk.DialogFlags.MODAL)
1617
1618 contentArea = dialog.get_content_area()
1619
1620 label = Gtk.Label(xstr("loadPIREP_send_help"))
1621 alignment = Gtk.Alignment(xalign = 0.5, yalign = 0.5,
1622 xscale = 0.0, yscale = 0.0)
1623 alignment.set_padding(padding_top = 16, padding_bottom = 0,
1624 padding_left = 48, padding_right = 48)
1625 alignment.add(label)
1626 contentArea.pack_start(alignment, False, False, 8)
1627
1628 table = Gtk.Table(5, 2)
1629 tableAlignment = Gtk.Alignment(xalign = 0.5, yalign = 0.5,
1630 xscale = 0.0, yscale = 0.0)
1631 tableAlignment.set_padding(padding_top = 0, padding_bottom = 32,
1632 padding_left = 48, padding_right = 48)
1633 table.set_row_spacings(4)
1634 table.set_col_spacings(16)
1635 tableAlignment.add(table)
1636 contentArea.pack_start(tableAlignment, True, True, 8)
1637
1638 bookedFlight = pirep.bookedFlight
1639
1640 label = Gtk.Label("<b>" + xstr("loadPIREP_send_flightno") + "</b>")
1641 label.set_use_markup(True)
1642 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1643 xscale = 0.0, yscale = 0.0)
1644 labelAlignment.add(label)
1645 table.attach(labelAlignment, 0, 1, 0, 1)
1646
1647 label = Gtk.Label(bookedFlight.callsign)
1648 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1649 xscale = 0.0, yscale = 0.0)
1650 labelAlignment.add(label)
1651 table.attach(labelAlignment, 1, 2, 0, 1)
1652
1653 label = Gtk.Label("<b>" + xstr("loadPIREP_send_date") + "</b>")
1654 label.set_use_markup(True)
1655 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1656 xscale = 0.0, yscale = 0.0)
1657 labelAlignment.add(label)
1658 table.attach(labelAlignment, 0, 1, 1, 2)
1659
1660 label = Gtk.Label(str(bookedFlight.departureTime.date()))
1661 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1662 xscale = 0.0, yscale = 0.0)
1663 labelAlignment.add(label)
1664 table.attach(labelAlignment, 1, 2, 1, 2)
1665
1666 label = Gtk.Label("<b>" + xstr("loadPIREP_send_from") + "</b>")
1667 label.set_use_markup(True)
1668 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1669 xscale = 0.0, yscale = 0.0)
1670 labelAlignment.add(label)
1671 table.attach(labelAlignment, 0, 1, 2, 3)
1672
1673 label = Gtk.Label(bookedFlight.departureICAO)
1674 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1675 xscale = 0.0, yscale = 0.0)
1676 labelAlignment.add(label)
1677 table.attach(labelAlignment, 1, 2, 2, 3)
1678
1679 label = Gtk.Label("<b>" + xstr("loadPIREP_send_to") + "</b>")
1680 label.set_use_markup(True)
1681 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1682 xscale = 0.0, yscale = 0.0)
1683 labelAlignment.add(label)
1684 table.attach(labelAlignment, 0, 1, 3, 4)
1685
1686 label = Gtk.Label(bookedFlight.arrivalICAO)
1687 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1688 xscale = 0.0, yscale = 0.0)
1689 labelAlignment.add(label)
1690 table.attach(labelAlignment, 1, 2, 3, 4)
1691
1692 label = Gtk.Label("<b>" + xstr("loadPIREP_send_rating") + "</b>")
1693 label.set_use_markup(True)
1694 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1695 xscale = 0.0, yscale = 0.0)
1696 labelAlignment.add(label)
1697 table.attach(labelAlignment, 0, 1, 4, 5)
1698
1699 rating = pirep.rating
1700 label = Gtk.Label()
1701 if rating<0:
1702 label.set_markup('<b><span foreground="red">NO GO</span></b>')
1703 else:
1704 label.set_text("%.1f %%" % (rating,))
1705
1706 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1707 xscale = 0.0, yscale = 0.0)
1708 labelAlignment.add(label)
1709 table.attach(labelAlignment, 1, 2, 4, 5)
1710
1711 dialog.add_button(xstr("button_cancel"), Gtk.ResponseType.REJECT)
1712 dialog.add_button(xstr("viewPIREP"), 1)
1713 dialog.add_button(xstr("sendPIREP"), Gtk.ResponseType.OK)
1714
1715 return dialog
1716
1717 def sendPIREP(self, pirep, callback = None):
1718 """Send the given PIREP."""
1719 self.beginBusy(xstr("sendPIREP_busy"))
1720 self._sendPIREPCallback = callback
1721 self.webHandler.sendPIREP(self._pirepSentCallback, pirep)
1722
1723 def _pirepSentCallback(self, returned, result):
1724 """Callback for the PIREP sending result."""
1725 GObject.idle_add(self._handlePIREPSent, returned, result)
1726
1727 def _handlePIREPSent(self, returned, result):
1728 """Callback for the PIREP sending result."""
1729 self.endBusy()
1730 secondaryMarkup = None
1731 type = Gtk.MessageType.ERROR
1732 if returned:
1733 if result.success:
1734 type = Gtk.MessageType.INFO
1735 messageFormat = xstr("sendPIREP_success")
1736 secondaryMarkup = xstr("sendPIREP_success_sec")
1737 elif result.alreadyFlown:
1738 messageFormat = xstr("sendPIREP_already")
1739 secondaryMarkup = xstr("sendPIREP_already_sec")
1740 elif result.notAvailable:
1741 messageFormat = xstr("sendPIREP_notavail")
1742 else:
1743 messageFormat = xstr("sendPIREP_unknown")
1744 secondaryMarkup = xstr("sendPIREP_unknown_sec")
1745 else:
1746 print("PIREP sending failed", result)
1747 messageFormat = xstr("sendPIREP_failed")
1748 secondaryMarkup = xstr("sendPIREP_failed_sec")
1749
1750 dialog = Gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
1751 type = type, message_format = messageFormat)
1752 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1753 dialog.set_title(WINDOW_TITLE_BASE)
1754 if secondaryMarkup is not None:
1755 dialog.format_secondary_markup(secondaryMarkup)
1756
1757 dialog.run()
1758 dialog.hide()
1759
1760 callback = self._sendPIREPCallback
1761 self._sendPIREPCallback = None
1762 if callback is not None:
1763 callback(returned, result)
1764
1765 def sendBugReport(self, summary, description, email, callback = None):
1766 """Send the bug report with the given data."""
1767 description += "\n\n" + ("=" * 40)
1768 description += "\n\nThe contents of the log:\n\n"
1769
1770 for (timestampString, text) in self._logger.lines:
1771 description += str(formatFlightLogLine(timestampString, text))
1772
1773 buffer = self._debugLogView.get_buffer()
1774 debugLogTooLong = buffer.get_line_count()>GUI._maxInlineDebugLogLines
1775
1776 description += "\n\n" + ("=" * 40)
1777 description += "\n\nThe contents of the debug log%s:\n\n" % \
1778 (" (truncated)" if debugLogTooLong else "")
1779
1780 debugLog = buffer.get_text(buffer.get_start_iter(),
1781 buffer.get_end_iter(), True)
1782 if debugLogTooLong:
1783 description += buffer.get_text(buffer.get_start_iter(),
1784 buffer.get_iter_at_line(GUI._maxInlineDebugLogLines),
1785 True)
1786 else:
1787 description += debugLog
1788 debugLog = None
1789
1790 self.beginBusy(xstr("sendBugReport_busy"))
1791 self._sendBugReportCallback = callback
1792 self.webHandler.sendBugReport(self._bugReportSentCallback,
1793 summary, description, email,
1794 debugLog = debugLog)
1795
1796 def _cefInitialized(self):
1797 """Called when CEF has been initialized."""
1798 self._acars.start()
1799 cef.initializeSimBrief()
1800
1801 def _bugReportSentCallback(self, returned, result):
1802 """Callback function for the bug report sending result."""
1803 GObject.idle_add(self._handleBugReportSent, returned, result)
1804
1805 def _handleBugReportSent(self, returned, result):
1806 """Callback for the bug report sending result."""
1807 self.endBusy()
1808 secondaryMarkup = None
1809 type = Gtk.MessageType.ERROR
1810 if returned:
1811 if result.success:
1812 type = Gtk.MessageType.INFO
1813 messageFormat = xstr("sendBugReport_success") % (result.ticketID,)
1814 secondaryMarkup = xstr("sendBugReport_success_sec")
1815 else:
1816 messageFormat = xstr("sendBugReport_error")
1817 secondaryMarkup = xstr("sendBugReport_siteerror_sec")
1818 else:
1819 messageFormat = xstr("sendBugReport_error")
1820 secondaryMarkup = xstr("sendBugReport_error_sec")
1821
1822 dialog = Gtk.MessageDialog(parent = self._wizard.gui._bugReportDialog,
1823 type = type, message_format = messageFormat)
1824 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1825 dialog.set_title(WINDOW_TITLE_BASE)
1826 if secondaryMarkup is not None:
1827 dialog.format_secondary_markup(secondaryMarkup)
1828
1829 dialog.run()
1830 dialog.hide()
1831
1832 callback = self._sendBugReportCallback
1833 self._sendBugReportCallback = None
1834 if callback is not None:
1835 callback(returned, result)
1836
1837 def _listenHotkeys(self):
1838 """Setup the hotkeys based on the configuration."""
1839 if self._hotkeySetID is None and self._simulator is not None:
1840 self._pilotHotkeyIndex = None
1841 self._checklistHotkeyIndex = None
1842
1843 hotkeys = []
1844
1845 config = self.config
1846 if config.enableSounds and config.pilotControlsSounds:
1847 self._pilotHotkeyIndex = len(hotkeys)
1848 hotkeys.append(config.pilotHotkey)
1849
1850 if config.enableChecklists:
1851 self._checklistHotkeyIndex = len(hotkeys)
1852 hotkeys.append(config.checklistHotkey)
1853
1854 if hotkeys:
1855 self._hotkeySetID = \
1856 self._simulator.listenHotkeys(hotkeys, self._handleHotkeys)
1857
1858 def _clearHotkeys(self):
1859 """Clear the hotkeys."""
1860 if self._hotkeySetID is not None:
1861 self._hotkeySetID=None
1862 self._simulator.clearHotkeys()
1863
1864 def _handleHotkeys(self, id, hotkeys):
1865 """Handle the hotkeys."""
1866 if id==self._hotkeySetID:
1867 for index in hotkeys:
1868 if index==self._pilotHotkeyIndex:
1869 print("gui.GUI._handleHotkeys: pilot hotkey pressed")
1870 self._flight.pilotHotkeyPressed()
1871 elif index==self._checklistHotkeyIndex:
1872 print("gui.GUI._handleHotkeys: checklist hotkey pressed")
1873 self._flight.checklistHotkeyPressed()
1874 else:
1875 print("gui.GUI._handleHotkeys: unhandled hotkey index:", index)
1876
1877 def _showManual(self, menuitem):
1878 """Show the user's manual."""
1879 webbrowser.open(url ="file://" +
1880 os.path.join(self._programDirectory, "doc", "manual",
1881 getLanguage(), "index.html"),
1882 new = 1)
1883
1884 def _showAbout(self, menuitem):
1885 """Show the about dialog."""
1886 dialog = self._getAboutDialog()
1887 dialog.show_all()
1888 dialog.run()
1889 dialog.hide()
1890
1891 def _getAboutDialog(self):
1892 """Get the about dialog.
1893
1894 If it does not exist yet, it will be created."""
1895 if self._aboutDialog is None:
1896 dialog = Gtk.AboutDialog()
1897 dialog.set_transient_for(self._mainWindow)
1898 dialog.set_modal(True)
1899
1900 logoPath = os.path.join(self._programDirectory, "logo.png")
1901 logo = GdkPixbuf.Pixbuf.new_from_file(logoPath)
1902 dialog.set_logo(logo)
1903
1904 dialog.set_program_name(PROGRAM_NAME)
1905 dialog.set_version(const.VERSION)
1906 dialog.set_copyright("(c) 2012 - 2023 by István Váradi")
1907 dialog.set_website("http://mlx.varadiistvan.hu")
1908 dialog.set_website_label(xstr("about_website"))
1909
1910 isHungarian = getLanguage()=="hu"
1911 authors = []
1912 for (familyName, firstName, role) in GUI._authors:
1913 author = "%s %s" % \
1914 (familyName if isHungarian else firstName,
1915 firstName if isHungarian else familyName)
1916 role = xstr("about_role_" + role)
1917 authors.append(author + " (" + role + ")")
1918 dialog.set_authors(authors)
1919
1920 dialog.set_license(xstr("about_license"))
1921
1922 self._aboutDialog = dialog
1923
1924 return self._aboutDialog
1925
1926 def _showAboutURL(self, dialog, link, user_data):
1927 """Show the about URL."""
1928 webbrowser.open(url = link, new = 1)
1929
1930 def _setTakeoffAntiIceOn(self, value):
1931 """Set the anti-ice on indicator."""
1932 self._wizard.takeoffAntiIceOn = value
1933
1934 def _setLandingAntiIceOn(self, value):
1935 """Set the anti-ice on indicator."""
1936 self._wizard.landingAntiIceOn = value
1937
1938 def _getCredentialsCallback(self):
1939 """Called when the web handler asks for the credentials."""
1940 # FIXME: this is almost the same as
1941 # SimBriefSetupPage._getCredentialsCallback
1942 with self._credentialsCondition:
1943 self._credentialsAvailable = False
1944
1945 GObject.idle_add(self._getCredentials)
1946
1947 while not self._credentialsAvailable:
1948 self._credentialsCondition.wait()
1949
1950 return (self._credentialsUserName, self._credentialsPassword)
1951
1952 def _getCredentials(self):
1953 """Get the credentials."""
1954 # FIXME: this is almost the same as
1955 # SimBriefSetupPage._getCredentials
1956 with self._credentialsCondition:
1957 config = self.config
1958
1959 dialog = CredentialsDialog(self, config.pilotID, config.password,
1960 xstr("login_title"),
1961 xstr("button_cancel"),
1962 xstr("button_ok"),
1963 xstr("label_pilotID"),
1964 xstr("login_pilotID_tooltip"),
1965 xstr("label_password"),
1966 xstr("login_password_tooltip"),
1967 xstr("login_info"),
1968 config.rememberPassword,
1969 xstr("remember_password"),
1970 xstr("login_remember_tooltip"))
1971 response = dialog.run()
1972
1973 if response==Gtk.ResponseType.OK:
1974 self._credentialsUserName = dialog.userName
1975 self._credentialsPassword = dialog.password
1976 rememberPassword = dialog.rememberPassword
1977
1978 config.pilotID = self._credentialsUserName
1979
1980 config.password = \
1981 self._credentialsPassword if rememberPassword else ""
1982 config.rememberPassword = rememberPassword
1983
1984 config.save()
1985 else:
1986 self._credentialsUserName = None
1987 self._credentialsPassword = None
1988
1989 self._credentialsAvailable = True
1990 self._credentialsCondition.notify()
1991
1992 def _updateDone(self):
1993 """Called when the update is done.
1994
1995 It checks if we already know the PID, and if not, asks the user whether
1996 to register."""
1997 cef.initialize(self._cefInitialized)
1998
1999 if not self.config.pilotID and not self.config.password:
2000 dialog = Gtk.MessageDialog(parent = self._mainWindow,
2001 type = Gtk.MessageType.QUESTION,
2002 message_format = xstr("register_ask"))
2003
2004 dialog.set_title(WINDOW_TITLE_BASE)
2005 dialog.format_secondary_markup(xstr("register_ask_sec"))
2006
2007 dialog.add_button(xstr("button_cancel"), 0)
2008 dialog.add_button(xstr("button_register"), 1)
2009 dialog.set_default_response(1)
2010
2011 result = dialog.run()
2012 dialog.hide()
2013 if result == 1:
2014 self._wizard.jumpPage("register")
Note: See TracBrowser for help on using the repository browser.