source: src/mlx/gui/flight.py@ 810:2414e2751578

Last change on this file since 810:2414e2751578 was 810:2414e2751578, checked in by István Váradi <ivaradi@…>, 8 years ago

Information about the number of rejected and pending flights is dispayed on the flight selection page (re #307)

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