source: src/mlx/gui/flight.py@ 698:19c20bbcd6a7

cef
Last change on this file since 698:19c20bbcd6a7 was 698:19c20bbcd6a7, checked in by István Váradi <ivaradi@…>, 9 years ago

The SimBrief browser is created only on the first activation, later on it is reused (re #279).

File size: 165.5 KB
Line 
1
2from mlx.gui.common import *
3import mlx.gui.cef as cef
4
5import mlx.const as const
6import mlx.fs as fs
7import mlx.acft as acft
8from mlx.flight import Flight
9from mlx.checks import PayloadChecker
10from mlx.gates import lhbpGates
11import mlx.util as util
12from mlx.pirep import PIREP
13from mlx.i18n import xstr
14from mlx.sound import startSound
15import mlx.web as web
16
17import datetime
18import time
19import os
20import tempfile
21
22#-----------------------------------------------------------------------------
23
24## @package mlx.gui.flight
25#
26# The flight "wizard".
27#
28# This module implements the main tab of the application, the flight
29# wizard. The wizard consists of \ref Page "pages", that come one after the
30# other. As some pages might be skipped, the pages dynamically store the index
31# of the previous page so that going back to it is simpler. The \ref
32# Page.activate "activate" function is called before a page is first shown
33# during a flight. This function should initialize the page's controls and fill
34# it with initial data. When a page is left for the first time, its \ref
35# Page.finalize "finalize" function is called. It should set those controls
36# insensitive, that will not be available if the user comes back to this page.
37#
38# Each page has a title at the top displayed in inverted colors and a big
39# font. There is a help text below it centered, that shortly describes what is
40# expected on the page. There can be two help texts: one shown when the page is
41# first displayed during a flight, another shown when the user goes back to the
42# page. The main content area is below this, also centered. Finally, there are
43# some buttons at the bottom on the right. As some buttons occur very
44# frequently, there are functions to add them (\ref Page.addCancelFlightButton
45# "addCancelFlightButton", \ref Page.addPreviousButton "addPreviousButton" and
46# \ref Page.addNextButton "addNextButton".
47#
48# The \ref Wizard class is the main class to collect the pages. It also stores
49# some data passed from one page to another and provides properties to access
50# data items set via the wizard pages.
51
52#-----------------------------------------------------------------------------
53
54comboModel = gtk.ListStore(gobject.TYPE_STRING)
55comboModel.append(("N/A",))
56comboModel.append(("VECTORS",))
57
58#-----------------------------------------------------------------------------
59
60class Page(gtk.Alignment):
61 """A page in the flight wizard."""
62 def __init__(self, wizard, title, help, completedHelp = None):
63 """Construct the page."""
64 super(Page, self).__init__(xalign = 0.0, yalign = 0.0,
65 xscale = 1.0, yscale = 1.0)
66 self.set_padding(padding_top = 4, padding_bottom = 4,
67 padding_left = 12, padding_right = 12)
68
69 frame = gtk.Frame()
70 self.add(frame)
71
72 self._vbox = gtk.VBox()
73 self._vbox.set_homogeneous(False)
74 frame.add(self._vbox)
75
76 eventBox = gtk.EventBox()
77
78 alignment = gtk.Alignment(xalign = 0.0, xscale = 0.0)
79
80 titleLabel = gtk.Label(title)
81 titleLabel.modify_font(pango.FontDescription("bold 24"))
82 alignment.set_padding(padding_top = 4, padding_bottom = 4,
83 padding_left = 6, padding_right = 0)
84
85 alignment.add(titleLabel)
86 eventBox.add(alignment)
87
88 self._vbox.pack_start(eventBox, False, False, 0)
89
90 self._titleEventBox = eventBox
91 self._titleLabel = titleLabel
92
93 mainBox = gtk.VBox()
94
95 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
96 xscale = 1.0, yscale = 1.0)
97 alignment.set_padding(padding_top = 16, padding_bottom = 16,
98 padding_left = 16, padding_right = 16)
99 alignment.add(mainBox)
100 self._vbox.pack_start(alignment, True, True, 0)
101
102 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
103 xscale = 0.0, yscale = 0.0)
104 alignment.set_padding(padding_top = 0, padding_bottom = 16,
105 padding_left = 0, padding_right = 0)
106
107 self._help = help
108 self._completedHelp = completedHelp
109
110 if self._completedHelp is None or \
111 len(help.splitlines())>=len(completedHelp.splitlines()):
112 longerHelp = help
113 else:
114 longerHelp = completedHelp
115
116 self._helpLabel = gtk.Label(longerHelp)
117 # FIXME: should be a constant in common
118 self._helpLabel.set_justify(gtk.Justification.CENTER if pygobject
119 else gtk.JUSTIFY_CENTER)
120 self._helpLabel.set_use_markup(True)
121 alignment.add(self._helpLabel)
122 mainBox.pack_start(alignment, False, False, 0)
123
124 self._mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
125 xscale = 1.0, yscale = 1.0)
126 mainBox.pack_start(self._mainAlignment, True, True, 0)
127
128 buttonAlignment = gtk.Alignment(xalign = 1.0, xscale=0.0, yscale = 0.0)
129 buttonAlignment.set_padding(padding_top = 4, padding_bottom = 10,
130 padding_left = 16, padding_right = 16)
131
132 self._buttonBox = gtk.HBox()
133 self._buttonBox.set_homogeneous(False)
134 self._defaultButton = None
135 buttonAlignment.add(self._buttonBox)
136
137 self._vbox.pack_start(buttonAlignment, False, False, 0)
138
139 self._wizard = wizard
140
141 self._cancelFlightButton = None
142
143 self._completed = False
144 self._fromPage = None
145
146 def setMainWidget(self, widget):
147 """Set the given widget as the main one."""
148 self._mainAlignment.add(widget)
149
150 def addButton(self, label, default = False, sensitive = True,
151 tooltip = None, clicked = None, padding = 4):
152 """Add a button with the given label.
153
154 Return the button object created."""
155 button = gtk.Button(label)
156 self._buttonBox.pack_start(button, False, False, padding)
157 button.set_use_underline(True)
158 if default:
159 button.set_can_default(True)
160 self._defaultButton = button
161 button.set_sensitive(sensitive)
162 if tooltip is not None:
163 button.set_tooltip_text(tooltip)
164 if clicked is not None:
165 button.connect("clicked", clicked)
166 return button
167
168 def addCancelFlightButton(self):
169 """Add the 'Cancel flight' button to the page."""
170 self._cancelFlightButton = \
171 self.addButton(xstr("button_cancelFlight"),
172 sensitive = True,
173 tooltip = xstr("button_cancelFlight_tooltip"),
174 clicked = self._cancelFlight,
175 padding = 16)
176 return self._cancelFlightButton
177
178 def addPreviousButton(self, sensitive = True, clicked = None):
179 """Add the 'Next' button to the page."""
180 return self.addButton(xstr("button_previous"),
181 sensitive = sensitive,
182 tooltip = xstr("button_previous_tooltip"),
183 clicked = clicked)
184
185 def addNextButton(self, default = True, sensitive = True,
186 clicked = None):
187 """Add the 'Next' button to the page."""
188 return self.addButton(xstr("button_next"),
189 default = default,
190 sensitive = sensitive,
191 tooltip = xstr("button_next_tooltip"),
192 clicked = clicked)
193
194 def setStyle(self):
195 """Set the styles of some of the items on the page."""
196 if pygobject:
197 context = self.get_style_context()
198 color = context.get_background_color(gtk.StateFlags.SELECTED)
199 self._titleEventBox.modify_bg(0, color.to_color())
200 color = context.get_color(gtk.StateFlags.SELECTED)
201 self._titleLabel.modify_fg(0, color.to_color())
202 else:
203 style = self.rc_get_style()
204 self._titleEventBox.modify_bg(0, style.bg[3])
205 self._titleLabel.modify_fg(0, style.fg[3])
206
207 def initialize(self):
208 """Initialize the page.
209
210 It sets up the primary help, and calls the activate() function."""
211 self._helpLabel.set_markup(self._help)
212 self._helpLabel.set_sensitive(True)
213 self.activate()
214
215 def activate(self):
216 """Called when this page becomes active.
217
218 This default implementation does nothing."""
219 pass
220
221 def setHelp(self, help):
222 """Set the help string."""
223 self._help = help
224 if not self._completed:
225 self._helpLabel.set_markup(self._help)
226 self._helpLabel.set_sensitive(True)
227
228 def complete(self):
229 """Called when the page is completed.
230
231 It greys out/changes the help text and then calls finalize()."""
232 self.finalize()
233 if self._completedHelp is None:
234 self._helpLabel.set_sensitive(False)
235 else:
236 self._helpLabel.set_markup(self._completedHelp)
237 self._completed = True
238
239 def finalize(self):
240 """Called when the page is finalized."""
241 pass
242
243 def grabDefault(self):
244 """If the page has a default button, make it the default one."""
245 if self._defaultButton is not None:
246 self._defaultButton.grab_default()
247
248 def reset(self):
249 """Reset the page if the wizard is reset."""
250 self._completed = False
251 self._fromPage = None
252 if self._cancelFlightButton is not None:
253 self._cancelFlightButton.set_sensitive(True)
254
255 def goBack(self):
256 """Go to the page we were invoked from."""
257 assert self._fromPage is not None
258
259 self._wizard.setCurrentPage(self._fromPage, finalize = False)
260
261 def flightEnded(self):
262 """Called when the flight has ended.
263
264 This default implementation disables the cancel flight button."""
265 if self._cancelFlightButton is not None:
266 self._cancelFlightButton.set_sensitive(False)
267
268 def _cancelFlight(self, button):
269 """Called when the Cancel flight button is clicked."""
270 self._wizard.gui.cancelFlight()
271
272#-----------------------------------------------------------------------------
273
274class LoginPage(Page):
275 """The login page."""
276 def __init__(self, wizard):
277 """Construct the login page."""
278 super(LoginPage, self).__init__(wizard, xstr("login"),
279 xstr("loginHelp"))
280
281 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
282 xscale = 0.0, yscale = 0.0)
283
284 table = gtk.Table(4, 2)
285 table.set_row_spacings(4)
286 table.set_col_spacings(32)
287 alignment.add(table)
288 self.setMainWidget(alignment)
289
290 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
291 xscale = 0.0, yscale = 0.0)
292 label = gtk.Label(xstr("label_pilotID"))
293 label.set_use_underline(True)
294 labelAlignment.add(label)
295 table.attach(labelAlignment, 0, 1, 0, 1)
296
297 self._pilotID = gtk.Entry()
298 self._pilotID.connect("changed", self._pilotIDChanged)
299 self._pilotID.set_tooltip_text(xstr("login_pilotID_tooltip"))
300 table.attach(self._pilotID, 1, 2, 0, 1)
301 label.set_mnemonic_widget(self._pilotID)
302
303 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
304 xscale = 0.0, yscale = 0.0)
305 label = gtk.Label(xstr("label_password"))
306 label.set_use_underline(True)
307 labelAlignment.add(label)
308 table.attach(labelAlignment, 0, 1, 1, 2)
309
310 self._password = gtk.Entry()
311 self._password.set_visibility(False)
312 self._password.connect("changed", self._setControls)
313 self._password.set_tooltip_text(xstr("login_password_tooltip"))
314 table.attach(self._password, 1, 2, 1, 2)
315 label.set_mnemonic_widget(self._password)
316
317 self._rememberButton = gtk.CheckButton(xstr("remember_password"))
318 self._rememberButton.set_use_underline(True)
319 self._rememberButton.set_tooltip_text(xstr("login_remember_tooltip"))
320 table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
321
322 self._entranceExam = gtk.CheckButton(xstr("login_entranceExam"))
323 self._entranceExam.set_use_underline(True)
324 self._entranceExam.set_tooltip_text(xstr("login_entranceExam_tooltip"))
325 self._entranceExam.connect("toggled", self._setControls)
326 table.attach(self._entranceExam, 1, 2, 3, 4, ypadding = 12)
327
328 self.addButton(xstr("button_offline"),
329 clicked = self._offlineClicked,
330 tooltip = xstr("button_offline_tooltip"))
331
332 self._loginButton = self.addButton(xstr("button_login"), default = True)
333 self._loginButton.connect("clicked", self._loginClicked)
334 self._loginButton.set_tooltip_text(xstr("login_button_tooltip"))
335
336
337 @property
338 def entranceExam(self):
339 """Get whether an entrance exam is being performed."""
340 return self._entranceExam.get_active() and \
341 self._pilotID.get_text()!=""
342
343 @property
344 def pilotID(self):
345 """Get the pilot ID, if given."""
346 return self._pilotID.get_text()
347
348 def activate(self):
349 """Activate the page."""
350 config = self._wizard.gui.config
351 self._pilotID.set_text(config.pilotID)
352 self._password.set_text(config.password)
353 self._rememberButton.set_active(config.rememberPassword)
354 self._setControls(None)
355
356 def _pilotIDChanged(self, entry):
357 """Called when the pilot ID has changed.
358
359 It sets the text to upper-case and calls _setControls to update other
360 stuff."""
361 entry.set_text(entry.get_text().upper())
362 self._setControls(entry)
363
364 def _setControls(self, entry = None):
365 """Set the sensitivity of the various controls.
366
367 The login button is sensitive only if both the pilot ID and the
368 password fields contain values.
369
370 The password field is sensitive only, if the entrance exam checkbox is
371 not selected.
372
373 The remember password checkbox is sensitive only, if the password field
374 contains text.
375
376 The entrance exam checkbox is sensitive only, if the pilot ID is not
377 empty."""
378 pilotID = self._pilotID.get_text()
379 password = self._password.get_text()
380 entranceExam = self._entranceExam.get_active()
381 self._password.set_sensitive(not entranceExam)
382 self._rememberButton.set_sensitive(password!="" and not entranceExam)
383 self._entranceExam.set_sensitive(pilotID!="")
384 self._loginButton.set_sensitive(pilotID!="" and
385 (password!="" or entranceExam))
386
387 def _offlineClicked(self, button):
388 """Called when the offline button was clicked."""
389 print "mlx.flight.LoginPage: offline flight selected"
390 self._wizard.nextPage()
391
392 def _loginClicked(self, button):
393 """Called when the login button was clicked."""
394 print "mlx.flight.LoginPage: logging in"
395 self._wizard.login(self._handleLoginResult,
396 self._pilotID.get_text(),
397 self._password.get_text(),
398 self.entranceExam)
399
400 def _handleLoginResult(self, returned, result):
401 """Handle the login result."""
402 self._loginButton.set_sensitive(True)
403 if returned and result.loggedIn:
404 config = self._wizard.gui.config
405
406 config.pilotID = self._pilotID.get_text()
407
408 rememberPassword = self._rememberButton.get_active()
409 config.password = result.password if rememberPassword else ""
410
411 config.rememberPassword = rememberPassword
412
413 config.save()
414 self._wizard.nextPage()
415
416#-----------------------------------------------------------------------------
417
418class FlightSelectionPage(Page):
419 """The page to select the flight."""
420 def __init__(self, wizard):
421 """Construct the flight selection page."""
422 help = xstr("flightsel_help")
423 completedHelp = xstr("flightsel_chelp")
424 super(FlightSelectionPage, self).__init__(wizard, xstr("flightsel_title"),
425 help, completedHelp = completedHelp)
426
427
428 self._listStore = gtk.ListStore(str, str, str, str)
429 self._flightList = gtk.TreeView(self._listStore)
430 column = gtk.TreeViewColumn(xstr("flightsel_no"), gtk.CellRendererText(),
431 text = 1)
432 column.set_expand(True)
433 self._flightList.append_column(column)
434 column = gtk.TreeViewColumn(xstr("flightsel_deptime"), gtk.CellRendererText(),
435 text = 0)
436 column.set_expand(True)
437 self._flightList.append_column(column)
438 column = gtk.TreeViewColumn(xstr("flightsel_from"), gtk.CellRendererText(),
439 text = 2)
440 column.set_expand(True)
441 self._flightList.append_column(column)
442 column = gtk.TreeViewColumn(xstr("flightsel_to"), gtk.CellRendererText(),
443 text = 3)
444 column.set_expand(True)
445 self._flightList.append_column(column)
446 self._flightList.connect("row-activated", self._rowActivated)
447 self._flightList.connect("button-press-event", self._listButtonPressed)
448
449 self._flightListPopupMenu = None
450
451 flightSelection = self._flightList.get_selection()
452 flightSelection.connect("changed", self._selectionChanged)
453
454 scrolledWindow = gtk.ScrolledWindow()
455 scrolledWindow.add(self._flightList)
456 scrolledWindow.set_size_request(400, -1)
457 # FIXME: these should be constants in common.py
458 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
459 else gtk.POLICY_AUTOMATIC,
460 gtk.PolicyType.AUTOMATIC if pygobject
461 else gtk.POLICY_AUTOMATIC)
462 scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
463 else gtk.SHADOW_IN)
464
465 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 0.0, yscale = 1.0)
466 alignment.add(scrolledWindow)
467
468 self.setMainWidget(alignment)
469
470 self._saveButton = self.addButton(xstr("flightsel_save"),
471 sensitive = False,
472 clicked = self._saveClicked,
473 tooltip = xstr("flightsel_save_tooltip"))
474 self._saveDialog = None
475
476 self._refreshButton = self.addButton(xstr("flightsel_refresh"),
477 sensitive = True,
478 clicked = self._refreshClicked,
479 tooltip = xstr("flightsel_refresh_tooltip"))
480
481 self._loadButton = self.addButton(xstr("flightsel_load"),
482 sensitive = True,
483 tooltip = xstr("flightsel_load_tooltip"))
484 self._loadButton.connect("clicked", self._loadButtonClicked)
485 self._loadDialog = None
486
487 self._button = self.addNextButton(sensitive = False,
488 clicked = self._forwardClicked)
489
490 self._flights = []
491
492 def activate(self):
493 """Fill the flight list."""
494 self._flightList.set_sensitive(True)
495 self._loadButton.set_sensitive(True)
496 self._refreshButton.set_sensitive(self._wizard.loggedIn)
497 self._buildFlights()
498
499 def finalize(self):
500 """Finalize the page."""
501 self._flightList.set_sensitive(False)
502 self._loadButton.set_sensitive(False)
503 self._refreshButton.set_sensitive(False)
504
505 def _buildFlights(self):
506 """Rebuild the flights from the login result."""
507 self._flights = []
508 self._listStore.clear()
509 if self._wizard.loggedIn:
510 for flight in self._wizard.loginResult.flights:
511 self._addFlight(flight)
512
513 def _addFlight(self, flight):
514 """Add the given file to the list of flights."""
515 self._flights.append(flight)
516 self._listStore.append([str(flight.departureTime),
517 flight.callsign,
518 flight.departureICAO,
519 flight.arrivalICAO])
520
521 def _saveClicked(self, button):
522 """Called when the Save flight button is clicked."""
523 self._saveSelected()
524
525 def _saveSelected(self):
526 """Save the selected flight."""
527 flight = self._getSelectedFlight()
528 date = flight.departureTime.date()
529 name = "%04d-%02d-%02d %s %s-%s.vaflight" % \
530 (date.year, date.month, date.day, flight.callsign,
531 flight.departureICAO, flight.arrivalICAO)
532
533 dialog = self._getSaveDialog()
534 dialog.set_current_name(name)
535 dialog.show_all()
536 response = dialog.run()
537 dialog.hide()
538
539 if response==RESPONSETYPE_OK:
540 fileName = text2unicode(dialog.get_filename())
541 print "Saving", fileName
542 try:
543 with open(fileName, "wt") as f:
544 flight.writeIntoFile(f)
545 except Exception, e:
546 print "Failed to save flight:", util.utf2unicode(str(e))
547 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
548 type = MESSAGETYPE_ERROR,
549 message_format =
550 xstr("flightsel_save_failed"))
551 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
552 dialog.set_title(WINDOW_TITLE_BASE)
553 secondary = xstr("flightsel_save_failed_sec")
554 dialog.format_secondary_markup(secondary)
555 dialog.run()
556 dialog.hide()
557
558 def _refreshClicked(self, button):
559 """Called when the refresh button is clicked."""
560 self._wizard.reloadFlights(self._refreshCallback)
561
562 def _refreshCallback(self, returned, result):
563 """Callback for the refresh."""
564 if returned and result.loggedIn:
565 self._buildFlights()
566
567 def _selectionChanged(self, selection):
568 """Called when the selection is changed."""
569 selected = selection.count_selected_rows()==1
570 self._saveButton.set_sensitive(selected)
571 self._button.set_sensitive(selected)
572
573 def _loadButtonClicked(self, loadButton):
574 """Called when the load a flight button is clicked."""
575 dialog = self._getLoadDialog()
576 dialog.show_all()
577 response = dialog.run()
578 dialog.hide()
579
580 if response==RESPONSETYPE_OK:
581 fileName = text2unicode(dialog.get_filename())
582 print "Loading", fileName
583 bookedFlight = web.BookedFlight()
584 try:
585 with open(fileName, "rt") as f:
586 bookedFlight.readFromFile(f)
587 self._addFlight(bookedFlight)
588 except Exception, e:
589 print "Failed to load flight:", util.utf2unicode(str(e))
590 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
591 type = MESSAGETYPE_ERROR,
592 message_format =
593 xstr("flightsel_load_failed"))
594 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
595 dialog.set_title(WINDOW_TITLE_BASE)
596 secondary = xstr("flightsel_load_failed_sec")
597 dialog.format_secondary_markup(secondary)
598 dialog.run()
599 dialog.hide()
600
601 def _forwardClicked(self, button):
602 """Called when the forward button was clicked."""
603 if self._completed:
604 self._wizard.jumpPage(self._nextDistance, finalize = False)
605 else:
606 self._flightSelected()
607
608 def _rowActivated(self, flightList, path, column):
609 """Called when a row is activated."""
610 if not self._completed:
611 self._flightSelected()
612
613 def _flightSelected(self):
614 """Called when a flight has been selected."""
615 flight = self._getSelectedFlight()
616 self._wizard._bookedFlight = flight
617 self._wizard.gui.enableFlightInfo(flight.aircraftType)
618
619 self._updateDepartureGate()
620
621 def _getSelectedFlight(self):
622 """Get the currently selected flight."""
623 selection = self._flightList.get_selection()
624 (listStore, iter) = selection.get_selected()
625 path = listStore.get_path(iter)
626 [index] = path.get_indices() if pygobject else path
627
628 return self._flights[index]
629
630 def _listButtonPressed(self, widget, event):
631 """Called when a mouse button is pressed on the flight list."""
632 if event.type!=EVENT_BUTTON_PRESS or event.button!=3:
633 return
634
635 (path, _, _, _) = self._flightList.get_path_at_pos(int(event.x),
636 int(event.y))
637 selection = self._flightList.get_selection()
638 selection.unselect_all()
639 selection.select_path(path)
640
641 menu = self._getListPopupMenu()
642 if pygobject:
643 menu.popup(None, None, None, None, event.button, event.time)
644 else:
645 menu.popup(None, None, None, event.button, event.time)
646
647 def _updateDepartureGate(self):
648 """Update the departure gate for the booked flight."""
649 flight = self._wizard._bookedFlight
650 if self._wizard.gui.config.onlineGateSystem and \
651 self._wizard.loggedIn and not self._wizard.entranceExam:
652 if flight.departureICAO=="LHBP":
653 self._wizard.getFleet(self._fleetRetrieved)
654 else:
655 self._wizard.updatePlane(self._planeUpdated,
656 flight.tailNumber,
657 const.PLANE_AWAY)
658 else:
659 self._nextDistance = 2
660 self._wizard.jumpPage(2)
661
662 def _fleetRetrieved(self, fleet):
663 """Called when the fleet has been retrieved."""
664 if fleet is None:
665 self._nextDistance = 2
666 self._wizard.jumpPage(2)
667 else:
668 plane = fleet[self._wizard._bookedFlight.tailNumber]
669 if plane is None:
670 self._nextDistance = 2
671 self._wizard.jumpPage(2)
672 elif plane.gateNumber is not None and \
673 not fleet.isGateConflicting(plane):
674 self._wizard._departureGate = plane.gateNumber
675 self._nextDistance = 2
676 self._wizard.jumpPage(2)
677 else:
678 self._nextDistance = 1
679 self._wizard.nextPage()
680
681 def _planeUpdated(self, success):
682 """Callback for the plane updating."""
683 self._nextDistance = 2
684 self._wizard.jumpPage(2)
685
686 def _getSaveDialog(self):
687 """Get the dialog to load a flight file."""
688 if self._saveDialog is not None:
689 return self._saveDialog
690
691 gui = self._wizard.gui
692 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
693 xstr("flightsel_save_title"),
694 action = FILE_CHOOSER_ACTION_SAVE,
695 buttons = (gtk.STOCK_CANCEL,
696 RESPONSETYPE_CANCEL,
697 gtk.STOCK_OK, RESPONSETYPE_OK),
698 parent = gui.mainWindow)
699 dialog.set_modal(True)
700 dialog.set_do_overwrite_confirmation(True)
701
702 filter = gtk.FileFilter()
703 filter.set_name(xstr("flightsel_filter_flights"))
704 filter.add_pattern("*.vaflight")
705 dialog.add_filter(filter)
706
707 filter = gtk.FileFilter()
708 filter.set_name(xstr("file_filter_all"))
709 filter.add_pattern("*.*")
710 dialog.add_filter(filter)
711
712 self._saveDialog = dialog
713
714 return dialog
715
716 def _getLoadDialog(self):
717 """Get the dialog to load a flight file."""
718 if self._loadDialog is not None:
719 return self._loadDialog
720
721 gui = self._wizard.gui
722 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
723 xstr("flightsel_load_title"),
724 action = FILE_CHOOSER_ACTION_OPEN,
725 buttons = (gtk.STOCK_CANCEL,
726 RESPONSETYPE_CANCEL,
727 gtk.STOCK_OK, RESPONSETYPE_OK),
728 parent = gui.mainWindow)
729 dialog.set_modal(True)
730
731 filter = gtk.FileFilter()
732 filter.set_name(xstr("flightsel_filter_flights"))
733 filter.add_pattern("*.vaflight")
734 dialog.add_filter(filter)
735
736 filter = gtk.FileFilter()
737 filter.set_name(xstr("file_filter_all"))
738 filter.add_pattern("*.*")
739 dialog.add_filter(filter)
740
741 self._loadDialog = dialog
742
743 return dialog
744
745 def _getListPopupMenu(self):
746 """Get the flight list popup menu."""
747 if self._flightListPopupMenu is None:
748 menu = gtk.Menu()
749
750 menuItem = gtk.MenuItem()
751 menuItem.set_label(xstr("flightsel_popup_select"))
752 menuItem.set_use_underline(True)
753 menuItem.connect("activate", self._popupSelect)
754 menuItem.show()
755
756 menu.append(menuItem)
757
758 menuItem = gtk.MenuItem()
759 menuItem.set_label(xstr("flightsel_popup_save"))
760 menuItem.set_use_underline(True)
761 menuItem.connect("activate", self._popupSave)
762 menuItem.show()
763
764 menu.append(menuItem)
765
766 self._flightListPopupMenu = menu
767
768 return self._flightListPopupMenu
769
770 def _popupSelect(self, menuItem):
771 """Called when the Select menu item is activated in the popup menu."""
772 if not self._completed:
773 self._flightSelected()
774
775 def _popupSave(self, menuItem):
776 """Called when the Save menu item is activated in the popup menu."""
777 if not self._completed:
778 self._saveSelected()
779
780#-----------------------------------------------------------------------------
781
782class GateSelectionPage(Page):
783 """Page to select a free gate at LHBP.
784 This page should be displayed only if we have fleet information!."""
785 def __init__(self, wizard):
786 """Construct the gate selection page."""
787 super(GateSelectionPage, self).__init__(wizard, xstr("gatesel_title"),
788 xstr("gatesel_help"))
789
790 self._listStore = gtk.ListStore(str)
791 self._gateList = gtk.TreeView(self._listStore)
792 column = gtk.TreeViewColumn(None, gtk.CellRendererText(),
793 text = 0)
794 column.set_expand(True)
795 self._gateList.append_column(column)
796 self._gateList.set_headers_visible(False)
797 self._gateList.connect("row-activated", self._rowActivated)
798
799 gateSelection = self._gateList.get_selection()
800 gateSelection.connect("changed", self._selectionChanged)
801
802 scrolledWindow = gtk.ScrolledWindow()
803 scrolledWindow.add(self._gateList)
804 scrolledWindow.set_size_request(50, -1)
805 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
806 else gtk.POLICY_AUTOMATIC,
807 gtk.PolicyType.AUTOMATIC if pygobject
808 else gtk.POLICY_AUTOMATIC)
809 scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
810 else gtk.SHADOW_IN)
811
812 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 0.0, yscale = 1.0)
813 alignment.add(scrolledWindow)
814
815 self.setMainWidget(alignment)
816
817 self.addCancelFlightButton()
818
819 self.addPreviousButton(clicked = self._backClicked)
820
821 self._button = self.addNextButton(sensitive = False,
822 clicked = self._forwardClicked)
823
824 def activate(self):
825 """Fill the gate list."""
826 self._listStore.clear()
827 self._gateList.set_sensitive(True)
828 occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
829 for gate in lhbpGates.gates:
830 if gate.isAvailable(lhbpGates, occupiedGateNumbers):
831 self._listStore.append([gate.number])
832
833 def finalize(self):
834 """Finalize the page."""
835 self._gateList.set_sensitive(False)
836
837 def _selectionChanged(self, selection):
838 """Called when the selection is changed."""
839 self._button.set_sensitive(selection.count_selected_rows()==1)
840
841 def _backClicked(self, button):
842 """Called when the Back button is pressed."""
843 self.goBack()
844
845 def _forwardClicked(self, button):
846 """Called when the forward button is clicked."""
847 if not self._completed:
848 self._gateSelected()
849 else:
850 self._wizard.nextPage()
851
852 def _rowActivated(self, flightList, path, column):
853 """Called when a row is activated."""
854 if not self._completed:
855 self._gateSelected()
856
857 def _gateSelected(self):
858 """Called when a gate has been selected."""
859 selection = self._gateList.get_selection()
860 (listStore, iter) = selection.get_selected()
861 (gateNumber,) = listStore.get(iter, 0)
862
863 self._wizard._departureGate = gateNumber
864
865 self._wizard.updatePlane(self._planeUpdated,
866 self._wizard._bookedFlight.tailNumber,
867 const.PLANE_HOME, gateNumber)
868
869 def _planeUpdated(self, success):
870 """Callback for the plane updating call."""
871 if success is None or success:
872 self._wizard.nextPage()
873 else:
874 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
875 type = MESSAGETYPE_ERROR,
876 message_format = xstr("gatesel_conflict"))
877 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
878 dialog.set_title(WINDOW_TITLE_BASE)
879 dialog.format_secondary_markup(xstr("gatesel_conflict_sec"))
880 dialog.run()
881 dialog.hide()
882
883 self._wizard.getFleet(self._fleetRetrieved)
884
885 def _fleetRetrieved(self, fleet):
886 """Called when the fleet has been retrieved."""
887 if fleet is None:
888 self._wizard.nextPage()
889 else:
890 self.activate()
891
892#-----------------------------------------------------------------------------
893
894class ConnectPage(Page):
895 """Page which displays the departure airport and gate (if at LHBP)."""
896 def __init__(self, wizard):
897 """Construct the connect page."""
898 help = "Load the aircraft below into the simulator and park it\n" \
899 "at the given airport, at the gate below, if present.\n\n" \
900 "Then press the Connect button to connect to the simulator."
901 completedHelp = "The basic data of your flight can be read below."
902 super(ConnectPage, self).__init__(wizard, xstr("connect_title"),
903 xstr("connect_help"),
904 completedHelp = xstr("connect_chelp"))
905
906 self._selectSimulator = os.name=="nt" or "FORCE_SELECT_SIM" in os.environ
907
908 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
909 xscale = 0.0, yscale = 0.0)
910
911 table = gtk.Table(7 if self._selectSimulator else 5, 2)
912 table.set_row_spacings(4)
913 table.set_col_spacings(16)
914 table.set_homogeneous(True)
915 alignment.add(table)
916 self.setMainWidget(alignment)
917
918 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
919 label = gtk.Label(xstr("connect_flightno"))
920 labelAlignment.add(label)
921 table.attach(labelAlignment, 0, 1, 0, 1)
922
923 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
924 self._flightNumber = gtk.Label()
925 self._flightNumber.set_width_chars(9)
926 self._flightNumber.set_alignment(0.0, 0.5)
927 labelAlignment.add(self._flightNumber)
928 table.attach(labelAlignment, 1, 2, 0, 1)
929
930 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
931 label = gtk.Label(xstr("connect_acft"))
932 labelAlignment.add(label)
933 table.attach(labelAlignment, 0, 1, 1, 2)
934
935 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
936 self._aircraft = gtk.Label()
937 self._aircraft.set_width_chars(25)
938 self._aircraft.set_alignment(0.0, 0.5)
939 labelAlignment.add(self._aircraft)
940 table.attach(labelAlignment, 1, 2, 1, 2)
941
942 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
943 label = gtk.Label(xstr("connect_tailno"))
944 labelAlignment.add(label)
945 table.attach(labelAlignment, 0, 1, 2, 3)
946
947 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
948 self._tailNumber = gtk.Label()
949 self._tailNumber.set_width_chars(10)
950 self._tailNumber.set_alignment(0.0, 0.5)
951 labelAlignment.add(self._tailNumber)
952 table.attach(labelAlignment, 1, 2, 2, 3)
953
954 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
955 label = gtk.Label(xstr("connect_airport"))
956 labelAlignment.add(label)
957 table.attach(labelAlignment, 0, 1, 3, 4)
958
959 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
960 self._departureICAO = gtk.Label()
961 self._departureICAO.set_width_chars(6)
962 self._departureICAO.set_alignment(0.0, 0.5)
963 labelAlignment.add(self._departureICAO)
964 table.attach(labelAlignment, 1, 2, 3, 4)
965
966 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
967 label = gtk.Label(xstr("connect_gate"))
968 labelAlignment.add(label)
969 table.attach(labelAlignment, 0, 1, 4, 5)
970
971 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
972 self._departureGate = gtk.Label()
973 self._departureGate.set_width_chars(5)
974 self._departureGate.set_alignment(0.0, 0.5)
975 labelAlignment.add(self._departureGate)
976 table.attach(labelAlignment, 1, 2, 4, 5)
977
978 if self._selectSimulator:
979 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0, yalign=0.5)
980 label = gtk.Label(xstr("connect_sim"))
981 labelAlignment.add(label)
982 table.attach(labelAlignment, 0, 1, 5, 7)
983
984 selectAlignment = gtk.Alignment(xalign=0.0, xscale=0.0, yalign=0.5)
985
986 selectBox = gtk.HBox()
987 if pygobject:
988 self._selectMSFS = \
989 gtk.RadioButton.new_with_mnemonic_from_widget(None,
990 xstr("connect_sim_msfs"))
991 else:
992 self._selectMSFS = gtk.RadioButton(None,
993 xstr("connect_sim_msfs"))
994
995 selectBox.pack_start(self._selectMSFS, False, False, 0);
996
997 if pygobject:
998 self._selectXPlane = \
999 gtk.RadioButton.new_with_mnemonic_from_widget(self._selectMSFS,
1000 xstr("connect_sim_xplane"))
1001 else:
1002 self._selectXPlane = gtk.RadioButton(self._selectMSFS,
1003 xstr("connect_sim_xplane"))
1004
1005 selectBox.pack_start(self._selectXPlane, False, False, 8);
1006
1007 selectAlignment.add(selectBox)
1008 table.attach(selectAlignment, 1, 2, 5, 7)
1009
1010
1011 self.addCancelFlightButton()
1012
1013 self.addPreviousButton(clicked = self._backClicked)
1014
1015 self._button = self.addButton(xstr("button_connect"), default = True,
1016 tooltip = xstr("button_connect_tooltip"))
1017 self._clickedID = self._button.connect("clicked", self._connectClicked)
1018
1019 def activate(self):
1020 """Setup the departure information."""
1021 self._button.set_label(xstr("button_connect"))
1022 self._button.set_use_underline(True)
1023 self._button.set_tooltip_text(xstr("button_connect_tooltip"))
1024 self._button.disconnect(self._clickedID)
1025 self._clickedID = self._button.connect("clicked", self._connectClicked)
1026
1027 bookedFlight = self._wizard._bookedFlight
1028
1029 self._flightNumber.set_markup("<b>" + bookedFlight.callsign + "</b>")
1030
1031 aircraftType = aircraftNames[bookedFlight.aircraftType]
1032 self._aircraft.set_markup("<b>" + aircraftType + "</b>")
1033
1034 self._tailNumber.set_markup("<b>" + bookedFlight.tailNumber + "</b>")
1035
1036 icao = bookedFlight.departureICAO
1037 self._departureICAO.set_markup("<b>" + icao + "</b>")
1038 gate = self._wizard._departureGate
1039 if gate!="-":
1040 gate = "<b>" + gate + "</b>"
1041 self._departureGate.set_markup(gate)
1042
1043 if self._selectSimulator:
1044 config = self._wizard.gui.config
1045 self._selectMSFS.set_active(config.defaultMSFS)
1046 self._selectXPlane.set_active(not config.defaultMSFS)
1047
1048 def finalize(self):
1049 """Finalize the page."""
1050 self._button.set_label(xstr("button_next"))
1051 self._button.set_use_underline(True)
1052 self._button.set_tooltip_text(xstr("button_next_tooltip"))
1053 self._button.disconnect(self._clickedID)
1054 self._clickedID = self._button.connect("clicked", self._forwardClicked)
1055
1056 def _backClicked(self, button):
1057 """Called when the Back button is pressed."""
1058 self.goBack()
1059
1060 def _connectClicked(self, button):
1061 """Called when the Connect button is pressed."""
1062 if self._selectSimulator:
1063 simulatorType = const.SIM_MSFS9 if self._selectMSFS.get_active() \
1064 else const.SIM_XPLANE10
1065 else:
1066 simulatorType = const.SIM_MSFS9 if os.name=="nt" \
1067 else const.SIM_XPLANE10
1068
1069 config = self._wizard.gui.config
1070 config.defaultMSFS = simulatorType == const.SIM_MSFS9
1071 config.save()
1072
1073 self._wizard._connectSimulator(simulatorType)
1074
1075 def _forwardClicked(self, button):
1076 """Called when the Forward button is pressed."""
1077 self._wizard.nextPage()
1078
1079#-----------------------------------------------------------------------------
1080
1081class PayloadPage(Page):
1082 """Page to allow setting up the payload."""
1083 def __init__(self, wizard):
1084 """Construct the page."""
1085 super(PayloadPage, self).__init__(wizard, xstr("payload_title"),
1086 xstr("payload_help"),
1087 completedHelp = xstr("payload_chelp"))
1088
1089 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1090 xscale = 0.0, yscale = 0.0)
1091
1092 table = gtk.Table(7, 3)
1093 table.set_row_spacings(4)
1094 table.set_col_spacings(16)
1095 table.set_homogeneous(False)
1096 alignment.add(table)
1097 self.setMainWidget(alignment)
1098
1099 label = gtk.Label(xstr("payload_crew"))
1100 label.set_use_underline(True)
1101 label.set_alignment(0.0, 0.5)
1102 table.attach(label, 0, 1, 0, 1)
1103
1104 self._numCrew = IntegerEntry(defaultValue = 0)
1105 self._numCrew.set_width_chars(6)
1106 self._numCrew.connect("integer-changed", self._weightChanged)
1107 self._numCrew.set_tooltip_text(xstr("payload_crew_tooltip"))
1108 table.attach(self._numCrew, 1, 2, 0, 1)
1109 label.set_mnemonic_widget(self._numCrew)
1110
1111 label = gtk.Label(xstr("payload_pax"))
1112 label.set_use_underline(True)
1113 label.set_alignment(0.0, 0.5)
1114 table.attach(label, 0, 1, 1, 2)
1115
1116 self._numPassengers = IntegerEntry(defaultValue = 0)
1117 self._numPassengers.set_width_chars(6)
1118 self._numPassengers.connect("integer-changed", self._weightChanged)
1119 self._numPassengers.set_tooltip_text(xstr("payload_pax_tooltip"))
1120 table.attach(self._numPassengers, 1, 2, 1, 2)
1121 label.set_mnemonic_widget(self._numPassengers)
1122
1123 label = gtk.Label(xstr("payload_bag"))
1124 label.set_use_underline(True)
1125 label.set_alignment(0.0, 0.5)
1126 table.attach(label, 0, 1, 2, 3)
1127
1128 self._bagWeight = IntegerEntry(defaultValue = 0)
1129 self._bagWeight.set_width_chars(6)
1130 self._bagWeight.connect("integer-changed", self._weightChanged)
1131 self._bagWeight.set_tooltip_text(xstr("payload_bag_tooltip"))
1132 table.attach(self._bagWeight, 1, 2, 2, 3)
1133 label.set_mnemonic_widget(self._bagWeight)
1134
1135 table.attach(gtk.Label("kg"), 2, 3, 2, 3)
1136
1137 label = gtk.Label(xstr("payload_cargo"))
1138 label.set_use_underline(True)
1139 label.set_alignment(0.0, 0.5)
1140 table.attach(label, 0, 1, 3, 4)
1141
1142 self._cargoWeight = IntegerEntry(defaultValue = 0)
1143 self._cargoWeight.set_width_chars(6)
1144 self._cargoWeight.connect("integer-changed", self._weightChanged)
1145 self._cargoWeight.set_tooltip_text(xstr("payload_cargo_tooltip"))
1146 table.attach(self._cargoWeight, 1, 2, 3, 4)
1147 label.set_mnemonic_widget(self._cargoWeight)
1148
1149 table.attach(gtk.Label("kg"), 2, 3, 3, 4)
1150
1151 label = gtk.Label(xstr("payload_mail"))
1152 label.set_use_underline(True)
1153 label.set_alignment(0.0, 0.5)
1154 table.attach(label, 0, 1, 4, 5)
1155
1156 self._mailWeight = IntegerEntry(defaultValue = 0)
1157 self._mailWeight.set_width_chars(6)
1158 self._mailWeight.connect("integer-changed", self._weightChanged)
1159 self._mailWeight.set_tooltip_text(xstr("payload_mail_tooltip"))
1160 table.attach(self._mailWeight, 1, 2, 4, 5)
1161 label.set_mnemonic_widget(self._mailWeight)
1162
1163 table.attach(gtk.Label("kg"), 2, 3, 4, 5)
1164
1165 label = gtk.Label("<b>" + xstr("payload_zfw") + "</b>")
1166 label.set_alignment(0.0, 0.5)
1167 label.set_use_markup(True)
1168 table.attach(label, 0, 1, 5, 6)
1169
1170 self._calculatedZFW = gtk.Label()
1171 self._calculatedZFW.set_width_chars(6)
1172 self._calculatedZFW.set_alignment(1.0, 0.5)
1173 table.attach(self._calculatedZFW, 1, 2, 5, 6)
1174
1175 table.attach(gtk.Label("kg"), 2, 3, 5, 6)
1176
1177 self._zfwButton = gtk.Button(xstr("payload_fszfw"))
1178 self._zfwButton.set_use_underline(True)
1179 self._zfwButton.connect("clicked", self._zfwRequested)
1180 self._zfwButton.set_tooltip_text(xstr("payload_fszfw_tooltip"))
1181 table.attach(self._zfwButton, 0, 1, 6, 7)
1182
1183 self._simulatorZFW = gtk.Label("-")
1184 self._simulatorZFW.set_width_chars(6)
1185 self._simulatorZFW.set_alignment(1.0, 0.5)
1186 table.attach(self._simulatorZFW, 1, 2, 6, 7)
1187 self._simulatorZFWValue = None
1188
1189 table.attach(gtk.Label("kg"), 2, 3, 6, 7)
1190
1191 self.addCancelFlightButton()
1192 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1193 self._button = self.addNextButton(clicked = self._forwardClicked)
1194
1195 @property
1196 def numCrew(self):
1197 """The number of the crew members on the flight."""
1198 return self._numCrew.get_int()
1199
1200 @property
1201 def numPassengers(self):
1202 """The number of the passengers on the flight."""
1203 return self._numPassengers.get_int()
1204
1205 @property
1206 def bagWeight(self):
1207 """Get the bag weight entered."""
1208 return self._bagWeight.get_int()
1209
1210 @property
1211 def cargoWeight(self):
1212 """Get the cargo weight entered."""
1213 return self._cargoWeight.get_int()
1214
1215 @property
1216 def mailWeight(self):
1217 """Get the bag weight entered."""
1218 return self._mailWeight.get_int()
1219
1220 def activate(self):
1221 """Setup the information."""
1222 bookedFlight = self._wizard._bookedFlight
1223
1224 self._numCrew.set_int(bookedFlight.numCrew)
1225 self._numCrew.set_sensitive(True)
1226 self._numPassengers.set_int(bookedFlight.numPassengers)
1227 self._numPassengers.set_sensitive(True)
1228
1229 self._bagWeight.set_int(bookedFlight.bagWeight)
1230 self._bagWeight.set_sensitive(True)
1231 self._cargoWeight.set_int(bookedFlight.cargoWeight)
1232 self._cargoWeight.set_sensitive(True)
1233 self._mailWeight.set_int(bookedFlight.mailWeight)
1234 self._mailWeight.set_sensitive(True)
1235
1236 self._simulatorZFW.set_text("-")
1237 self._simulatorZFWValue = None
1238 self._zfwButton.set_sensitive(True)
1239 self._updateCalculatedZFW()
1240
1241 def finalize(self):
1242 """Finalize the payload page."""
1243 self._numCrew.set_sensitive(False)
1244 self._numPassengers.set_sensitive(False)
1245 self._bagWeight.set_sensitive(False)
1246 self._cargoWeight.set_sensitive(False)
1247 self._mailWeight.set_sensitive(False)
1248 self._wizard.gui.initializeWeightHelp()
1249
1250 def calculateZFW(self):
1251 """Calculate the ZFW value."""
1252 zfw = self._wizard.gui._flight.aircraft.dow
1253 zfw += (self._numCrew.get_int() + self._numPassengers.get_int()) * 82
1254 zfw += self._bagWeight.get_int()
1255 zfw += self._cargoWeight.get_int()
1256 zfw += self._mailWeight.get_int()
1257 return zfw
1258
1259 def _updateCalculatedZFW(self):
1260 """Update the calculated ZFW"""
1261 zfw = self.calculateZFW()
1262
1263 markupBegin = "<b>"
1264 markupEnd = "</b>"
1265 if self._simulatorZFWValue is not None and \
1266 PayloadChecker.isZFWFaulty(self._simulatorZFWValue, zfw):
1267 markupBegin += '<span foreground="red">'
1268 markupEnd = "</span>" + markupEnd
1269 self._calculatedZFW.set_markup(markupBegin + str(zfw) + markupEnd)
1270
1271 def _weightChanged(self, entry, weight):
1272 """Called when one of the weight values or humanm counts has changed."""
1273 self._updateCalculatedZFW()
1274
1275 def _zfwRequested(self, button):
1276 """Called when the ZFW is requested from the simulator."""
1277 self._zfwButton.set_sensitive(False)
1278 self._backButton.set_sensitive(False)
1279 self._button.set_sensitive(False)
1280 gui = self._wizard.gui
1281 gui.beginBusy(xstr("payload_zfw_busy"))
1282 gui.simulator.requestZFW(self._handleZFW)
1283
1284 def _handleZFW(self, zfw):
1285 """Called when the ZFW value is retrieved."""
1286 gobject.idle_add(self._processZFW, zfw)
1287
1288 def _processZFW(self, zfw):
1289 """Process the given ZFW value received from the simulator."""
1290 self._wizard.gui.endBusy()
1291 self._zfwButton.set_sensitive(True)
1292 self._backButton.set_sensitive(True)
1293 self._button.set_sensitive(True)
1294 self._simulatorZFWValue = zfw
1295 self._simulatorZFW.set_text("%.0f" % (zfw,))
1296 self._updateCalculatedZFW()
1297
1298 def _forwardClicked(self, button):
1299 """Called when the forward button is clicked."""
1300 self._wizard.nextPage()
1301
1302 def _backClicked(self, button):
1303 """Called when the Back button is pressed."""
1304 self.goBack()
1305
1306#-----------------------------------------------------------------------------
1307
1308class TimePage(Page):
1309 """Page displaying the departure and arrival times and allows querying the
1310 current time from the flight simulator."""
1311 def __init__(self, wizard):
1312 super(TimePage, self).__init__(wizard, xstr("time_title"),
1313 xstr("time_help"),
1314 completedHelp = xstr("time_chelp"))
1315
1316 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1317 xscale = 0.0, yscale = 0.0)
1318
1319 table = gtk.Table(3, 2)
1320 table.set_row_spacings(4)
1321 table.set_col_spacings(16)
1322 table.set_homogeneous(False)
1323 alignment.add(table)
1324 self.setMainWidget(alignment)
1325
1326 label = gtk.Label(xstr("time_departure"))
1327 label.set_alignment(0.0, 0.5)
1328 table.attach(label, 0, 1, 0, 1)
1329
1330 self._departure = gtk.Label()
1331 self._departure.set_alignment(0.0, 0.5)
1332 table.attach(self._departure, 1, 2, 0, 1)
1333
1334 label = gtk.Label(xstr("time_arrival"))
1335 label.set_alignment(0.0, 0.5)
1336 table.attach(label, 0, 1, 1, 2)
1337
1338 self._arrival = gtk.Label()
1339 self._arrival.set_alignment(0.0, 0.5)
1340 table.attach(self._arrival, 1, 2, 1, 2)
1341
1342 self._timeButton = gtk.Button(xstr("time_fs"))
1343 self._timeButton.set_use_underline(True)
1344 self._timeButton.set_tooltip_text(xstr("time_fs_tooltip"))
1345 self._timeButton.connect("clicked", self._timeRequested)
1346 table.attach(self._timeButton, 0, 1, 2, 3)
1347
1348 self._simulatorTime = gtk.Label("-")
1349 self._simulatorTime.set_alignment(0.0, 0.5)
1350 table.attach(self._simulatorTime, 1, 2, 2, 3)
1351
1352 self.addCancelFlightButton()
1353
1354 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1355 self._button = self.addNextButton(clicked = self._forwardClicked)
1356
1357 def activate(self):
1358 """Activate the page."""
1359 self._timeButton.set_sensitive(True)
1360 bookedFlight = self._wizard._bookedFlight
1361 self._departure.set_text(str(bookedFlight.departureTime.time()))
1362 self._arrival.set_text(str(bookedFlight.arrivalTime.time()))
1363 self._simulatorTime.set_text("-")
1364
1365 def _timeRequested(self, button):
1366 """Request the time from the simulator."""
1367 self._timeButton.set_sensitive(False)
1368 self._backButton.set_sensitive(False)
1369 self._button.set_sensitive(False)
1370 self._wizard.gui.beginBusy(xstr("time_busy"))
1371 self._wizard.gui.simulator.requestTime(self._handleTime)
1372
1373 def _handleTime(self, timestamp):
1374 """Handle the result of a time retrieval."""
1375 gobject.idle_add(self._processTime, timestamp)
1376
1377 def _processTime(self, timestamp):
1378 """Process the given time."""
1379 self._wizard.gui.endBusy()
1380 self._timeButton.set_sensitive(True)
1381 self._backButton.set_sensitive(True)
1382 self._button.set_sensitive(True)
1383 tm = time.gmtime(timestamp)
1384 t = datetime.time(tm.tm_hour, tm.tm_min, tm.tm_sec)
1385 self._simulatorTime.set_text(str(t))
1386
1387 ts = tm.tm_hour * 3600 + tm.tm_min * 60 + tm.tm_sec
1388 dt = self._wizard._bookedFlight.departureTime.time()
1389 dts = dt.hour * 3600 + dt.minute * 60 + dt.second
1390 diff = dts-ts
1391
1392 markupBegin = ""
1393 markupEnd = ""
1394 if diff < 0:
1395 markupBegin = '<b><span foreground="red">'
1396 markupEnd = '</span></b>'
1397 elif diff < 3*60 or diff > 30*60:
1398 markupBegin = '<b><span foreground="orange">'
1399 markupEnd = '</span></b>'
1400
1401 self._departure.set_markup(markupBegin + str(dt) + markupEnd)
1402
1403 def _backClicked(self, button):
1404 """Called when the Back button is pressed."""
1405 self.goBack()
1406
1407 def _forwardClicked(self, button):
1408 """Called when the forward button is clicked."""
1409 if not self._completed:
1410 gui = self._wizard.gui
1411 gui.beginBusy(xstr("fuel_get_busy"))
1412
1413 gui.simulator.getFuel(self._handleFuel)
1414 else:
1415 self._wizard.nextPage()
1416
1417 def _handleFuel(self, fuelData):
1418 """Callback for the fuel query operation."""
1419 gobject.idle_add(self._processFuel, fuelData)
1420
1421 def _processFuel(self, fuelData):
1422 """Process the given fuel data."""
1423 self._wizard.gui.endBusy()
1424 self._wizard._fuelData = fuelData
1425 self._wizard.nextPage()
1426
1427#-----------------------------------------------------------------------------
1428
1429class RoutePage(Page):
1430 """The page containing the route and the flight level."""
1431 def __init__(self, wizard):
1432 """Construct the page."""
1433 super(RoutePage, self).__init__(wizard, xstr("route_title"),
1434 xstr("route_help"),
1435 completedHelp = xstr("route_chelp"))
1436
1437 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1438 xscale = 0.0, yscale = 0.0)
1439
1440 mainBox = gtk.VBox()
1441 alignment.add(mainBox)
1442 self.setMainWidget(alignment)
1443
1444 levelBox = gtk.HBox()
1445
1446 label = gtk.Label(xstr("route_level"))
1447 label.set_use_underline(True)
1448 levelBox.pack_start(label, True, True, 0)
1449
1450 self._cruiseLevel = gtk.SpinButton()
1451 self._cruiseLevel.set_increments(step = 10, page = 100)
1452 self._cruiseLevel.set_range(min = 0, max = 500)
1453 self._cruiseLevel.set_tooltip_text(xstr("route_level_tooltip"))
1454 self._cruiseLevel.set_numeric(True)
1455 self._cruiseLevel.connect("changed", self._cruiseLevelChanged)
1456 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
1457 label.set_mnemonic_widget(self._cruiseLevel)
1458
1459 levelBox.pack_start(self._cruiseLevel, False, False, 8)
1460
1461 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1462 xscale = 0.0, yscale = 0.0)
1463 alignment.add(levelBox)
1464
1465 mainBox.pack_start(alignment, False, False, 0)
1466
1467
1468 routeBox = gtk.VBox()
1469
1470 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1471 xscale = 0.0, yscale = 0.0)
1472 label = gtk.Label(xstr("route_route"))
1473 label.set_use_underline(True)
1474 alignment.add(label)
1475 routeBox.pack_start(alignment, True, True, 0)
1476
1477 routeWindow = gtk.ScrolledWindow()
1478 routeWindow.set_size_request(400, 80)
1479 routeWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
1480 else gtk.SHADOW_IN)
1481 routeWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1482 else gtk.POLICY_AUTOMATIC,
1483 gtk.PolicyType.AUTOMATIC if pygobject
1484 else gtk.POLICY_AUTOMATIC)
1485
1486 self._uppercasingRoute = False
1487
1488 self._route = gtk.TextView()
1489 self._route.set_tooltip_text(xstr("route_route_tooltip"))
1490 self._route.set_wrap_mode(WRAP_WORD)
1491 self._route.get_buffer().connect("changed", self._routeChanged)
1492 self._route.get_buffer().connect_after("insert-text", self._routeInserted)
1493 routeWindow.add(self._route)
1494
1495 label.set_mnemonic_widget(self._route)
1496 routeBox.pack_start(routeWindow, True, True, 0)
1497
1498 mainBox.pack_start(routeBox, True, True, 8)
1499
1500 alternateBox = gtk.HBox()
1501
1502 label = gtk.Label(xstr("route_altn"))
1503 label.set_use_underline(True)
1504 alternateBox.pack_start(label, True, True, 0)
1505
1506 self._alternate = gtk.Entry()
1507 self._alternate.set_width_chars(6)
1508 self._alternate.connect("changed", self._alternateChanged)
1509 self._alternate.set_tooltip_text(xstr("route_altn_tooltip"))
1510 label.set_mnemonic_widget(self._alternate)
1511
1512 alternateBox.pack_start(self._alternate, False, False, 8)
1513
1514 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1515 xscale = 0.0, yscale = 0.0)
1516 alignment.add(alternateBox)
1517
1518 mainBox.pack_start(alignment, False, False, 0)
1519
1520 self.addCancelFlightButton()
1521
1522 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1523 self._button = self.addNextButton(clicked = self._forwardClicked)
1524
1525 @property
1526 def filedCruiseLevel(self):
1527 """Get the filed cruise level."""
1528 return self._cruiseLevel.get_value_as_int()
1529
1530 @property
1531 def route(self):
1532 """Get the route."""
1533 return self._getRoute()
1534
1535 @property
1536 def alternate(self):
1537 """Get the ICAO code of the alternate airport."""
1538 return self._alternate.get_text()
1539
1540 def activate(self):
1541 """Setup the route from the booked flight."""
1542 self._cruiseLevel.set_value(0)
1543 self._cruiseLevel.set_text("")
1544 self._route.get_buffer().set_text(self._wizard._bookedFlight.route)
1545 self._alternate.set_text("")
1546 self._updateForwardButton()
1547
1548 def _getRoute(self):
1549 """Get the text of the route."""
1550 buffer = self._route.get_buffer()
1551 return buffer.get_text(buffer.get_start_iter(),
1552 buffer.get_end_iter(), True)
1553
1554 def _updateForwardButton(self):
1555 """Update the sensitivity of the forward button."""
1556 cruiseLevelText = self._cruiseLevel.get_text()
1557 cruiseLevel = int(cruiseLevelText) if cruiseLevelText else 0
1558 alternate = self._alternate.get_text()
1559 self._button.set_sensitive(cruiseLevel>=50 and self._getRoute()!="" and
1560 len(alternate)==4)
1561
1562 def _cruiseLevelChanged(self, *arg):
1563 """Called when the cruise level has changed."""
1564 self._updateForwardButton()
1565
1566 def _routeChanged(self, textBuffer):
1567 """Called when the route has changed."""
1568 if not self._uppercasingRoute:
1569 self._updateForwardButton()
1570
1571 def _routeInserted(self, textBuffer, iter, text, length):
1572 """Called when new characters are inserted into the route.
1573
1574 It uppercases all characters."""
1575 if not self._uppercasingRoute:
1576 self._uppercasingRoute = True
1577
1578 iter1 = iter.copy()
1579 iter1.backward_chars(length)
1580 textBuffer.delete(iter, iter1)
1581
1582 textBuffer.insert(iter, text.upper())
1583
1584 self._uppercasingRoute = False
1585
1586 def _alternateChanged(self, entry):
1587 """Called when the alternate airport has changed."""
1588 entry.set_text(entry.get_text().upper())
1589 self._updateForwardButton()
1590
1591 def _backClicked(self, button):
1592 """Called when the Back button is pressed."""
1593 self.goBack()
1594
1595 def _forwardClicked(self, button):
1596 """Called when the Forward button is clicked."""
1597 if self._wizard.gui.config.useSimBrief and \
1598 self._wizard.usingSimBrief is not False:
1599 self._wizard.nextPage()
1600 else:
1601 self._wizard.jumpPage(3)
1602
1603#-----------------------------------------------------------------------------
1604
1605class SimBriefSetupPage(Page):
1606 """Page for setting up some parameters for SimBrief."""
1607 monthNum2Name = [
1608 "JAN",
1609 "FEB",
1610 "MAR",
1611 "APR",
1612 "MAY",
1613 "JUN",
1614 "JUL",
1615 "AUG",
1616 "SEP",
1617 "OCT",
1618 "NOV",
1619 "DEC"
1620 ]
1621
1622 progress2Message = {
1623 cef.SIMBRIEF_PROGRESS_SEARCHING_BROWSER: "simbrief_progress_searching_browser",
1624 cef.SIMBRIEF_PROGRESS_LOADING_FORM: "simbrief_progress_loading_form",
1625 cef.SIMBRIEF_PROGRESS_FILLING_FORM: "simbrief_progress_filling_form",
1626 cef.SIMBRIEF_PROGRESS_WAITING_LOGIN: "simbrief_progress_waiting_login",
1627 cef.SIMBRIEF_PROGRESS_LOGGING_IN: "simbrief_progress_logging_in",
1628 cef.SIMBRIEF_PROGRESS_WAITING_RESULT: "simbrief_progress_waiting_result",
1629 cef.SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING: "simbrief_progress_retrieving_briefing"
1630 }
1631
1632 result2Message = {
1633 cef.SIMBRIEF_RESULT_ERROR_OTHER: "simbrief_result_error_other",
1634 cef.SIMBRIEF_RESULT_ERROR_NO_FORM: "simbrief_result_error_no_form",
1635 cef.SIMBRIEF_RESULT_ERROR_NO_POPUP: "simbrief_result_error_no_popup",
1636 cef.SIMBRIEF_RESULT_ERROR_LOGIN_FAILED: "simbrief_result_error_login_failed"
1637 }
1638
1639 @staticmethod
1640 def getHTMLFilePath():
1641 """Get the path of the HTML file to contain the generated flight
1642 plan."""
1643 if os.name=="nt":
1644 return os.path.join(tempfile.gettempdir(),
1645 "mlx_simbrief" +
1646 (".secondary" if secondaryInstallation else "") +
1647 ".html")
1648 else:
1649 import pwd
1650 return os.path.join(tempfile.gettempdir(),
1651 "mlx_simbrief." + pwd.getpwuid(os.getuid())[0] + "" +
1652 (".secondary" if secondaryInstallation else "") +
1653 ".html")
1654
1655 def __init__(self, wizard):
1656 """Construct the setup page."""
1657
1658 super(SimBriefSetupPage, self).__init__(wizard,
1659 xstr("simbrief_setup_title"),
1660 xstr("simbrief_setup_help"),
1661 xstr("simbrief_setup_chelp"))
1662
1663 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1664 xscale = 0.0, yscale = 0.0)
1665
1666 table = gtk.Table(7, 3)
1667 table.set_row_spacings(4)
1668 table.set_col_spacings(16)
1669 table.set_homogeneous(False)
1670 alignment.add(table)
1671 self.setMainWidget(alignment)
1672
1673 label = gtk.Label(xstr("simbrief_username"))
1674 label.set_use_underline(True)
1675 label.set_alignment(0.0, 0.5)
1676 table.attach(label, 0, 1, 0, 1)
1677
1678 self._userName = gtk.Entry()
1679 self._userName.set_width_chars(16)
1680 self._userName.connect("changed",
1681 lambda button: self._updateForwardButton())
1682 self._userName.set_tooltip_text(xstr("simbrief_username_tooltip"))
1683 table.attach(self._userName, 1, 2, 0, 1)
1684 label.set_mnemonic_widget(self._userName)
1685
1686 label = gtk.Label(xstr("simbrief_password"))
1687 label.set_use_underline(True)
1688 label.set_alignment(0.0, 0.5)
1689 table.attach(label, 0, 1, 1, 2)
1690
1691 self._password = gtk.Entry()
1692 self._password.set_visibility(False)
1693 self._password.connect("changed",
1694 lambda button: self._updateForwardButton())
1695 self._password.set_tooltip_text(xstr("simbrief_password_tooltip"))
1696 table.attach(self._password, 1, 2, 1, 2)
1697 label.set_mnemonic_widget(self._password)
1698
1699 self._rememberButton = gtk.CheckButton(xstr("simbrief_remember_password"))
1700 self._rememberButton.set_use_underline(True)
1701 self._rememberButton.set_tooltip_text(xstr("simbrief_remember_tooltip"))
1702 table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
1703
1704 self.addCancelFlightButton()
1705
1706 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1707 self._button = self.addNextButton(clicked = self._forwardClicked)
1708
1709 def activate(self):
1710 """Activate the SimBrief setup page"""
1711 config = self._wizard.gui.config
1712
1713 self._userName.set_text(config.simBriefUserName)
1714 self._password.set_text(config.simBriefPassword)
1715 self._rememberButton.set_active(config.rememberSimBriefPassword)
1716
1717 self._updateForwardButton()
1718
1719
1720 def _updateForwardButton(self):
1721 """Update the sensitivity of the forward button."""
1722 self._button.set_sensitive(len(self._userName.get_text())>0 and
1723 len(self._password.get_text())>0)
1724
1725 def _backClicked(self, button):
1726 """Called when the Back button is pressed."""
1727 self.goBack()
1728
1729 def _forwardClicked(self, button):
1730 if self._completed:
1731 self._wizard.nextPage()
1732 else:
1733 config = self._wizard.gui.config
1734
1735 config.simBriefUserName = self._userName.get_text()
1736
1737 rememberPassword = self._rememberButton.get_active()
1738 config.simBriefPassword = \
1739 self._password.get_text() if rememberPassword else ""
1740 config.rememberSimBriefPassword = rememberPassword
1741
1742 config.save()
1743
1744 plan = self._getPlan()
1745 print "plan:", plan
1746
1747 userName = self._userName.get_text()
1748 password = self._password.get_text()
1749
1750 self._wizard.gui.beginBusy("Calling SimBrief...")
1751
1752 cef.callSimBrief(plan,
1753 lambda count: (userName, password),
1754 self._simBriefProgressCallback,
1755 SimBriefSetupPage.getHTMLFilePath())
1756
1757 startSound(const.SOUND_NOTAM)
1758
1759 def _simBriefProgressCallback(self, progress, result, flightInfo):
1760 """Called by the SimBrief handling thread."""
1761 gobject.idle_add(self._simBriefProgress, progress, result, flightInfo)
1762
1763 def _simBriefProgress(self, progress, result, flightInfo):
1764 """The real SimBrief progress handler."""
1765 print "_simBriefProgress", progress, result, flightInfo
1766 if result==cef.SIMBRIEF_RESULT_NONE:
1767 message = SimBriefSetupPage.progress2Message.get(progress,
1768 "simbrief_progress_unknown")
1769 self._wizard.gui.updateBusyState(xstr(message))
1770 else:
1771 self._wizard.gui.endBusy()
1772
1773 if result==cef.SIMBRIEF_RESULT_OK:
1774 self._wizard.nextPage()
1775 else:
1776 message = SimBriefSetupPage.result2Message.get(result,
1777 "simbrief_result_unknown")
1778 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
1779 type = MESSAGETYPE_ERROR,
1780 message_format =
1781 xstr(message) + "\n"+
1782 xstr("simbrief_cancelled"))
1783
1784 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1785 dialog.set_title(WINDOW_TITLE_BASE)
1786 secondary = xstr("flightsel_save_failed_sec")
1787 dialog.format_secondary_markup(secondary)
1788 dialog.run()
1789 dialog.hide()
1790
1791 self._wizard.usingSimBrief = False
1792 self._wizard.jumpPage(2, fromPageShift = 1)
1793
1794 def _getPlan(self):
1795 """Get the flight plan data for SimBrief."""
1796 plan = {
1797 "airline": "MAH",
1798 "selcal": "XXXX",
1799 "fuelfactor": "P000",
1800 "contpct": "0.05",
1801 "resvrule": "45",
1802 "taxiout": "10",
1803 "taxiin": "10",
1804 "civalue": "AUTO"
1805 }
1806
1807 wizard = self._wizard
1808 gui = wizard.gui
1809
1810 loginResult = wizard.loginResult
1811 plan["cpt"] = loginResult.pilotName
1812 plan["pid"] = loginResult.pilotID
1813
1814 bookedFlight = wizard.bookedFlight
1815 plan["fltnum"] = wizard.bookedFlight.callsign[2:]
1816 plan["type"] = const.icaoCodes[bookedFlight.aircraftType]
1817 plan["orig"] = bookedFlight.departureICAO
1818 plan["dest"] = bookedFlight.arrivalICAO
1819 plan["reg"] = bookedFlight.tailNumber
1820 plan["fin"] = bookedFlight.tailNumber[3:]
1821 plan["pax"] = str(bookedFlight.numPassengers)
1822
1823 departureTime = bookedFlight.departureTime
1824 plan["date"] = "%d%s%d" % (departureTime.day,
1825 SimBriefSetupPage.monthNum2Name[departureTime.month-1],
1826 departureTime.year%100)
1827 plan["deph"] = str(departureTime.hour)
1828 plan["depm"] = str(departureTime.minute)
1829
1830 arrivalTime = bookedFlight.arrivalTime
1831 plan["steh"] = str(arrivalTime.hour)
1832 plan["stem"] = str(arrivalTime.minute)
1833
1834 plan["manualzfw"] = str(wizard.zfw)
1835 plan["cargo"] = str(wizard.bagWeight + wizard.cargoWeight + wizard.mailWeight)
1836
1837 plan["route"] = wizard.route
1838 plan["fl"] = str(wizard.filedCruiseAltitude)
1839 plan["altn"] = wizard.alternate
1840
1841 plan["addedfuel"] = "2.5" # FIXME: query
1842 plan["origrwy"] = "" # FIXME: query
1843 plan["destrwy"] = "" # FIXME: query
1844 plan["climb"] = "250/300/78" # FIXME: query
1845 plan["descent"] = "80/280/250" # FIXME: query
1846 plan["cruise"] = "LRC" # FIXME: query
1847
1848 return plan
1849
1850#-----------------------------------------------------------------------------
1851
1852class SimBriefingPage(Page):
1853 """Page to display the SimBrief HTML briefing."""
1854 def __init__(self, wizard):
1855 """Construct the setup page."""
1856
1857 super(SimBriefingPage, self).__init__(wizard,
1858 "SimBrief flight plan", "")
1859
1860 self._alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1861 xscale = 1.0, yscale = 1.0)
1862
1863 self._container = cef.getContainer()
1864 self._alignment.add(self._container)
1865
1866 self.setMainWidget(self._alignment)
1867
1868 self._browser = None
1869
1870 self.addCancelFlightButton()
1871
1872 self.addPreviousButton(clicked = self._backClicked)
1873
1874 self._button = self.addNextButton(clicked = self._forwardClicked)
1875 self._button.set_label(xstr("briefing_button"))
1876 self._button.set_has_tooltip(False)
1877 self._button.set_use_stock(False)
1878
1879 def activate(self):
1880 """Activate the SimBrief flight plan page"""
1881 if self._browser is None:
1882 url = "file://" + SimBriefSetupPage.getHTMLFilePath()
1883 self._browser = cef.startInContainer(self._container, url)
1884 else:
1885 self._browser.Reload()
1886
1887 def _backClicked(self, button):
1888 """Called when the Back button has been pressed."""
1889 self.goBack()
1890
1891 def _forwardClicked(self, button):
1892 """Called when the Forward button has been pressed."""
1893 if not self._completed:
1894 self._button.set_label(xstr("button_next"))
1895 self._button.set_tooltip_text(xstr("button_next_tooltip"))
1896 self._wizard.usingSimBrief = True
1897 self.complete()
1898
1899 self._wizard.nextPage()
1900
1901#-----------------------------------------------------------------------------
1902
1903class FuelTank(gtk.VBox):
1904 """Widget for the fuel tank."""
1905 def __init__(self, fuelTank, name, capacity, currentWeight):
1906 """Construct the widget for the tank with the given name."""
1907 super(FuelTank, self).__init__()
1908
1909 self._enabled = True
1910 self.fuelTank = fuelTank
1911 self.capacity = capacity
1912 self.currentWeight = currentWeight
1913 self.expectedWeight = currentWeight
1914
1915 label = gtk.Label("<b>" + name + "</b>")
1916 label.set_use_markup(True)
1917 label.set_use_underline(True)
1918 label.set_justify(JUSTIFY_CENTER)
1919 label.set_alignment(0.5, 1.0)
1920 self.pack_start(label, False, False, 4)
1921
1922 self._tankFigure = gtk.EventBox()
1923 self._tankFigure.set_size_request(38, -1)
1924 self._tankFigure.set_visible_window(False)
1925 self._tankFigure.set_tooltip_markup(xstr("fuel_tank_tooltip"))
1926
1927 if pygobject:
1928 self._tankFigure.connect("draw", self._drawTankFigure)
1929 else:
1930 self._tankFigure.connect("expose_event", self._drawTankFigure)
1931 self._tankFigure.connect("button_press_event", self._buttonPressed)
1932 self._tankFigure.connect("motion_notify_event", self._motionNotify)
1933 self._tankFigure.connect("scroll-event", self._scrolled)
1934
1935 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1936 xscale = 0.0, yscale = 1.0)
1937 alignment.add(self._tankFigure)
1938
1939 self.pack_start(alignment, True, True, 4)
1940
1941 self._expectedButton = gtk.SpinButton()
1942 self._expectedButton.set_numeric(True)
1943 self._expectedButton.set_range(0, self.capacity)
1944 self._expectedButton.set_increments(10, 100)
1945 self._expectedButton.set_value(currentWeight)
1946 self._expectedButton.set_alignment(1.0)
1947 self._expectedButton.set_width_chars(5)
1948 self._expectedButton.connect("value-changed", self._expectedChanged)
1949
1950 label.set_mnemonic_widget(self._expectedButton)
1951
1952 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1953 xscale = 0.0, yscale = 1.0)
1954 alignment.add(self._expectedButton)
1955 self.pack_start(alignment, False, False, 4)
1956
1957 def setCurrent(self, currentWeight):
1958 """Set the current weight."""
1959 self.currentWeight = currentWeight
1960 self._redraw()
1961
1962 def isCorrect(self):
1963 """Determine if the contents of the fuel tank are as expected"""
1964 return abs(self.expectedWeight - self.currentWeight)<=1
1965
1966 def disable(self):
1967 """Disable the fuel tank."""
1968 self._expectedButton.set_sensitive(False)
1969 self._enabled = False
1970
1971 def _redraw(self):
1972 """Redraw the tank figure."""
1973 self._tankFigure.queue_draw()
1974
1975 def _drawTankFigure(self, tankFigure, eventOrContext):
1976 """Draw the tank figure."""
1977 triangleSize = 5
1978
1979 context = eventOrContext if pygobject else tankFigure.window.cairo_create()
1980 (xOffset, yOffset) = (0, 0) if pygobject \
1981 else (tankFigure.allocation.x, tankFigure.allocation.y)
1982
1983 width = tankFigure.get_allocated_width() if pygobject \
1984 else tankFigure.allocation.width
1985 height = tankFigure.get_allocated_height() if pygobject \
1986 else tankFigure.allocation.height
1987
1988 rectangleX0 = triangleSize
1989 rectangleY0 = triangleSize
1990 rectangleX1 = width - 1 - triangleSize
1991 rectangleY1 = height - 1 - triangleSize
1992 rectangleLineWidth = 2.0
1993
1994 context.set_source_rgb(0.0, 0.0, 0.0)
1995 context.set_line_width(rectangleLineWidth)
1996 context.rectangle(xOffset + rectangleX0 + rectangleLineWidth/2,
1997 yOffset + rectangleY0 + rectangleLineWidth/2,
1998 rectangleX1 - rectangleX0 - rectangleLineWidth,
1999 rectangleY1 - rectangleY0 - rectangleLineWidth)
2000 context.stroke()
2001
2002 rectangleInnerLeft = rectangleX0 + rectangleLineWidth
2003 rectangleInnerRight = rectangleX1 - rectangleLineWidth
2004 self._rectangleInnerTop = rectangleInnerTop = rectangleY0 + rectangleLineWidth
2005 self._rectangleInnerBottom = rectangleInnerBottom = rectangleY1 - rectangleLineWidth
2006
2007 rectangleInnerWidth = rectangleInnerRight - rectangleInnerLeft
2008 rectangleInnerHeight = rectangleInnerBottom - rectangleInnerTop
2009
2010 context.set_source_rgb(1.0, 0.9, 0.6)
2011 currentHeight = self.currentWeight * rectangleInnerHeight / self.capacity
2012 currentX = rectangleInnerTop + rectangleInnerHeight - currentHeight
2013 context.rectangle(xOffset + rectangleInnerLeft,
2014 yOffset + rectangleInnerTop +
2015 rectangleInnerHeight - currentHeight,
2016 rectangleInnerWidth, currentHeight)
2017 context.fill()
2018
2019 expectedHeight = self.expectedWeight * rectangleInnerHeight / self.capacity
2020 expectedY = rectangleInnerTop + rectangleInnerHeight - expectedHeight
2021
2022 context.set_line_width(1.5)
2023 context.set_source_rgb(0.0, 0.85, 0.85)
2024 context.move_to(xOffset + rectangleX0, yOffset + expectedY)
2025 context.line_to(xOffset + rectangleX1, yOffset + expectedY)
2026 context.stroke()
2027
2028 context.set_line_width(0.0)
2029 context.move_to(xOffset + 0, yOffset + expectedY - triangleSize)
2030 context.line_to(xOffset + 0, yOffset + expectedY + triangleSize)
2031 context.line_to(xOffset + rectangleX0 + 1, yOffset + expectedY)
2032 context.line_to(xOffset + 0, yOffset + expectedY - triangleSize)
2033 context.fill()
2034
2035 context.set_line_width(0.0)
2036 context.move_to(xOffset + width, yOffset + expectedY - triangleSize)
2037 context.line_to(xOffset + width, yOffset + expectedY + triangleSize)
2038 context.line_to(xOffset + rectangleX1 - 1, yOffset + expectedY)
2039 context.line_to(xOffset + width, yOffset + expectedY - triangleSize)
2040 context.fill()
2041
2042 return True
2043
2044 def _setExpectedFromY(self, y):
2045 """Set the expected weight from the given Y-coordinate."""
2046 level = (self._rectangleInnerBottom - y) / \
2047 (self._rectangleInnerBottom - self._rectangleInnerTop)
2048 level = min(1.0, max(0.0, level))
2049 self._expectedButton.set_value(level * self.capacity)
2050
2051 def _buttonPressed(self, tankFigure, event):
2052 """Called when a button is pressed in the figure.
2053
2054 The expected level will be set there."""
2055 if self._enabled and event.button==1:
2056 self._setExpectedFromY(event.y)
2057
2058 def _motionNotify(self, tankFigure, event):
2059 """Called when the mouse pointer moves within the area of a tank figure."""
2060 if self._enabled and event.state==BUTTON1_MASK:
2061 self._setExpectedFromY(event.y)
2062
2063 def _scrolled(self, tankFigure, event):
2064 """Called when a scroll event is received."""
2065 if self._enabled:
2066 increment = 1 if event.state==CONTROL_MASK \
2067 else 100 if event.state==SHIFT_MASK \
2068 else 10 if event.state==0 else 0
2069 if increment!=0:
2070 if event.direction==SCROLL_DOWN:
2071 increment *= -1
2072 self._expectedButton.spin(SPIN_USER_DEFINED, increment)
2073
2074 def _expectedChanged(self, spinButton):
2075 """Called when the expected value has changed."""
2076 self.expectedWeight = spinButton.get_value_as_int()
2077 self._redraw()
2078
2079#-----------------------------------------------------------------------------
2080
2081class FuelPage(Page):
2082 """The page containing the fuel tank filling."""
2083 _pumpStep = 0.02
2084
2085 def __init__(self, wizard):
2086 """Construct the page."""
2087 super(FuelPage, self).__init__(wizard, xstr("fuel_title"),
2088 xstr("fuel_help_pre") +
2089 xstr("fuel_help_post"),
2090 completedHelp = xstr("fuel_chelp"))
2091
2092 self._fuelTanks = []
2093 self._fuelTable = None
2094 self._fuelAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2095 xscale = 0.0, yscale = 1.0)
2096 self.setMainWidget(self._fuelAlignment)
2097
2098 tankData = [(tank, 2500, 3900) for tank in acft.mostFuelTanks]
2099 self._setupTanks(tankData)
2100
2101 self.addCancelFlightButton()
2102
2103 self._backButton = self.addPreviousButton(clicked = self._backClicked)
2104 self._button = self.addNextButton(clicked = self._forwardClicked)
2105
2106 self._pumpIndex = 0
2107
2108 def activate(self):
2109 """Activate the page."""
2110 self._setupTanks(self._wizard._fuelData)
2111
2112 aircraft = self._wizard.gui.flight.aircraft
2113 minLandingFuel = aircraft.minLandingFuel
2114 recommendedLandingFuel = aircraft.recommendedLandingFuel
2115
2116 middleHelp = "" if minLandingFuel is None else \
2117 (xstr("fuel_help_min") % (minLandingFuel,)) \
2118 if recommendedLandingFuel is None else \
2119 (xstr("fuel_help_min_rec") % (minLandingFuel,
2120 recommendedLandingFuel))
2121 self.setHelp(xstr("fuel_help_pre") + middleHelp + xstr("fuel_help_post"))
2122
2123 def finalize(self):
2124 """Finalize the page."""
2125 for fuelTank in self._fuelTanks:
2126 fuelTank.disable()
2127
2128 def _backClicked(self, button):
2129 """Called when the Back button is pressed."""
2130 self.goBack()
2131
2132 def _forwardClicked(self, button):
2133 """Called when the forward button is clicked."""
2134 if not self._completed:
2135 self._pumpIndex = 0
2136 self._wizard.gui.beginBusy(xstr("fuel_pump_busy"))
2137 self._pump()
2138 elif self._wizard.usingSimBrief:
2139 self._wizard.jumpPage(3)
2140 else:
2141 self._wizard.nextPage()
2142
2143 def _setupTanks(self, tankData):
2144 """Setup the tanks for the given data."""
2145 numTanks = len(tankData)
2146 if self._fuelTable is not None:
2147 self._fuelAlignment.remove(self._fuelTable)
2148
2149 self._fuelTanks = []
2150 self._fuelTable = gtk.Table(numTanks, 1)
2151 self._fuelTable.set_col_spacings(16)
2152 index = 0
2153 for (tank, current, capacity) in tankData:
2154 fuelTank = FuelTank(tank,
2155 xstr("fuel_tank_" +
2156 const.fuelTank2string(tank)),
2157 capacity, current)
2158 self._fuelTable.attach(fuelTank, index, index+1, 0, 1)
2159 self._fuelTanks.append(fuelTank)
2160 index += 1
2161
2162 self._fuelAlignment.add(self._fuelTable)
2163 self.show_all()
2164
2165 def _pump(self):
2166 """Perform one step of pumping.
2167
2168 It is checked, if the current tank's contents are of the right
2169 quantity. If not, it is filled one step further to the desired
2170 contents. Otherwise the next tank is started. If all tanks are are
2171 filled, the next page is selected."""
2172 numTanks = len(self._fuelTanks)
2173
2174 fuelTank = None
2175 while self._pumpIndex < numTanks:
2176 fuelTank = self._fuelTanks[self._pumpIndex]
2177 if fuelTank.isCorrect():
2178 self._pumpIndex += 1
2179 fuelTank = None
2180 else:
2181 break
2182
2183 if fuelTank is None:
2184 self._wizard.gui.endBusy()
2185 if self._wizard.usingSimBrief:
2186 self._wizard.gui.startMonitoring()
2187 self._wizard.jumpPage(3)
2188 else:
2189 bookedFlight = self._wizard._bookedFlight
2190 self._wizard.gui.beginBusy(xstr("route_down_notams"))
2191 self._wizard.gui.webHandler.getNOTAMs(self._notamsCallback,
2192 bookedFlight.departureICAO,
2193 bookedFlight.arrivalICAO)
2194 startSound(const.SOUND_NOTAM)
2195 else:
2196 currentLevel = fuelTank.currentWeight / fuelTank.capacity
2197 expectedLevel = fuelTank.expectedWeight / fuelTank.capacity
2198 if currentLevel<expectedLevel:
2199 currentLevel += FuelPage._pumpStep
2200 if currentLevel>expectedLevel: currentLevel = expectedLevel
2201 else:
2202 currentLevel -= FuelPage._pumpStep
2203 if currentLevel<expectedLevel: currentLevel = expectedLevel
2204 fuelTank.setCurrent(currentLevel * fuelTank.capacity)
2205 self._wizard.gui.simulator.setFuelLevel([(fuelTank.fuelTank,
2206 currentLevel)])
2207 gobject.timeout_add(50, self._pump)
2208
2209 def _notamsCallback(self, returned, result):
2210 """Callback for the NOTAMs."""
2211 gobject.idle_add(self._handleNOTAMs, returned, result)
2212
2213 def _handleNOTAMs(self, returned, result):
2214 """Handle the NOTAMs."""
2215 if returned:
2216 self._wizard._departureNOTAMs = result.departureNOTAMs
2217 self._wizard._arrivalNOTAMs = result.arrivalNOTAMs
2218 else:
2219 self._wizard._departureNOTAMs = None
2220 self._wizard._arrivalNOTAMs = None
2221
2222 bookedFlight = self._wizard._bookedFlight
2223 self._wizard.gui.beginBusy(xstr("route_down_metars"))
2224 self._wizard.gui.webHandler.getMETARs(self._metarsCallback,
2225 [bookedFlight.departureICAO,
2226 bookedFlight.arrivalICAO])
2227
2228 def _metarsCallback(self, returned, result):
2229 """Callback for the METARs."""
2230 gobject.idle_add(self._handleMETARs, returned, result)
2231
2232 def _handleMETARs(self, returned, result):
2233 """Handle the METARs."""
2234 self._wizard._departureMETAR = None
2235 self._wizard._arrivalMETAR = None
2236 bookedFlight = self._wizard._bookedFlight
2237 if returned:
2238 if bookedFlight.departureICAO in result.metars:
2239 self._wizard._departureMETAR = result.metars[bookedFlight.departureICAO]
2240 if bookedFlight.arrivalICAO in result.metars:
2241 self._wizard._arrivalMETAR = result.metars[bookedFlight.arrivalICAO]
2242
2243 self._wizard.gui.endBusy()
2244 self._backButton.set_sensitive(True)
2245 self._button.set_sensitive(True)
2246 self._wizard.nextPage()
2247
2248#-----------------------------------------------------------------------------
2249
2250class BriefingPage(Page):
2251 """Page for the briefing."""
2252 def __init__(self, wizard, departure):
2253 """Construct the briefing page."""
2254 self._departure = departure
2255
2256 title = xstr("briefing_title") % (1 if departure else 2,
2257 xstr("briefing_departure")
2258 if departure
2259 else xstr("briefing_arrival"))
2260 super(BriefingPage, self).__init__(wizard, title, xstr("briefing_help"),
2261 completedHelp = xstr("briefing_chelp"))
2262
2263 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2264 xscale = 1.0, yscale = 1.0)
2265
2266 mainBox = gtk.VBox()
2267 alignment.add(mainBox)
2268 self.setMainWidget(alignment)
2269
2270 self._notamsFrame = gtk.Frame()
2271 self._notamsFrame.set_label(xstr("briefing_notams_init"))
2272 scrolledWindow = gtk.ScrolledWindow()
2273 scrolledWindow.set_size_request(-1, 128)
2274 # FIXME: these constants should be in common
2275 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
2276 else gtk.POLICY_AUTOMATIC,
2277 gtk.PolicyType.AUTOMATIC if pygobject
2278 else gtk.POLICY_AUTOMATIC)
2279 self._notams = gtk.TextView()
2280 self._notams.set_editable(False)
2281 self._notams.set_accepts_tab(False)
2282 self._notams.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
2283 scrolledWindow.add(self._notams)
2284 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
2285 xscale = 1.0, yscale = 1.0)
2286 alignment.set_padding(padding_top = 4, padding_bottom = 0,
2287 padding_left = 0, padding_right = 0)
2288 alignment.add(scrolledWindow)
2289 self._notamsFrame.add(alignment)
2290 mainBox.pack_start(self._notamsFrame, True, True, 4)
2291
2292 self._metarFrame = gtk.Frame()
2293 self._metarFrame.set_label(xstr("briefing_metar_init"))
2294 scrolledWindow = gtk.ScrolledWindow()
2295 scrolledWindow.set_size_request(-1, 32)
2296 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
2297 else gtk.POLICY_AUTOMATIC,
2298 gtk.PolicyType.AUTOMATIC if pygobject
2299 else gtk.POLICY_AUTOMATIC)
2300
2301 self._updatingMETAR = False
2302
2303 self._metar = gtk.TextView()
2304 self._metar.set_accepts_tab(False)
2305 self._metar.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
2306 self._metar.get_buffer().connect("changed", self._metarChanged)
2307 self._metar.get_buffer().connect_after("insert-text", self._metarInserted)
2308 scrolledWindow.add(self._metar)
2309 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
2310 xscale = 1.0, yscale = 1.0)
2311 alignment.set_padding(padding_top = 4, padding_bottom = 0,
2312 padding_left = 0, padding_right = 0)
2313 alignment.add(scrolledWindow)
2314 self._metarFrame.add(alignment)
2315 mainBox.pack_start(self._metarFrame, True, True, 4)
2316 self.metarEdited = False
2317
2318 self.addCancelFlightButton()
2319
2320 self.addPreviousButton(clicked = self._backClicked)
2321 self._button = self.addNextButton(clicked = self._forwardClicked)
2322
2323 @property
2324 def metar(self):
2325 """Get the METAR on the page."""
2326 buffer = self._metar.get_buffer()
2327 return buffer.get_text(buffer.get_start_iter(),
2328 buffer.get_end_iter(), True)
2329
2330 def setMETAR(self, metar):
2331 """Set the METAR."""
2332 self._metar.get_buffer().set_text(metar)
2333 self.metarEdited = False
2334
2335 def changeMETAR(self, metar):
2336 """Change the METAR as a result of an edit on one of the other
2337 pages."""
2338 self._updatingMETAR = True
2339 self._metar.get_buffer().set_text(metar)
2340 self._updatingMETAR = False
2341
2342 self._updateButton()
2343 self.metarEdited = True
2344
2345 def activate(self):
2346 """Activate the page."""
2347 if not self._departure:
2348 self._button.set_label(xstr("briefing_button"))
2349 self._button.set_has_tooltip(False)
2350 self._button.set_use_stock(False)
2351
2352 bookedFlight = self._wizard._bookedFlight
2353
2354 icao = bookedFlight.departureICAO if self._departure \
2355 else bookedFlight.arrivalICAO
2356 notams = self._wizard._departureNOTAMs if self._departure \
2357 else self._wizard._arrivalNOTAMs
2358 metar = self._wizard._departureMETAR if self._departure \
2359 else self._wizard._arrivalMETAR
2360
2361 self._notamsFrame.set_label(xstr("briefing_notams_template") % (icao,))
2362 buffer = self._notams.get_buffer()
2363 if notams is None:
2364 buffer.set_text(xstr("briefing_notams_failed"))
2365 elif not notams:
2366 buffer.set_text(xstr("briefing_notams_missing"))
2367 else:
2368 s = ""
2369 for notam in notams:
2370 s += str(notam)
2371 s += "-------------------- * --------------------\n"
2372 buffer.set_text(s)
2373
2374 self._metarFrame.set_label(xstr("briefing_metar_template") % (icao,))
2375 buffer = self._metar.get_buffer()
2376 self._updatingMETAR = True
2377 if metar is None:
2378 buffer.set_text("")
2379 self.setHelp(xstr("briefing_help_nometar"))
2380 else:
2381 buffer.set_text(metar)
2382 self._updatingMETAR = False
2383 self._updateButton()
2384
2385 label = self._metarFrame.get_label_widget()
2386 label.set_use_underline(True)
2387 label.set_mnemonic_widget(self._metar)
2388
2389 self.metarEdited = False
2390
2391 def _backClicked(self, button):
2392 """Called when the Back button is pressed."""
2393 self.goBack()
2394
2395 def _forwardClicked(self, button):
2396 """Called when the forward button is clicked."""
2397 if not self._departure:
2398 if not self._completed:
2399 self._wizard.gui.startMonitoring()
2400 self._button.set_label(xstr("button_next"))
2401 self._button.set_tooltip_text(xstr("button_next_tooltip"))
2402 self.complete()
2403
2404 self._wizard.nextPage()
2405
2406 def _metarChanged(self, buffer):
2407 """Called when the METAR has changed."""
2408 print "BriefingPage.metarChanged", self._updatingMETAR
2409 if not self._updatingMETAR:
2410 self.metarEdited = True
2411 self._updateButton()
2412 metar = buffer.get_text(buffer.get_start_iter(),
2413 buffer.get_end_iter(), True)
2414 self._wizard.metarChanged(metar, self)
2415
2416 def _metarInserted(self, textBuffer, iter, text, length):
2417 """Called when new characters are inserted into the METAR.
2418
2419 It uppercases all characters."""
2420 print "BriefingPage.metarInserted", self._updatingMETAR
2421 if not self._updatingMETAR:
2422 self._updatingMETAR = True
2423
2424 iter1 = iter.copy()
2425 iter1.backward_chars(length)
2426 textBuffer.delete(iter, iter1)
2427
2428 textBuffer.insert(iter, text.upper())
2429
2430 self._updatingMETAR = False
2431
2432 def _updateButton(self):
2433 """Update the sensitivity of the Next button based on the contents of
2434 the METAR field."""
2435 buffer = self._metar.get_buffer()
2436 self._button.set_sensitive(buffer.get_text(buffer.get_start_iter(),
2437 buffer.get_end_iter(),
2438 True)!="")
2439
2440
2441#-----------------------------------------------------------------------------
2442
2443class TakeoffPage(Page):
2444 """Page for entering the takeoff data."""
2445 def __init__(self, wizard):
2446 """Construct the takeoff page."""
2447 super(TakeoffPage, self).__init__(wizard, xstr("takeoff_title"),
2448 xstr("takeoff_help"),
2449 completedHelp = xstr("takeoff_chelp"))
2450
2451 self._forwardAllowed = False
2452
2453 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2454 xscale = 0.0, yscale = 0.0)
2455
2456 table = gtk.Table(9, 24)
2457 table.set_row_spacings(4)
2458 table.set_col_spacings(16)
2459 table.set_homogeneous(False)
2460 alignment.add(table)
2461 self.setMainWidget(alignment)
2462
2463 row = 0
2464
2465 label = gtk.Label(xstr("takeoff_metar"))
2466 label.set_use_underline(True)
2467 label.set_alignment(0.0, 0.5)
2468 table.attach(label, 0, 1, row, row+1)
2469
2470 self._metar = gtk.Entry()
2471 self._metar.set_width_chars(40)
2472 self._metar.set_tooltip_text(xstr("takeoff_metar_tooltip"))
2473 self._metar.connect("changed", self._metarChanged)
2474 self._metar.get_buffer().connect_after("inserted-text", self._metarInserted)
2475 table.attach(self._metar, 1, 24, row, row+1)
2476 label.set_mnemonic_widget(self._metar)
2477
2478 self._updatingMETAR = False
2479
2480 row += 1
2481
2482 label = gtk.Label(xstr("takeoff_runway"))
2483 label.set_use_underline(True)
2484 label.set_alignment(0.0, 0.5)
2485 table.attach(label, 0, 1, row, row+1)
2486
2487 self._runway = gtk.Entry()
2488 self._runway.set_width_chars(10)
2489 self._runway.set_tooltip_text(xstr("takeoff_runway_tooltip"))
2490 self._runway.connect("changed", self._upperChanged)
2491 table.attach(self._runway, 1, 3, row, row+1)
2492 label.set_mnemonic_widget(self._runway)
2493
2494 row += 1
2495
2496 label = gtk.Label(xstr("takeoff_sid"))
2497 label.set_use_underline(True)
2498 label.set_alignment(0.0, 0.5)
2499 table.attach(label, 0, 1, row, row+1)
2500
2501 if pygobject:
2502 self._sid = gtk.ComboBox.new_with_model_and_entry(comboModel)
2503 else:
2504 self._sid = gtk.ComboBoxEntry(comboModel)
2505
2506 self._sid.set_entry_text_column(0)
2507 self._sid.get_child().set_width_chars(10)
2508 self._sid.set_tooltip_text(xstr("takeoff_sid_tooltip"))
2509 self._sid.connect("changed", self._upperChangedComboBox)
2510 table.attach(self._sid, 1, 3, row, row+1)
2511 label.set_mnemonic_widget(self._sid)
2512
2513 row += 1
2514
2515 label = gtk.Label(xstr("takeoff_v1"))
2516 label.set_use_markup(True)
2517 label.set_use_underline(True)
2518 label.set_alignment(0.0, 0.5)
2519 table.attach(label, 0, 1, row, row+1)
2520
2521 self._v1 = IntegerEntry()
2522 self._v1.set_width_chars(4)
2523 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip_knots"))
2524 self._v1.connect("integer-changed", self._valueChanged)
2525 table.attach(self._v1, 2, 3, row, row+1)
2526 label.set_mnemonic_widget(self._v1)
2527
2528 self._v1Unit = gtk.Label(xstr("label_knots"))
2529 self._v1Unit.set_alignment(0.0, 0.5)
2530 table.attach(self._v1Unit, 3, 4, row, row+1)
2531
2532 row += 1
2533
2534 label = gtk.Label(xstr("takeoff_vr"))
2535 label.set_use_markup(True)
2536 label.set_use_underline(True)
2537 label.set_alignment(0.0, 0.5)
2538 table.attach(label, 0, 1, row, row+1)
2539
2540 self._vr = IntegerEntry()
2541 self._vr.set_width_chars(4)
2542 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip_knots"))
2543 self._vr.connect("integer-changed", self._valueChanged)
2544 table.attach(self._vr, 2, 3, row, row+1)
2545 label.set_mnemonic_widget(self._vr)
2546
2547 self._vrUnit = gtk.Label(xstr("label_knots"))
2548 self._vrUnit.set_alignment(0.0, 0.5)
2549 table.attach(self._vrUnit, 3, 4, row, row+1)
2550
2551 row += 1
2552
2553 label = gtk.Label(xstr("takeoff_v2"))
2554 label.set_use_markup(True)
2555 label.set_use_underline(True)
2556 label.set_alignment(0.0, 0.5)
2557 table.attach(label, 0, 1, row, row+1)
2558
2559 self._v2 = IntegerEntry()
2560 self._v2.set_width_chars(4)
2561 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip_knots"))
2562 self._v2.connect("integer-changed", self._valueChanged)
2563 table.attach(self._v2, 2, 3, row, row+1)
2564 label.set_mnemonic_widget(self._v2)
2565
2566 self._v2Unit = gtk.Label(xstr("label_knots"))
2567 self._v2Unit.set_alignment(0.0, 0.5)
2568 table.attach(self._v2Unit, 3, 4, row, row+1)
2569
2570 row += 1
2571
2572 self._derateType = acft.DERATE_NONE
2573
2574 self._derateLabel = gtk.Label()
2575 self._derateLabel.set_use_underline(True)
2576 self._derateLabel.set_markup(xstr("takeoff_derate_tupolev"))
2577 self._derateLabel.set_alignment(0.0, 0.5)
2578 table.attach(self._derateLabel, 0, 1, row, row+1)
2579
2580 self._derate = gtk.Alignment()
2581 table.attach(self._derate, 2, 4, row, row+1)
2582 self._derateWidget = None
2583 self._derateEntry = None
2584 self._derateUnit = None
2585 self._derateButtons = None
2586
2587 row += 1
2588
2589 self._antiIceOn = gtk.CheckButton(xstr("takeoff_antiice"))
2590 self._antiIceOn.set_use_underline(True)
2591 self._antiIceOn.set_tooltip_text(xstr("takeoff_antiice_tooltip"))
2592 table.attach(self._antiIceOn, 2, 4, row, row+1)
2593
2594 row += 1
2595
2596 self._rto = gtk.CheckButton(xstr("takeoff_rto"))
2597 self._rto.set_use_underline(True)
2598 self._rto.set_tooltip_text(xstr("takeoff_rto_tooltip"))
2599 self._rto.connect("toggled", self._rtoToggled)
2600 table.attach(self._rto, 2, 4, row, row+1, ypadding = 8)
2601
2602 self.addCancelFlightButton()
2603
2604 self.addPreviousButton(clicked = self._backClicked)
2605
2606 self._button = self.addNextButton(clicked = self._forwardClicked)
2607
2608 self._active = False
2609
2610 @property
2611 def runway(self):
2612 """Get the runway."""
2613 return self._runway.get_text()
2614
2615 @property
2616 def sid(self):
2617 """Get the SID."""
2618 text = self._sid.get_child().get_text()
2619 return text if self._sid.get_active()!=0 and text and text!="N/A" \
2620 else None
2621
2622 @property
2623 def v1(self):
2624 """Get the v1 speed."""
2625 return self._v1.get_int()
2626
2627 @property
2628 def vr(self):
2629 """Get the vr speed."""
2630 return self._vr.get_int()
2631
2632 @property
2633 def v2(self):
2634 """Get the v2 speed."""
2635 return self._v2.get_int()
2636
2637 @property
2638 def derate(self):
2639 """Get the derate value, if any."""
2640 if self._derateWidget is None:
2641 return None
2642 if self._derateType==acft.DERATE_BOEING:
2643 derate = self._derateEntry.get_text()
2644 return derate if derate else None
2645 elif self._derateType==acft.DERATE_EPR:
2646 derate = self._derateWidget.get_text()
2647 return derate if derate else None
2648 elif self._derateType==acft.DERATE_TUPOLEV:
2649 return acft.DERATE_TUPOLEV_NOMINAL \
2650 if self._derateButtons[0].get_active() \
2651 else acft.DERATE_TUPOLEV_TAKEOFF
2652 elif self._derateType==acft.DERATE_B462:
2653 return self._derateWidget.get_active()
2654 else:
2655 return None
2656
2657 @property
2658 def antiIceOn(self):
2659 """Get whether the anti-ice system has been turned on."""
2660 return self._antiIceOn.get_active()
2661
2662 @antiIceOn.setter
2663 def antiIceOn(self, value):
2664 """Set the anti-ice indicator."""
2665 self._antiIceOn.set_active(value)
2666
2667 @property
2668 def rtoIndicated(self):
2669 """Get whether the pilot has indicated if there was an RTO."""
2670 return self._rto.get_active()
2671
2672 def activate(self):
2673 """Activate the page."""
2674 print "TakeoffPage.activate"
2675
2676 self._updatingMETAR = True
2677 self._metar.get_buffer().set_text(self._wizard.departureMETAR, -1)
2678 self._updatingMETAR = False
2679
2680 self._runway.set_text("")
2681 self._runway.set_sensitive(True)
2682 self._sid.set_active(0)
2683 self._sid.set_sensitive(True)
2684 self._v1.set_int(None)
2685 self._v1.set_sensitive(True)
2686 self._vr.set_int(None)
2687 self._vr.set_sensitive(True)
2688 self._v2.set_int(None)
2689 self._v2.set_sensitive(True)
2690
2691 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
2692 speedUnit = xstr("label" + i18nSpeedUnit)
2693 self._v1Unit.set_text(speedUnit)
2694 self._vrUnit.set_text(speedUnit)
2695 self._v2Unit.set_text(speedUnit)
2696
2697 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip" + i18nSpeedUnit))
2698 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip" + i18nSpeedUnit))
2699 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip" + i18nSpeedUnit))
2700
2701 self._derateType = self._wizard.gui.flight.aircraft.derateType
2702
2703 self._setupDerateWidget()
2704
2705 self._rto.set_active(False)
2706 self._rto.set_sensitive(False)
2707
2708 self._button.set_sensitive(False)
2709 self._forwardAllowed = False
2710
2711 self._active = True
2712
2713 def allowForward(self):
2714 """Allow going to the next page."""
2715 print "TakeoffPage.allowForward"
2716 self._forwardAllowed = True
2717 self._updateForwardButton()
2718
2719 def reset(self):
2720 """Reset the page if the wizard is reset."""
2721 print "TakeoffPage.reset"
2722
2723 super(TakeoffPage, self).reset()
2724 self._v1.reset()
2725 self._vr.reset()
2726 self._v2.reset()
2727 self._hasDerate = False
2728 self._antiIceOn.set_active(False)
2729 self._active = False
2730
2731 def setRTOEnabled(self, enabled):
2732 """Set the RTO checkbox enabled or disabled."""
2733 if not enabled:
2734 self._rto.set_active(False)
2735 self._rto.set_sensitive(enabled)
2736
2737 def changeMETAR(self, metar):
2738 """Change the METAR as a result of an edit on one of the other
2739 pages."""
2740 if self._active:
2741 print "TakeoffPage.changeMETAR"
2742 self._updatingMETAR = True
2743 self._metar.get_buffer().set_text(metar, -1)
2744 self._updatingMETAR = False
2745
2746 self._updateForwardButton()
2747
2748 def _updateForwardButton(self):
2749 """Update the sensitivity of the forward button based on some conditions."""
2750 sensitive = self._forwardAllowed and \
2751 self._metar.get_text()!="" and \
2752 self._runway.get_text()!="" and \
2753 self.sid is not None and \
2754 self.v1 is not None and \
2755 self.vr is not None and \
2756 self.v2 is not None and \
2757 self.v1 <= self.vr and \
2758 self.vr <= self.v2 and \
2759 (self._derateType==acft.DERATE_NONE or
2760 self.derate is not None)
2761
2762 print "TakeoffPage._updateForwardButton: forwardAllowed:", self._forwardAllowed, ", sensitive:", sensitive
2763 if self._forwardAllowed:
2764 print " METAR: ", self._metar.get_text()
2765 print " runway: ", self._runway.get_text()
2766 print " SID:", self.sid
2767 print " V1:", self.v1
2768 print " VR:", self.vr
2769 print " V2:", self.v2
2770 print " derateType:", self._derateType
2771 print " derate:", self.derate
2772
2773 self._button.set_sensitive(sensitive)
2774
2775 def _valueChanged(self, widget, arg = None):
2776 """Called when the value of some widget has changed."""
2777 print "TakeoffPage._valueChanged"
2778
2779 self._updateForwardButton()
2780
2781 def _upperChanged(self, entry, arg = None):
2782 """Called when the value of some entry widget has changed and the value
2783 should be converted to uppercase."""
2784 print "TakeoffPage._upperChanged"
2785 entry.set_text(entry.get_text().upper())
2786 self._valueChanged(entry, arg)
2787
2788 def _upperChangedComboBox(self, comboBox):
2789 """Called for combo box widgets that must be converted to uppercase."""
2790 entry = comboBox.get_child()
2791 if comboBox.get_active()==-1:
2792 entry.set_text(entry.get_text().upper())
2793 self._valueChanged(entry)
2794
2795 def _derateChanged(self, entry):
2796 """Called when the value of the derate is changed."""
2797 print "TakeoffPage._derateChanged"
2798 self._updateForwardButton()
2799
2800 def _rtoToggled(self, button):
2801 """Called when the RTO check button is toggled."""
2802 self._wizard.rtoToggled(button.get_active())
2803
2804 def _backClicked(self, button):
2805 """Called when the Back button is pressed."""
2806 self.goBack()
2807
2808 def _forwardClicked(self, button):
2809 """Called when the forward button is clicked."""
2810 aircraft = self._wizard.gui.flight.aircraft
2811 aircraft.updateV1R2()
2812 if self.derate is not None:
2813 aircraft.updateDerate()
2814 aircraft.updateTakeoffAntiIce()
2815 self._wizard.nextPage()
2816
2817 def _setupDerateWidget(self):
2818 """Setup the derate widget."""
2819 if self._derateWidget is not None:
2820 self._derate.remove(self._derateWidget)
2821
2822 if self._derateType==acft.DERATE_BOEING:
2823 self._derateLabel.set_text(xstr("takeoff_derate_boeing"))
2824 self._derateLabel.set_use_underline(True)
2825 self._derateLabel.set_sensitive(True)
2826
2827 self._derateEntry = gtk.Entry()
2828 self._derateEntry.set_width_chars(7)
2829 self._derateEntry.set_tooltip_text(xstr("takeoff_derate_boeing_tooltip"))
2830 self._derateEntry.set_alignment(1.0)
2831 self._derateEntry.connect("changed", self._derateChanged)
2832 self._derateLabel.set_mnemonic_widget(self._derateEntry)
2833
2834 self._derateUnit = gtk.Label("%")
2835 self._derateUnit.set_alignment(0.0, 0.5)
2836
2837 self._derateWidget = gtk.Table(3, 1)
2838 self._derateWidget.set_row_spacings(4)
2839 self._derateWidget.set_col_spacings(16)
2840 self._derateWidget.set_homogeneous(False)
2841
2842 self._derateWidget.attach(self._derateEntry, 0, 2, 0, 1)
2843 self._derateWidget.attach(self._derateUnit, 2, 3, 0, 1)
2844
2845 self._derate.add(self._derateWidget)
2846 elif self._derateType==acft.DERATE_EPR:
2847 self._derateLabel.set_text("_EPR:")
2848 self._derateLabel.set_use_underline(True)
2849 self._derateLabel.set_sensitive(True)
2850
2851 self._derateWidget = gtk.Entry()
2852 self._derateWidget.set_width_chars(7)
2853 self._derateWidget.set_tooltip_text(xstr("takeoff_derate_epr_tooltip"))
2854 self._derateWidget.set_alignment(1.0)
2855 self._derateWidget.connect("changed", self._derateChanged)
2856 self._derateLabel.set_mnemonic_widget(self._derateWidget)
2857
2858 self._derate.add(self._derateWidget)
2859 elif self._derateType==acft.DERATE_TUPOLEV:
2860 self._derateLabel.set_text(xstr("takeoff_derate_tupolev"))
2861 self._derateLabel.set_use_underline(True)
2862 self._derateLabel.set_sensitive(True)
2863
2864 if pygobject:
2865 nominal = gtk.RadioButton.\
2866 new_with_label_from_widget(None,
2867 xstr("takeoff_derate_tupolev_nominal"))
2868 else:
2869 nominal = gtk.RadioButton(None,
2870 xstr("takeoff_derate_tupolev_nominal"))
2871 nominal.set_use_underline(True)
2872 nominal.set_tooltip_text(xstr("takeoff_derate_tupolev_nominal_tooltip"))
2873 nominal.connect("toggled", self._derateChanged)
2874
2875 if pygobject:
2876 takeoff = gtk.RadioButton.\
2877 new_with_label_from_widget(nominal,
2878 xstr("takeoff_derate_tupolev_takeoff"))
2879 else:
2880 takeoff = gtk.RadioButton(nominal,
2881 xstr("takeoff_derate_tupolev_takeoff"))
2882
2883 takeoff.set_use_underline(True)
2884 takeoff.set_tooltip_text(xstr("takeoff_derate_tupolev_takeoff_tooltip"))
2885 takeoff.connect("toggled", self._derateChanged)
2886
2887 self._derateButtons = [nominal, takeoff]
2888
2889 self._derateWidget = gtk.HBox()
2890 self._derateWidget.pack_start(nominal, False, False, 4)
2891 self._derateWidget.pack_start(takeoff, False, False, 4)
2892
2893 self._derate.add(self._derateWidget)
2894 elif self._derateType==acft.DERATE_B462:
2895 self._derateLabel.set_text("")
2896
2897 self._derateWidget = gtk.CheckButton(xstr("takeoff_derate_b462"))
2898 self._derateWidget.set_tooltip_text(xstr("takeoff_derate_b462_tooltip"))
2899 self._derateWidget.set_use_underline(True)
2900 self._derate.add(self._derateWidget)
2901 else:
2902 self._derateWidget = None
2903 self._derateLabel.set_text("")
2904 self._derateLabel.set_sensitive(False)
2905
2906 def _metarChanged(self, entry):
2907 """Called when the METAR has changed."""
2908 print "TakeoffPage.metarChanged", self._updatingMETAR
2909 if not self._updatingMETAR:
2910 self._updateForwardButton()
2911 self._wizard.metarChanged(entry.get_text(), self)
2912
2913 def _metarInserted(self, buffer, position, text, length):
2914 """Called when new characters are inserted into the METAR.
2915
2916 It uppercases all characters."""
2917 print "TakeoffPage.metarInserted", self._updatingMETAR
2918 if not self._updatingMETAR:
2919 self._updatingMETAR = True
2920
2921 buffer.delete_text(position, length)
2922 buffer.insert_text(position, text.upper(), length)
2923
2924 self._updatingMETAR = False
2925
2926#-----------------------------------------------------------------------------
2927
2928class CruisePage(Page):
2929 """The page containing the flight level that might change during flight."""
2930 def __init__(self, wizard):
2931 """Construct the page."""
2932 super(CruisePage, self).__init__(wizard, xstr("cruise_title"),
2933 xstr("cruise_help"))
2934
2935 self._loggable = False
2936 self._loggedCruiseLevel = 240
2937 self._activated = False
2938
2939 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
2940 xscale = 0.0, yscale = 1.0)
2941
2942 mainBox = gtk.VBox()
2943 alignment.add(mainBox)
2944 self.setMainWidget(alignment)
2945
2946 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
2947 xscale = 0.0, yscale = 0.0)
2948 mainBox.pack_start(alignment, False, False, 16)
2949
2950 levelBox = gtk.HBox()
2951
2952 label = gtk.Label(xstr("route_level"))
2953 label.set_use_underline(True)
2954 levelBox.pack_start(label, True, True, 0)
2955
2956 self._cruiseLevel = gtk.SpinButton()
2957 self._cruiseLevel.set_increments(step = 10, page = 100)
2958 self._cruiseLevel.set_range(min = 50, max = 500)
2959 self._cruiseLevel.set_tooltip_text(xstr("cruise_route_level_tooltip"))
2960 self._cruiseLevel.set_numeric(True)
2961 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
2962 label.set_mnemonic_widget(self._cruiseLevel)
2963
2964 levelBox.pack_start(self._cruiseLevel, False, False, 8)
2965
2966 self._updateButton = gtk.Button(xstr("cruise_route_level_update"));
2967 self._updateButton.set_use_underline(True)
2968 self._updateButton.set_tooltip_text(xstr("cruise_route_level_update_tooltip"))
2969 self._updateButton.connect("clicked", self._updateButtonClicked)
2970
2971 levelBox.pack_start(self._updateButton, False, False, 16)
2972
2973 mainBox.pack_start(levelBox, False, False, 0)
2974
2975 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
2976 xscale = 0.0, yscale = 1.0)
2977 mainBox.pack_start(alignment, True, True, 0)
2978
2979 self.addCancelFlightButton()
2980
2981 self._backButton = self.addPreviousButton(clicked = self._backClicked)
2982 self._button = self.addNextButton(clicked = self._forwardClicked)
2983
2984 @property
2985 def activated(self):
2986 """Determine if the page is already activated or not."""
2987 return self._activated
2988
2989 @property
2990 def cruiseLevel(self):
2991 """Get the cruise level."""
2992 return self._loggedCruiseLevel
2993
2994 @property
2995 def loggableCruiseLevel(self):
2996 """Get the cruise level which should be logged."""
2997 return self._cruiseLevel.get_value_as_int()
2998
2999 def setLoggable(self, loggable):
3000 """Set whether the cruise altitude can be logged."""
3001 self._loggable = loggable
3002 self._updateButtons()
3003
3004 def activate(self):
3005 """Setup the route from the booked flight."""
3006 self._loggedCruiseLevel = self._wizard.filedCruiseLevel
3007 self._cruiseLevel.set_value(self._loggedCruiseLevel)
3008 self._activated = True
3009
3010 def reset(self):
3011 """Reset the page."""
3012 self._loggable = False
3013 self._activated = False
3014 super(CruisePage, self).reset()
3015
3016 def _updateButtons(self):
3017 """Update the sensitivity of the buttons."""
3018 self._updateButton.set_sensitive(self._loggable and
3019 self.loggableCruiseLevel!=
3020 self._loggedCruiseLevel)
3021
3022 def _cruiseLevelChanged(self, spinButton):
3023 """Called when the cruise level has changed."""
3024 self._updateButtons()
3025
3026 def _updateButtonClicked(self, button):
3027 """Called when the update button is clicked."""
3028 if self._wizard.cruiseLevelChanged():
3029 self._loggedCruiseLevel = self.loggableCruiseLevel
3030 self._updateButtons()
3031
3032 def _backClicked(self, button):
3033 """Called when the Back button is pressed."""
3034 self.goBack()
3035
3036 def _forwardClicked(self, button):
3037 """Called when the Forward button is clicked."""
3038 self._wizard.nextPage()
3039
3040#-----------------------------------------------------------------------------
3041
3042class LandingPage(Page):
3043 """Page for entering landing data."""
3044 def __init__(self, wizard):
3045 """Construct the landing page."""
3046 super(LandingPage, self).__init__(wizard, xstr("landing_title"),
3047 xstr("landing_help"),
3048 completedHelp = xstr("landing_chelp"))
3049
3050 self._flightEnded = False
3051
3052 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3053 xscale = 0.0, yscale = 0.0)
3054
3055 table = gtk.Table(7, 24)
3056 table.set_row_spacings(4)
3057 table.set_col_spacings(16)
3058 table.set_homogeneous(False)
3059 alignment.add(table)
3060 self.setMainWidget(alignment)
3061
3062 row = 0
3063
3064 label = gtk.Label(xstr("landing_metar"))
3065 label.set_use_underline(True)
3066 label.set_alignment(0.0, 0.5)
3067 table.attach(label, 0, 1, row, row+1)
3068
3069 self._metar = gtk.Entry()
3070 self._metar.set_width_chars(40)
3071 self._metar.set_tooltip_text(xstr("landing_metar_tooltip"))
3072 self._metar.connect("changed", self._metarChanged)
3073 self._metar.get_buffer().connect_after("inserted-text", self._metarInserted)
3074 table.attach(self._metar, 1, 24, row, row+1)
3075 label.set_mnemonic_widget(self._metar)
3076
3077 self._updatingMETAR = False
3078
3079 row += 1
3080
3081 label = gtk.Label(xstr("landing_star"))
3082 label.set_use_underline(True)
3083 label.set_alignment(0.0, 0.5)
3084 table.attach(label, 1, 2, row, row + 1)
3085
3086 if pygobject:
3087 self._star = gtk.ComboBox.new_with_model_and_entry(comboModel)
3088 else:
3089 self._star = gtk.ComboBoxEntry(comboModel)
3090
3091 self._star.set_entry_text_column(0)
3092 self._star.get_child().set_width_chars(10)
3093 self._star.set_tooltip_text(xstr("landing_star_tooltip"))
3094 self._star.connect("changed", self._upperChangedComboBox)
3095 self._star.set_sensitive(False)
3096 table.attach(self._star, 2, 4, row, row + 1)
3097 label.set_mnemonic_widget(self._star)
3098
3099 row += 1
3100
3101 label = gtk.Label(xstr("landing_transition"))
3102 label.set_use_underline(True)
3103 label.set_alignment(0.0, 0.5)
3104 table.attach(label, 1, 2, row, row + 1)
3105
3106 if pygobject:
3107 self._transition = gtk.ComboBox.new_with_model_and_entry(comboModel)
3108 else:
3109 self._transition = gtk.ComboBoxEntry(comboModel)
3110
3111 self._transition.set_entry_text_column(0)
3112 self._transition.get_child().set_width_chars(10)
3113 self._transition.set_tooltip_text(xstr("landing_transition_tooltip"))
3114 self._transition.connect("changed", self._upperChangedComboBox)
3115 self._transition.set_sensitive(False)
3116 table.attach(self._transition, 2, 4, row, row + 1)
3117 label.set_mnemonic_widget(self._transition)
3118
3119 row += 1
3120
3121 label = gtk.Label(xstr("landing_runway"))
3122 label.set_use_underline(True)
3123 label.set_alignment(0.0, 0.5)
3124 table.attach(label, 1, 2, row, row + 1)
3125
3126 self._runway = gtk.Entry()
3127 self._runway.set_width_chars(10)
3128 self._runway.set_tooltip_text(xstr("landing_runway_tooltip"))
3129 self._runway.connect("changed", self._upperChanged)
3130 table.attach(self._runway, 2, 4, row, row + 1)
3131 label.set_mnemonic_widget(self._runway)
3132
3133 row += 1
3134
3135 label = gtk.Label(xstr("landing_approach"))
3136 label.set_use_underline(True)
3137 label.set_alignment(0.0, 0.5)
3138 table.attach(label, 1, 2, row, row + 1)
3139
3140 self._approachType = gtk.Entry()
3141 self._approachType.set_width_chars(10)
3142 self._approachType.set_tooltip_text(xstr("landing_approach_tooltip"))
3143 self._approachType.connect("changed", self._upperChanged)
3144 table.attach(self._approachType, 2, 4, row, row + 1)
3145 label.set_mnemonic_widget(self._approachType)
3146
3147 row += 1
3148
3149 label = gtk.Label(xstr("landing_vref"))
3150 label.set_use_markup(True)
3151 label.set_use_underline(True)
3152 label.set_alignment(0.0, 0.5)
3153 table.attach(label, 1, 2, row, row + 1)
3154
3155 self._vref = IntegerEntry()
3156 self._vref.set_width_chars(5)
3157 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip_knots"))
3158 self._vref.connect("integer-changed", self._vrefChanged)
3159 table.attach(self._vref, 3, 4, row, row + 1)
3160 label.set_mnemonic_widget(self._vref)
3161
3162 self._vrefUnit = gtk.Label(xstr("label_knots"))
3163 table.attach(self._vrefUnit, 4, 5, row, row + 1)
3164
3165 row += 1
3166
3167 self._antiIceOn = gtk.CheckButton(xstr("landing_antiice"))
3168 self._antiIceOn.set_use_underline(True)
3169 self._antiIceOn.set_tooltip_text(xstr("landing_antiice_tooltip"))
3170 table.attach(self._antiIceOn, 3, 5, row, row + 1)
3171
3172 self.addCancelFlightButton()
3173
3174 self.addPreviousButton(clicked = self._backClicked)
3175
3176 self._button = self.addNextButton(clicked = self._forwardClicked)
3177
3178 self._active = False
3179
3180 @property
3181 def star(self):
3182 """Get the STAR or None if none entered."""
3183 text = self._star.get_child().get_text()
3184 return text if self._star.get_active()!=0 and text and text!="N/A" \
3185 else None
3186
3187 @property
3188 def transition(self):
3189 """Get the transition or None if none entered."""
3190 text = self._transition.get_child().get_text()
3191 return text if self._transition.get_active()!=0 and text and text!="N/A" \
3192 else None
3193
3194 @property
3195 def approachType(self):
3196 """Get the approach type."""
3197 return self._approachType.get_text()
3198
3199 @property
3200 def runway(self):
3201 """Get the runway."""
3202 return self._runway.get_text()
3203
3204 @property
3205 def vref(self):
3206 """Return the landing reference speed."""
3207 return self._vref.get_int()
3208
3209 @property
3210 def antiIceOn(self):
3211 """Get whether the anti-ice system has been turned on."""
3212 return self._antiIceOn.get_active()
3213
3214 @antiIceOn.setter
3215 def antiIceOn(self, value):
3216 """Set the anti-ice indicator."""
3217 self._antiIceOn.set_active(value)
3218
3219 def reset(self):
3220 """Reset the page if the wizard is reset."""
3221 super(LandingPage, self).reset()
3222 self._vref.reset()
3223 self._antiIceOn.set_active(False)
3224 self._flightEnded = False
3225 self._active = False
3226
3227 def activate(self):
3228 """Called when the page is activated."""
3229 self._updatingMETAR = True
3230 self._metar.get_buffer().set_text(self._wizard.arrivalMETAR, -1)
3231 self._updatingMETAR = False
3232
3233 self._star.set_active(0)
3234 self._star.set_sensitive(True)
3235
3236 self._transition.set_active(0)
3237 self._transition.set_sensitive(True)
3238
3239 self._runway.set_text("")
3240 self._runway.set_sensitive(True)
3241
3242 self._approachType.set_text("")
3243 self._approachType.set_sensitive(True)
3244
3245 self._vref.set_int(None)
3246 self._vref.set_sensitive(True)
3247
3248 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
3249 speedUnit = xstr("label" + i18nSpeedUnit)
3250 self._vrefUnit.set_text(speedUnit)
3251
3252 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip" +
3253 i18nSpeedUnit))
3254
3255 self._updateForwardButton()
3256
3257 self._active = True
3258
3259 def flightEnded(self):
3260 """Called when the flight has ended."""
3261 super(LandingPage, self).flightEnded()
3262 self._flightEnded = True
3263 self._updateForwardButton()
3264
3265 def changeMETAR(self, metar):
3266 """Change the METAR as a result of an edit on one of the other
3267 pages."""
3268 if self._active:
3269 print "LandingPage.changeMETAR"
3270 self._updatingMETAR = True
3271 self._metar.get_buffer().set_text(metar, -1)
3272 self._updatingMETAR = False
3273
3274 self._updateForwardButton()
3275
3276 def _updateForwardButton(self):
3277 """Update the sensitivity of the forward button."""
3278 sensitive = self._flightEnded and \
3279 self._metar.get_text()!="" and \
3280 (self.star is not None or
3281 self.transition is not None) and \
3282 self._runway.get_text()!="" and \
3283 self._approachType.get_text()!="" and \
3284 self.vref is not None
3285 self._button.set_sensitive(sensitive)
3286
3287 def _upperChanged(self, entry):
3288 """Called for entry widgets that must be converted to uppercase."""
3289 entry.set_text(entry.get_text().upper())
3290 self._updateForwardButton()
3291
3292 def _upperChangedComboBox(self, comboBox):
3293 """Called for combo box widgets that must be converted to uppercase."""
3294 if comboBox.get_active()==-1:
3295 entry = comboBox.get_child()
3296 entry.set_text(entry.get_text().upper())
3297 self._updateForwardButton()
3298
3299 def _vrefChanged(self, widget, value):
3300 """Called when the Vref has changed."""
3301 self._updateForwardButton()
3302
3303 def _backClicked(self, button):
3304 """Called when the Back button is pressed."""
3305 self.goBack()
3306
3307 def _forwardClicked(self, button):
3308 """Called when the forward button is clicked."""
3309 aircraft = self._wizard.gui.flight.aircraft
3310 aircraft.updateVRef()
3311 aircraft.updateLandingAntiIce()
3312 if self._wizard.gui.config.onlineGateSystem and \
3313 self._wizard.loggedIn and not self._completed and \
3314 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
3315 not self._wizard.entranceExam:
3316 self._wizard.getFleet(callback = self._fleetRetrieved,
3317 force = True)
3318 else:
3319 self._wizard.nextPage()
3320
3321 def _fleetRetrieved(self, fleet):
3322 """Callback for the fleet retrieval."""
3323 self._wizard.nextPage()
3324
3325 def _metarChanged(self, entry):
3326 """Called when the METAR has changed."""
3327 print "LandingPage.metarChanged", self._updatingMETAR
3328 if not self._updatingMETAR:
3329 self._updateForwardButton()
3330 self._wizard.metarChanged(entry.get_text(), self)
3331
3332 def _metarInserted(self, buffer, position, text, length):
3333 """Called when new characters are inserted into the METAR.
3334
3335 It uppercases all characters."""
3336 print "LandingPage.metarInserted", self._updatingMETAR
3337 if not self._updatingMETAR:
3338 self._updatingMETAR = True
3339
3340 buffer.delete_text(position, length)
3341 buffer.insert_text(position, text.upper(), length)
3342
3343 self._updatingMETAR = False
3344
3345#-----------------------------------------------------------------------------
3346
3347class FinishPage(Page):
3348 """Flight finish page."""
3349 _flightTypes = [ ("flighttype_scheduled", const.FLIGHTTYPE_SCHEDULED),
3350 ("flighttype_ot", const.FLIGHTTYPE_OLDTIMER),
3351 ("flighttype_vip", const.FLIGHTTYPE_VIP),
3352 ("flighttype_charter", const.FLIGHTTYPE_CHARTER) ]
3353
3354 def __init__(self, wizard):
3355 """Construct the finish page."""
3356 help = xstr("finish_help") + xstr("finish_help_goodtime")
3357 super(FinishPage, self).__init__(wizard, xstr("finish_title"), help)
3358
3359 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3360 xscale = 0.0, yscale = 0.0)
3361
3362 table = gtk.Table(10, 2)
3363 table.set_row_spacings(4)
3364 table.set_col_spacings(16)
3365 table.set_homogeneous(False)
3366 alignment.add(table)
3367 self.setMainWidget(alignment)
3368
3369 row = 0
3370
3371 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3372 label = gtk.Label(xstr("finish_rating"))
3373 labelAlignment.add(label)
3374 table.attach(labelAlignment, 0, 1, row, row+1)
3375
3376 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3377 self._flightRating = gtk.Label()
3378 self._flightRating.set_width_chars(8)
3379 self._flightRating.set_alignment(0.0, 0.5)
3380 self._flightRating.set_use_markup(True)
3381 labelAlignment.add(self._flightRating)
3382 table.attach(labelAlignment, 1, 2, row, row+1)
3383
3384 row += 1
3385
3386 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3387 label = gtk.Label(xstr("finish_dep_time"))
3388 labelAlignment.add(label)
3389 table.attach(labelAlignment, 0, 1, row, row+1)
3390
3391 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3392 self._depTime = gtk.Label()
3393 self._depTime.set_width_chars(13)
3394 self._depTime.set_alignment(0.0, 0.5)
3395 self._depTime.set_use_markup(True)
3396 self._depTime.set_tooltip_markup(xstr("finish_dep_time_tooltip"))
3397 labelAlignment.add(self._depTime)
3398 table.attach(labelAlignment, 1, 2, row, row+1)
3399
3400 row += 1
3401
3402 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3403 label = gtk.Label(xstr("finish_flight_time"))
3404 labelAlignment.add(label)
3405 table.attach(labelAlignment, 0, 1, row, row+1)
3406
3407 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3408 self._flightTime = gtk.Label()
3409 self._flightTime.set_width_chars(10)
3410 self._flightTime.set_alignment(0.0, 0.5)
3411 self._flightTime.set_use_markup(True)
3412 labelAlignment.add(self._flightTime)
3413 table.attach(labelAlignment, 1, 2, row, row+1)
3414
3415 row += 1
3416
3417 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3418 label = gtk.Label(xstr("finish_block_time"))
3419 labelAlignment.add(label)
3420 table.attach(labelAlignment, 0, 1, row, row+1)
3421
3422 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3423 self._blockTime = gtk.Label()
3424 self._blockTime.set_width_chars(10)
3425 self._blockTime.set_alignment(0.0, 0.5)
3426 self._blockTime.set_use_markup(True)
3427 labelAlignment.add(self._blockTime)
3428 table.attach(labelAlignment, 1, 2, row, row+1)
3429
3430 row += 1
3431
3432 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3433 label = gtk.Label(xstr("finish_arr_time"))
3434 labelAlignment.add(label)
3435 table.attach(labelAlignment, 0, 1, row, row+1)
3436
3437 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3438 self._arrTime = gtk.Label()
3439 self._arrTime.set_width_chars(13)
3440 self._arrTime.set_alignment(0.0, 0.5)
3441 self._arrTime.set_use_markup(True)
3442 self._arrTime.set_tooltip_markup(xstr("finish_arr_time_tooltip"))
3443 labelAlignment.add(self._arrTime)
3444 table.attach(labelAlignment, 1, 2, row, row+1)
3445
3446 row += 1
3447
3448 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3449 label = gtk.Label(xstr("finish_distance"))
3450 labelAlignment.add(label)
3451 table.attach(labelAlignment, 0, 1, row, row+1)
3452
3453 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3454 self._distanceFlown = gtk.Label()
3455 self._distanceFlown.set_width_chars(10)
3456 self._distanceFlown.set_alignment(0.0, 0.5)
3457 self._distanceFlown.set_use_markup(True)
3458 labelAlignment.add(self._distanceFlown)
3459 table.attach(labelAlignment, 1, 2, row, row+1)
3460
3461 row += 1
3462
3463 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3464 label = gtk.Label(xstr("finish_fuel"))
3465 labelAlignment.add(label)
3466 table.attach(labelAlignment, 0, 1, row, row+1)
3467
3468 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3469 self._fuelUsed = gtk.Label()
3470 self._fuelUsed.set_width_chars(10)
3471 self._fuelUsed.set_alignment(0.0, 0.5)
3472 self._fuelUsed.set_use_markup(True)
3473 labelAlignment.add(self._fuelUsed)
3474 table.attach(labelAlignment, 1, 2, row, row+1)
3475
3476 row += 1
3477
3478 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
3479 yalign = 0.5, yscale = 0.0)
3480 label = gtk.Label(xstr("finish_type"))
3481 label.set_use_underline(True)
3482 labelAlignment.add(label)
3483 table.attach(labelAlignment, 0, 1, row, row+1)
3484
3485 flightTypeModel = gtk.ListStore(str, int)
3486 for (name, type) in FinishPage._flightTypes:
3487 flightTypeModel.append([xstr(name), type])
3488
3489 self._flightType = gtk.ComboBox(model = flightTypeModel)
3490 renderer = gtk.CellRendererText()
3491 self._flightType.pack_start(renderer, True)
3492 self._flightType.add_attribute(renderer, "text", 0)
3493 self._flightType.set_tooltip_text(xstr("finish_type_tooltip"))
3494 self._flightType.set_active(0)
3495 self._flightType.connect("changed", self._flightTypeChanged)
3496 flightTypeAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3497 flightTypeAlignment.add(self._flightType)
3498 table.attach(flightTypeAlignment, 1, 2, row, row+1)
3499 label.set_mnemonic_widget(self._flightType)
3500
3501 row += 1
3502
3503 self._onlineFlight = gtk.CheckButton(xstr("finish_online"))
3504 self._onlineFlight.set_use_underline(True)
3505 self._onlineFlight.set_tooltip_text(xstr("finish_online_tooltip"))
3506 onlineFlightAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3507 onlineFlightAlignment.add(self._onlineFlight)
3508 table.attach(onlineFlightAlignment, 1, 2, row, row + 1)
3509
3510 row += 1
3511
3512 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
3513 yalign = 0.5, yscale = 0.0)
3514 self._gateLabel = gtk.Label(xstr("finish_gate"))
3515 self._gateLabel.set_use_underline(True)
3516 labelAlignment.add(self._gateLabel)
3517 table.attach(labelAlignment, 0, 1, row, row+1)
3518
3519 self._gatesModel = gtk.ListStore(str)
3520
3521 self._gate = gtk.ComboBox(model = self._gatesModel)
3522 renderer = gtk.CellRendererText()
3523 self._gate.pack_start(renderer, True)
3524 self._gate.add_attribute(renderer, "text", 0)
3525 self._gate.set_tooltip_text(xstr("finish_gate_tooltip"))
3526 self._gate.connect("changed", self._gateChanged)
3527 gateAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
3528 gateAlignment.add(self._gate)
3529 table.attach(gateAlignment, 1, 2, row, row+1)
3530 self._gateLabel.set_mnemonic_widget(self._gate)
3531
3532 self.addButton(xstr("finish_newFlight"),
3533 sensitive = True,
3534 clicked = self._newFlightClicked,
3535 tooltip = xstr("finish_newFlight_tooltip"),
3536 padding = 16)
3537
3538 self.addPreviousButton(clicked = self._backClicked)
3539
3540 self._saveButton = self.addButton(xstr("finish_save"),
3541 sensitive = False,
3542 clicked = self._saveClicked,
3543 tooltip = xstr("finish_save_tooltip"))
3544 self._savePIREPDialog = None
3545 self._lastSavePath = None
3546
3547 self._tooBigTimeDifference = False
3548 self._deferredAutoSave = False
3549 self._pirepSaved = False
3550 self._pirepSent = False
3551
3552 self._sendButton = self.addButton(xstr("sendPIREP"), default = True,
3553 sensitive = False,
3554 clicked = self._sendClicked,
3555 tooltip = xstr("sendPIREP_tooltip"))
3556
3557 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 10*60.0)
3558 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 20*60.0)
3559 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 0*60.0)
3560 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), (23*60.0+50)*60.0)
3561 # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (1*60.0+5)*60.0)
3562 # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (0*60.0+50)*60.0)
3563 # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (0*60.0+5)*60.0)
3564 # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (23*60.0+45)*60.0)
3565
3566 @property
3567 def flightType(self):
3568 """Get the flight type."""
3569 index = self._flightType.get_active()
3570 return None if index<0 else self._flightType.get_model()[index][1]
3571
3572 @property
3573 def online(self):
3574 """Get whether the flight was an online flight or not."""
3575 return self._onlineFlight.get_active()
3576
3577 def activate(self):
3578 """Activate the page."""
3579 self._deferredAutoSave = False
3580 self._pirepSaved = False
3581 self._pirepSent = False
3582
3583 flight = self._wizard.gui._flight
3584 rating = flight.logger.getRating()
3585 if rating<0:
3586 self._flightRating.set_markup('<b><span foreground="red">NO GO</span></b>')
3587 else:
3588 self._flightRating.set_markup("<b>%.1f %%</b>" % (rating,))
3589
3590 flightLength = flight.flightTimeEnd - flight.flightTimeStart
3591 self._flightTime.set_markup("<b>%s</b>" % \
3592 (util.getTimeIntervalString(flightLength),))
3593
3594 blockLength = flight.blockTimeEnd - flight.blockTimeStart
3595 self._blockTime.set_markup("<b>%s</b>" % \
3596 (util.getTimeIntervalString(blockLength),))
3597
3598 self._distanceFlown.set_markup("<b>%.2f NM</b>" % \
3599 (flight.flownDistance,))
3600
3601 self._fuelUsed.set_markup("<b>%.0f kg</b>" % \
3602 (flight.startFuel - flight.endFuel,))
3603
3604 self._flightType.set_active(-1)
3605 self._onlineFlight.set_active(self._wizard.loggedIn)
3606
3607 self._gatesModel.clear()
3608 if self._wizard.gui.config.onlineGateSystem and \
3609 self._wizard.loggedIn and \
3610 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
3611 not self._wizard.entranceExam:
3612 occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
3613 for gate in lhbpGates.gates:
3614 if gate.isAvailable(lhbpGates, occupiedGateNumbers):
3615 self._gatesModel.append([gate.number])
3616 self._gateLabel.set_sensitive(True)
3617 self._gate.set_sensitive(True)
3618 self._gate.set_active(-1)
3619 else:
3620 self._gateLabel.set_sensitive(False)
3621 self._gate.set_sensitive(False)
3622
3623 self._updateTimes()
3624
3625 def updateButtons(self):
3626 """Update the sensitivity state of the buttons."""
3627 gui = self._wizard.gui
3628 faultsExplained = gui.faultsFullyExplained
3629 timesCorrect = self.flightType is None or \
3630 not self._tooBigTimeDifference or \
3631 gui.hasComments or gui.hasDelayCode
3632 sensitive = gui.flight is not None and \
3633 gui.flight.stage==const.STAGE_END and \
3634 self._flightType.get_active()>=0 and \
3635 (self._gatesModel.get_iter_first() is None or
3636 self._gate.get_active()>=0) and \
3637 faultsExplained and timesCorrect
3638
3639 self._updateHelp(faultsExplained, timesCorrect)
3640
3641 wasSensitive = self._saveButton.get_sensitive()
3642
3643 if gui.config.pirepAutoSave and sensitive and not wasSensitive:
3644 if gui.isWizardActive():
3645 self._autoSavePIREP()
3646 else:
3647 self._deferredAutoSave = True
3648
3649 if not sensitive:
3650 self._deferredAutoSave = False
3651
3652 self._saveButton.set_sensitive(sensitive)
3653 self._sendButton.set_sensitive(sensitive and
3654 self._wizard.bookedFlight.id is not None)
3655
3656 def grabDefault(self):
3657 """If the page has a default button, make it the default one."""
3658 super(FinishPage, self).grabDefault()
3659 if self._deferredAutoSave:
3660 self._autoSavePIREP()
3661 self._deferredAutoSave = False
3662
3663 def _autoSavePIREP(self):
3664 """Perform the automatic saving of the PIREP."""
3665 self._lastSavePath = os.path.join(self._wizard.gui.config.pirepDirectory,
3666 self._getDefaultPIREPName())
3667 self._lastSavePath = text2unicode(self._lastSavePath)
3668 self._savePIREP(automatic = True)
3669
3670 def _backClicked(self, button):
3671 """Called when the Back button is pressed."""
3672 self.goBack()
3673
3674 def _flightTypeChanged(self, comboBox):
3675 """Called when the flight type has changed."""
3676 self._updateTimes()
3677
3678 def _gateChanged(self, comboBox):
3679 """Called when the arrival gate has changed."""
3680 self.updateButtons()
3681
3682 def _newFlightClicked(self, button):
3683 """Called when the new flight button is clicked."""
3684 gui = self._wizard.gui
3685 if not self._pirepSent and not self._pirepSaved:
3686 dialog = gtk.MessageDialog(parent = gui.mainWindow,
3687 type = MESSAGETYPE_QUESTION,
3688 message_format = xstr("finish_newFlight_question"))
3689
3690 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
3691 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
3692
3693 dialog.set_title(WINDOW_TITLE_BASE)
3694 result = dialog.run()
3695 dialog.hide()
3696 if result!=RESPONSETYPE_YES:
3697 return
3698
3699 gui.reset()
3700
3701 def _getDefaultPIREPName(self):
3702 """Get the default name of the PIREP."""
3703 gui = self._wizard.gui
3704
3705 bookedFlight = gui.bookedFlight
3706 tm = time.gmtime()
3707
3708 pilotID = self._wizard.pilotID
3709 if pilotID: pilotID += " "
3710 return "%s%s %02d%02d %s-%s.pirep" % \
3711 (pilotID, str(bookedFlight.departureTime.date()),
3712 tm.tm_hour, tm.tm_min,
3713 bookedFlight.departureICAO, bookedFlight.arrivalICAO)
3714
3715
3716 def _saveClicked(self, button):
3717 """Called when the Save PIREP button is clicked."""
3718 gui = self._wizard.gui
3719
3720 fileName = self._getDefaultPIREPName()
3721
3722 dialog = self._getSaveDialog()
3723
3724 if self._lastSavePath is None:
3725 pirepDirectory = gui.config.pirepDirectory
3726 if pirepDirectory is not None:
3727 dialog.set_current_folder(pirepDirectory)
3728 else:
3729 dialog.set_current_folder(os.path.dirname(self._lastSavePath))
3730
3731 dialog.set_current_name(fileName)
3732 result = dialog.run()
3733 dialog.hide()
3734
3735 if result==RESPONSETYPE_OK:
3736 self._lastSavePath = text2unicode(dialog.get_filename())
3737 self._savePIREP()
3738
3739 def _savePIREP(self, automatic = False):
3740 """Perform the saving of the PIREP."""
3741
3742 gui = self._wizard.gui
3743
3744 if automatic:
3745 gui.beginBusy(xstr("finish_autosave_busy"))
3746
3747 pirep = PIREP(gui.flight)
3748 error = pirep.save(self._lastSavePath)
3749
3750 if automatic:
3751 gui.endBusy()
3752
3753 if error:
3754 type = MESSAGETYPE_ERROR
3755 message = xstr("finish_save_failed")
3756 secondary = xstr("finish_save_failed_sec") % (text2unicode(error),)
3757 else:
3758 type = MESSAGETYPE_INFO
3759 message = xstr("finish_save_done")
3760 if automatic:
3761 secondary = xstr("finish_save_done_sec") % (self._lastSavePath,)
3762 else:
3763 secondary = None
3764 self._pirepSaved = True
3765
3766 dialog = gtk.MessageDialog(parent = gui.mainWindow,
3767 type = type, message_format = message)
3768 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
3769 dialog.set_title(WINDOW_TITLE_BASE)
3770 if secondary is not None:
3771 dialog.format_secondary_markup(secondary)
3772
3773 dialog.run()
3774 dialog.hide()
3775
3776 def _getSaveDialog(self):
3777 """Get the PIREP saving dialog.
3778
3779 If it does not exist yet, create it."""
3780 if self._savePIREPDialog is None:
3781 gui = self._wizard.gui
3782 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
3783 xstr("finish_save_title"),
3784 action = FILE_CHOOSER_ACTION_SAVE,
3785 buttons = (gtk.STOCK_CANCEL,
3786 RESPONSETYPE_CANCEL,
3787 gtk.STOCK_OK, RESPONSETYPE_OK),
3788 parent = gui.mainWindow)
3789 dialog.set_modal(True)
3790 dialog.set_do_overwrite_confirmation(True)
3791
3792 filter = gtk.FileFilter()
3793 filter.set_name(xstr("file_filter_pireps"))
3794 filter.add_pattern("*.pirep")
3795 dialog.add_filter(filter)
3796
3797 filter = gtk.FileFilter()
3798 filter.set_name(xstr("file_filter_all"))
3799 filter.add_pattern("*.*")
3800 dialog.add_filter(filter)
3801
3802 self._savePIREPDialog = dialog
3803
3804 return self._savePIREPDialog
3805
3806
3807 def _sendClicked(self, button):
3808 """Called when the Send button is clicked."""
3809 pirep = PIREP(self._wizard.gui.flight)
3810 self._wizard.gui.sendPIREP(pirep,
3811 callback = self._handlePIREPSent)
3812
3813 def _handlePIREPSent(self, returned, result):
3814 """Callback for the PIREP sending result."""
3815 self._pirepSent = returned and result.success
3816 if self._wizard.gui.config.onlineGateSystem and \
3817 self._wizard.loggedIn and not self._wizard.entranceExam and \
3818 returned and result.success:
3819 bookedFlight = self._wizard.bookedFlight
3820 if bookedFlight.arrivalICAO=="LHBP":
3821 iter = self._gate.get_active_iter()
3822 gateNumber = None if iter is None \
3823 else self._gatesModel.get_value(iter, 0)
3824
3825 status = const.PLANE_PARKING if gateNumber is None \
3826 else const.PLANE_HOME
3827 else:
3828 gateNumber = None
3829 status = const.PLANE_AWAY
3830
3831 self._wizard.updatePlane(self._planeUpdated,
3832 bookedFlight.tailNumber,
3833 status, gateNumber = gateNumber)
3834
3835 def _planeUpdated(self, success):
3836 """Callback for the plane updating."""
3837 pass
3838
3839 def _formatTime(self, scheduledTime, realTimestamp, (warning, error)):
3840 """Format the departure or arrival time based on the given data as a
3841 markup for a label."""
3842 realTime = time.gmtime(realTimestamp)
3843
3844 if warning:
3845 colour = "red" if error else "orange"
3846 markupBegin = '<span foreground="%s">' % (colour,)
3847 markupEnd = '</span>'
3848 else:
3849 markupBegin = markupEnd = ""
3850
3851 markup = "<b>%s%02d:%02d [%02d:%02d]%s</b>" % \
3852 (markupBegin,
3853 realTime.tm_hour, realTime.tm_min,
3854 scheduledTime.hour, scheduledTime.minute,
3855 markupEnd)
3856
3857 return markup
3858
3859 def _updateTimes(self):
3860 """Format the flight times and the help text according to the flight
3861 type.
3862
3863 The buttons are also updated.
3864 """
3865 flight = self._wizard.gui._flight
3866 bookedFlight = flight.bookedFlight
3867
3868 (departureWarning, departureError) = flight.blockTimeStartWrong
3869 (arrivalWarning, arrivalError) = flight.blockTimeEndWrong
3870
3871 if self.flightType==const.FLIGHTTYPE_VIP:
3872 departureError = arrivalError = False
3873
3874 self._tooBigTimeDifference = departureError or arrivalError
3875
3876 self._depTime.set_markup(self._formatTime(bookedFlight.departureTime,
3877 flight.blockTimeStart,
3878 (departureWarning,
3879 departureError)))
3880
3881 self._arrTime.set_markup(self._formatTime(bookedFlight.arrivalTime,
3882 flight.blockTimeEnd,
3883 (arrivalWarning,
3884 arrivalError)))
3885
3886 self.updateButtons()
3887
3888 def _updateHelp(self, faultsExplained, timesCorrect):
3889 """Update the help text according to the actual situation."""
3890 if not faultsExplained:
3891 self.setHelp(xstr("finish_help") + xstr("finish_help_faults"))
3892 elif not timesCorrect:
3893 self.setHelp(xstr("finish_help") + xstr("finish_help_wrongtime"))
3894 else:
3895 self.setHelp(xstr("finish_help") + xstr("finish_help_goodtime"))
3896
3897
3898#-----------------------------------------------------------------------------
3899
3900class Wizard(gtk.VBox):
3901 """The flight wizard."""
3902 def __init__(self, gui):
3903 """Construct the wizard."""
3904 super(Wizard, self).__init__()
3905
3906 self.gui = gui
3907
3908 self._pages = []
3909 self._currentPage = None
3910
3911 self._loginPage = LoginPage(self)
3912 self._pages.append(self._loginPage)
3913 self._pages.append(FlightSelectionPage(self))
3914 self._pages.append(GateSelectionPage(self))
3915 self._pages.append(ConnectPage(self))
3916 self._payloadPage = PayloadPage(self)
3917 self._pages.append(self._payloadPage)
3918 self._payloadIndex = len(self._pages)
3919 self._pages.append(TimePage(self))
3920 self._routePage = RoutePage(self)
3921 self._pages.append(self._routePage)
3922 self._simBriefSetupPage = SimBriefSetupPage(self)
3923 self._pages.append(self._simBriefSetupPage)
3924 self._simBriefingPage = SimBriefingPage(self)
3925 self._pages.append(self._simBriefingPage)
3926 self._pages.append(FuelPage(self))
3927 self._departureBriefingPage = BriefingPage(self, True)
3928 self._pages.append(self._departureBriefingPage)
3929 self._arrivalBriefingPage = BriefingPage(self, False)
3930 self._pages.append(self._arrivalBriefingPage)
3931 self._arrivalBriefingIndex = len(self._pages)
3932 self._takeoffPage = TakeoffPage(self)
3933 self._pages.append(self._takeoffPage)
3934 self._cruisePage = CruisePage(self)
3935 self._pages.append(self._cruisePage)
3936 self._landingPage = LandingPage(self)
3937 self._pages.append(self._landingPage)
3938 self._finishPage = FinishPage(self)
3939 self._pages.append(self._finishPage)
3940
3941 self._requestedWidth = None
3942 self._requestedHeight = None
3943
3944 self.connect("size-allocate", self._sizeAllocate)
3945
3946 for page in self._pages:
3947 page.show_all()
3948 page.setStyle()
3949
3950 self._initialize()
3951
3952 def _sizeAllocate(self, widget, allocation):
3953 if self._requestedWidth is not None and \
3954 self._requestedHeight is not None:
3955 return
3956
3957 if self._currentPage is not None:
3958 self.remove(self._pages[self._currentPage])
3959
3960 maxWidth = 0
3961 maxHeight = 0
3962 for page in self._pages:
3963 self.add(page)
3964 self.show_all()
3965 pageSizeRequest = page.size_request()
3966 width = pageSizeRequest.width if pygobject else pageSizeRequest[0]
3967 height = pageSizeRequest.height if pygobject else pageSizeRequest[1]
3968 maxWidth = max(maxWidth, width)
3969 maxHeight = max(maxHeight, height)
3970 self.remove(page)
3971
3972 if self._currentPage is not None:
3973 self.add(self._pages[self._currentPage])
3974
3975 self._requestedWidth = maxWidth
3976 self._requestedHeight = maxHeight
3977 self.set_size_request(maxWidth, maxHeight)
3978
3979 @property
3980 def pilotID(self):
3981 """Get the pilot ID, if given."""
3982 return self._loginPage.pilotID
3983
3984 @property
3985 def entranceExam(self):
3986 """Get whether an entrance exam is about to be taken."""
3987 return self._loginPage.entranceExam
3988
3989 @property
3990 def loggedIn(self):
3991 """Indicate if there was a successful login."""
3992 return self._loginResult is not None
3993
3994 @property
3995 def loginResult(self):
3996 """Get the login result."""
3997 return self._loginResult
3998
3999 def setCurrentPage(self, index, finalize = False, fromPageShift = None):
4000 """Set the current page to the one with the given index.
4001
4002 @param fromPageShift if given, the relative index of one of the
4003 previous pages that should be used as the from-page of the next
4004 page. E.g. if fromPageShift is 1, the previous page will be the
4005 from-page."""
4006 assert index < len(self._pages)
4007
4008 fromPage = self._currentPage
4009 if fromPage is not None:
4010 page = self._pages[fromPage]
4011 if finalize and not page._completed:
4012 page.complete()
4013 self.remove(page)
4014 if fromPageShift is not None:
4015 fromPage -= fromPageShift
4016
4017 self._currentPage = index
4018 page = self._pages[index]
4019 self.add(page)
4020 if page._fromPage is None:
4021 page._fromPage = fromPage
4022 page.initialize()
4023 self.show_all()
4024 if fromPage is not None:
4025 self.grabDefault()
4026
4027 @property
4028 def bookedFlight(self):
4029 """Get the booked flight selected."""
4030 return self._bookedFlight
4031
4032 @property
4033 def numCrew(self):
4034 """Get the number of crew members."""
4035 return self._payloadPage.numCrew
4036
4037 @property
4038 def numPassengers(self):
4039 """Get the number of passengers."""
4040 return self._payloadPage.numPassengers
4041
4042 @property
4043 def bagWeight(self):
4044 """Get the baggage weight."""
4045 return self._payloadPage.bagWeight
4046
4047 @property
4048 def cargoWeight(self):
4049 """Get the cargo weight."""
4050 return self._payloadPage.cargoWeight
4051
4052 @property
4053 def mailWeight(self):
4054 """Get the mail weight."""
4055 return self._payloadPage.mailWeight
4056
4057 @property
4058 def zfw(self):
4059 """Get the calculated ZFW value."""
4060 return 0 if self._bookedFlight is None \
4061 else self._payloadPage.calculateZFW()
4062
4063 @property
4064 def filedCruiseLevel(self):
4065 """Get the filed cruise level."""
4066 return self._routePage.filedCruiseLevel
4067
4068 @property
4069 def filedCruiseAltitude(self):
4070 """Get the filed cruise altitude."""
4071 return self._routePage.filedCruiseLevel * 100
4072
4073 @property
4074 def cruiseAltitude(self):
4075 """Get the cruise altitude."""
4076 level = self._cruisePage.cruiseLevel if self._cruisePage.activated \
4077 else self._routePage.filedCruiseLevel
4078 return level * 100
4079
4080 @property
4081 def loggableCruiseAltitude(self):
4082 """Get the cruise altitude that can be logged."""
4083 if self._cruisePage.activated:
4084 return self._cruisePage.loggableCruiseLevel * 100
4085 else:
4086 return 0
4087
4088 @property
4089 def route(self):
4090 """Get the route."""
4091 return self._routePage.route
4092
4093 @property
4094 def alternate(self):
4095 """Get the ICAO code of the alternate airport."""
4096 return self._routePage.alternate
4097
4098 @property
4099 def departureMETAR(self):
4100 """Get the METAR of the departure airport."""
4101 return self._departureBriefingPage.metar
4102
4103 @property
4104 def arrivalMETAR(self):
4105 """Get the METAR of the arrival airport."""
4106 return self._arrivalBriefingPage.metar
4107
4108 @property
4109 def departureRunway(self):
4110 """Get the departure runway."""
4111 return self._takeoffPage.runway
4112
4113 @property
4114 def sid(self):
4115 """Get the SID."""
4116 return self._takeoffPage.sid
4117
4118 @property
4119 def v1(self):
4120 """Get the V1 speed."""
4121 return self._takeoffPage.v1
4122
4123 @property
4124 def vr(self):
4125 """Get the Vr speed."""
4126 return self._takeoffPage.vr
4127
4128 @property
4129 def v2(self):
4130 """Get the V2 speed."""
4131 return self._takeoffPage.v2
4132
4133 @property
4134 def derate(self):
4135 """Get the derate value."""
4136 return self._takeoffPage.derate
4137
4138 @property
4139 def takeoffAntiIceOn(self):
4140 """Get whether the anti-ice system was on during take-off."""
4141 return self._takeoffPage.antiIceOn
4142
4143 @takeoffAntiIceOn.setter
4144 def takeoffAntiIceOn(self, value):
4145 """Set anti-ice on indicator."""
4146 self._takeoffPage.antiIceOn = value
4147
4148 @property
4149 def rtoIndicated(self):
4150 """Get whether the pilot has indicated that an RTO has occured."""
4151 return self._takeoffPage.rtoIndicated
4152
4153 @property
4154 def arrivalRunway(self):
4155 """Get the arrival runway."""
4156 return self._landingPage.runway
4157
4158 @property
4159 def star(self):
4160 """Get the STAR."""
4161 return self._landingPage.star
4162
4163 @property
4164 def transition(self):
4165 """Get the transition."""
4166 return self._landingPage.transition
4167
4168 @property
4169 def approachType(self):
4170 """Get the approach type."""
4171 return self._landingPage.approachType
4172
4173 @property
4174 def vref(self):
4175 """Get the Vref speed."""
4176 return self._landingPage.vref
4177
4178 @property
4179 def landingAntiIceOn(self):
4180 """Get whether the anti-ice system was on during landing."""
4181 return self._landingPage.antiIceOn
4182
4183 @landingAntiIceOn.setter
4184 def landingAntiIceOn(self, value):
4185 """Set anti-ice on indicator."""
4186 self._landingPage.antiIceOn = value
4187
4188 @property
4189 def flightType(self):
4190 """Get the flight type."""
4191 return self._finishPage.flightType
4192
4193 @property
4194 def online(self):
4195 """Get whether the flight was online or not."""
4196 return self._finishPage.online
4197
4198 @property
4199 def usingSimBrief(self):
4200 """Indicate if we are using a SimBrief briefing or not."""
4201 return self._usingSimBrief
4202
4203 @usingSimBrief.setter
4204 def usingSimBrief(self, x):
4205 """Set whether we are using a SimBrief briefing or not."""
4206 self._usingSimBrief = x
4207
4208 def nextPage(self, finalize = True):
4209 """Go to the next page."""
4210 self.jumpPage(1, finalize)
4211
4212 def jumpPage(self, count, finalize = True, fromPageShift = None):
4213 """Go to the page which is 'count' pages after the current one."""
4214 self.setCurrentPage(self._currentPage + count,
4215 finalize = finalize, fromPageShift = fromPageShift)
4216
4217 def grabDefault(self):
4218 """Make the default button of the current page the default."""
4219 self._pages[self._currentPage].grabDefault()
4220
4221 def connected(self, fsType, descriptor):
4222 """Called when the connection could be made to the simulator."""
4223 self.nextPage()
4224
4225 def reset(self, loginResult):
4226 """Resets the wizard to go back to the login page."""
4227 self._initialize(keepLoginResult = loginResult is None,
4228 loginResult = loginResult)
4229
4230 def setStage(self, stage):
4231 """Set the flight stage to the given one."""
4232 if stage!=const.STAGE_END:
4233 self._cruisePage.setLoggable(Flight.canLogCruiseAltitude(stage))
4234
4235 if stage==const.STAGE_TAKEOFF:
4236 self._takeoffPage.allowForward()
4237 elif stage==const.STAGE_LANDING:
4238 if not self._arrivalBriefingPage.metarEdited:
4239 print "Downloading arrival METAR again"
4240 self.gui.webHandler.getMETARs(self._arrivalMETARCallback,
4241 [self._bookedFlight.arrivalICAO])
4242
4243 elif stage==const.STAGE_END:
4244 for page in self._pages:
4245 page.flightEnded()
4246
4247 def _initialize(self, keepLoginResult = False, loginResult = None):
4248 """Initialize the wizard."""
4249 if not keepLoginResult:
4250 self._loginResult = loginResult
4251
4252 self._loginCallback = None
4253
4254 self._fleet = None
4255 self._fleetCallback = None
4256
4257 self._bookedFlight = None
4258 self._departureGate = "-"
4259 self._fuelData = None
4260 self._departureNOTAMs = None
4261 self._departureMETAR = None
4262 self._arrivalNOTAMs = None
4263 self._arrivalMETAR = None
4264 self._usingSimBrief = None
4265
4266 firstPage = 0 if self._loginResult is None else 1
4267 for page in self._pages[firstPage:]:
4268 page.reset()
4269
4270 self.setCurrentPage(firstPage)
4271 #self.setCurrentPage(10)
4272
4273 def login(self, callback, pilotID, password, entranceExam):
4274 """Called when the login button was clicked."""
4275 self._loginCallback = callback
4276 if pilotID is None:
4277 loginResult = self._loginResult
4278 assert loginResult is not None and loginResult.loggedIn
4279 pilotID = loginResult.pilotID
4280 password = loginResult.password
4281 entranceExam = loginResult.entranceExam
4282 busyMessage = xstr("reload_busy")
4283 else:
4284 self._loginResult = None
4285 busyMessage = xstr("login_busy")
4286
4287 self.gui.beginBusy(busyMessage)
4288
4289 self.gui.webHandler.login(self._loginResultCallback,
4290 pilotID, password,
4291 entranceExam = entranceExam)
4292
4293 def reloadFlights(self, callback):
4294 """Reload the flights from the MAVA server."""
4295 self.login(callback, None, None, None)
4296
4297 def cruiseLevelChanged(self):
4298 """Called when the cruise level is changed."""
4299 return self.gui.cruiseLevelChanged()
4300
4301 def metarChanged(self, metar, originator):
4302 """Called when a METER is changed on on of the pages.
4303
4304 originator is the page that originated the changed. It will be used to
4305 determine which METAR (departure or arrival) has changed."""
4306 metar = metar.upper()
4307 if originator in [self._departureBriefingPage, self._takeoffPage]:
4308 self._departureMETARChanged(metar, originator)
4309 else:
4310 self._arrivalMETARChanged(metar, originator)
4311
4312 def _departureMETARChanged(self, metar, originator):
4313 """Called when the departure METAR has been edited on one of the
4314 pages.
4315
4316 originator is the page that originated the change. It will not be
4317 called to set the METAR, while others will be."""
4318 for page in [self._departureBriefingPage, self._takeoffPage]:
4319 if page is not originator:
4320 page.changeMETAR(metar)
4321
4322 def _arrivalMETARChanged(self, metar, originator):
4323 """Called when the arrival METAR has been edited on one of the
4324 pages.
4325
4326 originator is the page that originated the change. It will not be
4327 called to set the METAR, while others will be."""
4328 for page in [self._arrivalBriefingPage, self._landingPage]:
4329 if page is not originator:
4330 page.changeMETAR(metar)
4331
4332 def _loginResultCallback(self, returned, result):
4333 """The login result callback, called in the web handler's thread."""
4334 gobject.idle_add(self._handleLoginResult, returned, result)
4335
4336 def _handleLoginResult(self, returned, result):
4337 """Handle the login result."""
4338 self.gui.endBusy()
4339 returned = True
4340 isReload = self._loginResult is not None
4341 if returned:
4342 if result.loggedIn:
4343 self._loginResult = result
4344 else:
4345 if isReload:
4346 message = xstr("reload_failed")
4347 else:
4348 message = xstr("login_entranceExam_invalid"
4349 if self.entranceExam else
4350 xstr("login_invalid"))
4351 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
4352 type = MESSAGETYPE_ERROR,
4353 message_format = message)
4354 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
4355 dialog.set_title(WINDOW_TITLE_BASE)
4356 if isReload:
4357 secondary = xstr("reload_failed_sec")
4358 else:
4359 secondary = xstr("login_entranceExam_invalid_sec"
4360 if self.entranceExam else
4361 xstr("login_invalid_sec"))
4362 dialog.format_secondary_markup(secondary)
4363 dialog.run()
4364 dialog.hide()
4365 else:
4366 message = xstr("reload_failconn") if isReload \
4367 else xstr("login_failconn")
4368 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
4369 type = MESSAGETYPE_ERROR,
4370 message_format = message)
4371 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
4372 dialog.set_title(WINDOW_TITLE_BASE)
4373 secondary = xstr("reload_failconn_sec") if isReload \
4374 else xstr("login_failconn_sec")
4375 dialog.format_secondary_markup(secondary)
4376
4377 dialog.run()
4378 dialog.hide()
4379
4380 callback = self._loginCallback
4381 self._loginCallback = None
4382 callback(returned, result)
4383
4384 def getFleet(self, callback, force = False):
4385 """Get the fleet via the GUI and call the given callback."""
4386 self._fleetCallback = callback
4387 self.gui.getFleet(callback = self._fleetRetrieved, force = force)
4388
4389 def _fleetRetrieved(self, fleet):
4390 """Callback for the fleet retrieval."""
4391 self._fleet = fleet
4392 if self._fleetCallback is not None:
4393 self._fleetCallback(fleet)
4394 self._fleetCallback = None
4395
4396 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
4397 """Update the given plane's gate information."""
4398 self.gui.updatePlane(tailNumber, status, gateNumber = gateNumber,
4399 callback = callback)
4400
4401 def updateRTO(self):
4402 """Update the RTO state.
4403
4404 The RTO checkbox will be enabled if the flight has an RTO state and the
4405 comments field contains some text."""
4406 flight = self.gui.flight
4407 rtoEnabled = flight is not None and flight.hasRTO and \
4408 self.gui.hasComments
4409 self._takeoffPage.setRTOEnabled(rtoEnabled)
4410
4411 def commentsChanged(self):
4412 """Called when the comments have changed."""
4413 self.updateRTO()
4414 self._finishPage.updateButtons()
4415
4416 def delayCodesChanged(self):
4417 """Called when the delay codes have changed."""
4418 self._finishPage.updateButtons()
4419
4420 def faultExplanationsChanged(self):
4421 """Called when the faults and their explanations have changed."""
4422 self._finishPage.updateButtons()
4423
4424 def rtoToggled(self, indicated):
4425 """Called when the RTO indication has changed."""
4426 self.gui.rtoToggled(indicated)
4427
4428 def _connectSimulator(self, simulatorType):
4429 """Connect to the simulator."""
4430 self.gui.connectSimulator(self._bookedFlight.aircraftType,
4431 simulatorType)
4432
4433 def _arrivalMETARCallback(self, returned, result):
4434 """Called when the METAR of the arrival airport is retrieved."""
4435 gobject.idle_add(self._handleArrivalMETAR, returned, result)
4436
4437 def _handleArrivalMETAR(self, returned, result):
4438 """Called when the METAR of the arrival airport is retrieved."""
4439 icao = self._bookedFlight.arrivalICAO
4440 if returned and icao in result.metars:
4441 metar = result.metars[icao]
4442 if metar!="":
4443 self._arrivalBriefingPage.setMETAR(metar)
4444
4445#-----------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.