source: src/mlx/gui/flight.py@ 691:9fec000bc07f

cef
Last change on this file since 691:9fec000bc07f was 691:9fec000bc07f, checked in by István Váradi <ivaradi@…>, 7 years ago

Added translatable strings for the controls on the SimBrief setup page (re #279)

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