source: src/mlx/gui/gui.py@ 685:6391018c120b

cef
Last change on this file since 685:6391018c120b was 682:08e73d58a9e4, checked in by István Váradi <ivaradi@…>, 9 years ago

CEF is now started from Selenium

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