source: src/mlx/gui/flight.py@ 590:214ed755499b

Last change on this file since 590:214ed755499b was 590:214ed755499b, checked in by István Váradi <ivaradi@…>, 10 years ago

Added tooltips for the METAR fields (re #235)

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