source: src/mlx/gui/flight.py@ 621:4f7dbaa010d1

Last change on this file since 621:4f7dbaa010d1 was 621:4f7dbaa010d1, checked in by István Váradi <ivaradi@…>, 9 years ago

SID, STAR and transition are input using combo boxes (re #258)

File size: 151.1 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 self._flightEnded = True
2928 sensitive = self._flightEnded and \
2929 self._metar.get_text()!="" and \
2930 (self.star is not None or
2931 self.transition is not None) and \
2932 self._runway.get_text()!="" and \
2933 self._approachType.get_text()!="" and \
2934 self.vref is not None
2935 self._button.set_sensitive(sensitive)
2936
2937 def _upperChanged(self, entry):
2938 """Called for entry widgets that must be converted to uppercase."""
2939 entry.set_text(entry.get_text().upper())
2940 self._updateForwardButton()
2941
2942 def _upperChangedComboBox(self, comboBox):
2943 """Called for combo box widgets that must be converted to uppercase."""
2944 if comboBox.get_active()==-1:
2945 entry = comboBox.get_child()
2946 entry.set_text(entry.get_text().upper())
2947 self._updateForwardButton()
2948
2949 def _vrefChanged(self, widget, value):
2950 """Called when the Vref has changed."""
2951 self._updateForwardButton()
2952
2953 def _backClicked(self, button):
2954 """Called when the Back button is pressed."""
2955 self.goBack()
2956
2957 def _forwardClicked(self, button):
2958 """Called when the forward button is clicked."""
2959 aircraft = self._wizard.gui.flight.aircraft
2960 aircraft.updateVRef()
2961 aircraft.updateLandingAntiIce()
2962 if self._wizard.gui.config.onlineGateSystem and \
2963 self._wizard.loggedIn and not self._completed and \
2964 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
2965 not self._wizard.entranceExam:
2966 self._wizard.getFleet(callback = self._fleetRetrieved,
2967 force = True)
2968 else:
2969 self._wizard.nextPage()
2970
2971 def _fleetRetrieved(self, fleet):
2972 """Callback for the fleet retrieval."""
2973 self._wizard.nextPage()
2974
2975 def _metarChanged(self, entry):
2976 """Called when the METAR has changed."""
2977 print "LandingPage.metarChanged", self._updatingMETAR
2978 if not self._updatingMETAR:
2979 self._updateForwardButton()
2980 self._wizard.metarChanged(entry.get_text(), self)
2981
2982 def _metarInserted(self, buffer, position, text, length):
2983 """Called when new characters are inserted into the METAR.
2984
2985 It uppercases all characters."""
2986 print "LandingPage.metarInserted", self._updatingMETAR
2987 if not self._updatingMETAR:
2988 self._updatingMETAR = True
2989
2990 buffer.delete_text(position, length)
2991 buffer.insert_text(position, text.upper(), length)
2992
2993 self._updatingMETAR = False
2994
2995#-----------------------------------------------------------------------------
2996
2997class FinishPage(Page):
2998 """Flight finish page."""
2999 _flightTypes = [ ("flighttype_scheduled", const.FLIGHTTYPE_SCHEDULED),
3000 ("flighttype_ot", const.FLIGHTTYPE_OLDTIMER),
3001 ("flighttype_vip", const.FLIGHTTYPE_VIP),
3002 ("flighttype_charter", const.FLIGHTTYPE_CHARTER) ]
3003
3004 def __init__(self, wizard):
3005 """Construct the finish page."""
3006 help = xstr("finish_help") + xstr("finish_help_goodtime")
3007 super(FinishPage, self).__init__(wizard, xstr("finish_title"), help)
3008
3009 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3010 xscale = 0.0, yscale = 0.0)
3011
3012 table = gtk.Table(10, 2)
3013 table.set_row_spacings(4)
3014 table.set_col_spacings(16)
3015 table.set_homogeneous(False)
3016 alignment.add(table)
3017 self.setMainWidget(alignment)
3018
3019 row = 0
3020
3021 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3022 label = gtk.Label(xstr("finish_rating"))
3023 labelAlignment.add(label)
3024 table.attach(labelAlignment, 0, 1, row, row+1)
3025
3026 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3027 self._flightRating = gtk.Label()
3028 self._flightRating.set_width_chars(8)
3029 self._flightRating.set_alignment(0.0, 0.5)
3030 self._flightRating.set_use_markup(True)
3031 labelAlignment.add(self._flightRating)
3032 table.attach(labelAlignment, 1, 2, row, row+1)
3033
3034 row += 1
3035
3036 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3037 label = gtk.Label(xstr("finish_dep_time"))
3038 labelAlignment.add(label)
3039 table.attach(labelAlignment, 0, 1, row, row+1)
3040
3041 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3042 self._depTime = gtk.Label()
3043 self._depTime.set_width_chars(13)
3044 self._depTime.set_alignment(0.0, 0.5)
3045 self._depTime.set_use_markup(True)
3046 self._depTime.set_tooltip_markup(xstr("finish_dep_time_tooltip"))
3047 labelAlignment.add(self._depTime)
3048 table.attach(labelAlignment, 1, 2, row, row+1)
3049
3050 row += 1
3051
3052 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3053 label = gtk.Label(xstr("finish_flight_time"))
3054 labelAlignment.add(label)
3055 table.attach(labelAlignment, 0, 1, row, row+1)
3056
3057 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3058 self._flightTime = gtk.Label()
3059 self._flightTime.set_width_chars(10)
3060 self._flightTime.set_alignment(0.0, 0.5)
3061 self._flightTime.set_use_markup(True)
3062 labelAlignment.add(self._flightTime)
3063 table.attach(labelAlignment, 1, 2, row, row+1)
3064
3065 row += 1
3066
3067 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3068 label = gtk.Label(xstr("finish_block_time"))
3069 labelAlignment.add(label)
3070 table.attach(labelAlignment, 0, 1, row, row+1)
3071
3072 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3073 self._blockTime = gtk.Label()
3074 self._blockTime.set_width_chars(10)
3075 self._blockTime.set_alignment(0.0, 0.5)
3076 self._blockTime.set_use_markup(True)
3077 labelAlignment.add(self._blockTime)
3078 table.attach(labelAlignment, 1, 2, row, row+1)
3079
3080 row += 1
3081
3082 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3083 label = gtk.Label(xstr("finish_arr_time"))
3084 labelAlignment.add(label)
3085 table.attach(labelAlignment, 0, 1, row, row+1)
3086
3087 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3088 self._arrTime = gtk.Label()
3089 self._arrTime.set_width_chars(13)
3090 self._arrTime.set_alignment(0.0, 0.5)
3091 self._arrTime.set_use_markup(True)
3092 self._arrTime.set_tooltip_markup(xstr("finish_arr_time_tooltip"))
3093 labelAlignment.add(self._arrTime)
3094 table.attach(labelAlignment, 1, 2, row, row+1)
3095
3096 row += 1
3097
3098 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3099 label = gtk.Label(xstr("finish_distance"))
3100 labelAlignment.add(label)
3101 table.attach(labelAlignment, 0, 1, row, row+1)
3102
3103 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3104 self._distanceFlown = gtk.Label()
3105 self._distanceFlown.set_width_chars(10)
3106 self._distanceFlown.set_alignment(0.0, 0.5)
3107 self._distanceFlown.set_use_markup(True)
3108 labelAlignment.add(self._distanceFlown)
3109 table.attach(labelAlignment, 1, 2, row, row+1)
3110
3111 row += 1
3112
3113 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3114 label = gtk.Label(xstr("finish_fuel"))
3115 labelAlignment.add(label)
3116 table.attach(labelAlignment, 0, 1, row, row+1)
3117
3118 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3119 self._fuelUsed = gtk.Label()
3120 self._fuelUsed.set_width_chars(10)
3121 self._fuelUsed.set_alignment(0.0, 0.5)
3122 self._fuelUsed.set_use_markup(True)
3123 labelAlignment.add(self._fuelUsed)
3124 table.attach(labelAlignment, 1, 2, row, row+1)
3125
3126 row += 1
3127
3128 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
3129 yalign = 0.5, yscale = 0.0)
3130 label = gtk.Label(xstr("finish_type"))
3131 label.set_use_underline(True)
3132 labelAlignment.add(label)
3133 table.attach(labelAlignment, 0, 1, row, row+1)
3134
3135 flightTypeModel = gtk.ListStore(str, int)
3136 for (name, type) in FinishPage._flightTypes:
3137 flightTypeModel.append([xstr(name), type])
3138
3139 self._flightType = gtk.ComboBox(model = flightTypeModel)
3140 renderer = gtk.CellRendererText()
3141 self._flightType.pack_start(renderer, True)
3142 self._flightType.add_attribute(renderer, "text", 0)
3143 self._flightType.set_tooltip_text(xstr("finish_type_tooltip"))
3144 self._flightType.set_active(0)
3145 self._flightType.connect("changed", self._flightTypeChanged)
3146 flightTypeAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3147 flightTypeAlignment.add(self._flightType)
3148 table.attach(flightTypeAlignment, 1, 2, row, row+1)
3149 label.set_mnemonic_widget(self._flightType)
3150
3151 row += 1
3152
3153 self._onlineFlight = gtk.CheckButton(xstr("finish_online"))
3154 self._onlineFlight.set_use_underline(True)
3155 self._onlineFlight.set_tooltip_text(xstr("finish_online_tooltip"))
3156 onlineFlightAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3157 onlineFlightAlignment.add(self._onlineFlight)
3158 table.attach(onlineFlightAlignment, 1, 2, row, row + 1)
3159
3160 row += 1
3161
3162 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
3163 yalign = 0.5, yscale = 0.0)
3164 self._gateLabel = gtk.Label(xstr("finish_gate"))
3165 self._gateLabel.set_use_underline(True)
3166 labelAlignment.add(self._gateLabel)
3167 table.attach(labelAlignment, 0, 1, row, row+1)
3168
3169 self._gatesModel = gtk.ListStore(str)
3170
3171 self._gate = gtk.ComboBox(model = self._gatesModel)
3172 renderer = gtk.CellRendererText()
3173 self._gate.pack_start(renderer, True)
3174 self._gate.add_attribute(renderer, "text", 0)
3175 self._gate.set_tooltip_text(xstr("finish_gate_tooltip"))
3176 self._gate.connect("changed", self._gateChanged)
3177 gateAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
3178 gateAlignment.add(self._gate)
3179 table.attach(gateAlignment, 1, 2, row, row+1)
3180 self._gateLabel.set_mnemonic_widget(self._gate)
3181
3182 self.addButton(xstr("finish_newFlight"),
3183 sensitive = True,
3184 clicked = self._newFlightClicked,
3185 tooltip = xstr("finish_newFlight_tooltip"),
3186 padding = 16)
3187
3188 self.addPreviousButton(clicked = self._backClicked)
3189
3190 self._saveButton = self.addButton(xstr("finish_save"),
3191 sensitive = False,
3192 clicked = self._saveClicked,
3193 tooltip = xstr("finish_save_tooltip"))
3194 self._savePIREPDialog = None
3195 self._lastSavePath = None
3196
3197 self._tooBigTimeDifference = False
3198 self._deferredAutoSave = False
3199 self._pirepSaved = False
3200 self._pirepSent = False
3201
3202 self._sendButton = self.addButton(xstr("sendPIREP"), default = True,
3203 sensitive = False,
3204 clicked = self._sendClicked,
3205 tooltip = xstr("sendPIREP_tooltip"))
3206
3207 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 10*60.0)
3208 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 20*60.0)
3209 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 0*60.0)
3210 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), (23*60.0+50)*60.0)
3211 # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (1*60.0+5)*60.0)
3212 # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (0*60.0+50)*60.0)
3213 # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (0*60.0+5)*60.0)
3214 # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (23*60.0+45)*60.0)
3215
3216 @property
3217 def flightType(self):
3218 """Get the flight type."""
3219 index = self._flightType.get_active()
3220 return None if index<0 else self._flightType.get_model()[index][1]
3221
3222 @property
3223 def online(self):
3224 """Get whether the flight was an online flight or not."""
3225 return self._onlineFlight.get_active()
3226
3227 def activate(self):
3228 """Activate the page."""
3229 self._deferredAutoSave = False
3230 self._pirepSaved = False
3231 self._pirepSent = False
3232
3233 flight = self._wizard.gui._flight
3234 rating = flight.logger.getRating()
3235 if rating<0:
3236 self._flightRating.set_markup('<b><span foreground="red">NO GO</span></b>')
3237 else:
3238 self._flightRating.set_markup("<b>%.1f %%</b>" % (rating,))
3239
3240 flightLength = flight.flightTimeEnd - flight.flightTimeStart
3241 self._flightTime.set_markup("<b>%s</b>" % \
3242 (util.getTimeIntervalString(flightLength),))
3243
3244 blockLength = flight.blockTimeEnd - flight.blockTimeStart
3245 self._blockTime.set_markup("<b>%s</b>" % \
3246 (util.getTimeIntervalString(blockLength),))
3247
3248 self._distanceFlown.set_markup("<b>%.2f NM</b>" % \
3249 (flight.flownDistance,))
3250
3251 self._fuelUsed.set_markup("<b>%.0f kg</b>" % \
3252 (flight.startFuel - flight.endFuel,))
3253
3254 self._flightType.set_active(-1)
3255 self._onlineFlight.set_active(self._wizard.loggedIn)
3256
3257 self._gatesModel.clear()
3258 if self._wizard.gui.config.onlineGateSystem and \
3259 self._wizard.loggedIn and \
3260 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
3261 not self._wizard.entranceExam:
3262 occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
3263 for gate in lhbpGates.gates:
3264 if gate.isAvailable(lhbpGates, occupiedGateNumbers):
3265 self._gatesModel.append([gate.number])
3266 self._gateLabel.set_sensitive(True)
3267 self._gate.set_sensitive(True)
3268 self._gate.set_active(-1)
3269 else:
3270 self._gateLabel.set_sensitive(False)
3271 self._gate.set_sensitive(False)
3272
3273 self._updateTimes()
3274
3275 def updateButtons(self):
3276 """Update the sensitivity state of the buttons."""
3277 gui = self._wizard.gui
3278 faultsExplained = gui.faultsFullyExplained
3279 timesCorrect = self.flightType is None or \
3280 not self._tooBigTimeDifference or \
3281 gui.hasComments or gui.hasDelayCode
3282 sensitive = self._flightType.get_active()>=0 and \
3283 (self._gatesModel.get_iter_first() is None or
3284 self._gate.get_active()>=0) and \
3285 faultsExplained and timesCorrect
3286
3287 self._updateHelp(faultsExplained, timesCorrect)
3288
3289 wasSensitive = self._saveButton.get_sensitive()
3290
3291 if gui.config.pirepAutoSave and sensitive and not wasSensitive:
3292 if gui.isWizardActive():
3293 self._autoSavePIREP()
3294 else:
3295 self._deferredAutoSave = True
3296
3297 if not sensitive:
3298 self._deferredAutoSave = False
3299
3300 self._saveButton.set_sensitive(sensitive)
3301 self._sendButton.set_sensitive(sensitive and
3302 self._wizard.bookedFlight.id is not None)
3303
3304 def grabDefault(self):
3305 """If the page has a default button, make it the default one."""
3306 super(FinishPage, self).grabDefault()
3307 if self._deferredAutoSave:
3308 self._autoSavePIREP()
3309 self._deferredAutoSave = False
3310
3311 def _autoSavePIREP(self):
3312 """Perform the automatic saving of the PIREP."""
3313 self._lastSavePath = os.path.join(self._wizard.gui.config.pirepDirectory,
3314 self._getDefaultPIREPName())
3315 self._lastSavePath = text2unicode(self._lastSavePath)
3316 self._savePIREP(automatic = True)
3317
3318 def _backClicked(self, button):
3319 """Called when the Back button is pressed."""
3320 self.goBack()
3321
3322 def _flightTypeChanged(self, comboBox):
3323 """Called when the flight type has changed."""
3324 self._updateTimes()
3325
3326 def _gateChanged(self, comboBox):
3327 """Called when the arrival gate has changed."""
3328 self.updateButtons()
3329
3330 def _newFlightClicked(self, button):
3331 """Called when the new flight button is clicked."""
3332 gui = self._wizard.gui
3333 if not self._pirepSent and not self._pirepSaved:
3334 dialog = gtk.MessageDialog(parent = gui.mainWindow,
3335 type = MESSAGETYPE_QUESTION,
3336 message_format = xstr("finish_newFlight_question"))
3337
3338 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
3339 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
3340
3341 dialog.set_title(WINDOW_TITLE_BASE)
3342 result = dialog.run()
3343 dialog.hide()
3344 if result!=RESPONSETYPE_YES:
3345 return
3346
3347 gui.reset()
3348
3349 def _getDefaultPIREPName(self):
3350 """Get the default name of the PIREP."""
3351 gui = self._wizard.gui
3352
3353 bookedFlight = gui.bookedFlight
3354 tm = time.gmtime()
3355
3356 pilotID = self._wizard.pilotID
3357 if pilotID: pilotID += " "
3358 return "%s%s %02d%02d %s-%s.pirep" % \
3359 (pilotID, str(bookedFlight.departureTime.date()),
3360 tm.tm_hour, tm.tm_min,
3361 bookedFlight.departureICAO, bookedFlight.arrivalICAO)
3362
3363
3364 def _saveClicked(self, button):
3365 """Called when the Save PIREP button is clicked."""
3366 gui = self._wizard.gui
3367
3368 fileName = self._getDefaultPIREPName()
3369
3370 dialog = self._getSaveDialog()
3371
3372 if self._lastSavePath is None:
3373 pirepDirectory = gui.config.pirepDirectory
3374 if pirepDirectory is not None:
3375 dialog.set_current_folder(pirepDirectory)
3376 else:
3377 dialog.set_current_folder(os.path.dirname(self._lastSavePath))
3378
3379 dialog.set_current_name(fileName)
3380 result = dialog.run()
3381 dialog.hide()
3382
3383 if result==RESPONSETYPE_OK:
3384 self._lastSavePath = text2unicode(dialog.get_filename())
3385 self._savePIREP()
3386
3387 def _savePIREP(self, automatic = False):
3388 """Perform the saving of the PIREP."""
3389
3390 gui = self._wizard.gui
3391
3392 if automatic:
3393 gui.beginBusy(xstr("finish_autosave_busy"))
3394
3395 pirep = PIREP(gui.flight)
3396 error = pirep.save(self._lastSavePath)
3397
3398 if automatic:
3399 gui.endBusy()
3400
3401 if error:
3402 type = MESSAGETYPE_ERROR
3403 message = xstr("finish_save_failed")
3404 secondary = xstr("finish_save_failed_sec") % (text2unicode(error),)
3405 else:
3406 type = MESSAGETYPE_INFO
3407 message = xstr("finish_save_done")
3408 if automatic:
3409 secondary = xstr("finish_save_done_sec") % (self._lastSavePath,)
3410 else:
3411 secondary = None
3412 self._pirepSaved = True
3413
3414 dialog = gtk.MessageDialog(parent = gui.mainWindow,
3415 type = type, message_format = message)
3416 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
3417 dialog.set_title(WINDOW_TITLE_BASE)
3418 if secondary is not None:
3419 dialog.format_secondary_markup(secondary)
3420
3421 dialog.run()
3422 dialog.hide()
3423
3424 def _getSaveDialog(self):
3425 """Get the PIREP saving dialog.
3426
3427 If it does not exist yet, create it."""
3428 if self._savePIREPDialog is None:
3429 gui = self._wizard.gui
3430 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
3431 xstr("finish_save_title"),
3432 action = FILE_CHOOSER_ACTION_SAVE,
3433 buttons = (gtk.STOCK_CANCEL,
3434 RESPONSETYPE_CANCEL,
3435 gtk.STOCK_OK, RESPONSETYPE_OK),
3436 parent = gui.mainWindow)
3437 dialog.set_modal(True)
3438 dialog.set_do_overwrite_confirmation(True)
3439
3440 filter = gtk.FileFilter()
3441 filter.set_name(xstr("file_filter_pireps"))
3442 filter.add_pattern("*.pirep")
3443 dialog.add_filter(filter)
3444
3445 filter = gtk.FileFilter()
3446 filter.set_name(xstr("file_filter_all"))
3447 filter.add_pattern("*.*")
3448 dialog.add_filter(filter)
3449
3450 self._savePIREPDialog = dialog
3451
3452 return self._savePIREPDialog
3453
3454
3455 def _sendClicked(self, button):
3456 """Called when the Send button is clicked."""
3457 pirep = PIREP(self._wizard.gui.flight)
3458 self._wizard.gui.sendPIREP(pirep,
3459 callback = self._handlePIREPSent)
3460
3461 def _handlePIREPSent(self, returned, result):
3462 """Callback for the PIREP sending result."""
3463 self._pirepSent = returned and result.success
3464 if self._wizard.gui.config.onlineGateSystem and \
3465 self._wizard.loggedIn and not self._wizard.entranceExam and \
3466 returned and result.success:
3467 bookedFlight = self._wizard.bookedFlight
3468 if bookedFlight.arrivalICAO=="LHBP":
3469 iter = self._gate.get_active_iter()
3470 gateNumber = None if iter is None \
3471 else self._gatesModel.get_value(iter, 0)
3472
3473 status = const.PLANE_PARKING if gateNumber is None \
3474 else const.PLANE_HOME
3475 else:
3476 gateNumber = None
3477 status = const.PLANE_AWAY
3478
3479 self._wizard.updatePlane(self._planeUpdated,
3480 bookedFlight.tailNumber,
3481 status, gateNumber = gateNumber)
3482
3483 def _planeUpdated(self, success):
3484 """Callback for the plane updating."""
3485 pass
3486
3487 def _formatTime(self, scheduledTime, realTimestamp, (warning, error)):
3488 """Format the departure or arrival time based on the given data as a
3489 markup for a label."""
3490 realTime = time.gmtime(realTimestamp)
3491
3492 if warning:
3493 colour = "red" if error else "orange"
3494 markupBegin = '<span foreground="%s">' % (colour,)
3495 markupEnd = '</span>'
3496 else:
3497 markupBegin = markupEnd = ""
3498
3499 markup = "<b>%s%02d:%02d [%02d:%02d]%s</b>" % \
3500 (markupBegin,
3501 realTime.tm_hour, realTime.tm_min,
3502 scheduledTime.hour, scheduledTime.minute,
3503 markupEnd)
3504
3505 return markup
3506
3507 def _updateTimes(self):
3508 """Format the flight times and the help text according to the flight
3509 type.
3510
3511 The buttons are also updated.
3512 """
3513 flight = self._wizard.gui._flight
3514 bookedFlight = flight.bookedFlight
3515
3516 (departureWarning, departureError) = flight.blockTimeStartWrong
3517 (arrivalWarning, arrivalError) = flight.blockTimeEndWrong
3518
3519 if self.flightType==const.FLIGHTTYPE_VIP:
3520 departureError = arrivalError = False
3521
3522 self._tooBigTimeDifference = departureError or arrivalError
3523
3524 self._depTime.set_markup(self._formatTime(bookedFlight.departureTime,
3525 flight.blockTimeStart,
3526 (departureWarning,
3527 departureError)))
3528
3529 self._arrTime.set_markup(self._formatTime(bookedFlight.arrivalTime,
3530 flight.blockTimeEnd,
3531 (arrivalWarning,
3532 arrivalError)))
3533
3534 self.updateButtons()
3535
3536 def _updateHelp(self, faultsExplained, timesCorrect):
3537 """Update the help text according to the actual situation."""
3538 if not faultsExplained:
3539 self.setHelp(xstr("finish_help") + xstr("finish_help_faults"))
3540 elif not timesCorrect:
3541 self.setHelp(xstr("finish_help") + xstr("finish_help_wrongtime"))
3542 else:
3543 self.setHelp(xstr("finish_help") + xstr("finish_help_goodtime"))
3544
3545
3546#-----------------------------------------------------------------------------
3547
3548class Wizard(gtk.VBox):
3549 """The flight wizard."""
3550 def __init__(self, gui):
3551 """Construct the wizard."""
3552 super(Wizard, self).__init__()
3553
3554 self.gui = gui
3555
3556 self._pages = []
3557 self._currentPage = None
3558
3559 self._loginPage = LoginPage(self)
3560 self._pages.append(self._loginPage)
3561 self._pages.append(FlightSelectionPage(self))
3562 self._pages.append(GateSelectionPage(self))
3563 self._pages.append(ConnectPage(self))
3564 self._payloadPage = PayloadPage(self)
3565 self._pages.append(self._payloadPage)
3566 self._payloadIndex = len(self._pages)
3567 self._pages.append(TimePage(self))
3568 self._pages.append(FuelPage(self))
3569 self._routePage = RoutePage(self)
3570 self._pages.append(self._routePage)
3571 self._departureBriefingPage = BriefingPage(self, True)
3572 self._pages.append(self._departureBriefingPage)
3573 self._arrivalBriefingPage = BriefingPage(self, False)
3574 self._pages.append(self._arrivalBriefingPage)
3575 self._arrivalBriefingIndex = len(self._pages)
3576 self._takeoffPage = TakeoffPage(self)
3577 self._pages.append(self._takeoffPage)
3578 self._cruisePage = CruisePage(self)
3579 self._pages.append(self._cruisePage)
3580 self._landingPage = LandingPage(self)
3581 self._pages.append(self._landingPage)
3582 self._finishPage = FinishPage(self)
3583 self._pages.append(self._finishPage)
3584
3585 self._requestedWidth = None
3586 self._requestedHeight = None
3587
3588 self.connect("size-allocate", self._sizeAllocate)
3589
3590 for page in self._pages:
3591 page.show_all()
3592 page.setStyle()
3593
3594 self._initialize()
3595
3596 def _sizeAllocate(self, widget, allocation):
3597 if self._requestedWidth is not None and \
3598 self._requestedHeight is not None:
3599 return
3600
3601 if self._currentPage is not None:
3602 self.remove(self._pages[self._currentPage])
3603
3604 maxWidth = 0
3605 maxHeight = 0
3606 for page in self._pages:
3607 self.add(page)
3608 self.show_all()
3609 pageSizeRequest = page.size_request()
3610 width = pageSizeRequest.width if pygobject else pageSizeRequest[0]
3611 height = pageSizeRequest.height if pygobject else pageSizeRequest[1]
3612 maxWidth = max(maxWidth, width)
3613 maxHeight = max(maxHeight, height)
3614 self.remove(page)
3615
3616 if self._currentPage is not None:
3617 self.add(self._pages[self._currentPage])
3618
3619 self._requestedWidth = maxWidth
3620 self._requestedHeight = maxHeight
3621 self.set_size_request(maxWidth, maxHeight)
3622
3623 @property
3624 def pilotID(self):
3625 """Get the pilot ID, if given."""
3626 return self._loginPage.pilotID
3627
3628 @property
3629 def entranceExam(self):
3630 """Get whether an entrance exam is about to be taken."""
3631 return self._loginPage.entranceExam
3632
3633 @property
3634 def loggedIn(self):
3635 """Indicate if there was a successful login."""
3636 return self._loginResult is not None
3637
3638 @property
3639 def loginResult(self):
3640 """Get the login result."""
3641 return self._loginResult
3642
3643 def setCurrentPage(self, index, finalize = False):
3644 """Set the current page to the one with the given index."""
3645 assert index < len(self._pages)
3646
3647 fromPage = self._currentPage
3648 if fromPage is not None:
3649 page = self._pages[fromPage]
3650 if finalize and not page._completed:
3651 page.complete()
3652 self.remove(page)
3653
3654 self._currentPage = index
3655 page = self._pages[index]
3656 self.add(page)
3657 if page._fromPage is None:
3658 page._fromPage = fromPage
3659 page.initialize()
3660 self.show_all()
3661 if fromPage is not None:
3662 self.grabDefault()
3663
3664 @property
3665 def bookedFlight(self):
3666 """Get the booked flight selected."""
3667 return self._bookedFlight
3668
3669 @property
3670 def numCrew(self):
3671 """Get the number of crew members."""
3672 return self._payloadPage.numCrew
3673
3674 @property
3675 def numPassengers(self):
3676 """Get the number of passengers."""
3677 return self._payloadPage.numPassengers
3678
3679 @property
3680 def bagWeight(self):
3681 """Get the baggage weight."""
3682 return self._payloadPage.bagWeight
3683
3684 @property
3685 def cargoWeight(self):
3686 """Get the cargo weight."""
3687 return self._payloadPage.cargoWeight
3688
3689 @property
3690 def mailWeight(self):
3691 """Get the mail weight."""
3692 return self._payloadPage.mailWeight
3693
3694 @property
3695 def zfw(self):
3696 """Get the calculated ZFW value."""
3697 return 0 if self._bookedFlight is None \
3698 else self._payloadPage.calculateZFW()
3699
3700 @property
3701 def filedCruiseLevel(self):
3702 """Get the filed cruise level."""
3703 return self._routePage.filedCruiseLevel
3704
3705 @property
3706 def filedCruiseAltitude(self):
3707 """Get the filed cruise altitude."""
3708 return self._routePage.filedCruiseLevel * 100
3709
3710 @property
3711 def cruiseAltitude(self):
3712 """Get the cruise altitude."""
3713 level = self._cruisePage.cruiseLevel if self._cruisePage.activated \
3714 else self._routePage.filedCruiseLevel
3715 return level * 100
3716
3717 @property
3718 def loggableCruiseAltitude(self):
3719 """Get the cruise altitude that can be logged."""
3720 if self._cruisePage.activated:
3721 return self._cruisePage.loggableCruiseLevel * 100
3722 else:
3723 return 0
3724
3725 @property
3726 def route(self):
3727 """Get the route."""
3728 return self._routePage.route
3729
3730 @property
3731 def departureMETAR(self):
3732 """Get the METAR of the departure airport."""
3733 return self._departureBriefingPage.metar
3734
3735 @property
3736 def arrivalMETAR(self):
3737 """Get the METAR of the arrival airport."""
3738 return self._arrivalBriefingPage.metar
3739
3740 @property
3741 def departureRunway(self):
3742 """Get the departure runway."""
3743 return self._takeoffPage.runway
3744
3745 @property
3746 def sid(self):
3747 """Get the SID."""
3748 return self._takeoffPage.sid
3749
3750 @property
3751 def v1(self):
3752 """Get the V1 speed."""
3753 return self._takeoffPage.v1
3754
3755 @property
3756 def vr(self):
3757 """Get the Vr speed."""
3758 return self._takeoffPage.vr
3759
3760 @property
3761 def v2(self):
3762 """Get the V2 speed."""
3763 return self._takeoffPage.v2
3764
3765 @property
3766 def derate(self):
3767 """Get the derate value."""
3768 return self._takeoffPage.derate
3769
3770 @property
3771 def takeoffAntiIceOn(self):
3772 """Get whether the anti-ice system was on during take-off."""
3773 return self._takeoffPage.antiIceOn
3774
3775 @takeoffAntiIceOn.setter
3776 def takeoffAntiIceOn(self, value):
3777 """Set anti-ice on indicator."""
3778 self._takeoffPage.antiIceOn = value
3779
3780 @property
3781 def rtoIndicated(self):
3782 """Get whether the pilot has indicated that an RTO has occured."""
3783 return self._takeoffPage.rtoIndicated
3784
3785 @property
3786 def arrivalRunway(self):
3787 """Get the arrival runway."""
3788 return self._landingPage.runway
3789
3790 @property
3791 def star(self):
3792 """Get the STAR."""
3793 return self._landingPage.star
3794
3795 @property
3796 def transition(self):
3797 """Get the transition."""
3798 return self._landingPage.transition
3799
3800 @property
3801 def approachType(self):
3802 """Get the approach type."""
3803 return self._landingPage.approachType
3804
3805 @property
3806 def vref(self):
3807 """Get the Vref speed."""
3808 return self._landingPage.vref
3809
3810 @property
3811 def landingAntiIceOn(self):
3812 """Get whether the anti-ice system was on during landing."""
3813 return self._landingPage.antiIceOn
3814
3815 @landingAntiIceOn.setter
3816 def landingAntiIceOn(self, value):
3817 """Set anti-ice on indicator."""
3818 self._landingPage.antiIceOn = value
3819
3820 @property
3821 def flightType(self):
3822 """Get the flight type."""
3823 return self._finishPage.flightType
3824
3825 @property
3826 def online(self):
3827 """Get whether the flight was online or not."""
3828 return self._finishPage.online
3829
3830 def nextPage(self, finalize = True):
3831 """Go to the next page."""
3832 self.jumpPage(1, finalize)
3833
3834 def jumpPage(self, count, finalize = True):
3835 """Go to the page which is 'count' pages after the current one."""
3836 self.setCurrentPage(self._currentPage + count, finalize = finalize)
3837
3838 def grabDefault(self):
3839 """Make the default button of the current page the default."""
3840 self._pages[self._currentPage].grabDefault()
3841
3842 def connected(self, fsType, descriptor):
3843 """Called when the connection could be made to the simulator."""
3844 self.nextPage()
3845
3846 def reset(self, loginResult):
3847 """Resets the wizard to go back to the login page."""
3848 self._initialize(keepLoginResult = loginResult is None,
3849 loginResult = loginResult)
3850
3851 def setStage(self, stage):
3852 """Set the flight stage to the given one."""
3853 if stage!=const.STAGE_END:
3854 self._cruisePage.setLoggable(Flight.canLogCruiseAltitude(stage))
3855
3856 if stage==const.STAGE_TAKEOFF:
3857 self._takeoffPage.allowForward()
3858 elif stage==const.STAGE_LANDING:
3859 if not self._arrivalBriefingPage.metarEdited:
3860 print "Downloading arrival METAR again"
3861 self.gui.webHandler.getMETARs(self._arrivalMETARCallback,
3862 [self._bookedFlight.arrivalICAO])
3863
3864 elif stage==const.STAGE_END:
3865 for page in self._pages:
3866 page.flightEnded()
3867
3868 def _initialize(self, keepLoginResult = False, loginResult = None):
3869 """Initialize the wizard."""
3870 if not keepLoginResult:
3871 self._loginResult = loginResult
3872
3873 self._loginCallback = None
3874
3875 self._fleet = None
3876 self._fleetCallback = None
3877
3878 self._bookedFlight = None
3879 self._departureGate = "-"
3880 self._fuelData = None
3881 self._departureNOTAMs = None
3882 self._departureMETAR = None
3883 self._arrivalNOTAMs = None
3884 self._arrivalMETAR = None
3885
3886 firstPage = 0 if self._loginResult is None else 1
3887 for page in self._pages[firstPage:]:
3888 page.reset()
3889
3890 self.setCurrentPage(firstPage)
3891 #self.setCurrentPage(10)
3892
3893 def login(self, callback, pilotID, password, entranceExam):
3894 """Called when the login button was clicked."""
3895 self._loginCallback = callback
3896 if pilotID is None:
3897 loginResult = self._loginResult
3898 assert loginResult is not None and loginResult.loggedIn
3899 pilotID = loginResult.pilotID
3900 password = loginResult.password
3901 entranceExam = loginResult.entranceExam
3902 busyMessage = xstr("reload_busy")
3903 else:
3904 self._loginResult = None
3905 busyMessage = xstr("login_busy")
3906
3907 self.gui.beginBusy(busyMessage)
3908
3909 self.gui.webHandler.login(self._loginResultCallback,
3910 pilotID, password,
3911 entranceExam = entranceExam)
3912
3913 def reloadFlights(self, callback):
3914 """Reload the flights from the MAVA server."""
3915 self.login(callback, None, None, None)
3916
3917 def cruiseLevelChanged(self):
3918 """Called when the cruise level is changed."""
3919 return self.gui.cruiseLevelChanged()
3920
3921 def metarChanged(self, metar, originator):
3922 """Called when a METER is changed on on of the pages.
3923
3924 originator is the page that originated the changed. It will be used to
3925 determine which METAR (departure or arrival) has changed."""
3926 metar = metar.upper()
3927 if originator in [self._departureBriefingPage, self._takeoffPage]:
3928 self._departureMETARChanged(metar, originator)
3929 else:
3930 self._arrivalMETARChanged(metar, originator)
3931
3932 def _departureMETARChanged(self, metar, originator):
3933 """Called when the departure METAR has been edited on one of the
3934 pages.
3935
3936 originator is the page that originated the change. It will not be
3937 called to set the METAR, while others will be."""
3938 for page in [self._departureBriefingPage, self._takeoffPage]:
3939 if page is not originator:
3940 page.changeMETAR(metar)
3941
3942 def _arrivalMETARChanged(self, metar, originator):
3943 """Called when the arrival METAR has been edited on one of the
3944 pages.
3945
3946 originator is the page that originated the change. It will not be
3947 called to set the METAR, while others will be."""
3948 for page in [self._arrivalBriefingPage, self._landingPage]:
3949 if page is not originator:
3950 page.changeMETAR(metar)
3951
3952 def _loginResultCallback(self, returned, result):
3953 """The login result callback, called in the web handler's thread."""
3954 gobject.idle_add(self._handleLoginResult, returned, result)
3955
3956 def _handleLoginResult(self, returned, result):
3957 """Handle the login result."""
3958 self.gui.endBusy()
3959 returned = True
3960 isReload = self._loginResult is not None
3961 if returned:
3962 if result.loggedIn:
3963 self._loginResult = result
3964 else:
3965 if isReload:
3966 message = xstr("reload_failed")
3967 else:
3968 message = xstr("login_entranceExam_invalid"
3969 if self.entranceExam else
3970 xstr("login_invalid"))
3971 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
3972 type = MESSAGETYPE_ERROR,
3973 message_format = message)
3974 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
3975 dialog.set_title(WINDOW_TITLE_BASE)
3976 if isReload:
3977 secondary = xstr("reload_failed_sec")
3978 else:
3979 secondary = xstr("login_entranceExam_invalid_sec"
3980 if self.entranceExam else
3981 xstr("login_invalid_sec"))
3982 dialog.format_secondary_markup(secondary)
3983 dialog.run()
3984 dialog.hide()
3985 else:
3986 message = xstr("reload_failconn") if isReload \
3987 else xstr("login_failconn")
3988 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
3989 type = MESSAGETYPE_ERROR,
3990 message_format = message)
3991 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
3992 dialog.set_title(WINDOW_TITLE_BASE)
3993 secondary = xstr("reload_failconn_sec") if isReload \
3994 else xstr("login_failconn_sec")
3995 dialog.format_secondary_markup(secondary)
3996
3997 dialog.run()
3998 dialog.hide()
3999
4000 callback = self._loginCallback
4001 self._loginCallback = None
4002 callback(returned, result)
4003
4004 def getFleet(self, callback, force = False):
4005 """Get the fleet via the GUI and call the given callback."""
4006 self._fleetCallback = callback
4007 self.gui.getFleet(callback = self._fleetRetrieved, force = force)
4008
4009 def _fleetRetrieved(self, fleet):
4010 """Callback for the fleet retrieval."""
4011 self._fleet = fleet
4012 if self._fleetCallback is not None:
4013 self._fleetCallback(fleet)
4014 self._fleetCallback = None
4015
4016 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
4017 """Update the given plane's gate information."""
4018 self.gui.updatePlane(tailNumber, status, gateNumber = gateNumber,
4019 callback = callback)
4020
4021 def updateRTO(self):
4022 """Update the RTO state.
4023
4024 The RTO checkbox will be enabled if the flight has an RTO state and the
4025 comments field contains some text."""
4026 flight = self.gui.flight
4027 rtoEnabled = flight is not None and flight.hasRTO and \
4028 self.gui.hasComments
4029 self._takeoffPage.setRTOEnabled(rtoEnabled)
4030
4031 def commentsChanged(self):
4032 """Called when the comments have changed."""
4033 self.updateRTO()
4034 self._finishPage.updateButtons()
4035
4036 def delayCodesChanged(self):
4037 """Called when the delay codes have changed."""
4038 self._finishPage.updateButtons()
4039
4040 def faultExplanationsChanged(self):
4041 """Called when the faults and their explanations have changed."""
4042 self._finishPage.updateButtons()
4043
4044 def rtoToggled(self, indicated):
4045 """Called when the RTO indication has changed."""
4046 self.gui.rtoToggled(indicated)
4047
4048 def _connectSimulator(self, simulatorType):
4049 """Connect to the simulator."""
4050 self.gui.connectSimulator(self._bookedFlight.aircraftType,
4051 simulatorType)
4052
4053 def _arrivalMETARCallback(self, returned, result):
4054 """Called when the METAR of the arrival airport is retrieved."""
4055 gobject.idle_add(self._handleArrivalMETAR, returned, result)
4056
4057 def _handleArrivalMETAR(self, returned, result):
4058 """Called when the METAR of the arrival airport is retrieved."""
4059 icao = self._bookedFlight.arrivalICAO
4060 if returned and icao in result.metars:
4061 metar = result.metars[icao]
4062 if metar!="":
4063 self._arrivalBriefingPage.setMETAR(metar)
4064
4065#-----------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.