source: src/mlx/gui/gui.py@ 261:aad834a04851

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

Added airport names

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