source: src/mlx/gui/flight.py@ 708:2e411a2d77a0

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

The takeoff and landing runways can be entered (re #279).

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