source: src/mlx/gui/flight.py@ 610:66c6e9ccf72c

Last change on this file since 610:66c6e9ccf72c was 607:d0dccf044560, checked in by István Váradi <ivaradi@…>, 10 years ago

The help text on the Finish page is updated dynamically based on the situation (re #248)

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