source: src/mlx/gui/flight.py@ 734:fdad27df77c2

cef
Last change on this file since 734:fdad27df77c2 was 728:3b16f898a5b9, checked in by István Váradi <ivaradi@…>, 9 years ago

The widgets on the landing page are properly arranged

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