source: src/mlx/gui/gui.py@ 606:acd6bab9eda5

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

The fault updating operations are put into the main queue (re #248)

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