source: src/mlx/gui/flight.py@ 391:0f2e90eae832

Last change on this file since 391:0f2e90eae832 was 391:0f2e90eae832, checked in by István Váradi <ivaradi@…>, 11 years ago

Added support for logging the state of the anti-ice system (re #159)

File size: 129.6 KB
Line 
1
2from mlx.gui.common import *
3
4import mlx.const as const
5import mlx.fs as fs
6import mlx.acft as acft
7from mlx.flight import Flight
8from mlx.checks import PayloadChecker
9import mlx.util as util
10from mlx.pirep import PIREP
11from mlx.i18n import xstr
12from mlx.sound import startSound
13import mlx.web as web
14
15import datetime
16import time
17
18#-----------------------------------------------------------------------------
19
20## @package mlx.gui.flight
21#
22# The flight "wizard".
23#
24# This module implements the main tab of the application, the flight
25# wizard. The wizard consists of \ref Page "pages", that come one after the
26# other. As some pages might be skipped, the pages dynamically store the index
27# of the previous page so that going back to it is simpler. The \ref
28# Page.activate "activate" function is called before a page is first shown
29# during a flight. This function should initialize the page's controls and fill
30# it with initial data. When a page is left for the first time, its \ref
31# Page.finalize "finalize" function is called. It should set those controls
32# insensitive, that will not be available if the user comes back to this page.
33#
34# Each page has a title at the top displayed in inverted colors and a big
35# font. There is a help text below it centered, that shortly describes what is
36# expected on the page. There can be two help texts: one shown when the page is
37# first displayed during a flight, another shown when the user goes back to the
38# page. The main content area is below this, also centered. Finally, there are
39# some buttons at the bottom on the right. As some buttons occur very
40# frequently, there are functions to add them (\ref Page.addCancelFlightButton
41# "addCancelFlightButton", \ref Page.addPreviousButton "addPreviousButton" and
42# \ref Page.addNextButton "addNextButton".
43#
44# The \ref Wizard class is the main class to collect the pages. It also stores
45# some data passed from one page to another and provides properties to access
46# data items set via the wizard pages.
47
48#-----------------------------------------------------------------------------
49
50class Page(gtk.Alignment):
51 """A page in the flight wizard."""
52 def __init__(self, wizard, title, help, completedHelp = None):
53 """Construct the page."""
54 super(Page, self).__init__(xalign = 0.0, yalign = 0.0,
55 xscale = 1.0, yscale = 1.0)
56 self.set_padding(padding_top = 4, padding_bottom = 4,
57 padding_left = 12, padding_right = 12)
58
59 frame = gtk.Frame()
60 self.add(frame)
61
62 self._vbox = gtk.VBox()
63 self._vbox.set_homogeneous(False)
64 frame.add(self._vbox)
65
66 eventBox = gtk.EventBox()
67
68 alignment = gtk.Alignment(xalign = 0.0, xscale = 0.0)
69
70 titleLabel = gtk.Label(title)
71 titleLabel.modify_font(pango.FontDescription("bold 24"))
72 alignment.set_padding(padding_top = 4, padding_bottom = 4,
73 padding_left = 6, padding_right = 0)
74
75 alignment.add(titleLabel)
76 eventBox.add(alignment)
77
78 self._vbox.pack_start(eventBox, False, False, 0)
79
80 self._titleEventBox = eventBox
81 self._titleLabel = titleLabel
82
83 mainBox = gtk.VBox()
84
85 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
86 xscale = 1.0, yscale = 1.0)
87 alignment.set_padding(padding_top = 16, padding_bottom = 16,
88 padding_left = 16, padding_right = 16)
89 alignment.add(mainBox)
90 self._vbox.pack_start(alignment, True, True, 0)
91
92 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
93 xscale = 0.0, yscale = 0.0)
94 alignment.set_padding(padding_top = 0, padding_bottom = 16,
95 padding_left = 0, padding_right = 0)
96
97 self._help = help
98 self._completedHelp = completedHelp
99
100 if self._completedHelp is None or \
101 len(help.splitlines())>=len(completedHelp.splitlines()):
102 longerHelp = help
103 else:
104 longerHelp = completedHelp
105
106 self._helpLabel = gtk.Label(completedHelp)
107 # FIXME: should be a constant in common
108 self._helpLabel.set_justify(gtk.Justification.CENTER if pygobject
109 else gtk.JUSTIFY_CENTER)
110 self._helpLabel.set_use_markup(True)
111 alignment.add(self._helpLabel)
112 mainBox.pack_start(alignment, False, False, 0)
113
114 self._mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
115 xscale = 1.0, yscale = 1.0)
116 mainBox.pack_start(self._mainAlignment, True, True, 0)
117
118 buttonAlignment = gtk.Alignment(xalign = 1.0, xscale=0.0, yscale = 0.0)
119 buttonAlignment.set_padding(padding_top = 4, padding_bottom = 10,
120 padding_left = 16, padding_right = 16)
121
122 self._buttonBox = gtk.HBox()
123 self._buttonBox.set_homogeneous(False)
124 self._defaultButton = None
125 buttonAlignment.add(self._buttonBox)
126
127 self._vbox.pack_start(buttonAlignment, False, False, 0)
128
129 self._wizard = wizard
130
131 self._cancelFlightButton = None
132
133 self._completed = False
134 self._fromPage = None
135
136 def setMainWidget(self, widget):
137 """Set the given widget as the main one."""
138 self._mainAlignment.add(widget)
139
140 def addButton(self, label, default = False, sensitive = True,
141 tooltip = None, clicked = None, padding = 4):
142 """Add a button with the given label.
143
144 Return the button object created."""
145 button = gtk.Button(label)
146 self._buttonBox.pack_start(button, False, False, padding)
147 button.set_use_underline(True)
148 if default:
149 button.set_can_default(True)
150 self._defaultButton = button
151 button.set_sensitive(sensitive)
152 if tooltip is not None:
153 button.set_tooltip_text(tooltip)
154 if clicked is not None:
155 button.connect("clicked", clicked)
156 return button
157
158 def addCancelFlightButton(self):
159 """Add the 'Cancel flight' button to the page."""
160 self._cancelFlightButton = \
161 self.addButton(xstr("button_cancelFlight"),
162 sensitive = True,
163 tooltip = xstr("button_cancelFlight_tooltip"),
164 clicked = self._cancelFlight,
165 padding = 16)
166 return self._cancelFlightButton
167
168 def addPreviousButton(self, sensitive = True, clicked = None):
169 """Add the 'Next' button to the page."""
170 return self.addButton(xstr("button_previous"),
171 sensitive = sensitive,
172 tooltip = xstr("button_previous_tooltip"),
173 clicked = clicked)
174
175 def addNextButton(self, default = True, sensitive = True,
176 clicked = None):
177 """Add the 'Next' button to the page."""
178 return self.addButton(xstr("button_next"),
179 default = default,
180 sensitive = sensitive,
181 tooltip = xstr("button_next_tooltip"),
182 clicked = clicked)
183
184 def setStyle(self):
185 """Set the styles of some of the items on the page."""
186 style = self.get_style() if pygobject else self.rc_get_style()
187
188 self._titleEventBox.modify_bg(0, style.bg[3])
189 self._titleLabel.modify_fg(0, style.fg[3])
190
191 def initialize(self):
192 """Initialize the page.
193
194 It sets up the primary help, and calls the activate() function."""
195 self._helpLabel.set_markup(self._help)
196 self._helpLabel.set_sensitive(True)
197 self.activate()
198
199 def activate(self):
200 """Called when this page becomes active.
201
202 This default implementation does nothing."""
203 pass
204
205 def complete(self):
206 """Called when the page is completed.
207
208 It greys out/changes the help text and then calls finalize()."""
209 self.finalize()
210 if self._completedHelp is None:
211 self._helpLabel.set_sensitive(False)
212 else:
213 self._helpLabel.set_markup(self._completedHelp)
214 self._completed = True
215
216 def finalize(self):
217 """Called when the page is finalized."""
218 pass
219
220 def grabDefault(self):
221 """If the page has a default button, make it the default one."""
222 if self._defaultButton is not None:
223 self._defaultButton.grab_default()
224
225 def reset(self):
226 """Reset the page if the wizard is reset."""
227 self._completed = False
228 self._fromPage = None
229 if self._cancelFlightButton is not None:
230 self._cancelFlightButton.set_sensitive(True)
231
232 def goBack(self):
233 """Go to the page we were invoked from."""
234 assert self._fromPage is not None
235
236 self._wizard.setCurrentPage(self._fromPage, finalize = False)
237
238 def flightEnded(self):
239 """Called when the flight has ended.
240
241 This default implementation disables the cancel flight button."""
242 if self._cancelFlightButton is not None:
243 self._cancelFlightButton.set_sensitive(False)
244
245 def _cancelFlight(self, button):
246 """Called when the Cancel flight button is clicked."""
247 self._wizard.gui.cancelFlight()
248
249#-----------------------------------------------------------------------------
250
251class LoginPage(Page):
252 """The login page."""
253 def __init__(self, wizard):
254 """Construct the login page."""
255 super(LoginPage, self).__init__(wizard, xstr("login"),
256 xstr("loginHelp"))
257
258 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
259 xscale = 0.0, yscale = 0.0)
260
261 table = gtk.Table(4, 2)
262 table.set_row_spacings(4)
263 table.set_col_spacings(32)
264 alignment.add(table)
265 self.setMainWidget(alignment)
266
267 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
268 xscale = 0.0, yscale = 0.0)
269 label = gtk.Label(xstr("label_pilotID"))
270 label.set_use_underline(True)
271 labelAlignment.add(label)
272 table.attach(labelAlignment, 0, 1, 0, 1)
273
274 self._pilotID = gtk.Entry()
275 self._pilotID.connect("changed", self._setControls)
276 self._pilotID.set_tooltip_text(xstr("login_pilotID_tooltip"))
277 table.attach(self._pilotID, 1, 2, 0, 1)
278 label.set_mnemonic_widget(self._pilotID)
279
280 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
281 xscale = 0.0, yscale = 0.0)
282 label = gtk.Label(xstr("label_password"))
283 label.set_use_underline(True)
284 labelAlignment.add(label)
285 table.attach(labelAlignment, 0, 1, 1, 2)
286
287 self._password = gtk.Entry()
288 self._password.set_visibility(False)
289 self._password.connect("changed", self._setControls)
290 self._password.set_tooltip_text(xstr("login_password_tooltip"))
291 table.attach(self._password, 1, 2, 1, 2)
292 label.set_mnemonic_widget(self._password)
293
294 self._rememberButton = gtk.CheckButton(xstr("remember_password"))
295 self._rememberButton.set_use_underline(True)
296 self._rememberButton.set_tooltip_text(xstr("login_remember_tooltip"))
297 table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
298
299 self._entranceExam = gtk.CheckButton(xstr("login_entranceExam"))
300 self._entranceExam.set_use_underline(True)
301 self._entranceExam.set_tooltip_text(xstr("login_entranceExam_tooltip"))
302 self._entranceExam.connect("toggled", self._setControls)
303 table.attach(self._entranceExam, 1, 2, 3, 4, ypadding = 12)
304
305 self.addButton(xstr("button_offline"),
306 clicked = self._offlineClicked,
307 tooltip = xstr("button_offline_tooltip"))
308
309 self._loginButton = self.addButton(xstr("button_login"), default = True)
310 self._loginButton.connect("clicked", self._loginClicked)
311 self._loginButton.set_tooltip_text(xstr("login_button_tooltip"))
312
313
314 @property
315 def entranceExam(self):
316 """Get whether an entrance exam is being performed."""
317 return self._entranceExam.get_active() and \
318 self._pilotID.get_text()!=""
319
320 @property
321 def pilotID(self):
322 """Get the pilot ID, if given."""
323 return self._pilotID.get_text()
324
325 def activate(self):
326 """Activate the page."""
327 config = self._wizard.gui.config
328 self._pilotID.set_text(config.pilotID)
329 self._password.set_text(config.password)
330 self._rememberButton.set_active(config.rememberPassword)
331 self._setControls(None)
332
333 def _setControls(self, entry = None):
334 """Set the sensitivity of the various controls.
335
336 The login button is sensitive only if both the pilot ID and the
337 password fields contain values.
338
339 The password field is sensitive only, if the entrance exam checkbox is
340 not selected.
341
342 The remember password checkbox is sensitive only, if the password field
343 contains text.
344
345 The entrance exam checkbox is sensitive only, if the pilot ID is not
346 empty."""
347 pilotID = self._pilotID.get_text()
348 password = self._password.get_text()
349 entranceExam = self._entranceExam.get_active()
350 self._password.set_sensitive(not entranceExam)
351 self._rememberButton.set_sensitive(password!="" and not entranceExam)
352 self._entranceExam.set_sensitive(pilotID!="")
353 self._loginButton.set_sensitive(pilotID!="" and
354 (password!="" or entranceExam))
355
356 def _offlineClicked(self, button):
357 """Called when the offline button was clicked."""
358 self._wizard.nextPage()
359
360 def _loginClicked(self, button):
361 """Called when the login button was clicked."""
362 self._wizard.login(self._handleLoginResult,
363 self._pilotID.get_text(),
364 self._password.get_text(),
365 self.entranceExam)
366
367 def _handleLoginResult(self, returned, result):
368 """Handle the login result."""
369 self._loginButton.set_sensitive(True)
370 if returned and result.loggedIn:
371 config = self._wizard.gui.config
372
373 config.pilotID = self._pilotID.get_text()
374
375 rememberPassword = self._rememberButton.get_active()
376 config.password = result.password if rememberPassword else ""
377
378 config.rememberPassword = rememberPassword
379
380 config.save()
381 self._wizard.nextPage()
382
383#-----------------------------------------------------------------------------
384
385class FlightSelectionPage(Page):
386 """The page to select the flight."""
387 def __init__(self, wizard):
388 """Construct the flight selection page."""
389 help = xstr("flightsel_help")
390 completedHelp = xstr("flightsel_chelp")
391 super(FlightSelectionPage, self).__init__(wizard, xstr("flightsel_title"),
392 help, completedHelp = completedHelp)
393
394
395 self._listStore = gtk.ListStore(str, str, str, str)
396 self._flightList = gtk.TreeView(self._listStore)
397 column = gtk.TreeViewColumn(xstr("flightsel_no"), gtk.CellRendererText(),
398 text = 1)
399 column.set_expand(True)
400 self._flightList.append_column(column)
401 column = gtk.TreeViewColumn(xstr("flightsel_deptime"), gtk.CellRendererText(),
402 text = 0)
403 column.set_expand(True)
404 self._flightList.append_column(column)
405 column = gtk.TreeViewColumn(xstr("flightsel_from"), gtk.CellRendererText(),
406 text = 2)
407 column.set_expand(True)
408 self._flightList.append_column(column)
409 column = gtk.TreeViewColumn(xstr("flightsel_to"), gtk.CellRendererText(),
410 text = 3)
411 column.set_expand(True)
412 self._flightList.append_column(column)
413 self._flightList.connect("row-activated", self._rowActivated)
414 self._flightList.connect("button-press-event", self._listButtonPressed)
415
416 self._flightListPopupMenu = None
417
418 flightSelection = self._flightList.get_selection()
419 flightSelection.connect("changed", self._selectionChanged)
420
421 scrolledWindow = gtk.ScrolledWindow()
422 scrolledWindow.add(self._flightList)
423 scrolledWindow.set_size_request(400, -1)
424 # FIXME: these should be constants in common.py
425 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
426 else gtk.POLICY_AUTOMATIC,
427 gtk.PolicyType.AUTOMATIC if pygobject
428 else gtk.POLICY_AUTOMATIC)
429 scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
430 else gtk.SHADOW_IN)
431
432 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 0.0, yscale = 1.0)
433 alignment.add(scrolledWindow)
434
435 self.setMainWidget(alignment)
436
437 self._saveButton = self.addButton(xstr("flightsel_save"),
438 sensitive = False,
439 clicked = self._saveClicked,
440 tooltip = xstr("flightsel_save_tooltip"))
441 self._saveDialog = None
442
443 self._refreshButton = self.addButton(xstr("flightsel_refresh"),
444 sensitive = True,
445 clicked = self._refreshClicked,
446 tooltip = xstr("flightsel_refresh_tooltip"))
447
448 self._loadButton = self.addButton(xstr("flightsel_load"),
449 sensitive = True,
450 tooltip = xstr("flightsel_load_tooltip"))
451 self._loadButton.connect("clicked", self._loadButtonClicked)
452 self._loadDialog = None
453
454 self._button = self.addNextButton(sensitive = False,
455 clicked = self._forwardClicked)
456
457 self._flights = []
458
459 def activate(self):
460 """Fill the flight list."""
461 self._flightList.set_sensitive(True)
462 self._loadButton.set_sensitive(True)
463 self._refreshButton.set_sensitive(self._wizard.loggedIn)
464 self._buildFlights()
465
466 def finalize(self):
467 """Finalize the page."""
468 self._flightList.set_sensitive(False)
469 self._loadButton.set_sensitive(False)
470 self._refreshButton.set_sensitive(False)
471
472 def _buildFlights(self):
473 """Rebuild the flights from the login result."""
474 self._flights = []
475 self._listStore.clear()
476 if self._wizard.loggedIn:
477 for flight in self._wizard.loginResult.flights:
478 self._addFlight(flight)
479
480 def _addFlight(self, flight):
481 """Add the given file to the list of flights."""
482 self._flights.append(flight)
483 self._listStore.append([str(flight.departureTime),
484 flight.callsign,
485 flight.departureICAO,
486 flight.arrivalICAO])
487
488 def _saveClicked(self, button):
489 """Called when the Save flight button is clicked."""
490 self._saveSelected()
491
492 def _saveSelected(self):
493 """Save the selected flight."""
494 flight = self._getSelectedFlight()
495 date = flight.departureTime.date()
496 name = "%04d-%02d-%02d %s %s-%s.vaflight" % \
497 (date.year, date.month, date.day, flight.callsign,
498 flight.departureICAO, flight.arrivalICAO)
499
500 dialog = self._getSaveDialog()
501 dialog.set_current_name(name)
502 dialog.show_all()
503 response = dialog.run()
504 dialog.hide()
505
506 if response==RESPONSETYPE_OK:
507 fileName = text2unicode(dialog.get_filename())
508 print "Saving", fileName
509 try:
510 with open(fileName, "wt") as f:
511 flight.writeIntoFile(f)
512 except Exception, e:
513 print "Failed to save flight:", str(e)
514 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
515 type = MESSAGETYPE_ERROR,
516 message_format =
517 xstr("flightsel_save_failed"))
518 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
519 dialog.set_title(WINDOW_TITLE_BASE)
520 secondary = xstr("flightsel_save_failed_sec")
521 dialog.format_secondary_markup(secondary)
522 dialog.run()
523 dialog.hide()
524
525 def _refreshClicked(self, button):
526 """Called when the refresh button is clicked."""
527 self._wizard.reloadFlights(self._refreshCallback)
528
529 def _refreshCallback(self, returned, result):
530 """Callback for the refresh."""
531 if returned and result.loggedIn:
532 self._buildFlights()
533
534 def _selectionChanged(self, selection):
535 """Called when the selection is changed."""
536 selected = selection.count_selected_rows()==1
537 self._saveButton.set_sensitive(selected)
538 self._button.set_sensitive(selected)
539
540 def _loadButtonClicked(self, loadButton):
541 """Called when the load a flight button is clicked."""
542 dialog = self._getLoadDialog()
543 dialog.show_all()
544 response = dialog.run()
545 dialog.hide()
546
547 if response==RESPONSETYPE_OK:
548 fileName = text2unicode(dialog.get_filename())
549 print "Loading", fileName
550 bookedFlight = web.BookedFlight()
551 try:
552 with open(fileName, "rt") as f:
553 bookedFlight.readFromFile(f)
554 self._addFlight(bookedFlight)
555 except Exception, e:
556 print "Failed to load flight:", str(e)
557 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
558 type = MESSAGETYPE_ERROR,
559 message_format =
560 xstr("flightsel_load_failed"))
561 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
562 dialog.set_title(WINDOW_TITLE_BASE)
563 secondary = xstr("flightsel_load_failed_sec")
564 dialog.format_secondary_markup(secondary)
565 dialog.run()
566 dialog.hide()
567
568 def _forwardClicked(self, button):
569 """Called when the forward button was clicked."""
570 if self._completed:
571 self._wizard.jumpPage(self._nextDistance, finalize = False)
572 else:
573 self._flightSelected()
574
575 def _rowActivated(self, flightList, path, column):
576 """Called when a row is activated."""
577 if not self._completed:
578 self._flightSelected()
579
580 def _flightSelected(self):
581 """Called when a flight has been selected."""
582 flight = self._getSelectedFlight()
583 self._wizard._bookedFlight = flight
584 self._wizard.gui.enableFlightInfo()
585
586 self._updateDepartureGate()
587
588 def _getSelectedFlight(self):
589 """Get the currently selected flight."""
590 selection = self._flightList.get_selection()
591 (listStore, iter) = selection.get_selected()
592 path = listStore.get_path(iter)
593 [index] = path.get_indices() if pygobject else path
594
595 return self._flights[index]
596
597 def _listButtonPressed(self, widget, event):
598 """Called when a mouse button is pressed on the flight list."""
599 if event.type!=EVENT_BUTTON_PRESS or event.button!=3:
600 return
601
602 (path, _, _, _) = self._flightList.get_path_at_pos(int(event.x),
603 int(event.y))
604 selection = self._flightList.get_selection()
605 selection.unselect_all()
606 selection.select_path(path)
607
608 menu = self._getListPopupMenu()
609 if pygobject:
610 menu.popup(None, None, None, None, event.button, event.time)
611 else:
612 menu.popup(None, None, None, event.button, event.time)
613
614 def _updateDepartureGate(self):
615 """Update the departure gate for the booked flight."""
616 flight = self._wizard._bookedFlight
617 if self._wizard.gui.config.onlineGateSystem and \
618 self._wizard.loggedIn and not self._wizard.entranceExam:
619 if flight.departureICAO=="LHBP":
620 self._wizard.getFleet(self._fleetRetrieved)
621 else:
622 self._wizard.updatePlane(self._planeUpdated,
623 flight.tailNumber,
624 const.PLANE_AWAY)
625 else:
626 self._nextDistance = 2
627 self._wizard.jumpPage(2)
628
629 def _fleetRetrieved(self, fleet):
630 """Called when the fleet has been retrieved."""
631 if fleet is None:
632 self._nextDistance = 2
633 self._wizard.jumpPage(2)
634 else:
635 plane = fleet[self._wizard._bookedFlight.tailNumber]
636 if plane is None:
637 self._nextDistance = 2
638 self._wizard.jumpPage(2)
639 elif plane.gateNumber is not None and \
640 not fleet.isGateConflicting(plane):
641 self._wizard._departureGate = plane.gateNumber
642 self._nextDistance = 2
643 self._wizard.jumpPage(2)
644 else:
645 self._nextDistance = 1
646 self._wizard.nextPage()
647
648 def _planeUpdated(self, success):
649 """Callback for the plane updating."""
650 self._nextDistance = 2
651 self._wizard.jumpPage(2)
652
653 def _getSaveDialog(self):
654 """Get the dialog to load a flight file."""
655 if self._saveDialog is not None:
656 return self._saveDialog
657
658 gui = self._wizard.gui
659 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
660 xstr("flightsel_save_title"),
661 action = FILE_CHOOSER_ACTION_SAVE,
662 buttons = (gtk.STOCK_CANCEL,
663 RESPONSETYPE_CANCEL,
664 gtk.STOCK_OK, RESPONSETYPE_OK),
665 parent = gui.mainWindow)
666 dialog.set_modal(True)
667 dialog.set_do_overwrite_confirmation(True)
668
669 filter = gtk.FileFilter()
670 filter.set_name(xstr("flightsel_filter_flights"))
671 filter.add_pattern("*.vaflight")
672 dialog.add_filter(filter)
673
674 filter = gtk.FileFilter()
675 filter.set_name(xstr("file_filter_all"))
676 filter.add_pattern("*.*")
677 dialog.add_filter(filter)
678
679 self._saveDialog = dialog
680
681 return dialog
682
683 def _getLoadDialog(self):
684 """Get the dialog to load a flight file."""
685 if self._loadDialog is not None:
686 return self._loadDialog
687
688 gui = self._wizard.gui
689 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
690 xstr("flightsel_load_title"),
691 action = FILE_CHOOSER_ACTION_OPEN,
692 buttons = (gtk.STOCK_CANCEL,
693 RESPONSETYPE_CANCEL,
694 gtk.STOCK_OK, RESPONSETYPE_OK),
695 parent = gui.mainWindow)
696 dialog.set_modal(True)
697
698 filter = gtk.FileFilter()
699 filter.set_name(xstr("flightsel_filter_flights"))
700 filter.add_pattern("*.vaflight")
701 dialog.add_filter(filter)
702
703 filter = gtk.FileFilter()
704 filter.set_name(xstr("file_filter_all"))
705 filter.add_pattern("*.*")
706 dialog.add_filter(filter)
707
708 self._loadDialog = dialog
709
710 return dialog
711
712 def _getListPopupMenu(self):
713 """Get the flight list popup menu."""
714 if self._flightListPopupMenu is None:
715 menu = gtk.Menu()
716
717 menuItem = gtk.MenuItem()
718 menuItem.set_label(xstr("flightsel_popup_select"))
719 menuItem.set_use_underline(True)
720 menuItem.connect("activate", self._popupSelect)
721 menuItem.show()
722
723 menu.append(menuItem)
724
725 menuItem = gtk.MenuItem()
726 menuItem.set_label(xstr("flightsel_popup_save"))
727 menuItem.set_use_underline(True)
728 menuItem.connect("activate", self._popupSave)
729 menuItem.show()
730
731 menu.append(menuItem)
732
733 self._flightListPopupMenu = menu
734
735 return self._flightListPopupMenu
736
737 def _popupSelect(self, menuItem):
738 """Called when the Select menu item is activated in the popup menu."""
739 if not self._completed:
740 self._flightSelected()
741
742 def _popupSave(self, menuItem):
743 """Called when the Save menu item is activated in the popup menu."""
744 if not self._completed:
745 self._saveSelected()
746
747#-----------------------------------------------------------------------------
748
749class GateSelectionPage(Page):
750 """Page to select a free gate at LHBP.
751 This page should be displayed only if we have fleet information!."""
752 def __init__(self, wizard):
753 """Construct the gate selection page."""
754 super(GateSelectionPage, self).__init__(wizard, xstr("gatesel_title"),
755 xstr("gatesel_help"))
756
757 self._listStore = gtk.ListStore(str)
758 self._gateList = gtk.TreeView(self._listStore)
759 column = gtk.TreeViewColumn(None, gtk.CellRendererText(),
760 text = 0)
761 column.set_expand(True)
762 self._gateList.append_column(column)
763 self._gateList.set_headers_visible(False)
764 self._gateList.connect("row-activated", self._rowActivated)
765
766 gateSelection = self._gateList.get_selection()
767 gateSelection.connect("changed", self._selectionChanged)
768
769 scrolledWindow = gtk.ScrolledWindow()
770 scrolledWindow.add(self._gateList)
771 scrolledWindow.set_size_request(50, -1)
772 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
773 else gtk.POLICY_AUTOMATIC,
774 gtk.PolicyType.AUTOMATIC if pygobject
775 else gtk.POLICY_AUTOMATIC)
776 scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
777 else gtk.SHADOW_IN)
778
779 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 0.0, yscale = 1.0)
780 alignment.add(scrolledWindow)
781
782 self.setMainWidget(alignment)
783
784 self.addCancelFlightButton()
785
786 self.addPreviousButton(clicked = self._backClicked)
787
788 self._button = self.addNextButton(sensitive = False,
789 clicked = self._forwardClicked)
790
791 def activate(self):
792 """Fill the gate list."""
793 self._listStore.clear()
794 self._gateList.set_sensitive(True)
795 occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
796 for gateNumber in const.lhbpGateNumbers:
797 if gateNumber not in occupiedGateNumbers:
798 self._listStore.append([gateNumber])
799
800 def finalize(self):
801 """Finalize the page."""
802 self._gateList.set_sensitive(False)
803
804 def _selectionChanged(self, selection):
805 """Called when the selection is changed."""
806 self._button.set_sensitive(selection.count_selected_rows()==1)
807
808 def _backClicked(self, button):
809 """Called when the Back button is pressed."""
810 self.goBack()
811
812 def _forwardClicked(self, button):
813 """Called when the forward button is clicked."""
814 if not self._completed:
815 self._gateSelected()
816 else:
817 self._wizard.nextPage()
818
819 def _rowActivated(self, flightList, path, column):
820 """Called when a row is activated."""
821 if not self._completed:
822 self._gateSelected()
823
824 def _gateSelected(self):
825 """Called when a gate has been selected."""
826 selection = self._gateList.get_selection()
827 (listStore, iter) = selection.get_selected()
828 (gateNumber,) = listStore.get(iter, 0)
829
830 self._wizard._departureGate = gateNumber
831
832 self._wizard.updatePlane(self._planeUpdated,
833 self._wizard._bookedFlight.tailNumber,
834 const.PLANE_HOME, gateNumber)
835
836 def _planeUpdated(self, success):
837 """Callback for the plane updating call."""
838 if success is None or success:
839 self._wizard.nextPage()
840 else:
841 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
842 type = MESSAGETYPE_ERROR,
843 message_format = xstr("gatesel_conflict"))
844 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
845 dialog.set_title(WINDOW_TITLE_BASE)
846 dialog.format_secondary_markup(xstr("gatesel_conflict_sec"))
847 dialog.run()
848 dialog.hide()
849
850 self._wizard.getFleet(self._fleetRetrieved)
851
852 def _fleetRetrieved(self, fleet):
853 """Called when the fleet has been retrieved."""
854 if fleet is None:
855 self._wizard.nextPage()
856 else:
857 self.activate()
858
859#-----------------------------------------------------------------------------
860
861class ConnectPage(Page):
862 """Page which displays the departure airport and gate (if at LHBP)."""
863 def __init__(self, wizard):
864 """Construct the connect page."""
865 help = "Load the aircraft below into the simulator and park it\n" \
866 "at the given airport, at the gate below, if present.\n\n" \
867 "Then press the Connect button to connect to the simulator."
868 completedHelp = "The basic data of your flight can be read below."
869 super(ConnectPage, self).__init__(wizard, xstr("connect_title"),
870 xstr("connect_help"),
871 completedHelp = xstr("connect_chelp"))
872
873 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
874 xscale = 0.0, yscale = 0.0)
875
876 table = gtk.Table(5, 2)
877 table.set_row_spacings(4)
878 table.set_col_spacings(16)
879 table.set_homogeneous(True)
880 alignment.add(table)
881 self.setMainWidget(alignment)
882
883 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
884 label = gtk.Label(xstr("connect_flightno"))
885 labelAlignment.add(label)
886 table.attach(labelAlignment, 0, 1, 0, 1)
887
888 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
889 self._flightNumber = gtk.Label()
890 self._flightNumber.set_width_chars(9)
891 self._flightNumber.set_alignment(0.0, 0.5)
892 labelAlignment.add(self._flightNumber)
893 table.attach(labelAlignment, 1, 2, 0, 1)
894
895 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
896 label = gtk.Label(xstr("connect_acft"))
897 labelAlignment.add(label)
898 table.attach(labelAlignment, 0, 1, 1, 2)
899
900 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
901 self._aircraft = gtk.Label()
902 self._aircraft.set_width_chars(25)
903 self._aircraft.set_alignment(0.0, 0.5)
904 labelAlignment.add(self._aircraft)
905 table.attach(labelAlignment, 1, 2, 1, 2)
906
907 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
908 label = gtk.Label(xstr("connect_tailno"))
909 labelAlignment.add(label)
910 table.attach(labelAlignment, 0, 1, 2, 3)
911
912 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
913 self._tailNumber = gtk.Label()
914 self._tailNumber.set_width_chars(10)
915 self._tailNumber.set_alignment(0.0, 0.5)
916 labelAlignment.add(self._tailNumber)
917 table.attach(labelAlignment, 1, 2, 2, 3)
918
919 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
920 label = gtk.Label(xstr("connect_airport"))
921 labelAlignment.add(label)
922 table.attach(labelAlignment, 0, 1, 3, 4)
923
924 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
925 self._departureICAO = gtk.Label()
926 self._departureICAO.set_width_chars(6)
927 self._departureICAO.set_alignment(0.0, 0.5)
928 labelAlignment.add(self._departureICAO)
929 table.attach(labelAlignment, 1, 2, 3, 4)
930
931 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
932 label = gtk.Label(xstr("connect_gate"))
933 labelAlignment.add(label)
934 table.attach(labelAlignment, 0, 1, 4, 5)
935
936 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
937 self._departureGate = gtk.Label()
938 self._departureGate.set_width_chars(5)
939 self._departureGate.set_alignment(0.0, 0.5)
940 labelAlignment.add(self._departureGate)
941 table.attach(labelAlignment, 1, 2, 4, 5)
942
943 self.addCancelFlightButton()
944
945 self.addPreviousButton(clicked = self._backClicked)
946
947 self._button = self.addButton(xstr("button_connect"), default = True,
948 tooltip = xstr("button_connect_tooltip"))
949 self._clickedID = self._button.connect("clicked", self._connectClicked)
950
951 def activate(self):
952 """Setup the departure information."""
953 self._button.set_label(xstr("button_connect"))
954 self._button.set_use_underline(True)
955 self._button.set_tooltip_text(xstr("button_connect_tooltip"))
956 self._button.disconnect(self._clickedID)
957 self._clickedID = self._button.connect("clicked", self._connectClicked)
958
959 bookedFlight = self._wizard._bookedFlight
960
961 self._flightNumber.set_markup("<b>" + bookedFlight.callsign + "</b>")
962
963 aircraftType = aircraftNames[bookedFlight.aircraftType]
964 self._aircraft.set_markup("<b>" + aircraftType + "</b>")
965
966 self._tailNumber.set_markup("<b>" + bookedFlight.tailNumber + "</b>")
967
968 icao = bookedFlight.departureICAO
969 self._departureICAO.set_markup("<b>" + icao + "</b>")
970 gate = self._wizard._departureGate
971 if gate!="-":
972 gate = "<b>" + gate + "</b>"
973 self._departureGate.set_markup(gate)
974
975 def finalize(self):
976 """Finalize the page."""
977 self._button.set_label(xstr("button_next"))
978 self._button.set_use_underline(True)
979 self._button.set_tooltip_text(xstr("button_next_tooltip"))
980 self._button.disconnect(self._clickedID)
981 self._clickedID = self._button.connect("clicked", self._forwardClicked)
982
983 def _backClicked(self, button):
984 """Called when the Back button is pressed."""
985 self.goBack()
986
987 def _connectClicked(self, button):
988 """Called when the Connect button is pressed."""
989 self._wizard._connectSimulator()
990
991 def _forwardClicked(self, button):
992 """Called when the Forward button is pressed."""
993 self._wizard.nextPage()
994
995#-----------------------------------------------------------------------------
996
997class PayloadPage(Page):
998 """Page to allow setting up the payload."""
999 def __init__(self, wizard):
1000 """Construct the page."""
1001 super(PayloadPage, self).__init__(wizard, xstr("payload_title"),
1002 xstr("payload_help"),
1003 completedHelp = xstr("payload_chelp"))
1004
1005 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1006 xscale = 0.0, yscale = 0.0)
1007
1008 table = gtk.Table(7, 3)
1009 table.set_row_spacings(4)
1010 table.set_col_spacings(16)
1011 table.set_homogeneous(False)
1012 alignment.add(table)
1013 self.setMainWidget(alignment)
1014
1015 label = gtk.Label(xstr("payload_crew"))
1016 label.set_use_underline(True)
1017 label.set_alignment(0.0, 0.5)
1018 table.attach(label, 0, 1, 0, 1)
1019
1020 self._numCrew = IntegerEntry(defaultValue = 0)
1021 self._numCrew.set_width_chars(6)
1022 self._numCrew.connect("integer-changed", self._weightChanged)
1023 self._numCrew.set_tooltip_text(xstr("payload_crew_tooltip"))
1024 table.attach(self._numCrew, 1, 2, 0, 1)
1025 label.set_mnemonic_widget(self._numCrew)
1026
1027 label = gtk.Label(xstr("payload_pax"))
1028 label.set_use_underline(True)
1029 label.set_alignment(0.0, 0.5)
1030 table.attach(label, 0, 1, 1, 2)
1031
1032 self._numPassengers = IntegerEntry(defaultValue = 0)
1033 self._numPassengers.set_width_chars(6)
1034 self._numPassengers.connect("integer-changed", self._weightChanged)
1035 self._numPassengers.set_tooltip_text(xstr("payload_pax_tooltip"))
1036 table.attach(self._numPassengers, 1, 2, 1, 2)
1037 label.set_mnemonic_widget(self._numPassengers)
1038
1039 label = gtk.Label(xstr("payload_bag"))
1040 label.set_use_underline(True)
1041 label.set_alignment(0.0, 0.5)
1042 table.attach(label, 0, 1, 2, 3)
1043
1044 self._bagWeight = IntegerEntry(defaultValue = 0)
1045 self._bagWeight.set_width_chars(6)
1046 self._bagWeight.connect("integer-changed", self._weightChanged)
1047 self._bagWeight.set_tooltip_text(xstr("payload_bag_tooltip"))
1048 table.attach(self._bagWeight, 1, 2, 2, 3)
1049 label.set_mnemonic_widget(self._bagWeight)
1050
1051 table.attach(gtk.Label("kg"), 2, 3, 2, 3)
1052
1053 label = gtk.Label(xstr("payload_cargo"))
1054 label.set_use_underline(True)
1055 label.set_alignment(0.0, 0.5)
1056 table.attach(label, 0, 1, 3, 4)
1057
1058 self._cargoWeight = IntegerEntry(defaultValue = 0)
1059 self._cargoWeight.set_width_chars(6)
1060 self._cargoWeight.connect("integer-changed", self._weightChanged)
1061 self._cargoWeight.set_tooltip_text(xstr("payload_cargo_tooltip"))
1062 table.attach(self._cargoWeight, 1, 2, 3, 4)
1063 label.set_mnemonic_widget(self._cargoWeight)
1064
1065 table.attach(gtk.Label("kg"), 2, 3, 3, 4)
1066
1067 label = gtk.Label(xstr("payload_mail"))
1068 label.set_use_underline(True)
1069 label.set_alignment(0.0, 0.5)
1070 table.attach(label, 0, 1, 4, 5)
1071
1072 self._mailWeight = IntegerEntry(defaultValue = 0)
1073 self._mailWeight.set_width_chars(6)
1074 self._mailWeight.connect("integer-changed", self._weightChanged)
1075 self._mailWeight.set_tooltip_text(xstr("payload_mail_tooltip"))
1076 table.attach(self._mailWeight, 1, 2, 4, 5)
1077 label.set_mnemonic_widget(self._mailWeight)
1078
1079 table.attach(gtk.Label("kg"), 2, 3, 4, 5)
1080
1081 label = gtk.Label("<b>" + xstr("payload_zfw") + "</b>")
1082 label.set_alignment(0.0, 0.5)
1083 label.set_use_markup(True)
1084 table.attach(label, 0, 1, 5, 6)
1085
1086 self._calculatedZFW = gtk.Label()
1087 self._calculatedZFW.set_width_chars(6)
1088 self._calculatedZFW.set_alignment(1.0, 0.5)
1089 table.attach(self._calculatedZFW, 1, 2, 5, 6)
1090
1091 table.attach(gtk.Label("kg"), 2, 3, 5, 6)
1092
1093 self._zfwButton = gtk.Button(xstr("payload_fszfw"))
1094 self._zfwButton.set_use_underline(True)
1095 self._zfwButton.connect("clicked", self._zfwRequested)
1096 self._zfwButton.set_tooltip_text(xstr("payload_fszfw_tooltip"))
1097 table.attach(self._zfwButton, 0, 1, 6, 7)
1098
1099 self._simulatorZFW = gtk.Label("-")
1100 self._simulatorZFW.set_width_chars(6)
1101 self._simulatorZFW.set_alignment(1.0, 0.5)
1102 table.attach(self._simulatorZFW, 1, 2, 6, 7)
1103 self._simulatorZFWValue = None
1104
1105 table.attach(gtk.Label("kg"), 2, 3, 6, 7)
1106
1107 self.addCancelFlightButton()
1108 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1109 self._button = self.addNextButton(clicked = self._forwardClicked)
1110
1111 @property
1112 def numCrew(self):
1113 """The number of the crew members on the flight."""
1114 return self._numCrew.get_int()
1115
1116 @property
1117 def numPassengers(self):
1118 """The number of the passengers on the flight."""
1119 return self._numPassengers.get_int()
1120
1121 @property
1122 def bagWeight(self):
1123 """Get the bag weight entered."""
1124 return self._bagWeight.get_int()
1125
1126 @property
1127 def cargoWeight(self):
1128 """Get the cargo weight entered."""
1129 return self._cargoWeight.get_int()
1130
1131 @property
1132 def mailWeight(self):
1133 """Get the bag weight entered."""
1134 return self._mailWeight.get_int()
1135
1136 def activate(self):
1137 """Setup the information."""
1138 bookedFlight = self._wizard._bookedFlight
1139
1140 self._numCrew.set_int(bookedFlight.numCrew)
1141 self._numCrew.set_sensitive(True)
1142 self._numPassengers.set_int(bookedFlight.numPassengers)
1143 self._numPassengers.set_sensitive(True)
1144
1145 self._bagWeight.set_int(bookedFlight.bagWeight)
1146 self._bagWeight.set_sensitive(True)
1147 self._cargoWeight.set_int(bookedFlight.cargoWeight)
1148 self._cargoWeight.set_sensitive(True)
1149 self._mailWeight.set_int(bookedFlight.mailWeight)
1150 self._mailWeight.set_sensitive(True)
1151
1152 self._simulatorZFW.set_text("-")
1153 self._simulatorZFWValue = None
1154 self._zfwButton.set_sensitive(True)
1155 self._updateCalculatedZFW()
1156
1157 def finalize(self):
1158 """Finalize the payload page."""
1159 self._numCrew.set_sensitive(False)
1160 self._numPassengers.set_sensitive(False)
1161 self._bagWeight.set_sensitive(False)
1162 self._cargoWeight.set_sensitive(False)
1163 self._mailWeight.set_sensitive(False)
1164 self._wizard.gui.initializeWeightHelp()
1165
1166 def calculateZFW(self):
1167 """Calculate the ZFW value."""
1168 zfw = self._wizard.gui._flight.aircraft.dow
1169 zfw += (self._numCrew.get_int() + self._numPassengers.get_int()) * 82
1170 zfw += self._bagWeight.get_int()
1171 zfw += self._cargoWeight.get_int()
1172 zfw += self._mailWeight.get_int()
1173 return zfw
1174
1175 def _updateCalculatedZFW(self):
1176 """Update the calculated ZFW"""
1177 zfw = self.calculateZFW()
1178
1179 markupBegin = "<b>"
1180 markupEnd = "</b>"
1181 if self._simulatorZFWValue is not None and \
1182 PayloadChecker.isZFWFaulty(self._simulatorZFWValue, zfw):
1183 markupBegin += '<span foreground="red">'
1184 markupEnd = "</span>" + markupEnd
1185 self._calculatedZFW.set_markup(markupBegin + str(zfw) + markupEnd)
1186
1187 def _weightChanged(self, entry, weight):
1188 """Called when one of the weight values or humanm counts has changed."""
1189 self._updateCalculatedZFW()
1190
1191 def _zfwRequested(self, button):
1192 """Called when the ZFW is requested from the simulator."""
1193 self._zfwButton.set_sensitive(False)
1194 self._backButton.set_sensitive(False)
1195 self._button.set_sensitive(False)
1196 gui = self._wizard.gui
1197 gui.beginBusy(xstr("payload_zfw_busy"))
1198 gui.simulator.requestZFW(self._handleZFW)
1199
1200 def _handleZFW(self, zfw):
1201 """Called when the ZFW value is retrieved."""
1202 gobject.idle_add(self._processZFW, zfw)
1203
1204 def _processZFW(self, zfw):
1205 """Process the given ZFW value received from the simulator."""
1206 self._wizard.gui.endBusy()
1207 self._zfwButton.set_sensitive(True)
1208 self._backButton.set_sensitive(True)
1209 self._button.set_sensitive(True)
1210 self._simulatorZFWValue = zfw
1211 self._simulatorZFW.set_text("%.0f" % (zfw,))
1212 self._updateCalculatedZFW()
1213
1214 def _forwardClicked(self, button):
1215 """Called when the forward button is clicked."""
1216 self._wizard.nextPage()
1217
1218 def _backClicked(self, button):
1219 """Called when the Back button is pressed."""
1220 self.goBack()
1221
1222#-----------------------------------------------------------------------------
1223
1224class TimePage(Page):
1225 """Page displaying the departure and arrival times and allows querying the
1226 current time from the flight simulator."""
1227 def __init__(self, wizard):
1228 super(TimePage, self).__init__(wizard, xstr("time_title"),
1229 xstr("time_help"),
1230 completedHelp = xstr("time_chelp"))
1231
1232 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1233 xscale = 0.0, yscale = 0.0)
1234
1235 table = gtk.Table(3, 2)
1236 table.set_row_spacings(4)
1237 table.set_col_spacings(16)
1238 table.set_homogeneous(False)
1239 alignment.add(table)
1240 self.setMainWidget(alignment)
1241
1242 label = gtk.Label(xstr("time_departure"))
1243 label.set_alignment(0.0, 0.5)
1244 table.attach(label, 0, 1, 0, 1)
1245
1246 self._departure = gtk.Label()
1247 self._departure.set_alignment(0.0, 0.5)
1248 table.attach(self._departure, 1, 2, 0, 1)
1249
1250 label = gtk.Label(xstr("time_arrival"))
1251 label.set_alignment(0.0, 0.5)
1252 table.attach(label, 0, 1, 1, 2)
1253
1254 self._arrival = gtk.Label()
1255 self._arrival.set_alignment(0.0, 0.5)
1256 table.attach(self._arrival, 1, 2, 1, 2)
1257
1258 self._timeButton = gtk.Button(xstr("time_fs"))
1259 self._timeButton.set_use_underline(True)
1260 self._timeButton.set_tooltip_text(xstr("time_fs_tooltip"))
1261 self._timeButton.connect("clicked", self._timeRequested)
1262 table.attach(self._timeButton, 0, 1, 2, 3)
1263
1264 self._simulatorTime = gtk.Label("-")
1265 self._simulatorTime.set_alignment(0.0, 0.5)
1266 table.attach(self._simulatorTime, 1, 2, 2, 3)
1267
1268 self.addCancelFlightButton()
1269
1270 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1271 self._button = self.addNextButton(clicked = self._forwardClicked)
1272
1273 def activate(self):
1274 """Activate the page."""
1275 self._timeButton.set_sensitive(True)
1276 bookedFlight = self._wizard._bookedFlight
1277 self._departure.set_text(str(bookedFlight.departureTime.time()))
1278 self._arrival.set_text(str(bookedFlight.arrivalTime.time()))
1279 self._simulatorTime.set_text("-")
1280
1281 def _timeRequested(self, button):
1282 """Request the time from the simulator."""
1283 self._timeButton.set_sensitive(False)
1284 self._backButton.set_sensitive(False)
1285 self._button.set_sensitive(False)
1286 self._wizard.gui.beginBusy(xstr("time_busy"))
1287 self._wizard.gui.simulator.requestTime(self._handleTime)
1288
1289 def _handleTime(self, timestamp):
1290 """Handle the result of a time retrieval."""
1291 gobject.idle_add(self._processTime, timestamp)
1292
1293 def _processTime(self, timestamp):
1294 """Process the given time."""
1295 self._wizard.gui.endBusy()
1296 self._timeButton.set_sensitive(True)
1297 self._backButton.set_sensitive(True)
1298 self._button.set_sensitive(True)
1299 tm = time.gmtime(timestamp)
1300 t = datetime.time(tm.tm_hour, tm.tm_min, tm.tm_sec)
1301 self._simulatorTime.set_text(str(t))
1302
1303 ts = tm.tm_hour * 3600 + tm.tm_min * 60 + tm.tm_sec
1304 dt = self._wizard._bookedFlight.departureTime.time()
1305 dts = dt.hour * 3600 + dt.minute * 60 + dt.second
1306 diff = dts-ts
1307
1308 markupBegin = ""
1309 markupEnd = ""
1310 if diff < 0:
1311 markupBegin = '<b><span foreground="red">'
1312 markupEnd = '</span></b>'
1313 elif diff < 3*60 or diff > 30*60:
1314 markupBegin = '<b><span foreground="orange">'
1315 markupEnd = '</span></b>'
1316
1317 self._departure.set_markup(markupBegin + str(dt) + markupEnd)
1318
1319 def _backClicked(self, button):
1320 """Called when the Back button is pressed."""
1321 self.goBack()
1322
1323 def _forwardClicked(self, button):
1324 """Called when the forward button is clicked."""
1325 if not self._completed:
1326 gui = self._wizard.gui
1327 gui.beginBusy(xstr("fuel_get_busy"))
1328
1329 gui.simulator.getFuel(self._handleFuel)
1330 else:
1331 self._wizard.nextPage()
1332
1333 def _handleFuel(self, fuelData):
1334 """Callback for the fuel query operation."""
1335 gobject.idle_add(self._processFuel, fuelData)
1336
1337 def _processFuel(self, fuelData):
1338 """Process the given fuel data."""
1339 self._wizard.gui.endBusy()
1340 self._wizard._fuelData = fuelData
1341 self._wizard.nextPage()
1342
1343#-----------------------------------------------------------------------------
1344
1345class FuelTank(gtk.VBox):
1346 """Widget for the fuel tank."""
1347 def __init__(self, fuelTank, name, capacity, currentWeight):
1348 """Construct the widget for the tank with the given name."""
1349 super(FuelTank, self).__init__()
1350
1351 self._enabled = True
1352 self.fuelTank = fuelTank
1353 self.capacity = capacity
1354 self.currentWeight = currentWeight
1355 self.expectedWeight = currentWeight
1356
1357 label = gtk.Label("<b>" + name + "</b>")
1358 label.set_use_markup(True)
1359 label.set_use_underline(True)
1360 label.set_justify(JUSTIFY_CENTER)
1361 label.set_alignment(0.5, 1.0)
1362 self.pack_start(label, False, False, 4)
1363
1364 self._tankFigure = gtk.EventBox()
1365 self._tankFigure.set_size_request(38, -1)
1366 self._tankFigure.set_visible_window(False)
1367 self._tankFigure.set_tooltip_markup(xstr("fuel_tank_tooltip"))
1368
1369 if pygobject:
1370 self._tankFigure.connect("draw", self._drawTankFigure)
1371 else:
1372 self._tankFigure.connect("expose_event", self._drawTankFigure)
1373 self._tankFigure.connect("button_press_event", self._buttonPressed)
1374 self._tankFigure.connect("motion_notify_event", self._motionNotify)
1375 self._tankFigure.connect("scroll-event", self._scrolled)
1376
1377 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1378 xscale = 0.0, yscale = 1.0)
1379 alignment.add(self._tankFigure)
1380
1381 self.pack_start(alignment, True, True, 4)
1382
1383 self._expectedButton = gtk.SpinButton()
1384 self._expectedButton.set_numeric(True)
1385 self._expectedButton.set_range(0, self.capacity)
1386 self._expectedButton.set_increments(10, 100)
1387 self._expectedButton.set_value(currentWeight)
1388 self._expectedButton.set_alignment(1.0)
1389 self._expectedButton.set_width_chars(5)
1390 self._expectedButton.connect("value-changed", self._expectedChanged)
1391
1392 label.set_mnemonic_widget(self._expectedButton)
1393
1394 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1395 xscale = 0.0, yscale = 1.0)
1396 alignment.add(self._expectedButton)
1397 self.pack_start(alignment, False, False, 4)
1398
1399 def setCurrent(self, currentWeight):
1400 """Set the current weight."""
1401 self.currentWeight = currentWeight
1402 self._redraw()
1403
1404 def isCorrect(self):
1405 """Determine if the contents of the fuel tank are as expected"""
1406 return abs(self.expectedWeight - self.currentWeight)<=1
1407
1408 def disable(self):
1409 """Disable the fuel tank."""
1410 self._expectedButton.set_sensitive(False)
1411 self._enabled = False
1412
1413 def _redraw(self):
1414 """Redraw the tank figure."""
1415 self._tankFigure.queue_draw()
1416
1417 def _drawTankFigure(self, tankFigure, eventOrContext):
1418 """Draw the tank figure."""
1419 triangleSize = 5
1420
1421 context = eventOrContext if pygobject else tankFigure.window.cairo_create()
1422 (xOffset, yOffset) = (0, 0) if pygobject \
1423 else (tankFigure.allocation.x, tankFigure.allocation.y)
1424
1425 width = tankFigure.get_allocated_width() if pygobject \
1426 else tankFigure.allocation.width
1427 height = tankFigure.get_allocated_height() if pygobject \
1428 else tankFigure.allocation.height
1429
1430 rectangleX0 = triangleSize
1431 rectangleY0 = triangleSize
1432 rectangleX1 = width - 1 - triangleSize
1433 rectangleY1 = height - 1 - triangleSize
1434 rectangleLineWidth = 2.0
1435
1436 context.set_source_rgb(0.0, 0.0, 0.0)
1437 context.set_line_width(rectangleLineWidth)
1438 context.rectangle(xOffset + rectangleX0 + rectangleLineWidth/2,
1439 yOffset + rectangleY0 + rectangleLineWidth/2,
1440 rectangleX1 - rectangleX0 - rectangleLineWidth,
1441 rectangleY1 - rectangleY0 - rectangleLineWidth)
1442 context.stroke()
1443
1444 rectangleInnerLeft = rectangleX0 + rectangleLineWidth
1445 rectangleInnerRight = rectangleX1 - rectangleLineWidth
1446 self._rectangleInnerTop = rectangleInnerTop = rectangleY0 + rectangleLineWidth
1447 self._rectangleInnerBottom = rectangleInnerBottom = rectangleY1 - rectangleLineWidth
1448
1449 rectangleInnerWidth = rectangleInnerRight - rectangleInnerLeft
1450 rectangleInnerHeight = rectangleInnerBottom - rectangleInnerTop
1451
1452 context.set_source_rgb(1.0, 0.9, 0.6)
1453 currentHeight = self.currentWeight * rectangleInnerHeight / self.capacity
1454 currentX = rectangleInnerTop + rectangleInnerHeight - currentHeight
1455 context.rectangle(xOffset + rectangleInnerLeft,
1456 yOffset + rectangleInnerTop +
1457 rectangleInnerHeight - currentHeight,
1458 rectangleInnerWidth, currentHeight)
1459 context.fill()
1460
1461 expectedHeight = self.expectedWeight * rectangleInnerHeight / self.capacity
1462 expectedY = rectangleInnerTop + rectangleInnerHeight - expectedHeight
1463
1464 context.set_line_width(1.5)
1465 context.set_source_rgb(0.0, 0.85, 0.85)
1466 context.move_to(xOffset + rectangleX0, yOffset + expectedY)
1467 context.line_to(xOffset + rectangleX1, yOffset + expectedY)
1468 context.stroke()
1469
1470 context.set_line_width(0.0)
1471 context.move_to(xOffset + 0, yOffset + expectedY - triangleSize)
1472 context.line_to(xOffset + 0, yOffset + expectedY + triangleSize)
1473 context.line_to(xOffset + rectangleX0 + 1, yOffset + expectedY)
1474 context.line_to(xOffset + 0, yOffset + expectedY - triangleSize)
1475 context.fill()
1476
1477 context.set_line_width(0.0)
1478 context.move_to(xOffset + width, yOffset + expectedY - triangleSize)
1479 context.line_to(xOffset + width, yOffset + expectedY + triangleSize)
1480 context.line_to(xOffset + rectangleX1 - 1, yOffset + expectedY)
1481 context.line_to(xOffset + width, yOffset + expectedY - triangleSize)
1482 context.fill()
1483
1484 return True
1485
1486 def _setExpectedFromY(self, y):
1487 """Set the expected weight from the given Y-coordinate."""
1488 level = (self._rectangleInnerBottom - y) / \
1489 (self._rectangleInnerBottom - self._rectangleInnerTop)
1490 level = min(1.0, max(0.0, level))
1491 self._expectedButton.set_value(level * self.capacity)
1492
1493 def _buttonPressed(self, tankFigure, event):
1494 """Called when a button is pressed in the figure.
1495
1496 The expected level will be set there."""
1497 if self._enabled and event.button==1:
1498 self._setExpectedFromY(event.y)
1499
1500 def _motionNotify(self, tankFigure, event):
1501 """Called when the mouse pointer moves within the area of a tank figure."""
1502 if self._enabled and event.state==BUTTON1_MASK:
1503 self._setExpectedFromY(event.y)
1504
1505 def _scrolled(self, tankFigure, event):
1506 """Called when a scroll event is received."""
1507 if self._enabled:
1508 increment = 1 if event.state==CONTROL_MASK \
1509 else 100 if event.state==SHIFT_MASK \
1510 else 10 if event.state==0 else 0
1511 if increment!=0:
1512 if event.direction==SCROLL_DOWN:
1513 increment *= -1
1514 self._expectedButton.spin(SPIN_USER_DEFINED, increment)
1515
1516 def _expectedChanged(self, spinButton):
1517 """Called when the expected value has changed."""
1518 self.expectedWeight = spinButton.get_value_as_int()
1519 self._redraw()
1520
1521#-----------------------------------------------------------------------------
1522
1523class FuelPage(Page):
1524 """The page containing the fuel tank filling."""
1525 _pumpStep = 0.02
1526
1527 def __init__(self, wizard):
1528 """Construct the page."""
1529 super(FuelPage, self).__init__(wizard, xstr("fuel_title"),
1530 xstr("fuel_help"),
1531 completedHelp = xstr("fuel_chelp"))
1532
1533 self._fuelTanks = []
1534 self._fuelTable = None
1535 self._fuelAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1536 xscale = 0.0, yscale = 1.0)
1537 self.setMainWidget(self._fuelAlignment)
1538
1539 tankData = [(tank, 2500, 3900) for tank in acft.mostFuelTanks]
1540 self._setupTanks(tankData)
1541
1542 self.addCancelFlightButton()
1543
1544 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1545 self._button = self.addNextButton(clicked = self._forwardClicked)
1546
1547 self._pumpIndex = 0
1548
1549 def activate(self):
1550 """Activate the page."""
1551 self._setupTanks(self._wizard._fuelData)
1552
1553 def finalize(self):
1554 """Finalize the page."""
1555 for fuelTank in self._fuelTanks:
1556 fuelTank.disable()
1557
1558 def _backClicked(self, button):
1559 """Called when the Back button is pressed."""
1560 self.goBack()
1561
1562 def _forwardClicked(self, button):
1563 """Called when the forward button is clicked."""
1564 if not self._completed:
1565 self._pumpIndex = 0
1566 self._wizard.gui.beginBusy(xstr("fuel_pump_busy"))
1567 self._pump()
1568 else:
1569 self._wizard.nextPage()
1570
1571 def _setupTanks(self, tankData):
1572 """Setup the tanks for the given data."""
1573 numTanks = len(tankData)
1574 if self._fuelTable is not None:
1575 self._fuelAlignment.remove(self._fuelTable)
1576
1577 self._fuelTanks = []
1578 self._fuelTable = gtk.Table(numTanks, 1)
1579 self._fuelTable.set_col_spacings(16)
1580 index = 0
1581 for (tank, current, capacity) in tankData:
1582 fuelTank = FuelTank(tank,
1583 xstr("fuel_tank_" +
1584 const.fuelTank2string(tank)),
1585 capacity, current)
1586 self._fuelTable.attach(fuelTank, index, index+1, 0, 1)
1587 self._fuelTanks.append(fuelTank)
1588 index += 1
1589
1590 self._fuelAlignment.add(self._fuelTable)
1591 self.show_all()
1592
1593 def _pump(self):
1594 """Perform one step of pumping.
1595
1596 It is checked, if the current tank's contents are of the right
1597 quantity. If not, it is filled one step further to the desired
1598 contents. Otherwise the next tank is started. If all tanks are are
1599 filled, the next page is selected."""
1600 numTanks = len(self._fuelTanks)
1601
1602 fuelTank = None
1603 while self._pumpIndex < numTanks:
1604 fuelTank = self._fuelTanks[self._pumpIndex]
1605 if fuelTank.isCorrect():
1606 self._pumpIndex += 1
1607 fuelTank = None
1608 else:
1609 break
1610
1611 if fuelTank is None:
1612 self._wizard.gui.endBusy()
1613 self._wizard.nextPage()
1614 else:
1615 currentLevel = fuelTank.currentWeight / fuelTank.capacity
1616 expectedLevel = fuelTank.expectedWeight / fuelTank.capacity
1617 if currentLevel<expectedLevel:
1618 currentLevel += FuelPage._pumpStep
1619 if currentLevel>expectedLevel: currentLevel = expectedLevel
1620 else:
1621 currentLevel -= FuelPage._pumpStep
1622 if currentLevel<expectedLevel: currentLevel = expectedLevel
1623 fuelTank.setCurrent(currentLevel * fuelTank.capacity)
1624 self._wizard.gui.simulator.setFuelLevel([(fuelTank.fuelTank,
1625 currentLevel)])
1626 gobject.timeout_add(50, self._pump)
1627
1628#-----------------------------------------------------------------------------
1629
1630class RoutePage(Page):
1631 """The page containing the route and the flight level."""
1632 def __init__(self, wizard):
1633 """Construct the page."""
1634 super(RoutePage, self).__init__(wizard, xstr("route_title"),
1635 xstr("route_help"),
1636 completedHelp = xstr("route_chelp"))
1637
1638 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1639 xscale = 0.0, yscale = 0.0)
1640
1641 mainBox = gtk.VBox()
1642 alignment.add(mainBox)
1643 self.setMainWidget(alignment)
1644
1645 levelBox = gtk.HBox()
1646
1647 label = gtk.Label(xstr("route_level"))
1648 label.set_use_underline(True)
1649 levelBox.pack_start(label, True, True, 0)
1650
1651 self._cruiseLevel = gtk.SpinButton()
1652 self._cruiseLevel.set_increments(step = 10, page = 100)
1653 self._cruiseLevel.set_range(min = 50, max = 500)
1654 self._cruiseLevel.set_tooltip_text(xstr("route_level_tooltip"))
1655 self._cruiseLevel.set_numeric(True)
1656 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
1657 label.set_mnemonic_widget(self._cruiseLevel)
1658
1659 levelBox.pack_start(self._cruiseLevel, False, False, 8)
1660
1661 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1662 xscale = 0.0, yscale = 0.0)
1663 alignment.add(levelBox)
1664
1665 mainBox.pack_start(alignment, False, False, 0)
1666
1667
1668 routeBox = gtk.VBox()
1669
1670 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1671 xscale = 0.0, yscale = 0.0)
1672 label = gtk.Label(xstr("route_route"))
1673 label.set_use_underline(True)
1674 alignment.add(label)
1675 routeBox.pack_start(alignment, True, True, 0)
1676
1677 routeWindow = gtk.ScrolledWindow()
1678 routeWindow.set_size_request(400, 80)
1679 routeWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
1680 else gtk.SHADOW_IN)
1681 routeWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1682 else gtk.POLICY_AUTOMATIC,
1683 gtk.PolicyType.AUTOMATIC if pygobject
1684 else gtk.POLICY_AUTOMATIC)
1685
1686 self._uppercasingRoute = False
1687
1688 self._route = gtk.TextView()
1689 self._route.set_tooltip_text(xstr("route_route_tooltip"))
1690 self._route.set_wrap_mode(WRAP_WORD)
1691 self._route.get_buffer().connect("changed", self._routeChanged)
1692 self._route.get_buffer().connect_after("insert-text", self._routeInserted)
1693 routeWindow.add(self._route)
1694
1695 label.set_mnemonic_widget(self._route)
1696 routeBox.pack_start(routeWindow, True, True, 0)
1697
1698 mainBox.pack_start(routeBox, True, True, 8)
1699
1700 self.addCancelFlightButton()
1701
1702 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1703 self._button = self.addNextButton(clicked = self._forwardClicked)
1704
1705 @property
1706 def filedCruiseLevel(self):
1707 """Get the filed cruise level."""
1708 return self._cruiseLevel.get_value_as_int()
1709
1710 @property
1711 def route(self):
1712 """Get the route."""
1713 return self._getRoute()
1714
1715 def activate(self):
1716 """Setup the route from the booked flight."""
1717 self._cruiseLevel.set_value(240)
1718 self._route.get_buffer().set_text(self._wizard._bookedFlight.route)
1719 self._updateForwardButton()
1720
1721 def _getRoute(self):
1722 """Get the text of the route."""
1723 buffer = self._route.get_buffer()
1724 return buffer.get_text(buffer.get_start_iter(),
1725 buffer.get_end_iter(), True)
1726
1727 def _updateForwardButton(self):
1728 """Update the sensitivity of the forward button."""
1729 self._button.set_sensitive(self._cruiseLevel.get_value_as_int()>=50 and \
1730 self._getRoute()!="")
1731
1732 def _cruiseLevelChanged(self, spinButton):
1733 """Called when the cruise level has changed."""
1734 self._updateForwardButton()
1735
1736 def _routeChanged(self, textBuffer):
1737 """Called when the route has changed."""
1738 if not self._uppercasingRoute:
1739 self._updateForwardButton()
1740
1741 def _routeInserted(self, textBuffer, iter, text, length):
1742 """Called when new characters are inserted into the route.
1743
1744 It uppercases all characters."""
1745 if not self._uppercasingRoute:
1746 self._uppercasingRoute = True
1747
1748 iter1 = iter.copy()
1749 iter1.backward_chars(length)
1750 textBuffer.delete(iter, iter1)
1751
1752 textBuffer.insert(iter, text.upper())
1753
1754 self._uppercasingRoute = False
1755
1756 def _backClicked(self, button):
1757 """Called when the Back button is pressed."""
1758 self.goBack()
1759
1760 def _forwardClicked(self, button):
1761 """Called when the Forward button is clicked."""
1762 if self._completed:
1763 self._wizard.nextPage()
1764 else:
1765 bookedFlight = self._wizard._bookedFlight
1766 self._wizard.gui.beginBusy(xstr("route_down_notams"))
1767 self._wizard.gui.webHandler.getNOTAMs(self._notamsCallback,
1768 bookedFlight.departureICAO,
1769 bookedFlight.arrivalICAO)
1770 startSound(const.SOUND_NOTAM)
1771
1772 def _notamsCallback(self, returned, result):
1773 """Callback for the NOTAMs."""
1774 gobject.idle_add(self._handleNOTAMs, returned, result)
1775
1776 def _handleNOTAMs(self, returned, result):
1777 """Handle the NOTAMs."""
1778 if returned:
1779 self._wizard._departureNOTAMs = result.departureNOTAMs
1780 self._wizard._arrivalNOTAMs = result.arrivalNOTAMs
1781 else:
1782 self._wizard._departureNOTAMs = None
1783 self._wizard._arrivalNOTAMs = None
1784
1785 bookedFlight = self._wizard._bookedFlight
1786 self._wizard.gui.beginBusy(xstr("route_down_metars"))
1787 self._wizard.gui.webHandler.getMETARs(self._metarsCallback,
1788 [bookedFlight.departureICAO,
1789 bookedFlight.arrivalICAO])
1790
1791 def _metarsCallback(self, returned, result):
1792 """Callback for the METARs."""
1793 gobject.idle_add(self._handleMETARs, returned, result)
1794
1795 def _handleMETARs(self, returned, result):
1796 """Handle the METARs."""
1797 self._wizard._departureMETAR = None
1798 self._wizard._arrivalMETAR = None
1799 bookedFlight = self._wizard._bookedFlight
1800 if returned:
1801 if bookedFlight.departureICAO in result.metars:
1802 self._wizard._departureMETAR = result.metars[bookedFlight.departureICAO]
1803 if bookedFlight.arrivalICAO in result.metars:
1804 self._wizard._arrivalMETAR = result.metars[bookedFlight.arrivalICAO]
1805
1806 self._wizard.gui.endBusy()
1807 self._backButton.set_sensitive(True)
1808 self._button.set_sensitive(True)
1809 self._wizard.nextPage()
1810
1811#-----------------------------------------------------------------------------
1812
1813class BriefingPage(Page):
1814 """Page for the briefing."""
1815 def __init__(self, wizard, departure):
1816 """Construct the briefing page."""
1817 self._departure = departure
1818
1819 title = xstr("briefing_title") % (1 if departure else 2,
1820 xstr("briefing_departure")
1821 if departure
1822 else xstr("briefing_arrival"))
1823 super(BriefingPage, self).__init__(wizard, title, xstr("briefing_help"),
1824 completedHelp = xstr("briefing_chelp"))
1825
1826 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1827 xscale = 1.0, yscale = 1.0)
1828
1829 mainBox = gtk.VBox()
1830 alignment.add(mainBox)
1831 self.setMainWidget(alignment)
1832
1833 self._notamsFrame = gtk.Frame()
1834 self._notamsFrame.set_label(xstr("briefing_notams_init"))
1835 scrolledWindow = gtk.ScrolledWindow()
1836 scrolledWindow.set_size_request(-1, 128)
1837 # FIXME: these constants should be in common
1838 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1839 else gtk.POLICY_AUTOMATIC,
1840 gtk.PolicyType.AUTOMATIC if pygobject
1841 else gtk.POLICY_AUTOMATIC)
1842 self._notams = gtk.TextView()
1843 self._notams.set_editable(False)
1844 self._notams.set_accepts_tab(False)
1845 self._notams.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
1846 scrolledWindow.add(self._notams)
1847 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
1848 xscale = 1.0, yscale = 1.0)
1849 alignment.set_padding(padding_top = 4, padding_bottom = 0,
1850 padding_left = 0, padding_right = 0)
1851 alignment.add(scrolledWindow)
1852 self._notamsFrame.add(alignment)
1853 mainBox.pack_start(self._notamsFrame, True, True, 4)
1854
1855 self._metarFrame = gtk.Frame()
1856 self._metarFrame.set_label(xstr("briefing_metar_init"))
1857 scrolledWindow = gtk.ScrolledWindow()
1858 scrolledWindow.set_size_request(-1, 32)
1859 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1860 else gtk.POLICY_AUTOMATIC,
1861 gtk.PolicyType.AUTOMATIC if pygobject
1862 else gtk.POLICY_AUTOMATIC)
1863
1864 self._uppercasingMETAR = False
1865
1866 self._metar = gtk.TextView()
1867 self._metar.set_accepts_tab(False)
1868 self._metar.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
1869 self._metar.get_buffer().connect("changed", self._metarChanged)
1870 self._metar.get_buffer().connect_after("insert-text", self._metarInserted)
1871 scrolledWindow.add(self._metar)
1872 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
1873 xscale = 1.0, yscale = 1.0)
1874 alignment.set_padding(padding_top = 4, padding_bottom = 0,
1875 padding_left = 0, padding_right = 0)
1876 alignment.add(scrolledWindow)
1877 self._metarFrame.add(alignment)
1878 mainBox.pack_start(self._metarFrame, True, True, 4)
1879 self.metarEdited = False
1880
1881 self.addCancelFlightButton()
1882
1883 self.addPreviousButton(clicked = self._backClicked)
1884 self._button = self.addNextButton(clicked = self._forwardClicked)
1885
1886 @property
1887 def metar(self):
1888 """Get the METAR on the page."""
1889 buffer = self._metar.get_buffer()
1890 return buffer.get_text(buffer.get_start_iter(),
1891 buffer.get_end_iter(), True)
1892
1893 def setMETAR(self, metar):
1894 """Set the metar."""
1895 self._metar.get_buffer().set_text(metar)
1896 self.metarEdited = False
1897
1898 def activate(self):
1899 """Activate the page."""
1900 if not self._departure:
1901 self._button.set_label(xstr("briefing_button"))
1902 self._button.set_has_tooltip(False)
1903 self._button.set_use_stock(False)
1904
1905 bookedFlight = self._wizard._bookedFlight
1906
1907 icao = bookedFlight.departureICAO if self._departure \
1908 else bookedFlight.arrivalICAO
1909 notams = self._wizard._departureNOTAMs if self._departure \
1910 else self._wizard._arrivalNOTAMs
1911 metar = self._wizard._departureMETAR if self._departure \
1912 else self._wizard._arrivalMETAR
1913
1914 self._notamsFrame.set_label(xstr("briefing_notams_template") % (icao,))
1915 buffer = self._notams.get_buffer()
1916 if notams is None:
1917 buffer.set_text(xstr("briefing_notams_failed"))
1918 elif not notams:
1919 buffer.set_text(xstr("briefing_notams_missing"))
1920 else:
1921 s = ""
1922 for notam in notams:
1923 s += str(notam.begin)
1924 if notam.end is not None:
1925 s += " - " + str(notam.end)
1926 elif notam.permanent:
1927 s += " - PERMANENT"
1928 s += "\n"
1929 if notam.repeatCycle:
1930 s += "Repeat cycle: " + notam.repeatCycle + "\n"
1931 s += notam.notice + "\n"
1932 s += "-------------------- * --------------------\n"
1933 buffer.set_text(s)
1934
1935 self._metarFrame.set_label(xstr("briefing_metar_template") % (icao,))
1936 buffer = self._metar.get_buffer()
1937 if metar is None:
1938 buffer.set_text(xstr("briefing_metar_failed"))
1939 else:
1940 buffer.set_text(metar)
1941
1942 label = self._metarFrame.get_label_widget()
1943 label.set_use_underline(True)
1944 label.set_mnemonic_widget(self._metar)
1945
1946 self.metarEdited = False
1947
1948 def _backClicked(self, button):
1949 """Called when the Back button is pressed."""
1950 self.goBack()
1951
1952 def _forwardClicked(self, button):
1953 """Called when the forward button is clicked."""
1954 if not self._departure:
1955 if not self._completed:
1956 self._wizard.gui.startMonitoring()
1957 self._button.set_label(xstr("button_next"))
1958 self._button.set_tooltip_text(xstr("button_next_tooltip"))
1959 self.complete()
1960
1961 self._wizard.nextPage()
1962
1963 def _metarChanged(self, buffer):
1964 """Called when the METAR has changed."""
1965 if not self._uppercasingMETAR:
1966 self.metarEdited = True
1967 self._button.set_sensitive(buffer.get_text(buffer.get_start_iter(),
1968 buffer.get_end_iter(),
1969 True)!="")
1970
1971 def _metarInserted(self, textBuffer, iter, text, length):
1972 """Called when new characters are inserted into the METAR.
1973
1974 It uppercases all characters."""
1975 if not self._uppercasingMETAR:
1976 self._uppercasingMETAR = True
1977
1978 iter1 = iter.copy()
1979 iter1.backward_chars(length)
1980 textBuffer.delete(iter, iter1)
1981
1982 textBuffer.insert(iter, text.upper())
1983
1984 self._uppercasingMETAR = False
1985
1986#-----------------------------------------------------------------------------
1987
1988class TakeoffPage(Page):
1989 """Page for entering the takeoff data."""
1990 def __init__(self, wizard):
1991 """Construct the takeoff page."""
1992 super(TakeoffPage, self).__init__(wizard, xstr("takeoff_title"),
1993 xstr("takeoff_help"),
1994 completedHelp = xstr("takeoff_chelp"))
1995
1996 self._forwardAllowed = False
1997
1998 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1999 xscale = 0.0, yscale = 0.0)
2000
2001 table = gtk.Table(8, 4)
2002 table.set_row_spacings(4)
2003 table.set_col_spacings(16)
2004 table.set_homogeneous(False)
2005 alignment.add(table)
2006 self.setMainWidget(alignment)
2007
2008 label = gtk.Label(xstr("takeoff_runway"))
2009 label.set_use_underline(True)
2010 label.set_alignment(0.0, 0.5)
2011 table.attach(label, 0, 1, 0, 1)
2012
2013 self._runway = gtk.Entry()
2014 self._runway.set_width_chars(10)
2015 self._runway.set_tooltip_text(xstr("takeoff_runway_tooltip"))
2016 self._runway.connect("changed", self._upperChanged)
2017 table.attach(self._runway, 1, 3, 0, 1)
2018 label.set_mnemonic_widget(self._runway)
2019
2020 label = gtk.Label(xstr("takeoff_sid"))
2021 label.set_use_underline(True)
2022 label.set_alignment(0.0, 0.5)
2023 table.attach(label, 0, 1, 1, 2)
2024
2025 self._sid = gtk.Entry()
2026 self._sid.set_width_chars(10)
2027 self._sid.set_tooltip_text(xstr("takeoff_sid_tooltip"))
2028 self._sid.connect("changed", self._upperChanged)
2029 table.attach(self._sid, 1, 3, 1, 2)
2030 label.set_mnemonic_widget(self._sid)
2031
2032 label = gtk.Label(xstr("takeoff_v1"))
2033 label.set_use_markup(True)
2034 label.set_use_underline(True)
2035 label.set_alignment(0.0, 0.5)
2036 table.attach(label, 0, 1, 2, 3)
2037
2038 self._v1 = IntegerEntry()
2039 self._v1.set_width_chars(4)
2040 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip_knots"))
2041 self._v1.connect("integer-changed", self._valueChanged)
2042 table.attach(self._v1, 2, 3, 2, 3)
2043 label.set_mnemonic_widget(self._v1)
2044
2045 self._v1Unit = gtk.Label(xstr("label_knots"))
2046 self._v1Unit.set_alignment(0.0, 0.5)
2047 table.attach(self._v1Unit, 3, 4, 2, 3)
2048
2049 label = gtk.Label(xstr("takeoff_vr"))
2050 label.set_use_markup(True)
2051 label.set_use_underline(True)
2052 label.set_alignment(0.0, 0.5)
2053 table.attach(label, 0, 1, 3, 4)
2054
2055 self._vr = IntegerEntry()
2056 self._vr.set_width_chars(4)
2057 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip_knots"))
2058 self._vr.connect("integer-changed", self._valueChanged)
2059 table.attach(self._vr, 2, 3, 3, 4)
2060 label.set_mnemonic_widget(self._vr)
2061
2062 self._vrUnit = gtk.Label(xstr("label_knots"))
2063 self._vrUnit.set_alignment(0.0, 0.5)
2064 table.attach(self._vrUnit, 3, 4, 3, 4)
2065
2066 label = gtk.Label(xstr("takeoff_v2"))
2067 label.set_use_markup(True)
2068 label.set_use_underline(True)
2069 label.set_alignment(0.0, 0.5)
2070 table.attach(label, 0, 1, 4, 5)
2071
2072 self._v2 = IntegerEntry()
2073 self._v2.set_width_chars(4)
2074 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip_knots"))
2075 self._v2.connect("integer-changed", self._valueChanged)
2076 table.attach(self._v2, 2, 3, 4, 5)
2077 label.set_mnemonic_widget(self._v2)
2078
2079 self._v2Unit = gtk.Label(xstr("label_knots"))
2080 self._v2Unit.set_alignment(0.0, 0.5)
2081 table.attach(self._v2Unit, 3, 4, 4, 5)
2082
2083 self._hasDerate = False
2084
2085 self._derateLabel = gtk.Label()
2086 self._derateLabel.set_use_underline(True)
2087 self._derateLabel.set_markup(xstr("takeoff_derate_tupolev"))
2088 self._derateLabel.set_alignment(0.0, 0.5)
2089 table.attach(self._derateLabel, 0, 1, 5, 6)
2090
2091 self._derate = gtk.Entry()
2092 self._derate.set_width_chars(10)
2093 self._derate.set_tooltip_text(xstr("takeoff_derate_tooltip"))
2094 self._derate.set_alignment(1.0)
2095 self._derate.connect("changed", self._derateChanged)
2096 table.attach(self._derate, 1, 3, 5, 6)
2097 self._derateLabel.set_mnemonic_widget(self._derate)
2098
2099 self._derateUnit = gtk.Label("")
2100 self._derateUnit.set_use_markup(True)
2101 self._derateUnit.set_alignment(0.0, 0.5)
2102 table.attach(self._derateUnit, 3, 4, 5, 6)
2103
2104 self._antiIceOn = gtk.CheckButton(xstr("takeoff_antiice"))
2105 self._antiIceOn.set_use_underline(True)
2106 self._antiIceOn.set_tooltip_text(xstr("takeoff_antiice_tooltip"))
2107 table.attach(self._antiIceOn, 2, 4, 6, 7)
2108
2109 self._rto = gtk.CheckButton(xstr("takeoff_rto"))
2110 self._rto.set_use_underline(True)
2111 self._rto.set_tooltip_text(xstr("takeoff_rto_tooltip"))
2112 self._rto.connect("toggled", self._rtoToggled)
2113 table.attach(self._rto, 2, 4, 7, 8, ypadding = 8)
2114
2115 self.addCancelFlightButton()
2116
2117 self.addPreviousButton(clicked = self._backClicked)
2118
2119 self._button = self.addNextButton(clicked = self._forwardClicked)
2120
2121 @property
2122 def runway(self):
2123 """Get the runway."""
2124 return self._runway.get_text()
2125
2126 @property
2127 def sid(self):
2128 """Get the SID."""
2129 return self._sid.get_text()
2130
2131 @property
2132 def v1(self):
2133 """Get the v1 speed."""
2134 return self._v1.get_int()
2135
2136 @property
2137 def vr(self):
2138 """Get the vr speed."""
2139 return self._vr.get_int()
2140
2141 @property
2142 def v2(self):
2143 """Get the v2 speed."""
2144 return self._v2.get_int()
2145
2146 @property
2147 def derate(self):
2148 """Get the derate value, if any."""
2149 if self._hasDerate:
2150 derate = self._derate.get_text()
2151 return derate if derate else None
2152 else:
2153 return None
2154
2155 @property
2156 def antiIceOn(self):
2157 """Get whether the anti-ice system has been turned on."""
2158 return self._antiIceOn.get_active()
2159
2160 @antiIceOn.setter
2161 def antiIceOn(self, value):
2162 """Set the anti-ice indicator."""
2163 self._antiIceOn.set_active(value)
2164
2165 @property
2166 def rtoIndicated(self):
2167 """Get whether the pilot has indicated if there was an RTO."""
2168 return self._rto.get_active()
2169
2170 def activate(self):
2171 """Activate the page."""
2172 self._runway.set_text("")
2173 self._runway.set_sensitive(True)
2174 self._sid.set_text("")
2175 self._sid.set_sensitive(True)
2176 self._v1.set_int(None)
2177 self._v1.set_sensitive(True)
2178 self._vr.set_int(None)
2179 self._vr.set_sensitive(True)
2180 self._v2.set_int(None)
2181 self._v2.set_sensitive(True)
2182
2183 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
2184 speedUnit = xstr("label" + i18nSpeedUnit)
2185 self._v1Unit.set_text(speedUnit)
2186 self._vrUnit.set_text(speedUnit)
2187 self._v2Unit.set_text(speedUnit)
2188
2189 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip" + i18nSpeedUnit))
2190 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip" + i18nSpeedUnit))
2191 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip" + i18nSpeedUnit))
2192
2193 (derateLabel, derateUnit) = \
2194 self._wizard.gui.flight.aircraft.derateLabels
2195
2196 self._hasDerate = derateLabel is not None
2197
2198 if self._hasDerate:
2199 self._derateLabel.set_markup(derateLabel)
2200 self._derateLabel.set_use_underline(True)
2201 self._derateUnit.set_markup("" if derateUnit is None
2202 else derateUnit)
2203 else:
2204 self._derateLabel.set_markup(xstr("takeoff_derate"))
2205 self._derateUnit.set_text("")
2206
2207 self._derate.set_text("")
2208
2209 self._derateLabel.set_sensitive(self._hasDerate)
2210 self._derate.set_sensitive(self._hasDerate)
2211 self._derateUnit.set_sensitive(self._hasDerate)
2212
2213 self._rto.set_active(False)
2214 self._rto.set_sensitive(False)
2215
2216 self._button.set_sensitive(False)
2217 self._forwardAllowed = False
2218
2219 def allowForward(self):
2220 """Allow going to the next page."""
2221 self._forwardAllowed = True
2222 self._updateForwardButton()
2223
2224 def reset(self):
2225 """Reset the page if the wizard is reset."""
2226 super(TakeoffPage, self).reset()
2227 self._v1.reset()
2228 self._vr.reset()
2229 self._v2.reset()
2230 self._hasDerate = False
2231 self._antiIceOn.set_active(False)
2232
2233 def setRTOEnabled(self, enabled):
2234 """Set the RTO checkbox enabled or disabled."""
2235 if not enabled:
2236 self._rto.set_active(False)
2237 self._rto.set_sensitive(enabled)
2238
2239 def _updateForwardButton(self):
2240 """Update the sensitivity of the forward button based on some conditions."""
2241 sensitive = self._forwardAllowed and \
2242 self._runway.get_text()!="" and \
2243 self._sid.get_text()!="" and \
2244 self.v1 is not None and \
2245 self.vr is not None and \
2246 self.v2 is not None and \
2247 self.v1 <= self.vr and \
2248 self.vr <= self.v2 and \
2249 (not self._hasDerate or self._derate.get_text()!="")
2250 self._button.set_sensitive(sensitive)
2251
2252 def _valueChanged(self, widget, arg = None):
2253 """Called when the value of some widget has changed."""
2254 self._updateForwardButton()
2255
2256 def _upperChanged(self, entry, arg = None):
2257 """Called when the value of some entry widget has changed and the value
2258 should be converted to uppercase."""
2259 entry.set_text(entry.get_text().upper())
2260 self._valueChanged(entry, arg)
2261
2262 def _derateChanged(self, entry):
2263 """Called when the value of the derate is changed."""
2264 self._updateForwardButton()
2265
2266 def _rtoToggled(self, button):
2267 """Called when the RTO check button is toggled."""
2268 self._wizard.rtoToggled(button.get_active())
2269
2270 def _backClicked(self, button):
2271 """Called when the Back button is pressed."""
2272 self.goBack()
2273
2274 def _forwardClicked(self, button):
2275 """Called when the forward button is clicked."""
2276 aircraft = self._wizard.gui.flight.aircraft
2277 aircraft.updateV1R2()
2278 if self._hasDerate:
2279 aircraft.updateDerate()
2280 aircraft.updateTakeoffAntiIce()
2281 self._wizard.nextPage()
2282
2283#-----------------------------------------------------------------------------
2284
2285class CruisePage(Page):
2286 """The page containing the flight level that might change during flight."""
2287 def __init__(self, wizard):
2288 """Construct the page."""
2289 super(CruisePage, self).__init__(wizard, xstr("cruise_title"),
2290 xstr("cruise_help"))
2291
2292 self._loggable = False
2293 self._loggedCruiseLevel = 240
2294 self._activated = False
2295
2296 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
2297 xscale = 0.0, yscale = 1.0)
2298
2299 mainBox = gtk.VBox()
2300 alignment.add(mainBox)
2301 self.setMainWidget(alignment)
2302
2303 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
2304 xscale = 0.0, yscale = 0.0)
2305 mainBox.pack_start(alignment, False, False, 16)
2306
2307 levelBox = gtk.HBox()
2308
2309 label = gtk.Label(xstr("route_level"))
2310 label.set_use_underline(True)
2311 levelBox.pack_start(label, True, True, 0)
2312
2313 self._cruiseLevel = gtk.SpinButton()
2314 self._cruiseLevel.set_increments(step = 10, page = 100)
2315 self._cruiseLevel.set_range(min = 50, max = 500)
2316 self._cruiseLevel.set_tooltip_text(xstr("cruise_route_level_tooltip"))
2317 self._cruiseLevel.set_numeric(True)
2318 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
2319 label.set_mnemonic_widget(self._cruiseLevel)
2320
2321 levelBox.pack_start(self._cruiseLevel, False, False, 8)
2322
2323 self._updateButton = gtk.Button(xstr("cruise_route_level_update"));
2324 self._updateButton.set_use_underline(True)
2325 self._updateButton.set_tooltip_text(xstr("cruise_route_level_update_tooltip"))
2326 self._updateButton.connect("clicked", self._updateButtonClicked)
2327
2328 levelBox.pack_start(self._updateButton, False, False, 16)
2329
2330 mainBox.pack_start(levelBox, False, False, 0)
2331
2332 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
2333 xscale = 0.0, yscale = 1.0)
2334 mainBox.pack_start(alignment, True, True, 0)
2335
2336 self.addCancelFlightButton()
2337
2338 self._backButton = self.addPreviousButton(clicked = self._backClicked)
2339 self._button = self.addNextButton(clicked = self._forwardClicked)
2340
2341 @property
2342 def activated(self):
2343 """Determine if the page is already activated or not."""
2344 return self._activated
2345
2346 @property
2347 def cruiseLevel(self):
2348 """Get the cruise level."""
2349 return self._loggedCruiseLevel
2350
2351 @property
2352 def loggableCruiseLevel(self):
2353 """Get the cruise level which should be logged."""
2354 return self._cruiseLevel.get_value_as_int()
2355
2356 def setLoggable(self, loggable):
2357 """Set whether the cruise altitude can be logged."""
2358 self._loggable = loggable
2359 self._updateButtons()
2360
2361 def activate(self):
2362 """Setup the route from the booked flight."""
2363 self._loggedCruiseLevel = self._wizard.filedCruiseLevel
2364 self._cruiseLevel.set_value(self._loggedCruiseLevel)
2365 self._activated = True
2366
2367 def reset(self):
2368 """Reset the page."""
2369 self._loggable = False
2370 self._activated = False
2371 super(CruisePage, self).reset()
2372
2373 def _updateButtons(self):
2374 """Update the sensitivity of the buttons."""
2375 self._updateButton.set_sensitive(self._loggable and
2376 self.loggableCruiseLevel!=
2377 self._loggedCruiseLevel)
2378
2379 def _cruiseLevelChanged(self, spinButton):
2380 """Called when the cruise level has changed."""
2381 self._updateButtons()
2382
2383 def _updateButtonClicked(self, button):
2384 """Called when the update button is clicked."""
2385 if self._wizard.cruiseLevelChanged():
2386 self._loggedCruiseLevel = self.loggableCruiseLevel
2387 self._updateButtons()
2388
2389 def _backClicked(self, button):
2390 """Called when the Back button is pressed."""
2391 self.goBack()
2392
2393 def _forwardClicked(self, button):
2394 """Called when the Forward button is clicked."""
2395 self._wizard.nextPage()
2396
2397#-----------------------------------------------------------------------------
2398
2399class LandingPage(Page):
2400 """Page for entering landing data."""
2401 def __init__(self, wizard):
2402 """Construct the landing page."""
2403 super(LandingPage, self).__init__(wizard, xstr("landing_title"),
2404 xstr("landing_help"),
2405 completedHelp = xstr("landing_chelp"))
2406
2407 self._flightEnded = False
2408
2409 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2410 xscale = 0.0, yscale = 0.0)
2411
2412 table = gtk.Table(6, 5)
2413 table.set_row_spacings(4)
2414 table.set_col_spacings(16)
2415 table.set_homogeneous(False)
2416 alignment.add(table)
2417 self.setMainWidget(alignment)
2418
2419 self._starButton = gtk.CheckButton()
2420 self._starButton.connect("clicked", self._starButtonClicked)
2421 table.attach(self._starButton, 0, 1, 0, 1)
2422
2423 label = gtk.Label(xstr("landing_star"))
2424 label.set_use_underline(True)
2425 label.set_alignment(0.0, 0.5)
2426 table.attach(label, 1, 2, 0, 1)
2427
2428 self._star = gtk.Entry()
2429 self._star.set_width_chars(10)
2430 self._star.set_tooltip_text(xstr("landing_star_tooltip"))
2431 self._star.connect("changed", self._upperChanged)
2432 self._star.set_sensitive(False)
2433 table.attach(self._star, 2, 4, 0, 1)
2434 label.set_mnemonic_widget(self._starButton)
2435
2436 self._transitionButton = gtk.CheckButton()
2437 self._transitionButton.connect("clicked", self._transitionButtonClicked)
2438 table.attach(self._transitionButton, 0, 1, 1, 2)
2439
2440 label = gtk.Label(xstr("landing_transition"))
2441 label.set_use_underline(True)
2442 label.set_alignment(0.0, 0.5)
2443 table.attach(label, 1, 2, 1, 2)
2444
2445 self._transition = gtk.Entry()
2446 self._transition.set_width_chars(10)
2447 self._transition.set_tooltip_text(xstr("landing_transition_tooltip"))
2448 self._transition.connect("changed", self._upperChanged)
2449 self._transition.set_sensitive(False)
2450 table.attach(self._transition, 2, 4, 1, 2)
2451 label.set_mnemonic_widget(self._transitionButton)
2452
2453 label = gtk.Label(xstr("landing_runway"))
2454 label.set_use_underline(True)
2455 label.set_alignment(0.0, 0.5)
2456 table.attach(label, 1, 2, 2, 3)
2457
2458 self._runway = gtk.Entry()
2459 self._runway.set_width_chars(10)
2460 self._runway.set_tooltip_text(xstr("landing_runway_tooltip"))
2461 self._runway.connect("changed", self._upperChanged)
2462 table.attach(self._runway, 2, 4, 2, 3)
2463 label.set_mnemonic_widget(self._runway)
2464
2465 label = gtk.Label(xstr("landing_approach"))
2466 label.set_use_underline(True)
2467 label.set_alignment(0.0, 0.5)
2468 table.attach(label, 1, 2, 3, 4)
2469
2470 self._approachType = gtk.Entry()
2471 self._approachType.set_width_chars(10)
2472 self._approachType.set_tooltip_text(xstr("landing_approach_tooltip"))
2473 self._approachType.connect("changed", self._upperChanged)
2474 table.attach(self._approachType, 2, 4, 3, 4)
2475 label.set_mnemonic_widget(self._approachType)
2476
2477 label = gtk.Label(xstr("landing_vref"))
2478 label.set_use_markup(True)
2479 label.set_use_underline(True)
2480 label.set_alignment(0.0, 0.5)
2481 table.attach(label, 1, 2, 4, 5)
2482
2483 self._vref = IntegerEntry()
2484 self._vref.set_width_chars(5)
2485 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip_knots"))
2486 self._vref.connect("integer-changed", self._vrefChanged)
2487 table.attach(self._vref, 3, 4, 4, 5)
2488 label.set_mnemonic_widget(self._vref)
2489
2490 self._vrefUnit = gtk.Label(xstr("label_knots"))
2491 table.attach(self._vrefUnit, 4, 5, 4, 5)
2492
2493 self._antiIceOn = gtk.CheckButton(xstr("landing_antiice"))
2494 self._antiIceOn.set_use_underline(True)
2495 self._antiIceOn.set_tooltip_text(xstr("landing_antiice_tooltip"))
2496 table.attach(self._antiIceOn, 3, 5, 5, 6)
2497
2498 self.addCancelFlightButton()
2499
2500 self.addPreviousButton(clicked = self._backClicked)
2501
2502 self._button = self.addNextButton(clicked = self._forwardClicked)
2503
2504 # These are needed for correct size calculations
2505 self._starButton.set_active(True)
2506 self._transitionButton.set_active(True)
2507
2508 @property
2509 def star(self):
2510 """Get the STAR or None if none entered."""
2511 return self._star.get_text() if self._starButton.get_active() else None
2512
2513 @property
2514 def transition(self):
2515 """Get the transition or None if none entered."""
2516 return self._transition.get_text() \
2517 if self._transitionButton.get_active() else None
2518
2519 @property
2520 def approachType(self):
2521 """Get the approach type."""
2522 return self._approachType.get_text()
2523
2524 @property
2525 def runway(self):
2526 """Get the runway."""
2527 return self._runway.get_text()
2528
2529 @property
2530 def vref(self):
2531 """Return the landing reference speed."""
2532 return self._vref.get_int()
2533
2534 @property
2535 def antiIceOn(self):
2536 """Get whether the anti-ice system has been turned on."""
2537 return self._antiIceOn.get_active()
2538
2539 @antiIceOn.setter
2540 def antiIceOn(self, value):
2541 """Set the anti-ice indicator."""
2542 self._antiIceOn.set_active(value)
2543
2544 def reset(self):
2545 """Reset the page if the wizard is reset."""
2546 super(LandingPage, self).reset()
2547 self._vref.reset()
2548 self._antiIceOn.set_active(False)
2549 self._flightEnded = False
2550
2551 def activate(self):
2552 """Called when the page is activated."""
2553 self._starButton.set_sensitive(True)
2554 self._starButton.set_active(False)
2555 self._star.set_text("")
2556
2557 self._transitionButton.set_sensitive(True)
2558 self._transitionButton.set_active(False)
2559 self._transition.set_text("")
2560
2561 self._runway.set_text("")
2562 self._runway.set_sensitive(True)
2563
2564 self._approachType.set_text("")
2565 self._approachType.set_sensitive(True)
2566
2567 self._vref.set_int(None)
2568 self._vref.set_sensitive(True)
2569
2570 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
2571 speedUnit = xstr("label" + i18nSpeedUnit)
2572 self._vrefUnit.set_text(speedUnit)
2573
2574 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip" +
2575 i18nSpeedUnit))
2576
2577 self._updateForwardButton()
2578
2579 def flightEnded(self):
2580 """Called when the flight has ended."""
2581 super(LandingPage, self).flightEnded()
2582 self._flightEnded = True
2583 self._updateForwardButton()
2584
2585 def _starButtonClicked(self, button):
2586 """Called when the STAR button is clicked."""
2587 active = button.get_active()
2588 self._star.set_sensitive(active)
2589 if active:
2590 self._star.grab_focus()
2591 self._updateForwardButton()
2592
2593 def _transitionButtonClicked(self, button):
2594 """Called when the Transition button is clicked."""
2595 active = button.get_active()
2596 self._transition.set_sensitive(active)
2597 if active:
2598 self._transition.grab_focus()
2599 self._updateForwardButton()
2600
2601 def _updateForwardButton(self):
2602 """Update the sensitivity of the forward button."""
2603 sensitive = self._flightEnded and \
2604 (self._starButton.get_active() or \
2605 self._transitionButton.get_active()) and \
2606 (self._star.get_text()!="" or
2607 not self._starButton.get_active()) and \
2608 (self._transition.get_text()!="" or
2609 not self._transitionButton.get_active()) and \
2610 self._runway.get_text()!="" and \
2611 self._approachType.get_text()!="" and \
2612 self.vref is not None
2613 self._button.set_sensitive(sensitive)
2614
2615 def _upperChanged(self, entry):
2616 """Called for entry widgets that must be converted to uppercase."""
2617 entry.set_text(entry.get_text().upper())
2618 self._updateForwardButton()
2619
2620 def _vrefChanged(self, widget, value):
2621 """Called when the Vref has changed."""
2622 self._updateForwardButton()
2623
2624 def _backClicked(self, button):
2625 """Called when the Back button is pressed."""
2626 self.goBack()
2627
2628 def _forwardClicked(self, button):
2629 """Called when the forward button is clicked."""
2630 aircraft = self._wizard.gui.flight.aircraft
2631 aircraft.updateVRef()
2632 aircraft.updateLandingAntiIce()
2633 if self._wizard.gui.config.onlineGateSystem and \
2634 self._wizard.loggedIn and not self._completed and \
2635 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
2636 not self._wizard.entranceExam:
2637 self._wizard.getFleet(callback = self._fleetRetrieved,
2638 force = True)
2639 else:
2640 self._wizard.nextPage()
2641
2642 def _fleetRetrieved(self, fleet):
2643 """Callback for the fleet retrieval."""
2644 self._wizard.nextPage()
2645
2646#-----------------------------------------------------------------------------
2647
2648class FinishPage(Page):
2649 """Flight finish page."""
2650 _flightTypes = [ ("flighttype_scheduled", const.FLIGHTTYPE_SCHEDULED),
2651 ("flighttype_ot", const.FLIGHTTYPE_OLDTIMER),
2652 ("flighttype_vip", const.FLIGHTTYPE_VIP),
2653 ("flighttype_charter", const.FLIGHTTYPE_CHARTER) ]
2654
2655 def __init__(self, wizard):
2656 """Construct the finish page."""
2657 super(FinishPage, self).__init__(wizard, xstr("finish_title"),
2658 xstr("finish_help"))
2659
2660 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2661 xscale = 0.0, yscale = 0.0)
2662
2663 table = gtk.Table(8, 2)
2664 table.set_row_spacings(4)
2665 table.set_col_spacings(16)
2666 table.set_homogeneous(False)
2667 alignment.add(table)
2668 self.setMainWidget(alignment)
2669
2670 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2671 label = gtk.Label(xstr("finish_rating"))
2672 labelAlignment.add(label)
2673 table.attach(labelAlignment, 0, 1, 0, 1)
2674
2675 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2676 self._flightRating = gtk.Label()
2677 self._flightRating.set_width_chars(8)
2678 self._flightRating.set_alignment(0.0, 0.5)
2679 self._flightRating.set_use_markup(True)
2680 labelAlignment.add(self._flightRating)
2681 table.attach(labelAlignment, 1, 2, 0, 1)
2682
2683 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2684 label = gtk.Label(xstr("finish_flight_time"))
2685 labelAlignment.add(label)
2686 table.attach(labelAlignment, 0, 1, 1, 2)
2687
2688 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2689 self._flightTime = gtk.Label()
2690 self._flightTime.set_width_chars(10)
2691 self._flightTime.set_alignment(0.0, 0.5)
2692 self._flightTime.set_use_markup(True)
2693 labelAlignment.add(self._flightTime)
2694 table.attach(labelAlignment, 1, 2, 1, 2)
2695
2696 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2697 label = gtk.Label(xstr("finish_block_time"))
2698 labelAlignment.add(label)
2699 table.attach(labelAlignment, 0, 1, 2, 3)
2700
2701 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2702 self._blockTime = gtk.Label()
2703 self._blockTime.set_width_chars(10)
2704 self._blockTime.set_alignment(0.0, 0.5)
2705 self._blockTime.set_use_markup(True)
2706 labelAlignment.add(self._blockTime)
2707 table.attach(labelAlignment, 1, 2, 2, 3)
2708
2709 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2710 label = gtk.Label(xstr("finish_distance"))
2711 labelAlignment.add(label)
2712 table.attach(labelAlignment, 0, 1, 3, 4)
2713
2714 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2715 self._distanceFlown = gtk.Label()
2716 self._distanceFlown.set_width_chars(10)
2717 self._distanceFlown.set_alignment(0.0, 0.5)
2718 self._distanceFlown.set_use_markup(True)
2719 labelAlignment.add(self._distanceFlown)
2720 table.attach(labelAlignment, 1, 2, 3, 4)
2721
2722 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2723 label = gtk.Label(xstr("finish_fuel"))
2724 labelAlignment.add(label)
2725 table.attach(labelAlignment, 0, 1, 4, 5)
2726
2727 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2728 self._fuelUsed = gtk.Label()
2729 self._fuelUsed.set_width_chars(10)
2730 self._fuelUsed.set_alignment(0.0, 0.5)
2731 self._fuelUsed.set_use_markup(True)
2732 labelAlignment.add(self._fuelUsed)
2733 table.attach(labelAlignment, 1, 2, 4, 5)
2734
2735 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
2736 yalign = 0.5, yscale = 0.0)
2737 label = gtk.Label(xstr("finish_type"))
2738 label.set_use_underline(True)
2739 labelAlignment.add(label)
2740 table.attach(labelAlignment, 0, 1, 5, 6)
2741
2742 flightTypeModel = gtk.ListStore(str, int)
2743 for (name, type) in FinishPage._flightTypes:
2744 flightTypeModel.append([xstr(name), type])
2745
2746 self._flightType = gtk.ComboBox(model = flightTypeModel)
2747 renderer = gtk.CellRendererText()
2748 self._flightType.pack_start(renderer, True)
2749 self._flightType.add_attribute(renderer, "text", 0)
2750 self._flightType.set_tooltip_text(xstr("finish_type_tooltip"))
2751 self._flightType.set_active(0)
2752 self._flightType.connect("changed", self._flightTypeChanged)
2753 flightTypeAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2754 flightTypeAlignment.add(self._flightType)
2755 table.attach(flightTypeAlignment, 1, 2, 5, 6)
2756 label.set_mnemonic_widget(self._flightType)
2757
2758 self._onlineFlight = gtk.CheckButton(xstr("finish_online"))
2759 self._onlineFlight.set_use_underline(True)
2760 self._onlineFlight.set_tooltip_text(xstr("finish_online_tooltip"))
2761 onlineFlightAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2762 onlineFlightAlignment.add(self._onlineFlight)
2763 table.attach(onlineFlightAlignment, 1, 2, 6, 7)
2764
2765 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
2766 yalign = 0.5, yscale = 0.0)
2767 self._gateLabel = gtk.Label(xstr("finish_gate"))
2768 self._gateLabel.set_use_underline(True)
2769 labelAlignment.add(self._gateLabel)
2770 table.attach(labelAlignment, 0, 1, 7, 8)
2771
2772 self._gatesModel = gtk.ListStore(str)
2773
2774 self._gate = gtk.ComboBox(model = self._gatesModel)
2775 renderer = gtk.CellRendererText()
2776 self._gate.pack_start(renderer, True)
2777 self._gate.add_attribute(renderer, "text", 0)
2778 self._gate.set_tooltip_text(xstr("finish_gate_tooltip"))
2779 self._gate.connect("changed", self._gateChanged)
2780 gateAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
2781 gateAlignment.add(self._gate)
2782 table.attach(gateAlignment, 1, 2, 7, 8)
2783 self._gateLabel.set_mnemonic_widget(self._gate)
2784
2785 self.addButton(xstr("finish_newFlight"),
2786 sensitive = True,
2787 clicked = self._newFlightClicked,
2788 tooltip = xstr("finish_newFlight_tooltip"),
2789 padding = 16)
2790
2791 self.addPreviousButton(clicked = self._backClicked)
2792
2793 self._saveButton = self.addButton(xstr("finish_save"),
2794 sensitive = False,
2795 clicked = self._saveClicked,
2796 tooltip = xstr("finish_save_tooltip"))
2797 self._savePIREPDialog = None
2798 self._lastSavePath = None
2799
2800 self._pirepSaved = False
2801 self._pirepSent = False
2802
2803 self._sendButton = self.addButton(xstr("sendPIREP"), default = True,
2804 sensitive = False,
2805 clicked = self._sendClicked,
2806 tooltip = xstr("sendPIREP_tooltip"))
2807
2808 @property
2809 def flightType(self):
2810 """Get the flight type."""
2811 index = self._flightType.get_active()
2812 return None if index<0 else self._flightType.get_model()[index][1]
2813
2814 @property
2815 def online(self):
2816 """Get whether the flight was an online flight or not."""
2817 return self._onlineFlight.get_active()
2818
2819 def activate(self):
2820 """Activate the page."""
2821 self._pirepSaved = False
2822 self._pirepSent = False
2823
2824 flight = self._wizard.gui._flight
2825 rating = flight.logger.getRating()
2826 if rating<0:
2827 self._flightRating.set_markup('<b><span foreground="red">NO GO</span></b>')
2828 else:
2829 self._flightRating.set_markup("<b>%.1f %%</b>" % (rating,))
2830
2831 flightLength = flight.flightTimeEnd - flight.flightTimeStart
2832 self._flightTime.set_markup("<b>%s</b>" % \
2833 (util.getTimeIntervalString(flightLength),))
2834
2835 blockLength = flight.blockTimeEnd - flight.blockTimeStart
2836 self._blockTime.set_markup("<b>%s</b>" % \
2837 (util.getTimeIntervalString(blockLength),))
2838
2839 self._distanceFlown.set_markup("<b>%.2f NM</b>" % \
2840 (flight.flownDistance,))
2841
2842 self._fuelUsed.set_markup("<b>%.0f kg</b>" % \
2843 (flight.startFuel - flight.endFuel,))
2844
2845 self._flightType.set_active(-1)
2846 self._onlineFlight.set_active(self._wizard.loggedIn)
2847
2848 self._gatesModel.clear()
2849 if self._wizard.gui.config.onlineGateSystem and \
2850 self._wizard.loggedIn and \
2851 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
2852 not self._wizard.entranceExam:
2853 occupiedGates = self._wizard._fleet.getOccupiedGateNumbers()
2854 for gateNumber in const.lhbpGateNumbers:
2855 if gateNumber not in occupiedGates:
2856 self._gatesModel.append([gateNumber])
2857 self._gateLabel.set_sensitive(True)
2858 self._gate.set_sensitive(True)
2859 self._gate.set_active(-1)
2860 else:
2861 self._gateLabel.set_sensitive(False)
2862 self._gate.set_sensitive(False)
2863
2864 def _backClicked(self, button):
2865 """Called when the Back button is pressed."""
2866 self.goBack()
2867
2868 def _updateButtons(self):
2869 """Update the sensitivity state of the buttons."""
2870 sensitive = self._flightType.get_active()>=0 and \
2871 (self._gatesModel.get_iter_first() is None or
2872 self._gate.get_active()>=0)
2873
2874 self._saveButton.set_sensitive(sensitive)
2875 self._sendButton.set_sensitive(sensitive and
2876 self._wizard.bookedFlight.id is not None)
2877
2878 def _flightTypeChanged(self, comboBox):
2879 """Called when the flight type has changed."""
2880 self._updateButtons()
2881
2882 def _gateChanged(self, comboBox):
2883 """Called when the arrival gate has changed."""
2884 self._updateButtons()
2885
2886 def _newFlightClicked(self, button):
2887 """Called when the new flight button is clicked."""
2888 gui = self._wizard.gui
2889 if not self._pirepSent and not self._pirepSaved:
2890 dialog = gtk.MessageDialog(parent = gui.mainWindow,
2891 type = MESSAGETYPE_QUESTION,
2892 message_format = xstr("finish_newFlight_question"))
2893
2894 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
2895 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
2896
2897 dialog.set_title(WINDOW_TITLE_BASE)
2898 result = dialog.run()
2899 dialog.hide()
2900 if result!=RESPONSETYPE_YES:
2901 return
2902
2903 gui.reset()
2904
2905 def _saveClicked(self, button):
2906 """Called when the Save PIREP button is clicked."""
2907 gui = self._wizard.gui
2908
2909 bookedFlight = gui.bookedFlight
2910 tm = time.gmtime()
2911
2912 pilotID = self._wizard.pilotID
2913 if pilotID: pilotID += " "
2914 fileName = "%s%s %02d%02d %s-%s.pirep" % \
2915 (pilotID, str(bookedFlight.departureTime.date()),
2916 tm.tm_hour, tm.tm_min,
2917 bookedFlight.departureICAO,
2918 bookedFlight.arrivalICAO)
2919
2920 dialog = self._getSaveDialog()
2921
2922 if self._lastSavePath is None:
2923 pirepDirectory = gui.config.pirepDirectory
2924 if pirepDirectory is not None:
2925 dialog.set_current_folder(pirepDirectory)
2926 else:
2927 dialog.set_current_folder(os.path.dirname(self._lastSavePath))
2928
2929 dialog.set_current_name(fileName)
2930 result = dialog.run()
2931 dialog.hide()
2932
2933 if result==RESPONSETYPE_OK:
2934 pirep = PIREP(gui.flight)
2935
2936 self._lastSavePath = text2unicode(dialog.get_filename())
2937
2938 if pirep.save(self._lastSavePath):
2939 type = MESSAGETYPE_INFO
2940 message = xstr("finish_save_done")
2941 secondary = None
2942 self._pirepSaved = True
2943 else:
2944 type = MESSAGETYPE_ERROR
2945 message = xstr("finish_save_failed")
2946 secondary = xstr("finish_save_failed_sec")
2947
2948 dialog = gtk.MessageDialog(parent = gui.mainWindow,
2949 type = type, message_format = message)
2950 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
2951 dialog.set_title(WINDOW_TITLE_BASE)
2952 if secondary is not None:
2953 dialog.format_secondary_markup(secondary)
2954
2955 dialog.run()
2956 dialog.hide()
2957
2958 def _getSaveDialog(self):
2959 """Get the PIREP saving dialog.
2960
2961 If it does not exist yet, create it."""
2962 if self._savePIREPDialog is None:
2963 gui = self._wizard.gui
2964 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
2965 xstr("finish_save_title"),
2966 action = FILE_CHOOSER_ACTION_SAVE,
2967 buttons = (gtk.STOCK_CANCEL,
2968 RESPONSETYPE_CANCEL,
2969 gtk.STOCK_OK, RESPONSETYPE_OK),
2970 parent = gui.mainWindow)
2971 dialog.set_modal(True)
2972 dialog.set_do_overwrite_confirmation(True)
2973
2974 filter = gtk.FileFilter()
2975 filter.set_name(xstr("file_filter_pireps"))
2976 filter.add_pattern("*.pirep")
2977 dialog.add_filter(filter)
2978
2979 filter = gtk.FileFilter()
2980 filter.set_name(xstr("file_filter_all"))
2981 filter.add_pattern("*.*")
2982 dialog.add_filter(filter)
2983
2984 self._savePIREPDialog = dialog
2985
2986 return self._savePIREPDialog
2987
2988
2989 def _sendClicked(self, button):
2990 """Called when the Send button is clicked."""
2991 pirep = PIREP(self._wizard.gui.flight)
2992 self._wizard.gui.sendPIREP(pirep,
2993 callback = self._handlePIREPSent)
2994
2995 def _handlePIREPSent(self, returned, result):
2996 """Callback for the PIREP sending result."""
2997 self._pirepSent = returned and result.success
2998 if self._wizard.gui.config.onlineGateSystem and \
2999 self._wizard.loggedIn and not self._wizard.entranceExam and \
3000 returned and result.success:
3001 bookedFlight = self._wizard.bookedFlight
3002 if bookedFlight.arrivalICAO=="LHBP":
3003 iter = self._gate.get_active_iter()
3004 gateNumber = None if iter is None \
3005 else self._gatesModel.get_value(iter, 0)
3006
3007 status = const.PLANE_PARKING if gateNumber is None \
3008 else const.PLANE_HOME
3009 else:
3010 gateNumber = None
3011 status = const.PLANE_AWAY
3012
3013 self._wizard.updatePlane(self._planeUpdated,
3014 bookedFlight.tailNumber,
3015 status, gateNumber = gateNumber)
3016
3017 def _planeUpdated(self, success):
3018 """Callback for the plane updating."""
3019 pass
3020
3021#-----------------------------------------------------------------------------
3022
3023class Wizard(gtk.VBox):
3024 """The flight wizard."""
3025 def __init__(self, gui):
3026 """Construct the wizard."""
3027 super(Wizard, self).__init__()
3028
3029 self.gui = gui
3030
3031 self._pages = []
3032 self._currentPage = None
3033
3034 self._loginPage = LoginPage(self)
3035 self._pages.append(self._loginPage)
3036 self._pages.append(FlightSelectionPage(self))
3037 self._pages.append(GateSelectionPage(self))
3038 self._pages.append(ConnectPage(self))
3039 self._payloadPage = PayloadPage(self)
3040 self._pages.append(self._payloadPage)
3041 self._payloadIndex = len(self._pages)
3042 self._pages.append(TimePage(self))
3043 self._pages.append(FuelPage(self))
3044 self._routePage = RoutePage(self)
3045 self._pages.append(self._routePage)
3046 self._departureBriefingPage = BriefingPage(self, True)
3047 self._pages.append(self._departureBriefingPage)
3048 self._arrivalBriefingPage = BriefingPage(self, False)
3049 self._pages.append(self._arrivalBriefingPage)
3050 self._arrivalBriefingIndex = len(self._pages)
3051 self._takeoffPage = TakeoffPage(self)
3052 self._pages.append(self._takeoffPage)
3053 self._cruisePage = CruisePage(self)
3054 self._pages.append(self._cruisePage)
3055 self._landingPage = LandingPage(self)
3056 self._pages.append(self._landingPage)
3057 self._finishPage = FinishPage(self)
3058 self._pages.append(self._finishPage)
3059
3060 maxWidth = 0
3061 maxHeight = 0
3062 for page in self._pages:
3063 page.show_all()
3064 pageSizeRequest = page.size_request()
3065 width = pageSizeRequest.width if pygobject else pageSizeRequest[0]
3066 height = pageSizeRequest.height if pygobject else pageSizeRequest[1]
3067 maxWidth = max(maxWidth, width)
3068 maxHeight = max(maxHeight, height)
3069 page.setStyle()
3070 maxWidth += 16
3071 maxHeight += 32
3072 self.set_size_request(maxWidth, maxHeight)
3073
3074 self._initialize()
3075
3076 @property
3077 def pilotID(self):
3078 """Get the pilot ID, if given."""
3079 return self._loginPage.pilotID
3080
3081 @property
3082 def entranceExam(self):
3083 """Get whether an entrance exam is about to be taken."""
3084 return self._loginPage.entranceExam
3085
3086 @property
3087 def loggedIn(self):
3088 """Indicate if there was a successful login."""
3089 return self._loginResult is not None
3090
3091 @property
3092 def loginResult(self):
3093 """Get the login result."""
3094 return self._loginResult
3095
3096 def setCurrentPage(self, index, finalize = False):
3097 """Set the current page to the one with the given index."""
3098 assert index < len(self._pages)
3099
3100 fromPage = self._currentPage
3101 if fromPage is not None:
3102 page = self._pages[fromPage]
3103 if finalize and not page._completed:
3104 page.complete()
3105 self.remove(page)
3106
3107 self._currentPage = index
3108 page = self._pages[index]
3109 self.add(page)
3110 if page._fromPage is None:
3111 page._fromPage = fromPage
3112 page.initialize()
3113 self.show_all()
3114 if fromPage is not None:
3115 self.grabDefault()
3116
3117 @property
3118 def bookedFlight(self):
3119 """Get the booked flight selected."""
3120 return self._bookedFlight
3121
3122 @property
3123 def numCrew(self):
3124 """Get the number of crew members."""
3125 return self._payloadPage.numCrew
3126
3127 @property
3128 def numPassengers(self):
3129 """Get the number of passengers."""
3130 return self._payloadPage.numPassengers
3131
3132 @property
3133 def bagWeight(self):
3134 """Get the baggage weight."""
3135 return self._payloadPage.bagWeight
3136
3137 @property
3138 def cargoWeight(self):
3139 """Get the cargo weight."""
3140 return self._payloadPage.cargoWeight
3141
3142 @property
3143 def mailWeight(self):
3144 """Get the mail weight."""
3145 return self._payloadPage.mailWeight
3146
3147 @property
3148 def zfw(self):
3149 """Get the calculated ZFW value."""
3150 return 0 if self._bookedFlight is None \
3151 else self._payloadPage.calculateZFW()
3152
3153 @property
3154 def filedCruiseLevel(self):
3155 """Get the filed cruise level."""
3156 return self._routePage.filedCruiseLevel
3157
3158 @property
3159 def filedCruiseAltitude(self):
3160 """Get the filed cruise altitude."""
3161 return self._routePage.filedCruiseLevel * 100
3162
3163 @property
3164 def cruiseAltitude(self):
3165 """Get the cruise altitude."""
3166 level = self._cruisePage.cruiseLevel if self._cruisePage.activated \
3167 else self._routePage.filedCruiseLevel
3168 return level * 100
3169
3170 @property
3171 def loggableCruiseAltitude(self):
3172 """Get the cruise altitude that can be logged."""
3173 if self._cruisePage.activated:
3174 return self._cruisePage.loggableCruiseLevel * 100
3175 else:
3176 return 0
3177
3178 @property
3179 def route(self):
3180 """Get the route."""
3181 return self._routePage.route
3182
3183 @property
3184 def departureMETAR(self):
3185 """Get the METAR of the departure airport."""
3186 return self._departureBriefingPage.metar
3187
3188 @property
3189 def arrivalMETAR(self):
3190 """Get the METAR of the arrival airport."""
3191 return self._arrivalBriefingPage.metar
3192
3193 @property
3194 def departureRunway(self):
3195 """Get the departure runway."""
3196 return self._takeoffPage.runway
3197
3198 @property
3199 def sid(self):
3200 """Get the SID."""
3201 return self._takeoffPage.sid
3202
3203 @property
3204 def v1(self):
3205 """Get the V1 speed."""
3206 return self._takeoffPage.v1
3207
3208 @property
3209 def vr(self):
3210 """Get the Vr speed."""
3211 return self._takeoffPage.vr
3212
3213 @property
3214 def v2(self):
3215 """Get the V2 speed."""
3216 return self._takeoffPage.v2
3217
3218 @property
3219 def derate(self):
3220 """Get the derate value."""
3221 return self._takeoffPage.derate
3222
3223 @property
3224 def takeoffAntiIceOn(self):
3225 """Get whether the anti-ice system was on during take-off."""
3226 return self._takeoffPage.antiIceOn
3227
3228 @takeoffAntiIceOn.setter
3229 def takeoffAntiIceOn(self, value):
3230 """Set anti-ice on indicator."""
3231 self._takeoffPage.antiIceOn = value
3232
3233 @property
3234 def rtoIndicated(self):
3235 """Get whether the pilot has indicated that an RTO has occured."""
3236 return self._takeoffPage.rtoIndicated
3237
3238 @property
3239 def arrivalRunway(self):
3240 """Get the arrival runway."""
3241 return self._landingPage.runway
3242
3243 @property
3244 def star(self):
3245 """Get the STAR."""
3246 return self._landingPage.star
3247
3248 @property
3249 def transition(self):
3250 """Get the transition."""
3251 return self._landingPage.transition
3252
3253 @property
3254 def approachType(self):
3255 """Get the approach type."""
3256 return self._landingPage.approachType
3257
3258 @property
3259 def vref(self):
3260 """Get the Vref speed."""
3261 return self._landingPage.vref
3262
3263 @property
3264 def landingAntiIceOn(self):
3265 """Get whether the anti-ice system was on during landing."""
3266 return self._landingPage.antiIceOn
3267
3268 @landingAntiIceOn.setter
3269 def landingAntiIceOn(self, value):
3270 """Set anti-ice on indicator."""
3271 self._landingPage.antiIceOn = value
3272
3273 @property
3274 def flightType(self):
3275 """Get the flight type."""
3276 return self._finishPage.flightType
3277
3278 @property
3279 def online(self):
3280 """Get whether the flight was online or not."""
3281 return self._finishPage.online
3282
3283 def nextPage(self, finalize = True):
3284 """Go to the next page."""
3285 self.jumpPage(1, finalize)
3286
3287 def jumpPage(self, count, finalize = True):
3288 """Go to the page which is 'count' pages after the current one."""
3289 self.setCurrentPage(self._currentPage + count, finalize = finalize)
3290
3291 def grabDefault(self):
3292 """Make the default button of the current page the default."""
3293 self._pages[self._currentPage].grabDefault()
3294
3295 def connected(self, fsType, descriptor):
3296 """Called when the connection could be made to the simulator."""
3297 self.nextPage()
3298
3299 def reset(self, loginResult):
3300 """Resets the wizard to go back to the login page."""
3301 self._initialize(keepLoginResult = loginResult is None,
3302 loginResult = loginResult)
3303
3304 def setStage(self, stage):
3305 """Set the flight stage to the given one."""
3306 if stage!=const.STAGE_END:
3307 self._cruisePage.setLoggable(Flight.canLogCruiseAltitude(stage))
3308
3309 if stage==const.STAGE_TAKEOFF:
3310 self._takeoffPage.allowForward()
3311 elif stage==const.STAGE_LANDING:
3312 if not self._arrivalBriefingPage.metarEdited:
3313 print "Downloading arrival METAR again"
3314 self.gui.webHandler.getMETARs(self._arrivalMETARCallback,
3315 [self._bookedFlight.arrivalICAO])
3316
3317 elif stage==const.STAGE_END:
3318 for page in self._pages:
3319 page.flightEnded()
3320
3321 def _initialize(self, keepLoginResult = False, loginResult = None):
3322 """Initialize the wizard."""
3323 if not keepLoginResult:
3324 self._loginResult = loginResult
3325
3326 self._loginCallback = None
3327
3328 self._fleet = None
3329 self._fleetCallback = None
3330
3331 self._bookedFlight = None
3332 self._departureGate = "-"
3333 self._fuelData = None
3334 self._departureNOTAMs = None
3335 self._departureMETAR = None
3336 self._arrivalNOTAMs = None
3337 self._arrivalMETAR = None
3338
3339 firstPage = 0 if self._loginResult is None else 1
3340 for page in self._pages[firstPage:]:
3341 page.reset()
3342
3343 self.setCurrentPage(firstPage)
3344
3345 def login(self, callback, pilotID, password, entranceExam):
3346 """Called when the login button was clicked."""
3347 self._loginCallback = callback
3348 if pilotID is None:
3349 loginResult = self._loginResult
3350 assert loginResult is not None and loginResult.loggedIn
3351 pilotID = loginResult.pilotID
3352 password = loginResult.password
3353 entranceExam = loginResult.entranceExam
3354 busyMessage = xstr("reload_busy")
3355 else:
3356 self._loginResult = None
3357 busyMessage = xstr("login_busy")
3358
3359 self.gui.beginBusy(busyMessage)
3360
3361 self.gui.webHandler.login(self._loginResultCallback,
3362 pilotID, password,
3363 entranceExam = entranceExam)
3364
3365 def reloadFlights(self, callback):
3366 """Reload the flights from the MAVA server."""
3367 self.login(callback, None, None, None)
3368
3369 def cruiseLevelChanged(self):
3370 """Called when the cruise level is changed."""
3371 return self.gui.cruiseLevelChanged()
3372
3373 def _loginResultCallback(self, returned, result):
3374 """The login result callback, called in the web handler's thread."""
3375 gobject.idle_add(self._handleLoginResult, returned, result)
3376
3377 def _handleLoginResult(self, returned, result):
3378 """Handle the login result."""
3379 self.gui.endBusy()
3380 isReload = self._loginResult is not None
3381 if returned:
3382 if result.loggedIn:
3383 self._loginResult = result
3384 else:
3385 if isReload:
3386 message = xstr("reload_failed")
3387 else:
3388 message = xstr("login_entranceExam_invalid"
3389 if self.entranceExam else
3390 xstr("login_invalid"))
3391 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
3392 type = MESSAGETYPE_ERROR,
3393 message_format = message)
3394 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
3395 dialog.set_title(WINDOW_TITLE_BASE)
3396 if isReload:
3397 secondary = xstr("reload_failed_sec")
3398 else:
3399 secondary = xstr("login_entranceExam_invalid_sec"
3400 if self.entranceExam else
3401 xstr("login_invalid_sec"))
3402 dialog.format_secondary_markup(secondary)
3403 dialog.run()
3404 dialog.hide()
3405 else:
3406 message = xstr("reload_failconn") if isReload \
3407 else xstr("login_failconn")
3408 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
3409 type = MESSAGETYPE_ERROR,
3410 message_format = message)
3411 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
3412 dialog.set_title(WINDOW_TITLE_BASE)
3413 secondary = xstr("reload_failconn_sec") if isReload \
3414 else xstr("login_failconn_sec")
3415 dialog.format_secondary_markup(secondary)
3416
3417 dialog.run()
3418 dialog.hide()
3419
3420 callback = self._loginCallback
3421 self._loginCallback = None
3422 callback(returned, result)
3423
3424 def getFleet(self, callback, force = False):
3425 """Get the fleet via the GUI and call the given callback."""
3426 self._fleetCallback = callback
3427 self.gui.getFleet(callback = self._fleetRetrieved, force = force)
3428
3429 def _fleetRetrieved(self, fleet):
3430 """Callback for the fleet retrieval."""
3431 self._fleet = fleet
3432 if self._fleetCallback is not None:
3433 self._fleetCallback(fleet)
3434 self._fleetCallback = None
3435
3436 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
3437 """Update the given plane's gate information."""
3438 self.gui.updatePlane(tailNumber, status, gateNumber = gateNumber,
3439 callback = callback)
3440
3441 def updateRTO(self):
3442 """Update the RTO state.
3443
3444 The RTO checkbox will be enabled if the flight has an RTO state and the
3445 comments field contains some text."""
3446 flight = self.gui.flight
3447 rtoEnabled = flight is not None and flight.hasRTO and \
3448 self.gui.hasComments
3449 self._takeoffPage.setRTOEnabled(rtoEnabled)
3450
3451 def rtoToggled(self, indicated):
3452 """Called when the RTO indication has changed."""
3453 self.gui.rtoToggled(indicated)
3454
3455 def _connectSimulator(self):
3456 """Connect to the simulator."""
3457 self.gui.connectSimulator(self._bookedFlight.aircraftType)
3458
3459 def _arrivalMETARCallback(self, returned, result):
3460 """Called when the METAR of the arrival airport is retrieved."""
3461 gobject.idle_add(self._handleArrivalMETAR, returned, result)
3462
3463 def _handleArrivalMETAR(self, returned, result):
3464 """Called when the METAR of the arrival airport is retrieved."""
3465 icao = self._bookedFlight.arrivalICAO
3466 if returned and icao in result.metars:
3467 metar = result.metars[icao]
3468 if metar!="":
3469 self._arrivalBriefingPage.setMETAR(metar)
3470
3471#-----------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.