source: src/mlx/gui/flight.py@ 690:5ae4c108757e

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

The SimBrief credentials are stored and used, if available (re #279)

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