source: src/mlx/gui/gui.py@ 1163:7233de0fe18a

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

Program name and class are set to 'nicer' values

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