source: src/mlx/gui/gui.py@ 604:0ec6a6f58f08

Last change on this file since 604:0ec6a6f58f08 was 604:0ec6a6f58f08, checked in by István Váradi <ivaradi@…>, 9 years ago

Added a new widget to list the faults and provide space for the user to enter an explanation (re #248).

File size: 57.1 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.pirep import PIREPViewer
16from mlx.gui.bugreport import BugReportDialog
17
18import mlx.const as const
19import mlx.fs as fs
20import mlx.flight as flight
21import mlx.logger as logger
22import mlx.acft as acft
23import mlx.web as web
24import mlx.singleton as singleton
25import mlx.airports as airports
26from mlx.i18n import xstr, getLanguage
27from mlx.pirep import PIREP
28
29import time
30import threading
31import sys
32import datetime
33import webbrowser
34
35#------------------------------------------------------------------------------
36
37## @package mlx.gui.gui
38#
39# The main GUI class.
40#
41# The \ref GUI class is the main class of the GUI. It is a connection listener,
42# and aggregates all the windows, the menu, etc. It maintains the connection to
43# the simulator as well as the flight object.
44
45#------------------------------------------------------------------------------
46
47class GUI(fs.ConnectionListener):
48 """The main GUI class."""
49 _authors = [ (u"Váradi", u"István", "prog_test"),
50 (u"Galyassy", u"Tamás", "negotiation"),
51 (u"Kurják", u"Ákos", "test"),
52 (u"Nagy", u"Dániel", "test"),
53 (u"Radó", u"Iván", "test"),
54 (u"Petrovszki", u"Gábor", "test"),
55 (u"Serfőző", u"Tamás", "test"),
56 (u"Szebenyi", u"Bálint", "test"),
57 (u"Zsebényi-Loksa", u"Gergely", "test") ]
58
59 def __init__(self, programDirectory, config):
60 """Construct the GUI."""
61 gobject.threads_init()
62
63 self._programDirectory = programDirectory
64 self.config = config
65 self._connecting = False
66 self._reconnecting = False
67 self._connected = False
68 self._logger = logger.Logger(self)
69 self._flight = None
70 self._simulator = None
71 self._fsType = None
72 self._monitoring = False
73
74 self._fleet = None
75
76 self._fleetCallback = None
77
78 self._updatePlaneCallback = None
79 self._updatePlaneTailNumber = None
80 self._updatePlaneStatus = None
81 self._updatePlaneGateNumber = None
82
83 self._stdioLock = threading.Lock()
84 self._stdioText = ""
85 self._stdioStartingLine = True
86
87 self._sendPIREPCallback = None
88 self._sendBugReportCallback = None
89
90 self.webHandler = web.Handler()
91 self.webHandler.start()
92
93 self.toRestart = False
94
95 def build(self, iconDirectory):
96 """Build the GUI."""
97
98 self._mainWindow = window = gtk.Window()
99 window.set_title(WINDOW_TITLE_BASE)
100 window.set_icon_from_file(os.path.join(iconDirectory, "logo.ico"))
101 window.set_resizable(False)
102 window.connect("delete-event", self.deleteMainWindow)
103 window.connect("window-state-event", self._handleMainWindowState)
104 accelGroup = gtk.AccelGroup()
105 window.add_accel_group(accelGroup)
106
107 mainVBox = gtk.VBox()
108 window.add(mainVBox)
109
110 self._preferences = Preferences(self)
111 self._checklistEditor = ChecklistEditor(self)
112 self._approachCalloutsEditor = ApproachCalloutsEditor(self)
113 self._bugReportDialog = BugReportDialog(self)
114
115 menuBar = self._buildMenuBar(accelGroup)
116 mainVBox.pack_start(menuBar, False, False, 0)
117
118 self._notebook = gtk.Notebook()
119 mainVBox.pack_start(self._notebook, True, True, 4)
120
121 self._wizard = Wizard(self)
122 label = gtk.Label(xstr("tab_flight"))
123 label.set_use_underline(True)
124 label.set_tooltip_text(xstr("tab_flight_tooltip"))
125 self._notebook.append_page(self._wizard, label)
126
127 self._flightInfo = FlightInfo(self)
128 label = gtk.Label(xstr("tab_flight_info"))
129 label.set_use_underline(True)
130 label.set_tooltip_text(xstr("tab_flight_info_tooltip"))
131 self._notebook.append_page(self._flightInfo, label)
132 self._flightInfo.disable()
133
134 self._weightHelp = WeightHelp(self)
135 label = gtk.Label(xstr("tab_weight_help"))
136 label.set_use_underline(True)
137 label.set_tooltip_text(xstr("tab_weight_help_tooltip"))
138 self._notebook.append_page(self._weightHelp, label)
139
140 (logWidget, self._logView) = self._buildLogWidget()
141 addFaultTag(self._logView.get_buffer())
142 label = gtk.Label(xstr("tab_log"))
143 label.set_use_underline(True)
144 label.set_tooltip_text(xstr("tab_log_tooltip"))
145 self._notebook.append_page(logWidget, label)
146
147 self._fleetGateStatus = FleetGateStatus(self)
148 label = gtk.Label(xstr("tab_gates"))
149 label.set_use_underline(True)
150 label.set_tooltip_text(xstr("tab_gates_tooltip"))
151 self._notebook.append_page(self._fleetGateStatus, label)
152
153 (self._debugLogWidget, self._debugLogView) = self._buildLogWidget()
154 self._debugLogWidget.show_all()
155
156 mainVBox.pack_start(gtk.HSeparator(), False, False, 0)
157
158 self._statusbar = Statusbar(iconDirectory)
159 mainVBox.pack_start(self._statusbar, False, False, 0)
160
161 self._notebook.connect("switch-page", self._notebookPageSwitch)
162
163 self._monitorWindow = MonitorWindow(self, iconDirectory)
164 self._monitorWindow.add_accel_group(accelGroup)
165 self._monitorWindowX = None
166 self._monitorWindowY = None
167 self._selfToggling = False
168
169 self._pirepViewer = PIREPViewer(self)
170
171 window.show_all()
172 self._wizard.grabDefault()
173 self._weightHelp.reset()
174 self._weightHelp.disable()
175
176 self._statusIcon = StatusIcon(iconDirectory, self)
177
178 self._busyCursor = gdk.Cursor(gdk.CursorType.WATCH if pygobject
179 else gdk.WATCH)
180
181 self._loadPIREPDialog = None
182 self._lastLoadedPIREP = None
183
184 self._hotkeySetID = None
185 self._pilotHotkeyIndex = None
186 self._checklistHotkeyIndex = None
187
188 self._aboutDialog = None
189
190 @property
191 def mainWindow(self):
192 """Get the main window of the GUI."""
193 return self._mainWindow
194
195 @property
196 def logger(self):
197 """Get the logger used by us."""
198 return self._logger
199
200 @property
201 def simulator(self):
202 """Get the simulator used by us."""
203 return self._simulator
204
205 @property
206 def flight(self):
207 """Get the flight being performed."""
208 return self._flight
209
210 @property
211 def fsType(self):
212 """Get the flight simulator type."""
213 return self._fsType
214
215 @property
216 def entranceExam(self):
217 """Get whether an entrance exam is about to be taken."""
218 return self._wizard.entranceExam
219
220 @property
221 def loggedIn(self):
222 """Indicate if the user has logged in properly."""
223 return self._wizard.loggedIn
224
225 @property
226 def loginResult(self):
227 """Get the result of the login."""
228 return self._wizard.loginResult
229
230 @property
231 def bookedFlight(self):
232 """Get the booked flight selected, if any."""
233 return self._wizard.bookedFlight
234
235 @property
236 def numCrew(self):
237 """Get the number of crew members."""
238 return self._wizard.numCrew
239
240 @property
241 def numPassengers(self):
242 """Get the number of passengers."""
243 return self._wizard.numPassengers
244
245 @property
246 def bagWeight(self):
247 """Get the bag weight."""
248 return self._wizard.bagWeight
249
250 @property
251 def cargoWeight(self):
252 """Get the cargo weight."""
253 return self._wizard.cargoWeight
254
255 @property
256 def mailWeight(self):
257 """Get the mail weight."""
258 return self._wizard.mailWeight
259
260 @property
261 def zfw(self):
262 """Get Zero-Fuel Weight calculated for the current flight."""
263 return self._wizard.zfw
264
265 @property
266 def filedCruiseAltitude(self):
267 """Get cruise altitude filed for the current flight."""
268 return self._wizard.filedCruiseAltitude
269
270 @property
271 def cruiseAltitude(self):
272 """Get cruise altitude set for the current flight."""
273 return self._wizard.cruiseAltitude
274
275 @property
276 def loggableCruiseAltitude(self):
277 """Get the cruise altitude that can be logged."""
278 return self._wizard.loggableCruiseAltitude
279
280 @property
281 def route(self):
282 """Get the flight route."""
283 return self._wizard.route
284
285 @property
286 def departureMETAR(self):
287 """Get the METAR of the deprature airport."""
288 return self._wizard.departureMETAR
289
290 @property
291 def arrivalMETAR(self):
292 """Get the METAR of the deprature airport."""
293 return self._wizard.arrivalMETAR
294
295 @property
296 def departureRunway(self):
297 """Get the name of the departure runway."""
298 return self._wizard.departureRunway
299
300 @property
301 def sid(self):
302 """Get the SID."""
303 return self._wizard.sid
304
305 @property
306 def v1(self):
307 """Get the V1 speed calculated for the flight."""
308 return self._wizard.v1
309
310 @property
311 def vr(self):
312 """Get the Vr speed calculated for the flight."""
313 return self._wizard.vr
314
315 @property
316 def v2(self):
317 """Get the V2 speed calculated for the flight."""
318 return self._wizard.v2
319
320 @property
321 def derate(self):
322 """Get the derate value calculated for the flight."""
323 return self._wizard.derate
324
325 @property
326 def takeoffAntiIceOn(self):
327 """Get whether the anti-ice system was on during take-off."""
328 return self._wizard.takeoffAntiIceOn
329
330 @takeoffAntiIceOn.setter
331 def takeoffAntiIceOn(self, value):
332 """Set the anti-ice on indicator."""
333 gobject.idle_add(self._setTakeoffAntiIceOn, value)
334
335 @property
336 def rtoIndicated(self):
337 """Get whether the pilot has indicated than an RTO has occured."""
338 return self._wizard.rtoIndicated
339
340 @property
341 def arrivalRunway(self):
342 """Get the arrival runway."""
343 return self._wizard.arrivalRunway
344
345 @property
346 def star(self):
347 """Get the STAR."""
348 return self._wizard.star
349
350 @property
351 def transition(self):
352 """Get the transition."""
353 return self._wizard.transition
354
355 @property
356 def approachType(self):
357 """Get the approach type."""
358 return self._wizard.approachType
359
360 @property
361 def vref(self):
362 """Get the Vref speed calculated for the flight."""
363 return self._wizard.vref
364
365 @property
366 def landingAntiIceOn(self):
367 """Get whether the anti-ice system was on during landing."""
368 return self._wizard.landingAntiIceOn
369
370 @landingAntiIceOn.setter
371 def landingAntiIceOn(self, value):
372 """Set the anti-ice on indicator."""
373 gobject.idle_add(self._setLandingAntiIceOn, value)
374
375 @property
376 def flightType(self):
377 """Get the flight type."""
378 return self._wizard.flightType
379
380 @property
381 def online(self):
382 """Get whether the flight was online or not."""
383 return self._wizard.online
384
385 @property
386 def comments(self):
387 """Get the comments."""
388 return self._flightInfo.comments
389
390 @property
391 def hasComments(self):
392 """Indicate whether there is a comment."""
393 return self._flightInfo.hasComments
394
395 @property
396 def flightDefects(self):
397 """Get the flight defects."""
398 return self._flightInfo.faultsAndExplanations
399
400 @property
401 def delayCodes(self):
402 """Get the delay codes."""
403 return self._flightInfo.delayCodes
404
405 @property
406 def hasDelayCode(self):
407 """Determine if there is at least one delay code selected."""
408 return self._flightInfo.hasDelayCode
409
410 def run(self):
411 """Run the GUI."""
412 if self.config.autoUpdate:
413 self._updater = Updater(self,
414 self._programDirectory,
415 self.config.updateURL,
416 self._mainWindow)
417 self._updater.start()
418
419 singleton.raiseCallback = self.raiseCallback
420 gtk.main()
421 singleton.raiseCallback = None
422
423 self._disconnect()
424
425 def connected(self, fsType, descriptor):
426 """Called when we have connected to the simulator."""
427 self._connected = True
428 self._logger.untimedMessage("MLX %s connected to the simulator %s" % \
429 (const.VERSION, descriptor))
430 fs.sendMessage(const.MESSAGETYPE_INFORMATION,
431 "Welcome to MAVA Logger X " + const.VERSION)
432 gobject.idle_add(self._handleConnected, fsType, descriptor)
433
434 def _handleConnected(self, fsType, descriptor):
435 """Called when the connection to the simulator has succeeded."""
436 self._statusbar.updateConnection(self._connecting, self._connected)
437 self.endBusy()
438 if not self._reconnecting:
439 self._wizard.connected(fsType, descriptor)
440 self._reconnecting = False
441 self._fsType = fsType
442 self._listenHotkeys()
443
444 def connectionFailed(self):
445 """Called when the connection failed."""
446 self._logger.untimedMessage("Connection to the simulator failed")
447 gobject.idle_add(self._connectionFailed)
448
449 def _connectionFailed(self):
450 """Called when the connection failed."""
451 self.endBusy()
452 self._statusbar.updateConnection(self._connecting, self._connected)
453
454 dialog = gtk.MessageDialog(parent = self._mainWindow,
455 type = MESSAGETYPE_ERROR,
456 message_format = xstr("conn_failed"))
457
458 dialog.set_title(WINDOW_TITLE_BASE)
459 dialog.format_secondary_markup(xstr("conn_failed_sec"))
460
461 dialog.add_button(xstr("button_cancel"), 0)
462 dialog.add_button(xstr("button_tryagain"), 1)
463 dialog.set_default_response(1)
464
465 result = dialog.run()
466 dialog.hide()
467 if result == 1:
468 self.beginBusy(xstr("connect_busy"))
469 self._simulator.reconnect()
470 else:
471 self.reset()
472
473 def disconnected(self):
474 """Called when we have disconnected from the simulator."""
475 self._connected = False
476 self._logger.untimedMessage("Disconnected from the simulator")
477
478 gobject.idle_add(self._disconnected)
479
480 def _disconnected(self):
481 """Called when we have disconnected from the simulator unexpectedly."""
482 self._statusbar.updateConnection(self._connecting, self._connected)
483
484 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
485 message_format = xstr("conn_broken"),
486 parent = self._mainWindow)
487 dialog.set_title(WINDOW_TITLE_BASE)
488 dialog.format_secondary_markup(xstr("conn_broken_sec"))
489
490 dialog.add_button(xstr("button_cancel"), 0)
491 dialog.add_button(xstr("button_reconnect"), 1)
492 dialog.set_default_response(1)
493
494 result = dialog.run()
495 dialog.hide()
496 if result == 1:
497 self.beginBusy(xstr("connect_busy"))
498 self._reconnecting = True
499 self._simulator.reconnect()
500 else:
501 self.reset()
502
503 def enableFlightInfo(self, aircraftType):
504 """Enable the flight info tab."""
505 self._flightInfo.enable(aircraftType)
506
507 def cancelFlight(self):
508 """Cancel the current file, if the user confirms it."""
509 dialog = gtk.MessageDialog(parent = self._mainWindow,
510 type = MESSAGETYPE_QUESTION,
511 message_format = xstr("cancelFlight_question"))
512
513 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
514 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
515
516 dialog.set_title(WINDOW_TITLE_BASE)
517 result = dialog.run()
518 dialog.hide()
519
520 if result==RESPONSETYPE_YES:
521 self.reset()
522
523 def reset(self):
524 """Reset the GUI."""
525 self._disconnect()
526
527 self._simulator = None
528
529 self._flightInfo.reset()
530 self._flightInfo.disable()
531 self.resetFlightStatus()
532
533 self._weightHelp.reset()
534 self._weightHelp.disable()
535 self._notebook.set_current_page(0)
536
537 self._logView.get_buffer().set_text("")
538
539 if self.loggedIn:
540 self._wizard.reloadFlights(self._handleReloadResult)
541 else:
542 self._wizard.reset(None)
543
544 def _handleReloadResult(self, returned, result):
545 """Handle the result of the reloading of the flights."""
546 self._wizard.reset(result if returned and result.loggedIn else None)
547
548 def _disconnect(self, closingMessage = None, duration = 3):
549 """Disconnect from the simulator if connected."""
550 self.stopMonitoring()
551 self._clearHotkeys()
552
553 if self._connected:
554 if closingMessage is None:
555 self._flight.simulator.disconnect()
556 else:
557 fs.sendMessage(const.MESSAGETYPE_ENVIRONMENT,
558 closingMessage, duration,
559 disconnect = True)
560 self._connected = False
561
562 self._connecting = False
563 self._reconnecting = False
564 self._statusbar.updateConnection(False, False)
565 self._weightHelp.disable()
566
567 return True
568
569 def insertFlightLogLine(self, index, timestampString, text, isFault):
570 """Insert the flight log line with the given data."""
571 gobject.idle_add(self._insertFlightLogLine, index,
572 formatFlightLogLine(timestampString, text),
573 isFault)
574
575 def _insertFlightLogLine(self, index, line, isFault):
576 """Perform the real insertion.
577
578 To be called from the event loop."""
579 buffer = self._logView.get_buffer()
580 lineIter = buffer.get_iter_at_line(index)
581 insertTextBuffer(buffer, lineIter, line, isFault = isFault)
582 self._logView.scroll_mark_onscreen(buffer.get_insert())
583
584 def removeFlightLogLine(self, index):
585 """Remove the flight log line with the given index."""
586 gobject.idle_add(self._removeFlightLogLine, index)
587
588 def addFault(self, id, timestampString, text):
589 """Add a fault to the list of faults."""
590 faultText = formatFlightLogLine(timestampString, text).strip()
591 self._flightInfo.addFault(id, faultText)
592
593 def updateFault(self, id, timestampString, text):
594 """Update a fault in the list of faults."""
595 faultText = formatFlightLogLine(timestampString, text).strip()
596 self._flightInfo.updateFault(id, faultText)
597
598 def clearFault(self, id):
599 """Clear a fault in the list of faults."""
600 self._flightInfo.clearFault(id)
601
602 def _removeFlightLogLine(self, index):
603 """Perform the real removal."""
604 buffer = self._logView.get_buffer()
605 startIter = buffer.get_iter_at_line(index)
606 endIter = buffer.get_iter_at_line(index+1)
607 buffer.delete(startIter, endIter)
608 self._logView.scroll_mark_onscreen(buffer.get_insert())
609
610 def check(self, flight, aircraft, logger, oldState, state):
611 """Update the data."""
612 gobject.idle_add(self._monitorWindow.setData, state)
613 gobject.idle_add(self._statusbar.updateTime, state.timestamp)
614
615 def resetFlightStatus(self):
616 """Reset the status of the flight."""
617 self._statusbar.resetFlightStatus()
618 self._statusbar.updateTime()
619 self._statusIcon.resetFlightStatus()
620
621 def setStage(self, stage):
622 """Set the stage of the flight."""
623 gobject.idle_add(self._setStage, stage)
624
625 def _setStage(self, stage):
626 """Set the stage of the flight."""
627 self._statusbar.setStage(stage)
628 self._statusIcon.setStage(stage)
629 self._wizard.setStage(stage)
630 if stage==const.STAGE_END:
631 welcomeMessage = \
632 airports.getWelcomeMessage(self.bookedFlight.arrivalICAO)
633 self._disconnect(closingMessage =
634 "Flight plan closed. " + welcomeMessage,
635 duration = 5)
636
637 def setRating(self, rating):
638 """Set the rating of the flight."""
639 gobject.idle_add(self._setRating, rating)
640
641 def _setRating(self, rating):
642 """Set the rating of the flight."""
643 self._statusbar.setRating(rating)
644 self._statusIcon.setRating(rating)
645
646 def setNoGo(self, reason):
647 """Set the rating of the flight to No-Go with the given reason."""
648 gobject.idle_add(self._setNoGo, reason)
649
650 def _setNoGo(self, reason):
651 """Set the rating of the flight."""
652 self._statusbar.setNoGo(reason)
653 self._statusIcon.setNoGo(reason)
654
655 def _handleMainWindowState(self, window, event):
656 """Hande a change in the state of the window"""
657 iconified = gdk.WindowState.ICONIFIED if pygobject \
658 else gdk.WINDOW_STATE_ICONIFIED
659
660 if (event.changed_mask&WINDOW_STATE_WITHDRAWN)!=0:
661 if (event.new_window_state&WINDOW_STATE_WITHDRAWN)!=0:
662 self._statusIcon.mainWindowHidden()
663 else:
664 self._statusIcon.mainWindowShown()
665
666 if self.config.hideMinimizedWindow and not pygobject and \
667 (event.changed_mask&WINDOW_STATE_ICONIFIED)!=0 and \
668 (event.new_window_state&WINDOW_STATE_ICONIFIED)!=0:
669 self.hideMainWindow(savePosition = False)
670 elif (event.changed_mask&WINDOW_STATE_ICONIFIED)!=0 and \
671 (event.new_window_state&WINDOW_STATE_ICONIFIED)==0:
672 self._mainWindow.present()
673
674 def raiseCallback(self):
675 """Callback for the singleton handling code."""
676 gobject.idle_add(self.raiseMainWindow)
677
678 def raiseMainWindow(self):
679 """Show the main window if invisible, and raise it."""
680 if not self._mainWindow.get_visible():
681 self.showMainWindow()
682 self._mainWindow.present()
683
684 def deleteMainWindow(self, window, event):
685 """Handle the delete event for the main window."""
686 if self.config.quitOnClose:
687 self._quit()
688 else:
689 self.hideMainWindow()
690 return True
691
692 def hideMainWindow(self, savePosition = True):
693 """Hide the main window and save its position."""
694 if savePosition:
695 (self._mainWindowX, self._mainWindowY) = \
696 self._mainWindow.get_window().get_root_origin()
697 else:
698 self._mainWindowX = self._mainWindowY = None
699 self._mainWindow.hide()
700 return True
701
702 def showMainWindow(self):
703 """Show the main window at its former position."""
704 if self._mainWindowX is not None and self._mainWindowY is not None:
705 self._mainWindow.move(self._mainWindowX, self._mainWindowY)
706
707 if pygobject:
708 self._mainWindow.show()
709 else:
710 self._mainWindow.present()
711 self._mainWindow.deiconify()
712
713 def toggleMainWindow(self):
714 """Toggle the main window."""
715 if self._mainWindow.get_visible():
716 self.hideMainWindow()
717 else:
718 self.showMainWindow()
719
720 def hideMonitorWindow(self, savePosition = True):
721 """Hide the monitor window."""
722 if savePosition:
723 (self._monitorWindowX, self._monitorWindowY) = \
724 self._monitorWindow.get_window().get_root_origin()
725 else:
726 self._monitorWindowX = self._monitorWindowY = None
727 self._monitorWindow.hide()
728 self._statusIcon.monitorWindowHidden()
729 if self._showMonitorMenuItem.get_active():
730 self._selfToggling = True
731 self._showMonitorMenuItem.set_active(False)
732 return True
733
734 def showMonitorWindow(self):
735 """Show the monitor window."""
736 if self._monitorWindowX is not None and self._monitorWindowY is not None:
737 self._monitorWindow.move(self._monitorWindowX, self._monitorWindowY)
738 self._monitorWindow.show_all()
739 self._statusIcon.monitorWindowShown()
740 if not self._showMonitorMenuItem.get_active():
741 self._selfToggling = True
742 self._showMonitorMenuItem.set_active(True)
743
744 def _toggleMonitorWindow(self, menuItem):
745 if self._selfToggling:
746 self._selfToggling = False
747 elif self._monitorWindow.get_visible():
748 self.hideMonitorWindow()
749 else:
750 self.showMonitorWindow()
751
752 def restart(self):
753 """Quit and restart the application."""
754 self.toRestart = True
755 self._quit(force = True)
756
757 def flushStdIO(self):
758 """Flush any text to the standard error that could not be logged."""
759 if self._stdioText:
760 sys.__stderr__.write(self._stdioText)
761
762 def writeStdIO(self, text):
763 """Write the given text into standard I/O log."""
764 with self._stdioLock:
765 self._stdioText += text
766
767 gobject.idle_add(self._writeStdIO)
768
769 def beginBusy(self, message):
770 """Begin a period of background processing."""
771 self._wizard.set_sensitive(False)
772 self._weightHelp.set_sensitive(False)
773 self._mainWindow.get_window().set_cursor(self._busyCursor)
774 self._statusbar.updateBusyState(message)
775
776 def endBusy(self):
777 """End a period of background processing."""
778 self._mainWindow.get_window().set_cursor(None)
779 self._weightHelp.set_sensitive(True)
780 self._wizard.set_sensitive(True)
781 self._statusbar.updateBusyState(None)
782
783 def initializeWeightHelp(self):
784 """Initialize the weight help tab."""
785 self._weightHelp.reset()
786 self._weightHelp.enable()
787
788 def getFleetAsync(self, callback = None, force = None):
789 """Get the fleet asynchronously."""
790 gobject.idle_add(self.getFleet, callback, force)
791
792 def getFleet(self, callback = None, force = False):
793 """Get the fleet.
794
795 If force is False, and we already have a fleet retrieved,
796 that one will be used."""
797 if self._fleet is None or force:
798 self._fleetCallback = callback
799 self.beginBusy(xstr("fleet_busy"))
800 self.webHandler.getFleet(self._fleetResultCallback)
801 else:
802 callback(self._fleet)
803
804 def commentsChanged(self):
805 """Indicate that the comments have changed."""
806 self._wizard.commentsChanged()
807
808 def delayCodesChanged(self):
809 """Called when the delay codes have changed."""
810 self._wizard.delayCodesChanged()
811
812 def updateRTO(self, inLoop = False):
813 """Indicate that the RTO state should be updated."""
814 if inLoop:
815 self._wizard.updateRTO()
816 else:
817 gobject.idle_add(self.updateRTO, True)
818
819 def rtoToggled(self, indicated):
820 """Called when the user has toggled the RTO checkbox."""
821 self._flight.rtoToggled(indicated)
822
823 def _fleetResultCallback(self, returned, result):
824 """Called when the fleet has been queried."""
825 gobject.idle_add(self._handleFleetResult, returned, result)
826
827 def _handleFleetResult(self, returned, result):
828 """Handle the fleet result."""
829 self.endBusy()
830 if returned:
831 self._fleet = result.fleet
832 else:
833 self._fleet = None
834
835 dialog = gtk.MessageDialog(parent = self.mainWindow,
836 type = MESSAGETYPE_ERROR,
837 message_format = xstr("fleet_failed"))
838 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
839 dialog.set_title(WINDOW_TITLE_BASE)
840 dialog.run()
841 dialog.hide()
842
843 callback = self._fleetCallback
844 self._fleetCallback = None
845 if callback is not None:
846 callback(self._fleet)
847 self._fleetGateStatus.handleFleet(self._fleet)
848
849 def updatePlane(self, tailNumber, status,
850 gateNumber = None, callback = None):
851 """Update the status of the given plane."""
852 self.beginBusy(xstr("fleet_update_busy"))
853
854 self._updatePlaneCallback = callback
855
856 self._updatePlaneTailNumber = tailNumber
857 self._updatePlaneStatus = status
858 self._updatePlaneGateNumber = gateNumber
859
860 self.webHandler.updatePlane(self._updatePlaneResultCallback,
861 tailNumber, status, gateNumber)
862
863 def _updatePlaneResultCallback(self, returned, result):
864 """Called when the status of a plane has been updated."""
865 gobject.idle_add(self._handleUpdatePlaneResult, returned, result)
866
867 def _handleUpdatePlaneResult(self, returned, result):
868 """Handle the plane update result."""
869 self.endBusy()
870 if returned:
871 success = result.success
872 if success:
873 if self._fleet is not None:
874 self._fleet.updatePlane(self._updatePlaneTailNumber,
875 self._updatePlaneStatus,
876 self._updatePlaneGateNumber)
877 self._fleetGateStatus.handleFleet(self._fleet)
878 else:
879 dialog = gtk.MessageDialog(parent = self.mainWindow,
880 type = MESSAGETYPE_ERROR,
881 message_format = xstr("fleet_update_failed"))
882 dialog.add_button(xstr("button_ok"), RESPONSETYPE_ACCEPT)
883 dialog.set_title(WINDOW_TITLE_BASE)
884 dialog.run()
885 dialog.hide()
886
887 success = None
888
889 callback = self._updatePlaneCallback
890 self._updatePlaneCallback = None
891 if callback is not None:
892 callback(success)
893
894 def _writeStdIO(self):
895 """Perform the real writing."""
896 with self._stdioLock:
897 text = self._stdioText
898 self._stdioText = ""
899 if not text: return
900
901 lines = text.splitlines()
902 if text[-1]=="\n":
903 text = ""
904 else:
905 text = lines[-1]
906 lines = lines[:-1]
907
908 now = datetime.datetime.now()
909 timeStr = "%02d:%02d:%02d: " % (now.hour, now.minute, now.second)
910
911 for line in lines:
912 #print >> sys.__stdout__, line
913 if self._stdioStartingLine:
914 self._writeLog(timeStr, self._debugLogView)
915 self._writeLog(line + "\n", self._debugLogView)
916 self._stdioStartingLine = True
917
918 if text:
919 #print >> sys.__stdout__, text,
920 if self._stdioStartingLine:
921 self._writeLog(timeStr, self._debugLogView)
922 self._writeLog(text, self._debugLogView)
923 self._stdioStartingLine = False
924
925 def connectSimulator(self, aircraftType, simulatorType):
926 """Connect to the simulator for the first time."""
927 self._logger.reset()
928
929 self._flight = flight.Flight(self._logger, self)
930 self._flight.flareTimeFromFS = self.config.flareTimeFromFS
931 self._flight.aircraftType = aircraftType
932 self._flight.aircraft = acft.Aircraft.create(self._flight)
933 self._flight.aircraft._checkers.append(self)
934
935 if self._simulator is None:
936 self._simulator = fs.createSimulator(simulatorType, self)
937 fs.setupMessageSending(self.config, self._simulator)
938 self._setupTimeSync()
939
940 self._flight.simulator = self._simulator
941
942 self.beginBusy(xstr("connect_busy"))
943 self._statusbar.updateConnection(self._connecting, self._connected)
944
945 self._connecting = True
946 self._simulator.connect(self._flight.aircraft)
947
948 def startMonitoring(self):
949 """Start monitoring."""
950 if not self._monitoring:
951 self.simulator.startMonitoring()
952 self._monitoring = True
953
954 def stopMonitoring(self):
955 """Stop monitoring."""
956 if self._monitoring:
957 self.simulator.stopMonitoring()
958 self._monitoring = False
959
960 def cruiseLevelChanged(self):
961 """Called when the cruise level is changed in the flight wizard."""
962 if self._flight is not None:
963 return self._flight.cruiseLevelChanged()
964 else:
965 return False
966
967 def _buildMenuBar(self, accelGroup):
968 """Build the main menu bar."""
969 menuBar = gtk.MenuBar()
970
971 fileMenuItem = gtk.MenuItem(xstr("menu_file"))
972 fileMenu = gtk.Menu()
973 fileMenuItem.set_submenu(fileMenu)
974 menuBar.append(fileMenuItem)
975
976 loadPIREPMenuItem = gtk.ImageMenuItem(gtk.STOCK_OPEN)
977 loadPIREPMenuItem.set_use_stock(True)
978 loadPIREPMenuItem.set_label(xstr("menu_file_loadPIREP"))
979 loadPIREPMenuItem.add_accelerator("activate", accelGroup,
980 ord(xstr("menu_file_loadPIREP_key")),
981 CONTROL_MASK, ACCEL_VISIBLE)
982 loadPIREPMenuItem.connect("activate", self._loadPIREP)
983 fileMenu.append(loadPIREPMenuItem)
984
985 fileMenu.append(gtk.SeparatorMenuItem())
986
987 quitMenuItem = gtk.ImageMenuItem(gtk.STOCK_QUIT)
988 quitMenuItem.set_use_stock(True)
989 quitMenuItem.set_label(xstr("menu_file_quit"))
990 quitMenuItem.add_accelerator("activate", accelGroup,
991 ord(xstr("menu_file_quit_key")),
992 CONTROL_MASK, ACCEL_VISIBLE)
993 quitMenuItem.connect("activate", self._quit)
994 fileMenu.append(quitMenuItem)
995
996 toolsMenuItem = gtk.MenuItem(xstr("menu_tools"))
997 toolsMenu = gtk.Menu()
998 toolsMenuItem.set_submenu(toolsMenu)
999 menuBar.append(toolsMenuItem)
1000
1001 checklistMenuItem = gtk.ImageMenuItem(gtk.STOCK_APPLY)
1002 checklistMenuItem.set_use_stock(True)
1003 checklistMenuItem.set_label(xstr("menu_tools_chklst"))
1004 checklistMenuItem.add_accelerator("activate", accelGroup,
1005 ord(xstr("menu_tools_chklst_key")),
1006 CONTROL_MASK, ACCEL_VISIBLE)
1007 checklistMenuItem.connect("activate", self._editChecklist)
1008 toolsMenu.append(checklistMenuItem)
1009
1010 approachCalloutsMenuItem = gtk.ImageMenuItem(gtk.STOCK_EDIT)
1011 approachCalloutsMenuItem.set_use_stock(True)
1012 approachCalloutsMenuItem.set_label(xstr("menu_tools_callouts"))
1013 approachCalloutsMenuItem.add_accelerator("activate", accelGroup,
1014 ord(xstr("menu_tools_callouts_key")),
1015 CONTROL_MASK, ACCEL_VISIBLE)
1016 approachCalloutsMenuItem.connect("activate", self._editApproachCallouts)
1017 toolsMenu.append(approachCalloutsMenuItem)
1018
1019 prefsMenuItem = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
1020 prefsMenuItem.set_use_stock(True)
1021 prefsMenuItem.set_label(xstr("menu_tools_prefs"))
1022 prefsMenuItem.add_accelerator("activate", accelGroup,
1023 ord(xstr("menu_tools_prefs_key")),
1024 CONTROL_MASK, ACCEL_VISIBLE)
1025 prefsMenuItem.connect("activate", self._editPreferences)
1026 toolsMenu.append(prefsMenuItem)
1027
1028 toolsMenu.append(gtk.SeparatorMenuItem())
1029
1030 bugReportMenuItem = gtk.ImageMenuItem(gtk.STOCK_PASTE)
1031 bugReportMenuItem.set_use_stock(True)
1032 bugReportMenuItem.set_label(xstr("menu_tools_bugreport"))
1033 bugReportMenuItem.add_accelerator("activate", accelGroup,
1034 ord(xstr("menu_tools_bugreport_key")),
1035 CONTROL_MASK, ACCEL_VISIBLE)
1036 bugReportMenuItem.connect("activate", self._reportBug)
1037 toolsMenu.append(bugReportMenuItem)
1038
1039 viewMenuItem = gtk.MenuItem(xstr("menu_view"))
1040 viewMenu = gtk.Menu()
1041 viewMenuItem.set_submenu(viewMenu)
1042 menuBar.append(viewMenuItem)
1043
1044 self._showMonitorMenuItem = gtk.CheckMenuItem()
1045 self._showMonitorMenuItem.set_label(xstr("menu_view_monitor"))
1046 self._showMonitorMenuItem.set_use_underline(True)
1047 self._showMonitorMenuItem.set_active(False)
1048 self._showMonitorMenuItem.add_accelerator("activate", accelGroup,
1049 ord(xstr("menu_view_monitor_key")),
1050 CONTROL_MASK, ACCEL_VISIBLE)
1051 self._showMonitorMenuItem.connect("toggled", self._toggleMonitorWindow)
1052 viewMenu.append(self._showMonitorMenuItem)
1053
1054 showDebugMenuItem = gtk.CheckMenuItem()
1055 showDebugMenuItem.set_label(xstr("menu_view_debug"))
1056 showDebugMenuItem.set_use_underline(True)
1057 showDebugMenuItem.set_active(False)
1058 showDebugMenuItem.add_accelerator("activate", accelGroup,
1059 ord(xstr("menu_view_debug_key")),
1060 CONTROL_MASK, ACCEL_VISIBLE)
1061 showDebugMenuItem.connect("toggled", self._toggleDebugLog)
1062 viewMenu.append(showDebugMenuItem)
1063
1064 helpMenuItem = gtk.MenuItem(xstr("menu_help"))
1065 helpMenu = gtk.Menu()
1066 helpMenuItem.set_submenu(helpMenu)
1067 menuBar.append(helpMenuItem)
1068
1069 manualMenuItem = gtk.ImageMenuItem(gtk.STOCK_HELP)
1070 manualMenuItem.set_use_stock(True)
1071 manualMenuItem.set_label(xstr("menu_help_manual"))
1072 manualMenuItem.add_accelerator("activate", accelGroup,
1073 ord(xstr("menu_help_manual_key")),
1074 CONTROL_MASK, ACCEL_VISIBLE)
1075 manualMenuItem.connect("activate", self._showManual)
1076 helpMenu.append(manualMenuItem)
1077
1078 helpMenu.append(gtk.SeparatorMenuItem())
1079
1080 aboutMenuItem = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
1081 aboutMenuItem.set_use_stock(True)
1082 aboutMenuItem.set_label(xstr("menu_help_about"))
1083 aboutMenuItem.add_accelerator("activate", accelGroup,
1084 ord(xstr("menu_help_about_key")),
1085 CONTROL_MASK, ACCEL_VISIBLE)
1086 aboutMenuItem.connect("activate", self._showAbout)
1087 helpMenu.append(aboutMenuItem)
1088
1089 return menuBar
1090
1091 def _toggleDebugLog(self, menuItem):
1092 """Toggle the debug log."""
1093 if menuItem.get_active():
1094 label = gtk.Label(xstr("tab_debug_log"))
1095 label.set_use_underline(True)
1096 label.set_tooltip_text(xstr("tab_debug_log_tooltip"))
1097 self._debugLogPage = self._notebook.append_page(self._debugLogWidget, label)
1098 self._notebook.set_current_page(self._debugLogPage)
1099 else:
1100 self._notebook.remove_page(self._debugLogPage)
1101
1102 def _buildLogWidget(self):
1103 """Build the widget for the log."""
1104 alignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
1105
1106 alignment.set_padding(padding_top = 8, padding_bottom = 8,
1107 padding_left = 16, padding_right = 16)
1108
1109 logScroller = gtk.ScrolledWindow()
1110 # FIXME: these should be constants in common
1111 logScroller.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1112 else gtk.POLICY_AUTOMATIC,
1113 gtk.PolicyType.AUTOMATIC if pygobject
1114 else gtk.POLICY_AUTOMATIC)
1115 logScroller.set_shadow_type(gtk.ShadowType.IN if pygobject
1116 else gtk.SHADOW_IN)
1117 logView = gtk.TextView()
1118 logView.set_editable(False)
1119 logView.set_cursor_visible(False)
1120 logScroller.add(logView)
1121
1122 logBox = gtk.VBox()
1123 logBox.pack_start(logScroller, True, True, 0)
1124 logBox.set_size_request(-1, 200)
1125
1126 alignment.add(logBox)
1127
1128 return (alignment, logView)
1129
1130 def _writeLog(self, msg, logView, isFault = False):
1131 """Write the given message to the log."""
1132 buffer = logView.get_buffer()
1133 appendTextBuffer(buffer, msg, isFault = isFault)
1134 logView.scroll_mark_onscreen(buffer.get_insert())
1135
1136 def _quit(self, what = None, force = False):
1137 """Quit from the application."""
1138 if force:
1139 result=RESPONSETYPE_YES
1140 else:
1141 dialog = gtk.MessageDialog(parent = self._mainWindow,
1142 type = MESSAGETYPE_QUESTION,
1143 message_format = xstr("quit_question"))
1144
1145 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
1146 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
1147
1148 dialog.set_title(WINDOW_TITLE_BASE)
1149 result = dialog.run()
1150 dialog.hide()
1151
1152 if result==RESPONSETYPE_YES:
1153 self._statusIcon.destroy()
1154 return gtk.main_quit()
1155
1156 def _notebookPageSwitch(self, notebook, page, page_num):
1157 """Called when the current page of the notebook has changed."""
1158 if page_num==0:
1159 gobject.idle_add(self._wizard.grabDefault)
1160 else:
1161 self._mainWindow.set_default(None)
1162
1163 def isWizardActive(self):
1164 """Determine if the flight wizard is active."""
1165 return self._notebook.get_current_page()==0
1166
1167 def _editChecklist(self, menuItem):
1168 """Callback for editing the checklists."""
1169 self._checklistEditor.run()
1170
1171 def _editApproachCallouts(self, menuItem):
1172 """Callback for editing the approach callouts."""
1173 self._approachCalloutsEditor.run()
1174
1175 def _editPreferences(self, menuItem):
1176 """Callback for editing the preferences."""
1177 self._clearHotkeys()
1178 self._preferences.run(self.config)
1179 self._setupTimeSync()
1180 self._listenHotkeys()
1181
1182 def _reportBug(self, menuItem):
1183 """Callback for reporting a bug."""
1184 self._bugReportDialog.run()
1185
1186 def _setupTimeSync(self):
1187 """Enable or disable the simulator time synchronization based on the
1188 configuration."""
1189 simulator = self._simulator
1190 if simulator is not None:
1191 if self.config.syncFSTime:
1192 simulator.enableTimeSync()
1193 else:
1194 simulator.disableTimeSync()
1195
1196 def _loadPIREP(self, menuItem):
1197 """Load a PIREP for sending."""
1198 dialog = self._getLoadPirepDialog()
1199
1200 if self._lastLoadedPIREP:
1201 dialog.set_current_folder(os.path.dirname(self._lastLoadedPIREP))
1202 else:
1203 pirepDirectory = self.config.pirepDirectory
1204 if pirepDirectory is not None:
1205 dialog.set_current_folder(pirepDirectory)
1206
1207 result = dialog.run()
1208 dialog.hide()
1209
1210 if result==RESPONSETYPE_OK:
1211 self._lastLoadedPIREP = text2unicode(dialog.get_filename())
1212
1213 pirep = PIREP.load(self._lastLoadedPIREP)
1214 if pirep is None:
1215 dialog = gtk.MessageDialog(parent = self._mainWindow,
1216 type = MESSAGETYPE_ERROR,
1217 message_format = xstr("loadPIREP_failed"))
1218 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1219 dialog.set_title(WINDOW_TITLE_BASE)
1220 dialog.format_secondary_markup(xstr("loadPIREP_failed_sec"))
1221 dialog.run()
1222 dialog.hide()
1223 else:
1224 dialog = self._getSendLoadedDialog(pirep)
1225 dialog.show_all()
1226 while True:
1227 result = dialog.run()
1228
1229 if result==RESPONSETYPE_OK:
1230 self.sendPIREP(pirep)
1231 elif result==1:
1232 self._pirepViewer.setPIREP(pirep)
1233 self._pirepViewer.show_all()
1234 self._pirepViewer.run()
1235 self._pirepViewer.hide()
1236 else:
1237 break
1238
1239 dialog.hide()
1240
1241 def _getLoadPirepDialog(self):
1242 """Get the PIREP loading file chooser dialog.
1243
1244 If it is not created yet, it will be created."""
1245 if self._loadPIREPDialog is None:
1246 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
1247 xstr("loadPIREP_browser_title"),
1248 action = FILE_CHOOSER_ACTION_OPEN,
1249 buttons = (gtk.STOCK_CANCEL,
1250 RESPONSETYPE_CANCEL,
1251 gtk.STOCK_OK, RESPONSETYPE_OK),
1252 parent = self._mainWindow)
1253 dialog.set_modal(True)
1254
1255
1256 filter = gtk.FileFilter()
1257 filter.set_name(xstr("file_filter_pireps"))
1258 filter.add_pattern("*.pirep")
1259 dialog.add_filter(filter)
1260
1261 filter = gtk.FileFilter()
1262 filter.set_name(xstr("file_filter_all"))
1263 filter.add_pattern("*.*")
1264 dialog.add_filter(filter)
1265
1266 self._loadPIREPDialog = dialog
1267
1268 return self._loadPIREPDialog
1269
1270 def _getSendLoadedDialog(self, pirep):
1271 """Get a dialog displaying the main information of the flight from the
1272 PIREP and providing Cancel and Send buttons."""
1273 dialog = gtk.Dialog(title = WINDOW_TITLE_BASE + " - " +
1274 xstr("loadPIREP_send_title"),
1275 parent = self._mainWindow,
1276 flags = DIALOG_MODAL)
1277
1278 contentArea = dialog.get_content_area()
1279
1280 label = gtk.Label(xstr("loadPIREP_send_help"))
1281 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1282 xscale = 0.0, yscale = 0.0)
1283 alignment.set_padding(padding_top = 16, padding_bottom = 0,
1284 padding_left = 48, padding_right = 48)
1285 alignment.add(label)
1286 contentArea.pack_start(alignment, False, False, 8)
1287
1288 table = gtk.Table(5, 2)
1289 tableAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1290 xscale = 0.0, yscale = 0.0)
1291 tableAlignment.set_padding(padding_top = 0, padding_bottom = 32,
1292 padding_left = 48, padding_right = 48)
1293 table.set_row_spacings(4)
1294 table.set_col_spacings(16)
1295 tableAlignment.add(table)
1296 contentArea.pack_start(tableAlignment, True, True, 8)
1297
1298 bookedFlight = pirep.bookedFlight
1299
1300 label = gtk.Label("<b>" + xstr("loadPIREP_send_flightno") + "</b>")
1301 label.set_use_markup(True)
1302 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1303 xscale = 0.0, yscale = 0.0)
1304 labelAlignment.add(label)
1305 table.attach(labelAlignment, 0, 1, 0, 1)
1306
1307 label = gtk.Label(bookedFlight.callsign)
1308 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1309 xscale = 0.0, yscale = 0.0)
1310 labelAlignment.add(label)
1311 table.attach(labelAlignment, 1, 2, 0, 1)
1312
1313 label = gtk.Label("<b>" + xstr("loadPIREP_send_date") + "</b>")
1314 label.set_use_markup(True)
1315 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1316 xscale = 0.0, yscale = 0.0)
1317 labelAlignment.add(label)
1318 table.attach(labelAlignment, 0, 1, 1, 2)
1319
1320 label = gtk.Label(str(bookedFlight.departureTime.date()))
1321 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1322 xscale = 0.0, yscale = 0.0)
1323 labelAlignment.add(label)
1324 table.attach(labelAlignment, 1, 2, 1, 2)
1325
1326 label = gtk.Label("<b>" + xstr("loadPIREP_send_from") + "</b>")
1327 label.set_use_markup(True)
1328 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1329 xscale = 0.0, yscale = 0.0)
1330 labelAlignment.add(label)
1331 table.attach(labelAlignment, 0, 1, 2, 3)
1332
1333 label = gtk.Label(bookedFlight.departureICAO)
1334 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1335 xscale = 0.0, yscale = 0.0)
1336 labelAlignment.add(label)
1337 table.attach(labelAlignment, 1, 2, 2, 3)
1338
1339 label = gtk.Label("<b>" + xstr("loadPIREP_send_to") + "</b>")
1340 label.set_use_markup(True)
1341 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1342 xscale = 0.0, yscale = 0.0)
1343 labelAlignment.add(label)
1344 table.attach(labelAlignment, 0, 1, 3, 4)
1345
1346 label = gtk.Label(bookedFlight.arrivalICAO)
1347 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1348 xscale = 0.0, yscale = 0.0)
1349 labelAlignment.add(label)
1350 table.attach(labelAlignment, 1, 2, 3, 4)
1351
1352 label = gtk.Label("<b>" + xstr("loadPIREP_send_rating") + "</b>")
1353 label.set_use_markup(True)
1354 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1355 xscale = 0.0, yscale = 0.0)
1356 labelAlignment.add(label)
1357 table.attach(labelAlignment, 0, 1, 4, 5)
1358
1359 rating = pirep.rating
1360 label = gtk.Label()
1361 if rating<0:
1362 label.set_markup('<b><span foreground="red">NO GO</span></b>')
1363 else:
1364 label.set_text("%.1f %%" % (rating,))
1365
1366 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1367 xscale = 0.0, yscale = 0.0)
1368 labelAlignment.add(label)
1369 table.attach(labelAlignment, 1, 2, 4, 5)
1370
1371 dialog.add_button(xstr("button_cancel"), RESPONSETYPE_REJECT)
1372 dialog.add_button(xstr("viewPIREP"), 1)
1373 dialog.add_button(xstr("sendPIREP"), RESPONSETYPE_OK)
1374
1375 return dialog
1376
1377 def sendPIREP(self, pirep, callback = None):
1378 """Send the given PIREP."""
1379 self.beginBusy(xstr("sendPIREP_busy"))
1380 self._sendPIREPCallback = callback
1381 self.webHandler.sendPIREP(self._pirepSentCallback, pirep)
1382
1383 def _pirepSentCallback(self, returned, result):
1384 """Callback for the PIREP sending result."""
1385 gobject.idle_add(self._handlePIREPSent, returned, result)
1386
1387 def _handlePIREPSent(self, returned, result):
1388 """Callback for the PIREP sending result."""
1389 self.endBusy()
1390 secondaryMarkup = None
1391 type = MESSAGETYPE_ERROR
1392 if returned:
1393 if result.success:
1394 type = MESSAGETYPE_INFO
1395 messageFormat = xstr("sendPIREP_success")
1396 secondaryMarkup = xstr("sendPIREP_success_sec")
1397 elif result.alreadyFlown:
1398 messageFormat = xstr("sendPIREP_already")
1399 secondaryMarkup = xstr("sendPIREP_already_sec")
1400 elif result.notAvailable:
1401 messageFormat = xstr("sendPIREP_notavail")
1402 else:
1403 messageFormat = xstr("sendPIREP_unknown")
1404 secondaryMarkup = xstr("sendPIREP_unknown_sec")
1405 else:
1406 print "PIREP sending failed", result
1407 messageFormat = xstr("sendPIREP_failed")
1408 secondaryMarkup = xstr("sendPIREP_failed_sec")
1409
1410 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
1411 type = type, message_format = messageFormat)
1412 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1413 dialog.set_title(WINDOW_TITLE_BASE)
1414 if secondaryMarkup is not None:
1415 dialog.format_secondary_markup(secondaryMarkup)
1416
1417 dialog.run()
1418 dialog.hide()
1419
1420 callback = self._sendPIREPCallback
1421 self._sendPIREPCallback = None
1422 if callback is not None:
1423 callback(returned, result)
1424
1425 def sendBugReport(self, summary, description, email, callback = None):
1426 """Send the bug report with the given data."""
1427 description += "\n\n" + ("=" * 40)
1428 description += "\n\nThe contents of the log:\n\n"
1429
1430 for (timestampString, text) in self._logger.lines:
1431 description += unicode(formatFlightLogLine(timestampString, text))
1432
1433 description += "\n\n" + ("=" * 40)
1434 description += "\n\nThe contents of the debug log:\n\n"
1435
1436 buffer = self._debugLogView.get_buffer()
1437 description += buffer.get_text(buffer.get_start_iter(),
1438 buffer.get_end_iter(), True)
1439
1440 self.beginBusy(xstr("sendBugReport_busy"))
1441 self._sendBugReportCallback = callback
1442 self.webHandler.sendBugReport(self._bugReportSentCallback,
1443 summary, description, email)
1444
1445 def _bugReportSentCallback(self, returned, result):
1446 """Callback function for the bug report sending result."""
1447 gobject.idle_add(self._handleBugReportSent, returned, result)
1448
1449 def _handleBugReportSent(self, returned, result):
1450 """Callback for the bug report sending result."""
1451 self.endBusy()
1452 secondaryMarkup = None
1453 type = MESSAGETYPE_ERROR
1454 if returned:
1455 if result.success:
1456 type = MESSAGETYPE_INFO
1457 messageFormat = xstr("sendBugReport_success") % (result.ticketID,)
1458 secondaryMarkup = xstr("sendBugReport_success_sec")
1459 else:
1460 messageFormat = xstr("sendBugReport_error")
1461 secondaryMarkup = xstr("sendBugReport_siteerror_sec")
1462 else:
1463 messageFormat = xstr("sendBugReport_error")
1464 secondaryMarkup = xstr("sendBugReport_error_sec")
1465
1466 dialog = gtk.MessageDialog(parent = self._wizard.gui._bugReportDialog,
1467 type = type, message_format = messageFormat)
1468 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1469 dialog.set_title(WINDOW_TITLE_BASE)
1470 if secondaryMarkup is not None:
1471 dialog.format_secondary_markup(secondaryMarkup)
1472
1473 dialog.run()
1474 dialog.hide()
1475
1476 callback = self._sendBugReportCallback
1477 self._sendBugReportCallback = None
1478 if callback is not None:
1479 callback(returned, result)
1480
1481 def _listenHotkeys(self):
1482 """Setup the hotkeys based on the configuration."""
1483 if self._hotkeySetID is None and self._simulator is not None:
1484 self._pilotHotkeyIndex = None
1485 self._checklistHotkeyIndex = None
1486
1487 hotkeys = []
1488
1489 config = self.config
1490 if config.enableSounds and config.pilotControlsSounds:
1491 self._pilotHotkeyIndex = len(hotkeys)
1492 hotkeys.append(config.pilotHotkey)
1493
1494 if config.enableChecklists:
1495 self._checklistHotkeyIndex = len(hotkeys)
1496 hotkeys.append(config.checklistHotkey)
1497
1498 if hotkeys:
1499 self._hotkeySetID = \
1500 self._simulator.listenHotkeys(hotkeys, self._handleHotkeys)
1501
1502 def _clearHotkeys(self):
1503 """Clear the hotkeys."""
1504 if self._hotkeySetID is not None:
1505 self._hotkeySetID=None
1506 self._simulator.clearHotkeys()
1507
1508 def _handleHotkeys(self, id, hotkeys):
1509 """Handle the hotkeys."""
1510 if id==self._hotkeySetID:
1511 for index in hotkeys:
1512 if index==self._pilotHotkeyIndex:
1513 print "gui.GUI._handleHotkeys: pilot hotkey pressed"
1514 self._flight.pilotHotkeyPressed()
1515 elif index==self._checklistHotkeyIndex:
1516 print "gui.GUI._handleHotkeys: checklist hotkey pressed"
1517 self._flight.checklistHotkeyPressed()
1518 else:
1519 print "gui.GUI._handleHotkeys: unhandled hotkey index:", index
1520
1521 def _showManual(self, menuitem):
1522 """Show the user's manual."""
1523 webbrowser.open(url ="file://" +
1524 os.path.join(self._programDirectory, "doc", "manual",
1525 getLanguage(), "index.html"),
1526 new = 1)
1527
1528 def _showAbout(self, menuitem):
1529 """Show the about dialog."""
1530 dialog = self._getAboutDialog()
1531 dialog.show_all()
1532 dialog.run()
1533 dialog.hide()
1534
1535 def _getAboutDialog(self):
1536 """Get the about dialog.
1537
1538 If it does not exist yet, it will be created."""
1539 if self._aboutDialog is None:
1540 dialog = gtk.AboutDialog()
1541 dialog.set_transient_for(self._mainWindow)
1542 dialog.set_modal(True)
1543
1544 logoPath = os.path.join(self._programDirectory, "logo.png")
1545 logo = pixbuf_new_from_file(logoPath)
1546 dialog.set_logo(logo)
1547
1548 dialog.set_program_name(PROGRAM_NAME)
1549 dialog.set_version(const.VERSION)
1550 dialog.set_copyright("(c) 2012 by István Váradi")
1551 dialog.set_website("http://mlx.varadiistvan.hu")
1552 dialog.set_website_label(xstr("about_website"))
1553
1554 isHungarian = getLanguage()=="hu"
1555 authors = []
1556 for (familyName, firstName, role) in GUI._authors:
1557 author = "%s %s" % \
1558 (familyName if isHungarian else firstName,
1559 firstName if isHungarian else familyName)
1560 role = xstr("about_role_" + role)
1561 authors.append(author + " (" + role + ")")
1562 dialog.set_authors(authors)
1563
1564 dialog.set_license(xstr("about_license"))
1565
1566 if not pygobject:
1567 gtk.about_dialog_set_url_hook(self._showAboutURL, None)
1568
1569 self._aboutDialog = dialog
1570
1571 return self._aboutDialog
1572
1573 def _showAboutURL(self, dialog, link, user_data):
1574 """Show the about URL."""
1575 webbrowser.open(url = link, new = 1)
1576
1577 def _setTakeoffAntiIceOn(self, value):
1578 """Set the anti-ice on indicator."""
1579 self._wizard.takeoffAntiIceOn = value
1580
1581 def _setLandingAntiIceOn(self, value):
1582 """Set the anti-ice on indicator."""
1583 self._wizard.landingAntiIceOn = value
Note: See TracBrowser for help on using the repository browser.