source: src/mlx/gui/gui.py@ 384:97052bda0e22

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

Implemented support for entering derate values (#158)

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