source: src/mlx/gui/flight.py@ 628:c305067cdeef

Last change on this file since 628:c305067cdeef was 626:2cba7da41678, checked in by István Váradi <ivaradi@…>, 10 years ago

Removed debug hack

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