source: src/mlx/gui/flight.py@ 712:b46a773d4b9e

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

Added a life-span handler for the SimBrief browser page as the browser is closed on Windows when navigating to another page in the flight wizard (re #279).

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