source: src/mlx/gui/flight.py@ 692:eea9d6135944

cef
Last change on this file since 692:eea9d6135944 was 692:eea9d6135944, checked in by István Váradi <ivaradi@…>, 7 years ago

Progress reporting is more detailed and uses translatable strings (re #279)

File size: 165.4 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.setMainWidget(self._alignment)
1864
1865 self.addCancelFlightButton()
1866
1867 self.addPreviousButton(clicked = self._backClicked)
1868
1869 self._button = self.addNextButton(clicked = self._forwardClicked)
1870 self._button.set_label(xstr("briefing_button"))
1871 self._button.set_has_tooltip(False)
1872 self._button.set_use_stock(False)
1873
1874 def activate(self):
1875 """Activate the SimBrief flight plan page"""
1876 container = cef.getContainer()
1877 self._alignment.add(container)
1878
1879 self._browser = \
1880 cef.startInContainer(container,
1881 "file://" + SimBriefSetupPage.getHTMLFilePath())
1882
1883 def _backClicked(self, button):
1884 """Called when the Back button has been pressed."""
1885 self.goBack()
1886
1887 def _forwardClicked(self, button):
1888 """Called when the Forward button has been pressed."""
1889 if not self._completed:
1890 self._button.set_label(xstr("button_next"))
1891 self._button.set_tooltip_text(xstr("button_next_tooltip"))
1892 self._wizard.usingSimBrief = True
1893 self.complete()
1894
1895 self._wizard.nextPage()
1896
1897#-----------------------------------------------------------------------------
1898
1899class FuelTank(gtk.VBox):
1900 """Widget for the fuel tank."""
1901 def __init__(self, fuelTank, name, capacity, currentWeight):
1902 """Construct the widget for the tank with the given name."""
1903 super(FuelTank, self).__init__()
1904
1905 self._enabled = True
1906 self.fuelTank = fuelTank
1907 self.capacity = capacity
1908 self.currentWeight = currentWeight
1909 self.expectedWeight = currentWeight
1910
1911 label = gtk.Label("<b>" + name + "</b>")
1912 label.set_use_markup(True)
1913 label.set_use_underline(True)
1914 label.set_justify(JUSTIFY_CENTER)
1915 label.set_alignment(0.5, 1.0)
1916 self.pack_start(label, False, False, 4)
1917
1918 self._tankFigure = gtk.EventBox()
1919 self._tankFigure.set_size_request(38, -1)
1920 self._tankFigure.set_visible_window(False)
1921 self._tankFigure.set_tooltip_markup(xstr("fuel_tank_tooltip"))
1922
1923 if pygobject:
1924 self._tankFigure.connect("draw", self._drawTankFigure)
1925 else:
1926 self._tankFigure.connect("expose_event", self._drawTankFigure)
1927 self._tankFigure.connect("button_press_event", self._buttonPressed)
1928 self._tankFigure.connect("motion_notify_event", self._motionNotify)
1929 self._tankFigure.connect("scroll-event", self._scrolled)
1930
1931 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1932 xscale = 0.0, yscale = 1.0)
1933 alignment.add(self._tankFigure)
1934
1935 self.pack_start(alignment, True, True, 4)
1936
1937 self._expectedButton = gtk.SpinButton()
1938 self._expectedButton.set_numeric(True)
1939 self._expectedButton.set_range(0, self.capacity)
1940 self._expectedButton.set_increments(10, 100)
1941 self._expectedButton.set_value(currentWeight)
1942 self._expectedButton.set_alignment(1.0)
1943 self._expectedButton.set_width_chars(5)
1944 self._expectedButton.connect("value-changed", self._expectedChanged)
1945
1946 label.set_mnemonic_widget(self._expectedButton)
1947
1948 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1949 xscale = 0.0, yscale = 1.0)
1950 alignment.add(self._expectedButton)
1951 self.pack_start(alignment, False, False, 4)
1952
1953 def setCurrent(self, currentWeight):
1954 """Set the current weight."""
1955 self.currentWeight = currentWeight
1956 self._redraw()
1957
1958 def isCorrect(self):
1959 """Determine if the contents of the fuel tank are as expected"""
1960 return abs(self.expectedWeight - self.currentWeight)<=1
1961
1962 def disable(self):
1963 """Disable the fuel tank."""
1964 self._expectedButton.set_sensitive(False)
1965 self._enabled = False
1966
1967 def _redraw(self):
1968 """Redraw the tank figure."""
1969 self._tankFigure.queue_draw()
1970
1971 def _drawTankFigure(self, tankFigure, eventOrContext):
1972 """Draw the tank figure."""
1973 triangleSize = 5
1974
1975 context = eventOrContext if pygobject else tankFigure.window.cairo_create()
1976 (xOffset, yOffset) = (0, 0) if pygobject \
1977 else (tankFigure.allocation.x, tankFigure.allocation.y)
1978
1979 width = tankFigure.get_allocated_width() if pygobject \
1980 else tankFigure.allocation.width
1981 height = tankFigure.get_allocated_height() if pygobject \
1982 else tankFigure.allocation.height
1983
1984 rectangleX0 = triangleSize
1985 rectangleY0 = triangleSize
1986 rectangleX1 = width - 1 - triangleSize
1987 rectangleY1 = height - 1 - triangleSize
1988 rectangleLineWidth = 2.0
1989
1990 context.set_source_rgb(0.0, 0.0, 0.0)
1991 context.set_line_width(rectangleLineWidth)
1992 context.rectangle(xOffset + rectangleX0 + rectangleLineWidth/2,
1993 yOffset + rectangleY0 + rectangleLineWidth/2,
1994 rectangleX1 - rectangleX0 - rectangleLineWidth,
1995 rectangleY1 - rectangleY0 - rectangleLineWidth)
1996 context.stroke()
1997
1998 rectangleInnerLeft = rectangleX0 + rectangleLineWidth
1999 rectangleInnerRight = rectangleX1 - rectangleLineWidth
2000 self._rectangleInnerTop = rectangleInnerTop = rectangleY0 + rectangleLineWidth
2001 self._rectangleInnerBottom = rectangleInnerBottom = rectangleY1 - rectangleLineWidth
2002
2003 rectangleInnerWidth = rectangleInnerRight - rectangleInnerLeft
2004 rectangleInnerHeight = rectangleInnerBottom - rectangleInnerTop
2005
2006 context.set_source_rgb(1.0, 0.9, 0.6)
2007 currentHeight = self.currentWeight * rectangleInnerHeight / self.capacity
2008 currentX = rectangleInnerTop + rectangleInnerHeight - currentHeight
2009 context.rectangle(xOffset + rectangleInnerLeft,
2010 yOffset + rectangleInnerTop +
2011 rectangleInnerHeight - currentHeight,
2012 rectangleInnerWidth, currentHeight)
2013 context.fill()
2014
2015 expectedHeight = self.expectedWeight * rectangleInnerHeight / self.capacity
2016 expectedY = rectangleInnerTop + rectangleInnerHeight - expectedHeight
2017
2018 context.set_line_width(1.5)
2019 context.set_source_rgb(0.0, 0.85, 0.85)
2020 context.move_to(xOffset + rectangleX0, yOffset + expectedY)
2021 context.line_to(xOffset + rectangleX1, yOffset + expectedY)
2022 context.stroke()
2023
2024 context.set_line_width(0.0)
2025 context.move_to(xOffset + 0, yOffset + expectedY - triangleSize)
2026 context.line_to(xOffset + 0, yOffset + expectedY + triangleSize)
2027 context.line_to(xOffset + rectangleX0 + 1, yOffset + expectedY)
2028 context.line_to(xOffset + 0, yOffset + expectedY - triangleSize)
2029 context.fill()
2030
2031 context.set_line_width(0.0)
2032 context.move_to(xOffset + width, yOffset + expectedY - triangleSize)
2033 context.line_to(xOffset + width, yOffset + expectedY + triangleSize)
2034 context.line_to(xOffset + rectangleX1 - 1, yOffset + expectedY)
2035 context.line_to(xOffset + width, yOffset + expectedY - triangleSize)
2036 context.fill()
2037
2038 return True
2039
2040 def _setExpectedFromY(self, y):
2041 """Set the expected weight from the given Y-coordinate."""
2042 level = (self._rectangleInnerBottom - y) / \
2043 (self._rectangleInnerBottom - self._rectangleInnerTop)
2044 level = min(1.0, max(0.0, level))
2045 self._expectedButton.set_value(level * self.capacity)
2046
2047 def _buttonPressed(self, tankFigure, event):
2048 """Called when a button is pressed in the figure.
2049
2050 The expected level will be set there."""
2051 if self._enabled and event.button==1:
2052 self._setExpectedFromY(event.y)
2053
2054 def _motionNotify(self, tankFigure, event):
2055 """Called when the mouse pointer moves within the area of a tank figure."""
2056 if self._enabled and event.state==BUTTON1_MASK:
2057 self._setExpectedFromY(event.y)
2058
2059 def _scrolled(self, tankFigure, event):
2060 """Called when a scroll event is received."""
2061 if self._enabled:
2062 increment = 1 if event.state==CONTROL_MASK \
2063 else 100 if event.state==SHIFT_MASK \
2064 else 10 if event.state==0 else 0
2065 if increment!=0:
2066 if event.direction==SCROLL_DOWN:
2067 increment *= -1
2068 self._expectedButton.spin(SPIN_USER_DEFINED, increment)
2069
2070 def _expectedChanged(self, spinButton):
2071 """Called when the expected value has changed."""
2072 self.expectedWeight = spinButton.get_value_as_int()
2073 self._redraw()
2074
2075#-----------------------------------------------------------------------------
2076
2077class FuelPage(Page):
2078 """The page containing the fuel tank filling."""
2079 _pumpStep = 0.02
2080
2081 def __init__(self, wizard):
2082 """Construct the page."""
2083 super(FuelPage, self).__init__(wizard, xstr("fuel_title"),
2084 xstr("fuel_help_pre") +
2085 xstr("fuel_help_post"),
2086 completedHelp = xstr("fuel_chelp"))
2087
2088 self._fuelTanks = []
2089 self._fuelTable = None
2090 self._fuelAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2091 xscale = 0.0, yscale = 1.0)
2092 self.setMainWidget(self._fuelAlignment)
2093
2094 tankData = [(tank, 2500, 3900) for tank in acft.mostFuelTanks]
2095 self._setupTanks(tankData)
2096
2097 self.addCancelFlightButton()
2098
2099 self._backButton = self.addPreviousButton(clicked = self._backClicked)
2100 self._button = self.addNextButton(clicked = self._forwardClicked)
2101
2102 self._pumpIndex = 0
2103
2104 def activate(self):
2105 """Activate the page."""
2106 self._setupTanks(self._wizard._fuelData)
2107
2108 aircraft = self._wizard.gui.flight.aircraft
2109 minLandingFuel = aircraft.minLandingFuel
2110 recommendedLandingFuel = aircraft.recommendedLandingFuel
2111
2112 middleHelp = "" if minLandingFuel is None else \
2113 (xstr("fuel_help_min") % (minLandingFuel,)) \
2114 if recommendedLandingFuel is None else \
2115 (xstr("fuel_help_min_rec") % (minLandingFuel,
2116 recommendedLandingFuel))
2117 self.setHelp(xstr("fuel_help_pre") + middleHelp + xstr("fuel_help_post"))
2118
2119 def finalize(self):
2120 """Finalize the page."""
2121 for fuelTank in self._fuelTanks:
2122 fuelTank.disable()
2123
2124 def _backClicked(self, button):
2125 """Called when the Back button is pressed."""
2126 self.goBack()
2127
2128 def _forwardClicked(self, button):
2129 """Called when the forward button is clicked."""
2130 if not self._completed:
2131 self._pumpIndex = 0
2132 self._wizard.gui.beginBusy(xstr("fuel_pump_busy"))
2133 self._pump()
2134 elif self._wizard.usingSimBrief:
2135 self._wizard.jumpPage(3)
2136 else:
2137 self._wizard.nextPage()
2138
2139 def _setupTanks(self, tankData):
2140 """Setup the tanks for the given data."""
2141 numTanks = len(tankData)
2142 if self._fuelTable is not None:
2143 self._fuelAlignment.remove(self._fuelTable)
2144
2145 self._fuelTanks = []
2146 self._fuelTable = gtk.Table(numTanks, 1)
2147 self._fuelTable.set_col_spacings(16)
2148 index = 0
2149 for (tank, current, capacity) in tankData:
2150 fuelTank = FuelTank(tank,
2151 xstr("fuel_tank_" +
2152 const.fuelTank2string(tank)),
2153 capacity, current)
2154 self._fuelTable.attach(fuelTank, index, index+1, 0, 1)
2155 self._fuelTanks.append(fuelTank)
2156 index += 1
2157
2158 self._fuelAlignment.add(self._fuelTable)
2159 self.show_all()
2160
2161 def _pump(self):
2162 """Perform one step of pumping.
2163
2164 It is checked, if the current tank's contents are of the right
2165 quantity. If not, it is filled one step further to the desired
2166 contents. Otherwise the next tank is started. If all tanks are are
2167 filled, the next page is selected."""
2168 numTanks = len(self._fuelTanks)
2169
2170 fuelTank = None
2171 while self._pumpIndex < numTanks:
2172 fuelTank = self._fuelTanks[self._pumpIndex]
2173 if fuelTank.isCorrect():
2174 self._pumpIndex += 1
2175 fuelTank = None
2176 else:
2177 break
2178
2179 if fuelTank is None:
2180 self._wizard.gui.endBusy()
2181 if self._wizard.usingSimBrief:
2182 self._wizard.gui.startMonitoring()
2183 self._wizard.jumpPage(3)
2184 else:
2185 bookedFlight = self._wizard._bookedFlight
2186 self._wizard.gui.beginBusy(xstr("route_down_notams"))
2187 self._wizard.gui.webHandler.getNOTAMs(self._notamsCallback,
2188 bookedFlight.departureICAO,
2189 bookedFlight.arrivalICAO)
2190 startSound(const.SOUND_NOTAM)
2191 else:
2192 currentLevel = fuelTank.currentWeight / fuelTank.capacity
2193 expectedLevel = fuelTank.expectedWeight / fuelTank.capacity
2194 if currentLevel<expectedLevel:
2195 currentLevel += FuelPage._pumpStep
2196 if currentLevel>expectedLevel: currentLevel = expectedLevel
2197 else:
2198 currentLevel -= FuelPage._pumpStep
2199 if currentLevel<expectedLevel: currentLevel = expectedLevel
2200 fuelTank.setCurrent(currentLevel * fuelTank.capacity)
2201 self._wizard.gui.simulator.setFuelLevel([(fuelTank.fuelTank,
2202 currentLevel)])
2203 gobject.timeout_add(50, self._pump)
2204
2205 def _notamsCallback(self, returned, result):
2206 """Callback for the NOTAMs."""
2207 gobject.idle_add(self._handleNOTAMs, returned, result)
2208
2209 def _handleNOTAMs(self, returned, result):
2210 """Handle the NOTAMs."""
2211 if returned:
2212 self._wizard._departureNOTAMs = result.departureNOTAMs
2213 self._wizard._arrivalNOTAMs = result.arrivalNOTAMs
2214 else:
2215 self._wizard._departureNOTAMs = None
2216 self._wizard._arrivalNOTAMs = None
2217
2218 bookedFlight = self._wizard._bookedFlight
2219 self._wizard.gui.beginBusy(xstr("route_down_metars"))
2220 self._wizard.gui.webHandler.getMETARs(self._metarsCallback,
2221 [bookedFlight.departureICAO,
2222 bookedFlight.arrivalICAO])
2223
2224 def _metarsCallback(self, returned, result):
2225 """Callback for the METARs."""
2226 gobject.idle_add(self._handleMETARs, returned, result)
2227
2228 def _handleMETARs(self, returned, result):
2229 """Handle the METARs."""
2230 self._wizard._departureMETAR = None
2231 self._wizard._arrivalMETAR = None
2232 bookedFlight = self._wizard._bookedFlight
2233 if returned:
2234 if bookedFlight.departureICAO in result.metars:
2235 self._wizard._departureMETAR = result.metars[bookedFlight.departureICAO]
2236 if bookedFlight.arrivalICAO in result.metars:
2237 self._wizard._arrivalMETAR = result.metars[bookedFlight.arrivalICAO]
2238
2239 self._wizard.gui.endBusy()
2240 self._backButton.set_sensitive(True)
2241 self._button.set_sensitive(True)
2242 self._wizard.nextPage()
2243
2244#-----------------------------------------------------------------------------
2245
2246class BriefingPage(Page):
2247 """Page for the briefing."""
2248 def __init__(self, wizard, departure):
2249 """Construct the briefing page."""
2250 self._departure = departure
2251
2252 title = xstr("briefing_title") % (1 if departure else 2,
2253 xstr("briefing_departure")
2254 if departure
2255 else xstr("briefing_arrival"))
2256 super(BriefingPage, self).__init__(wizard, title, xstr("briefing_help"),
2257 completedHelp = xstr("briefing_chelp"))
2258
2259 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2260 xscale = 1.0, yscale = 1.0)
2261
2262 mainBox = gtk.VBox()
2263 alignment.add(mainBox)
2264 self.setMainWidget(alignment)
2265
2266 self._notamsFrame = gtk.Frame()
2267 self._notamsFrame.set_label(xstr("briefing_notams_init"))
2268 scrolledWindow = gtk.ScrolledWindow()
2269 scrolledWindow.set_size_request(-1, 128)
2270 # FIXME: these constants should be in common
2271 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
2272 else gtk.POLICY_AUTOMATIC,
2273 gtk.PolicyType.AUTOMATIC if pygobject
2274 else gtk.POLICY_AUTOMATIC)
2275 self._notams = gtk.TextView()
2276 self._notams.set_editable(False)
2277 self._notams.set_accepts_tab(False)
2278 self._notams.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
2279 scrolledWindow.add(self._notams)
2280 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
2281 xscale = 1.0, yscale = 1.0)
2282 alignment.set_padding(padding_top = 4, padding_bottom = 0,
2283 padding_left = 0, padding_right = 0)
2284 alignment.add(scrolledWindow)
2285 self._notamsFrame.add(alignment)
2286 mainBox.pack_start(self._notamsFrame, True, True, 4)
2287
2288 self._metarFrame = gtk.Frame()
2289 self._metarFrame.set_label(xstr("briefing_metar_init"))
2290 scrolledWindow = gtk.ScrolledWindow()
2291 scrolledWindow.set_size_request(-1, 32)
2292 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
2293 else gtk.POLICY_AUTOMATIC,
2294 gtk.PolicyType.AUTOMATIC if pygobject
2295 else gtk.POLICY_AUTOMATIC)
2296
2297 self._updatingMETAR = False
2298
2299 self._metar = gtk.TextView()
2300 self._metar.set_accepts_tab(False)
2301 self._metar.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
2302 self._metar.get_buffer().connect("changed", self._metarChanged)
2303 self._metar.get_buffer().connect_after("insert-text", self._metarInserted)
2304 scrolledWindow.add(self._metar)
2305 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
2306 xscale = 1.0, yscale = 1.0)
2307 alignment.set_padding(padding_top = 4, padding_bottom = 0,
2308 padding_left = 0, padding_right = 0)
2309 alignment.add(scrolledWindow)
2310 self._metarFrame.add(alignment)
2311 mainBox.pack_start(self._metarFrame, True, True, 4)
2312 self.metarEdited = False
2313
2314 self.addCancelFlightButton()
2315
2316 self.addPreviousButton(clicked = self._backClicked)
2317 self._button = self.addNextButton(clicked = self._forwardClicked)
2318
2319 @property
2320 def metar(self):
2321 """Get the METAR on the page."""
2322 buffer = self._metar.get_buffer()
2323 return buffer.get_text(buffer.get_start_iter(),
2324 buffer.get_end_iter(), True)
2325
2326 def setMETAR(self, metar):
2327 """Set the METAR."""
2328 self._metar.get_buffer().set_text(metar)
2329 self.metarEdited = False
2330
2331 def changeMETAR(self, metar):
2332 """Change the METAR as a result of an edit on one of the other
2333 pages."""
2334 self._updatingMETAR = True
2335 self._metar.get_buffer().set_text(metar)
2336 self._updatingMETAR = False
2337
2338 self._updateButton()
2339 self.metarEdited = True
2340
2341 def activate(self):
2342 """Activate the page."""
2343 if not self._departure:
2344 self._button.set_label(xstr("briefing_button"))
2345 self._button.set_has_tooltip(False)
2346 self._button.set_use_stock(False)
2347
2348 bookedFlight = self._wizard._bookedFlight
2349
2350 icao = bookedFlight.departureICAO if self._departure \
2351 else bookedFlight.arrivalICAO
2352 notams = self._wizard._departureNOTAMs if self._departure \
2353 else self._wizard._arrivalNOTAMs
2354 metar = self._wizard._departureMETAR if self._departure \
2355 else self._wizard._arrivalMETAR
2356
2357 self._notamsFrame.set_label(xstr("briefing_notams_template") % (icao,))
2358 buffer = self._notams.get_buffer()
2359 if notams is None:
2360 buffer.set_text(xstr("briefing_notams_failed"))
2361 elif not notams:
2362 buffer.set_text(xstr("briefing_notams_missing"))
2363 else:
2364 s = ""
2365 for notam in notams:
2366 s += str(notam)
2367 s += "-------------------- * --------------------\n"
2368 buffer.set_text(s)
2369
2370 self._metarFrame.set_label(xstr("briefing_metar_template") % (icao,))
2371 buffer = self._metar.get_buffer()
2372 self._updatingMETAR = True
2373 if metar is None:
2374 buffer.set_text("")
2375 self.setHelp(xstr("briefing_help_nometar"))
2376 else:
2377 buffer.set_text(metar)
2378 self._updatingMETAR = False
2379 self._updateButton()
2380
2381 label = self._metarFrame.get_label_widget()
2382 label.set_use_underline(True)
2383 label.set_mnemonic_widget(self._metar)
2384
2385 self.metarEdited = False
2386
2387 def _backClicked(self, button):
2388 """Called when the Back button is pressed."""
2389 self.goBack()
2390
2391 def _forwardClicked(self, button):
2392 """Called when the forward button is clicked."""
2393 if not self._departure:
2394 if not self._completed:
2395 self._wizard.gui.startMonitoring()
2396 self._button.set_label(xstr("button_next"))
2397 self._button.set_tooltip_text(xstr("button_next_tooltip"))
2398 self.complete()
2399
2400 self._wizard.nextPage()
2401
2402 def _metarChanged(self, buffer):
2403 """Called when the METAR has changed."""
2404 print "BriefingPage.metarChanged", self._updatingMETAR
2405 if not self._updatingMETAR:
2406 self.metarEdited = True
2407 self._updateButton()
2408 metar = buffer.get_text(buffer.get_start_iter(),
2409 buffer.get_end_iter(), True)
2410 self._wizard.metarChanged(metar, self)
2411
2412 def _metarInserted(self, textBuffer, iter, text, length):
2413 """Called when new characters are inserted into the METAR.
2414
2415 It uppercases all characters."""
2416 print "BriefingPage.metarInserted", self._updatingMETAR
2417 if not self._updatingMETAR:
2418 self._updatingMETAR = True
2419
2420 iter1 = iter.copy()
2421 iter1.backward_chars(length)
2422 textBuffer.delete(iter, iter1)
2423
2424 textBuffer.insert(iter, text.upper())
2425
2426 self._updatingMETAR = False
2427
2428 def _updateButton(self):
2429 """Update the sensitivity of the Next button based on the contents of
2430 the METAR field."""
2431 buffer = self._metar.get_buffer()
2432 self._button.set_sensitive(buffer.get_text(buffer.get_start_iter(),
2433 buffer.get_end_iter(),
2434 True)!="")
2435
2436
2437#-----------------------------------------------------------------------------
2438
2439class TakeoffPage(Page):
2440 """Page for entering the takeoff data."""
2441 def __init__(self, wizard):
2442 """Construct the takeoff page."""
2443 super(TakeoffPage, self).__init__(wizard, xstr("takeoff_title"),
2444 xstr("takeoff_help"),
2445 completedHelp = xstr("takeoff_chelp"))
2446
2447 self._forwardAllowed = False
2448
2449 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2450 xscale = 0.0, yscale = 0.0)
2451
2452 table = gtk.Table(9, 24)
2453 table.set_row_spacings(4)
2454 table.set_col_spacings(16)
2455 table.set_homogeneous(False)
2456 alignment.add(table)
2457 self.setMainWidget(alignment)
2458
2459 row = 0
2460
2461 label = gtk.Label(xstr("takeoff_metar"))
2462 label.set_use_underline(True)
2463 label.set_alignment(0.0, 0.5)
2464 table.attach(label, 0, 1, row, row+1)
2465
2466 self._metar = gtk.Entry()
2467 self._metar.set_width_chars(40)
2468 self._metar.set_tooltip_text(xstr("takeoff_metar_tooltip"))
2469 self._metar.connect("changed", self._metarChanged)
2470 self._metar.get_buffer().connect_after("inserted-text", self._metarInserted)
2471 table.attach(self._metar, 1, 24, row, row+1)
2472 label.set_mnemonic_widget(self._metar)
2473
2474 self._updatingMETAR = False
2475
2476 row += 1
2477
2478 label = gtk.Label(xstr("takeoff_runway"))
2479 label.set_use_underline(True)
2480 label.set_alignment(0.0, 0.5)
2481 table.attach(label, 0, 1, row, row+1)
2482
2483 self._runway = gtk.Entry()
2484 self._runway.set_width_chars(10)
2485 self._runway.set_tooltip_text(xstr("takeoff_runway_tooltip"))
2486 self._runway.connect("changed", self._upperChanged)
2487 table.attach(self._runway, 1, 3, row, row+1)
2488 label.set_mnemonic_widget(self._runway)
2489
2490 row += 1
2491
2492 label = gtk.Label(xstr("takeoff_sid"))
2493 label.set_use_underline(True)
2494 label.set_alignment(0.0, 0.5)
2495 table.attach(label, 0, 1, row, row+1)
2496
2497 if pygobject:
2498 self._sid = gtk.ComboBox.new_with_model_and_entry(comboModel)
2499 else:
2500 self._sid = gtk.ComboBoxEntry(comboModel)
2501
2502 self._sid.set_entry_text_column(0)
2503 self._sid.get_child().set_width_chars(10)
2504 self._sid.set_tooltip_text(xstr("takeoff_sid_tooltip"))
2505 self._sid.connect("changed", self._upperChangedComboBox)
2506 table.attach(self._sid, 1, 3, row, row+1)
2507 label.set_mnemonic_widget(self._sid)
2508
2509 row += 1
2510
2511 label = gtk.Label(xstr("takeoff_v1"))
2512 label.set_use_markup(True)
2513 label.set_use_underline(True)
2514 label.set_alignment(0.0, 0.5)
2515 table.attach(label, 0, 1, row, row+1)
2516
2517 self._v1 = IntegerEntry()
2518 self._v1.set_width_chars(4)
2519 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip_knots"))
2520 self._v1.connect("integer-changed", self._valueChanged)
2521 table.attach(self._v1, 2, 3, row, row+1)
2522 label.set_mnemonic_widget(self._v1)
2523
2524 self._v1Unit = gtk.Label(xstr("label_knots"))
2525 self._v1Unit.set_alignment(0.0, 0.5)
2526 table.attach(self._v1Unit, 3, 4, row, row+1)
2527
2528 row += 1
2529
2530 label = gtk.Label(xstr("takeoff_vr"))
2531 label.set_use_markup(True)
2532 label.set_use_underline(True)
2533 label.set_alignment(0.0, 0.5)
2534 table.attach(label, 0, 1, row, row+1)
2535
2536 self._vr = IntegerEntry()
2537 self._vr.set_width_chars(4)
2538 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip_knots"))
2539 self._vr.connect("integer-changed", self._valueChanged)
2540 table.attach(self._vr, 2, 3, row, row+1)
2541 label.set_mnemonic_widget(self._vr)
2542
2543 self._vrUnit = gtk.Label(xstr("label_knots"))
2544 self._vrUnit.set_alignment(0.0, 0.5)
2545 table.attach(self._vrUnit, 3, 4, row, row+1)
2546
2547 row += 1
2548
2549 label = gtk.Label(xstr("takeoff_v2"))
2550 label.set_use_markup(True)
2551 label.set_use_underline(True)
2552 label.set_alignment(0.0, 0.5)
2553 table.attach(label, 0, 1, row, row+1)
2554
2555 self._v2 = IntegerEntry()
2556 self._v2.set_width_chars(4)
2557 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip_knots"))
2558 self._v2.connect("integer-changed", self._valueChanged)
2559 table.attach(self._v2, 2, 3, row, row+1)
2560 label.set_mnemonic_widget(self._v2)
2561
2562 self._v2Unit = gtk.Label(xstr("label_knots"))
2563 self._v2Unit.set_alignment(0.0, 0.5)
2564 table.attach(self._v2Unit, 3, 4, row, row+1)
2565
2566 row += 1
2567
2568 self._derateType = acft.DERATE_NONE
2569
2570 self._derateLabel = gtk.Label()
2571 self._derateLabel.set_use_underline(True)
2572 self._derateLabel.set_markup(xstr("takeoff_derate_tupolev"))
2573 self._derateLabel.set_alignment(0.0, 0.5)
2574 table.attach(self._derateLabel, 0, 1, row, row+1)
2575
2576 self._derate = gtk.Alignment()
2577 table.attach(self._derate, 2, 4, row, row+1)
2578 self._derateWidget = None
2579 self._derateEntry = None
2580 self._derateUnit = None
2581 self._derateButtons = None
2582
2583 row += 1
2584
2585 self._antiIceOn = gtk.CheckButton(xstr("takeoff_antiice"))
2586 self._antiIceOn.set_use_underline(True)
2587 self._antiIceOn.set_tooltip_text(xstr("takeoff_antiice_tooltip"))
2588 table.attach(self._antiIceOn, 2, 4, row, row+1)
2589
2590 row += 1
2591
2592 self._rto = gtk.CheckButton(xstr("takeoff_rto"))
2593 self._rto.set_use_underline(True)
2594 self._rto.set_tooltip_text(xstr("takeoff_rto_tooltip"))
2595 self._rto.connect("toggled", self._rtoToggled)
2596 table.attach(self._rto, 2, 4, row, row+1, ypadding = 8)
2597
2598 self.addCancelFlightButton()
2599
2600 self.addPreviousButton(clicked = self._backClicked)
2601
2602 self._button = self.addNextButton(clicked = self._forwardClicked)
2603
2604 self._active = False
2605
2606 @property
2607 def runway(self):
2608 """Get the runway."""
2609 return self._runway.get_text()
2610
2611 @property
2612 def sid(self):
2613 """Get the SID."""
2614 text = self._sid.get_child().get_text()
2615 return text if self._sid.get_active()!=0 and text and text!="N/A" \
2616 else None
2617
2618 @property
2619 def v1(self):
2620 """Get the v1 speed."""
2621 return self._v1.get_int()
2622
2623 @property
2624 def vr(self):
2625 """Get the vr speed."""
2626 return self._vr.get_int()
2627
2628 @property
2629 def v2(self):
2630 """Get the v2 speed."""
2631 return self._v2.get_int()
2632
2633 @property
2634 def derate(self):
2635 """Get the derate value, if any."""
2636 if self._derateWidget is None:
2637 return None
2638 if self._derateType==acft.DERATE_BOEING:
2639 derate = self._derateEntry.get_text()
2640 return derate if derate else None
2641 elif self._derateType==acft.DERATE_EPR:
2642 derate = self._derateWidget.get_text()
2643 return derate if derate else None
2644 elif self._derateType==acft.DERATE_TUPOLEV:
2645 return acft.DERATE_TUPOLEV_NOMINAL \
2646 if self._derateButtons[0].get_active() \
2647 else acft.DERATE_TUPOLEV_TAKEOFF
2648 elif self._derateType==acft.DERATE_B462:
2649 return self._derateWidget.get_active()
2650 else:
2651 return None
2652
2653 @property
2654 def antiIceOn(self):
2655 """Get whether the anti-ice system has been turned on."""
2656 return self._antiIceOn.get_active()
2657
2658 @antiIceOn.setter
2659 def antiIceOn(self, value):
2660 """Set the anti-ice indicator."""
2661 self._antiIceOn.set_active(value)
2662
2663 @property
2664 def rtoIndicated(self):
2665 """Get whether the pilot has indicated if there was an RTO."""
2666 return self._rto.get_active()
2667
2668 def activate(self):
2669 """Activate the page."""
2670 print "TakeoffPage.activate"
2671
2672 self._updatingMETAR = True
2673 self._metar.get_buffer().set_text(self._wizard.departureMETAR, -1)
2674 self._updatingMETAR = False
2675
2676 self._runway.set_text("")
2677 self._runway.set_sensitive(True)
2678 self._sid.set_active(0)
2679 self._sid.set_sensitive(True)
2680 self._v1.set_int(None)
2681 self._v1.set_sensitive(True)
2682 self._vr.set_int(None)
2683 self._vr.set_sensitive(True)
2684 self._v2.set_int(None)
2685 self._v2.set_sensitive(True)
2686
2687 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
2688 speedUnit = xstr("label" + i18nSpeedUnit)
2689 self._v1Unit.set_text(speedUnit)
2690 self._vrUnit.set_text(speedUnit)
2691 self._v2Unit.set_text(speedUnit)
2692
2693 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip" + i18nSpeedUnit))
2694 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip" + i18nSpeedUnit))
2695 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip" + i18nSpeedUnit))
2696
2697 self._derateType = self._wizard.gui.flight.aircraft.derateType
2698
2699 self._setupDerateWidget()
2700
2701 self._rto.set_active(False)
2702 self._rto.set_sensitive(False)
2703
2704 self._button.set_sensitive(False)
2705 self._forwardAllowed = False
2706
2707 self._active = True
2708
2709 def allowForward(self):
2710 """Allow going to the next page."""
2711 print "TakeoffPage.allowForward"
2712 self._forwardAllowed = True
2713 self._updateForwardButton()
2714
2715 def reset(self):
2716 """Reset the page if the wizard is reset."""
2717 print "TakeoffPage.reset"
2718
2719 super(TakeoffPage, self).reset()
2720 self._v1.reset()
2721 self._vr.reset()
2722 self._v2.reset()
2723 self._hasDerate = False
2724 self._antiIceOn.set_active(False)
2725 self._active = False
2726
2727 def setRTOEnabled(self, enabled):
2728 """Set the RTO checkbox enabled or disabled."""
2729 if not enabled:
2730 self._rto.set_active(False)
2731 self._rto.set_sensitive(enabled)
2732
2733 def changeMETAR(self, metar):
2734 """Change the METAR as a result of an edit on one of the other
2735 pages."""
2736 if self._active:
2737 print "TakeoffPage.changeMETAR"
2738 self._updatingMETAR = True
2739 self._metar.get_buffer().set_text(metar, -1)
2740 self._updatingMETAR = False
2741
2742 self._updateForwardButton()
2743
2744 def _updateForwardButton(self):
2745 """Update the sensitivity of the forward button based on some conditions."""
2746 sensitive = self._forwardAllowed and \
2747 self._metar.get_text()!="" and \
2748 self._runway.get_text()!="" and \
2749 self.sid is not None and \
2750 self.v1 is not None and \
2751 self.vr is not None and \
2752 self.v2 is not None and \
2753 self.v1 <= self.vr and \
2754 self.vr <= self.v2 and \
2755 (self._derateType==acft.DERATE_NONE or
2756 self.derate is not None)
2757
2758 print "TakeoffPage._updateForwardButton: forwardAllowed:", self._forwardAllowed, ", sensitive:", sensitive
2759 if self._forwardAllowed:
2760 print " METAR: ", self._metar.get_text()
2761 print " runway: ", self._runway.get_text()
2762 print " SID:", self.sid
2763 print " V1:", self.v1
2764 print " VR:", self.vr
2765 print " V2:", self.v2
2766 print " derateType:", self._derateType
2767 print " derate:", self.derate
2768
2769 self._button.set_sensitive(sensitive)
2770
2771 def _valueChanged(self, widget, arg = None):
2772 """Called when the value of some widget has changed."""
2773 print "TakeoffPage._valueChanged"
2774
2775 self._updateForwardButton()
2776
2777 def _upperChanged(self, entry, arg = None):
2778 """Called when the value of some entry widget has changed and the value
2779 should be converted to uppercase."""
2780 print "TakeoffPage._upperChanged"
2781 entry.set_text(entry.get_text().upper())
2782 self._valueChanged(entry, arg)
2783
2784 def _upperChangedComboBox(self, comboBox):
2785 """Called for combo box widgets that must be converted to uppercase."""
2786 entry = comboBox.get_child()
2787 if comboBox.get_active()==-1:
2788 entry.set_text(entry.get_text().upper())
2789 self._valueChanged(entry)
2790
2791 def _derateChanged(self, entry):
2792 """Called when the value of the derate is changed."""
2793 print "TakeoffPage._derateChanged"
2794 self._updateForwardButton()
2795
2796 def _rtoToggled(self, button):
2797 """Called when the RTO check button is toggled."""
2798 self._wizard.rtoToggled(button.get_active())
2799
2800 def _backClicked(self, button):
2801 """Called when the Back button is pressed."""
2802 self.goBack()
2803
2804 def _forwardClicked(self, button):
2805 """Called when the forward button is clicked."""
2806 aircraft = self._wizard.gui.flight.aircraft
2807 aircraft.updateV1R2()
2808 if self.derate is not None:
2809 aircraft.updateDerate()
2810 aircraft.updateTakeoffAntiIce()
2811 self._wizard.nextPage()
2812
2813 def _setupDerateWidget(self):
2814 """Setup the derate widget."""
2815 if self._derateWidget is not None:
2816 self._derate.remove(self._derateWidget)
2817
2818 if self._derateType==acft.DERATE_BOEING:
2819 self._derateLabel.set_text(xstr("takeoff_derate_boeing"))
2820 self._derateLabel.set_use_underline(True)
2821 self._derateLabel.set_sensitive(True)
2822
2823 self._derateEntry = gtk.Entry()
2824 self._derateEntry.set_width_chars(7)
2825 self._derateEntry.set_tooltip_text(xstr("takeoff_derate_boeing_tooltip"))
2826 self._derateEntry.set_alignment(1.0)
2827 self._derateEntry.connect("changed", self._derateChanged)
2828 self._derateLabel.set_mnemonic_widget(self._derateEntry)
2829
2830 self._derateUnit = gtk.Label("%")
2831 self._derateUnit.set_alignment(0.0, 0.5)
2832
2833 self._derateWidget = gtk.Table(3, 1)
2834 self._derateWidget.set_row_spacings(4)
2835 self._derateWidget.set_col_spacings(16)
2836 self._derateWidget.set_homogeneous(False)
2837
2838 self._derateWidget.attach(self._derateEntry, 0, 2, 0, 1)
2839 self._derateWidget.attach(self._derateUnit, 2, 3, 0, 1)
2840
2841 self._derate.add(self._derateWidget)
2842 elif self._derateType==acft.DERATE_EPR:
2843 self._derateLabel.set_text("_EPR:")
2844 self._derateLabel.set_use_underline(True)
2845 self._derateLabel.set_sensitive(True)
2846
2847 self._derateWidget = gtk.Entry()
2848 self._derateWidget.set_width_chars(7)
2849 self._derateWidget.set_tooltip_text(xstr("takeoff_derate_epr_tooltip"))
2850 self._derateWidget.set_alignment(1.0)
2851 self._derateWidget.connect("changed", self._derateChanged)
2852 self._derateLabel.set_mnemonic_widget(self._derateWidget)
2853
2854 self._derate.add(self._derateWidget)
2855 elif self._derateType==acft.DERATE_TUPOLEV:
2856 self._derateLabel.set_text(xstr("takeoff_derate_tupolev"))
2857 self._derateLabel.set_use_underline(True)
2858 self._derateLabel.set_sensitive(True)
2859
2860 if pygobject:
2861 nominal = gtk.RadioButton.\
2862 new_with_label_from_widget(None,
2863 xstr("takeoff_derate_tupolev_nominal"))
2864 else:
2865 nominal = gtk.RadioButton(None,
2866 xstr("takeoff_derate_tupolev_nominal"))
2867 nominal.set_use_underline(True)
2868 nominal.set_tooltip_text(xstr("takeoff_derate_tupolev_nominal_tooltip"))
2869 nominal.connect("toggled", self._derateChanged)
2870
2871 if pygobject:
2872 takeoff = gtk.RadioButton.\
2873 new_with_label_from_widget(nominal,
2874 xstr("takeoff_derate_tupolev_takeoff"))
2875 else:
2876 takeoff = gtk.RadioButton(nominal,
2877 xstr("takeoff_derate_tupolev_takeoff"))
2878
2879 takeoff.set_use_underline(True)
2880 takeoff.set_tooltip_text(xstr("takeoff_derate_tupolev_takeoff_tooltip"))
2881 takeoff.connect("toggled", self._derateChanged)
2882
2883 self._derateButtons = [nominal, takeoff]
2884
2885 self._derateWidget = gtk.HBox()
2886 self._derateWidget.pack_start(nominal, False, False, 4)
2887 self._derateWidget.pack_start(takeoff, False, False, 4)
2888
2889 self._derate.add(self._derateWidget)
2890 elif self._derateType==acft.DERATE_B462:
2891 self._derateLabel.set_text("")
2892
2893 self._derateWidget = gtk.CheckButton(xstr("takeoff_derate_b462"))
2894 self._derateWidget.set_tooltip_text(xstr("takeoff_derate_b462_tooltip"))
2895 self._derateWidget.set_use_underline(True)
2896 self._derate.add(self._derateWidget)
2897 else:
2898 self._derateWidget = None
2899 self._derateLabel.set_text("")
2900 self._derateLabel.set_sensitive(False)
2901
2902 def _metarChanged(self, entry):
2903 """Called when the METAR has changed."""
2904 print "TakeoffPage.metarChanged", self._updatingMETAR
2905 if not self._updatingMETAR:
2906 self._updateForwardButton()
2907 self._wizard.metarChanged(entry.get_text(), self)
2908
2909 def _metarInserted(self, buffer, position, text, length):
2910 """Called when new characters are inserted into the METAR.
2911
2912 It uppercases all characters."""
2913 print "TakeoffPage.metarInserted", self._updatingMETAR
2914 if not self._updatingMETAR:
2915 self._updatingMETAR = True
2916
2917 buffer.delete_text(position, length)
2918 buffer.insert_text(position, text.upper(), length)
2919
2920 self._updatingMETAR = False
2921
2922#-----------------------------------------------------------------------------
2923
2924class CruisePage(Page):
2925 """The page containing the flight level that might change during flight."""
2926 def __init__(self, wizard):
2927 """Construct the page."""
2928 super(CruisePage, self).__init__(wizard, xstr("cruise_title"),
2929 xstr("cruise_help"))
2930
2931 self._loggable = False
2932 self._loggedCruiseLevel = 240
2933 self._activated = False
2934
2935 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
2936 xscale = 0.0, yscale = 1.0)
2937
2938 mainBox = gtk.VBox()
2939 alignment.add(mainBox)
2940 self.setMainWidget(alignment)
2941
2942 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
2943 xscale = 0.0, yscale = 0.0)
2944 mainBox.pack_start(alignment, False, False, 16)
2945
2946 levelBox = gtk.HBox()
2947
2948 label = gtk.Label(xstr("route_level"))
2949 label.set_use_underline(True)
2950 levelBox.pack_start(label, True, True, 0)
2951
2952 self._cruiseLevel = gtk.SpinButton()
2953 self._cruiseLevel.set_increments(step = 10, page = 100)
2954 self._cruiseLevel.set_range(min = 50, max = 500)
2955 self._cruiseLevel.set_tooltip_text(xstr("cruise_route_level_tooltip"))
2956 self._cruiseLevel.set_numeric(True)
2957 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
2958 label.set_mnemonic_widget(self._cruiseLevel)
2959
2960 levelBox.pack_start(self._cruiseLevel, False, False, 8)
2961
2962 self._updateButton = gtk.Button(xstr("cruise_route_level_update"));
2963 self._updateButton.set_use_underline(True)
2964 self._updateButton.set_tooltip_text(xstr("cruise_route_level_update_tooltip"))
2965 self._updateButton.connect("clicked", self._updateButtonClicked)
2966
2967 levelBox.pack_start(self._updateButton, False, False, 16)
2968
2969 mainBox.pack_start(levelBox, False, False, 0)
2970
2971 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
2972 xscale = 0.0, yscale = 1.0)
2973 mainBox.pack_start(alignment, True, True, 0)
2974
2975 self.addCancelFlightButton()
2976
2977 self._backButton = self.addPreviousButton(clicked = self._backClicked)
2978 self._button = self.addNextButton(clicked = self._forwardClicked)
2979
2980 @property
2981 def activated(self):
2982 """Determine if the page is already activated or not."""
2983 return self._activated
2984
2985 @property
2986 def cruiseLevel(self):
2987 """Get the cruise level."""
2988 return self._loggedCruiseLevel
2989
2990 @property
2991 def loggableCruiseLevel(self):
2992 """Get the cruise level which should be logged."""
2993 return self._cruiseLevel.get_value_as_int()
2994
2995 def setLoggable(self, loggable):
2996 """Set whether the cruise altitude can be logged."""
2997 self._loggable = loggable
2998 self._updateButtons()
2999
3000 def activate(self):
3001 """Setup the route from the booked flight."""
3002 self._loggedCruiseLevel = self._wizard.filedCruiseLevel
3003 self._cruiseLevel.set_value(self._loggedCruiseLevel)
3004 self._activated = True
3005
3006 def reset(self):
3007 """Reset the page."""
3008 self._loggable = False
3009 self._activated = False
3010 super(CruisePage, self).reset()
3011
3012 def _updateButtons(self):
3013 """Update the sensitivity of the buttons."""
3014 self._updateButton.set_sensitive(self._loggable and
3015 self.loggableCruiseLevel!=
3016 self._loggedCruiseLevel)
3017
3018 def _cruiseLevelChanged(self, spinButton):
3019 """Called when the cruise level has changed."""
3020 self._updateButtons()
3021
3022 def _updateButtonClicked(self, button):
3023 """Called when the update button is clicked."""
3024 if self._wizard.cruiseLevelChanged():
3025 self._loggedCruiseLevel = self.loggableCruiseLevel
3026 self._updateButtons()
3027
3028 def _backClicked(self, button):
3029 """Called when the Back button is pressed."""
3030 self.goBack()
3031
3032 def _forwardClicked(self, button):
3033 """Called when the Forward button is clicked."""
3034 self._wizard.nextPage()
3035
3036#-----------------------------------------------------------------------------
3037
3038class LandingPage(Page):
3039 """Page for entering landing data."""
3040 def __init__(self, wizard):
3041 """Construct the landing page."""
3042 super(LandingPage, self).__init__(wizard, xstr("landing_title"),
3043 xstr("landing_help"),
3044 completedHelp = xstr("landing_chelp"))
3045
3046 self._flightEnded = False
3047
3048 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3049 xscale = 0.0, yscale = 0.0)
3050
3051 table = gtk.Table(7, 24)
3052 table.set_row_spacings(4)
3053 table.set_col_spacings(16)
3054 table.set_homogeneous(False)
3055 alignment.add(table)
3056 self.setMainWidget(alignment)
3057
3058 row = 0
3059
3060 label = gtk.Label(xstr("landing_metar"))
3061 label.set_use_underline(True)
3062 label.set_alignment(0.0, 0.5)
3063 table.attach(label, 0, 1, row, row+1)
3064
3065 self._metar = gtk.Entry()
3066 self._metar.set_width_chars(40)
3067 self._metar.set_tooltip_text(xstr("landing_metar_tooltip"))
3068 self._metar.connect("changed", self._metarChanged)
3069 self._metar.get_buffer().connect_after("inserted-text", self._metarInserted)
3070 table.attach(self._metar, 1, 24, row, row+1)
3071 label.set_mnemonic_widget(self._metar)
3072
3073 self._updatingMETAR = False
3074
3075 row += 1
3076
3077 label = gtk.Label(xstr("landing_star"))
3078 label.set_use_underline(True)
3079 label.set_alignment(0.0, 0.5)
3080 table.attach(label, 1, 2, row, row + 1)
3081
3082 if pygobject:
3083 self._star = gtk.ComboBox.new_with_model_and_entry(comboModel)
3084 else:
3085 self._star = gtk.ComboBoxEntry(comboModel)
3086
3087 self._star.set_entry_text_column(0)
3088 self._star.get_child().set_width_chars(10)
3089 self._star.set_tooltip_text(xstr("landing_star_tooltip"))
3090 self._star.connect("changed", self._upperChangedComboBox)
3091 self._star.set_sensitive(False)
3092 table.attach(self._star, 2, 4, row, row + 1)
3093 label.set_mnemonic_widget(self._star)
3094
3095 row += 1
3096
3097 label = gtk.Label(xstr("landing_transition"))
3098 label.set_use_underline(True)
3099 label.set_alignment(0.0, 0.5)
3100 table.attach(label, 1, 2, row, row + 1)
3101
3102 if pygobject:
3103 self._transition = gtk.ComboBox.new_with_model_and_entry(comboModel)
3104 else:
3105 self._transition = gtk.ComboBoxEntry(comboModel)
3106
3107 self._transition.set_entry_text_column(0)
3108 self._transition.get_child().set_width_chars(10)
3109 self._transition.set_tooltip_text(xstr("landing_transition_tooltip"))
3110 self._transition.connect("changed", self._upperChangedComboBox)
3111 self._transition.set_sensitive(False)
3112 table.attach(self._transition, 2, 4, row, row + 1)
3113 label.set_mnemonic_widget(self._transition)
3114
3115 row += 1
3116
3117 label = gtk.Label(xstr("landing_runway"))
3118 label.set_use_underline(True)
3119 label.set_alignment(0.0, 0.5)
3120 table.attach(label, 1, 2, row, row + 1)
3121
3122 self._runway = gtk.Entry()
3123 self._runway.set_width_chars(10)
3124 self._runway.set_tooltip_text(xstr("landing_runway_tooltip"))
3125 self._runway.connect("changed", self._upperChanged)
3126 table.attach(self._runway, 2, 4, row, row + 1)
3127 label.set_mnemonic_widget(self._runway)
3128
3129 row += 1
3130
3131 label = gtk.Label(xstr("landing_approach"))
3132 label.set_use_underline(True)
3133 label.set_alignment(0.0, 0.5)
3134 table.attach(label, 1, 2, row, row + 1)
3135
3136 self._approachType = gtk.Entry()
3137 self._approachType.set_width_chars(10)
3138 self._approachType.set_tooltip_text(xstr("landing_approach_tooltip"))
3139 self._approachType.connect("changed", self._upperChanged)
3140 table.attach(self._approachType, 2, 4, row, row + 1)
3141 label.set_mnemonic_widget(self._approachType)
3142
3143 row += 1
3144
3145 label = gtk.Label(xstr("landing_vref"))
3146 label.set_use_markup(True)
3147 label.set_use_underline(True)
3148 label.set_alignment(0.0, 0.5)
3149 table.attach(label, 1, 2, row, row + 1)
3150
3151 self._vref = IntegerEntry()
3152 self._vref.set_width_chars(5)
3153 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip_knots"))
3154 self._vref.connect("integer-changed", self._vrefChanged)
3155 table.attach(self._vref, 3, 4, row, row + 1)
3156 label.set_mnemonic_widget(self._vref)
3157
3158 self._vrefUnit = gtk.Label(xstr("label_knots"))
3159 table.attach(self._vrefUnit, 4, 5, row, row + 1)
3160
3161 row += 1
3162
3163 self._antiIceOn = gtk.CheckButton(xstr("landing_antiice"))
3164 self._antiIceOn.set_use_underline(True)
3165 self._antiIceOn.set_tooltip_text(xstr("landing_antiice_tooltip"))
3166 table.attach(self._antiIceOn, 3, 5, row, row + 1)
3167
3168 self.addCancelFlightButton()
3169
3170 self.addPreviousButton(clicked = self._backClicked)
3171
3172 self._button = self.addNextButton(clicked = self._forwardClicked)
3173
3174 self._active = False
3175
3176 @property
3177 def star(self):
3178 """Get the STAR or None if none entered."""
3179 text = self._star.get_child().get_text()
3180 return text if self._star.get_active()!=0 and text and text!="N/A" \
3181 else None
3182
3183 @property
3184 def transition(self):
3185 """Get the transition or None if none entered."""
3186 text = self._transition.get_child().get_text()
3187 return text if self._transition.get_active()!=0 and text and text!="N/A" \
3188 else None
3189
3190 @property
3191 def approachType(self):
3192 """Get the approach type."""
3193 return self._approachType.get_text()
3194
3195 @property
3196 def runway(self):
3197 """Get the runway."""
3198 return self._runway.get_text()
3199
3200 @property
3201 def vref(self):
3202 """Return the landing reference speed."""
3203 return self._vref.get_int()
3204
3205 @property
3206 def antiIceOn(self):
3207 """Get whether the anti-ice system has been turned on."""
3208 return self._antiIceOn.get_active()
3209
3210 @antiIceOn.setter
3211 def antiIceOn(self, value):
3212 """Set the anti-ice indicator."""
3213 self._antiIceOn.set_active(value)
3214
3215 def reset(self):
3216 """Reset the page if the wizard is reset."""
3217 super(LandingPage, self).reset()
3218 self._vref.reset()
3219 self._antiIceOn.set_active(False)
3220 self._flightEnded = False
3221 self._active = False
3222
3223 def activate(self):
3224 """Called when the page is activated."""
3225 self._updatingMETAR = True
3226 self._metar.get_buffer().set_text(self._wizard.arrivalMETAR, -1)
3227 self._updatingMETAR = False
3228
3229 self._star.set_active(0)
3230 self._star.set_sensitive(True)
3231
3232 self._transition.set_active(0)
3233 self._transition.set_sensitive(True)
3234
3235 self._runway.set_text("")
3236 self._runway.set_sensitive(True)
3237
3238 self._approachType.set_text("")
3239 self._approachType.set_sensitive(True)
3240
3241 self._vref.set_int(None)
3242 self._vref.set_sensitive(True)
3243
3244 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
3245 speedUnit = xstr("label" + i18nSpeedUnit)
3246 self._vrefUnit.set_text(speedUnit)
3247
3248 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip" +
3249 i18nSpeedUnit))
3250
3251 self._updateForwardButton()
3252
3253 self._active = True
3254
3255 def flightEnded(self):
3256 """Called when the flight has ended."""
3257 super(LandingPage, self).flightEnded()
3258 self._flightEnded = True
3259 self._updateForwardButton()
3260
3261 def changeMETAR(self, metar):
3262 """Change the METAR as a result of an edit on one of the other
3263 pages."""
3264 if self._active:
3265 print "LandingPage.changeMETAR"
3266 self._updatingMETAR = True
3267 self._metar.get_buffer().set_text(metar, -1)
3268 self._updatingMETAR = False
3269
3270 self._updateForwardButton()
3271
3272 def _updateForwardButton(self):
3273 """Update the sensitivity of the forward button."""
3274 sensitive = self._flightEnded and \
3275 self._metar.get_text()!="" and \
3276 (self.star is not None or
3277 self.transition is not None) and \
3278 self._runway.get_text()!="" and \
3279 self._approachType.get_text()!="" and \
3280 self.vref is not None
3281 self._button.set_sensitive(sensitive)
3282
3283 def _upperChanged(self, entry):
3284 """Called for entry widgets that must be converted to uppercase."""
3285 entry.set_text(entry.get_text().upper())
3286 self._updateForwardButton()
3287
3288 def _upperChangedComboBox(self, comboBox):
3289 """Called for combo box widgets that must be converted to uppercase."""
3290 if comboBox.get_active()==-1:
3291 entry = comboBox.get_child()
3292 entry.set_text(entry.get_text().upper())
3293 self._updateForwardButton()
3294
3295 def _vrefChanged(self, widget, value):
3296 """Called when the Vref has changed."""
3297 self._updateForwardButton()
3298
3299 def _backClicked(self, button):
3300 """Called when the Back button is pressed."""
3301 self.goBack()
3302
3303 def _forwardClicked(self, button):
3304 """Called when the forward button is clicked."""
3305 aircraft = self._wizard.gui.flight.aircraft
3306 aircraft.updateVRef()
3307 aircraft.updateLandingAntiIce()
3308 if self._wizard.gui.config.onlineGateSystem and \
3309 self._wizard.loggedIn and not self._completed and \
3310 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
3311 not self._wizard.entranceExam:
3312 self._wizard.getFleet(callback = self._fleetRetrieved,
3313 force = True)
3314 else:
3315 self._wizard.nextPage()
3316
3317 def _fleetRetrieved(self, fleet):
3318 """Callback for the fleet retrieval."""
3319 self._wizard.nextPage()
3320
3321 def _metarChanged(self, entry):
3322 """Called when the METAR has changed."""
3323 print "LandingPage.metarChanged", self._updatingMETAR
3324 if not self._updatingMETAR:
3325 self._updateForwardButton()
3326 self._wizard.metarChanged(entry.get_text(), self)
3327
3328 def _metarInserted(self, buffer, position, text, length):
3329 """Called when new characters are inserted into the METAR.
3330
3331 It uppercases all characters."""
3332 print "LandingPage.metarInserted", self._updatingMETAR
3333 if not self._updatingMETAR:
3334 self._updatingMETAR = True
3335
3336 buffer.delete_text(position, length)
3337 buffer.insert_text(position, text.upper(), length)
3338
3339 self._updatingMETAR = False
3340
3341#-----------------------------------------------------------------------------
3342
3343class FinishPage(Page):
3344 """Flight finish page."""
3345 _flightTypes = [ ("flighttype_scheduled", const.FLIGHTTYPE_SCHEDULED),
3346 ("flighttype_ot", const.FLIGHTTYPE_OLDTIMER),
3347 ("flighttype_vip", const.FLIGHTTYPE_VIP),
3348 ("flighttype_charter", const.FLIGHTTYPE_CHARTER) ]
3349
3350 def __init__(self, wizard):
3351 """Construct the finish page."""
3352 help = xstr("finish_help") + xstr("finish_help_goodtime")
3353 super(FinishPage, self).__init__(wizard, xstr("finish_title"), help)
3354
3355 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3356 xscale = 0.0, yscale = 0.0)
3357
3358 table = gtk.Table(10, 2)
3359 table.set_row_spacings(4)
3360 table.set_col_spacings(16)
3361 table.set_homogeneous(False)
3362 alignment.add(table)
3363 self.setMainWidget(alignment)
3364
3365 row = 0
3366
3367 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3368 label = gtk.Label(xstr("finish_rating"))
3369 labelAlignment.add(label)
3370 table.attach(labelAlignment, 0, 1, row, row+1)
3371
3372 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3373 self._flightRating = gtk.Label()
3374 self._flightRating.set_width_chars(8)
3375 self._flightRating.set_alignment(0.0, 0.5)
3376 self._flightRating.set_use_markup(True)
3377 labelAlignment.add(self._flightRating)
3378 table.attach(labelAlignment, 1, 2, row, row+1)
3379
3380 row += 1
3381
3382 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3383 label = gtk.Label(xstr("finish_dep_time"))
3384 labelAlignment.add(label)
3385 table.attach(labelAlignment, 0, 1, row, row+1)
3386
3387 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3388 self._depTime = gtk.Label()
3389 self._depTime.set_width_chars(13)
3390 self._depTime.set_alignment(0.0, 0.5)
3391 self._depTime.set_use_markup(True)
3392 self._depTime.set_tooltip_markup(xstr("finish_dep_time_tooltip"))
3393 labelAlignment.add(self._depTime)
3394 table.attach(labelAlignment, 1, 2, row, row+1)
3395
3396 row += 1
3397
3398 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3399 label = gtk.Label(xstr("finish_flight_time"))
3400 labelAlignment.add(label)
3401 table.attach(labelAlignment, 0, 1, row, row+1)
3402
3403 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3404 self._flightTime = gtk.Label()
3405 self._flightTime.set_width_chars(10)
3406 self._flightTime.set_alignment(0.0, 0.5)
3407 self._flightTime.set_use_markup(True)
3408 labelAlignment.add(self._flightTime)
3409 table.attach(labelAlignment, 1, 2, row, row+1)
3410
3411 row += 1
3412
3413 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3414 label = gtk.Label(xstr("finish_block_time"))
3415 labelAlignment.add(label)
3416 table.attach(labelAlignment, 0, 1, row, row+1)
3417
3418 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3419 self._blockTime = gtk.Label()
3420 self._blockTime.set_width_chars(10)
3421 self._blockTime.set_alignment(0.0, 0.5)
3422 self._blockTime.set_use_markup(True)
3423 labelAlignment.add(self._blockTime)
3424 table.attach(labelAlignment, 1, 2, row, row+1)
3425
3426 row += 1
3427
3428 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3429 label = gtk.Label(xstr("finish_arr_time"))
3430 labelAlignment.add(label)
3431 table.attach(labelAlignment, 0, 1, row, row+1)
3432
3433 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3434 self._arrTime = gtk.Label()
3435 self._arrTime.set_width_chars(13)
3436 self._arrTime.set_alignment(0.0, 0.5)
3437 self._arrTime.set_use_markup(True)
3438 self._arrTime.set_tooltip_markup(xstr("finish_arr_time_tooltip"))
3439 labelAlignment.add(self._arrTime)
3440 table.attach(labelAlignment, 1, 2, row, row+1)
3441
3442 row += 1
3443
3444 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3445 label = gtk.Label(xstr("finish_distance"))
3446 labelAlignment.add(label)
3447 table.attach(labelAlignment, 0, 1, row, row+1)
3448
3449 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3450 self._distanceFlown = gtk.Label()
3451 self._distanceFlown.set_width_chars(10)
3452 self._distanceFlown.set_alignment(0.0, 0.5)
3453 self._distanceFlown.set_use_markup(True)
3454 labelAlignment.add(self._distanceFlown)
3455 table.attach(labelAlignment, 1, 2, row, row+1)
3456
3457 row += 1
3458
3459 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3460 label = gtk.Label(xstr("finish_fuel"))
3461 labelAlignment.add(label)
3462 table.attach(labelAlignment, 0, 1, row, row+1)
3463
3464 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3465 self._fuelUsed = gtk.Label()
3466 self._fuelUsed.set_width_chars(10)
3467 self._fuelUsed.set_alignment(0.0, 0.5)
3468 self._fuelUsed.set_use_markup(True)
3469 labelAlignment.add(self._fuelUsed)
3470 table.attach(labelAlignment, 1, 2, row, row+1)
3471
3472 row += 1
3473
3474 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
3475 yalign = 0.5, yscale = 0.0)
3476 label = gtk.Label(xstr("finish_type"))
3477 label.set_use_underline(True)
3478 labelAlignment.add(label)
3479 table.attach(labelAlignment, 0, 1, row, row+1)
3480
3481 flightTypeModel = gtk.ListStore(str, int)
3482 for (name, type) in FinishPage._flightTypes:
3483 flightTypeModel.append([xstr(name), type])
3484
3485 self._flightType = gtk.ComboBox(model = flightTypeModel)
3486 renderer = gtk.CellRendererText()
3487 self._flightType.pack_start(renderer, True)
3488 self._flightType.add_attribute(renderer, "text", 0)
3489 self._flightType.set_tooltip_text(xstr("finish_type_tooltip"))
3490 self._flightType.set_active(0)
3491 self._flightType.connect("changed", self._flightTypeChanged)
3492 flightTypeAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3493 flightTypeAlignment.add(self._flightType)
3494 table.attach(flightTypeAlignment, 1, 2, row, row+1)
3495 label.set_mnemonic_widget(self._flightType)
3496
3497 row += 1
3498
3499 self._onlineFlight = gtk.CheckButton(xstr("finish_online"))
3500 self._onlineFlight.set_use_underline(True)
3501 self._onlineFlight.set_tooltip_text(xstr("finish_online_tooltip"))
3502 onlineFlightAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3503 onlineFlightAlignment.add(self._onlineFlight)
3504 table.attach(onlineFlightAlignment, 1, 2, row, row + 1)
3505
3506 row += 1
3507
3508 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
3509 yalign = 0.5, yscale = 0.0)
3510 self._gateLabel = gtk.Label(xstr("finish_gate"))
3511 self._gateLabel.set_use_underline(True)
3512 labelAlignment.add(self._gateLabel)
3513 table.attach(labelAlignment, 0, 1, row, row+1)
3514
3515 self._gatesModel = gtk.ListStore(str)
3516
3517 self._gate = gtk.ComboBox(model = self._gatesModel)
3518 renderer = gtk.CellRendererText()
3519 self._gate.pack_start(renderer, True)
3520 self._gate.add_attribute(renderer, "text", 0)
3521 self._gate.set_tooltip_text(xstr("finish_gate_tooltip"))
3522 self._gate.connect("changed", self._gateChanged)
3523 gateAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
3524 gateAlignment.add(self._gate)
3525 table.attach(gateAlignment, 1, 2, row, row+1)
3526 self._gateLabel.set_mnemonic_widget(self._gate)
3527
3528 self.addButton(xstr("finish_newFlight"),
3529 sensitive = True,
3530 clicked = self._newFlightClicked,
3531 tooltip = xstr("finish_newFlight_tooltip"),
3532 padding = 16)
3533
3534 self.addPreviousButton(clicked = self._backClicked)
3535
3536 self._saveButton = self.addButton(xstr("finish_save"),
3537 sensitive = False,
3538 clicked = self._saveClicked,
3539 tooltip = xstr("finish_save_tooltip"))
3540 self._savePIREPDialog = None
3541 self._lastSavePath = None
3542
3543 self._tooBigTimeDifference = False
3544 self._deferredAutoSave = False
3545 self._pirepSaved = False
3546 self._pirepSent = False
3547
3548 self._sendButton = self.addButton(xstr("sendPIREP"), default = True,
3549 sensitive = False,
3550 clicked = self._sendClicked,
3551 tooltip = xstr("sendPIREP_tooltip"))
3552
3553 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 10*60.0)
3554 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 20*60.0)
3555 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 0*60.0)
3556 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), (23*60.0+50)*60.0)
3557 # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (1*60.0+5)*60.0)
3558 # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (0*60.0+50)*60.0)
3559 # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (0*60.0+5)*60.0)
3560 # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (23*60.0+45)*60.0)
3561
3562 @property
3563 def flightType(self):
3564 """Get the flight type."""
3565 index = self._flightType.get_active()
3566 return None if index<0 else self._flightType.get_model()[index][1]
3567
3568 @property
3569 def online(self):
3570 """Get whether the flight was an online flight or not."""
3571 return self._onlineFlight.get_active()
3572
3573 def activate(self):
3574 """Activate the page."""
3575 self._deferredAutoSave = False
3576 self._pirepSaved = False
3577 self._pirepSent = False
3578
3579 flight = self._wizard.gui._flight
3580 rating = flight.logger.getRating()
3581 if rating<0:
3582 self._flightRating.set_markup('<b><span foreground="red">NO GO</span></b>')
3583 else:
3584 self._flightRating.set_markup("<b>%.1f %%</b>" % (rating,))
3585
3586 flightLength = flight.flightTimeEnd - flight.flightTimeStart
3587 self._flightTime.set_markup("<b>%s</b>" % \
3588 (util.getTimeIntervalString(flightLength),))
3589
3590 blockLength = flight.blockTimeEnd - flight.blockTimeStart
3591 self._blockTime.set_markup("<b>%s</b>" % \
3592 (util.getTimeIntervalString(blockLength),))
3593
3594 self._distanceFlown.set_markup("<b>%.2f NM</b>" % \
3595 (flight.flownDistance,))
3596
3597 self._fuelUsed.set_markup("<b>%.0f kg</b>" % \
3598 (flight.startFuel - flight.endFuel,))
3599
3600 self._flightType.set_active(-1)
3601 self._onlineFlight.set_active(self._wizard.loggedIn)
3602
3603 self._gatesModel.clear()
3604 if self._wizard.gui.config.onlineGateSystem and \
3605 self._wizard.loggedIn and \
3606 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
3607 not self._wizard.entranceExam:
3608 occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
3609 for gate in lhbpGates.gates:
3610 if gate.isAvailable(lhbpGates, occupiedGateNumbers):
3611 self._gatesModel.append([gate.number])
3612 self._gateLabel.set_sensitive(True)
3613 self._gate.set_sensitive(True)
3614 self._gate.set_active(-1)
3615 else:
3616 self._gateLabel.set_sensitive(False)
3617 self._gate.set_sensitive(False)
3618
3619 self._updateTimes()
3620
3621 def updateButtons(self):
3622 """Update the sensitivity state of the buttons."""
3623 gui = self._wizard.gui
3624 faultsExplained = gui.faultsFullyExplained
3625 timesCorrect = self.flightType is None or \
3626 not self._tooBigTimeDifference or \
3627 gui.hasComments or gui.hasDelayCode
3628 sensitive = gui.flight is not None and \
3629 gui.flight.stage==const.STAGE_END and \
3630 self._flightType.get_active()>=0 and \
3631 (self._gatesModel.get_iter_first() is None or
3632 self._gate.get_active()>=0) and \
3633 faultsExplained and timesCorrect
3634
3635 self._updateHelp(faultsExplained, timesCorrect)
3636
3637 wasSensitive = self._saveButton.get_sensitive()
3638
3639 if gui.config.pirepAutoSave and sensitive and not wasSensitive:
3640 if gui.isWizardActive():
3641 self._autoSavePIREP()
3642 else:
3643 self._deferredAutoSave = True
3644
3645 if not sensitive:
3646 self._deferredAutoSave = False
3647
3648 self._saveButton.set_sensitive(sensitive)
3649 self._sendButton.set_sensitive(sensitive and
3650 self._wizard.bookedFlight.id is not None)
3651
3652 def grabDefault(self):
3653 """If the page has a default button, make it the default one."""
3654 super(FinishPage, self).grabDefault()
3655 if self._deferredAutoSave:
3656 self._autoSavePIREP()
3657 self._deferredAutoSave = False
3658
3659 def _autoSavePIREP(self):
3660 """Perform the automatic saving of the PIREP."""
3661 self._lastSavePath = os.path.join(self._wizard.gui.config.pirepDirectory,
3662 self._getDefaultPIREPName())
3663 self._lastSavePath = text2unicode(self._lastSavePath)
3664 self._savePIREP(automatic = True)
3665
3666 def _backClicked(self, button):
3667 """Called when the Back button is pressed."""
3668 self.goBack()
3669
3670 def _flightTypeChanged(self, comboBox):
3671 """Called when the flight type has changed."""
3672 self._updateTimes()
3673
3674 def _gateChanged(self, comboBox):
3675 """Called when the arrival gate has changed."""
3676 self.updateButtons()
3677
3678 def _newFlightClicked(self, button):
3679 """Called when the new flight button is clicked."""
3680 gui = self._wizard.gui
3681 if not self._pirepSent and not self._pirepSaved:
3682 dialog = gtk.MessageDialog(parent = gui.mainWindow,
3683 type = MESSAGETYPE_QUESTION,
3684 message_format = xstr("finish_newFlight_question"))
3685
3686 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
3687 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
3688
3689 dialog.set_title(WINDOW_TITLE_BASE)
3690 result = dialog.run()
3691 dialog.hide()
3692 if result!=RESPONSETYPE_YES:
3693 return
3694
3695 gui.reset()
3696
3697 def _getDefaultPIREPName(self):
3698 """Get the default name of the PIREP."""
3699 gui = self._wizard.gui
3700
3701 bookedFlight = gui.bookedFlight
3702 tm = time.gmtime()
3703
3704 pilotID = self._wizard.pilotID
3705 if pilotID: pilotID += " "
3706 return "%s%s %02d%02d %s-%s.pirep" % \
3707 (pilotID, str(bookedFlight.departureTime.date()),
3708 tm.tm_hour, tm.tm_min,
3709 bookedFlight.departureICAO, bookedFlight.arrivalICAO)
3710
3711
3712 def _saveClicked(self, button):
3713 """Called when the Save PIREP button is clicked."""
3714 gui = self._wizard.gui
3715
3716 fileName = self._getDefaultPIREPName()
3717
3718 dialog = self._getSaveDialog()
3719
3720 if self._lastSavePath is None:
3721 pirepDirectory = gui.config.pirepDirectory
3722 if pirepDirectory is not None:
3723 dialog.set_current_folder(pirepDirectory)
3724 else:
3725 dialog.set_current_folder(os.path.dirname(self._lastSavePath))
3726
3727 dialog.set_current_name(fileName)
3728 result = dialog.run()
3729 dialog.hide()
3730
3731 if result==RESPONSETYPE_OK:
3732 self._lastSavePath = text2unicode(dialog.get_filename())
3733 self._savePIREP()
3734
3735 def _savePIREP(self, automatic = False):
3736 """Perform the saving of the PIREP."""
3737
3738 gui = self._wizard.gui
3739
3740 if automatic:
3741 gui.beginBusy(xstr("finish_autosave_busy"))
3742
3743 pirep = PIREP(gui.flight)
3744 error = pirep.save(self._lastSavePath)
3745
3746 if automatic:
3747 gui.endBusy()
3748
3749 if error:
3750 type = MESSAGETYPE_ERROR
3751 message = xstr("finish_save_failed")
3752 secondary = xstr("finish_save_failed_sec") % (text2unicode(error),)
3753 else:
3754 type = MESSAGETYPE_INFO
3755 message = xstr("finish_save_done")
3756 if automatic:
3757 secondary = xstr("finish_save_done_sec") % (self._lastSavePath,)
3758 else:
3759 secondary = None
3760 self._pirepSaved = True
3761
3762 dialog = gtk.MessageDialog(parent = gui.mainWindow,
3763 type = type, message_format = message)
3764 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
3765 dialog.set_title(WINDOW_TITLE_BASE)
3766 if secondary is not None:
3767 dialog.format_secondary_markup(secondary)
3768
3769 dialog.run()
3770 dialog.hide()
3771
3772 def _getSaveDialog(self):
3773 """Get the PIREP saving dialog.
3774
3775 If it does not exist yet, create it."""
3776 if self._savePIREPDialog is None:
3777 gui = self._wizard.gui
3778 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
3779 xstr("finish_save_title"),
3780 action = FILE_CHOOSER_ACTION_SAVE,
3781 buttons = (gtk.STOCK_CANCEL,
3782 RESPONSETYPE_CANCEL,
3783 gtk.STOCK_OK, RESPONSETYPE_OK),
3784 parent = gui.mainWindow)
3785 dialog.set_modal(True)
3786 dialog.set_do_overwrite_confirmation(True)
3787
3788 filter = gtk.FileFilter()
3789 filter.set_name(xstr("file_filter_pireps"))
3790 filter.add_pattern("*.pirep")
3791 dialog.add_filter(filter)
3792
3793 filter = gtk.FileFilter()
3794 filter.set_name(xstr("file_filter_all"))
3795 filter.add_pattern("*.*")
3796 dialog.add_filter(filter)
3797
3798 self._savePIREPDialog = dialog
3799
3800 return self._savePIREPDialog
3801
3802
3803 def _sendClicked(self, button):
3804 """Called when the Send button is clicked."""
3805 pirep = PIREP(self._wizard.gui.flight)
3806 self._wizard.gui.sendPIREP(pirep,
3807 callback = self._handlePIREPSent)
3808
3809 def _handlePIREPSent(self, returned, result):
3810 """Callback for the PIREP sending result."""
3811 self._pirepSent = returned and result.success
3812 if self._wizard.gui.config.onlineGateSystem and \
3813 self._wizard.loggedIn and not self._wizard.entranceExam and \
3814 returned and result.success:
3815 bookedFlight = self._wizard.bookedFlight
3816 if bookedFlight.arrivalICAO=="LHBP":
3817 iter = self._gate.get_active_iter()
3818 gateNumber = None if iter is None \
3819 else self._gatesModel.get_value(iter, 0)
3820
3821 status = const.PLANE_PARKING if gateNumber is None \
3822 else const.PLANE_HOME
3823 else:
3824 gateNumber = None
3825 status = const.PLANE_AWAY
3826
3827 self._wizard.updatePlane(self._planeUpdated,
3828 bookedFlight.tailNumber,
3829 status, gateNumber = gateNumber)
3830
3831 def _planeUpdated(self, success):
3832 """Callback for the plane updating."""
3833 pass
3834
3835 def _formatTime(self, scheduledTime, realTimestamp, (warning, error)):
3836 """Format the departure or arrival time based on the given data as a
3837 markup for a label."""
3838 realTime = time.gmtime(realTimestamp)
3839
3840 if warning:
3841 colour = "red" if error else "orange"
3842 markupBegin = '<span foreground="%s">' % (colour,)
3843 markupEnd = '</span>'
3844 else:
3845 markupBegin = markupEnd = ""
3846
3847 markup = "<b>%s%02d:%02d [%02d:%02d]%s</b>" % \
3848 (markupBegin,
3849 realTime.tm_hour, realTime.tm_min,
3850 scheduledTime.hour, scheduledTime.minute,
3851 markupEnd)
3852
3853 return markup
3854
3855 def _updateTimes(self):
3856 """Format the flight times and the help text according to the flight
3857 type.
3858
3859 The buttons are also updated.
3860 """
3861 flight = self._wizard.gui._flight
3862 bookedFlight = flight.bookedFlight
3863
3864 (departureWarning, departureError) = flight.blockTimeStartWrong
3865 (arrivalWarning, arrivalError) = flight.blockTimeEndWrong
3866
3867 if self.flightType==const.FLIGHTTYPE_VIP:
3868 departureError = arrivalError = False
3869
3870 self._tooBigTimeDifference = departureError or arrivalError
3871
3872 self._depTime.set_markup(self._formatTime(bookedFlight.departureTime,
3873 flight.blockTimeStart,
3874 (departureWarning,
3875 departureError)))
3876
3877 self._arrTime.set_markup(self._formatTime(bookedFlight.arrivalTime,
3878 flight.blockTimeEnd,
3879 (arrivalWarning,
3880 arrivalError)))
3881
3882 self.updateButtons()
3883
3884 def _updateHelp(self, faultsExplained, timesCorrect):
3885 """Update the help text according to the actual situation."""
3886 if not faultsExplained:
3887 self.setHelp(xstr("finish_help") + xstr("finish_help_faults"))
3888 elif not timesCorrect:
3889 self.setHelp(xstr("finish_help") + xstr("finish_help_wrongtime"))
3890 else:
3891 self.setHelp(xstr("finish_help") + xstr("finish_help_goodtime"))
3892
3893
3894#-----------------------------------------------------------------------------
3895
3896class Wizard(gtk.VBox):
3897 """The flight wizard."""
3898 def __init__(self, gui):
3899 """Construct the wizard."""
3900 super(Wizard, self).__init__()
3901
3902 self.gui = gui
3903
3904 self._pages = []
3905 self._currentPage = None
3906
3907 self._loginPage = LoginPage(self)
3908 self._pages.append(self._loginPage)
3909 self._pages.append(FlightSelectionPage(self))
3910 self._pages.append(GateSelectionPage(self))
3911 self._pages.append(ConnectPage(self))
3912 self._payloadPage = PayloadPage(self)
3913 self._pages.append(self._payloadPage)
3914 self._payloadIndex = len(self._pages)
3915 self._pages.append(TimePage(self))
3916 self._routePage = RoutePage(self)
3917 self._pages.append(self._routePage)
3918 self._simBriefSetupPage = SimBriefSetupPage(self)
3919 self._pages.append(self._simBriefSetupPage)
3920 self._simBriefingPage = SimBriefingPage(self)
3921 self._pages.append(self._simBriefingPage)
3922 self._pages.append(FuelPage(self))
3923 self._departureBriefingPage = BriefingPage(self, True)
3924 self._pages.append(self._departureBriefingPage)
3925 self._arrivalBriefingPage = BriefingPage(self, False)
3926 self._pages.append(self._arrivalBriefingPage)
3927 self._arrivalBriefingIndex = len(self._pages)
3928 self._takeoffPage = TakeoffPage(self)
3929 self._pages.append(self._takeoffPage)
3930 self._cruisePage = CruisePage(self)
3931 self._pages.append(self._cruisePage)
3932 self._landingPage = LandingPage(self)
3933 self._pages.append(self._landingPage)
3934 self._finishPage = FinishPage(self)
3935 self._pages.append(self._finishPage)
3936
3937 self._requestedWidth = None
3938 self._requestedHeight = None
3939
3940 self.connect("size-allocate", self._sizeAllocate)
3941
3942 for page in self._pages:
3943 page.show_all()
3944 page.setStyle()
3945
3946 self._initialize()
3947
3948 def _sizeAllocate(self, widget, allocation):
3949 if self._requestedWidth is not None and \
3950 self._requestedHeight is not None:
3951 return
3952
3953 if self._currentPage is not None:
3954 self.remove(self._pages[self._currentPage])
3955
3956 maxWidth = 0
3957 maxHeight = 0
3958 for page in self._pages:
3959 self.add(page)
3960 self.show_all()
3961 pageSizeRequest = page.size_request()
3962 width = pageSizeRequest.width if pygobject else pageSizeRequest[0]
3963 height = pageSizeRequest.height if pygobject else pageSizeRequest[1]
3964 maxWidth = max(maxWidth, width)
3965 maxHeight = max(maxHeight, height)
3966 self.remove(page)
3967
3968 if self._currentPage is not None:
3969 self.add(self._pages[self._currentPage])
3970
3971 self._requestedWidth = maxWidth
3972 self._requestedHeight = maxHeight
3973 self.set_size_request(maxWidth, maxHeight)
3974
3975 @property
3976 def pilotID(self):
3977 """Get the pilot ID, if given."""
3978 return self._loginPage.pilotID
3979
3980 @property
3981 def entranceExam(self):
3982 """Get whether an entrance exam is about to be taken."""
3983 return self._loginPage.entranceExam
3984
3985 @property
3986 def loggedIn(self):
3987 """Indicate if there was a successful login."""
3988 return self._loginResult is not None
3989
3990 @property
3991 def loginResult(self):
3992 """Get the login result."""
3993 return self._loginResult
3994
3995 def setCurrentPage(self, index, finalize = False, fromPageShift = None):
3996 """Set the current page to the one with the given index.
3997
3998 @param fromPageShift if given, the relative index of one of the
3999 previous pages that should be used as the from-page of the next
4000 page. E.g. if fromPageShift is 1, the previous page will be the
4001 from-page."""
4002 assert index < len(self._pages)
4003
4004 fromPage = self._currentPage
4005 if fromPage is not None:
4006 page = self._pages[fromPage]
4007 if finalize and not page._completed:
4008 page.complete()
4009 self.remove(page)
4010 if fromPageShift is not None:
4011 fromPage -= fromPageShift
4012
4013 self._currentPage = index
4014 page = self._pages[index]
4015 self.add(page)
4016 if page._fromPage is None:
4017 page._fromPage = fromPage
4018 page.initialize()
4019 self.show_all()
4020 if fromPage is not None:
4021 self.grabDefault()
4022
4023 @property
4024 def bookedFlight(self):
4025 """Get the booked flight selected."""
4026 return self._bookedFlight
4027
4028 @property
4029 def numCrew(self):
4030 """Get the number of crew members."""
4031 return self._payloadPage.numCrew
4032
4033 @property
4034 def numPassengers(self):
4035 """Get the number of passengers."""
4036 return self._payloadPage.numPassengers
4037
4038 @property
4039 def bagWeight(self):
4040 """Get the baggage weight."""
4041 return self._payloadPage.bagWeight
4042
4043 @property
4044 def cargoWeight(self):
4045 """Get the cargo weight."""
4046 return self._payloadPage.cargoWeight
4047
4048 @property
4049 def mailWeight(self):
4050 """Get the mail weight."""
4051 return self._payloadPage.mailWeight
4052
4053 @property
4054 def zfw(self):
4055 """Get the calculated ZFW value."""
4056 return 0 if self._bookedFlight is None \
4057 else self._payloadPage.calculateZFW()
4058
4059 @property
4060 def filedCruiseLevel(self):
4061 """Get the filed cruise level."""
4062 return self._routePage.filedCruiseLevel
4063
4064 @property
4065 def filedCruiseAltitude(self):
4066 """Get the filed cruise altitude."""
4067 return self._routePage.filedCruiseLevel * 100
4068
4069 @property
4070 def cruiseAltitude(self):
4071 """Get the cruise altitude."""
4072 level = self._cruisePage.cruiseLevel if self._cruisePage.activated \
4073 else self._routePage.filedCruiseLevel
4074 return level * 100
4075
4076 @property
4077 def loggableCruiseAltitude(self):
4078 """Get the cruise altitude that can be logged."""
4079 if self._cruisePage.activated:
4080 return self._cruisePage.loggableCruiseLevel * 100
4081 else:
4082 return 0
4083
4084 @property
4085 def route(self):
4086 """Get the route."""
4087 return self._routePage.route
4088
4089 @property
4090 def alternate(self):
4091 """Get the ICAO code of the alternate airport."""
4092 return self._routePage.alternate
4093
4094 @property
4095 def departureMETAR(self):
4096 """Get the METAR of the departure airport."""
4097 return self._departureBriefingPage.metar
4098
4099 @property
4100 def arrivalMETAR(self):
4101 """Get the METAR of the arrival airport."""
4102 return self._arrivalBriefingPage.metar
4103
4104 @property
4105 def departureRunway(self):
4106 """Get the departure runway."""
4107 return self._takeoffPage.runway
4108
4109 @property
4110 def sid(self):
4111 """Get the SID."""
4112 return self._takeoffPage.sid
4113
4114 @property
4115 def v1(self):
4116 """Get the V1 speed."""
4117 return self._takeoffPage.v1
4118
4119 @property
4120 def vr(self):
4121 """Get the Vr speed."""
4122 return self._takeoffPage.vr
4123
4124 @property
4125 def v2(self):
4126 """Get the V2 speed."""
4127 return self._takeoffPage.v2
4128
4129 @property
4130 def derate(self):
4131 """Get the derate value."""
4132 return self._takeoffPage.derate
4133
4134 @property
4135 def takeoffAntiIceOn(self):
4136 """Get whether the anti-ice system was on during take-off."""
4137 return self._takeoffPage.antiIceOn
4138
4139 @takeoffAntiIceOn.setter
4140 def takeoffAntiIceOn(self, value):
4141 """Set anti-ice on indicator."""
4142 self._takeoffPage.antiIceOn = value
4143
4144 @property
4145 def rtoIndicated(self):
4146 """Get whether the pilot has indicated that an RTO has occured."""
4147 return self._takeoffPage.rtoIndicated
4148
4149 @property
4150 def arrivalRunway(self):
4151 """Get the arrival runway."""
4152 return self._landingPage.runway
4153
4154 @property
4155 def star(self):
4156 """Get the STAR."""
4157 return self._landingPage.star
4158
4159 @property
4160 def transition(self):
4161 """Get the transition."""
4162 return self._landingPage.transition
4163
4164 @property
4165 def approachType(self):
4166 """Get the approach type."""
4167 return self._landingPage.approachType
4168
4169 @property
4170 def vref(self):
4171 """Get the Vref speed."""
4172 return self._landingPage.vref
4173
4174 @property
4175 def landingAntiIceOn(self):
4176 """Get whether the anti-ice system was on during landing."""
4177 return self._landingPage.antiIceOn
4178
4179 @landingAntiIceOn.setter
4180 def landingAntiIceOn(self, value):
4181 """Set anti-ice on indicator."""
4182 self._landingPage.antiIceOn = value
4183
4184 @property
4185 def flightType(self):
4186 """Get the flight type."""
4187 return self._finishPage.flightType
4188
4189 @property
4190 def online(self):
4191 """Get whether the flight was online or not."""
4192 return self._finishPage.online
4193
4194 @property
4195 def usingSimBrief(self):
4196 """Indicate if we are using a SimBrief briefing or not."""
4197 return self._usingSimBrief
4198
4199 @usingSimBrief.setter
4200 def usingSimBrief(self, x):
4201 """Set whether we are using a SimBrief briefing or not."""
4202 self._usingSimBrief = x
4203
4204 def nextPage(self, finalize = True):
4205 """Go to the next page."""
4206 self.jumpPage(1, finalize)
4207
4208 def jumpPage(self, count, finalize = True, fromPageShift = None):
4209 """Go to the page which is 'count' pages after the current one."""
4210 self.setCurrentPage(self._currentPage + count,
4211 finalize = finalize, fromPageShift = fromPageShift)
4212
4213 def grabDefault(self):
4214 """Make the default button of the current page the default."""
4215 self._pages[self._currentPage].grabDefault()
4216
4217 def connected(self, fsType, descriptor):
4218 """Called when the connection could be made to the simulator."""
4219 self.nextPage()
4220
4221 def reset(self, loginResult):
4222 """Resets the wizard to go back to the login page."""
4223 self._initialize(keepLoginResult = loginResult is None,
4224 loginResult = loginResult)
4225
4226 def setStage(self, stage):
4227 """Set the flight stage to the given one."""
4228 if stage!=const.STAGE_END:
4229 self._cruisePage.setLoggable(Flight.canLogCruiseAltitude(stage))
4230
4231 if stage==const.STAGE_TAKEOFF:
4232 self._takeoffPage.allowForward()
4233 elif stage==const.STAGE_LANDING:
4234 if not self._arrivalBriefingPage.metarEdited:
4235 print "Downloading arrival METAR again"
4236 self.gui.webHandler.getMETARs(self._arrivalMETARCallback,
4237 [self._bookedFlight.arrivalICAO])
4238
4239 elif stage==const.STAGE_END:
4240 for page in self._pages:
4241 page.flightEnded()
4242
4243 def _initialize(self, keepLoginResult = False, loginResult = None):
4244 """Initialize the wizard."""
4245 if not keepLoginResult:
4246 self._loginResult = loginResult
4247
4248 self._loginCallback = None
4249
4250 self._fleet = None
4251 self._fleetCallback = None
4252
4253 self._bookedFlight = None
4254 self._departureGate = "-"
4255 self._fuelData = None
4256 self._departureNOTAMs = None
4257 self._departureMETAR = None
4258 self._arrivalNOTAMs = None
4259 self._arrivalMETAR = None
4260 self._usingSimBrief = None
4261
4262 firstPage = 0 if self._loginResult is None else 1
4263 for page in self._pages[firstPage:]:
4264 page.reset()
4265
4266 self.setCurrentPage(firstPage)
4267 #self.setCurrentPage(10)
4268
4269 def login(self, callback, pilotID, password, entranceExam):
4270 """Called when the login button was clicked."""
4271 self._loginCallback = callback
4272 if pilotID is None:
4273 loginResult = self._loginResult
4274 assert loginResult is not None and loginResult.loggedIn
4275 pilotID = loginResult.pilotID
4276 password = loginResult.password
4277 entranceExam = loginResult.entranceExam
4278 busyMessage = xstr("reload_busy")
4279 else:
4280 self._loginResult = None
4281 busyMessage = xstr("login_busy")
4282
4283 self.gui.beginBusy(busyMessage)
4284
4285 self.gui.webHandler.login(self._loginResultCallback,
4286 pilotID, password,
4287 entranceExam = entranceExam)
4288
4289 def reloadFlights(self, callback):
4290 """Reload the flights from the MAVA server."""
4291 self.login(callback, None, None, None)
4292
4293 def cruiseLevelChanged(self):
4294 """Called when the cruise level is changed."""
4295 return self.gui.cruiseLevelChanged()
4296
4297 def metarChanged(self, metar, originator):
4298 """Called when a METER is changed on on of the pages.
4299
4300 originator is the page that originated the changed. It will be used to
4301 determine which METAR (departure or arrival) has changed."""
4302 metar = metar.upper()
4303 if originator in [self._departureBriefingPage, self._takeoffPage]:
4304 self._departureMETARChanged(metar, originator)
4305 else:
4306 self._arrivalMETARChanged(metar, originator)
4307
4308 def _departureMETARChanged(self, metar, originator):
4309 """Called when the departure METAR has been edited on one of the
4310 pages.
4311
4312 originator is the page that originated the change. It will not be
4313 called to set the METAR, while others will be."""
4314 for page in [self._departureBriefingPage, self._takeoffPage]:
4315 if page is not originator:
4316 page.changeMETAR(metar)
4317
4318 def _arrivalMETARChanged(self, metar, originator):
4319 """Called when the arrival METAR has been edited on one of the
4320 pages.
4321
4322 originator is the page that originated the change. It will not be
4323 called to set the METAR, while others will be."""
4324 for page in [self._arrivalBriefingPage, self._landingPage]:
4325 if page is not originator:
4326 page.changeMETAR(metar)
4327
4328 def _loginResultCallback(self, returned, result):
4329 """The login result callback, called in the web handler's thread."""
4330 gobject.idle_add(self._handleLoginResult, returned, result)
4331
4332 def _handleLoginResult(self, returned, result):
4333 """Handle the login result."""
4334 self.gui.endBusy()
4335 returned = True
4336 isReload = self._loginResult is not None
4337 if returned:
4338 if result.loggedIn:
4339 self._loginResult = result
4340 else:
4341 if isReload:
4342 message = xstr("reload_failed")
4343 else:
4344 message = xstr("login_entranceExam_invalid"
4345 if self.entranceExam else
4346 xstr("login_invalid"))
4347 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
4348 type = MESSAGETYPE_ERROR,
4349 message_format = message)
4350 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
4351 dialog.set_title(WINDOW_TITLE_BASE)
4352 if isReload:
4353 secondary = xstr("reload_failed_sec")
4354 else:
4355 secondary = xstr("login_entranceExam_invalid_sec"
4356 if self.entranceExam else
4357 xstr("login_invalid_sec"))
4358 dialog.format_secondary_markup(secondary)
4359 dialog.run()
4360 dialog.hide()
4361 else:
4362 message = xstr("reload_failconn") if isReload \
4363 else xstr("login_failconn")
4364 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
4365 type = MESSAGETYPE_ERROR,
4366 message_format = message)
4367 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
4368 dialog.set_title(WINDOW_TITLE_BASE)
4369 secondary = xstr("reload_failconn_sec") if isReload \
4370 else xstr("login_failconn_sec")
4371 dialog.format_secondary_markup(secondary)
4372
4373 dialog.run()
4374 dialog.hide()
4375
4376 callback = self._loginCallback
4377 self._loginCallback = None
4378 callback(returned, result)
4379
4380 def getFleet(self, callback, force = False):
4381 """Get the fleet via the GUI and call the given callback."""
4382 self._fleetCallback = callback
4383 self.gui.getFleet(callback = self._fleetRetrieved, force = force)
4384
4385 def _fleetRetrieved(self, fleet):
4386 """Callback for the fleet retrieval."""
4387 self._fleet = fleet
4388 if self._fleetCallback is not None:
4389 self._fleetCallback(fleet)
4390 self._fleetCallback = None
4391
4392 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
4393 """Update the given plane's gate information."""
4394 self.gui.updatePlane(tailNumber, status, gateNumber = gateNumber,
4395 callback = callback)
4396
4397 def updateRTO(self):
4398 """Update the RTO state.
4399
4400 The RTO checkbox will be enabled if the flight has an RTO state and the
4401 comments field contains some text."""
4402 flight = self.gui.flight
4403 rtoEnabled = flight is not None and flight.hasRTO and \
4404 self.gui.hasComments
4405 self._takeoffPage.setRTOEnabled(rtoEnabled)
4406
4407 def commentsChanged(self):
4408 """Called when the comments have changed."""
4409 self.updateRTO()
4410 self._finishPage.updateButtons()
4411
4412 def delayCodesChanged(self):
4413 """Called when the delay codes have changed."""
4414 self._finishPage.updateButtons()
4415
4416 def faultExplanationsChanged(self):
4417 """Called when the faults and their explanations have changed."""
4418 self._finishPage.updateButtons()
4419
4420 def rtoToggled(self, indicated):
4421 """Called when the RTO indication has changed."""
4422 self.gui.rtoToggled(indicated)
4423
4424 def _connectSimulator(self, simulatorType):
4425 """Connect to the simulator."""
4426 self.gui.connectSimulator(self._bookedFlight.aircraftType,
4427 simulatorType)
4428
4429 def _arrivalMETARCallback(self, returned, result):
4430 """Called when the METAR of the arrival airport is retrieved."""
4431 gobject.idle_add(self._handleArrivalMETAR, returned, result)
4432
4433 def _handleArrivalMETAR(self, returned, result):
4434 """Called when the METAR of the arrival airport is retrieved."""
4435 icao = self._bookedFlight.arrivalICAO
4436 if returned and icao in result.metars:
4437 metar = result.metars[icao]
4438 if metar!="":
4439 self._arrivalBriefingPage.setMETAR(metar)
4440
4441#-----------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.