source: src/mlx/gui/flight.py@ 717:b6904f6488a4

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

The takeoff and landing runways are reused from the SimBrief setup page on the takeoff and landing pages (re #279).

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