source: src/mlx/gui/gui.py@ 391:0f2e90eae832

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

Added support for logging the state of the anti-ice system (re #159)

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