source: src/mlx/gui/flight.py@ 680:965523af85d4

Last change on this file since 680:965523af85d4 was 675:122e92f35a2c, checked in by István Váradi <ivaradi@…>, 9 years ago

The status of the Next button on the Route page is continuously updated while entering the FL on the keyboard (re #278)

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