source: src/mlx/gui/gui.py@ 450:d009a75685e8

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

Merged with the main branch

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