source: src/mlx/gui/gui.py@ 249:d055e454a7ea

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

Implemented option to control whether to quit or hide when the window close button is pressed

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