source: src/mlx/gui/gui.py@ 257:0f7fb7fec4fc

Last change on this file since 257:0f7fb7fec4fc was 257:0f7fb7fec4fc, checked in by István Váradi <ivaradi@…>, 12 years ago

Added Nagy Dani as tester

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