source: src/mlx/gui/flight.py@ 710:1a47a0bef14c

cef
Last change on this file since 710:1a47a0bef14c was 710:1a47a0bef14c, checked in by István Váradi <ivaradi@…>, 7 years ago

If there is no SimBrief data for the aircraft, skip SimBrief even if enabled
(re #279).

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