source: src/mlx/gui/flight.py@ 707:27d3bbba18a7

cef
Last change on this file since 707:27d3bbba18a7 was 707:27d3bbba18a7, checked in by István Váradi <ivaradi@…>, 8 years ago

The extra fuel can be entered for SimBrief (re #279).

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