source: src/mlx/gui/flight.py@ 711:6a17a5922ee4

cef
Last change on this file since 711:6a17a5922ee4 was 711:6a17a5922ee4, checked in by István Váradi <ivaradi@…>, 9 years ago

The climb, cruise and descent profiles are queried and sent to SimBrief

File size: 177.1 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."""
[710]1598 if self._wizard.gui.flight.aircraft.simBriefData is None:
1599 self._wizard.usingSimBrief = False
[692]1600 if self._wizard.gui.config.useSimBrief and \
1601 self._wizard.usingSimBrief is not False:
[687]1602 self._wizard.nextPage()
1603 else:
1604 self._wizard.jumpPage(3)
1605
1606#-----------------------------------------------------------------------------
1607
[700]1608class SimBriefCredentialsDialog(gtk.Dialog):
1609 """A dialog window to ask for SimBrief credentials."""
1610 def __init__(self, gui, userName, password, rememberPassword):
1611 """Construct the dialog."""
1612 super(SimBriefCredentialsDialog, self).__init__(WINDOW_TITLE_BASE + " - " +
1613 xstr("simbrief_credentials_title"),
1614 gui.mainWindow,
1615 DIALOG_MODAL)
1616 self.add_button(xstr("button_cancel"), RESPONSETYPE_CANCEL)
1617 self.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1618
1619 contentArea = self.get_content_area()
1620
1621 contentAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1622 xscale = 0.0, yscale = 0.0)
1623 contentAlignment.set_padding(padding_top = 4, padding_bottom = 16,
1624 padding_left = 8, padding_right = 8)
1625
1626 contentArea.pack_start(contentAlignment, False, False, 0)
1627
1628 contentVBox = gtk.VBox()
1629 contentAlignment.add(contentVBox)
1630
1631 label = gtk.Label(xstr("simbrief_login_failed"))
1632 label.set_alignment(0.0, 0.0)
1633
1634 contentVBox.pack_start(label, False, False, 0)
1635
1636 tableAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1637 xscale = 0.0, yscale = 0.0)
1638 tableAlignment.set_padding(padding_top = 24, padding_bottom = 0,
1639 padding_left = 0, padding_right = 0)
1640
1641 table = gtk.Table(3, 2)
1642 table.set_row_spacings(4)
1643 table.set_col_spacings(16)
1644 table.set_homogeneous(False)
1645
1646 tableAlignment.add(table)
1647 contentVBox.pack_start(tableAlignment, True, True, 0)
1648
1649 label = gtk.Label(xstr("simbrief_username"))
1650 label.set_use_underline(True)
1651 label.set_alignment(0.0, 0.5)
1652 table.attach(label, 0, 1, 0, 1)
1653
1654 self._userName = gtk.Entry()
1655 self._userName.set_width_chars(16)
1656 #self._userName.connect("changed",
1657 # lambda button: self._updateForwardButton())
1658 self._userName.set_tooltip_text(xstr("simbrief_username_tooltip"))
1659 self._userName.set_text(userName)
1660 table.attach(self._userName, 1, 2, 0, 1)
1661 label.set_mnemonic_widget(self._userName)
1662
1663 label = gtk.Label(xstr("simbrief_password"))
1664 label.set_use_underline(True)
1665 label.set_alignment(0.0, 0.5)
1666 table.attach(label, 0, 1, 1, 2)
1667
1668 self._password = gtk.Entry()
1669 self._password.set_visibility(False)
1670 #self._password.connect("changed",
1671 # lambda button: self._updateForwardButton())
1672 self._password.set_tooltip_text(xstr("simbrief_password_tooltip"))
1673 self._password.set_text(password)
1674 table.attach(self._password, 1, 2, 1, 2)
1675 label.set_mnemonic_widget(self._password)
1676
1677 self._rememberButton = gtk.CheckButton(xstr("simbrief_remember_password"))
1678 self._rememberButton.set_use_underline(True)
1679 self._rememberButton.set_tooltip_text(xstr("simbrief_remember_tooltip"))
1680 self._rememberButton.set_active(rememberPassword)
1681 table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
1682
1683 @property
1684 def userName(self):
1685 """Get the user name entered."""
1686 return self._userName.get_text()
1687
1688 @property
1689 def password(self):
1690 """Get the password entered."""
1691 return self._password.get_text()
1692
1693 @property
1694 def rememberPassword(self):
1695 """Get whether the password is to be remembered."""
1696 return self._rememberButton.get_active()
1697
1698 def run(self):
1699 """Run the dialog."""
1700 self.show_all()
1701
1702 response = super(SimBriefCredentialsDialog, self).run()
1703
1704 self.hide()
1705
1706 return response
1707
1708#-----------------------------------------------------------------------------
1709
[687]1710class SimBriefSetupPage(Page):
1711 """Page for setting up some parameters for SimBrief."""
1712 monthNum2Name = [
1713 "JAN",
1714 "FEB",
1715 "MAR",
1716 "APR",
1717 "MAY",
1718 "JUN",
1719 "JUL",
1720 "AUG",
1721 "SEP",
1722 "OCT",
1723 "NOV",
1724 "DEC"
1725 ]
1726
[692]1727 progress2Message = {
1728 cef.SIMBRIEF_PROGRESS_SEARCHING_BROWSER: "simbrief_progress_searching_browser",
1729 cef.SIMBRIEF_PROGRESS_LOADING_FORM: "simbrief_progress_loading_form",
1730 cef.SIMBRIEF_PROGRESS_FILLING_FORM: "simbrief_progress_filling_form",
1731 cef.SIMBRIEF_PROGRESS_WAITING_LOGIN: "simbrief_progress_waiting_login",
1732 cef.SIMBRIEF_PROGRESS_LOGGING_IN: "simbrief_progress_logging_in",
1733 cef.SIMBRIEF_PROGRESS_WAITING_RESULT: "simbrief_progress_waiting_result",
1734 cef.SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING: "simbrief_progress_retrieving_briefing"
1735 }
1736
1737 result2Message = {
1738 cef.SIMBRIEF_RESULT_ERROR_OTHER: "simbrief_result_error_other",
1739 cef.SIMBRIEF_RESULT_ERROR_NO_FORM: "simbrief_result_error_no_form",
1740 cef.SIMBRIEF_RESULT_ERROR_NO_POPUP: "simbrief_result_error_no_popup",
1741 cef.SIMBRIEF_RESULT_ERROR_LOGIN_FAILED: "simbrief_result_error_login_failed"
1742 }
1743
[687]1744 @staticmethod
1745 def getHTMLFilePath():
1746 """Get the path of the HTML file to contain the generated flight
1747 plan."""
1748 if os.name=="nt":
1749 return os.path.join(tempfile.gettempdir(),
1750 "mlx_simbrief" +
1751 (".secondary" if secondaryInstallation else "") +
1752 ".html")
1753 else:
1754 import pwd
1755 return os.path.join(tempfile.gettempdir(),
1756 "mlx_simbrief." + pwd.getpwuid(os.getuid())[0] + "" +
1757 (".secondary" if secondaryInstallation else "") +
1758 ".html")
1759
1760 def __init__(self, wizard):
1761 """Construct the setup page."""
1762
1763 super(SimBriefSetupPage, self).__init__(wizard,
[691]1764 xstr("simbrief_setup_title"),
1765 xstr("simbrief_setup_help"),
1766 xstr("simbrief_setup_chelp"))
[687]1767
1768 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1769 xscale = 0.0, yscale = 0.0)
1770
[711]1771 table = gtk.Table(9, 3)
[687]1772 table.set_row_spacings(4)
1773 table.set_col_spacings(16)
1774 table.set_homogeneous(False)
1775 alignment.add(table)
1776 self.setMainWidget(alignment)
1777
[691]1778 label = gtk.Label(xstr("simbrief_username"))
[687]1779 label.set_use_underline(True)
1780 label.set_alignment(0.0, 0.5)
1781 table.attach(label, 0, 1, 0, 1)
1782
1783 self._userName = gtk.Entry()
1784 self._userName.set_width_chars(16)
1785 self._userName.connect("changed",
1786 lambda button: self._updateForwardButton())
[691]1787 self._userName.set_tooltip_text(xstr("simbrief_username_tooltip"))
[687]1788 table.attach(self._userName, 1, 2, 0, 1)
1789 label.set_mnemonic_widget(self._userName)
1790
[691]1791 label = gtk.Label(xstr("simbrief_password"))
[687]1792 label.set_use_underline(True)
1793 label.set_alignment(0.0, 0.5)
1794 table.attach(label, 0, 1, 1, 2)
1795
1796 self._password = gtk.Entry()
1797 self._password.set_visibility(False)
1798 self._password.connect("changed",
1799 lambda button: self._updateForwardButton())
[691]1800 self._password.set_tooltip_text(xstr("simbrief_password_tooltip"))
[687]1801 table.attach(self._password, 1, 2, 1, 2)
1802 label.set_mnemonic_widget(self._password)
1803
[690]1804 self._rememberButton = gtk.CheckButton(xstr("simbrief_remember_password"))
1805 self._rememberButton.set_use_underline(True)
1806 self._rememberButton.set_tooltip_text(xstr("simbrief_remember_tooltip"))
1807 table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
1808
[700]1809 self._credentialsCondition = threading.Condition()
1810 self._credentialsAvailable = False
1811 self._credentialsUserName = None
1812 self._credentialsPassword = None
1813
[707]1814 label = gtk.Label(xstr("simbrief_extra_fuel"))
1815 label.set_use_underline(True)
1816 label.set_alignment(0.0, 0.5)
1817 table.attach(label, 0, 1, 3, 4)
1818
1819 self._extraFuel = IntegerEntry(defaultValue = 0)
1820 self._extraFuel.set_width_chars(6)
1821 self._extraFuel.set_tooltip_text(xstr("simbrief_extra_fuel_tooltip"))
1822 table.attach(self._extraFuel, 1, 2, 3, 4)
1823 label.set_mnemonic_widget(self._extraFuel)
1824
1825 table.attach(gtk.Label("kg"), 2, 3, 3, 4)
1826
[708]1827 label = gtk.Label(xstr("simbrief_takeoff_runway"))
1828 label.set_use_underline(True)
1829 label.set_alignment(0.0, 0.5)
1830 table.attach(label, 0, 1, 4, 5)
1831
1832 self._takeoffRunway = gtk.Entry()
1833 self._takeoffRunway.set_width_chars(10)
1834 self._takeoffRunway.set_tooltip_text(xstr("simbrief_takeoff_runway_tooltip"))
1835 self._takeoffRunway.connect("changed", self._upperChanged)
1836 table.attach(self._takeoffRunway, 1, 2, 4, 5)
1837 label.set_mnemonic_widget(self._takeoffRunway)
1838
1839 label = gtk.Label(xstr("simbrief_landing_runway"))
1840 label.set_use_underline(True)
1841 label.set_alignment(0.0, 0.5)
1842 table.attach(label, 0, 1, 5, 6)
1843
1844 self._landingRunway = gtk.Entry()
1845 self._landingRunway.set_width_chars(10)
1846 self._landingRunway.set_tooltip_text(xstr("simbrief_takeoff_runway_tooltip"))
1847 self._landingRunway.connect("changed", self._upperChanged)
1848 table.attach(self._landingRunway, 1, 2, 5, 6)
1849 label.set_mnemonic_widget(self._landingRunway)
1850
[711]1851 label = gtk.Label(xstr("simbrief_climb_profile"))
1852 label.set_use_underline(True)
1853 label.set_alignment(0.0, 0.5)
1854 table.attach(label, 0, 1, 6, 7)
1855
1856 self._climbProfile = gtk.ComboBox()
1857 renderer = gtk.CellRendererText()
1858 self._climbProfile.pack_start(renderer, True)
1859 self._climbProfile.add_attribute(renderer, "text", 0)
1860 self._climbProfile.set_tooltip_text(xstr("simbrief_climb_profile_tooltip"))
1861 table.attach(self._climbProfile, 1, 2, 6, 7)
1862 label.set_mnemonic_widget(self._climbProfile)
1863
1864 label = gtk.Label(xstr("simbrief_cruise_profile"))
1865 label.set_use_underline(True)
1866 label.set_alignment(0.0, 0.5)
1867 table.attach(label, 0, 1, 7, 8)
1868
1869 self._cruiseProfile = gtk.ComboBox()
1870 renderer = gtk.CellRendererText()
1871 self._cruiseProfile.pack_start(renderer, True)
1872 self._cruiseProfile.add_attribute(renderer, "text", 0)
1873 self._cruiseProfile.set_tooltip_text(xstr("simbrief_cruise_profile_tooltip"))
1874 table.attach(self._cruiseProfile, 1, 2, 7, 8)
1875 label.set_mnemonic_widget(self._cruiseProfile)
1876
1877 label = gtk.Label(xstr("simbrief_descent_profile"))
1878 label.set_use_underline(True)
1879 label.set_alignment(0.0, 0.5)
1880 table.attach(label, 0, 1, 8, 9)
1881
1882 self._descentProfile = gtk.ComboBox()
1883 renderer = gtk.CellRendererText()
1884 self._descentProfile.pack_start(renderer, True)
1885 self._descentProfile.add_attribute(renderer, "text", 0)
1886 self._descentProfile.set_tooltip_text(xstr("simbrief_descent_profile_tooltip"))
1887 table.attach(self._descentProfile, 1, 2, 8, 9)
1888 label.set_mnemonic_widget(self._descentProfile)
1889
[687]1890 self.addCancelFlightButton()
1891
1892 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1893 self._button = self.addNextButton(clicked = self._forwardClicked)
1894
1895 def activate(self):
1896 """Activate the SimBrief setup page"""
[690]1897 config = self._wizard.gui.config
1898
1899 self._userName.set_text(config.simBriefUserName)
[704]1900 self._userName.set_sensitive(True)
1901
[690]1902 self._password.set_text(config.simBriefPassword)
[704]1903 self._password.set_sensitive(True)
1904
[690]1905 self._rememberButton.set_active(config.rememberSimBriefPassword)
[704]1906 self._rememberButton.set_sensitive(True)
[690]1907
[707]1908 self._extraFuel.set_int(0)
1909 self._extraFuel.set_sensitive(True)
1910
[708]1911 self._takeoffRunway.set_text("")
1912 self._takeoffRunway.set_sensitive(True)
1913
1914 self._landingRunway.set_text("")
1915 self._landingRunway.set_sensitive(True)
1916
[711]1917 simBriefData = self._wizard.gui.flight.aircraft.simBriefData
1918 for (control, profiles) in [(self._climbProfile,
1919 simBriefData.climbProfiles),
1920 (self._cruiseProfile,
1921 simBriefData.cruiseProfiles),
1922 (self._descentProfile,
1923 simBriefData.descentProfiles)]:
1924 model = gtk.ListStore(str)
1925 for profile in profiles:
1926 model.append([profile])
1927 control.set_model(model)
1928 control.set_sensitive(True)
1929
1930 self._climbProfile.set_active(0)
1931 self._cruiseProfile.set_active(0)
1932 self._descentProfile.set_active(0)
1933
[687]1934 self._updateForwardButton()
1935
1936 def _updateForwardButton(self):
1937 """Update the sensitivity of the forward button."""
1938 self._button.set_sensitive(len(self._userName.get_text())>0 and
1939 len(self._password.get_text())>0)
1940
1941 def _backClicked(self, button):
1942 """Called when the Back button is pressed."""
1943 self.goBack()
1944
1945 def _forwardClicked(self, button):
1946 if self._completed:
1947 self._wizard.nextPage()
1948 else:
[690]1949 config = self._wizard.gui.config
1950
1951 config.simBriefUserName = self._userName.get_text()
1952
1953 rememberPassword = self._rememberButton.get_active()
1954 config.simBriefPassword = \
1955 self._password.get_text() if rememberPassword else ""
1956 config.rememberSimBriefPassword = rememberPassword
1957
1958 config.save()
1959
[687]1960 plan = self._getPlan()
1961 print "plan:", plan
1962
[704]1963 self._userName.set_sensitive(False)
1964 self._password.set_sensitive(False)
1965 self._rememberButton.set_sensitive(False)
[707]1966 self._extraFuel.set_sensitive(False)
[708]1967 self._takeoffRunway.set_sensitive(False)
1968 self._landingRunway.set_sensitive(False)
[704]1969
[711]1970 self._climbProfile.set_sensitive(False)
1971 self._cruiseProfile.set_sensitive(False)
1972 self._descentProfile.set_sensitive(False)
1973
[687]1974 self._wizard.gui.beginBusy("Calling SimBrief...")
1975
1976 cef.callSimBrief(plan,
[700]1977 self._getCredentialsCallback,
[687]1978 self._simBriefProgressCallback,
1979 SimBriefSetupPage.getHTMLFilePath())
1980
1981 startSound(const.SOUND_NOTAM)
1982
[700]1983 def _getCredentialsCallback(self, count):
1984 """Called when the SimBrief home page requests the credentials."""
1985 with self._credentialsCondition:
1986 self._credentialsAvailable = False
1987
1988 gobject.idle_add(self._getCredentials, count)
1989
1990 while not self._credentialsAvailable:
1991 self._credentialsCondition.wait()
1992
1993 return (self._credentialsUserName, self._credentialsPassword)
1994
1995 def _getCredentials(self, count):
1996 """Get the credentials.
1997
1998 If count is 0, the user name and password entered into the setup page
1999 are returned. Otherwise a dialog box is displayed informing the user of
2000 invalid credentials and requesting another set of them."""
2001 with self._credentialsCondition:
2002 if count==0:
2003 self._credentialsUserName = self._userName.get_text()
2004 self._credentialsPassword = self._password.get_text()
2005 else:
2006 gui = self._wizard.gui
2007 config = gui.config
2008
2009 dialog = SimBriefCredentialsDialog(gui,
2010 config.simBriefUserName,
2011 config.simBriefPassword,
2012 config.rememberSimBriefPassword)
2013 response = dialog.run()
2014
2015 if response==RESPONSETYPE_OK:
2016 self._credentialsUserName = dialog.userName
2017 self._userName.set_text(self._credentialsUserName)
2018 self._credentialsPassword = dialog.password
2019 self._password.set_text(self._credentialsPassword)
2020 rememberPassword = dialog.rememberPassword
2021
2022 config.simBriefUserName = self._credentialsUserName
2023
2024 config.simBriefPassword = \
2025 self._credentialsPassword if rememberPassword else ""
2026 config.rememberSimBriefPassword = rememberPassword
2027
2028 config.save()
2029 else:
2030 self._credentialsUserName = None
2031 self._credentialsPassword = None
2032
2033 self._credentialsAvailable = True
2034 self._credentialsCondition.notify()
2035
[692]2036 def _simBriefProgressCallback(self, progress, result, flightInfo):
[687]2037 """Called by the SimBrief handling thread."""
[692]2038 gobject.idle_add(self._simBriefProgress, progress, result, flightInfo)
2039
2040 def _simBriefProgress(self, progress, result, flightInfo):
[687]2041 """The real SimBrief progress handler."""
[692]2042 print "_simBriefProgress", progress, result, flightInfo
2043 if result==cef.SIMBRIEF_RESULT_NONE:
2044 message = SimBriefSetupPage.progress2Message.get(progress,
2045 "simbrief_progress_unknown")
2046 self._wizard.gui.updateBusyState(xstr(message))
2047 else:
[687]2048 self._wizard.gui.endBusy()
[692]2049
2050 if result==cef.SIMBRIEF_RESULT_OK:
2051 self._wizard.nextPage()
2052 else:
2053 message = SimBriefSetupPage.result2Message.get(result,
2054 "simbrief_result_unknown")
2055 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
2056 type = MESSAGETYPE_ERROR,
2057 message_format =
2058 xstr(message) + "\n"+
2059 xstr("simbrief_cancelled"))
2060
2061 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
2062 dialog.set_title(WINDOW_TITLE_BASE)
2063 secondary = xstr("flightsel_save_failed_sec")
2064 dialog.format_secondary_markup(secondary)
2065 dialog.run()
2066 dialog.hide()
2067
2068 self._wizard.usingSimBrief = False
2069 self._wizard.jumpPage(2, fromPageShift = 1)
[687]2070
2071 def _getPlan(self):
2072 """Get the flight plan data for SimBrief."""
2073 plan = {
2074 "airline": "MAH",
2075 "selcal": "XXXX",
2076 "fuelfactor": "P000",
2077 "contpct": "0.05",
2078 "resvrule": "45",
2079 "taxiout": "10",
2080 "taxiin": "10",
2081 "civalue": "AUTO"
2082 }
2083
2084 wizard = self._wizard
2085 gui = wizard.gui
2086
2087 loginResult = wizard.loginResult
2088 plan["cpt"] = loginResult.pilotName
2089 plan["pid"] = loginResult.pilotID
2090
2091 bookedFlight = wizard.bookedFlight
2092 plan["fltnum"] = wizard.bookedFlight.callsign[2:]
2093 plan["type"] = const.icaoCodes[bookedFlight.aircraftType]
2094 plan["orig"] = bookedFlight.departureICAO
2095 plan["dest"] = bookedFlight.arrivalICAO
2096 plan["reg"] = bookedFlight.tailNumber
2097 plan["fin"] = bookedFlight.tailNumber[3:]
2098 plan["pax"] = str(bookedFlight.numPassengers)
2099
2100 departureTime = bookedFlight.departureTime
2101 plan["date"] = "%d%s%d" % (departureTime.day,
2102 SimBriefSetupPage.monthNum2Name[departureTime.month-1],
2103 departureTime.year%100)
2104 plan["deph"] = str(departureTime.hour)
2105 plan["depm"] = str(departureTime.minute)
2106
2107 arrivalTime = bookedFlight.arrivalTime
2108 plan["steh"] = str(arrivalTime.hour)
2109 plan["stem"] = str(arrivalTime.minute)
2110
[705]2111 plan["manualzfw"] = str(wizard.zfw / 1000.0)
2112 plan["cargo"] = str((wizard.bagWeight + wizard.cargoWeight + wizard.mailWeight)/1000.0)
[687]2113
2114 plan["route"] = wizard.route
2115 plan["fl"] = str(wizard.filedCruiseAltitude)
2116 plan["altn"] = wizard.alternate
2117
[707]2118 plan["addedfuel"] = str(self._extraFuel.get_int() / 1000.0)
[708]2119 plan["origrwy"] = self._takeoffRunway.get_text()
2120 plan["destrwy"] = self._landingRunway.get_text()
[711]2121
2122 for (key, control) in [("climb", self._climbProfile),
2123 ("cruise", self._cruiseProfile),
2124 ("descent", self._descentProfile)]:
2125 model = control.get_model()
2126 active = control.get_active_iter()
2127 value = model.get_value(active, 0)
2128 plan[key] = value
[687]2129
2130 return plan
2131
[708]2132 def _upperChanged(self, entry, arg = None):
2133 """Called when the value of some entry widget has changed and the value
2134 should be converted to uppercase."""
2135 entry.set_text(entry.get_text().upper())
2136
[687]2137#-----------------------------------------------------------------------------
2138
2139class SimBriefingPage(Page):
2140 """Page to display the SimBrief HTML briefing."""
2141 def __init__(self, wizard):
2142 """Construct the setup page."""
2143
2144 super(SimBriefingPage, self).__init__(wizard,
2145 "SimBrief flight plan", "")
2146
2147 self._alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2148 xscale = 1.0, yscale = 1.0)
2149
[698]2150 self._container = cef.getContainer()
2151 self._alignment.add(self._container)
2152
[687]2153 self.setMainWidget(self._alignment)
2154
[698]2155 self._browser = None
2156
[687]2157 self.addCancelFlightButton()
2158
2159 self.addPreviousButton(clicked = self._backClicked)
2160
2161 self._button = self.addNextButton(clicked = self._forwardClicked)
2162 self._button.set_label(xstr("briefing_button"))
2163 self._button.set_has_tooltip(False)
2164 self._button.set_use_stock(False)
2165
2166 def activate(self):
2167 """Activate the SimBrief flight plan page"""
[698]2168 if self._browser is None:
2169 url = "file://" + SimBriefSetupPage.getHTMLFilePath()
2170 self._browser = cef.startInContainer(self._container, url)
2171 else:
2172 self._browser.Reload()
[687]2173
2174 def _backClicked(self, button):
2175 """Called when the Back button has been pressed."""
2176 self.goBack()
2177
2178 def _forwardClicked(self, button):
2179 """Called when the Forward button has been pressed."""
2180 if not self._completed:
2181 self._button.set_label(xstr("button_next"))
2182 self._button.set_tooltip_text(xstr("button_next_tooltip"))
2183 self._wizard.usingSimBrief = True
2184 self.complete()
2185
2186 self._wizard.nextPage()
2187
2188#-----------------------------------------------------------------------------
2189
[141]2190class FuelTank(gtk.VBox):
2191 """Widget for the fuel tank."""
2192 def __init__(self, fuelTank, name, capacity, currentWeight):
2193 """Construct the widget for the tank with the given name."""
2194 super(FuelTank, self).__init__()
2195
[146]2196 self._enabled = True
[141]2197 self.fuelTank = fuelTank
2198 self.capacity = capacity
2199 self.currentWeight = currentWeight
2200 self.expectedWeight = currentWeight
2201
2202 label = gtk.Label("<b>" + name + "</b>")
2203 label.set_use_markup(True)
[145]2204 label.set_use_underline(True)
[141]2205 label.set_justify(JUSTIFY_CENTER)
2206 label.set_alignment(0.5, 1.0)
2207 self.pack_start(label, False, False, 4)
2208
[142]2209 self._tankFigure = gtk.EventBox()
[141]2210 self._tankFigure.set_size_request(38, -1)
[142]2211 self._tankFigure.set_visible_window(False)
[144]2212 self._tankFigure.set_tooltip_markup(xstr("fuel_tank_tooltip"))
[141]2213
2214 if pygobject:
2215 self._tankFigure.connect("draw", self._drawTankFigure)
2216 else:
2217 self._tankFigure.connect("expose_event", self._drawTankFigure)
[144]2218 self._tankFigure.connect("button_press_event", self._buttonPressed)
2219 self._tankFigure.connect("motion_notify_event", self._motionNotify)
2220 self._tankFigure.connect("scroll-event", self._scrolled)
[347]2221
[141]2222 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2223 xscale = 0.0, yscale = 1.0)
[142]2224 alignment.add(self._tankFigure)
[141]2225
2226 self.pack_start(alignment, True, True, 4)
2227
2228 self._expectedButton = gtk.SpinButton()
2229 self._expectedButton.set_numeric(True)
2230 self._expectedButton.set_range(0, self.capacity)
2231 self._expectedButton.set_increments(10, 100)
2232 self._expectedButton.set_value(currentWeight)
2233 self._expectedButton.set_alignment(1.0)
2234 self._expectedButton.set_width_chars(5)
2235 self._expectedButton.connect("value-changed", self._expectedChanged)
2236
[145]2237 label.set_mnemonic_widget(self._expectedButton)
2238
[141]2239 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2240 xscale = 0.0, yscale = 1.0)
[347]2241 alignment.add(self._expectedButton)
[141]2242 self.pack_start(alignment, False, False, 4)
2243
2244 def setCurrent(self, currentWeight):
2245 """Set the current weight."""
2246 self.currentWeight = currentWeight
2247 self._redraw()
2248
2249 def isCorrect(self):
2250 """Determine if the contents of the fuel tank are as expected"""
2251 return abs(self.expectedWeight - self.currentWeight)<=1
2252
2253 def disable(self):
2254 """Disable the fuel tank."""
2255 self._expectedButton.set_sensitive(False)
[146]2256 self._enabled = False
[141]2257
2258 def _redraw(self):
2259 """Redraw the tank figure."""
2260 self._tankFigure.queue_draw()
2261
[142]2262 def _drawTankFigure(self, tankFigure, eventOrContext):
[141]2263 """Draw the tank figure."""
2264 triangleSize = 5
[142]2265
2266 context = eventOrContext if pygobject else tankFigure.window.cairo_create()
2267 (xOffset, yOffset) = (0, 0) if pygobject \
2268 else (tankFigure.allocation.x, tankFigure.allocation.y)
[347]2269
[142]2270 width = tankFigure.get_allocated_width() if pygobject \
2271 else tankFigure.allocation.width
2272 height = tankFigure.get_allocated_height() if pygobject \
2273 else tankFigure.allocation.height
[141]2274
2275 rectangleX0 = triangleSize
2276 rectangleY0 = triangleSize
2277 rectangleX1 = width - 1 - triangleSize
2278 rectangleY1 = height - 1 - triangleSize
2279 rectangleLineWidth = 2.0
2280
2281 context.set_source_rgb(0.0, 0.0, 0.0)
2282 context.set_line_width(rectangleLineWidth)
[142]2283 context.rectangle(xOffset + rectangleX0 + rectangleLineWidth/2,
2284 yOffset + rectangleY0 + rectangleLineWidth/2,
[141]2285 rectangleX1 - rectangleX0 - rectangleLineWidth,
2286 rectangleY1 - rectangleY0 - rectangleLineWidth)
2287 context.stroke()
2288
2289 rectangleInnerLeft = rectangleX0 + rectangleLineWidth
2290 rectangleInnerRight = rectangleX1 - rectangleLineWidth
[144]2291 self._rectangleInnerTop = rectangleInnerTop = rectangleY0 + rectangleLineWidth
2292 self._rectangleInnerBottom = rectangleInnerBottom = rectangleY1 - rectangleLineWidth
[141]2293
2294 rectangleInnerWidth = rectangleInnerRight - rectangleInnerLeft
2295 rectangleInnerHeight = rectangleInnerBottom - rectangleInnerTop
2296
2297 context.set_source_rgb(1.0, 0.9, 0.6)
2298 currentHeight = self.currentWeight * rectangleInnerHeight / self.capacity
2299 currentX = rectangleInnerTop + rectangleInnerHeight - currentHeight
[142]2300 context.rectangle(xOffset + rectangleInnerLeft,
2301 yOffset + rectangleInnerTop +
2302 rectangleInnerHeight - currentHeight,
[141]2303 rectangleInnerWidth, currentHeight)
2304 context.fill()
2305
2306 expectedHeight = self.expectedWeight * rectangleInnerHeight / self.capacity
2307 expectedY = rectangleInnerTop + rectangleInnerHeight - expectedHeight
2308
2309 context.set_line_width(1.5)
2310 context.set_source_rgb(0.0, 0.85, 0.85)
[142]2311 context.move_to(xOffset + rectangleX0, yOffset + expectedY)
2312 context.line_to(xOffset + rectangleX1, yOffset + expectedY)
[141]2313 context.stroke()
2314
2315 context.set_line_width(0.0)
[142]2316 context.move_to(xOffset + 0, yOffset + expectedY - triangleSize)
2317 context.line_to(xOffset + 0, yOffset + expectedY + triangleSize)
2318 context.line_to(xOffset + rectangleX0 + 1, yOffset + expectedY)
2319 context.line_to(xOffset + 0, yOffset + expectedY - triangleSize)
[141]2320 context.fill()
2321
2322 context.set_line_width(0.0)
[142]2323 context.move_to(xOffset + width, yOffset + expectedY - triangleSize)
2324 context.line_to(xOffset + width, yOffset + expectedY + triangleSize)
2325 context.line_to(xOffset + rectangleX1 - 1, yOffset + expectedY)
2326 context.line_to(xOffset + width, yOffset + expectedY - triangleSize)
[141]2327 context.fill()
2328
[142]2329 return True
2330
[144]2331 def _setExpectedFromY(self, y):
2332 """Set the expected weight from the given Y-coordinate."""
2333 level = (self._rectangleInnerBottom - y) / \
2334 (self._rectangleInnerBottom - self._rectangleInnerTop)
2335 level = min(1.0, max(0.0, level))
2336 self._expectedButton.set_value(level * self.capacity)
[347]2337
[144]2338 def _buttonPressed(self, tankFigure, event):
2339 """Called when a button is pressed in the figure.
2340
2341 The expected level will be set there."""
[146]2342 if self._enabled and event.button==1:
[144]2343 self._setExpectedFromY(event.y)
[347]2344
[144]2345 def _motionNotify(self, tankFigure, event):
2346 """Called when the mouse pointer moves within the area of a tank figure."""
[347]2347 if self._enabled and event.state==BUTTON1_MASK:
[144]2348 self._setExpectedFromY(event.y)
2349
2350 def _scrolled(self, tankFigure, event):
2351 """Called when a scroll event is received."""
[146]2352 if self._enabled:
2353 increment = 1 if event.state==CONTROL_MASK \
2354 else 100 if event.state==SHIFT_MASK \
2355 else 10 if event.state==0 else 0
2356 if increment!=0:
2357 if event.direction==SCROLL_DOWN:
2358 increment *= -1
2359 self._expectedButton.spin(SPIN_USER_DEFINED, increment)
[347]2360
[141]2361 def _expectedChanged(self, spinButton):
2362 """Called when the expected value has changed."""
2363 self.expectedWeight = spinButton.get_value_as_int()
[347]2364 self._redraw()
[141]2365
2366#-----------------------------------------------------------------------------
2367
2368class FuelPage(Page):
2369 """The page containing the fuel tank filling."""
2370 _pumpStep = 0.02
[347]2371
[141]2372 def __init__(self, wizard):
2373 """Construct the page."""
2374 super(FuelPage, self).__init__(wizard, xstr("fuel_title"),
[592]2375 xstr("fuel_help_pre") +
2376 xstr("fuel_help_post"),
[141]2377 completedHelp = xstr("fuel_chelp"))
2378
2379 self._fuelTanks = []
2380 self._fuelTable = None
2381 self._fuelAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2382 xscale = 0.0, yscale = 1.0)
2383 self.setMainWidget(self._fuelAlignment)
2384
[274]2385 tankData = [(tank, 2500, 3900) for tank in acft.mostFuelTanks]
2386 self._setupTanks(tankData)
[141]2387
[208]2388 self.addCancelFlightButton()
2389
[141]2390 self._backButton = self.addPreviousButton(clicked = self._backClicked)
2391 self._button = self.addNextButton(clicked = self._forwardClicked)
2392
2393 self._pumpIndex = 0
2394
[145]2395 def activate(self):
2396 """Activate the page."""
[274]2397 self._setupTanks(self._wizard._fuelData)
[141]2398
[592]2399 aircraft = self._wizard.gui.flight.aircraft
2400 minLandingFuel = aircraft.minLandingFuel
2401 recommendedLandingFuel = aircraft.recommendedLandingFuel
2402
2403 middleHelp = "" if minLandingFuel is None else \
2404 (xstr("fuel_help_min") % (minLandingFuel,)) \
2405 if recommendedLandingFuel is None else \
2406 (xstr("fuel_help_min_rec") % (minLandingFuel,
2407 recommendedLandingFuel))
2408 self.setHelp(xstr("fuel_help_pre") + middleHelp + xstr("fuel_help_post"))
2409
[141]2410 def finalize(self):
2411 """Finalize the page."""
2412 for fuelTank in self._fuelTanks:
2413 fuelTank.disable()
2414
2415 def _backClicked(self, button):
2416 """Called when the Back button is pressed."""
2417 self.goBack()
[347]2418
[141]2419 def _forwardClicked(self, button):
2420 """Called when the forward button is clicked."""
2421 if not self._completed:
2422 self._pumpIndex = 0
2423 self._wizard.gui.beginBusy(xstr("fuel_pump_busy"))
2424 self._pump()
[687]2425 elif self._wizard.usingSimBrief:
2426 self._wizard.jumpPage(3)
[141]2427 else:
[347]2428 self._wizard.nextPage()
[141]2429
[274]2430 def _setupTanks(self, tankData):
[141]2431 """Setup the tanks for the given data."""
[274]2432 numTanks = len(tankData)
[141]2433 if self._fuelTable is not None:
2434 self._fuelAlignment.remove(self._fuelTable)
2435
2436 self._fuelTanks = []
2437 self._fuelTable = gtk.Table(numTanks, 1)
2438 self._fuelTable.set_col_spacings(16)
[274]2439 index = 0
2440 for (tank, current, capacity) in tankData:
[141]2441 fuelTank = FuelTank(tank,
2442 xstr("fuel_tank_" +
2443 const.fuelTank2string(tank)),
2444 capacity, current)
[274]2445 self._fuelTable.attach(fuelTank, index, index+1, 0, 1)
[141]2446 self._fuelTanks.append(fuelTank)
[274]2447 index += 1
[347]2448
[141]2449 self._fuelAlignment.add(self._fuelTable)
2450 self.show_all()
2451
2452 def _pump(self):
2453 """Perform one step of pumping.
2454
2455 It is checked, if the current tank's contents are of the right
2456 quantity. If not, it is filled one step further to the desired
2457 contents. Otherwise the next tank is started. If all tanks are are
2458 filled, the next page is selected."""
2459 numTanks = len(self._fuelTanks)
2460
2461 fuelTank = None
2462 while self._pumpIndex < numTanks:
2463 fuelTank = self._fuelTanks[self._pumpIndex]
2464 if fuelTank.isCorrect():
2465 self._pumpIndex += 1
2466 fuelTank = None
2467 else:
2468 break
2469
2470 if fuelTank is None:
2471 self._wizard.gui.endBusy()
[687]2472 if self._wizard.usingSimBrief:
2473 self._wizard.gui.startMonitoring()
2474 self._wizard.jumpPage(3)
2475 else:
2476 bookedFlight = self._wizard._bookedFlight
2477 self._wizard.gui.beginBusy(xstr("route_down_notams"))
2478 self._wizard.gui.webHandler.getNOTAMs(self._notamsCallback,
2479 bookedFlight.departureICAO,
2480 bookedFlight.arrivalICAO)
2481 startSound(const.SOUND_NOTAM)
[141]2482 else:
2483 currentLevel = fuelTank.currentWeight / fuelTank.capacity
2484 expectedLevel = fuelTank.expectedWeight / fuelTank.capacity
2485 if currentLevel<expectedLevel:
2486 currentLevel += FuelPage._pumpStep
2487 if currentLevel>expectedLevel: currentLevel = expectedLevel
2488 else:
2489 currentLevel -= FuelPage._pumpStep
2490 if currentLevel<expectedLevel: currentLevel = expectedLevel
2491 fuelTank.setCurrent(currentLevel * fuelTank.capacity)
2492 self._wizard.gui.simulator.setFuelLevel([(fuelTank.fuelTank,
2493 currentLevel)])
2494 gobject.timeout_add(50, self._pump)
[347]2495
[64]2496 def _notamsCallback(self, returned, result):
2497 """Callback for the NOTAMs."""
2498 gobject.idle_add(self._handleNOTAMs, returned, result)
2499
2500 def _handleNOTAMs(self, returned, result):
2501 """Handle the NOTAMs."""
2502 if returned:
2503 self._wizard._departureNOTAMs = result.departureNOTAMs
2504 self._wizard._arrivalNOTAMs = result.arrivalNOTAMs
[67]2505 else:
2506 self._wizard._departureNOTAMs = None
2507 self._wizard._arrivalNOTAMs = None
[64]2508
[67]2509 bookedFlight = self._wizard._bookedFlight
[107]2510 self._wizard.gui.beginBusy(xstr("route_down_metars"))
[67]2511 self._wizard.gui.webHandler.getMETARs(self._metarsCallback,
2512 [bookedFlight.departureICAO,
2513 bookedFlight.arrivalICAO])
2514
2515 def _metarsCallback(self, returned, result):
2516 """Callback for the METARs."""
2517 gobject.idle_add(self._handleMETARs, returned, result)
2518
2519 def _handleMETARs(self, returned, result):
2520 """Handle the METARs."""
2521 self._wizard._departureMETAR = None
2522 self._wizard._arrivalMETAR = None
2523 bookedFlight = self._wizard._bookedFlight
2524 if returned:
2525 if bookedFlight.departureICAO in result.metars:
2526 self._wizard._departureMETAR = result.metars[bookedFlight.departureICAO]
2527 if bookedFlight.arrivalICAO in result.metars:
2528 self._wizard._arrivalMETAR = result.metars[bookedFlight.arrivalICAO]
2529
2530 self._wizard.gui.endBusy()
[70]2531 self._backButton.set_sensitive(True)
2532 self._button.set_sensitive(True)
[62]2533 self._wizard.nextPage()
2534
2535#-----------------------------------------------------------------------------
2536
[67]2537class BriefingPage(Page):
2538 """Page for the briefing."""
2539 def __init__(self, wizard, departure):
2540 """Construct the briefing page."""
2541 self._departure = departure
[347]2542
[107]2543 title = xstr("briefing_title") % (1 if departure else 2,
2544 xstr("briefing_departure")
2545 if departure
2546 else xstr("briefing_arrival"))
2547 super(BriefingPage, self).__init__(wizard, title, xstr("briefing_help"),
2548 completedHelp = xstr("briefing_chelp"))
[64]2549
2550 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2551 xscale = 1.0, yscale = 1.0)
2552
2553 mainBox = gtk.VBox()
2554 alignment.add(mainBox)
2555 self.setMainWidget(alignment)
2556
[67]2557 self._notamsFrame = gtk.Frame()
[107]2558 self._notamsFrame.set_label(xstr("briefing_notams_init"))
[64]2559 scrolledWindow = gtk.ScrolledWindow()
[67]2560 scrolledWindow.set_size_request(-1, 128)
[94]2561 # FIXME: these constants should be in common
[69]2562 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
2563 else gtk.POLICY_AUTOMATIC,
2564 gtk.PolicyType.AUTOMATIC if pygobject
2565 else gtk.POLICY_AUTOMATIC)
[67]2566 self._notams = gtk.TextView()
2567 self._notams.set_editable(False)
2568 self._notams.set_accepts_tab(False)
2569 self._notams.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
2570 scrolledWindow.add(self._notams)
2571 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
2572 xscale = 1.0, yscale = 1.0)
2573 alignment.set_padding(padding_top = 4, padding_bottom = 0,
2574 padding_left = 0, padding_right = 0)
2575 alignment.add(scrolledWindow)
2576 self._notamsFrame.add(alignment)
2577 mainBox.pack_start(self._notamsFrame, True, True, 4)
[347]2578
[67]2579 self._metarFrame = gtk.Frame()
[107]2580 self._metarFrame.set_label(xstr("briefing_metar_init"))
[67]2581 scrolledWindow = gtk.ScrolledWindow()
2582 scrolledWindow.set_size_request(-1, 32)
[69]2583 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
2584 else gtk.POLICY_AUTOMATIC,
2585 gtk.PolicyType.AUTOMATIC if pygobject
2586 else gtk.POLICY_AUTOMATIC)
[138]2587
[586]2588 self._updatingMETAR = False
[138]2589
[67]2590 self._metar = gtk.TextView()
2591 self._metar.set_accepts_tab(False)
2592 self._metar.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
[106]2593 self._metar.get_buffer().connect("changed", self._metarChanged)
[138]2594 self._metar.get_buffer().connect_after("insert-text", self._metarInserted)
[67]2595 scrolledWindow.add(self._metar)
2596 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
2597 xscale = 1.0, yscale = 1.0)
2598 alignment.set_padding(padding_top = 4, padding_bottom = 0,
2599 padding_left = 0, padding_right = 0)
2600 alignment.add(scrolledWindow)
2601 self._metarFrame.add(alignment)
2602 mainBox.pack_start(self._metarFrame, True, True, 4)
[106]2603 self.metarEdited = False
[64]2604
[208]2605 self.addCancelFlightButton()
2606
[107]2607 self.addPreviousButton(clicked = self._backClicked)
2608 self._button = self.addNextButton(clicked = self._forwardClicked)
[64]2609
[97]2610 @property
2611 def metar(self):
2612 """Get the METAR on the page."""
2613 buffer = self._metar.get_buffer()
2614 return buffer.get_text(buffer.get_start_iter(),
[347]2615 buffer.get_end_iter(), True)
[97]2616
[106]2617 def setMETAR(self, metar):
[586]2618 """Set the METAR."""
[106]2619 self._metar.get_buffer().set_text(metar)
2620 self.metarEdited = False
2621
[586]2622 def changeMETAR(self, metar):
2623 """Change the METAR as a result of an edit on one of the other
2624 pages."""
2625 self._updatingMETAR = True
2626 self._metar.get_buffer().set_text(metar)
2627 self._updatingMETAR = False
[588]2628
2629 self._updateButton()
[586]2630 self.metarEdited = True
2631
[64]2632 def activate(self):
2633 """Activate the page."""
[70]2634 if not self._departure:
[107]2635 self._button.set_label(xstr("briefing_button"))
2636 self._button.set_has_tooltip(False)
[70]2637 self._button.set_use_stock(False)
2638
2639 bookedFlight = self._wizard._bookedFlight
[67]2640
[70]2641 icao = bookedFlight.departureICAO if self._departure \
2642 else bookedFlight.arrivalICAO
2643 notams = self._wizard._departureNOTAMs if self._departure \
2644 else self._wizard._arrivalNOTAMs
2645 metar = self._wizard._departureMETAR if self._departure \
2646 else self._wizard._arrivalMETAR
[64]2647
[107]2648 self._notamsFrame.set_label(xstr("briefing_notams_template") % (icao,))
[70]2649 buffer = self._notams.get_buffer()
2650 if notams is None:
[107]2651 buffer.set_text(xstr("briefing_notams_failed"))
[92]2652 elif not notams:
[107]2653 buffer.set_text(xstr("briefing_notams_missing"))
[70]2654 else:
2655 s = ""
2656 for notam in notams:
[570]2657 s += str(notam)
[70]2658 s += "-------------------- * --------------------\n"
2659 buffer.set_text(s)
[67]2660
[107]2661 self._metarFrame.set_label(xstr("briefing_metar_template") % (icao,))
[70]2662 buffer = self._metar.get_buffer()
[586]2663 self._updatingMETAR = True
[70]2664 if metar is None:
[584]2665 buffer.set_text("")
2666 self.setHelp(xstr("briefing_help_nometar"))
[70]2667 else:
2668 buffer.set_text(metar)
[586]2669 self._updatingMETAR = False
[584]2670 self._updateButton()
[67]2671
[107]2672 label = self._metarFrame.get_label_widget()
2673 label.set_use_underline(True)
2674 label.set_mnemonic_widget(self._metar)
2675
[106]2676 self.metarEdited = False
2677
[70]2678 def _backClicked(self, button):
2679 """Called when the Back button is pressed."""
2680 self.goBack()
[347]2681
[67]2682 def _forwardClicked(self, button):
[64]2683 """Called when the forward button is clicked."""
[70]2684 if not self._departure:
[94]2685 if not self._completed:
[70]2686 self._wizard.gui.startMonitoring()
[107]2687 self._button.set_label(xstr("button_next"))
2688 self._button.set_tooltip_text(xstr("button_next_tooltip"))
[94]2689 self.complete()
[71]2690
2691 self._wizard.nextPage()
2692
[106]2693 def _metarChanged(self, buffer):
2694 """Called when the METAR has changed."""
[586]2695 print "BriefingPage.metarChanged", self._updatingMETAR
2696 if not self._updatingMETAR:
[138]2697 self.metarEdited = True
[584]2698 self._updateButton()
[586]2699 metar = buffer.get_text(buffer.get_start_iter(),
2700 buffer.get_end_iter(), True)
2701 self._wizard.metarChanged(metar, self)
[138]2702
2703 def _metarInserted(self, textBuffer, iter, text, length):
2704 """Called when new characters are inserted into the METAR.
2705
2706 It uppercases all characters."""
[586]2707 print "BriefingPage.metarInserted", self._updatingMETAR
2708 if not self._updatingMETAR:
2709 self._updatingMETAR = True
[138]2710
2711 iter1 = iter.copy()
2712 iter1.backward_chars(length)
2713 textBuffer.delete(iter, iter1)
2714
2715 textBuffer.insert(iter, text.upper())
2716
[586]2717 self._updatingMETAR = False
[106]2718
[584]2719 def _updateButton(self):
2720 """Update the sensitivity of the Next button based on the contents of
2721 the METAR field."""
2722 buffer = self._metar.get_buffer()
2723 self._button.set_sensitive(buffer.get_text(buffer.get_start_iter(),
2724 buffer.get_end_iter(),
2725 True)!="")
2726
2727
[71]2728#-----------------------------------------------------------------------------
2729
2730class TakeoffPage(Page):
2731 """Page for entering the takeoff data."""
2732 def __init__(self, wizard):
2733 """Construct the takeoff page."""
[107]2734 super(TakeoffPage, self).__init__(wizard, xstr("takeoff_title"),
2735 xstr("takeoff_help"),
2736 completedHelp = xstr("takeoff_chelp"))
[71]2737
[101]2738 self._forwardAllowed = False
2739
[71]2740 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2741 xscale = 0.0, yscale = 0.0)
2742
[586]2743 table = gtk.Table(9, 24)
[71]2744 table.set_row_spacings(4)
2745 table.set_col_spacings(16)
2746 table.set_homogeneous(False)
2747 alignment.add(table)
2748 self.setMainWidget(alignment)
2749
[586]2750 row = 0
2751
2752 label = gtk.Label(xstr("takeoff_metar"))
2753 label.set_use_underline(True)
2754 label.set_alignment(0.0, 0.5)
2755 table.attach(label, 0, 1, row, row+1)
2756
2757 self._metar = gtk.Entry()
2758 self._metar.set_width_chars(40)
[590]2759 self._metar.set_tooltip_text(xstr("takeoff_metar_tooltip"))
[586]2760 self._metar.connect("changed", self._metarChanged)
[587]2761 self._metar.get_buffer().connect_after("inserted-text", self._metarInserted)
[586]2762 table.attach(self._metar, 1, 24, row, row+1)
2763 label.set_mnemonic_widget(self._metar)
2764
2765 self._updatingMETAR = False
2766
2767 row += 1
2768
[107]2769 label = gtk.Label(xstr("takeoff_runway"))
[71]2770 label.set_use_underline(True)
2771 label.set_alignment(0.0, 0.5)
[586]2772 table.attach(label, 0, 1, row, row+1)
[71]2773
2774 self._runway = gtk.Entry()
2775 self._runway.set_width_chars(10)
[107]2776 self._runway.set_tooltip_text(xstr("takeoff_runway_tooltip"))
[138]2777 self._runway.connect("changed", self._upperChanged)
[586]2778 table.attach(self._runway, 1, 3, row, row+1)
[71]2779 label.set_mnemonic_widget(self._runway)
[347]2780
[586]2781 row += 1
2782
[107]2783 label = gtk.Label(xstr("takeoff_sid"))
[71]2784 label.set_use_underline(True)
2785 label.set_alignment(0.0, 0.5)
[586]2786 table.attach(label, 0, 1, row, row+1)
[71]2787
[621]2788 if pygobject:
2789 self._sid = gtk.ComboBox.new_with_model_and_entry(comboModel)
2790 else:
2791 self._sid = gtk.ComboBoxEntry(comboModel)
2792
2793 self._sid.set_entry_text_column(0)
2794 self._sid.get_child().set_width_chars(10)
[107]2795 self._sid.set_tooltip_text(xstr("takeoff_sid_tooltip"))
[621]2796 self._sid.connect("changed", self._upperChangedComboBox)
[586]2797 table.attach(self._sid, 1, 3, row, row+1)
[71]2798 label.set_mnemonic_widget(self._sid)
[347]2799
[586]2800 row += 1
2801
[107]2802 label = gtk.Label(xstr("takeoff_v1"))
[71]2803 label.set_use_markup(True)
2804 label.set_use_underline(True)
2805 label.set_alignment(0.0, 0.5)
[586]2806 table.attach(label, 0, 1, row, row+1)
[71]2807
[84]2808 self._v1 = IntegerEntry()
[86]2809 self._v1.set_width_chars(4)
[241]2810 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip_knots"))
[101]2811 self._v1.connect("integer-changed", self._valueChanged)
[586]2812 table.attach(self._v1, 2, 3, row, row+1)
[71]2813 label.set_mnemonic_widget(self._v1)
[241]2814
2815 self._v1Unit = gtk.Label(xstr("label_knots"))
[384]2816 self._v1Unit.set_alignment(0.0, 0.5)
[586]2817 table.attach(self._v1Unit, 3, 4, row, row+1)
2818
2819 row += 1
[347]2820
[107]2821 label = gtk.Label(xstr("takeoff_vr"))
[71]2822 label.set_use_markup(True)
2823 label.set_use_underline(True)
2824 label.set_alignment(0.0, 0.5)
[586]2825 table.attach(label, 0, 1, row, row+1)
[71]2826
[84]2827 self._vr = IntegerEntry()
[86]2828 self._vr.set_width_chars(4)
[241]2829 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip_knots"))
[101]2830 self._vr.connect("integer-changed", self._valueChanged)
[586]2831 table.attach(self._vr, 2, 3, row, row+1)
[71]2832 label.set_mnemonic_widget(self._vr)
[347]2833
[241]2834 self._vrUnit = gtk.Label(xstr("label_knots"))
[384]2835 self._vrUnit.set_alignment(0.0, 0.5)
[586]2836 table.attach(self._vrUnit, 3, 4, row, row+1)
2837
2838 row += 1
[347]2839
[107]2840 label = gtk.Label(xstr("takeoff_v2"))
[71]2841 label.set_use_markup(True)
2842 label.set_use_underline(True)
2843 label.set_alignment(0.0, 0.5)
[586]2844 table.attach(label, 0, 1, row, row+1)
[71]2845
[84]2846 self._v2 = IntegerEntry()
[86]2847 self._v2.set_width_chars(4)
[241]2848 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip_knots"))
[101]2849 self._v2.connect("integer-changed", self._valueChanged)
[586]2850 table.attach(self._v2, 2, 3, row, row+1)
[71]2851 label.set_mnemonic_widget(self._v2)
[347]2852
[241]2853 self._v2Unit = gtk.Label(xstr("label_knots"))
[384]2854 self._v2Unit.set_alignment(0.0, 0.5)
[586]2855 table.attach(self._v2Unit, 3, 4, row, row+1)
2856
2857 row += 1
[71]2858
[512]2859 self._derateType = acft.DERATE_NONE
[384]2860
2861 self._derateLabel = gtk.Label()
2862 self._derateLabel.set_use_underline(True)
2863 self._derateLabel.set_markup(xstr("takeoff_derate_tupolev"))
2864 self._derateLabel.set_alignment(0.0, 0.5)
[586]2865 table.attach(self._derateLabel, 0, 1, row, row+1)
[384]2866
[512]2867 self._derate = gtk.Alignment()
[586]2868 table.attach(self._derate, 2, 4, row, row+1)
[512]2869 self._derateWidget = None
2870 self._derateEntry = None
2871 self._derateUnit = None
2872 self._derateButtons = None
[384]2873
[586]2874 row += 1
2875
[391]2876 self._antiIceOn = gtk.CheckButton(xstr("takeoff_antiice"))
2877 self._antiIceOn.set_use_underline(True)
2878 self._antiIceOn.set_tooltip_text(xstr("takeoff_antiice_tooltip"))
[586]2879 table.attach(self._antiIceOn, 2, 4, row, row+1)
2880
2881 row += 1
[391]2882
[349]2883 self._rto = gtk.CheckButton(xstr("takeoff_rto"))
2884 self._rto.set_use_underline(True)
2885 self._rto.set_tooltip_text(xstr("takeoff_rto_tooltip"))
2886 self._rto.connect("toggled", self._rtoToggled)
[586]2887 table.attach(self._rto, 2, 4, row, row+1, ypadding = 8)
[349]2888
[208]2889 self.addCancelFlightButton()
2890
[107]2891 self.addPreviousButton(clicked = self._backClicked)
2892
2893 self._button = self.addNextButton(clicked = self._forwardClicked)
[71]2894
[586]2895 self._active = False
2896
[84]2897 @property
[97]2898 def runway(self):
2899 """Get the runway."""
2900 return self._runway.get_text()
2901
2902 @property
2903 def sid(self):
2904 """Get the SID."""
[621]2905 text = self._sid.get_child().get_text()
2906 return text if self._sid.get_active()!=0 and text and text!="N/A" \
2907 else None
[97]2908
2909 @property
[84]2910 def v1(self):
2911 """Get the v1 speed."""
2912 return self._v1.get_int()
2913
2914 @property
2915 def vr(self):
2916 """Get the vr speed."""
2917 return self._vr.get_int()
2918
2919 @property
2920 def v2(self):
2921 """Get the v2 speed."""
2922 return self._v2.get_int()
2923
[349]2924 @property
[384]2925 def derate(self):
2926 """Get the derate value, if any."""
[512]2927 if self._derateWidget is None:
2928 return None
2929 if self._derateType==acft.DERATE_BOEING:
2930 derate = self._derateEntry.get_text()
[384]2931 return derate if derate else None
[512]2932 elif self._derateType==acft.DERATE_EPR:
2933 derate = self._derateWidget.get_text()
2934 return derate if derate else None
2935 elif self._derateType==acft.DERATE_TUPOLEV:
2936 return acft.DERATE_TUPOLEV_NOMINAL \
2937 if self._derateButtons[0].get_active() \
2938 else acft.DERATE_TUPOLEV_TAKEOFF
2939 elif self._derateType==acft.DERATE_B462:
2940 return self._derateWidget.get_active()
[384]2941 else:
2942 return None
2943
2944 @property
[391]2945 def antiIceOn(self):
2946 """Get whether the anti-ice system has been turned on."""
2947 return self._antiIceOn.get_active()
2948
2949 @antiIceOn.setter
2950 def antiIceOn(self, value):
2951 """Set the anti-ice indicator."""
2952 self._antiIceOn.set_active(value)
2953
2954 @property
[349]2955 def rtoIndicated(self):
2956 """Get whether the pilot has indicated if there was an RTO."""
2957 return self._rto.get_active()
2958
[71]2959 def activate(self):
2960 """Activate the page."""
[551]2961 print "TakeoffPage.activate"
2962
[586]2963 self._updatingMETAR = True
2964 self._metar.get_buffer().set_text(self._wizard.departureMETAR, -1)
2965 self._updatingMETAR = False
2966
[72]2967 self._runway.set_text("")
[71]2968 self._runway.set_sensitive(True)
[621]2969 self._sid.set_active(0)
[71]2970 self._sid.set_sensitive(True)
[84]2971 self._v1.set_int(None)
[71]2972 self._v1.set_sensitive(True)
[86]2973 self._vr.set_int(None)
[71]2974 self._vr.set_sensitive(True)
[86]2975 self._v2.set_int(None)
[71]2976 self._v2.set_sensitive(True)
[241]2977
2978 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
2979 speedUnit = xstr("label" + i18nSpeedUnit)
2980 self._v1Unit.set_text(speedUnit)
2981 self._vrUnit.set_text(speedUnit)
2982 self._v2Unit.set_text(speedUnit)
2983
2984 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip" + i18nSpeedUnit))
2985 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip" + i18nSpeedUnit))
2986 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip" + i18nSpeedUnit))
2987
[512]2988 self._derateType = self._wizard.gui.flight.aircraft.derateType
2989
2990 self._setupDerateWidget()
[384]2991
[349]2992 self._rto.set_active(False)
2993 self._rto.set_sensitive(False)
2994
[84]2995 self._button.set_sensitive(False)
[218]2996 self._forwardAllowed = False
[347]2997
[586]2998 self._active = True
2999
[101]3000 def allowForward(self):
3001 """Allow going to the next page."""
[551]3002 print "TakeoffPage.allowForward"
[101]3003 self._forwardAllowed = True
3004 self._updateForwardButton()
3005
[241]3006 def reset(self):
3007 """Reset the page if the wizard is reset."""
[551]3008 print "TakeoffPage.reset"
3009
[241]3010 super(TakeoffPage, self).reset()
3011 self._v1.reset()
3012 self._vr.reset()
3013 self._v2.reset()
[384]3014 self._hasDerate = False
[391]3015 self._antiIceOn.set_active(False)
[586]3016 self._active = False
[384]3017
[349]3018 def setRTOEnabled(self, enabled):
3019 """Set the RTO checkbox enabled or disabled."""
3020 if not enabled:
3021 self._rto.set_active(False)
3022 self._rto.set_sensitive(enabled)
3023
[586]3024 def changeMETAR(self, metar):
3025 """Change the METAR as a result of an edit on one of the other
3026 pages."""
3027 if self._active:
3028 print "TakeoffPage.changeMETAR"
3029 self._updatingMETAR = True
3030 self._metar.get_buffer().set_text(metar, -1)
3031 self._updatingMETAR = False
3032
[588]3033 self._updateForwardButton()
3034
[101]3035 def _updateForwardButton(self):
3036 """Update the sensitivity of the forward button based on some conditions."""
3037 sensitive = self._forwardAllowed and \
[588]3038 self._metar.get_text()!="" and \
[101]3039 self._runway.get_text()!="" and \
[621]3040 self.sid is not None and \
[101]3041 self.v1 is not None and \
3042 self.vr is not None and \
3043 self.v2 is not None and \
3044 self.v1 <= self.vr and \
[384]3045 self.vr <= self.v2 and \
[512]3046 (self._derateType==acft.DERATE_NONE or
3047 self.derate is not None)
[551]3048
3049 print "TakeoffPage._updateForwardButton: forwardAllowed:", self._forwardAllowed, ", sensitive:", sensitive
[638]3050 if self._forwardAllowed:
3051 print " METAR: ", self._metar.get_text()
3052 print " runway: ", self._runway.get_text()
3053 print " SID:", self.sid
3054 print " V1:", self.v1
3055 print " VR:", self.vr
3056 print " V2:", self.v2
3057 print " derateType:", self._derateType
3058 print " derate:", self.derate
[551]3059
[101]3060 self._button.set_sensitive(sensitive)
3061
3062 def _valueChanged(self, widget, arg = None):
3063 """Called when the value of some widget has changed."""
[551]3064 print "TakeoffPage._valueChanged"
3065
[101]3066 self._updateForwardButton()
[347]3067
[138]3068 def _upperChanged(self, entry, arg = None):
3069 """Called when the value of some entry widget has changed and the value
3070 should be converted to uppercase."""
[551]3071 print "TakeoffPage._upperChanged"
[138]3072 entry.set_text(entry.get_text().upper())
3073 self._valueChanged(entry, arg)
[347]3074
[621]3075 def _upperChangedComboBox(self, comboBox):
3076 """Called for combo box widgets that must be converted to uppercase."""
3077 entry = comboBox.get_child()
3078 if comboBox.get_active()==-1:
3079 entry.set_text(entry.get_text().upper())
3080 self._valueChanged(entry)
3081
[384]3082 def _derateChanged(self, entry):
3083 """Called when the value of the derate is changed."""
[551]3084 print "TakeoffPage._derateChanged"
[384]3085 self._updateForwardButton()
3086
[349]3087 def _rtoToggled(self, button):
3088 """Called when the RTO check button is toggled."""
3089 self._wizard.rtoToggled(button.get_active())
3090
[71]3091 def _backClicked(self, button):
3092 """Called when the Back button is pressed."""
3093 self.goBack()
[347]3094
[71]3095 def _forwardClicked(self, button):
3096 """Called when the forward button is clicked."""
[391]3097 aircraft = self._wizard.gui.flight.aircraft
3098 aircraft.updateV1R2()
[512]3099 if self.derate is not None:
[391]3100 aircraft.updateDerate()
3101 aircraft.updateTakeoffAntiIce()
[75]3102 self._wizard.nextPage()
3103
[512]3104 def _setupDerateWidget(self):
3105 """Setup the derate widget."""
3106 if self._derateWidget is not None:
3107 self._derate.remove(self._derateWidget)
3108
3109 if self._derateType==acft.DERATE_BOEING:
3110 self._derateLabel.set_text(xstr("takeoff_derate_boeing"))
3111 self._derateLabel.set_use_underline(True)
3112 self._derateLabel.set_sensitive(True)
3113
3114 self._derateEntry = gtk.Entry()
3115 self._derateEntry.set_width_chars(7)
3116 self._derateEntry.set_tooltip_text(xstr("takeoff_derate_boeing_tooltip"))
3117 self._derateEntry.set_alignment(1.0)
3118 self._derateEntry.connect("changed", self._derateChanged)
3119 self._derateLabel.set_mnemonic_widget(self._derateEntry)
3120
3121 self._derateUnit = gtk.Label("%")
3122 self._derateUnit.set_alignment(0.0, 0.5)
3123
3124 self._derateWidget = gtk.Table(3, 1)
3125 self._derateWidget.set_row_spacings(4)
3126 self._derateWidget.set_col_spacings(16)
3127 self._derateWidget.set_homogeneous(False)
3128
3129 self._derateWidget.attach(self._derateEntry, 0, 2, 0, 1)
3130 self._derateWidget.attach(self._derateUnit, 2, 3, 0, 1)
3131
3132 self._derate.add(self._derateWidget)
3133 elif self._derateType==acft.DERATE_EPR:
3134 self._derateLabel.set_text("_EPR:")
3135 self._derateLabel.set_use_underline(True)
3136 self._derateLabel.set_sensitive(True)
3137
3138 self._derateWidget = gtk.Entry()
3139 self._derateWidget.set_width_chars(7)
3140 self._derateWidget.set_tooltip_text(xstr("takeoff_derate_epr_tooltip"))
3141 self._derateWidget.set_alignment(1.0)
3142 self._derateWidget.connect("changed", self._derateChanged)
3143 self._derateLabel.set_mnemonic_widget(self._derateWidget)
3144
3145 self._derate.add(self._derateWidget)
3146 elif self._derateType==acft.DERATE_TUPOLEV:
3147 self._derateLabel.set_text(xstr("takeoff_derate_tupolev"))
3148 self._derateLabel.set_use_underline(True)
3149 self._derateLabel.set_sensitive(True)
3150
3151 if pygobject:
3152 nominal = gtk.RadioButton.\
3153 new_with_label_from_widget(None,
3154 xstr("takeoff_derate_tupolev_nominal"))
3155 else:
3156 nominal = gtk.RadioButton(None,
3157 xstr("takeoff_derate_tupolev_nominal"))
3158 nominal.set_use_underline(True)
3159 nominal.set_tooltip_text(xstr("takeoff_derate_tupolev_nominal_tooltip"))
3160 nominal.connect("toggled", self._derateChanged)
3161
3162 if pygobject:
3163 takeoff = gtk.RadioButton.\
3164 new_with_label_from_widget(nominal,
3165 xstr("takeoff_derate_tupolev_takeoff"))
3166 else:
3167 takeoff = gtk.RadioButton(nominal,
3168 xstr("takeoff_derate_tupolev_takeoff"))
3169
3170 takeoff.set_use_underline(True)
3171 takeoff.set_tooltip_text(xstr("takeoff_derate_tupolev_takeoff_tooltip"))
3172 takeoff.connect("toggled", self._derateChanged)
3173
3174 self._derateButtons = [nominal, takeoff]
3175
3176 self._derateWidget = gtk.HBox()
3177 self._derateWidget.pack_start(nominal, False, False, 4)
3178 self._derateWidget.pack_start(takeoff, False, False, 4)
3179
3180 self._derate.add(self._derateWidget)
3181 elif self._derateType==acft.DERATE_B462:
3182 self._derateLabel.set_text("")
3183
3184 self._derateWidget = gtk.CheckButton(xstr("takeoff_derate_b462"))
3185 self._derateWidget.set_tooltip_text(xstr("takeoff_derate_b462_tooltip"))
3186 self._derateWidget.set_use_underline(True)
3187 self._derate.add(self._derateWidget)
3188 else:
3189 self._derateWidget = None
3190 self._derateLabel.set_text("")
3191 self._derateLabel.set_sensitive(False)
3192
[586]3193 def _metarChanged(self, entry):
3194 """Called when the METAR has changed."""
3195 print "TakeoffPage.metarChanged", self._updatingMETAR
3196 if not self._updatingMETAR:
3197 self._updateForwardButton()
3198 self._wizard.metarChanged(entry.get_text(), self)
3199
[587]3200 def _metarInserted(self, buffer, position, text, length):
[586]3201 """Called when new characters are inserted into the METAR.
3202
3203 It uppercases all characters."""
3204 print "TakeoffPage.metarInserted", self._updatingMETAR
3205 if not self._updatingMETAR:
3206 self._updatingMETAR = True
3207
[587]3208 buffer.delete_text(position, length)
3209 buffer.insert_text(position, text.upper(), length)
[586]3210
3211 self._updatingMETAR = False
3212
[75]3213#-----------------------------------------------------------------------------
3214
[383]3215class CruisePage(Page):
3216 """The page containing the flight level that might change during flight."""
3217 def __init__(self, wizard):
3218 """Construct the page."""
3219 super(CruisePage, self).__init__(wizard, xstr("cruise_title"),
3220 xstr("cruise_help"))
3221
3222 self._loggable = False
3223 self._loggedCruiseLevel = 240
3224 self._activated = False
3225
3226 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
3227 xscale = 0.0, yscale = 1.0)
3228
3229 mainBox = gtk.VBox()
3230 alignment.add(mainBox)
3231 self.setMainWidget(alignment)
3232
3233 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
3234 xscale = 0.0, yscale = 0.0)
3235 mainBox.pack_start(alignment, False, False, 16)
3236
3237 levelBox = gtk.HBox()
3238
3239 label = gtk.Label(xstr("route_level"))
3240 label.set_use_underline(True)
3241 levelBox.pack_start(label, True, True, 0)
3242
3243 self._cruiseLevel = gtk.SpinButton()
3244 self._cruiseLevel.set_increments(step = 10, page = 100)
3245 self._cruiseLevel.set_range(min = 50, max = 500)
3246 self._cruiseLevel.set_tooltip_text(xstr("cruise_route_level_tooltip"))
3247 self._cruiseLevel.set_numeric(True)
3248 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
3249 label.set_mnemonic_widget(self._cruiseLevel)
3250
3251 levelBox.pack_start(self._cruiseLevel, False, False, 8)
3252
3253 self._updateButton = gtk.Button(xstr("cruise_route_level_update"));
3254 self._updateButton.set_use_underline(True)
3255 self._updateButton.set_tooltip_text(xstr("cruise_route_level_update_tooltip"))
3256 self._updateButton.connect("clicked", self._updateButtonClicked)
3257
3258 levelBox.pack_start(self._updateButton, False, False, 16)
3259
3260 mainBox.pack_start(levelBox, False, False, 0)
3261
3262 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
3263 xscale = 0.0, yscale = 1.0)
3264 mainBox.pack_start(alignment, True, True, 0)
3265
3266 self.addCancelFlightButton()
3267
3268 self._backButton = self.addPreviousButton(clicked = self._backClicked)
3269 self._button = self.addNextButton(clicked = self._forwardClicked)
3270
3271 @property
3272 def activated(self):
3273 """Determine if the page is already activated or not."""
3274 return self._activated
3275
3276 @property
3277 def cruiseLevel(self):
3278 """Get the cruise level."""
3279 return self._loggedCruiseLevel
3280
3281 @property
3282 def loggableCruiseLevel(self):
3283 """Get the cruise level which should be logged."""
3284 return self._cruiseLevel.get_value_as_int()
3285
3286 def setLoggable(self, loggable):
3287 """Set whether the cruise altitude can be logged."""
3288 self._loggable = loggable
3289 self._updateButtons()
3290
3291 def activate(self):
3292 """Setup the route from the booked flight."""
3293 self._loggedCruiseLevel = self._wizard.filedCruiseLevel
3294 self._cruiseLevel.set_value(self._loggedCruiseLevel)
3295 self._activated = True
3296
3297 def reset(self):
3298 """Reset the page."""
3299 self._loggable = False
3300 self._activated = False
3301 super(CruisePage, self).reset()
3302
3303 def _updateButtons(self):
3304 """Update the sensitivity of the buttons."""
3305 self._updateButton.set_sensitive(self._loggable and
3306 self.loggableCruiseLevel!=
3307 self._loggedCruiseLevel)
3308
3309 def _cruiseLevelChanged(self, spinButton):
3310 """Called when the cruise level has changed."""
3311 self._updateButtons()
3312
3313 def _updateButtonClicked(self, button):
3314 """Called when the update button is clicked."""
3315 if self._wizard.cruiseLevelChanged():
3316 self._loggedCruiseLevel = self.loggableCruiseLevel
3317 self._updateButtons()
3318
3319 def _backClicked(self, button):
3320 """Called when the Back button is pressed."""
3321 self.goBack()
3322
3323 def _forwardClicked(self, button):
3324 """Called when the Forward button is clicked."""
3325 self._wizard.nextPage()
3326
3327#-----------------------------------------------------------------------------
3328
[75]3329class LandingPage(Page):
3330 """Page for entering landing data."""
3331 def __init__(self, wizard):
3332 """Construct the landing page."""
[107]3333 super(LandingPage, self).__init__(wizard, xstr("landing_title"),
3334 xstr("landing_help"),
3335 completedHelp = xstr("landing_chelp"))
[75]3336
[88]3337 self._flightEnded = False
3338
[75]3339 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3340 xscale = 0.0, yscale = 0.0)
3341
[589]3342 table = gtk.Table(7, 24)
[75]3343 table.set_row_spacings(4)
3344 table.set_col_spacings(16)
3345 table.set_homogeneous(False)
3346 alignment.add(table)
3347 self.setMainWidget(alignment)
3348
[589]3349 row = 0
3350
3351 label = gtk.Label(xstr("landing_metar"))
3352 label.set_use_underline(True)
3353 label.set_alignment(0.0, 0.5)
3354 table.attach(label, 0, 1, row, row+1)
3355
3356 self._metar = gtk.Entry()
3357 self._metar.set_width_chars(40)
[590]3358 self._metar.set_tooltip_text(xstr("landing_metar_tooltip"))
[589]3359 self._metar.connect("changed", self._metarChanged)
3360 self._metar.get_buffer().connect_after("inserted-text", self._metarInserted)
3361 table.attach(self._metar, 1, 24, row, row+1)
3362 label.set_mnemonic_widget(self._metar)
3363
3364 self._updatingMETAR = False
3365
3366 row += 1
3367
[107]3368 label = gtk.Label(xstr("landing_star"))
[75]3369 label.set_use_underline(True)
3370 label.set_alignment(0.0, 0.5)
[589]3371 table.attach(label, 1, 2, row, row + 1)
[75]3372
[621]3373 if pygobject:
3374 self._star = gtk.ComboBox.new_with_model_and_entry(comboModel)
3375 else:
3376 self._star = gtk.ComboBoxEntry(comboModel)
3377
3378 self._star.set_entry_text_column(0)
3379 self._star.get_child().set_width_chars(10)
[107]3380 self._star.set_tooltip_text(xstr("landing_star_tooltip"))
[621]3381 self._star.connect("changed", self._upperChangedComboBox)
[75]3382 self._star.set_sensitive(False)
[589]3383 table.attach(self._star, 2, 4, row, row + 1)
[621]3384 label.set_mnemonic_widget(self._star)
[75]3385
[589]3386 row += 1
3387
[107]3388 label = gtk.Label(xstr("landing_transition"))
[75]3389 label.set_use_underline(True)
3390 label.set_alignment(0.0, 0.5)
[589]3391 table.attach(label, 1, 2, row, row + 1)
[75]3392
[621]3393 if pygobject:
3394 self._transition = gtk.ComboBox.new_with_model_and_entry(comboModel)
3395 else:
3396 self._transition = gtk.ComboBoxEntry(comboModel)
3397
3398 self._transition.set_entry_text_column(0)
3399 self._transition.get_child().set_width_chars(10)
[107]3400 self._transition.set_tooltip_text(xstr("landing_transition_tooltip"))
[621]3401 self._transition.connect("changed", self._upperChangedComboBox)
[75]3402 self._transition.set_sensitive(False)
[589]3403 table.attach(self._transition, 2, 4, row, row + 1)
[621]3404 label.set_mnemonic_widget(self._transition)
[75]3405
[589]3406 row += 1
3407
[107]3408 label = gtk.Label(xstr("landing_runway"))
[75]3409 label.set_use_underline(True)
3410 label.set_alignment(0.0, 0.5)
[589]3411 table.attach(label, 1, 2, row, row + 1)
[75]3412
3413 self._runway = gtk.Entry()
3414 self._runway.set_width_chars(10)
[107]3415 self._runway.set_tooltip_text(xstr("landing_runway_tooltip"))
[138]3416 self._runway.connect("changed", self._upperChanged)
[589]3417 table.attach(self._runway, 2, 4, row, row + 1)
[75]3418 label.set_mnemonic_widget(self._runway)
3419
[589]3420 row += 1
3421
[107]3422 label = gtk.Label(xstr("landing_approach"))
[75]3423 label.set_use_underline(True)
3424 label.set_alignment(0.0, 0.5)
[589]3425 table.attach(label, 1, 2, row, row + 1)
[75]3426
3427 self._approachType = gtk.Entry()
3428 self._approachType.set_width_chars(10)
[107]3429 self._approachType.set_tooltip_text(xstr("landing_approach_tooltip"))
[138]3430 self._approachType.connect("changed", self._upperChanged)
[589]3431 table.attach(self._approachType, 2, 4, row, row + 1)
[75]3432 label.set_mnemonic_widget(self._approachType)
3433
[589]3434 row += 1
3435
[107]3436 label = gtk.Label(xstr("landing_vref"))
[75]3437 label.set_use_markup(True)
3438 label.set_use_underline(True)
3439 label.set_alignment(0.0, 0.5)
[589]3440 table.attach(label, 1, 2, row, row + 1)
[75]3441
[86]3442 self._vref = IntegerEntry()
3443 self._vref.set_width_chars(5)
[241]3444 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip_knots"))
[86]3445 self._vref.connect("integer-changed", self._vrefChanged)
[589]3446 table.attach(self._vref, 3, 4, row, row + 1)
[75]3447 label.set_mnemonic_widget(self._vref)
[241]3448
3449 self._vrefUnit = gtk.Label(xstr("label_knots"))
[589]3450 table.attach(self._vrefUnit, 4, 5, row, row + 1)
3451
3452 row += 1
[391]3453
3454 self._antiIceOn = gtk.CheckButton(xstr("landing_antiice"))
3455 self._antiIceOn.set_use_underline(True)
3456 self._antiIceOn.set_tooltip_text(xstr("landing_antiice_tooltip"))
[589]3457 table.attach(self._antiIceOn, 3, 5, row, row + 1)
[75]3458
[208]3459 self.addCancelFlightButton()
3460
[107]3461 self.addPreviousButton(clicked = self._backClicked)
3462
3463 self._button = self.addNextButton(clicked = self._forwardClicked)
[75]3464
[589]3465 self._active = False
3466
[86]3467 @property
[97]3468 def star(self):
3469 """Get the STAR or None if none entered."""
[621]3470 text = self._star.get_child().get_text()
3471 return text if self._star.get_active()!=0 and text and text!="N/A" \
3472 else None
[97]3473
3474 @property
3475 def transition(self):
3476 """Get the transition or None if none entered."""
[621]3477 text = self._transition.get_child().get_text()
3478 return text if self._transition.get_active()!=0 and text and text!="N/A" \
3479 else None
[97]3480
3481 @property
3482 def approachType(self):
3483 """Get the approach type."""
3484 return self._approachType.get_text()
3485
3486 @property
3487 def runway(self):
3488 """Get the runway."""
3489 return self._runway.get_text()
3490
3491 @property
[86]3492 def vref(self):
3493 """Return the landing reference speed."""
3494 return self._vref.get_int()
3495
[391]3496 @property
3497 def antiIceOn(self):
3498 """Get whether the anti-ice system has been turned on."""
3499 return self._antiIceOn.get_active()
3500
3501 @antiIceOn.setter
3502 def antiIceOn(self, value):
3503 """Set the anti-ice indicator."""
3504 self._antiIceOn.set_active(value)
3505
[218]3506 def reset(self):
3507 """Reset the page if the wizard is reset."""
3508 super(LandingPage, self).reset()
[241]3509 self._vref.reset()
[391]3510 self._antiIceOn.set_active(False)
[218]3511 self._flightEnded = False
[589]3512 self._active = False
[347]3513
[75]3514 def activate(self):
3515 """Called when the page is activated."""
[589]3516 self._updatingMETAR = True
3517 self._metar.get_buffer().set_text(self._wizard.arrivalMETAR, -1)
3518 self._updatingMETAR = False
3519
[621]3520 self._star.set_active(0)
3521 self._star.set_sensitive(True)
3522
3523 self._transition.set_active(0)
3524 self._transition.set_sensitive(True)
[75]3525
3526 self._runway.set_text("")
3527 self._runway.set_sensitive(True)
3528
3529 self._approachType.set_text("")
3530 self._approachType.set_sensitive(True)
3531
[86]3532 self._vref.set_int(None)
[75]3533 self._vref.set_sensitive(True)
3534
[241]3535 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
3536 speedUnit = xstr("label" + i18nSpeedUnit)
3537 self._vrefUnit.set_text(speedUnit)
3538
3539 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip" +
3540 i18nSpeedUnit))
3541
[75]3542 self._updateForwardButton()
3543
[589]3544 self._active = True
3545
[88]3546 def flightEnded(self):
3547 """Called when the flight has ended."""
[275]3548 super(LandingPage, self).flightEnded()
[88]3549 self._flightEnded = True
3550 self._updateForwardButton()
3551
[589]3552 def changeMETAR(self, metar):
3553 """Change the METAR as a result of an edit on one of the other
3554 pages."""
3555 if self._active:
3556 print "LandingPage.changeMETAR"
3557 self._updatingMETAR = True
3558 self._metar.get_buffer().set_text(metar, -1)
3559 self._updatingMETAR = False
3560
3561 self._updateForwardButton()
3562
[138]3563 def _updateForwardButton(self):
[75]3564 """Update the sensitivity of the forward button."""
[88]3565 sensitive = self._flightEnded and \
[589]3566 self._metar.get_text()!="" and \
[621]3567 (self.star is not None or
3568 self.transition is not None) and \
[75]3569 self._runway.get_text()!="" and \
[86]3570 self._approachType.get_text()!="" and \
3571 self.vref is not None
[75]3572 self._button.set_sensitive(sensitive)
3573
[138]3574 def _upperChanged(self, entry):
3575 """Called for entry widgets that must be converted to uppercase."""
3576 entry.set_text(entry.get_text().upper())
3577 self._updateForwardButton()
3578
[621]3579 def _upperChangedComboBox(self, comboBox):
3580 """Called for combo box widgets that must be converted to uppercase."""
3581 if comboBox.get_active()==-1:
3582 entry = comboBox.get_child()
3583 entry.set_text(entry.get_text().upper())
3584 self._updateForwardButton()
3585
[86]3586 def _vrefChanged(self, widget, value):
3587 """Called when the Vref has changed."""
3588 self._updateForwardButton()
3589
[75]3590 def _backClicked(self, button):
3591 """Called when the Back button is pressed."""
3592 self.goBack()
[347]3593
[75]3594 def _forwardClicked(self, button):
3595 """Called when the forward button is clicked."""
[391]3596 aircraft = self._wizard.gui.flight.aircraft
3597 aircraft.updateVRef()
3598 aircraft.updateLandingAntiIce()
[136]3599 if self._wizard.gui.config.onlineGateSystem and \
[215]3600 self._wizard.loggedIn and not self._completed and \
[184]3601 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
3602 not self._wizard.entranceExam:
[130]3603 self._wizard.getFleet(callback = self._fleetRetrieved,
3604 force = True)
3605 else:
3606 self._wizard.nextPage()
3607
3608 def _fleetRetrieved(self, fleet):
3609 """Callback for the fleet retrieval."""
[89]3610 self._wizard.nextPage()
3611
[589]3612 def _metarChanged(self, entry):
3613 """Called when the METAR has changed."""
3614 print "LandingPage.metarChanged", self._updatingMETAR
3615 if not self._updatingMETAR:
3616 self._updateForwardButton()
3617 self._wizard.metarChanged(entry.get_text(), self)
3618
3619 def _metarInserted(self, buffer, position, text, length):
3620 """Called when new characters are inserted into the METAR.
3621
3622 It uppercases all characters."""
3623 print "LandingPage.metarInserted", self._updatingMETAR
3624 if not self._updatingMETAR:
3625 self._updatingMETAR = True
3626
3627 buffer.delete_text(position, length)
3628 buffer.insert_text(position, text.upper(), length)
3629
3630 self._updatingMETAR = False
3631
[89]3632#-----------------------------------------------------------------------------
3633
3634class FinishPage(Page):
3635 """Flight finish page."""
[107]3636 _flightTypes = [ ("flighttype_scheduled", const.FLIGHTTYPE_SCHEDULED),
3637 ("flighttype_ot", const.FLIGHTTYPE_OLDTIMER),
3638 ("flighttype_vip", const.FLIGHTTYPE_VIP),
3639 ("flighttype_charter", const.FLIGHTTYPE_CHARTER) ]
[347]3640
[89]3641 def __init__(self, wizard):
3642 """Construct the finish page."""
[564]3643 help = xstr("finish_help") + xstr("finish_help_goodtime")
[563]3644 super(FinishPage, self).__init__(wizard, xstr("finish_title"), help)
[347]3645
[89]3646 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3647 xscale = 0.0, yscale = 0.0)
3648
[556]3649 table = gtk.Table(10, 2)
[89]3650 table.set_row_spacings(4)
3651 table.set_col_spacings(16)
[96]3652 table.set_homogeneous(False)
[89]3653 alignment.add(table)
3654 self.setMainWidget(alignment)
3655
[555]3656 row = 0
3657
[89]3658 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
[107]3659 label = gtk.Label(xstr("finish_rating"))
[89]3660 labelAlignment.add(label)
[555]3661 table.attach(labelAlignment, 0, 1, row, row+1)
[89]3662
3663 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3664 self._flightRating = gtk.Label()
[154]3665 self._flightRating.set_width_chars(8)
[89]3666 self._flightRating.set_alignment(0.0, 0.5)
3667 self._flightRating.set_use_markup(True)
3668 labelAlignment.add(self._flightRating)
[555]3669 table.attach(labelAlignment, 1, 2, row, row+1)
3670
3671 row += 1
3672
3673 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3674 label = gtk.Label(xstr("finish_dep_time"))
3675 labelAlignment.add(label)
3676 table.attach(labelAlignment, 0, 1, row, row+1)
3677
3678 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3679 self._depTime = gtk.Label()
[556]3680 self._depTime.set_width_chars(13)
[555]3681 self._depTime.set_alignment(0.0, 0.5)
3682 self._depTime.set_use_markup(True)
[558]3683 self._depTime.set_tooltip_markup(xstr("finish_dep_time_tooltip"))
[555]3684 labelAlignment.add(self._depTime)
3685 table.attach(labelAlignment, 1, 2, row, row+1)
3686
3687 row += 1
[89]3688
3689 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
[107]3690 label = gtk.Label(xstr("finish_flight_time"))
[89]3691 labelAlignment.add(label)
[555]3692 table.attach(labelAlignment, 0, 1, row, row+1)
[89]3693
3694 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3695 self._flightTime = gtk.Label()
3696 self._flightTime.set_width_chars(10)
3697 self._flightTime.set_alignment(0.0, 0.5)
3698 self._flightTime.set_use_markup(True)
3699 labelAlignment.add(self._flightTime)
[555]3700 table.attach(labelAlignment, 1, 2, row, row+1)
3701
3702 row += 1
[89]3703
3704 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
[107]3705 label = gtk.Label(xstr("finish_block_time"))
[89]3706 labelAlignment.add(label)
[555]3707 table.attach(labelAlignment, 0, 1, row, row+1)
[89]3708
3709 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3710 self._blockTime = gtk.Label()
3711 self._blockTime.set_width_chars(10)
3712 self._blockTime.set_alignment(0.0, 0.5)
3713 self._blockTime.set_use_markup(True)
3714 labelAlignment.add(self._blockTime)
[555]3715 table.attach(labelAlignment, 1, 2, row, row+1)
3716
3717 row += 1
3718
3719 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3720 label = gtk.Label(xstr("finish_arr_time"))
3721 labelAlignment.add(label)
3722 table.attach(labelAlignment, 0, 1, row, row+1)
3723
3724 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3725 self._arrTime = gtk.Label()
[556]3726 self._arrTime.set_width_chars(13)
[555]3727 self._arrTime.set_alignment(0.0, 0.5)
3728 self._arrTime.set_use_markup(True)
[558]3729 self._arrTime.set_tooltip_markup(xstr("finish_arr_time_tooltip"))
[555]3730 labelAlignment.add(self._arrTime)
3731 table.attach(labelAlignment, 1, 2, row, row+1)
3732
3733 row += 1
[89]3734
3735 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
[107]3736 label = gtk.Label(xstr("finish_distance"))
[89]3737 labelAlignment.add(label)
[555]3738 table.attach(labelAlignment, 0, 1, row, row+1)
[89]3739
3740 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3741 self._distanceFlown = gtk.Label()
3742 self._distanceFlown.set_width_chars(10)
3743 self._distanceFlown.set_alignment(0.0, 0.5)
3744 self._distanceFlown.set_use_markup(True)
3745 labelAlignment.add(self._distanceFlown)
[555]3746 table.attach(labelAlignment, 1, 2, row, row+1)
3747
3748 row += 1
[347]3749
[89]3750 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
[107]3751 label = gtk.Label(xstr("finish_fuel"))
[89]3752 labelAlignment.add(label)
[555]3753 table.attach(labelAlignment, 0, 1, row, row+1)
[89]3754
3755 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3756 self._fuelUsed = gtk.Label()
3757 self._fuelUsed.set_width_chars(10)
3758 self._fuelUsed.set_alignment(0.0, 0.5)
3759 self._fuelUsed.set_use_markup(True)
3760 labelAlignment.add(self._fuelUsed)
[555]3761 table.attach(labelAlignment, 1, 2, row, row+1)
3762
3763 row += 1
[89]3764
[107]3765 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
3766 yalign = 0.5, yscale = 0.0)
3767 label = gtk.Label(xstr("finish_type"))
[96]3768 label.set_use_underline(True)
3769 labelAlignment.add(label)
[555]3770 table.attach(labelAlignment, 0, 1, row, row+1)
[96]3771
3772 flightTypeModel = gtk.ListStore(str, int)
[97]3773 for (name, type) in FinishPage._flightTypes:
[107]3774 flightTypeModel.append([xstr(name), type])
[96]3775
3776 self._flightType = gtk.ComboBox(model = flightTypeModel)
3777 renderer = gtk.CellRendererText()
3778 self._flightType.pack_start(renderer, True)
3779 self._flightType.add_attribute(renderer, "text", 0)
[107]3780 self._flightType.set_tooltip_text(xstr("finish_type_tooltip"))
[96]3781 self._flightType.set_active(0)
3782 self._flightType.connect("changed", self._flightTypeChanged)
3783 flightTypeAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3784 flightTypeAlignment.add(self._flightType)
[555]3785 table.attach(flightTypeAlignment, 1, 2, row, row+1)
[347]3786 label.set_mnemonic_widget(self._flightType)
[96]3787
[555]3788 row += 1
3789
[107]3790 self._onlineFlight = gtk.CheckButton(xstr("finish_online"))
[96]3791 self._onlineFlight.set_use_underline(True)
[107]3792 self._onlineFlight.set_tooltip_text(xstr("finish_online_tooltip"))
[96]3793 onlineFlightAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3794 onlineFlightAlignment.add(self._onlineFlight)
[555]3795 table.attach(onlineFlightAlignment, 1, 2, row, row + 1)
3796
3797 row += 1
[96]3798
[130]3799 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
3800 yalign = 0.5, yscale = 0.0)
3801 self._gateLabel = gtk.Label(xstr("finish_gate"))
3802 self._gateLabel.set_use_underline(True)
3803 labelAlignment.add(self._gateLabel)
[555]3804 table.attach(labelAlignment, 0, 1, row, row+1)
[130]3805
3806 self._gatesModel = gtk.ListStore(str)
3807
3808 self._gate = gtk.ComboBox(model = self._gatesModel)
3809 renderer = gtk.CellRendererText()
3810 self._gate.pack_start(renderer, True)
3811 self._gate.add_attribute(renderer, "text", 0)
3812 self._gate.set_tooltip_text(xstr("finish_gate_tooltip"))
[347]3813 self._gate.connect("changed", self._gateChanged)
[130]3814 gateAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
3815 gateAlignment.add(self._gate)
[555]3816 table.attach(gateAlignment, 1, 2, row, row+1)
[130]3817 self._gateLabel.set_mnemonic_widget(self._gate)
3818
[208]3819 self.addButton(xstr("finish_newFlight"),
3820 sensitive = True,
3821 clicked = self._newFlightClicked,
3822 tooltip = xstr("finish_newFlight_tooltip"),
3823 padding = 16)
3824
[107]3825 self.addPreviousButton(clicked = self._backClicked)
[94]3826
[107]3827 self._saveButton = self.addButton(xstr("finish_save"),
3828 sensitive = False,
[151]3829 clicked = self._saveClicked,
3830 tooltip = xstr("finish_save_tooltip"))
3831 self._savePIREPDialog = None
3832 self._lastSavePath = None
[208]3833
[555]3834 self._tooBigTimeDifference = False
3835 self._deferredAutoSave = False
[208]3836 self._pirepSaved = False
3837 self._pirepSent = False
[347]3838
[151]3839 self._sendButton = self.addButton(xstr("sendPIREP"), default = True,
[107]3840 sensitive = False,
3841 clicked = self._sendClicked,
[151]3842 tooltip = xstr("sendPIREP_tooltip"))
[347]3843
[555]3844 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 10*60.0)
3845 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 20*60.0)
3846 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 0*60.0)
3847 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), (23*60.0+50)*60.0)
3848 # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (1*60.0+5)*60.0)
3849 # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (0*60.0+50)*60.0)
3850 # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (0*60.0+5)*60.0)
3851 # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (23*60.0+45)*60.0)
3852
[97]3853 @property
3854 def flightType(self):
3855 """Get the flight type."""
3856 index = self._flightType.get_active()
3857 return None if index<0 else self._flightType.get_model()[index][1]
3858
3859 @property
3860 def online(self):
3861 """Get whether the flight was an online flight or not."""
3862 return self._onlineFlight.get_active()
[89]3863
3864 def activate(self):
3865 """Activate the page."""
[555]3866 self._deferredAutoSave = False
[208]3867 self._pirepSaved = False
3868 self._pirepSent = False
3869
[89]3870 flight = self._wizard.gui._flight
3871 rating = flight.logger.getRating()
3872 if rating<0:
3873 self._flightRating.set_markup('<b><span foreground="red">NO GO</span></b>')
3874 else:
3875 self._flightRating.set_markup("<b>%.1f %%</b>" % (rating,))
3876
3877 flightLength = flight.flightTimeEnd - flight.flightTimeStart
3878 self._flightTime.set_markup("<b>%s</b>" % \
3879 (util.getTimeIntervalString(flightLength),))
[347]3880
[89]3881 blockLength = flight.blockTimeEnd - flight.blockTimeStart
3882 self._blockTime.set_markup("<b>%s</b>" % \
3883 (util.getTimeIntervalString(blockLength),))
3884
3885 self._distanceFlown.set_markup("<b>%.2f NM</b>" % \
3886 (flight.flownDistance,))
[347]3887
[89]3888 self._fuelUsed.set_markup("<b>%.0f kg</b>" % \
[102]3889 (flight.startFuel - flight.endFuel,))
[64]3890
[96]3891 self._flightType.set_active(-1)
[215]3892 self._onlineFlight.set_active(self._wizard.loggedIn)
[96]3893
[130]3894 self._gatesModel.clear()
[136]3895 if self._wizard.gui.config.onlineGateSystem and \
[215]3896 self._wizard.loggedIn and \
[184]3897 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
3898 not self._wizard.entranceExam:
[619]3899 occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
3900 for gate in lhbpGates.gates:
3901 if gate.isAvailable(lhbpGates, occupiedGateNumbers):
3902 self._gatesModel.append([gate.number])
[130]3903 self._gateLabel.set_sensitive(True)
3904 self._gate.set_sensitive(True)
3905 self._gate.set_active(-1)
3906 else:
3907 self._gateLabel.set_sensitive(False)
3908 self._gate.set_sensitive(False)
3909
[564]3910 self._updateTimes()
[555]3911
3912 def updateButtons(self):
[130]3913 """Update the sensitivity state of the buttons."""
[555]3914 gui = self._wizard.gui
[607]3915 faultsExplained = gui.faultsFullyExplained
3916 timesCorrect = self.flightType is None or \
3917 not self._tooBigTimeDifference or \
3918 gui.hasComments or gui.hasDelayCode
[625]3919 sensitive = gui.flight is not None and \
3920 gui.flight.stage==const.STAGE_END and \
3921 self._flightType.get_active()>=0 and \
[130]3922 (self._gatesModel.get_iter_first() is None or
[555]3923 self._gate.get_active()>=0) and \
[607]3924 faultsExplained and timesCorrect
3925
3926 self._updateHelp(faultsExplained, timesCorrect)
[347]3927
[393]3928 wasSensitive = self._saveButton.get_sensitive()
3929
[555]3930 if gui.config.pirepAutoSave and sensitive and not wasSensitive:
3931 if gui.isWizardActive():
3932 self._autoSavePIREP()
3933 else:
3934 self._deferredAutoSave = True
3935
3936 if not sensitive:
3937 self._deferredAutoSave = False
[393]3938
[151]3939 self._saveButton.set_sensitive(sensitive)
[184]3940 self._sendButton.set_sensitive(sensitive and
3941 self._wizard.bookedFlight.id is not None)
[130]3942
[555]3943 def grabDefault(self):
3944 """If the page has a default button, make it the default one."""
3945 super(FinishPage, self).grabDefault()
3946 if self._deferredAutoSave:
3947 self._autoSavePIREP()
3948 self._deferredAutoSave = False
3949
3950 def _autoSavePIREP(self):
3951 """Perform the automatic saving of the PIREP."""
3952 self._lastSavePath = os.path.join(self._wizard.gui.config.pirepDirectory,
3953 self._getDefaultPIREPName())
3954 self._lastSavePath = text2unicode(self._lastSavePath)
3955 self._savePIREP(automatic = True)
3956
3957 def _backClicked(self, button):
3958 """Called when the Back button is pressed."""
3959 self.goBack()
3960
[96]3961 def _flightTypeChanged(self, comboBox):
3962 """Called when the flight type has changed."""
[564]3963 self._updateTimes()
[130]3964
3965 def _gateChanged(self, comboBox):
3966 """Called when the arrival gate has changed."""
[555]3967 self.updateButtons()
[97]3968
[208]3969 def _newFlightClicked(self, button):
3970 """Called when the new flight button is clicked."""
3971 gui = self._wizard.gui
3972 if not self._pirepSent and not self._pirepSaved:
3973 dialog = gtk.MessageDialog(parent = gui.mainWindow,
3974 type = MESSAGETYPE_QUESTION,
3975 message_format = xstr("finish_newFlight_question"))
3976
3977 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
3978 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
3979
3980 dialog.set_title(WINDOW_TITLE_BASE)
3981 result = dialog.run()
3982 dialog.hide()
3983 if result!=RESPONSETYPE_YES:
3984 return
[347]3985
[208]3986 gui.reset()
3987
[393]3988 def _getDefaultPIREPName(self):
3989 """Get the default name of the PIREP."""
[151]3990 gui = self._wizard.gui
3991
3992 bookedFlight = gui.bookedFlight
3993 tm = time.gmtime()
[215]3994
3995 pilotID = self._wizard.pilotID
3996 if pilotID: pilotID += " "
[393]3997 return "%s%s %02d%02d %s-%s.pirep" % \
3998 (pilotID, str(bookedFlight.departureTime.date()),
3999 tm.tm_hour, tm.tm_min,
4000 bookedFlight.departureICAO, bookedFlight.arrivalICAO)
4001
4002
4003 def _saveClicked(self, button):
4004 """Called when the Save PIREP button is clicked."""
4005 gui = self._wizard.gui
4006
4007 fileName = self._getDefaultPIREPName()
[151]4008
4009 dialog = self._getSaveDialog()
4010
4011 if self._lastSavePath is None:
4012 pirepDirectory = gui.config.pirepDirectory
4013 if pirepDirectory is not None:
4014 dialog.set_current_folder(pirepDirectory)
4015 else:
4016 dialog.set_current_folder(os.path.dirname(self._lastSavePath))
[347]4017
[151]4018 dialog.set_current_name(fileName)
4019 result = dialog.run()
4020 dialog.hide()
4021
4022 if result==RESPONSETYPE_OK:
[164]4023 self._lastSavePath = text2unicode(dialog.get_filename())
[393]4024 self._savePIREP()
4025
4026 def _savePIREP(self, automatic = False):
4027 """Perform the saving of the PIREP."""
4028
4029 gui = self._wizard.gui
4030
4031 if automatic:
4032 gui.beginBusy(xstr("finish_autosave_busy"))
4033
4034 pirep = PIREP(gui.flight)
4035 error = pirep.save(self._lastSavePath)
4036
4037 if automatic:
4038 gui.endBusy()
4039
4040 if error:
4041 type = MESSAGETYPE_ERROR
4042 message = xstr("finish_save_failed")
4043 secondary = xstr("finish_save_failed_sec") % (text2unicode(error),)
4044 else:
4045 type = MESSAGETYPE_INFO
4046 message = xstr("finish_save_done")
4047 if automatic:
4048 secondary = xstr("finish_save_done_sec") % (self._lastSavePath,)
[151]4049 else:
[393]4050 secondary = None
4051 self._pirepSaved = True
4052
4053 dialog = gtk.MessageDialog(parent = gui.mainWindow,
4054 type = type, message_format = message)
4055 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
4056 dialog.set_title(WINDOW_TITLE_BASE)
4057 if secondary is not None:
4058 dialog.format_secondary_markup(secondary)
4059
4060 dialog.run()
4061 dialog.hide()
[151]4062
4063 def _getSaveDialog(self):
4064 """Get the PIREP saving dialog.
4065
4066 If it does not exist yet, create it."""
4067 if self._savePIREPDialog is None:
4068 gui = self._wizard.gui
4069 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
4070 xstr("finish_save_title"),
4071 action = FILE_CHOOSER_ACTION_SAVE,
4072 buttons = (gtk.STOCK_CANCEL,
4073 RESPONSETYPE_CANCEL,
4074 gtk.STOCK_OK, RESPONSETYPE_OK),
4075 parent = gui.mainWindow)
4076 dialog.set_modal(True)
4077 dialog.set_do_overwrite_confirmation(True)
[347]4078
[151]4079 filter = gtk.FileFilter()
[184]4080 filter.set_name(xstr("file_filter_pireps"))
[151]4081 filter.add_pattern("*.pirep")
4082 dialog.add_filter(filter)
[347]4083
[151]4084 filter = gtk.FileFilter()
[184]4085 filter.set_name(xstr("file_filter_all"))
[151]4086 filter.add_pattern("*.*")
4087 dialog.add_filter(filter)
4088
4089 self._savePIREPDialog = dialog
4090
4091 return self._savePIREPDialog
[347]4092
[151]4093
[97]4094 def _sendClicked(self, button):
4095 """Called when the Send button is clicked."""
[262]4096 pirep = PIREP(self._wizard.gui.flight)
[151]4097 self._wizard.gui.sendPIREP(pirep,
4098 callback = self._handlePIREPSent)
[97]4099
4100 def _handlePIREPSent(self, returned, result):
4101 """Callback for the PIREP sending result."""
[208]4102 self._pirepSent = returned and result.success
[184]4103 if self._wizard.gui.config.onlineGateSystem and \
[215]4104 self._wizard.loggedIn and not self._wizard.entranceExam and \
[184]4105 returned and result.success:
[130]4106 bookedFlight = self._wizard.bookedFlight
4107 if bookedFlight.arrivalICAO=="LHBP":
[347]4108 iter = self._gate.get_active_iter()
[130]4109 gateNumber = None if iter is None \
4110 else self._gatesModel.get_value(iter, 0)
[347]4111
[130]4112 status = const.PLANE_PARKING if gateNumber is None \
4113 else const.PLANE_HOME
4114 else:
4115 gateNumber = None
4116 status = const.PLANE_AWAY
4117
4118 self._wizard.updatePlane(self._planeUpdated,
4119 bookedFlight.tailNumber,
4120 status, gateNumber = gateNumber)
4121
4122 def _planeUpdated(self, success):
4123 """Callback for the plane updating."""
4124 pass
[347]4125
[564]4126 def _formatTime(self, scheduledTime, realTimestamp, (warning, error)):
[555]4127 """Format the departure or arrival time based on the given data as a
[564]4128 markup for a label."""
[555]4129 realTime = time.gmtime(realTimestamp)
4130
[564]4131 if warning:
4132 colour = "red" if error else "orange"
[555]4133 markupBegin = '<span foreground="%s">' % (colour,)
4134 markupEnd = '</span>'
4135 else:
4136 markupBegin = markupEnd = ""
4137
4138 markup = "<b>%s%02d:%02d [%02d:%02d]%s</b>" % \
4139 (markupBegin,
4140 realTime.tm_hour, realTime.tm_min,
4141 scheduledTime.hour, scheduledTime.minute,
4142 markupEnd)
4143
[564]4144 return markup
4145
4146 def _updateTimes(self):
4147 """Format the flight times and the help text according to the flight
4148 type.
4149
4150 The buttons are also updated.
4151 """
4152 flight = self._wizard.gui._flight
4153 bookedFlight = flight.bookedFlight
4154
4155 (departureWarning, departureError) = flight.blockTimeStartWrong
4156 (arrivalWarning, arrivalError) = flight.blockTimeEndWrong
4157
4158 if self.flightType==const.FLIGHTTYPE_VIP:
4159 departureError = arrivalError = False
4160
4161 self._tooBigTimeDifference = departureError or arrivalError
4162
4163 self._depTime.set_markup(self._formatTime(bookedFlight.departureTime,
4164 flight.blockTimeStart,
4165 (departureWarning,
4166 departureError)))
4167
4168 self._arrTime.set_markup(self._formatTime(bookedFlight.arrivalTime,
4169 flight.blockTimeEnd,
4170 (arrivalWarning,
4171 arrivalError)))
4172
4173 self.updateButtons()
[555]4174
[607]4175 def _updateHelp(self, faultsExplained, timesCorrect):
4176 """Update the help text according to the actual situation."""
4177 if not faultsExplained:
4178 self.setHelp(xstr("finish_help") + xstr("finish_help_faults"))
4179 elif not timesCorrect:
4180 self.setHelp(xstr("finish_help") + xstr("finish_help_wrongtime"))
4181 else:
4182 self.setHelp(xstr("finish_help") + xstr("finish_help_goodtime"))
4183
4184
[64]4185#-----------------------------------------------------------------------------
4186
[42]4187class Wizard(gtk.VBox):
4188 """The flight wizard."""
4189 def __init__(self, gui):
4190 """Construct the wizard."""
4191 super(Wizard, self).__init__()
4192
4193 self.gui = gui
4194
4195 self._pages = []
4196 self._currentPage = None
[184]4197
4198 self._loginPage = LoginPage(self)
4199 self._pages.append(self._loginPage)
[42]4200 self._pages.append(FlightSelectionPage(self))
[51]4201 self._pages.append(GateSelectionPage(self))
4202 self._pages.append(ConnectPage(self))
[347]4203 self._payloadPage = PayloadPage(self)
[84]4204 self._pages.append(self._payloadPage)
[117]4205 self._payloadIndex = len(self._pages)
[61]4206 self._pages.append(TimePage(self))
[84]4207 self._routePage = RoutePage(self)
4208 self._pages.append(self._routePage)
[687]4209 self._simBriefSetupPage = SimBriefSetupPage(self)
4210 self._pages.append(self._simBriefSetupPage)
4211 self._simBriefingPage = SimBriefingPage(self)
4212 self._pages.append(self._simBriefingPage)
4213 self._pages.append(FuelPage(self))
[97]4214 self._departureBriefingPage = BriefingPage(self, True)
4215 self._pages.append(self._departureBriefingPage)
4216 self._arrivalBriefingPage = BriefingPage(self, False)
4217 self._pages.append(self._arrivalBriefingPage)
[117]4218 self._arrivalBriefingIndex = len(self._pages)
[347]4219 self._takeoffPage = TakeoffPage(self)
[84]4220 self._pages.append(self._takeoffPage)
[383]4221 self._cruisePage = CruisePage(self)
4222 self._pages.append(self._cruisePage)
[347]4223 self._landingPage = LandingPage(self)
[86]4224 self._pages.append(self._landingPage)
[97]4225 self._finishPage = FinishPage(self)
4226 self._pages.append(self._finishPage)
[201]4227
[556]4228 self._requestedWidth = None
4229 self._requestedHeight = None
4230
4231 self.connect("size-allocate", self._sizeAllocate)
4232
4233 for page in self._pages:
4234 page.show_all()
4235 page.setStyle()
4236
4237 self._initialize()
4238
4239 def _sizeAllocate(self, widget, allocation):
4240 if self._requestedWidth is not None and \
4241 self._requestedHeight is not None:
4242 return
4243
4244 if self._currentPage is not None:
4245 self.remove(self._pages[self._currentPage])
4246
[51]4247 maxWidth = 0
4248 maxHeight = 0
4249 for page in self._pages:
[556]4250 self.add(page)
4251 self.show_all()
[51]4252 pageSizeRequest = page.size_request()
4253 width = pageSizeRequest.width if pygobject else pageSizeRequest[0]
4254 height = pageSizeRequest.height if pygobject else pageSizeRequest[1]
4255 maxWidth = max(maxWidth, width)
4256 maxHeight = max(maxHeight, height)
[556]4257 self.remove(page)
4258
4259 if self._currentPage is not None:
4260 self.add(self._pages[self._currentPage])
4261
4262 self._requestedWidth = maxWidth
4263 self._requestedHeight = maxHeight
[51]4264 self.set_size_request(maxWidth, maxHeight)
4265
[184]4266 @property
[215]4267 def pilotID(self):
4268 """Get the pilot ID, if given."""
4269 return self._loginPage.pilotID
4270
4271 @property
[184]4272 def entranceExam(self):
4273 """Get whether an entrance exam is about to be taken."""
4274 return self._loginPage.entranceExam
[215]4275
4276 @property
4277 def loggedIn(self):
4278 """Indicate if there was a successful login."""
4279 return self._loginResult is not None
[347]4280
[48]4281 @property
4282 def loginResult(self):
4283 """Get the login result."""
4284 return self._loginResult
4285
[692]4286 def setCurrentPage(self, index, finalize = False, fromPageShift = None):
4287 """Set the current page to the one with the given index.
4288
4289 @param fromPageShift if given, the relative index of one of the
4290 previous pages that should be used as the from-page of the next
4291 page. E.g. if fromPageShift is 1, the previous page will be the
4292 from-page."""
[42]4293 assert index < len(self._pages)
[70]4294
4295 fromPage = self._currentPage
4296 if fromPage is not None:
4297 page = self._pages[fromPage]
[94]4298 if finalize and not page._completed:
4299 page.complete()
[70]4300 self.remove(page)
[692]4301 if fromPageShift is not None:
4302 fromPage -= fromPageShift
[42]4303
4304 self._currentPage = index
[70]4305 page = self._pages[index]
4306 self.add(page)
4307 if page._fromPage is None:
4308 page._fromPage = fromPage
[94]4309 page.initialize()
[42]4310 self.show_all()
[72]4311 if fromPage is not None:
4312 self.grabDefault()
[42]4313
[84]4314 @property
[97]4315 def bookedFlight(self):
4316 """Get the booked flight selected."""
4317 return self._bookedFlight
4318
4319 @property
[303]4320 def numCrew(self):
4321 """Get the number of crew members."""
4322 return self._payloadPage.numCrew
4323
4324 @property
4325 def numPassengers(self):
4326 """Get the number of passengers."""
4327 return self._payloadPage.numPassengers
4328
4329 @property
4330 def bagWeight(self):
4331 """Get the baggage weight."""
4332 return self._payloadPage.bagWeight
4333
4334 @property
[97]4335 def cargoWeight(self):
[303]4336 """Get the cargo weight."""
[97]4337 return self._payloadPage.cargoWeight
4338
4339 @property
[303]4340 def mailWeight(self):
4341 """Get the mail weight."""
4342 return self._payloadPage.mailWeight
4343
4344 @property
[84]4345 def zfw(self):
4346 """Get the calculated ZFW value."""
4347 return 0 if self._bookedFlight is None \
4348 else self._payloadPage.calculateZFW()
4349
4350 @property
[383]4351 def filedCruiseLevel(self):
4352 """Get the filed cruise level."""
4353 return self._routePage.filedCruiseLevel
4354
4355 @property
[97]4356 def filedCruiseAltitude(self):
4357 """Get the filed cruise altitude."""
4358 return self._routePage.filedCruiseLevel * 100
4359
4360 @property
[84]4361 def cruiseAltitude(self):
4362 """Get the cruise altitude."""
[383]4363 level = self._cruisePage.cruiseLevel if self._cruisePage.activated \
4364 else self._routePage.filedCruiseLevel
4365 return level * 100
4366
4367 @property
4368 def loggableCruiseAltitude(self):
4369 """Get the cruise altitude that can be logged."""
4370 if self._cruisePage.activated:
4371 return self._cruisePage.loggableCruiseLevel * 100
4372 else:
4373 return 0
[84]4374
4375 @property
[97]4376 def route(self):
4377 """Get the route."""
4378 return self._routePage.route
4379
4380 @property
[687]4381 def alternate(self):
4382 """Get the ICAO code of the alternate airport."""
4383 return self._routePage.alternate
4384
4385 @property
[97]4386 def departureMETAR(self):
4387 """Get the METAR of the departure airport."""
4388 return self._departureBriefingPage.metar
4389
4390 @property
4391 def arrivalMETAR(self):
4392 """Get the METAR of the arrival airport."""
4393 return self._arrivalBriefingPage.metar
4394
4395 @property
4396 def departureRunway(self):
4397 """Get the departure runway."""
4398 return self._takeoffPage.runway
4399
4400 @property
4401 def sid(self):
4402 """Get the SID."""
4403 return self._takeoffPage.sid
4404
4405 @property
[84]4406 def v1(self):
4407 """Get the V1 speed."""
[86]4408 return self._takeoffPage.v1
[84]4409
4410 @property
4411 def vr(self):
4412 """Get the Vr speed."""
[86]4413 return self._takeoffPage.vr
[84]4414
4415 @property
4416 def v2(self):
4417 """Get the V2 speed."""
[86]4418 return self._takeoffPage.v2
4419
4420 @property
[384]4421 def derate(self):
4422 """Get the derate value."""
4423 return self._takeoffPage.derate
4424
4425 @property
[391]4426 def takeoffAntiIceOn(self):
4427 """Get whether the anti-ice system was on during take-off."""
4428 return self._takeoffPage.antiIceOn
4429
4430 @takeoffAntiIceOn.setter
4431 def takeoffAntiIceOn(self, value):
4432 """Set anti-ice on indicator."""
4433 self._takeoffPage.antiIceOn = value
4434
4435 @property
[349]4436 def rtoIndicated(self):
4437 """Get whether the pilot has indicated that an RTO has occured."""
4438 return self._takeoffPage.rtoIndicated
4439
4440 @property
[97]4441 def arrivalRunway(self):
4442 """Get the arrival runway."""
4443 return self._landingPage.runway
4444
4445 @property
4446 def star(self):
4447 """Get the STAR."""
4448 return self._landingPage.star
4449
4450 @property
4451 def transition(self):
4452 """Get the transition."""
4453 return self._landingPage.transition
4454
4455 @property
4456 def approachType(self):
4457 """Get the approach type."""
4458 return self._landingPage.approachType
4459
4460 @property
[86]4461 def vref(self):
4462 """Get the Vref speed."""
4463 return self._landingPage.vref
[84]4464
[97]4465 @property
[391]4466 def landingAntiIceOn(self):
4467 """Get whether the anti-ice system was on during landing."""
4468 return self._landingPage.antiIceOn
4469
4470 @landingAntiIceOn.setter
4471 def landingAntiIceOn(self, value):
4472 """Set anti-ice on indicator."""
4473 self._landingPage.antiIceOn = value
4474
4475 @property
[97]4476 def flightType(self):
4477 """Get the flight type."""
4478 return self._finishPage.flightType
4479
4480 @property
4481 def online(self):
4482 """Get whether the flight was online or not."""
4483 return self._finishPage.online
4484
[687]4485 @property
4486 def usingSimBrief(self):
4487 """Indicate if we are using a SimBrief briefing or not."""
4488 return self._usingSimBrief
4489
4490 @usingSimBrief.setter
4491 def usingSimBrief(self, x):
4492 """Set whether we are using a SimBrief briefing or not."""
4493 self._usingSimBrief = x
4494
[70]4495 def nextPage(self, finalize = True):
[42]4496 """Go to the next page."""
[70]4497 self.jumpPage(1, finalize)
[51]4498
[692]4499 def jumpPage(self, count, finalize = True, fromPageShift = None):
[51]4500 """Go to the page which is 'count' pages after the current one."""
[692]4501 self.setCurrentPage(self._currentPage + count,
4502 finalize = finalize, fromPageShift = fromPageShift)
[46]4503
4504 def grabDefault(self):
4505 """Make the default button of the current page the default."""
4506 self._pages[self._currentPage].grabDefault()
[51]4507
[59]4508 def connected(self, fsType, descriptor):
4509 """Called when the connection could be made to the simulator."""
4510 self.nextPage()
4511
[208]4512 def reset(self, loginResult):
[91]4513 """Resets the wizard to go back to the login page."""
[208]4514 self._initialize(keepLoginResult = loginResult is None,
4515 loginResult = loginResult)
[59]4516
[84]4517 def setStage(self, stage):
4518 """Set the flight stage to the given one."""
[383]4519 if stage!=const.STAGE_END:
4520 self._cruisePage.setLoggable(Flight.canLogCruiseAltitude(stage))
4521
[84]4522 if stage==const.STAGE_TAKEOFF:
[101]4523 self._takeoffPage.allowForward()
[106]4524 elif stage==const.STAGE_LANDING:
4525 if not self._arrivalBriefingPage.metarEdited:
4526 print "Downloading arrival METAR again"
4527 self.gui.webHandler.getMETARs(self._arrivalMETARCallback,
4528 [self._bookedFlight.arrivalICAO])
[347]4529
[88]4530 elif stage==const.STAGE_END:
[275]4531 for page in self._pages:
4532 page.flightEnded()
[84]4533
[208]4534 def _initialize(self, keepLoginResult = False, loginResult = None):
[59]4535 """Initialize the wizard."""
[208]4536 if not keepLoginResult:
4537 self._loginResult = loginResult
4538
4539 self._loginCallback = None
4540
[59]4541 self._fleet = None
4542 self._fleetCallback = None
[347]4543
[59]4544 self._bookedFlight = None
4545 self._departureGate = "-"
[141]4546 self._fuelData = None
[64]4547 self._departureNOTAMs = None
[67]4548 self._departureMETAR = None
[64]4549 self._arrivalNOTAMs = None
[67]4550 self._arrivalMETAR = None
[692]4551 self._usingSimBrief = None
[67]4552
[208]4553 firstPage = 0 if self._loginResult is None else 1
4554 for page in self._pages[firstPage:]:
[67]4555 page.reset()
[347]4556
[208]4557 self.setCurrentPage(firstPage)
[621]4558 #self.setCurrentPage(10)
[208]4559
4560 def login(self, callback, pilotID, password, entranceExam):
4561 """Called when the login button was clicked."""
4562 self._loginCallback = callback
4563 if pilotID is None:
4564 loginResult = self._loginResult
4565 assert loginResult is not None and loginResult.loggedIn
4566 pilotID = loginResult.pilotID
4567 password = loginResult.password
4568 entranceExam = loginResult.entranceExam
4569 busyMessage = xstr("reload_busy")
4570 else:
4571 self._loginResult = None
4572 busyMessage = xstr("login_busy")
4573
4574 self.gui.beginBusy(busyMessage)
[347]4575
[208]4576 self.gui.webHandler.login(self._loginResultCallback,
4577 pilotID, password,
4578 entranceExam = entranceExam)
4579
4580 def reloadFlights(self, callback):
4581 """Reload the flights from the MAVA server."""
4582 self.login(callback, None, None, None)
4583
[304]4584 def cruiseLevelChanged(self):
4585 """Called when the cruise level is changed."""
[383]4586 return self.gui.cruiseLevelChanged()
[304]4587
[586]4588 def metarChanged(self, metar, originator):
4589 """Called when a METER is changed on on of the pages.
4590
4591 originator is the page that originated the changed. It will be used to
4592 determine which METAR (departure or arrival) has changed."""
4593 metar = metar.upper()
4594 if originator in [self._departureBriefingPage, self._takeoffPage]:
4595 self._departureMETARChanged(metar, originator)
4596 else:
4597 self._arrivalMETARChanged(metar, originator)
4598
4599 def _departureMETARChanged(self, metar, originator):
4600 """Called when the departure METAR has been edited on one of the
4601 pages.
4602
4603 originator is the page that originated the change. It will not be
4604 called to set the METAR, while others will be."""
4605 for page in [self._departureBriefingPage, self._takeoffPage]:
4606 if page is not originator:
4607 page.changeMETAR(metar)
4608
4609 def _arrivalMETARChanged(self, metar, originator):
4610 """Called when the arrival METAR has been edited on one of the
4611 pages.
4612
4613 originator is the page that originated the change. It will not be
4614 called to set the METAR, while others will be."""
[589]4615 for page in [self._arrivalBriefingPage, self._landingPage]:
[586]4616 if page is not originator:
4617 page.changeMETAR(metar)
4618
[208]4619 def _loginResultCallback(self, returned, result):
4620 """The login result callback, called in the web handler's thread."""
4621 gobject.idle_add(self._handleLoginResult, returned, result)
4622
4623 def _handleLoginResult(self, returned, result):
4624 """Handle the login result."""
4625 self.gui.endBusy()
[555]4626 returned = True
[208]4627 isReload = self._loginResult is not None
4628 if returned:
4629 if result.loggedIn:
4630 self._loginResult = result
4631 else:
4632 if isReload:
4633 message = xstr("reload_failed")
4634 else:
4635 message = xstr("login_entranceExam_invalid"
4636 if self.entranceExam else
4637 xstr("login_invalid"))
4638 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
4639 type = MESSAGETYPE_ERROR,
4640 message_format = message)
4641 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
4642 dialog.set_title(WINDOW_TITLE_BASE)
4643 if isReload:
4644 secondary = xstr("reload_failed_sec")
4645 else:
4646 secondary = xstr("login_entranceExam_invalid_sec"
4647 if self.entranceExam else
4648 xstr("login_invalid_sec"))
4649 dialog.format_secondary_markup(secondary)
4650 dialog.run()
4651 dialog.hide()
4652 else:
4653 message = xstr("reload_failconn") if isReload \
4654 else xstr("login_failconn")
4655 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
4656 type = MESSAGETYPE_ERROR,
4657 message_format = message)
4658 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
4659 dialog.set_title(WINDOW_TITLE_BASE)
4660 secondary = xstr("reload_failconn_sec") if isReload \
4661 else xstr("login_failconn_sec")
4662 dialog.format_secondary_markup(secondary)
[347]4663
[208]4664 dialog.run()
4665 dialog.hide()
4666
4667 callback = self._loginCallback
4668 self._loginCallback = None
4669 callback(returned, result)
[126]4670
[130]4671 def getFleet(self, callback, force = False):
[126]4672 """Get the fleet via the GUI and call the given callback."""
4673 self._fleetCallback = callback
[130]4674 self.gui.getFleet(callback = self._fleetRetrieved, force = force)
[126]4675
4676 def _fleetRetrieved(self, fleet):
4677 """Callback for the fleet retrieval."""
4678 self._fleet = fleet
4679 if self._fleetCallback is not None:
4680 self._fleetCallback(fleet)
4681 self._fleetCallback = None
[347]4682
[130]4683 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
[51]4684 """Update the given plane's gate information."""
[130]4685 self.gui.updatePlane(tailNumber, status, gateNumber = gateNumber,
4686 callback = callback)
[51]4687
[349]4688 def updateRTO(self):
4689 """Update the RTO state.
4690
4691 The RTO checkbox will be enabled if the flight has an RTO state and the
4692 comments field contains some text."""
4693 flight = self.gui.flight
4694 rtoEnabled = flight is not None and flight.hasRTO and \
4695 self.gui.hasComments
4696 self._takeoffPage.setRTOEnabled(rtoEnabled)
4697
[555]4698 def commentsChanged(self):
4699 """Called when the comments have changed."""
4700 self.updateRTO()
4701 self._finishPage.updateButtons()
4702
4703 def delayCodesChanged(self):
4704 """Called when the delay codes have changed."""
4705 self._finishPage.updateButtons()
4706
[605]4707 def faultExplanationsChanged(self):
4708 """Called when the faults and their explanations have changed."""
4709 self._finishPage.updateButtons()
4710
[349]4711 def rtoToggled(self, indicated):
4712 """Called when the RTO indication has changed."""
4713 self.gui.rtoToggled(indicated)
4714
[501]4715 def _connectSimulator(self, simulatorType):
[51]4716 """Connect to the simulator."""
[501]4717 self.gui.connectSimulator(self._bookedFlight.aircraftType,
4718 simulatorType)
[106]4719
4720 def _arrivalMETARCallback(self, returned, result):
4721 """Called when the METAR of the arrival airport is retrieved."""
4722 gobject.idle_add(self._handleArrivalMETAR, returned, result)
[347]4723
[106]4724 def _handleArrivalMETAR(self, returned, result):
4725 """Called when the METAR of the arrival airport is retrieved."""
4726 icao = self._bookedFlight.arrivalICAO
4727 if returned and icao in result.metars:
4728 metar = result.metars[icao]
4729 if metar!="":
4730 self._arrivalBriefingPage.setMETAR(metar)
[347]4731
[42]4732#-----------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.