source: src/mlx/gui/flight.py@ 619:7763179ff6b0

Last change on this file since 619:7763179ff6b0 was 619:7763179ff6b0, checked in by István Váradi <ivaradi@…>, 9 years ago

Reworked the representation and handling of gate data (re #113)

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