source: src/mlx/gui/flight.py@ 702:b76cdc5ff2dc

cef
Last change on this file since 702:b76cdc5ff2dc was 700:829f72992a92, checked in by István Váradi <ivaradi@…>, 8 years ago

Added a dialog to query the credentials if login fails (re #279).[H

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