source: src/mlx/gui/flight.py@ 718:0acdad0d34c0

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

The departure and arrival METARs are taken from the SimBrief flight plan (re #279).

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