source: src/mlx/gui/gui.py@ 555:e62f78ae65a0

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

The departure and arrival times are displayed on the finish page and if one of the differences is greater than 15 minutes, the PIREP cannot be saved or sent unless a delay code or a comment is provided (re #224)

File size: 56.6 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 @property
406 def hasDelayCode(self):
407 """Determine if there is at least one delay code selected."""
408 return self._flightInfo.hasDelayCode
409
410 def run(self):
411 """Run the GUI."""
412 if self.config.autoUpdate:
413 self._updater = Updater(self,
414 self._programDirectory,
415 self.config.updateURL,
416 self._mainWindow)
417 self._updater.start()
418
419 singleton.raiseCallback = self.raiseCallback
420 gtk.main()
421 singleton.raiseCallback = None
422
423 self._disconnect()
424
425 def connected(self, fsType, descriptor):
426 """Called when we have connected to the simulator."""
427 self._connected = True
428 self._logger.untimedMessage("MLX %s connected to the simulator %s" % \
429 (const.VERSION, descriptor))
430 fs.sendMessage(const.MESSAGETYPE_INFORMATION,
431 "Welcome to MAVA Logger X " + const.VERSION)
432 gobject.idle_add(self._handleConnected, fsType, descriptor)
433
434 def _handleConnected(self, fsType, descriptor):
435 """Called when the connection to the simulator has succeeded."""
436 self._statusbar.updateConnection(self._connecting, self._connected)
437 self.endBusy()
438 if not self._reconnecting:
439 self._wizard.connected(fsType, descriptor)
440 self._reconnecting = False
441 self._fsType = fsType
442 self._listenHotkeys()
443
444 def connectionFailed(self):
445 """Called when the connection failed."""
446 self._logger.untimedMessage("Connection to the simulator failed")
447 gobject.idle_add(self._connectionFailed)
448
449 def _connectionFailed(self):
450 """Called when the connection failed."""
451 self.endBusy()
452 self._statusbar.updateConnection(self._connecting, self._connected)
453
454 dialog = gtk.MessageDialog(parent = self._mainWindow,
455 type = MESSAGETYPE_ERROR,
456 message_format = xstr("conn_failed"))
457
458 dialog.set_title(WINDOW_TITLE_BASE)
459 dialog.format_secondary_markup(xstr("conn_failed_sec"))
460
461 dialog.add_button(xstr("button_cancel"), 0)
462 dialog.add_button(xstr("button_tryagain"), 1)
463 dialog.set_default_response(1)
464
465 result = dialog.run()
466 dialog.hide()
467 if result == 1:
468 self.beginBusy(xstr("connect_busy"))
469 self._simulator.reconnect()
470 else:
471 self.reset()
472
473 def disconnected(self):
474 """Called when we have disconnected from the simulator."""
475 self._connected = False
476 self._logger.untimedMessage("Disconnected from the simulator")
477
478 gobject.idle_add(self._disconnected)
479
480 def _disconnected(self):
481 """Called when we have disconnected from the simulator unexpectedly."""
482 self._statusbar.updateConnection(self._connecting, self._connected)
483
484 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
485 message_format = xstr("conn_broken"),
486 parent = self._mainWindow)
487 dialog.set_title(WINDOW_TITLE_BASE)
488 dialog.format_secondary_markup(xstr("conn_broken_sec"))
489
490 dialog.add_button(xstr("button_cancel"), 0)
491 dialog.add_button(xstr("button_reconnect"), 1)
492 dialog.set_default_response(1)
493
494 result = dialog.run()
495 dialog.hide()
496 if result == 1:
497 self.beginBusy(xstr("connect_busy"))
498 self._reconnecting = True
499 self._simulator.reconnect()
500 else:
501 self.reset()
502
503 def enableFlightInfo(self, aircraftType):
504 """Enable the flight info tab."""
505 self._flightInfo.enable(aircraftType)
506
507 def cancelFlight(self):
508 """Cancel the current file, if the user confirms it."""
509 dialog = gtk.MessageDialog(parent = self._mainWindow,
510 type = MESSAGETYPE_QUESTION,
511 message_format = xstr("cancelFlight_question"))
512
513 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
514 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
515
516 dialog.set_title(WINDOW_TITLE_BASE)
517 result = dialog.run()
518 dialog.hide()
519
520 if result==RESPONSETYPE_YES:
521 self.reset()
522
523 def reset(self):
524 """Reset the GUI."""
525 self._disconnect()
526
527 self._simulator = None
528
529 self._flightInfo.reset()
530 self._flightInfo.disable()
531 self.resetFlightStatus()
532
533 self._weightHelp.reset()
534 self._weightHelp.disable()
535 self._notebook.set_current_page(0)
536
537 self._logView.get_buffer().set_text("")
538
539 if self.loggedIn:
540 self._wizard.reloadFlights(self._handleReloadResult)
541 else:
542 self._wizard.reset(None)
543
544 def _handleReloadResult(self, returned, result):
545 """Handle the result of the reloading of the flights."""
546 self._wizard.reset(result if returned and result.loggedIn else None)
547
548 def _disconnect(self, closingMessage = None, duration = 3):
549 """Disconnect from the simulator if connected."""
550 self.stopMonitoring()
551 self._clearHotkeys()
552
553 if self._connected:
554 if closingMessage is None:
555 self._flight.simulator.disconnect()
556 else:
557 fs.sendMessage(const.MESSAGETYPE_ENVIRONMENT,
558 closingMessage, duration,
559 disconnect = True)
560 self._connected = False
561
562 self._connecting = False
563 self._reconnecting = False
564 self._statusbar.updateConnection(False, False)
565 self._weightHelp.disable()
566
567 return True
568
569 def insertFlightLogLine(self, index, timestampString, text, isFault):
570 """Insert the flight log line with the given data."""
571 gobject.idle_add(self._insertFlightLogLine, index,
572 formatFlightLogLine(timestampString, text),
573 isFault)
574
575 def _insertFlightLogLine(self, index, line, isFault):
576 """Perform the real insertion.
577
578 To be called from the event loop."""
579 buffer = self._logView.get_buffer()
580 lineIter = buffer.get_iter_at_line(index)
581 insertTextBuffer(buffer, lineIter, line, isFault = isFault)
582 self._logView.scroll_mark_onscreen(buffer.get_insert())
583
584 def removeFlightLogLine(self, index):
585 """Remove the flight log line with the given index."""
586 gobject.idle_add(self._removeFlightLogLine, index)
587
588 def _removeFlightLogLine(self, index):
589 """Perform the real removal."""
590 buffer = self._logView.get_buffer()
591 startIter = buffer.get_iter_at_line(index)
592 endIter = buffer.get_iter_at_line(index+1)
593 buffer.delete(startIter, endIter)
594 self._logView.scroll_mark_onscreen(buffer.get_insert())
595
596 def check(self, flight, aircraft, logger, oldState, state):
597 """Update the data."""
598 gobject.idle_add(self._monitorWindow.setData, state)
599 gobject.idle_add(self._statusbar.updateTime, state.timestamp)
600
601 def resetFlightStatus(self):
602 """Reset the status of the flight."""
603 self._statusbar.resetFlightStatus()
604 self._statusbar.updateTime()
605 self._statusIcon.resetFlightStatus()
606
607 def setStage(self, stage):
608 """Set the stage of the flight."""
609 gobject.idle_add(self._setStage, stage)
610
611 def _setStage(self, stage):
612 """Set the stage of the flight."""
613 self._statusbar.setStage(stage)
614 self._statusIcon.setStage(stage)
615 self._wizard.setStage(stage)
616 if stage==const.STAGE_END:
617 welcomeMessage = \
618 airports.getWelcomeMessage(self.bookedFlight.arrivalICAO)
619 self._disconnect(closingMessage =
620 "Flight plan closed. " + welcomeMessage,
621 duration = 5)
622
623 def setRating(self, rating):
624 """Set the rating of the flight."""
625 gobject.idle_add(self._setRating, rating)
626
627 def _setRating(self, rating):
628 """Set the rating of the flight."""
629 self._statusbar.setRating(rating)
630 self._statusIcon.setRating(rating)
631
632 def setNoGo(self, reason):
633 """Set the rating of the flight to No-Go with the given reason."""
634 gobject.idle_add(self._setNoGo, reason)
635
636 def _setNoGo(self, reason):
637 """Set the rating of the flight."""
638 self._statusbar.setNoGo(reason)
639 self._statusIcon.setNoGo(reason)
640
641 def _handleMainWindowState(self, window, event):
642 """Hande a change in the state of the window"""
643 iconified = gdk.WindowState.ICONIFIED if pygobject \
644 else gdk.WINDOW_STATE_ICONIFIED
645
646 if (event.changed_mask&WINDOW_STATE_WITHDRAWN)!=0:
647 if (event.new_window_state&WINDOW_STATE_WITHDRAWN)!=0:
648 self._statusIcon.mainWindowHidden()
649 else:
650 self._statusIcon.mainWindowShown()
651
652 if self.config.hideMinimizedWindow and not pygobject and \
653 (event.changed_mask&WINDOW_STATE_ICONIFIED)!=0 and \
654 (event.new_window_state&WINDOW_STATE_ICONIFIED)!=0:
655 self.hideMainWindow(savePosition = False)
656 elif (event.changed_mask&WINDOW_STATE_ICONIFIED)!=0 and \
657 (event.new_window_state&WINDOW_STATE_ICONIFIED)==0:
658 self._mainWindow.present()
659
660 def raiseCallback(self):
661 """Callback for the singleton handling code."""
662 gobject.idle_add(self.raiseMainWindow)
663
664 def raiseMainWindow(self):
665 """Show the main window if invisible, and raise it."""
666 if not self._mainWindow.get_visible():
667 self.showMainWindow()
668 self._mainWindow.present()
669
670 def deleteMainWindow(self, window, event):
671 """Handle the delete event for the main window."""
672 if self.config.quitOnClose:
673 self._quit()
674 else:
675 self.hideMainWindow()
676 return True
677
678 def hideMainWindow(self, savePosition = True):
679 """Hide the main window and save its position."""
680 if savePosition:
681 (self._mainWindowX, self._mainWindowY) = \
682 self._mainWindow.get_window().get_root_origin()
683 else:
684 self._mainWindowX = self._mainWindowY = None
685 self._mainWindow.hide()
686 return True
687
688 def showMainWindow(self):
689 """Show the main window at its former position."""
690 if self._mainWindowX is not None and self._mainWindowY is not None:
691 self._mainWindow.move(self._mainWindowX, self._mainWindowY)
692
693 if pygobject:
694 self._mainWindow.show()
695 else:
696 self._mainWindow.present()
697 self._mainWindow.deiconify()
698
699 def toggleMainWindow(self):
700 """Toggle the main window."""
701 if self._mainWindow.get_visible():
702 self.hideMainWindow()
703 else:
704 self.showMainWindow()
705
706 def hideMonitorWindow(self, savePosition = True):
707 """Hide the monitor window."""
708 if savePosition:
709 (self._monitorWindowX, self._monitorWindowY) = \
710 self._monitorWindow.get_window().get_root_origin()
711 else:
712 self._monitorWindowX = self._monitorWindowY = None
713 self._monitorWindow.hide()
714 self._statusIcon.monitorWindowHidden()
715 if self._showMonitorMenuItem.get_active():
716 self._selfToggling = True
717 self._showMonitorMenuItem.set_active(False)
718 return True
719
720 def showMonitorWindow(self):
721 """Show the monitor window."""
722 if self._monitorWindowX is not None and self._monitorWindowY is not None:
723 self._monitorWindow.move(self._monitorWindowX, self._monitorWindowY)
724 self._monitorWindow.show_all()
725 self._statusIcon.monitorWindowShown()
726 if not self._showMonitorMenuItem.get_active():
727 self._selfToggling = True
728 self._showMonitorMenuItem.set_active(True)
729
730 def _toggleMonitorWindow(self, menuItem):
731 if self._selfToggling:
732 self._selfToggling = False
733 elif self._monitorWindow.get_visible():
734 self.hideMonitorWindow()
735 else:
736 self.showMonitorWindow()
737
738 def restart(self):
739 """Quit and restart the application."""
740 self.toRestart = True
741 self._quit(force = True)
742
743 def flushStdIO(self):
744 """Flush any text to the standard error that could not be logged."""
745 if self._stdioText:
746 sys.__stderr__.write(self._stdioText)
747
748 def writeStdIO(self, text):
749 """Write the given text into standard I/O log."""
750 with self._stdioLock:
751 self._stdioText += text
752
753 gobject.idle_add(self._writeStdIO)
754
755 def beginBusy(self, message):
756 """Begin a period of background processing."""
757 self._wizard.set_sensitive(False)
758 self._weightHelp.set_sensitive(False)
759 self._mainWindow.get_window().set_cursor(self._busyCursor)
760 self._statusbar.updateBusyState(message)
761
762 def endBusy(self):
763 """End a period of background processing."""
764 self._mainWindow.get_window().set_cursor(None)
765 self._weightHelp.set_sensitive(True)
766 self._wizard.set_sensitive(True)
767 self._statusbar.updateBusyState(None)
768
769 def initializeWeightHelp(self):
770 """Initialize the weight help tab."""
771 self._weightHelp.reset()
772 self._weightHelp.enable()
773
774 def getFleetAsync(self, callback = None, force = None):
775 """Get the fleet asynchronously."""
776 gobject.idle_add(self.getFleet, callback, force)
777
778 def getFleet(self, callback = None, force = False):
779 """Get the fleet.
780
781 If force is False, and we already have a fleet retrieved,
782 that one will be used."""
783 if self._fleet is None or force:
784 self._fleetCallback = callback
785 self.beginBusy(xstr("fleet_busy"))
786 self.webHandler.getFleet(self._fleetResultCallback)
787 else:
788 callback(self._fleet)
789
790 def commentsChanged(self):
791 """Indicate that the comments have changed."""
792 self._wizard.commentsChanged()
793
794 def delayCodesChanged(self):
795 """Called when the delay codes have changed."""
796 self._wizard.delayCodesChanged()
797
798 def updateRTO(self, inLoop = False):
799 """Indicate that the RTO state should be updated."""
800 if inLoop:
801 self._wizard.updateRTO()
802 else:
803 gobject.idle_add(self.updateRTO, True)
804
805 def rtoToggled(self, indicated):
806 """Called when the user has toggled the RTO checkbox."""
807 self._flight.rtoToggled(indicated)
808
809 def _fleetResultCallback(self, returned, result):
810 """Called when the fleet has been queried."""
811 gobject.idle_add(self._handleFleetResult, returned, result)
812
813 def _handleFleetResult(self, returned, result):
814 """Handle the fleet result."""
815 self.endBusy()
816 if returned:
817 self._fleet = result.fleet
818 else:
819 self._fleet = None
820
821 dialog = gtk.MessageDialog(parent = self.mainWindow,
822 type = MESSAGETYPE_ERROR,
823 message_format = xstr("fleet_failed"))
824 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
825 dialog.set_title(WINDOW_TITLE_BASE)
826 dialog.run()
827 dialog.hide()
828
829 callback = self._fleetCallback
830 self._fleetCallback = None
831 if callback is not None:
832 callback(self._fleet)
833 self._fleetGateStatus.handleFleet(self._fleet)
834
835 def updatePlane(self, tailNumber, status,
836 gateNumber = None, callback = None):
837 """Update the status of the given plane."""
838 self.beginBusy(xstr("fleet_update_busy"))
839
840 self._updatePlaneCallback = callback
841
842 self._updatePlaneTailNumber = tailNumber
843 self._updatePlaneStatus = status
844 self._updatePlaneGateNumber = gateNumber
845
846 self.webHandler.updatePlane(self._updatePlaneResultCallback,
847 tailNumber, status, gateNumber)
848
849 def _updatePlaneResultCallback(self, returned, result):
850 """Called when the status of a plane has been updated."""
851 gobject.idle_add(self._handleUpdatePlaneResult, returned, result)
852
853 def _handleUpdatePlaneResult(self, returned, result):
854 """Handle the plane update result."""
855 self.endBusy()
856 if returned:
857 success = result.success
858 if success:
859 if self._fleet is not None:
860 self._fleet.updatePlane(self._updatePlaneTailNumber,
861 self._updatePlaneStatus,
862 self._updatePlaneGateNumber)
863 self._fleetGateStatus.handleFleet(self._fleet)
864 else:
865 dialog = gtk.MessageDialog(parent = self.mainWindow,
866 type = MESSAGETYPE_ERROR,
867 message_format = xstr("fleet_update_failed"))
868 dialog.add_button(xstr("button_ok"), RESPONSETYPE_ACCEPT)
869 dialog.set_title(WINDOW_TITLE_BASE)
870 dialog.run()
871 dialog.hide()
872
873 success = None
874
875 callback = self._updatePlaneCallback
876 self._updatePlaneCallback = None
877 if callback is not None:
878 callback(success)
879
880 def _writeStdIO(self):
881 """Perform the real writing."""
882 with self._stdioLock:
883 text = self._stdioText
884 self._stdioText = ""
885 if not text: return
886
887 lines = text.splitlines()
888 if text[-1]=="\n":
889 text = ""
890 else:
891 text = lines[-1]
892 lines = lines[:-1]
893
894 now = datetime.datetime.now()
895 timeStr = "%02d:%02d:%02d: " % (now.hour, now.minute, now.second)
896
897 for line in lines:
898 #print >> sys.__stdout__, line
899 if self._stdioStartingLine:
900 self._writeLog(timeStr, self._debugLogView)
901 self._writeLog(line + "\n", self._debugLogView)
902 self._stdioStartingLine = True
903
904 if text:
905 #print >> sys.__stdout__, text,
906 if self._stdioStartingLine:
907 self._writeLog(timeStr, self._debugLogView)
908 self._writeLog(text, self._debugLogView)
909 self._stdioStartingLine = False
910
911 def connectSimulator(self, aircraftType, simulatorType):
912 """Connect to the simulator for the first time."""
913 self._logger.reset()
914
915 self._flight = flight.Flight(self._logger, self)
916 self._flight.flareTimeFromFS = self.config.flareTimeFromFS
917 self._flight.aircraftType = aircraftType
918 self._flight.aircraft = acft.Aircraft.create(self._flight)
919 self._flight.aircraft._checkers.append(self)
920
921 if self._simulator is None:
922 self._simulator = fs.createSimulator(simulatorType, self)
923 fs.setupMessageSending(self.config, self._simulator)
924 self._setupTimeSync()
925
926 self._flight.simulator = self._simulator
927
928 self.beginBusy(xstr("connect_busy"))
929 self._statusbar.updateConnection(self._connecting, self._connected)
930
931 self._connecting = True
932 self._simulator.connect(self._flight.aircraft)
933
934 def startMonitoring(self):
935 """Start monitoring."""
936 if not self._monitoring:
937 self.simulator.startMonitoring()
938 self._monitoring = True
939
940 def stopMonitoring(self):
941 """Stop monitoring."""
942 if self._monitoring:
943 self.simulator.stopMonitoring()
944 self._monitoring = False
945
946 def cruiseLevelChanged(self):
947 """Called when the cruise level is changed in the flight wizard."""
948 if self._flight is not None:
949 return self._flight.cruiseLevelChanged()
950 else:
951 return False
952
953 def _buildMenuBar(self, accelGroup):
954 """Build the main menu bar."""
955 menuBar = gtk.MenuBar()
956
957 fileMenuItem = gtk.MenuItem(xstr("menu_file"))
958 fileMenu = gtk.Menu()
959 fileMenuItem.set_submenu(fileMenu)
960 menuBar.append(fileMenuItem)
961
962 loadPIREPMenuItem = gtk.ImageMenuItem(gtk.STOCK_OPEN)
963 loadPIREPMenuItem.set_use_stock(True)
964 loadPIREPMenuItem.set_label(xstr("menu_file_loadPIREP"))
965 loadPIREPMenuItem.add_accelerator("activate", accelGroup,
966 ord(xstr("menu_file_loadPIREP_key")),
967 CONTROL_MASK, ACCEL_VISIBLE)
968 loadPIREPMenuItem.connect("activate", self._loadPIREP)
969 fileMenu.append(loadPIREPMenuItem)
970
971 fileMenu.append(gtk.SeparatorMenuItem())
972
973 quitMenuItem = gtk.ImageMenuItem(gtk.STOCK_QUIT)
974 quitMenuItem.set_use_stock(True)
975 quitMenuItem.set_label(xstr("menu_file_quit"))
976 quitMenuItem.add_accelerator("activate", accelGroup,
977 ord(xstr("menu_file_quit_key")),
978 CONTROL_MASK, ACCEL_VISIBLE)
979 quitMenuItem.connect("activate", self._quit)
980 fileMenu.append(quitMenuItem)
981
982 toolsMenuItem = gtk.MenuItem(xstr("menu_tools"))
983 toolsMenu = gtk.Menu()
984 toolsMenuItem.set_submenu(toolsMenu)
985 menuBar.append(toolsMenuItem)
986
987 checklistMenuItem = gtk.ImageMenuItem(gtk.STOCK_APPLY)
988 checklistMenuItem.set_use_stock(True)
989 checklistMenuItem.set_label(xstr("menu_tools_chklst"))
990 checklistMenuItem.add_accelerator("activate", accelGroup,
991 ord(xstr("menu_tools_chklst_key")),
992 CONTROL_MASK, ACCEL_VISIBLE)
993 checklistMenuItem.connect("activate", self._editChecklist)
994 toolsMenu.append(checklistMenuItem)
995
996 approachCalloutsMenuItem = gtk.ImageMenuItem(gtk.STOCK_EDIT)
997 approachCalloutsMenuItem.set_use_stock(True)
998 approachCalloutsMenuItem.set_label(xstr("menu_tools_callouts"))
999 approachCalloutsMenuItem.add_accelerator("activate", accelGroup,
1000 ord(xstr("menu_tools_callouts_key")),
1001 CONTROL_MASK, ACCEL_VISIBLE)
1002 approachCalloutsMenuItem.connect("activate", self._editApproachCallouts)
1003 toolsMenu.append(approachCalloutsMenuItem)
1004
1005 prefsMenuItem = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
1006 prefsMenuItem.set_use_stock(True)
1007 prefsMenuItem.set_label(xstr("menu_tools_prefs"))
1008 prefsMenuItem.add_accelerator("activate", accelGroup,
1009 ord(xstr("menu_tools_prefs_key")),
1010 CONTROL_MASK, ACCEL_VISIBLE)
1011 prefsMenuItem.connect("activate", self._editPreferences)
1012 toolsMenu.append(prefsMenuItem)
1013
1014 toolsMenu.append(gtk.SeparatorMenuItem())
1015
1016 bugReportMenuItem = gtk.ImageMenuItem(gtk.STOCK_PASTE)
1017 bugReportMenuItem.set_use_stock(True)
1018 bugReportMenuItem.set_label(xstr("menu_tools_bugreport"))
1019 bugReportMenuItem.add_accelerator("activate", accelGroup,
1020 ord(xstr("menu_tools_bugreport_key")),
1021 CONTROL_MASK, ACCEL_VISIBLE)
1022 bugReportMenuItem.connect("activate", self._reportBug)
1023 toolsMenu.append(bugReportMenuItem)
1024
1025 viewMenuItem = gtk.MenuItem(xstr("menu_view"))
1026 viewMenu = gtk.Menu()
1027 viewMenuItem.set_submenu(viewMenu)
1028 menuBar.append(viewMenuItem)
1029
1030 self._showMonitorMenuItem = gtk.CheckMenuItem()
1031 self._showMonitorMenuItem.set_label(xstr("menu_view_monitor"))
1032 self._showMonitorMenuItem.set_use_underline(True)
1033 self._showMonitorMenuItem.set_active(False)
1034 self._showMonitorMenuItem.add_accelerator("activate", accelGroup,
1035 ord(xstr("menu_view_monitor_key")),
1036 CONTROL_MASK, ACCEL_VISIBLE)
1037 self._showMonitorMenuItem.connect("toggled", self._toggleMonitorWindow)
1038 viewMenu.append(self._showMonitorMenuItem)
1039
1040 showDebugMenuItem = gtk.CheckMenuItem()
1041 showDebugMenuItem.set_label(xstr("menu_view_debug"))
1042 showDebugMenuItem.set_use_underline(True)
1043 showDebugMenuItem.set_active(False)
1044 showDebugMenuItem.add_accelerator("activate", accelGroup,
1045 ord(xstr("menu_view_debug_key")),
1046 CONTROL_MASK, ACCEL_VISIBLE)
1047 showDebugMenuItem.connect("toggled", self._toggleDebugLog)
1048 viewMenu.append(showDebugMenuItem)
1049
1050 helpMenuItem = gtk.MenuItem(xstr("menu_help"))
1051 helpMenu = gtk.Menu()
1052 helpMenuItem.set_submenu(helpMenu)
1053 menuBar.append(helpMenuItem)
1054
1055 manualMenuItem = gtk.ImageMenuItem(gtk.STOCK_HELP)
1056 manualMenuItem.set_use_stock(True)
1057 manualMenuItem.set_label(xstr("menu_help_manual"))
1058 manualMenuItem.add_accelerator("activate", accelGroup,
1059 ord(xstr("menu_help_manual_key")),
1060 CONTROL_MASK, ACCEL_VISIBLE)
1061 manualMenuItem.connect("activate", self._showManual)
1062 helpMenu.append(manualMenuItem)
1063
1064 helpMenu.append(gtk.SeparatorMenuItem())
1065
1066 aboutMenuItem = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
1067 aboutMenuItem.set_use_stock(True)
1068 aboutMenuItem.set_label(xstr("menu_help_about"))
1069 aboutMenuItem.add_accelerator("activate", accelGroup,
1070 ord(xstr("menu_help_about_key")),
1071 CONTROL_MASK, ACCEL_VISIBLE)
1072 aboutMenuItem.connect("activate", self._showAbout)
1073 helpMenu.append(aboutMenuItem)
1074
1075 return menuBar
1076
1077 def _toggleDebugLog(self, menuItem):
1078 """Toggle the debug log."""
1079 if menuItem.get_active():
1080 label = gtk.Label(xstr("tab_debug_log"))
1081 label.set_use_underline(True)
1082 label.set_tooltip_text(xstr("tab_debug_log_tooltip"))
1083 self._debugLogPage = self._notebook.append_page(self._debugLogWidget, label)
1084 self._notebook.set_current_page(self._debugLogPage)
1085 else:
1086 self._notebook.remove_page(self._debugLogPage)
1087
1088 def _buildLogWidget(self):
1089 """Build the widget for the log."""
1090 alignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
1091
1092 alignment.set_padding(padding_top = 8, padding_bottom = 8,
1093 padding_left = 16, padding_right = 16)
1094
1095 logScroller = gtk.ScrolledWindow()
1096 # FIXME: these should be constants in common
1097 logScroller.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1098 else gtk.POLICY_AUTOMATIC,
1099 gtk.PolicyType.AUTOMATIC if pygobject
1100 else gtk.POLICY_AUTOMATIC)
1101 logScroller.set_shadow_type(gtk.ShadowType.IN if pygobject
1102 else gtk.SHADOW_IN)
1103 logView = gtk.TextView()
1104 logView.set_editable(False)
1105 logView.set_cursor_visible(False)
1106 logScroller.add(logView)
1107
1108 logBox = gtk.VBox()
1109 logBox.pack_start(logScroller, True, True, 0)
1110 logBox.set_size_request(-1, 200)
1111
1112 alignment.add(logBox)
1113
1114 return (alignment, logView)
1115
1116 def _writeLog(self, msg, logView, isFault = False):
1117 """Write the given message to the log."""
1118 buffer = logView.get_buffer()
1119 appendTextBuffer(buffer, msg, isFault = isFault)
1120 logView.scroll_mark_onscreen(buffer.get_insert())
1121
1122 def _quit(self, what = None, force = False):
1123 """Quit from the application."""
1124 if force:
1125 result=RESPONSETYPE_YES
1126 else:
1127 dialog = gtk.MessageDialog(parent = self._mainWindow,
1128 type = MESSAGETYPE_QUESTION,
1129 message_format = xstr("quit_question"))
1130
1131 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
1132 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
1133
1134 dialog.set_title(WINDOW_TITLE_BASE)
1135 result = dialog.run()
1136 dialog.hide()
1137
1138 if result==RESPONSETYPE_YES:
1139 self._statusIcon.destroy()
1140 return gtk.main_quit()
1141
1142 def _notebookPageSwitch(self, notebook, page, page_num):
1143 """Called when the current page of the notebook has changed."""
1144 if page_num==0:
1145 gobject.idle_add(self._wizard.grabDefault)
1146 else:
1147 self._mainWindow.set_default(None)
1148
1149 def isWizardActive(self):
1150 """Determine if the flight wizard is active."""
1151 return self._notebook.get_current_page()==0
1152
1153 def _editChecklist(self, menuItem):
1154 """Callback for editing the checklists."""
1155 self._checklistEditor.run()
1156
1157 def _editApproachCallouts(self, menuItem):
1158 """Callback for editing the approach callouts."""
1159 self._approachCalloutsEditor.run()
1160
1161 def _editPreferences(self, menuItem):
1162 """Callback for editing the preferences."""
1163 self._clearHotkeys()
1164 self._preferences.run(self.config)
1165 self._setupTimeSync()
1166 self._listenHotkeys()
1167
1168 def _reportBug(self, menuItem):
1169 """Callback for reporting a bug."""
1170 self._bugReportDialog.run()
1171
1172 def _setupTimeSync(self):
1173 """Enable or disable the simulator time synchronization based on the
1174 configuration."""
1175 simulator = self._simulator
1176 if simulator is not None:
1177 if self.config.syncFSTime:
1178 simulator.enableTimeSync()
1179 else:
1180 simulator.disableTimeSync()
1181
1182 def _loadPIREP(self, menuItem):
1183 """Load a PIREP for sending."""
1184 dialog = self._getLoadPirepDialog()
1185
1186 if self._lastLoadedPIREP:
1187 dialog.set_current_folder(os.path.dirname(self._lastLoadedPIREP))
1188 else:
1189 pirepDirectory = self.config.pirepDirectory
1190 if pirepDirectory is not None:
1191 dialog.set_current_folder(pirepDirectory)
1192
1193 result = dialog.run()
1194 dialog.hide()
1195
1196 if result==RESPONSETYPE_OK:
1197 self._lastLoadedPIREP = text2unicode(dialog.get_filename())
1198
1199 pirep = PIREP.load(self._lastLoadedPIREP)
1200 if pirep is None:
1201 dialog = gtk.MessageDialog(parent = self._mainWindow,
1202 type = MESSAGETYPE_ERROR,
1203 message_format = xstr("loadPIREP_failed"))
1204 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1205 dialog.set_title(WINDOW_TITLE_BASE)
1206 dialog.format_secondary_markup(xstr("loadPIREP_failed_sec"))
1207 dialog.run()
1208 dialog.hide()
1209 else:
1210 dialog = self._getSendLoadedDialog(pirep)
1211 dialog.show_all()
1212 while True:
1213 result = dialog.run()
1214
1215 if result==RESPONSETYPE_OK:
1216 self.sendPIREP(pirep)
1217 elif result==1:
1218 self._pirepViewer.setPIREP(pirep)
1219 self._pirepViewer.show_all()
1220 self._pirepViewer.run()
1221 self._pirepViewer.hide()
1222 else:
1223 break
1224
1225 dialog.hide()
1226
1227 def _getLoadPirepDialog(self):
1228 """Get the PIREP loading file chooser dialog.
1229
1230 If it is not created yet, it will be created."""
1231 if self._loadPIREPDialog is None:
1232 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
1233 xstr("loadPIREP_browser_title"),
1234 action = FILE_CHOOSER_ACTION_OPEN,
1235 buttons = (gtk.STOCK_CANCEL,
1236 RESPONSETYPE_CANCEL,
1237 gtk.STOCK_OK, RESPONSETYPE_OK),
1238 parent = self._mainWindow)
1239 dialog.set_modal(True)
1240
1241
1242 filter = gtk.FileFilter()
1243 filter.set_name(xstr("file_filter_pireps"))
1244 filter.add_pattern("*.pirep")
1245 dialog.add_filter(filter)
1246
1247 filter = gtk.FileFilter()
1248 filter.set_name(xstr("file_filter_all"))
1249 filter.add_pattern("*.*")
1250 dialog.add_filter(filter)
1251
1252 self._loadPIREPDialog = dialog
1253
1254 return self._loadPIREPDialog
1255
1256 def _getSendLoadedDialog(self, pirep):
1257 """Get a dialog displaying the main information of the flight from the
1258 PIREP and providing Cancel and Send buttons."""
1259 dialog = gtk.Dialog(title = WINDOW_TITLE_BASE + " - " +
1260 xstr("loadPIREP_send_title"),
1261 parent = self._mainWindow,
1262 flags = DIALOG_MODAL)
1263
1264 contentArea = dialog.get_content_area()
1265
1266 label = gtk.Label(xstr("loadPIREP_send_help"))
1267 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1268 xscale = 0.0, yscale = 0.0)
1269 alignment.set_padding(padding_top = 16, padding_bottom = 0,
1270 padding_left = 48, padding_right = 48)
1271 alignment.add(label)
1272 contentArea.pack_start(alignment, False, False, 8)
1273
1274 table = gtk.Table(5, 2)
1275 tableAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1276 xscale = 0.0, yscale = 0.0)
1277 tableAlignment.set_padding(padding_top = 0, padding_bottom = 32,
1278 padding_left = 48, padding_right = 48)
1279 table.set_row_spacings(4)
1280 table.set_col_spacings(16)
1281 tableAlignment.add(table)
1282 contentArea.pack_start(tableAlignment, True, True, 8)
1283
1284 bookedFlight = pirep.bookedFlight
1285
1286 label = gtk.Label("<b>" + xstr("loadPIREP_send_flightno") + "</b>")
1287 label.set_use_markup(True)
1288 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1289 xscale = 0.0, yscale = 0.0)
1290 labelAlignment.add(label)
1291 table.attach(labelAlignment, 0, 1, 0, 1)
1292
1293 label = gtk.Label(bookedFlight.callsign)
1294 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1295 xscale = 0.0, yscale = 0.0)
1296 labelAlignment.add(label)
1297 table.attach(labelAlignment, 1, 2, 0, 1)
1298
1299 label = gtk.Label("<b>" + xstr("loadPIREP_send_date") + "</b>")
1300 label.set_use_markup(True)
1301 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1302 xscale = 0.0, yscale = 0.0)
1303 labelAlignment.add(label)
1304 table.attach(labelAlignment, 0, 1, 1, 2)
1305
1306 label = gtk.Label(str(bookedFlight.departureTime.date()))
1307 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1308 xscale = 0.0, yscale = 0.0)
1309 labelAlignment.add(label)
1310 table.attach(labelAlignment, 1, 2, 1, 2)
1311
1312 label = gtk.Label("<b>" + xstr("loadPIREP_send_from") + "</b>")
1313 label.set_use_markup(True)
1314 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1315 xscale = 0.0, yscale = 0.0)
1316 labelAlignment.add(label)
1317 table.attach(labelAlignment, 0, 1, 2, 3)
1318
1319 label = gtk.Label(bookedFlight.departureICAO)
1320 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1321 xscale = 0.0, yscale = 0.0)
1322 labelAlignment.add(label)
1323 table.attach(labelAlignment, 1, 2, 2, 3)
1324
1325 label = gtk.Label("<b>" + xstr("loadPIREP_send_to") + "</b>")
1326 label.set_use_markup(True)
1327 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1328 xscale = 0.0, yscale = 0.0)
1329 labelAlignment.add(label)
1330 table.attach(labelAlignment, 0, 1, 3, 4)
1331
1332 label = gtk.Label(bookedFlight.arrivalICAO)
1333 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1334 xscale = 0.0, yscale = 0.0)
1335 labelAlignment.add(label)
1336 table.attach(labelAlignment, 1, 2, 3, 4)
1337
1338 label = gtk.Label("<b>" + xstr("loadPIREP_send_rating") + "</b>")
1339 label.set_use_markup(True)
1340 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1341 xscale = 0.0, yscale = 0.0)
1342 labelAlignment.add(label)
1343 table.attach(labelAlignment, 0, 1, 4, 5)
1344
1345 rating = pirep.rating
1346 label = gtk.Label()
1347 if rating<0:
1348 label.set_markup('<b><span foreground="red">NO GO</span></b>')
1349 else:
1350 label.set_text("%.1f %%" % (rating,))
1351
1352 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1353 xscale = 0.0, yscale = 0.0)
1354 labelAlignment.add(label)
1355 table.attach(labelAlignment, 1, 2, 4, 5)
1356
1357 dialog.add_button(xstr("button_cancel"), RESPONSETYPE_REJECT)
1358 dialog.add_button(xstr("viewPIREP"), 1)
1359 dialog.add_button(xstr("sendPIREP"), RESPONSETYPE_OK)
1360
1361 return dialog
1362
1363 def sendPIREP(self, pirep, callback = None):
1364 """Send the given PIREP."""
1365 self.beginBusy(xstr("sendPIREP_busy"))
1366 self._sendPIREPCallback = callback
1367 self.webHandler.sendPIREP(self._pirepSentCallback, pirep)
1368
1369 def _pirepSentCallback(self, returned, result):
1370 """Callback for the PIREP sending result."""
1371 gobject.idle_add(self._handlePIREPSent, returned, result)
1372
1373 def _handlePIREPSent(self, returned, result):
1374 """Callback for the PIREP sending result."""
1375 self.endBusy()
1376 secondaryMarkup = None
1377 type = MESSAGETYPE_ERROR
1378 if returned:
1379 if result.success:
1380 type = MESSAGETYPE_INFO
1381 messageFormat = xstr("sendPIREP_success")
1382 secondaryMarkup = xstr("sendPIREP_success_sec")
1383 elif result.alreadyFlown:
1384 messageFormat = xstr("sendPIREP_already")
1385 secondaryMarkup = xstr("sendPIREP_already_sec")
1386 elif result.notAvailable:
1387 messageFormat = xstr("sendPIREP_notavail")
1388 else:
1389 messageFormat = xstr("sendPIREP_unknown")
1390 secondaryMarkup = xstr("sendPIREP_unknown_sec")
1391 else:
1392 print "PIREP sending failed", result
1393 messageFormat = xstr("sendPIREP_failed")
1394 secondaryMarkup = xstr("sendPIREP_failed_sec")
1395
1396 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
1397 type = type, message_format = messageFormat)
1398 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1399 dialog.set_title(WINDOW_TITLE_BASE)
1400 if secondaryMarkup is not None:
1401 dialog.format_secondary_markup(secondaryMarkup)
1402
1403 dialog.run()
1404 dialog.hide()
1405
1406 callback = self._sendPIREPCallback
1407 self._sendPIREPCallback = None
1408 if callback is not None:
1409 callback(returned, result)
1410
1411 def sendBugReport(self, summary, description, email, callback = None):
1412 """Send the bug report with the given data."""
1413 description += "\n\n" + ("=" * 40)
1414 description += "\n\nThe contents of the log:\n\n"
1415
1416 for (timestampString, text) in self._logger.lines:
1417 description += unicode(formatFlightLogLine(timestampString, text))
1418
1419 description += "\n\n" + ("=" * 40)
1420 description += "\n\nThe contents of the debug log:\n\n"
1421
1422 buffer = self._debugLogView.get_buffer()
1423 description += buffer.get_text(buffer.get_start_iter(),
1424 buffer.get_end_iter(), True)
1425
1426 self.beginBusy(xstr("sendBugReport_busy"))
1427 self._sendBugReportCallback = callback
1428 self.webHandler.sendBugReport(self._bugReportSentCallback,
1429 summary, description, email)
1430
1431 def _bugReportSentCallback(self, returned, result):
1432 """Callback function for the bug report sending result."""
1433 gobject.idle_add(self._handleBugReportSent, returned, result)
1434
1435 def _handleBugReportSent(self, returned, result):
1436 """Callback for the bug report sending result."""
1437 self.endBusy()
1438 secondaryMarkup = None
1439 type = MESSAGETYPE_ERROR
1440 if returned:
1441 if result.success:
1442 type = MESSAGETYPE_INFO
1443 messageFormat = xstr("sendBugReport_success") % (result.ticketID,)
1444 secondaryMarkup = xstr("sendBugReport_success_sec")
1445 else:
1446 messageFormat = xstr("sendBugReport_error")
1447 secondaryMarkup = xstr("sendBugReport_siteerror_sec")
1448 else:
1449 messageFormat = xstr("sendBugReport_error")
1450 secondaryMarkup = xstr("sendBugReport_error_sec")
1451
1452 dialog = gtk.MessageDialog(parent = self._wizard.gui._bugReportDialog,
1453 type = type, message_format = messageFormat)
1454 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1455 dialog.set_title(WINDOW_TITLE_BASE)
1456 if secondaryMarkup is not None:
1457 dialog.format_secondary_markup(secondaryMarkup)
1458
1459 dialog.run()
1460 dialog.hide()
1461
1462 callback = self._sendBugReportCallback
1463 self._sendBugReportCallback = None
1464 if callback is not None:
1465 callback(returned, result)
1466
1467 def _listenHotkeys(self):
1468 """Setup the hotkeys based on the configuration."""
1469 if self._hotkeySetID is None and self._simulator is not None:
1470 self._pilotHotkeyIndex = None
1471 self._checklistHotkeyIndex = None
1472
1473 hotkeys = []
1474
1475 config = self.config
1476 if config.enableSounds and config.pilotControlsSounds:
1477 self._pilotHotkeyIndex = len(hotkeys)
1478 hotkeys.append(config.pilotHotkey)
1479
1480 if config.enableChecklists:
1481 self._checklistHotkeyIndex = len(hotkeys)
1482 hotkeys.append(config.checklistHotkey)
1483
1484 if hotkeys:
1485 self._hotkeySetID = \
1486 self._simulator.listenHotkeys(hotkeys, self._handleHotkeys)
1487
1488 def _clearHotkeys(self):
1489 """Clear the hotkeys."""
1490 if self._hotkeySetID is not None:
1491 self._hotkeySetID=None
1492 self._simulator.clearHotkeys()
1493
1494 def _handleHotkeys(self, id, hotkeys):
1495 """Handle the hotkeys."""
1496 if id==self._hotkeySetID:
1497 for index in hotkeys:
1498 if index==self._pilotHotkeyIndex:
1499 print "gui.GUI._handleHotkeys: pilot hotkey pressed"
1500 self._flight.pilotHotkeyPressed()
1501 elif index==self._checklistHotkeyIndex:
1502 print "gui.GUI._handleHotkeys: checklist hotkey pressed"
1503 self._flight.checklistHotkeyPressed()
1504 else:
1505 print "gui.GUI._handleHotkeys: unhandled hotkey index:", index
1506
1507 def _showManual(self, menuitem):
1508 """Show the user's manual."""
1509 webbrowser.open(url ="file://" +
1510 os.path.join(self._programDirectory, "doc", "manual",
1511 getLanguage(), "index.html"),
1512 new = 1)
1513
1514 def _showAbout(self, menuitem):
1515 """Show the about dialog."""
1516 dialog = self._getAboutDialog()
1517 dialog.show_all()
1518 dialog.run()
1519 dialog.hide()
1520
1521 def _getAboutDialog(self):
1522 """Get the about dialog.
1523
1524 If it does not exist yet, it will be created."""
1525 if self._aboutDialog is None:
1526 dialog = gtk.AboutDialog()
1527 dialog.set_transient_for(self._mainWindow)
1528 dialog.set_modal(True)
1529
1530 logoPath = os.path.join(self._programDirectory, "logo.png")
1531 logo = pixbuf_new_from_file(logoPath)
1532 dialog.set_logo(logo)
1533
1534 dialog.set_program_name(PROGRAM_NAME)
1535 dialog.set_version(const.VERSION)
1536 dialog.set_copyright("(c) 2012 by István Váradi")
1537 dialog.set_website("http://mlx.varadiistvan.hu")
1538 dialog.set_website_label(xstr("about_website"))
1539
1540 isHungarian = getLanguage()=="hu"
1541 authors = []
1542 for (familyName, firstName, role) in GUI._authors:
1543 author = "%s %s" % \
1544 (familyName if isHungarian else firstName,
1545 firstName if isHungarian else familyName)
1546 role = xstr("about_role_" + role)
1547 authors.append(author + " (" + role + ")")
1548 dialog.set_authors(authors)
1549
1550 dialog.set_license(xstr("about_license"))
1551
1552 if not pygobject:
1553 gtk.about_dialog_set_url_hook(self._showAboutURL, None)
1554
1555 self._aboutDialog = dialog
1556
1557 return self._aboutDialog
1558
1559 def _showAboutURL(self, dialog, link, user_data):
1560 """Show the about URL."""
1561 webbrowser.open(url = link, new = 1)
1562
1563 def _setTakeoffAntiIceOn(self, value):
1564 """Set the anti-ice on indicator."""
1565 self._wizard.takeoffAntiIceOn = value
1566
1567 def _setLandingAntiIceOn(self, value):
1568 """Set the anti-ice on indicator."""
1569 self._wizard.landingAntiIceOn = value
Note: See TracBrowser for help on using the repository browser.