source: src/mlx/gui/flight.py@ 388:61c304ff3d63

Last change on this file since 388:61c304ff3d63 was 384:97052bda0e22, checked in by István Váradi <ivaradi@…>, 12 years ago

Implemented support for entering derate values (#158)

File size: 127.7 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(7, 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._rto = gtk.CheckButton(xstr("takeoff_rto"))
2105 self._rto.set_use_underline(True)
2106 self._rto.set_tooltip_text(xstr("takeoff_rto_tooltip"))
2107 self._rto.connect("toggled", self._rtoToggled)
2108 table.attach(self._rto, 2, 4, 6, 7, ypadding = 8)
2109
2110 self.addCancelFlightButton()
2111
2112 self.addPreviousButton(clicked = self._backClicked)
2113
2114 self._button = self.addNextButton(clicked = self._forwardClicked)
2115
2116 @property
2117 def runway(self):
2118 """Get the runway."""
2119 return self._runway.get_text()
2120
2121 @property
2122 def sid(self):
2123 """Get the SID."""
2124 return self._sid.get_text()
2125
2126 @property
2127 def v1(self):
2128 """Get the v1 speed."""
2129 return self._v1.get_int()
2130
2131 @property
2132 def vr(self):
2133 """Get the vr speed."""
2134 return self._vr.get_int()
2135
2136 @property
2137 def v2(self):
2138 """Get the v2 speed."""
2139 return self._v2.get_int()
2140
2141 @property
2142 def derate(self):
2143 """Get the derate value, if any."""
2144 if self._hasDerate:
2145 derate = self._derate.get_text()
2146 return derate if derate else None
2147 else:
2148 return None
2149
2150 @property
2151 def rtoIndicated(self):
2152 """Get whether the pilot has indicated if there was an RTO."""
2153 return self._rto.get_active()
2154
2155 def activate(self):
2156 """Activate the page."""
2157 self._runway.set_text("")
2158 self._runway.set_sensitive(True)
2159 self._sid.set_text("")
2160 self._sid.set_sensitive(True)
2161 self._v1.set_int(None)
2162 self._v1.set_sensitive(True)
2163 self._vr.set_int(None)
2164 self._vr.set_sensitive(True)
2165 self._v2.set_int(None)
2166 self._v2.set_sensitive(True)
2167
2168 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
2169 speedUnit = xstr("label" + i18nSpeedUnit)
2170 self._v1Unit.set_text(speedUnit)
2171 self._vrUnit.set_text(speedUnit)
2172 self._v2Unit.set_text(speedUnit)
2173
2174 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip" + i18nSpeedUnit))
2175 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip" + i18nSpeedUnit))
2176 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip" + i18nSpeedUnit))
2177
2178 (derateLabel, derateUnit) = \
2179 self._wizard.gui.flight.aircraft.derateLabels
2180
2181 self._hasDerate = derateLabel is not None
2182
2183 if self._hasDerate:
2184 self._derateLabel.set_markup(derateLabel)
2185 self._derateLabel.set_use_underline(True)
2186 self._derateUnit.set_markup("" if derateUnit is None
2187 else derateUnit)
2188 else:
2189 self._derateLabel.set_markup(xstr("takeoff_derate"))
2190 self._derateUnit.set_text("")
2191
2192 self._derate.set_text("")
2193
2194 self._derateLabel.set_sensitive(self._hasDerate)
2195 self._derate.set_sensitive(self._hasDerate)
2196 self._derateUnit.set_sensitive(self._hasDerate)
2197
2198 self._rto.set_active(False)
2199 self._rto.set_sensitive(False)
2200
2201 self._button.set_sensitive(False)
2202 self._forwardAllowed = False
2203
2204 def allowForward(self):
2205 """Allow going to the next page."""
2206 self._forwardAllowed = True
2207 self._updateForwardButton()
2208
2209 def reset(self):
2210 """Reset the page if the wizard is reset."""
2211 super(TakeoffPage, self).reset()
2212 self._v1.reset()
2213 self._vr.reset()
2214 self._v2.reset()
2215
2216 self._hasDerate = False
2217
2218 def setRTOEnabled(self, enabled):
2219 """Set the RTO checkbox enabled or disabled."""
2220 if not enabled:
2221 self._rto.set_active(False)
2222 self._rto.set_sensitive(enabled)
2223
2224 def _updateForwardButton(self):
2225 """Update the sensitivity of the forward button based on some conditions."""
2226 sensitive = self._forwardAllowed and \
2227 self._runway.get_text()!="" and \
2228 self._sid.get_text()!="" and \
2229 self.v1 is not None and \
2230 self.vr is not None and \
2231 self.v2 is not None and \
2232 self.v1 <= self.vr and \
2233 self.vr <= self.v2 and \
2234 (not self._hasDerate or self._derate.get_text()!="")
2235 self._button.set_sensitive(sensitive)
2236
2237 def _valueChanged(self, widget, arg = None):
2238 """Called when the value of some widget has changed."""
2239 self._updateForwardButton()
2240
2241 def _upperChanged(self, entry, arg = None):
2242 """Called when the value of some entry widget has changed and the value
2243 should be converted to uppercase."""
2244 entry.set_text(entry.get_text().upper())
2245 self._valueChanged(entry, arg)
2246
2247 def _derateChanged(self, entry):
2248 """Called when the value of the derate is changed."""
2249 self._updateForwardButton()
2250
2251 def _rtoToggled(self, button):
2252 """Called when the RTO check button is toggled."""
2253 self._wizard.rtoToggled(button.get_active())
2254
2255 def _backClicked(self, button):
2256 """Called when the Back button is pressed."""
2257 self.goBack()
2258
2259 def _forwardClicked(self, button):
2260 """Called when the forward button is clicked."""
2261 self._wizard.gui.flight.aircraft.updateV1R2()
2262 if self._hasDerate:
2263 self._wizard.gui.flight.aircraft.updateDerate()
2264 self._wizard.nextPage()
2265
2266#-----------------------------------------------------------------------------
2267
2268class CruisePage(Page):
2269 """The page containing the flight level that might change during flight."""
2270 def __init__(self, wizard):
2271 """Construct the page."""
2272 super(CruisePage, self).__init__(wizard, xstr("cruise_title"),
2273 xstr("cruise_help"))
2274
2275 self._loggable = False
2276 self._loggedCruiseLevel = 240
2277 self._activated = False
2278
2279 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
2280 xscale = 0.0, yscale = 1.0)
2281
2282 mainBox = gtk.VBox()
2283 alignment.add(mainBox)
2284 self.setMainWidget(alignment)
2285
2286 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
2287 xscale = 0.0, yscale = 0.0)
2288 mainBox.pack_start(alignment, False, False, 16)
2289
2290 levelBox = gtk.HBox()
2291
2292 label = gtk.Label(xstr("route_level"))
2293 label.set_use_underline(True)
2294 levelBox.pack_start(label, True, True, 0)
2295
2296 self._cruiseLevel = gtk.SpinButton()
2297 self._cruiseLevel.set_increments(step = 10, page = 100)
2298 self._cruiseLevel.set_range(min = 50, max = 500)
2299 self._cruiseLevel.set_tooltip_text(xstr("cruise_route_level_tooltip"))
2300 self._cruiseLevel.set_numeric(True)
2301 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
2302 label.set_mnemonic_widget(self._cruiseLevel)
2303
2304 levelBox.pack_start(self._cruiseLevel, False, False, 8)
2305
2306 self._updateButton = gtk.Button(xstr("cruise_route_level_update"));
2307 self._updateButton.set_use_underline(True)
2308 self._updateButton.set_tooltip_text(xstr("cruise_route_level_update_tooltip"))
2309 self._updateButton.connect("clicked", self._updateButtonClicked)
2310
2311 levelBox.pack_start(self._updateButton, False, False, 16)
2312
2313 mainBox.pack_start(levelBox, False, False, 0)
2314
2315 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
2316 xscale = 0.0, yscale = 1.0)
2317 mainBox.pack_start(alignment, True, True, 0)
2318
2319 self.addCancelFlightButton()
2320
2321 self._backButton = self.addPreviousButton(clicked = self._backClicked)
2322 self._button = self.addNextButton(clicked = self._forwardClicked)
2323
2324 @property
2325 def activated(self):
2326 """Determine if the page is already activated or not."""
2327 return self._activated
2328
2329 @property
2330 def cruiseLevel(self):
2331 """Get the cruise level."""
2332 return self._loggedCruiseLevel
2333
2334 @property
2335 def loggableCruiseLevel(self):
2336 """Get the cruise level which should be logged."""
2337 return self._cruiseLevel.get_value_as_int()
2338
2339 def setLoggable(self, loggable):
2340 """Set whether the cruise altitude can be logged."""
2341 self._loggable = loggable
2342 self._updateButtons()
2343
2344 def activate(self):
2345 """Setup the route from the booked flight."""
2346 self._loggedCruiseLevel = self._wizard.filedCruiseLevel
2347 self._cruiseLevel.set_value(self._loggedCruiseLevel)
2348 self._activated = True
2349
2350 def reset(self):
2351 """Reset the page."""
2352 self._loggable = False
2353 self._activated = False
2354 super(CruisePage, self).reset()
2355
2356 def _updateButtons(self):
2357 """Update the sensitivity of the buttons."""
2358 self._updateButton.set_sensitive(self._loggable and
2359 self.loggableCruiseLevel!=
2360 self._loggedCruiseLevel)
2361
2362 def _cruiseLevelChanged(self, spinButton):
2363 """Called when the cruise level has changed."""
2364 self._updateButtons()
2365
2366 def _updateButtonClicked(self, button):
2367 """Called when the update button is clicked."""
2368 if self._wizard.cruiseLevelChanged():
2369 self._loggedCruiseLevel = self.loggableCruiseLevel
2370 self._updateButtons()
2371
2372 def _backClicked(self, button):
2373 """Called when the Back button is pressed."""
2374 self.goBack()
2375
2376 def _forwardClicked(self, button):
2377 """Called when the Forward button is clicked."""
2378 self._wizard.nextPage()
2379
2380#-----------------------------------------------------------------------------
2381
2382class LandingPage(Page):
2383 """Page for entering landing data."""
2384 def __init__(self, wizard):
2385 """Construct the landing page."""
2386 super(LandingPage, self).__init__(wizard, xstr("landing_title"),
2387 xstr("landing_help"),
2388 completedHelp = xstr("landing_chelp"))
2389
2390 self._flightEnded = False
2391
2392 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2393 xscale = 0.0, yscale = 0.0)
2394
2395 table = gtk.Table(5, 5)
2396 table.set_row_spacings(4)
2397 table.set_col_spacings(16)
2398 table.set_homogeneous(False)
2399 alignment.add(table)
2400 self.setMainWidget(alignment)
2401
2402 self._starButton = gtk.CheckButton()
2403 self._starButton.connect("clicked", self._starButtonClicked)
2404 table.attach(self._starButton, 0, 1, 0, 1)
2405
2406 label = gtk.Label(xstr("landing_star"))
2407 label.set_use_underline(True)
2408 label.set_alignment(0.0, 0.5)
2409 table.attach(label, 1, 2, 0, 1)
2410
2411 self._star = gtk.Entry()
2412 self._star.set_width_chars(10)
2413 self._star.set_tooltip_text(xstr("landing_star_tooltip"))
2414 self._star.connect("changed", self._upperChanged)
2415 self._star.set_sensitive(False)
2416 table.attach(self._star, 2, 4, 0, 1)
2417 label.set_mnemonic_widget(self._starButton)
2418
2419 self._transitionButton = gtk.CheckButton()
2420 self._transitionButton.connect("clicked", self._transitionButtonClicked)
2421 table.attach(self._transitionButton, 0, 1, 1, 2)
2422
2423 label = gtk.Label(xstr("landing_transition"))
2424 label.set_use_underline(True)
2425 label.set_alignment(0.0, 0.5)
2426 table.attach(label, 1, 2, 1, 2)
2427
2428 self._transition = gtk.Entry()
2429 self._transition.set_width_chars(10)
2430 self._transition.set_tooltip_text(xstr("landing_transition_tooltip"))
2431 self._transition.connect("changed", self._upperChanged)
2432 self._transition.set_sensitive(False)
2433 table.attach(self._transition, 2, 4, 1, 2)
2434 label.set_mnemonic_widget(self._transitionButton)
2435
2436 label = gtk.Label(xstr("landing_runway"))
2437 label.set_use_underline(True)
2438 label.set_alignment(0.0, 0.5)
2439 table.attach(label, 1, 2, 2, 3)
2440
2441 self._runway = gtk.Entry()
2442 self._runway.set_width_chars(10)
2443 self._runway.set_tooltip_text(xstr("landing_runway_tooltip"))
2444 self._runway.connect("changed", self._upperChanged)
2445 table.attach(self._runway, 2, 4, 2, 3)
2446 label.set_mnemonic_widget(self._runway)
2447
2448 label = gtk.Label(xstr("landing_approach"))
2449 label.set_use_underline(True)
2450 label.set_alignment(0.0, 0.5)
2451 table.attach(label, 1, 2, 3, 4)
2452
2453 self._approachType = gtk.Entry()
2454 self._approachType.set_width_chars(10)
2455 self._approachType.set_tooltip_text(xstr("landing_approach_tooltip"))
2456 self._approachType.connect("changed", self._upperChanged)
2457 table.attach(self._approachType, 2, 4, 3, 4)
2458 label.set_mnemonic_widget(self._approachType)
2459
2460 label = gtk.Label(xstr("landing_vref"))
2461 label.set_use_markup(True)
2462 label.set_use_underline(True)
2463 label.set_alignment(0.0, 0.5)
2464 table.attach(label, 1, 2, 5, 6)
2465
2466 self._vref = IntegerEntry()
2467 self._vref.set_width_chars(5)
2468 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip_knots"))
2469 self._vref.connect("integer-changed", self._vrefChanged)
2470 table.attach(self._vref, 3, 4, 5, 6)
2471 label.set_mnemonic_widget(self._vref)
2472
2473 self._vrefUnit = gtk.Label(xstr("label_knots"))
2474 table.attach(self._vrefUnit, 4, 5, 5, 6)
2475
2476 self.addCancelFlightButton()
2477
2478 self.addPreviousButton(clicked = self._backClicked)
2479
2480 self._button = self.addNextButton(clicked = self._forwardClicked)
2481
2482 # These are needed for correct size calculations
2483 self._starButton.set_active(True)
2484 self._transitionButton.set_active(True)
2485
2486 @property
2487 def star(self):
2488 """Get the STAR or None if none entered."""
2489 return self._star.get_text() if self._starButton.get_active() else None
2490
2491 @property
2492 def transition(self):
2493 """Get the transition or None if none entered."""
2494 return self._transition.get_text() \
2495 if self._transitionButton.get_active() else None
2496
2497 @property
2498 def approachType(self):
2499 """Get the approach type."""
2500 return self._approachType.get_text()
2501
2502 @property
2503 def runway(self):
2504 """Get the runway."""
2505 return self._runway.get_text()
2506
2507 @property
2508 def vref(self):
2509 """Return the landing reference speed."""
2510 return self._vref.get_int()
2511
2512 def reset(self):
2513 """Reset the page if the wizard is reset."""
2514 super(LandingPage, self).reset()
2515 self._vref.reset()
2516 self._flightEnded = False
2517
2518 def activate(self):
2519 """Called when the page is activated."""
2520 self._starButton.set_sensitive(True)
2521 self._starButton.set_active(False)
2522 self._star.set_text("")
2523
2524 self._transitionButton.set_sensitive(True)
2525 self._transitionButton.set_active(False)
2526 self._transition.set_text("")
2527
2528 self._runway.set_text("")
2529 self._runway.set_sensitive(True)
2530
2531 self._approachType.set_text("")
2532 self._approachType.set_sensitive(True)
2533
2534 self._vref.set_int(None)
2535 self._vref.set_sensitive(True)
2536
2537 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
2538 speedUnit = xstr("label" + i18nSpeedUnit)
2539 self._vrefUnit.set_text(speedUnit)
2540
2541 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip" +
2542 i18nSpeedUnit))
2543
2544 self._updateForwardButton()
2545
2546 def flightEnded(self):
2547 """Called when the flight has ended."""
2548 super(LandingPage, self).flightEnded()
2549 self._flightEnded = True
2550 self._updateForwardButton()
2551
2552 def _starButtonClicked(self, button):
2553 """Called when the STAR button is clicked."""
2554 active = button.get_active()
2555 self._star.set_sensitive(active)
2556 if active:
2557 self._star.grab_focus()
2558 self._updateForwardButton()
2559
2560 def _transitionButtonClicked(self, button):
2561 """Called when the Transition button is clicked."""
2562 active = button.get_active()
2563 self._transition.set_sensitive(active)
2564 if active:
2565 self._transition.grab_focus()
2566 self._updateForwardButton()
2567
2568 def _updateForwardButton(self):
2569 """Update the sensitivity of the forward button."""
2570 sensitive = self._flightEnded and \
2571 (self._starButton.get_active() or \
2572 self._transitionButton.get_active()) and \
2573 (self._star.get_text()!="" or
2574 not self._starButton.get_active()) and \
2575 (self._transition.get_text()!="" or
2576 not self._transitionButton.get_active()) and \
2577 self._runway.get_text()!="" and \
2578 self._approachType.get_text()!="" and \
2579 self.vref is not None
2580 self._button.set_sensitive(sensitive)
2581
2582 def _upperChanged(self, entry):
2583 """Called for entry widgets that must be converted to uppercase."""
2584 entry.set_text(entry.get_text().upper())
2585 self._updateForwardButton()
2586
2587 def _vrefChanged(self, widget, value):
2588 """Called when the Vref has changed."""
2589 self._updateForwardButton()
2590
2591 def _backClicked(self, button):
2592 """Called when the Back button is pressed."""
2593 self.goBack()
2594
2595 def _forwardClicked(self, button):
2596 """Called when the forward button is clicked."""
2597 self._wizard.gui.flight.aircraft.updateVRef()
2598 if self._wizard.gui.config.onlineGateSystem and \
2599 self._wizard.loggedIn and not self._completed and \
2600 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
2601 not self._wizard.entranceExam:
2602 self._wizard.getFleet(callback = self._fleetRetrieved,
2603 force = True)
2604 else:
2605 self._wizard.nextPage()
2606
2607 def _fleetRetrieved(self, fleet):
2608 """Callback for the fleet retrieval."""
2609 self._wizard.nextPage()
2610
2611#-----------------------------------------------------------------------------
2612
2613class FinishPage(Page):
2614 """Flight finish page."""
2615 _flightTypes = [ ("flighttype_scheduled", const.FLIGHTTYPE_SCHEDULED),
2616 ("flighttype_ot", const.FLIGHTTYPE_OLDTIMER),
2617 ("flighttype_vip", const.FLIGHTTYPE_VIP),
2618 ("flighttype_charter", const.FLIGHTTYPE_CHARTER) ]
2619
2620 def __init__(self, wizard):
2621 """Construct the finish page."""
2622 super(FinishPage, self).__init__(wizard, xstr("finish_title"),
2623 xstr("finish_help"))
2624
2625 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2626 xscale = 0.0, yscale = 0.0)
2627
2628 table = gtk.Table(8, 2)
2629 table.set_row_spacings(4)
2630 table.set_col_spacings(16)
2631 table.set_homogeneous(False)
2632 alignment.add(table)
2633 self.setMainWidget(alignment)
2634
2635 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2636 label = gtk.Label(xstr("finish_rating"))
2637 labelAlignment.add(label)
2638 table.attach(labelAlignment, 0, 1, 0, 1)
2639
2640 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2641 self._flightRating = gtk.Label()
2642 self._flightRating.set_width_chars(8)
2643 self._flightRating.set_alignment(0.0, 0.5)
2644 self._flightRating.set_use_markup(True)
2645 labelAlignment.add(self._flightRating)
2646 table.attach(labelAlignment, 1, 2, 0, 1)
2647
2648 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2649 label = gtk.Label(xstr("finish_flight_time"))
2650 labelAlignment.add(label)
2651 table.attach(labelAlignment, 0, 1, 1, 2)
2652
2653 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2654 self._flightTime = gtk.Label()
2655 self._flightTime.set_width_chars(10)
2656 self._flightTime.set_alignment(0.0, 0.5)
2657 self._flightTime.set_use_markup(True)
2658 labelAlignment.add(self._flightTime)
2659 table.attach(labelAlignment, 1, 2, 1, 2)
2660
2661 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2662 label = gtk.Label(xstr("finish_block_time"))
2663 labelAlignment.add(label)
2664 table.attach(labelAlignment, 0, 1, 2, 3)
2665
2666 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2667 self._blockTime = gtk.Label()
2668 self._blockTime.set_width_chars(10)
2669 self._blockTime.set_alignment(0.0, 0.5)
2670 self._blockTime.set_use_markup(True)
2671 labelAlignment.add(self._blockTime)
2672 table.attach(labelAlignment, 1, 2, 2, 3)
2673
2674 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2675 label = gtk.Label(xstr("finish_distance"))
2676 labelAlignment.add(label)
2677 table.attach(labelAlignment, 0, 1, 3, 4)
2678
2679 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2680 self._distanceFlown = gtk.Label()
2681 self._distanceFlown.set_width_chars(10)
2682 self._distanceFlown.set_alignment(0.0, 0.5)
2683 self._distanceFlown.set_use_markup(True)
2684 labelAlignment.add(self._distanceFlown)
2685 table.attach(labelAlignment, 1, 2, 3, 4)
2686
2687 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2688 label = gtk.Label(xstr("finish_fuel"))
2689 labelAlignment.add(label)
2690 table.attach(labelAlignment, 0, 1, 4, 5)
2691
2692 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2693 self._fuelUsed = gtk.Label()
2694 self._fuelUsed.set_width_chars(10)
2695 self._fuelUsed.set_alignment(0.0, 0.5)
2696 self._fuelUsed.set_use_markup(True)
2697 labelAlignment.add(self._fuelUsed)
2698 table.attach(labelAlignment, 1, 2, 4, 5)
2699
2700 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
2701 yalign = 0.5, yscale = 0.0)
2702 label = gtk.Label(xstr("finish_type"))
2703 label.set_use_underline(True)
2704 labelAlignment.add(label)
2705 table.attach(labelAlignment, 0, 1, 5, 6)
2706
2707 flightTypeModel = gtk.ListStore(str, int)
2708 for (name, type) in FinishPage._flightTypes:
2709 flightTypeModel.append([xstr(name), type])
2710
2711 self._flightType = gtk.ComboBox(model = flightTypeModel)
2712 renderer = gtk.CellRendererText()
2713 self._flightType.pack_start(renderer, True)
2714 self._flightType.add_attribute(renderer, "text", 0)
2715 self._flightType.set_tooltip_text(xstr("finish_type_tooltip"))
2716 self._flightType.set_active(0)
2717 self._flightType.connect("changed", self._flightTypeChanged)
2718 flightTypeAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2719 flightTypeAlignment.add(self._flightType)
2720 table.attach(flightTypeAlignment, 1, 2, 5, 6)
2721 label.set_mnemonic_widget(self._flightType)
2722
2723 self._onlineFlight = gtk.CheckButton(xstr("finish_online"))
2724 self._onlineFlight.set_use_underline(True)
2725 self._onlineFlight.set_tooltip_text(xstr("finish_online_tooltip"))
2726 onlineFlightAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2727 onlineFlightAlignment.add(self._onlineFlight)
2728 table.attach(onlineFlightAlignment, 1, 2, 6, 7)
2729
2730 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
2731 yalign = 0.5, yscale = 0.0)
2732 self._gateLabel = gtk.Label(xstr("finish_gate"))
2733 self._gateLabel.set_use_underline(True)
2734 labelAlignment.add(self._gateLabel)
2735 table.attach(labelAlignment, 0, 1, 7, 8)
2736
2737 self._gatesModel = gtk.ListStore(str)
2738
2739 self._gate = gtk.ComboBox(model = self._gatesModel)
2740 renderer = gtk.CellRendererText()
2741 self._gate.pack_start(renderer, True)
2742 self._gate.add_attribute(renderer, "text", 0)
2743 self._gate.set_tooltip_text(xstr("finish_gate_tooltip"))
2744 self._gate.connect("changed", self._gateChanged)
2745 gateAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
2746 gateAlignment.add(self._gate)
2747 table.attach(gateAlignment, 1, 2, 7, 8)
2748 self._gateLabel.set_mnemonic_widget(self._gate)
2749
2750 self.addButton(xstr("finish_newFlight"),
2751 sensitive = True,
2752 clicked = self._newFlightClicked,
2753 tooltip = xstr("finish_newFlight_tooltip"),
2754 padding = 16)
2755
2756 self.addPreviousButton(clicked = self._backClicked)
2757
2758 self._saveButton = self.addButton(xstr("finish_save"),
2759 sensitive = False,
2760 clicked = self._saveClicked,
2761 tooltip = xstr("finish_save_tooltip"))
2762 self._savePIREPDialog = None
2763 self._lastSavePath = None
2764
2765 self._pirepSaved = False
2766 self._pirepSent = False
2767
2768 self._sendButton = self.addButton(xstr("sendPIREP"), default = True,
2769 sensitive = False,
2770 clicked = self._sendClicked,
2771 tooltip = xstr("sendPIREP_tooltip"))
2772
2773 @property
2774 def flightType(self):
2775 """Get the flight type."""
2776 index = self._flightType.get_active()
2777 return None if index<0 else self._flightType.get_model()[index][1]
2778
2779 @property
2780 def online(self):
2781 """Get whether the flight was an online flight or not."""
2782 return self._onlineFlight.get_active()
2783
2784 def activate(self):
2785 """Activate the page."""
2786 self._pirepSaved = False
2787 self._pirepSent = False
2788
2789 flight = self._wizard.gui._flight
2790 rating = flight.logger.getRating()
2791 if rating<0:
2792 self._flightRating.set_markup('<b><span foreground="red">NO GO</span></b>')
2793 else:
2794 self._flightRating.set_markup("<b>%.1f %%</b>" % (rating,))
2795
2796 flightLength = flight.flightTimeEnd - flight.flightTimeStart
2797 self._flightTime.set_markup("<b>%s</b>" % \
2798 (util.getTimeIntervalString(flightLength),))
2799
2800 blockLength = flight.blockTimeEnd - flight.blockTimeStart
2801 self._blockTime.set_markup("<b>%s</b>" % \
2802 (util.getTimeIntervalString(blockLength),))
2803
2804 self._distanceFlown.set_markup("<b>%.2f NM</b>" % \
2805 (flight.flownDistance,))
2806
2807 self._fuelUsed.set_markup("<b>%.0f kg</b>" % \
2808 (flight.startFuel - flight.endFuel,))
2809
2810 self._flightType.set_active(-1)
2811 self._onlineFlight.set_active(self._wizard.loggedIn)
2812
2813 self._gatesModel.clear()
2814 if self._wizard.gui.config.onlineGateSystem and \
2815 self._wizard.loggedIn and \
2816 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
2817 not self._wizard.entranceExam:
2818 occupiedGates = self._wizard._fleet.getOccupiedGateNumbers()
2819 for gateNumber in const.lhbpGateNumbers:
2820 if gateNumber not in occupiedGates:
2821 self._gatesModel.append([gateNumber])
2822 self._gateLabel.set_sensitive(True)
2823 self._gate.set_sensitive(True)
2824 self._gate.set_active(-1)
2825 else:
2826 self._gateLabel.set_sensitive(False)
2827 self._gate.set_sensitive(False)
2828
2829 def _backClicked(self, button):
2830 """Called when the Back button is pressed."""
2831 self.goBack()
2832
2833 def _updateButtons(self):
2834 """Update the sensitivity state of the buttons."""
2835 sensitive = self._flightType.get_active()>=0 and \
2836 (self._gatesModel.get_iter_first() is None or
2837 self._gate.get_active()>=0)
2838
2839 self._saveButton.set_sensitive(sensitive)
2840 self._sendButton.set_sensitive(sensitive and
2841 self._wizard.bookedFlight.id is not None)
2842
2843 def _flightTypeChanged(self, comboBox):
2844 """Called when the flight type has changed."""
2845 self._updateButtons()
2846
2847 def _gateChanged(self, comboBox):
2848 """Called when the arrival gate has changed."""
2849 self._updateButtons()
2850
2851 def _newFlightClicked(self, button):
2852 """Called when the new flight button is clicked."""
2853 gui = self._wizard.gui
2854 if not self._pirepSent and not self._pirepSaved:
2855 dialog = gtk.MessageDialog(parent = gui.mainWindow,
2856 type = MESSAGETYPE_QUESTION,
2857 message_format = xstr("finish_newFlight_question"))
2858
2859 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
2860 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
2861
2862 dialog.set_title(WINDOW_TITLE_BASE)
2863 result = dialog.run()
2864 dialog.hide()
2865 if result!=RESPONSETYPE_YES:
2866 return
2867
2868 gui.reset()
2869
2870 def _saveClicked(self, button):
2871 """Called when the Save PIREP button is clicked."""
2872 gui = self._wizard.gui
2873
2874 bookedFlight = gui.bookedFlight
2875 tm = time.gmtime()
2876
2877 pilotID = self._wizard.pilotID
2878 if pilotID: pilotID += " "
2879 fileName = "%s%s %02d%02d %s-%s.pirep" % \
2880 (pilotID, str(bookedFlight.departureTime.date()),
2881 tm.tm_hour, tm.tm_min,
2882 bookedFlight.departureICAO,
2883 bookedFlight.arrivalICAO)
2884
2885 dialog = self._getSaveDialog()
2886
2887 if self._lastSavePath is None:
2888 pirepDirectory = gui.config.pirepDirectory
2889 if pirepDirectory is not None:
2890 dialog.set_current_folder(pirepDirectory)
2891 else:
2892 dialog.set_current_folder(os.path.dirname(self._lastSavePath))
2893
2894 dialog.set_current_name(fileName)
2895 result = dialog.run()
2896 dialog.hide()
2897
2898 if result==RESPONSETYPE_OK:
2899 pirep = PIREP(gui.flight)
2900
2901 self._lastSavePath = text2unicode(dialog.get_filename())
2902
2903 if pirep.save(self._lastSavePath):
2904 type = MESSAGETYPE_INFO
2905 message = xstr("finish_save_done")
2906 secondary = None
2907 self._pirepSaved = True
2908 else:
2909 type = MESSAGETYPE_ERROR
2910 message = xstr("finish_save_failed")
2911 secondary = xstr("finish_save_failed_sec")
2912
2913 dialog = gtk.MessageDialog(parent = gui.mainWindow,
2914 type = type, message_format = message)
2915 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
2916 dialog.set_title(WINDOW_TITLE_BASE)
2917 if secondary is not None:
2918 dialog.format_secondary_markup(secondary)
2919
2920 dialog.run()
2921 dialog.hide()
2922
2923 def _getSaveDialog(self):
2924 """Get the PIREP saving dialog.
2925
2926 If it does not exist yet, create it."""
2927 if self._savePIREPDialog is None:
2928 gui = self._wizard.gui
2929 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
2930 xstr("finish_save_title"),
2931 action = FILE_CHOOSER_ACTION_SAVE,
2932 buttons = (gtk.STOCK_CANCEL,
2933 RESPONSETYPE_CANCEL,
2934 gtk.STOCK_OK, RESPONSETYPE_OK),
2935 parent = gui.mainWindow)
2936 dialog.set_modal(True)
2937 dialog.set_do_overwrite_confirmation(True)
2938
2939 filter = gtk.FileFilter()
2940 filter.set_name(xstr("file_filter_pireps"))
2941 filter.add_pattern("*.pirep")
2942 dialog.add_filter(filter)
2943
2944 filter = gtk.FileFilter()
2945 filter.set_name(xstr("file_filter_all"))
2946 filter.add_pattern("*.*")
2947 dialog.add_filter(filter)
2948
2949 self._savePIREPDialog = dialog
2950
2951 return self._savePIREPDialog
2952
2953
2954 def _sendClicked(self, button):
2955 """Called when the Send button is clicked."""
2956 pirep = PIREP(self._wizard.gui.flight)
2957 self._wizard.gui.sendPIREP(pirep,
2958 callback = self._handlePIREPSent)
2959
2960 def _handlePIREPSent(self, returned, result):
2961 """Callback for the PIREP sending result."""
2962 self._pirepSent = returned and result.success
2963 if self._wizard.gui.config.onlineGateSystem and \
2964 self._wizard.loggedIn and not self._wizard.entranceExam and \
2965 returned and result.success:
2966 bookedFlight = self._wizard.bookedFlight
2967 if bookedFlight.arrivalICAO=="LHBP":
2968 iter = self._gate.get_active_iter()
2969 gateNumber = None if iter is None \
2970 else self._gatesModel.get_value(iter, 0)
2971
2972 status = const.PLANE_PARKING if gateNumber is None \
2973 else const.PLANE_HOME
2974 else:
2975 gateNumber = None
2976 status = const.PLANE_AWAY
2977
2978 self._wizard.updatePlane(self._planeUpdated,
2979 bookedFlight.tailNumber,
2980 status, gateNumber = gateNumber)
2981
2982 def _planeUpdated(self, success):
2983 """Callback for the plane updating."""
2984 pass
2985
2986#-----------------------------------------------------------------------------
2987
2988class Wizard(gtk.VBox):
2989 """The flight wizard."""
2990 def __init__(self, gui):
2991 """Construct the wizard."""
2992 super(Wizard, self).__init__()
2993
2994 self.gui = gui
2995
2996 self._pages = []
2997 self._currentPage = None
2998
2999 self._loginPage = LoginPage(self)
3000 self._pages.append(self._loginPage)
3001 self._pages.append(FlightSelectionPage(self))
3002 self._pages.append(GateSelectionPage(self))
3003 self._pages.append(ConnectPage(self))
3004 self._payloadPage = PayloadPage(self)
3005 self._pages.append(self._payloadPage)
3006 self._payloadIndex = len(self._pages)
3007 self._pages.append(TimePage(self))
3008 self._pages.append(FuelPage(self))
3009 self._routePage = RoutePage(self)
3010 self._pages.append(self._routePage)
3011 self._departureBriefingPage = BriefingPage(self, True)
3012 self._pages.append(self._departureBriefingPage)
3013 self._arrivalBriefingPage = BriefingPage(self, False)
3014 self._pages.append(self._arrivalBriefingPage)
3015 self._arrivalBriefingIndex = len(self._pages)
3016 self._takeoffPage = TakeoffPage(self)
3017 self._pages.append(self._takeoffPage)
3018 self._cruisePage = CruisePage(self)
3019 self._pages.append(self._cruisePage)
3020 self._landingPage = LandingPage(self)
3021 self._pages.append(self._landingPage)
3022 self._finishPage = FinishPage(self)
3023 self._pages.append(self._finishPage)
3024
3025 maxWidth = 0
3026 maxHeight = 0
3027 for page in self._pages:
3028 page.show_all()
3029 pageSizeRequest = page.size_request()
3030 width = pageSizeRequest.width if pygobject else pageSizeRequest[0]
3031 height = pageSizeRequest.height if pygobject else pageSizeRequest[1]
3032 maxWidth = max(maxWidth, width)
3033 maxHeight = max(maxHeight, height)
3034 page.setStyle()
3035 maxWidth += 16
3036 maxHeight += 32
3037 self.set_size_request(maxWidth, maxHeight)
3038
3039 self._initialize()
3040
3041 @property
3042 def pilotID(self):
3043 """Get the pilot ID, if given."""
3044 return self._loginPage.pilotID
3045
3046 @property
3047 def entranceExam(self):
3048 """Get whether an entrance exam is about to be taken."""
3049 return self._loginPage.entranceExam
3050
3051 @property
3052 def loggedIn(self):
3053 """Indicate if there was a successful login."""
3054 return self._loginResult is not None
3055
3056 @property
3057 def loginResult(self):
3058 """Get the login result."""
3059 return self._loginResult
3060
3061 def setCurrentPage(self, index, finalize = False):
3062 """Set the current page to the one with the given index."""
3063 assert index < len(self._pages)
3064
3065 fromPage = self._currentPage
3066 if fromPage is not None:
3067 page = self._pages[fromPage]
3068 if finalize and not page._completed:
3069 page.complete()
3070 self.remove(page)
3071
3072 self._currentPage = index
3073 page = self._pages[index]
3074 self.add(page)
3075 if page._fromPage is None:
3076 page._fromPage = fromPage
3077 page.initialize()
3078 self.show_all()
3079 if fromPage is not None:
3080 self.grabDefault()
3081
3082 @property
3083 def bookedFlight(self):
3084 """Get the booked flight selected."""
3085 return self._bookedFlight
3086
3087 @property
3088 def numCrew(self):
3089 """Get the number of crew members."""
3090 return self._payloadPage.numCrew
3091
3092 @property
3093 def numPassengers(self):
3094 """Get the number of passengers."""
3095 return self._payloadPage.numPassengers
3096
3097 @property
3098 def bagWeight(self):
3099 """Get the baggage weight."""
3100 return self._payloadPage.bagWeight
3101
3102 @property
3103 def cargoWeight(self):
3104 """Get the cargo weight."""
3105 return self._payloadPage.cargoWeight
3106
3107 @property
3108 def mailWeight(self):
3109 """Get the mail weight."""
3110 return self._payloadPage.mailWeight
3111
3112 @property
3113 def zfw(self):
3114 """Get the calculated ZFW value."""
3115 return 0 if self._bookedFlight is None \
3116 else self._payloadPage.calculateZFW()
3117
3118 @property
3119 def filedCruiseLevel(self):
3120 """Get the filed cruise level."""
3121 return self._routePage.filedCruiseLevel
3122
3123 @property
3124 def filedCruiseAltitude(self):
3125 """Get the filed cruise altitude."""
3126 return self._routePage.filedCruiseLevel * 100
3127
3128 @property
3129 def cruiseAltitude(self):
3130 """Get the cruise altitude."""
3131 level = self._cruisePage.cruiseLevel if self._cruisePage.activated \
3132 else self._routePage.filedCruiseLevel
3133 return level * 100
3134
3135 @property
3136 def loggableCruiseAltitude(self):
3137 """Get the cruise altitude that can be logged."""
3138 if self._cruisePage.activated:
3139 return self._cruisePage.loggableCruiseLevel * 100
3140 else:
3141 return 0
3142
3143 @property
3144 def route(self):
3145 """Get the route."""
3146 return self._routePage.route
3147
3148 @property
3149 def departureMETAR(self):
3150 """Get the METAR of the departure airport."""
3151 return self._departureBriefingPage.metar
3152
3153 @property
3154 def arrivalMETAR(self):
3155 """Get the METAR of the arrival airport."""
3156 return self._arrivalBriefingPage.metar
3157
3158 @property
3159 def departureRunway(self):
3160 """Get the departure runway."""
3161 return self._takeoffPage.runway
3162
3163 @property
3164 def sid(self):
3165 """Get the SID."""
3166 return self._takeoffPage.sid
3167
3168 @property
3169 def v1(self):
3170 """Get the V1 speed."""
3171 return self._takeoffPage.v1
3172
3173 @property
3174 def vr(self):
3175 """Get the Vr speed."""
3176 return self._takeoffPage.vr
3177
3178 @property
3179 def v2(self):
3180 """Get the V2 speed."""
3181 return self._takeoffPage.v2
3182
3183 @property
3184 def derate(self):
3185 """Get the derate value."""
3186 return self._takeoffPage.derate
3187
3188 @property
3189 def rtoIndicated(self):
3190 """Get whether the pilot has indicated that an RTO has occured."""
3191 return self._takeoffPage.rtoIndicated
3192
3193 @property
3194 def arrivalRunway(self):
3195 """Get the arrival runway."""
3196 return self._landingPage.runway
3197
3198 @property
3199 def star(self):
3200 """Get the STAR."""
3201 return self._landingPage.star
3202
3203 @property
3204 def transition(self):
3205 """Get the transition."""
3206 return self._landingPage.transition
3207
3208 @property
3209 def approachType(self):
3210 """Get the approach type."""
3211 return self._landingPage.approachType
3212
3213 @property
3214 def vref(self):
3215 """Get the Vref speed."""
3216 return self._landingPage.vref
3217
3218 @property
3219 def flightType(self):
3220 """Get the flight type."""
3221 return self._finishPage.flightType
3222
3223 @property
3224 def online(self):
3225 """Get whether the flight was online or not."""
3226 return self._finishPage.online
3227
3228 def nextPage(self, finalize = True):
3229 """Go to the next page."""
3230 self.jumpPage(1, finalize)
3231
3232 def jumpPage(self, count, finalize = True):
3233 """Go to the page which is 'count' pages after the current one."""
3234 self.setCurrentPage(self._currentPage + count, finalize = finalize)
3235
3236 def grabDefault(self):
3237 """Make the default button of the current page the default."""
3238 self._pages[self._currentPage].grabDefault()
3239
3240 def connected(self, fsType, descriptor):
3241 """Called when the connection could be made to the simulator."""
3242 self.nextPage()
3243
3244 def reset(self, loginResult):
3245 """Resets the wizard to go back to the login page."""
3246 self._initialize(keepLoginResult = loginResult is None,
3247 loginResult = loginResult)
3248
3249 def setStage(self, stage):
3250 """Set the flight stage to the given one."""
3251 if stage!=const.STAGE_END:
3252 self._cruisePage.setLoggable(Flight.canLogCruiseAltitude(stage))
3253
3254 if stage==const.STAGE_TAKEOFF:
3255 self._takeoffPage.allowForward()
3256 elif stage==const.STAGE_LANDING:
3257 if not self._arrivalBriefingPage.metarEdited:
3258 print "Downloading arrival METAR again"
3259 self.gui.webHandler.getMETARs(self._arrivalMETARCallback,
3260 [self._bookedFlight.arrivalICAO])
3261
3262 elif stage==const.STAGE_END:
3263 for page in self._pages:
3264 page.flightEnded()
3265
3266 def _initialize(self, keepLoginResult = False, loginResult = None):
3267 """Initialize the wizard."""
3268 if not keepLoginResult:
3269 self._loginResult = loginResult
3270
3271 self._loginCallback = None
3272
3273 self._fleet = None
3274 self._fleetCallback = None
3275
3276 self._bookedFlight = None
3277 self._departureGate = "-"
3278 self._fuelData = None
3279 self._departureNOTAMs = None
3280 self._departureMETAR = None
3281 self._arrivalNOTAMs = None
3282 self._arrivalMETAR = None
3283
3284 firstPage = 0 if self._loginResult is None else 1
3285 for page in self._pages[firstPage:]:
3286 page.reset()
3287
3288 self.setCurrentPage(firstPage)
3289
3290 def login(self, callback, pilotID, password, entranceExam):
3291 """Called when the login button was clicked."""
3292 self._loginCallback = callback
3293 if pilotID is None:
3294 loginResult = self._loginResult
3295 assert loginResult is not None and loginResult.loggedIn
3296 pilotID = loginResult.pilotID
3297 password = loginResult.password
3298 entranceExam = loginResult.entranceExam
3299 busyMessage = xstr("reload_busy")
3300 else:
3301 self._loginResult = None
3302 busyMessage = xstr("login_busy")
3303
3304 self.gui.beginBusy(busyMessage)
3305
3306 self.gui.webHandler.login(self._loginResultCallback,
3307 pilotID, password,
3308 entranceExam = entranceExam)
3309
3310 def reloadFlights(self, callback):
3311 """Reload the flights from the MAVA server."""
3312 self.login(callback, None, None, None)
3313
3314 def cruiseLevelChanged(self):
3315 """Called when the cruise level is changed."""
3316 return self.gui.cruiseLevelChanged()
3317
3318 def _loginResultCallback(self, returned, result):
3319 """The login result callback, called in the web handler's thread."""
3320 gobject.idle_add(self._handleLoginResult, returned, result)
3321
3322 def _handleLoginResult(self, returned, result):
3323 """Handle the login result."""
3324 self.gui.endBusy()
3325 isReload = self._loginResult is not None
3326 if returned:
3327 if result.loggedIn:
3328 self._loginResult = result
3329 else:
3330 if isReload:
3331 message = xstr("reload_failed")
3332 else:
3333 message = xstr("login_entranceExam_invalid"
3334 if self.entranceExam else
3335 xstr("login_invalid"))
3336 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
3337 type = MESSAGETYPE_ERROR,
3338 message_format = message)
3339 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
3340 dialog.set_title(WINDOW_TITLE_BASE)
3341 if isReload:
3342 secondary = xstr("reload_failed_sec")
3343 else:
3344 secondary = xstr("login_entranceExam_invalid_sec"
3345 if self.entranceExam else
3346 xstr("login_invalid_sec"))
3347 dialog.format_secondary_markup(secondary)
3348 dialog.run()
3349 dialog.hide()
3350 else:
3351 message = xstr("reload_failconn") if isReload \
3352 else xstr("login_failconn")
3353 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
3354 type = MESSAGETYPE_ERROR,
3355 message_format = message)
3356 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
3357 dialog.set_title(WINDOW_TITLE_BASE)
3358 secondary = xstr("reload_failconn_sec") if isReload \
3359 else xstr("login_failconn_sec")
3360 dialog.format_secondary_markup(secondary)
3361
3362 dialog.run()
3363 dialog.hide()
3364
3365 callback = self._loginCallback
3366 self._loginCallback = None
3367 callback(returned, result)
3368
3369 def getFleet(self, callback, force = False):
3370 """Get the fleet via the GUI and call the given callback."""
3371 self._fleetCallback = callback
3372 self.gui.getFleet(callback = self._fleetRetrieved, force = force)
3373
3374 def _fleetRetrieved(self, fleet):
3375 """Callback for the fleet retrieval."""
3376 self._fleet = fleet
3377 if self._fleetCallback is not None:
3378 self._fleetCallback(fleet)
3379 self._fleetCallback = None
3380
3381 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
3382 """Update the given plane's gate information."""
3383 self.gui.updatePlane(tailNumber, status, gateNumber = gateNumber,
3384 callback = callback)
3385
3386 def updateRTO(self):
3387 """Update the RTO state.
3388
3389 The RTO checkbox will be enabled if the flight has an RTO state and the
3390 comments field contains some text."""
3391 flight = self.gui.flight
3392 rtoEnabled = flight is not None and flight.hasRTO and \
3393 self.gui.hasComments
3394 self._takeoffPage.setRTOEnabled(rtoEnabled)
3395
3396 def rtoToggled(self, indicated):
3397 """Called when the RTO indication has changed."""
3398 self.gui.rtoToggled(indicated)
3399
3400 def _connectSimulator(self):
3401 """Connect to the simulator."""
3402 self.gui.connectSimulator(self._bookedFlight.aircraftType)
3403
3404 def _arrivalMETARCallback(self, returned, result):
3405 """Called when the METAR of the arrival airport is retrieved."""
3406 gobject.idle_add(self._handleArrivalMETAR, returned, result)
3407
3408 def _handleArrivalMETAR(self, returned, result):
3409 """Called when the METAR of the arrival airport is retrieved."""
3410 icao = self._bookedFlight.arrivalICAO
3411 if returned and icao in result.metars:
3412 metar = result.metars[icao]
3413 if metar!="":
3414 self._arrivalBriefingPage.setMETAR(metar)
3415
3416#-----------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.