source: src/mlx/gui/gui.py@ 509:77b730a1b092

xplane
Last change on this file since 509:77b730a1b092 was 501:2fd9b3270f6d, checked in by István Váradi <ivaradi@…>, 12 years ago

Added logic to select the simulator type

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