source: src/mlx/gui/flight.py@ 674:138c5401d806

Last change on this file since 674:138c5401d806 was 638:bcba0d5cde1d, checked in by István Váradi <ivaradi@…>, 9 years ago

Extended the debug message when the flight stage would allow the user to leave the Takeoff page (re #268)

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