source: src/mlx/gui/gui.py@ 483:a1b49fbab4f0

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

Implemented the dialog window (re #190)

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