source: src/mlx/gui/flight.py@ 687:bb05f0618b5b

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

SimBrief works basically (re #279)

File size: 162.1 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 "SimBrief setup",
1641 "Complete the following data to "
1642 "start generating your "
1643 "SimBrief briefing.",
1644 "Your SimBrief briefing was "
1645 "generated with the following "
1646 "data.")
1647
1648 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1649 xscale = 0.0, yscale = 0.0)
1650
1651 table = gtk.Table(7, 3)
1652 table.set_row_spacings(4)
1653 table.set_col_spacings(16)
1654 table.set_homogeneous(False)
1655 alignment.add(table)
1656 self.setMainWidget(alignment)
1657
1658 label = gtk.Label("_Username:")
1659 label.set_use_underline(True)
1660 label.set_alignment(0.0, 0.5)
1661 table.attach(label, 0, 1, 0, 1)
1662
1663 self._userName = gtk.Entry()
1664 self._userName.set_width_chars(16)
1665 self._userName.connect("changed",
1666 lambda button: self._updateForwardButton())
1667 self._userName.set_tooltip_text("Your SimBrief username")
1668 table.attach(self._userName, 1, 2, 0, 1)
1669 label.set_mnemonic_widget(self._userName)
1670
1671 label = gtk.Label("_Password:")
1672 label.set_use_underline(True)
1673 label.set_alignment(0.0, 0.5)
1674 table.attach(label, 0, 1, 1, 2)
1675
1676 self._password = gtk.Entry()
1677 self._password.set_visibility(False)
1678 self._password.connect("changed",
1679 lambda button: self._updateForwardButton())
1680 self._password.set_tooltip_text("Your SimBrief password")
1681 table.attach(self._password, 1, 2, 1, 2)
1682 label.set_mnemonic_widget(self._password)
1683
1684 self.addCancelFlightButton()
1685
1686 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1687 self._button = self.addNextButton(clicked = self._forwardClicked)
1688
1689 def activate(self):
1690 """Activate the SimBrief setup page"""
1691 self._updateForwardButton()
1692
1693 def _updateForwardButton(self):
1694 """Update the sensitivity of the forward button."""
1695 self._button.set_sensitive(len(self._userName.get_text())>0 and
1696 len(self._password.get_text())>0)
1697
1698 def _backClicked(self, button):
1699 """Called when the Back button is pressed."""
1700 self.goBack()
1701
1702 def _forwardClicked(self, button):
1703 if self._completed:
1704 self._wizard.nextPage()
1705 else:
1706 plan = self._getPlan()
1707 print "plan:", plan
1708
1709 userName = self._userName.get_text()
1710 password = self._password.get_text()
1711
1712 self._wizard.gui.beginBusy("Calling SimBrief...")
1713
1714 cef.callSimBrief(plan,
1715 lambda count: (userName, password),
1716 self._simBriefProgressCallback,
1717 SimBriefSetupPage.getHTMLFilePath())
1718
1719 startSound(const.SOUND_NOTAM)
1720
1721 def _simBriefProgressCallback(self, message, done):
1722 """Called by the SimBrief handling thread."""
1723 gobject.idle_add(self._simBriefProgress, message, done)
1724
1725 def _simBriefProgress(self, message, done):
1726 """The real SimBrief progress handler."""
1727 if done:
1728 self._wizard.gui.endBusy()
1729 self._wizard.nextPage()
1730 else:
1731 self._wizard.gui.updateBusyState(message)
1732
1733 def _getPlan(self):
1734 """Get the flight plan data for SimBrief."""
1735 plan = {
1736 "airline": "MAH",
1737 "selcal": "XXXX",
1738 "fuelfactor": "P000",
1739 "contpct": "0.05",
1740 "resvrule": "45",
1741 "taxiout": "10",
1742 "taxiin": "10",
1743 "civalue": "AUTO"
1744 }
1745
1746 wizard = self._wizard
1747 gui = wizard.gui
1748
1749 loginResult = wizard.loginResult
1750 plan["cpt"] = loginResult.pilotName
1751 plan["pid"] = loginResult.pilotID
1752
1753 bookedFlight = wizard.bookedFlight
1754 plan["fltnum"] = wizard.bookedFlight.callsign[2:]
1755 plan["type"] = const.icaoCodes[bookedFlight.aircraftType]
1756 plan["orig"] = bookedFlight.departureICAO
1757 plan["dest"] = bookedFlight.arrivalICAO
1758 plan["reg"] = bookedFlight.tailNumber
1759 plan["fin"] = bookedFlight.tailNumber[3:]
1760 plan["pax"] = str(bookedFlight.numPassengers)
1761
1762 departureTime = bookedFlight.departureTime
1763 plan["date"] = "%d%s%d" % (departureTime.day,
1764 SimBriefSetupPage.monthNum2Name[departureTime.month-1],
1765 departureTime.year%100)
1766 plan["deph"] = str(departureTime.hour)
1767 plan["depm"] = str(departureTime.minute)
1768
1769 arrivalTime = bookedFlight.arrivalTime
1770 plan["steh"] = str(arrivalTime.hour)
1771 plan["stem"] = str(arrivalTime.minute)
1772
1773 plan["manualzfw"] = str(wizard.zfw)
1774 plan["cargo"] = str(wizard.bagWeight + wizard.cargoWeight + wizard.mailWeight)
1775
1776 plan["route"] = wizard.route
1777 plan["fl"] = str(wizard.filedCruiseAltitude)
1778 plan["altn"] = wizard.alternate
1779
1780 plan["addedfuel"] = "2.5" # FIXME: query
1781 plan["origrwy"] = "" # FIXME: query
1782 plan["destrwy"] = "" # FIXME: query
1783 plan["climb"] = "250/300/78" # FIXME: query
1784 plan["descent"] = "80/280/250" # FIXME: query
1785 plan["cruise"] = "LRC" # FIXME: query
1786
1787 return plan
1788
1789#-----------------------------------------------------------------------------
1790
1791class SimBriefingPage(Page):
1792 """Page to display the SimBrief HTML briefing."""
1793 def __init__(self, wizard):
1794 """Construct the setup page."""
1795
1796 super(SimBriefingPage, self).__init__(wizard,
1797 "SimBrief flight plan", "")
1798
1799 self._alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1800 xscale = 1.0, yscale = 1.0)
1801
1802 self.setMainWidget(self._alignment)
1803
1804 self.addCancelFlightButton()
1805
1806 self.addPreviousButton(clicked = self._backClicked)
1807
1808 self._button = self.addNextButton(clicked = self._forwardClicked)
1809 self._button.set_label(xstr("briefing_button"))
1810 self._button.set_has_tooltip(False)
1811 self._button.set_use_stock(False)
1812
1813 def activate(self):
1814 """Activate the SimBrief flight plan page"""
1815 container = cef.getContainer()
1816 self._alignment.add(container)
1817
1818 self._browser = \
1819 cef.startInContainer(container,
1820 "file://" + SimBriefSetupPage.getHTMLFilePath())
1821
1822 def _backClicked(self, button):
1823 """Called when the Back button has been pressed."""
1824 self.goBack()
1825
1826 def _forwardClicked(self, button):
1827 """Called when the Forward button has been pressed."""
1828 if not self._completed:
1829 self._button.set_label(xstr("button_next"))
1830 self._button.set_tooltip_text(xstr("button_next_tooltip"))
1831 self._wizard.usingSimBrief = True
1832 self.complete()
1833
1834 self._wizard.nextPage()
1835
1836#-----------------------------------------------------------------------------
1837
1838class FuelTank(gtk.VBox):
1839 """Widget for the fuel tank."""
1840 def __init__(self, fuelTank, name, capacity, currentWeight):
1841 """Construct the widget for the tank with the given name."""
1842 super(FuelTank, self).__init__()
1843
1844 self._enabled = True
1845 self.fuelTank = fuelTank
1846 self.capacity = capacity
1847 self.currentWeight = currentWeight
1848 self.expectedWeight = currentWeight
1849
1850 label = gtk.Label("<b>" + name + "</b>")
1851 label.set_use_markup(True)
1852 label.set_use_underline(True)
1853 label.set_justify(JUSTIFY_CENTER)
1854 label.set_alignment(0.5, 1.0)
1855 self.pack_start(label, False, False, 4)
1856
1857 self._tankFigure = gtk.EventBox()
1858 self._tankFigure.set_size_request(38, -1)
1859 self._tankFigure.set_visible_window(False)
1860 self._tankFigure.set_tooltip_markup(xstr("fuel_tank_tooltip"))
1861
1862 if pygobject:
1863 self._tankFigure.connect("draw", self._drawTankFigure)
1864 else:
1865 self._tankFigure.connect("expose_event", self._drawTankFigure)
1866 self._tankFigure.connect("button_press_event", self._buttonPressed)
1867 self._tankFigure.connect("motion_notify_event", self._motionNotify)
1868 self._tankFigure.connect("scroll-event", self._scrolled)
1869
1870 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1871 xscale = 0.0, yscale = 1.0)
1872 alignment.add(self._tankFigure)
1873
1874 self.pack_start(alignment, True, True, 4)
1875
1876 self._expectedButton = gtk.SpinButton()
1877 self._expectedButton.set_numeric(True)
1878 self._expectedButton.set_range(0, self.capacity)
1879 self._expectedButton.set_increments(10, 100)
1880 self._expectedButton.set_value(currentWeight)
1881 self._expectedButton.set_alignment(1.0)
1882 self._expectedButton.set_width_chars(5)
1883 self._expectedButton.connect("value-changed", self._expectedChanged)
1884
1885 label.set_mnemonic_widget(self._expectedButton)
1886
1887 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1888 xscale = 0.0, yscale = 1.0)
1889 alignment.add(self._expectedButton)
1890 self.pack_start(alignment, False, False, 4)
1891
1892 def setCurrent(self, currentWeight):
1893 """Set the current weight."""
1894 self.currentWeight = currentWeight
1895 self._redraw()
1896
1897 def isCorrect(self):
1898 """Determine if the contents of the fuel tank are as expected"""
1899 return abs(self.expectedWeight - self.currentWeight)<=1
1900
1901 def disable(self):
1902 """Disable the fuel tank."""
1903 self._expectedButton.set_sensitive(False)
1904 self._enabled = False
1905
1906 def _redraw(self):
1907 """Redraw the tank figure."""
1908 self._tankFigure.queue_draw()
1909
1910 def _drawTankFigure(self, tankFigure, eventOrContext):
1911 """Draw the tank figure."""
1912 triangleSize = 5
1913
1914 context = eventOrContext if pygobject else tankFigure.window.cairo_create()
1915 (xOffset, yOffset) = (0, 0) if pygobject \
1916 else (tankFigure.allocation.x, tankFigure.allocation.y)
1917
1918 width = tankFigure.get_allocated_width() if pygobject \
1919 else tankFigure.allocation.width
1920 height = tankFigure.get_allocated_height() if pygobject \
1921 else tankFigure.allocation.height
1922
1923 rectangleX0 = triangleSize
1924 rectangleY0 = triangleSize
1925 rectangleX1 = width - 1 - triangleSize
1926 rectangleY1 = height - 1 - triangleSize
1927 rectangleLineWidth = 2.0
1928
1929 context.set_source_rgb(0.0, 0.0, 0.0)
1930 context.set_line_width(rectangleLineWidth)
1931 context.rectangle(xOffset + rectangleX0 + rectangleLineWidth/2,
1932 yOffset + rectangleY0 + rectangleLineWidth/2,
1933 rectangleX1 - rectangleX0 - rectangleLineWidth,
1934 rectangleY1 - rectangleY0 - rectangleLineWidth)
1935 context.stroke()
1936
1937 rectangleInnerLeft = rectangleX0 + rectangleLineWidth
1938 rectangleInnerRight = rectangleX1 - rectangleLineWidth
1939 self._rectangleInnerTop = rectangleInnerTop = rectangleY0 + rectangleLineWidth
1940 self._rectangleInnerBottom = rectangleInnerBottom = rectangleY1 - rectangleLineWidth
1941
1942 rectangleInnerWidth = rectangleInnerRight - rectangleInnerLeft
1943 rectangleInnerHeight = rectangleInnerBottom - rectangleInnerTop
1944
1945 context.set_source_rgb(1.0, 0.9, 0.6)
1946 currentHeight = self.currentWeight * rectangleInnerHeight / self.capacity
1947 currentX = rectangleInnerTop + rectangleInnerHeight - currentHeight
1948 context.rectangle(xOffset + rectangleInnerLeft,
1949 yOffset + rectangleInnerTop +
1950 rectangleInnerHeight - currentHeight,
1951 rectangleInnerWidth, currentHeight)
1952 context.fill()
1953
1954 expectedHeight = self.expectedWeight * rectangleInnerHeight / self.capacity
1955 expectedY = rectangleInnerTop + rectangleInnerHeight - expectedHeight
1956
1957 context.set_line_width(1.5)
1958 context.set_source_rgb(0.0, 0.85, 0.85)
1959 context.move_to(xOffset + rectangleX0, yOffset + expectedY)
1960 context.line_to(xOffset + rectangleX1, yOffset + expectedY)
1961 context.stroke()
1962
1963 context.set_line_width(0.0)
1964 context.move_to(xOffset + 0, yOffset + expectedY - triangleSize)
1965 context.line_to(xOffset + 0, yOffset + expectedY + triangleSize)
1966 context.line_to(xOffset + rectangleX0 + 1, yOffset + expectedY)
1967 context.line_to(xOffset + 0, yOffset + expectedY - triangleSize)
1968 context.fill()
1969
1970 context.set_line_width(0.0)
1971 context.move_to(xOffset + width, yOffset + expectedY - triangleSize)
1972 context.line_to(xOffset + width, yOffset + expectedY + triangleSize)
1973 context.line_to(xOffset + rectangleX1 - 1, yOffset + expectedY)
1974 context.line_to(xOffset + width, yOffset + expectedY - triangleSize)
1975 context.fill()
1976
1977 return True
1978
1979 def _setExpectedFromY(self, y):
1980 """Set the expected weight from the given Y-coordinate."""
1981 level = (self._rectangleInnerBottom - y) / \
1982 (self._rectangleInnerBottom - self._rectangleInnerTop)
1983 level = min(1.0, max(0.0, level))
1984 self._expectedButton.set_value(level * self.capacity)
1985
1986 def _buttonPressed(self, tankFigure, event):
1987 """Called when a button is pressed in the figure.
1988
1989 The expected level will be set there."""
1990 if self._enabled and event.button==1:
1991 self._setExpectedFromY(event.y)
1992
1993 def _motionNotify(self, tankFigure, event):
1994 """Called when the mouse pointer moves within the area of a tank figure."""
1995 if self._enabled and event.state==BUTTON1_MASK:
1996 self._setExpectedFromY(event.y)
1997
1998 def _scrolled(self, tankFigure, event):
1999 """Called when a scroll event is received."""
2000 if self._enabled:
2001 increment = 1 if event.state==CONTROL_MASK \
2002 else 100 if event.state==SHIFT_MASK \
2003 else 10 if event.state==0 else 0
2004 if increment!=0:
2005 if event.direction==SCROLL_DOWN:
2006 increment *= -1
2007 self._expectedButton.spin(SPIN_USER_DEFINED, increment)
2008
2009 def _expectedChanged(self, spinButton):
2010 """Called when the expected value has changed."""
2011 self.expectedWeight = spinButton.get_value_as_int()
2012 self._redraw()
2013
2014#-----------------------------------------------------------------------------
2015
2016class FuelPage(Page):
2017 """The page containing the fuel tank filling."""
2018 _pumpStep = 0.02
2019
2020 def __init__(self, wizard):
2021 """Construct the page."""
2022 super(FuelPage, self).__init__(wizard, xstr("fuel_title"),
2023 xstr("fuel_help_pre") +
2024 xstr("fuel_help_post"),
2025 completedHelp = xstr("fuel_chelp"))
2026
2027 self._fuelTanks = []
2028 self._fuelTable = None
2029 self._fuelAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2030 xscale = 0.0, yscale = 1.0)
2031 self.setMainWidget(self._fuelAlignment)
2032
2033 tankData = [(tank, 2500, 3900) for tank in acft.mostFuelTanks]
2034 self._setupTanks(tankData)
2035
2036 self.addCancelFlightButton()
2037
2038 self._backButton = self.addPreviousButton(clicked = self._backClicked)
2039 self._button = self.addNextButton(clicked = self._forwardClicked)
2040
2041 self._pumpIndex = 0
2042
2043 def activate(self):
2044 """Activate the page."""
2045 self._setupTanks(self._wizard._fuelData)
2046
2047 aircraft = self._wizard.gui.flight.aircraft
2048 minLandingFuel = aircraft.minLandingFuel
2049 recommendedLandingFuel = aircraft.recommendedLandingFuel
2050
2051 middleHelp = "" if minLandingFuel is None else \
2052 (xstr("fuel_help_min") % (minLandingFuel,)) \
2053 if recommendedLandingFuel is None else \
2054 (xstr("fuel_help_min_rec") % (minLandingFuel,
2055 recommendedLandingFuel))
2056 self.setHelp(xstr("fuel_help_pre") + middleHelp + xstr("fuel_help_post"))
2057
2058 def finalize(self):
2059 """Finalize the page."""
2060 for fuelTank in self._fuelTanks:
2061 fuelTank.disable()
2062
2063 def _backClicked(self, button):
2064 """Called when the Back button is pressed."""
2065 self.goBack()
2066
2067 def _forwardClicked(self, button):
2068 """Called when the forward button is clicked."""
2069 if not self._completed:
2070 self._pumpIndex = 0
2071 self._wizard.gui.beginBusy(xstr("fuel_pump_busy"))
2072 self._pump()
2073 elif self._wizard.usingSimBrief:
2074 self._wizard.jumpPage(3)
2075 else:
2076 self._wizard.nextPage()
2077
2078 def _setupTanks(self, tankData):
2079 """Setup the tanks for the given data."""
2080 numTanks = len(tankData)
2081 if self._fuelTable is not None:
2082 self._fuelAlignment.remove(self._fuelTable)
2083
2084 self._fuelTanks = []
2085 self._fuelTable = gtk.Table(numTanks, 1)
2086 self._fuelTable.set_col_spacings(16)
2087 index = 0
2088 for (tank, current, capacity) in tankData:
2089 fuelTank = FuelTank(tank,
2090 xstr("fuel_tank_" +
2091 const.fuelTank2string(tank)),
2092 capacity, current)
2093 self._fuelTable.attach(fuelTank, index, index+1, 0, 1)
2094 self._fuelTanks.append(fuelTank)
2095 index += 1
2096
2097 self._fuelAlignment.add(self._fuelTable)
2098 self.show_all()
2099
2100 def _pump(self):
2101 """Perform one step of pumping.
2102
2103 It is checked, if the current tank's contents are of the right
2104 quantity. If not, it is filled one step further to the desired
2105 contents. Otherwise the next tank is started. If all tanks are are
2106 filled, the next page is selected."""
2107 numTanks = len(self._fuelTanks)
2108
2109 fuelTank = None
2110 while self._pumpIndex < numTanks:
2111 fuelTank = self._fuelTanks[self._pumpIndex]
2112 if fuelTank.isCorrect():
2113 self._pumpIndex += 1
2114 fuelTank = None
2115 else:
2116 break
2117
2118 if fuelTank is None:
2119 self._wizard.gui.endBusy()
2120 if self._wizard.usingSimBrief:
2121 self._wizard.gui.startMonitoring()
2122 self._wizard.jumpPage(3)
2123 else:
2124 bookedFlight = self._wizard._bookedFlight
2125 self._wizard.gui.beginBusy(xstr("route_down_notams"))
2126 self._wizard.gui.webHandler.getNOTAMs(self._notamsCallback,
2127 bookedFlight.departureICAO,
2128 bookedFlight.arrivalICAO)
2129 startSound(const.SOUND_NOTAM)
2130 else:
2131 currentLevel = fuelTank.currentWeight / fuelTank.capacity
2132 expectedLevel = fuelTank.expectedWeight / fuelTank.capacity
2133 if currentLevel<expectedLevel:
2134 currentLevel += FuelPage._pumpStep
2135 if currentLevel>expectedLevel: currentLevel = expectedLevel
2136 else:
2137 currentLevel -= FuelPage._pumpStep
2138 if currentLevel<expectedLevel: currentLevel = expectedLevel
2139 fuelTank.setCurrent(currentLevel * fuelTank.capacity)
2140 self._wizard.gui.simulator.setFuelLevel([(fuelTank.fuelTank,
2141 currentLevel)])
2142 gobject.timeout_add(50, self._pump)
2143
2144 def _notamsCallback(self, returned, result):
2145 """Callback for the NOTAMs."""
2146 gobject.idle_add(self._handleNOTAMs, returned, result)
2147
2148 def _handleNOTAMs(self, returned, result):
2149 """Handle the NOTAMs."""
2150 if returned:
2151 self._wizard._departureNOTAMs = result.departureNOTAMs
2152 self._wizard._arrivalNOTAMs = result.arrivalNOTAMs
2153 else:
2154 self._wizard._departureNOTAMs = None
2155 self._wizard._arrivalNOTAMs = None
2156
2157 bookedFlight = self._wizard._bookedFlight
2158 self._wizard.gui.beginBusy(xstr("route_down_metars"))
2159 self._wizard.gui.webHandler.getMETARs(self._metarsCallback,
2160 [bookedFlight.departureICAO,
2161 bookedFlight.arrivalICAO])
2162
2163 def _metarsCallback(self, returned, result):
2164 """Callback for the METARs."""
2165 gobject.idle_add(self._handleMETARs, returned, result)
2166
2167 def _handleMETARs(self, returned, result):
2168 """Handle the METARs."""
2169 self._wizard._departureMETAR = None
2170 self._wizard._arrivalMETAR = None
2171 bookedFlight = self._wizard._bookedFlight
2172 if returned:
2173 if bookedFlight.departureICAO in result.metars:
2174 self._wizard._departureMETAR = result.metars[bookedFlight.departureICAO]
2175 if bookedFlight.arrivalICAO in result.metars:
2176 self._wizard._arrivalMETAR = result.metars[bookedFlight.arrivalICAO]
2177
2178 self._wizard.gui.endBusy()
2179 self._backButton.set_sensitive(True)
2180 self._button.set_sensitive(True)
2181 self._wizard.nextPage()
2182
2183#-----------------------------------------------------------------------------
2184
2185class BriefingPage(Page):
2186 """Page for the briefing."""
2187 def __init__(self, wizard, departure):
2188 """Construct the briefing page."""
2189 self._departure = departure
2190
2191 title = xstr("briefing_title") % (1 if departure else 2,
2192 xstr("briefing_departure")
2193 if departure
2194 else xstr("briefing_arrival"))
2195 super(BriefingPage, self).__init__(wizard, title, xstr("briefing_help"),
2196 completedHelp = xstr("briefing_chelp"))
2197
2198 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2199 xscale = 1.0, yscale = 1.0)
2200
2201 mainBox = gtk.VBox()
2202 alignment.add(mainBox)
2203 self.setMainWidget(alignment)
2204
2205 self._notamsFrame = gtk.Frame()
2206 self._notamsFrame.set_label(xstr("briefing_notams_init"))
2207 scrolledWindow = gtk.ScrolledWindow()
2208 scrolledWindow.set_size_request(-1, 128)
2209 # FIXME: these constants should be in common
2210 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
2211 else gtk.POLICY_AUTOMATIC,
2212 gtk.PolicyType.AUTOMATIC if pygobject
2213 else gtk.POLICY_AUTOMATIC)
2214 self._notams = gtk.TextView()
2215 self._notams.set_editable(False)
2216 self._notams.set_accepts_tab(False)
2217 self._notams.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
2218 scrolledWindow.add(self._notams)
2219 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
2220 xscale = 1.0, yscale = 1.0)
2221 alignment.set_padding(padding_top = 4, padding_bottom = 0,
2222 padding_left = 0, padding_right = 0)
2223 alignment.add(scrolledWindow)
2224 self._notamsFrame.add(alignment)
2225 mainBox.pack_start(self._notamsFrame, True, True, 4)
2226
2227 self._metarFrame = gtk.Frame()
2228 self._metarFrame.set_label(xstr("briefing_metar_init"))
2229 scrolledWindow = gtk.ScrolledWindow()
2230 scrolledWindow.set_size_request(-1, 32)
2231 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
2232 else gtk.POLICY_AUTOMATIC,
2233 gtk.PolicyType.AUTOMATIC if pygobject
2234 else gtk.POLICY_AUTOMATIC)
2235
2236 self._updatingMETAR = False
2237
2238 self._metar = gtk.TextView()
2239 self._metar.set_accepts_tab(False)
2240 self._metar.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
2241 self._metar.get_buffer().connect("changed", self._metarChanged)
2242 self._metar.get_buffer().connect_after("insert-text", self._metarInserted)
2243 scrolledWindow.add(self._metar)
2244 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
2245 xscale = 1.0, yscale = 1.0)
2246 alignment.set_padding(padding_top = 4, padding_bottom = 0,
2247 padding_left = 0, padding_right = 0)
2248 alignment.add(scrolledWindow)
2249 self._metarFrame.add(alignment)
2250 mainBox.pack_start(self._metarFrame, True, True, 4)
2251 self.metarEdited = False
2252
2253 self.addCancelFlightButton()
2254
2255 self.addPreviousButton(clicked = self._backClicked)
2256 self._button = self.addNextButton(clicked = self._forwardClicked)
2257
2258 @property
2259 def metar(self):
2260 """Get the METAR on the page."""
2261 buffer = self._metar.get_buffer()
2262 return buffer.get_text(buffer.get_start_iter(),
2263 buffer.get_end_iter(), True)
2264
2265 def setMETAR(self, metar):
2266 """Set the METAR."""
2267 self._metar.get_buffer().set_text(metar)
2268 self.metarEdited = False
2269
2270 def changeMETAR(self, metar):
2271 """Change the METAR as a result of an edit on one of the other
2272 pages."""
2273 self._updatingMETAR = True
2274 self._metar.get_buffer().set_text(metar)
2275 self._updatingMETAR = False
2276
2277 self._updateButton()
2278 self.metarEdited = True
2279
2280 def activate(self):
2281 """Activate the page."""
2282 if not self._departure:
2283 self._button.set_label(xstr("briefing_button"))
2284 self._button.set_has_tooltip(False)
2285 self._button.set_use_stock(False)
2286
2287 bookedFlight = self._wizard._bookedFlight
2288
2289 icao = bookedFlight.departureICAO if self._departure \
2290 else bookedFlight.arrivalICAO
2291 notams = self._wizard._departureNOTAMs if self._departure \
2292 else self._wizard._arrivalNOTAMs
2293 metar = self._wizard._departureMETAR if self._departure \
2294 else self._wizard._arrivalMETAR
2295
2296 self._notamsFrame.set_label(xstr("briefing_notams_template") % (icao,))
2297 buffer = self._notams.get_buffer()
2298 if notams is None:
2299 buffer.set_text(xstr("briefing_notams_failed"))
2300 elif not notams:
2301 buffer.set_text(xstr("briefing_notams_missing"))
2302 else:
2303 s = ""
2304 for notam in notams:
2305 s += str(notam)
2306 s += "-------------------- * --------------------\n"
2307 buffer.set_text(s)
2308
2309 self._metarFrame.set_label(xstr("briefing_metar_template") % (icao,))
2310 buffer = self._metar.get_buffer()
2311 self._updatingMETAR = True
2312 if metar is None:
2313 buffer.set_text("")
2314 self.setHelp(xstr("briefing_help_nometar"))
2315 else:
2316 buffer.set_text(metar)
2317 self._updatingMETAR = False
2318 self._updateButton()
2319
2320 label = self._metarFrame.get_label_widget()
2321 label.set_use_underline(True)
2322 label.set_mnemonic_widget(self._metar)
2323
2324 self.metarEdited = False
2325
2326 def _backClicked(self, button):
2327 """Called when the Back button is pressed."""
2328 self.goBack()
2329
2330 def _forwardClicked(self, button):
2331 """Called when the forward button is clicked."""
2332 if not self._departure:
2333 if not self._completed:
2334 self._wizard.gui.startMonitoring()
2335 self._button.set_label(xstr("button_next"))
2336 self._button.set_tooltip_text(xstr("button_next_tooltip"))
2337 self.complete()
2338
2339 self._wizard.nextPage()
2340
2341 def _metarChanged(self, buffer):
2342 """Called when the METAR has changed."""
2343 print "BriefingPage.metarChanged", self._updatingMETAR
2344 if not self._updatingMETAR:
2345 self.metarEdited = True
2346 self._updateButton()
2347 metar = buffer.get_text(buffer.get_start_iter(),
2348 buffer.get_end_iter(), True)
2349 self._wizard.metarChanged(metar, self)
2350
2351 def _metarInserted(self, textBuffer, iter, text, length):
2352 """Called when new characters are inserted into the METAR.
2353
2354 It uppercases all characters."""
2355 print "BriefingPage.metarInserted", self._updatingMETAR
2356 if not self._updatingMETAR:
2357 self._updatingMETAR = True
2358
2359 iter1 = iter.copy()
2360 iter1.backward_chars(length)
2361 textBuffer.delete(iter, iter1)
2362
2363 textBuffer.insert(iter, text.upper())
2364
2365 self._updatingMETAR = False
2366
2367 def _updateButton(self):
2368 """Update the sensitivity of the Next button based on the contents of
2369 the METAR field."""
2370 buffer = self._metar.get_buffer()
2371 self._button.set_sensitive(buffer.get_text(buffer.get_start_iter(),
2372 buffer.get_end_iter(),
2373 True)!="")
2374
2375
2376#-----------------------------------------------------------------------------
2377
2378class TakeoffPage(Page):
2379 """Page for entering the takeoff data."""
2380 def __init__(self, wizard):
2381 """Construct the takeoff page."""
2382 super(TakeoffPage, self).__init__(wizard, xstr("takeoff_title"),
2383 xstr("takeoff_help"),
2384 completedHelp = xstr("takeoff_chelp"))
2385
2386 self._forwardAllowed = False
2387
2388 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2389 xscale = 0.0, yscale = 0.0)
2390
2391 table = gtk.Table(9, 24)
2392 table.set_row_spacings(4)
2393 table.set_col_spacings(16)
2394 table.set_homogeneous(False)
2395 alignment.add(table)
2396 self.setMainWidget(alignment)
2397
2398 row = 0
2399
2400 label = gtk.Label(xstr("takeoff_metar"))
2401 label.set_use_underline(True)
2402 label.set_alignment(0.0, 0.5)
2403 table.attach(label, 0, 1, row, row+1)
2404
2405 self._metar = gtk.Entry()
2406 self._metar.set_width_chars(40)
2407 self._metar.set_tooltip_text(xstr("takeoff_metar_tooltip"))
2408 self._metar.connect("changed", self._metarChanged)
2409 self._metar.get_buffer().connect_after("inserted-text", self._metarInserted)
2410 table.attach(self._metar, 1, 24, row, row+1)
2411 label.set_mnemonic_widget(self._metar)
2412
2413 self._updatingMETAR = False
2414
2415 row += 1
2416
2417 label = gtk.Label(xstr("takeoff_runway"))
2418 label.set_use_underline(True)
2419 label.set_alignment(0.0, 0.5)
2420 table.attach(label, 0, 1, row, row+1)
2421
2422 self._runway = gtk.Entry()
2423 self._runway.set_width_chars(10)
2424 self._runway.set_tooltip_text(xstr("takeoff_runway_tooltip"))
2425 self._runway.connect("changed", self._upperChanged)
2426 table.attach(self._runway, 1, 3, row, row+1)
2427 label.set_mnemonic_widget(self._runway)
2428
2429 row += 1
2430
2431 label = gtk.Label(xstr("takeoff_sid"))
2432 label.set_use_underline(True)
2433 label.set_alignment(0.0, 0.5)
2434 table.attach(label, 0, 1, row, row+1)
2435
2436 if pygobject:
2437 self._sid = gtk.ComboBox.new_with_model_and_entry(comboModel)
2438 else:
2439 self._sid = gtk.ComboBoxEntry(comboModel)
2440
2441 self._sid.set_entry_text_column(0)
2442 self._sid.get_child().set_width_chars(10)
2443 self._sid.set_tooltip_text(xstr("takeoff_sid_tooltip"))
2444 self._sid.connect("changed", self._upperChangedComboBox)
2445 table.attach(self._sid, 1, 3, row, row+1)
2446 label.set_mnemonic_widget(self._sid)
2447
2448 row += 1
2449
2450 label = gtk.Label(xstr("takeoff_v1"))
2451 label.set_use_markup(True)
2452 label.set_use_underline(True)
2453 label.set_alignment(0.0, 0.5)
2454 table.attach(label, 0, 1, row, row+1)
2455
2456 self._v1 = IntegerEntry()
2457 self._v1.set_width_chars(4)
2458 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip_knots"))
2459 self._v1.connect("integer-changed", self._valueChanged)
2460 table.attach(self._v1, 2, 3, row, row+1)
2461 label.set_mnemonic_widget(self._v1)
2462
2463 self._v1Unit = gtk.Label(xstr("label_knots"))
2464 self._v1Unit.set_alignment(0.0, 0.5)
2465 table.attach(self._v1Unit, 3, 4, row, row+1)
2466
2467 row += 1
2468
2469 label = gtk.Label(xstr("takeoff_vr"))
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._vr = IntegerEntry()
2476 self._vr.set_width_chars(4)
2477 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip_knots"))
2478 self._vr.connect("integer-changed", self._valueChanged)
2479 table.attach(self._vr, 2, 3, row, row+1)
2480 label.set_mnemonic_widget(self._vr)
2481
2482 self._vrUnit = gtk.Label(xstr("label_knots"))
2483 self._vrUnit.set_alignment(0.0, 0.5)
2484 table.attach(self._vrUnit, 3, 4, row, row+1)
2485
2486 row += 1
2487
2488 label = gtk.Label(xstr("takeoff_v2"))
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._v2 = IntegerEntry()
2495 self._v2.set_width_chars(4)
2496 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip_knots"))
2497 self._v2.connect("integer-changed", self._valueChanged)
2498 table.attach(self._v2, 2, 3, row, row+1)
2499 label.set_mnemonic_widget(self._v2)
2500
2501 self._v2Unit = gtk.Label(xstr("label_knots"))
2502 self._v2Unit.set_alignment(0.0, 0.5)
2503 table.attach(self._v2Unit, 3, 4, row, row+1)
2504
2505 row += 1
2506
2507 self._derateType = acft.DERATE_NONE
2508
2509 self._derateLabel = gtk.Label()
2510 self._derateLabel.set_use_underline(True)
2511 self._derateLabel.set_markup(xstr("takeoff_derate_tupolev"))
2512 self._derateLabel.set_alignment(0.0, 0.5)
2513 table.attach(self._derateLabel, 0, 1, row, row+1)
2514
2515 self._derate = gtk.Alignment()
2516 table.attach(self._derate, 2, 4, row, row+1)
2517 self._derateWidget = None
2518 self._derateEntry = None
2519 self._derateUnit = None
2520 self._derateButtons = None
2521
2522 row += 1
2523
2524 self._antiIceOn = gtk.CheckButton(xstr("takeoff_antiice"))
2525 self._antiIceOn.set_use_underline(True)
2526 self._antiIceOn.set_tooltip_text(xstr("takeoff_antiice_tooltip"))
2527 table.attach(self._antiIceOn, 2, 4, row, row+1)
2528
2529 row += 1
2530
2531 self._rto = gtk.CheckButton(xstr("takeoff_rto"))
2532 self._rto.set_use_underline(True)
2533 self._rto.set_tooltip_text(xstr("takeoff_rto_tooltip"))
2534 self._rto.connect("toggled", self._rtoToggled)
2535 table.attach(self._rto, 2, 4, row, row+1, ypadding = 8)
2536
2537 self.addCancelFlightButton()
2538
2539 self.addPreviousButton(clicked = self._backClicked)
2540
2541 self._button = self.addNextButton(clicked = self._forwardClicked)
2542
2543 self._active = False
2544
2545 @property
2546 def runway(self):
2547 """Get the runway."""
2548 return self._runway.get_text()
2549
2550 @property
2551 def sid(self):
2552 """Get the SID."""
2553 text = self._sid.get_child().get_text()
2554 return text if self._sid.get_active()!=0 and text and text!="N/A" \
2555 else None
2556
2557 @property
2558 def v1(self):
2559 """Get the v1 speed."""
2560 return self._v1.get_int()
2561
2562 @property
2563 def vr(self):
2564 """Get the vr speed."""
2565 return self._vr.get_int()
2566
2567 @property
2568 def v2(self):
2569 """Get the v2 speed."""
2570 return self._v2.get_int()
2571
2572 @property
2573 def derate(self):
2574 """Get the derate value, if any."""
2575 if self._derateWidget is None:
2576 return None
2577 if self._derateType==acft.DERATE_BOEING:
2578 derate = self._derateEntry.get_text()
2579 return derate if derate else None
2580 elif self._derateType==acft.DERATE_EPR:
2581 derate = self._derateWidget.get_text()
2582 return derate if derate else None
2583 elif self._derateType==acft.DERATE_TUPOLEV:
2584 return acft.DERATE_TUPOLEV_NOMINAL \
2585 if self._derateButtons[0].get_active() \
2586 else acft.DERATE_TUPOLEV_TAKEOFF
2587 elif self._derateType==acft.DERATE_B462:
2588 return self._derateWidget.get_active()
2589 else:
2590 return None
2591
2592 @property
2593 def antiIceOn(self):
2594 """Get whether the anti-ice system has been turned on."""
2595 return self._antiIceOn.get_active()
2596
2597 @antiIceOn.setter
2598 def antiIceOn(self, value):
2599 """Set the anti-ice indicator."""
2600 self._antiIceOn.set_active(value)
2601
2602 @property
2603 def rtoIndicated(self):
2604 """Get whether the pilot has indicated if there was an RTO."""
2605 return self._rto.get_active()
2606
2607 def activate(self):
2608 """Activate the page."""
2609 print "TakeoffPage.activate"
2610
2611 self._updatingMETAR = True
2612 self._metar.get_buffer().set_text(self._wizard.departureMETAR, -1)
2613 self._updatingMETAR = False
2614
2615 self._runway.set_text("")
2616 self._runway.set_sensitive(True)
2617 self._sid.set_active(0)
2618 self._sid.set_sensitive(True)
2619 self._v1.set_int(None)
2620 self._v1.set_sensitive(True)
2621 self._vr.set_int(None)
2622 self._vr.set_sensitive(True)
2623 self._v2.set_int(None)
2624 self._v2.set_sensitive(True)
2625
2626 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
2627 speedUnit = xstr("label" + i18nSpeedUnit)
2628 self._v1Unit.set_text(speedUnit)
2629 self._vrUnit.set_text(speedUnit)
2630 self._v2Unit.set_text(speedUnit)
2631
2632 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip" + i18nSpeedUnit))
2633 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip" + i18nSpeedUnit))
2634 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip" + i18nSpeedUnit))
2635
2636 self._derateType = self._wizard.gui.flight.aircraft.derateType
2637
2638 self._setupDerateWidget()
2639
2640 self._rto.set_active(False)
2641 self._rto.set_sensitive(False)
2642
2643 self._button.set_sensitive(False)
2644 self._forwardAllowed = False
2645
2646 self._active = True
2647
2648 def allowForward(self):
2649 """Allow going to the next page."""
2650 print "TakeoffPage.allowForward"
2651 self._forwardAllowed = True
2652 self._updateForwardButton()
2653
2654 def reset(self):
2655 """Reset the page if the wizard is reset."""
2656 print "TakeoffPage.reset"
2657
2658 super(TakeoffPage, self).reset()
2659 self._v1.reset()
2660 self._vr.reset()
2661 self._v2.reset()
2662 self._hasDerate = False
2663 self._antiIceOn.set_active(False)
2664 self._active = False
2665
2666 def setRTOEnabled(self, enabled):
2667 """Set the RTO checkbox enabled or disabled."""
2668 if not enabled:
2669 self._rto.set_active(False)
2670 self._rto.set_sensitive(enabled)
2671
2672 def changeMETAR(self, metar):
2673 """Change the METAR as a result of an edit on one of the other
2674 pages."""
2675 if self._active:
2676 print "TakeoffPage.changeMETAR"
2677 self._updatingMETAR = True
2678 self._metar.get_buffer().set_text(metar, -1)
2679 self._updatingMETAR = False
2680
2681 self._updateForwardButton()
2682
2683 def _updateForwardButton(self):
2684 """Update the sensitivity of the forward button based on some conditions."""
2685 sensitive = self._forwardAllowed and \
2686 self._metar.get_text()!="" and \
2687 self._runway.get_text()!="" and \
2688 self.sid is not None and \
2689 self.v1 is not None and \
2690 self.vr is not None and \
2691 self.v2 is not None and \
2692 self.v1 <= self.vr and \
2693 self.vr <= self.v2 and \
2694 (self._derateType==acft.DERATE_NONE or
2695 self.derate is not None)
2696
2697 print "TakeoffPage._updateForwardButton: forwardAllowed:", self._forwardAllowed, ", sensitive:", sensitive
2698 if self._forwardAllowed:
2699 print " METAR: ", self._metar.get_text()
2700 print " runway: ", self._runway.get_text()
2701 print " SID:", self.sid
2702 print " V1:", self.v1
2703 print " VR:", self.vr
2704 print " V2:", self.v2
2705 print " derateType:", self._derateType
2706 print " derate:", self.derate
2707
2708 self._button.set_sensitive(sensitive)
2709
2710 def _valueChanged(self, widget, arg = None):
2711 """Called when the value of some widget has changed."""
2712 print "TakeoffPage._valueChanged"
2713
2714 self._updateForwardButton()
2715
2716 def _upperChanged(self, entry, arg = None):
2717 """Called when the value of some entry widget has changed and the value
2718 should be converted to uppercase."""
2719 print "TakeoffPage._upperChanged"
2720 entry.set_text(entry.get_text().upper())
2721 self._valueChanged(entry, arg)
2722
2723 def _upperChangedComboBox(self, comboBox):
2724 """Called for combo box widgets that must be converted to uppercase."""
2725 entry = comboBox.get_child()
2726 if comboBox.get_active()==-1:
2727 entry.set_text(entry.get_text().upper())
2728 self._valueChanged(entry)
2729
2730 def _derateChanged(self, entry):
2731 """Called when the value of the derate is changed."""
2732 print "TakeoffPage._derateChanged"
2733 self._updateForwardButton()
2734
2735 def _rtoToggled(self, button):
2736 """Called when the RTO check button is toggled."""
2737 self._wizard.rtoToggled(button.get_active())
2738
2739 def _backClicked(self, button):
2740 """Called when the Back button is pressed."""
2741 self.goBack()
2742
2743 def _forwardClicked(self, button):
2744 """Called when the forward button is clicked."""
2745 aircraft = self._wizard.gui.flight.aircraft
2746 aircraft.updateV1R2()
2747 if self.derate is not None:
2748 aircraft.updateDerate()
2749 aircraft.updateTakeoffAntiIce()
2750 self._wizard.nextPage()
2751
2752 def _setupDerateWidget(self):
2753 """Setup the derate widget."""
2754 if self._derateWidget is not None:
2755 self._derate.remove(self._derateWidget)
2756
2757 if self._derateType==acft.DERATE_BOEING:
2758 self._derateLabel.set_text(xstr("takeoff_derate_boeing"))
2759 self._derateLabel.set_use_underline(True)
2760 self._derateLabel.set_sensitive(True)
2761
2762 self._derateEntry = gtk.Entry()
2763 self._derateEntry.set_width_chars(7)
2764 self._derateEntry.set_tooltip_text(xstr("takeoff_derate_boeing_tooltip"))
2765 self._derateEntry.set_alignment(1.0)
2766 self._derateEntry.connect("changed", self._derateChanged)
2767 self._derateLabel.set_mnemonic_widget(self._derateEntry)
2768
2769 self._derateUnit = gtk.Label("%")
2770 self._derateUnit.set_alignment(0.0, 0.5)
2771
2772 self._derateWidget = gtk.Table(3, 1)
2773 self._derateWidget.set_row_spacings(4)
2774 self._derateWidget.set_col_spacings(16)
2775 self._derateWidget.set_homogeneous(False)
2776
2777 self._derateWidget.attach(self._derateEntry, 0, 2, 0, 1)
2778 self._derateWidget.attach(self._derateUnit, 2, 3, 0, 1)
2779
2780 self._derate.add(self._derateWidget)
2781 elif self._derateType==acft.DERATE_EPR:
2782 self._derateLabel.set_text("_EPR:")
2783 self._derateLabel.set_use_underline(True)
2784 self._derateLabel.set_sensitive(True)
2785
2786 self._derateWidget = gtk.Entry()
2787 self._derateWidget.set_width_chars(7)
2788 self._derateWidget.set_tooltip_text(xstr("takeoff_derate_epr_tooltip"))
2789 self._derateWidget.set_alignment(1.0)
2790 self._derateWidget.connect("changed", self._derateChanged)
2791 self._derateLabel.set_mnemonic_widget(self._derateWidget)
2792
2793 self._derate.add(self._derateWidget)
2794 elif self._derateType==acft.DERATE_TUPOLEV:
2795 self._derateLabel.set_text(xstr("takeoff_derate_tupolev"))
2796 self._derateLabel.set_use_underline(True)
2797 self._derateLabel.set_sensitive(True)
2798
2799 if pygobject:
2800 nominal = gtk.RadioButton.\
2801 new_with_label_from_widget(None,
2802 xstr("takeoff_derate_tupolev_nominal"))
2803 else:
2804 nominal = gtk.RadioButton(None,
2805 xstr("takeoff_derate_tupolev_nominal"))
2806 nominal.set_use_underline(True)
2807 nominal.set_tooltip_text(xstr("takeoff_derate_tupolev_nominal_tooltip"))
2808 nominal.connect("toggled", self._derateChanged)
2809
2810 if pygobject:
2811 takeoff = gtk.RadioButton.\
2812 new_with_label_from_widget(nominal,
2813 xstr("takeoff_derate_tupolev_takeoff"))
2814 else:
2815 takeoff = gtk.RadioButton(nominal,
2816 xstr("takeoff_derate_tupolev_takeoff"))
2817
2818 takeoff.set_use_underline(True)
2819 takeoff.set_tooltip_text(xstr("takeoff_derate_tupolev_takeoff_tooltip"))
2820 takeoff.connect("toggled", self._derateChanged)
2821
2822 self._derateButtons = [nominal, takeoff]
2823
2824 self._derateWidget = gtk.HBox()
2825 self._derateWidget.pack_start(nominal, False, False, 4)
2826 self._derateWidget.pack_start(takeoff, False, False, 4)
2827
2828 self._derate.add(self._derateWidget)
2829 elif self._derateType==acft.DERATE_B462:
2830 self._derateLabel.set_text("")
2831
2832 self._derateWidget = gtk.CheckButton(xstr("takeoff_derate_b462"))
2833 self._derateWidget.set_tooltip_text(xstr("takeoff_derate_b462_tooltip"))
2834 self._derateWidget.set_use_underline(True)
2835 self._derate.add(self._derateWidget)
2836 else:
2837 self._derateWidget = None
2838 self._derateLabel.set_text("")
2839 self._derateLabel.set_sensitive(False)
2840
2841 def _metarChanged(self, entry):
2842 """Called when the METAR has changed."""
2843 print "TakeoffPage.metarChanged", self._updatingMETAR
2844 if not self._updatingMETAR:
2845 self._updateForwardButton()
2846 self._wizard.metarChanged(entry.get_text(), self)
2847
2848 def _metarInserted(self, buffer, position, text, length):
2849 """Called when new characters are inserted into the METAR.
2850
2851 It uppercases all characters."""
2852 print "TakeoffPage.metarInserted", self._updatingMETAR
2853 if not self._updatingMETAR:
2854 self._updatingMETAR = True
2855
2856 buffer.delete_text(position, length)
2857 buffer.insert_text(position, text.upper(), length)
2858
2859 self._updatingMETAR = False
2860
2861#-----------------------------------------------------------------------------
2862
2863class CruisePage(Page):
2864 """The page containing the flight level that might change during flight."""
2865 def __init__(self, wizard):
2866 """Construct the page."""
2867 super(CruisePage, self).__init__(wizard, xstr("cruise_title"),
2868 xstr("cruise_help"))
2869
2870 self._loggable = False
2871 self._loggedCruiseLevel = 240
2872 self._activated = False
2873
2874 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
2875 xscale = 0.0, yscale = 1.0)
2876
2877 mainBox = gtk.VBox()
2878 alignment.add(mainBox)
2879 self.setMainWidget(alignment)
2880
2881 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
2882 xscale = 0.0, yscale = 0.0)
2883 mainBox.pack_start(alignment, False, False, 16)
2884
2885 levelBox = gtk.HBox()
2886
2887 label = gtk.Label(xstr("route_level"))
2888 label.set_use_underline(True)
2889 levelBox.pack_start(label, True, True, 0)
2890
2891 self._cruiseLevel = gtk.SpinButton()
2892 self._cruiseLevel.set_increments(step = 10, page = 100)
2893 self._cruiseLevel.set_range(min = 50, max = 500)
2894 self._cruiseLevel.set_tooltip_text(xstr("cruise_route_level_tooltip"))
2895 self._cruiseLevel.set_numeric(True)
2896 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
2897 label.set_mnemonic_widget(self._cruiseLevel)
2898
2899 levelBox.pack_start(self._cruiseLevel, False, False, 8)
2900
2901 self._updateButton = gtk.Button(xstr("cruise_route_level_update"));
2902 self._updateButton.set_use_underline(True)
2903 self._updateButton.set_tooltip_text(xstr("cruise_route_level_update_tooltip"))
2904 self._updateButton.connect("clicked", self._updateButtonClicked)
2905
2906 levelBox.pack_start(self._updateButton, False, False, 16)
2907
2908 mainBox.pack_start(levelBox, False, False, 0)
2909
2910 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
2911 xscale = 0.0, yscale = 1.0)
2912 mainBox.pack_start(alignment, True, True, 0)
2913
2914 self.addCancelFlightButton()
2915
2916 self._backButton = self.addPreviousButton(clicked = self._backClicked)
2917 self._button = self.addNextButton(clicked = self._forwardClicked)
2918
2919 @property
2920 def activated(self):
2921 """Determine if the page is already activated or not."""
2922 return self._activated
2923
2924 @property
2925 def cruiseLevel(self):
2926 """Get the cruise level."""
2927 return self._loggedCruiseLevel
2928
2929 @property
2930 def loggableCruiseLevel(self):
2931 """Get the cruise level which should be logged."""
2932 return self._cruiseLevel.get_value_as_int()
2933
2934 def setLoggable(self, loggable):
2935 """Set whether the cruise altitude can be logged."""
2936 self._loggable = loggable
2937 self._updateButtons()
2938
2939 def activate(self):
2940 """Setup the route from the booked flight."""
2941 self._loggedCruiseLevel = self._wizard.filedCruiseLevel
2942 self._cruiseLevel.set_value(self._loggedCruiseLevel)
2943 self._activated = True
2944
2945 def reset(self):
2946 """Reset the page."""
2947 self._loggable = False
2948 self._activated = False
2949 super(CruisePage, self).reset()
2950
2951 def _updateButtons(self):
2952 """Update the sensitivity of the buttons."""
2953 self._updateButton.set_sensitive(self._loggable and
2954 self.loggableCruiseLevel!=
2955 self._loggedCruiseLevel)
2956
2957 def _cruiseLevelChanged(self, spinButton):
2958 """Called when the cruise level has changed."""
2959 self._updateButtons()
2960
2961 def _updateButtonClicked(self, button):
2962 """Called when the update button is clicked."""
2963 if self._wizard.cruiseLevelChanged():
2964 self._loggedCruiseLevel = self.loggableCruiseLevel
2965 self._updateButtons()
2966
2967 def _backClicked(self, button):
2968 """Called when the Back button is pressed."""
2969 self.goBack()
2970
2971 def _forwardClicked(self, button):
2972 """Called when the Forward button is clicked."""
2973 self._wizard.nextPage()
2974
2975#-----------------------------------------------------------------------------
2976
2977class LandingPage(Page):
2978 """Page for entering landing data."""
2979 def __init__(self, wizard):
2980 """Construct the landing page."""
2981 super(LandingPage, self).__init__(wizard, xstr("landing_title"),
2982 xstr("landing_help"),
2983 completedHelp = xstr("landing_chelp"))
2984
2985 self._flightEnded = False
2986
2987 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2988 xscale = 0.0, yscale = 0.0)
2989
2990 table = gtk.Table(7, 24)
2991 table.set_row_spacings(4)
2992 table.set_col_spacings(16)
2993 table.set_homogeneous(False)
2994 alignment.add(table)
2995 self.setMainWidget(alignment)
2996
2997 row = 0
2998
2999 label = gtk.Label(xstr("landing_metar"))
3000 label.set_use_underline(True)
3001 label.set_alignment(0.0, 0.5)
3002 table.attach(label, 0, 1, row, row+1)
3003
3004 self._metar = gtk.Entry()
3005 self._metar.set_width_chars(40)
3006 self._metar.set_tooltip_text(xstr("landing_metar_tooltip"))
3007 self._metar.connect("changed", self._metarChanged)
3008 self._metar.get_buffer().connect_after("inserted-text", self._metarInserted)
3009 table.attach(self._metar, 1, 24, row, row+1)
3010 label.set_mnemonic_widget(self._metar)
3011
3012 self._updatingMETAR = False
3013
3014 row += 1
3015
3016 label = gtk.Label(xstr("landing_star"))
3017 label.set_use_underline(True)
3018 label.set_alignment(0.0, 0.5)
3019 table.attach(label, 1, 2, row, row + 1)
3020
3021 if pygobject:
3022 self._star = gtk.ComboBox.new_with_model_and_entry(comboModel)
3023 else:
3024 self._star = gtk.ComboBoxEntry(comboModel)
3025
3026 self._star.set_entry_text_column(0)
3027 self._star.get_child().set_width_chars(10)
3028 self._star.set_tooltip_text(xstr("landing_star_tooltip"))
3029 self._star.connect("changed", self._upperChangedComboBox)
3030 self._star.set_sensitive(False)
3031 table.attach(self._star, 2, 4, row, row + 1)
3032 label.set_mnemonic_widget(self._star)
3033
3034 row += 1
3035
3036 label = gtk.Label(xstr("landing_transition"))
3037 label.set_use_underline(True)
3038 label.set_alignment(0.0, 0.5)
3039 table.attach(label, 1, 2, row, row + 1)
3040
3041 if pygobject:
3042 self._transition = gtk.ComboBox.new_with_model_and_entry(comboModel)
3043 else:
3044 self._transition = gtk.ComboBoxEntry(comboModel)
3045
3046 self._transition.set_entry_text_column(0)
3047 self._transition.get_child().set_width_chars(10)
3048 self._transition.set_tooltip_text(xstr("landing_transition_tooltip"))
3049 self._transition.connect("changed", self._upperChangedComboBox)
3050 self._transition.set_sensitive(False)
3051 table.attach(self._transition, 2, 4, row, row + 1)
3052 label.set_mnemonic_widget(self._transition)
3053
3054 row += 1
3055
3056 label = gtk.Label(xstr("landing_runway"))
3057 label.set_use_underline(True)
3058 label.set_alignment(0.0, 0.5)
3059 table.attach(label, 1, 2, row, row + 1)
3060
3061 self._runway = gtk.Entry()
3062 self._runway.set_width_chars(10)
3063 self._runway.set_tooltip_text(xstr("landing_runway_tooltip"))
3064 self._runway.connect("changed", self._upperChanged)
3065 table.attach(self._runway, 2, 4, row, row + 1)
3066 label.set_mnemonic_widget(self._runway)
3067
3068 row += 1
3069
3070 label = gtk.Label(xstr("landing_approach"))
3071 label.set_use_underline(True)
3072 label.set_alignment(0.0, 0.5)
3073 table.attach(label, 1, 2, row, row + 1)
3074
3075 self._approachType = gtk.Entry()
3076 self._approachType.set_width_chars(10)
3077 self._approachType.set_tooltip_text(xstr("landing_approach_tooltip"))
3078 self._approachType.connect("changed", self._upperChanged)
3079 table.attach(self._approachType, 2, 4, row, row + 1)
3080 label.set_mnemonic_widget(self._approachType)
3081
3082 row += 1
3083
3084 label = gtk.Label(xstr("landing_vref"))
3085 label.set_use_markup(True)
3086 label.set_use_underline(True)
3087 label.set_alignment(0.0, 0.5)
3088 table.attach(label, 1, 2, row, row + 1)
3089
3090 self._vref = IntegerEntry()
3091 self._vref.set_width_chars(5)
3092 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip_knots"))
3093 self._vref.connect("integer-changed", self._vrefChanged)
3094 table.attach(self._vref, 3, 4, row, row + 1)
3095 label.set_mnemonic_widget(self._vref)
3096
3097 self._vrefUnit = gtk.Label(xstr("label_knots"))
3098 table.attach(self._vrefUnit, 4, 5, row, row + 1)
3099
3100 row += 1
3101
3102 self._antiIceOn = gtk.CheckButton(xstr("landing_antiice"))
3103 self._antiIceOn.set_use_underline(True)
3104 self._antiIceOn.set_tooltip_text(xstr("landing_antiice_tooltip"))
3105 table.attach(self._antiIceOn, 3, 5, row, row + 1)
3106
3107 self.addCancelFlightButton()
3108
3109 self.addPreviousButton(clicked = self._backClicked)
3110
3111 self._button = self.addNextButton(clicked = self._forwardClicked)
3112
3113 self._active = False
3114
3115 @property
3116 def star(self):
3117 """Get the STAR or None if none entered."""
3118 text = self._star.get_child().get_text()
3119 return text if self._star.get_active()!=0 and text and text!="N/A" \
3120 else None
3121
3122 @property
3123 def transition(self):
3124 """Get the transition or None if none entered."""
3125 text = self._transition.get_child().get_text()
3126 return text if self._transition.get_active()!=0 and text and text!="N/A" \
3127 else None
3128
3129 @property
3130 def approachType(self):
3131 """Get the approach type."""
3132 return self._approachType.get_text()
3133
3134 @property
3135 def runway(self):
3136 """Get the runway."""
3137 return self._runway.get_text()
3138
3139 @property
3140 def vref(self):
3141 """Return the landing reference speed."""
3142 return self._vref.get_int()
3143
3144 @property
3145 def antiIceOn(self):
3146 """Get whether the anti-ice system has been turned on."""
3147 return self._antiIceOn.get_active()
3148
3149 @antiIceOn.setter
3150 def antiIceOn(self, value):
3151 """Set the anti-ice indicator."""
3152 self._antiIceOn.set_active(value)
3153
3154 def reset(self):
3155 """Reset the page if the wizard is reset."""
3156 super(LandingPage, self).reset()
3157 self._vref.reset()
3158 self._antiIceOn.set_active(False)
3159 self._flightEnded = False
3160 self._active = False
3161
3162 def activate(self):
3163 """Called when the page is activated."""
3164 self._updatingMETAR = True
3165 self._metar.get_buffer().set_text(self._wizard.arrivalMETAR, -1)
3166 self._updatingMETAR = False
3167
3168 self._star.set_active(0)
3169 self._star.set_sensitive(True)
3170
3171 self._transition.set_active(0)
3172 self._transition.set_sensitive(True)
3173
3174 self._runway.set_text("")
3175 self._runway.set_sensitive(True)
3176
3177 self._approachType.set_text("")
3178 self._approachType.set_sensitive(True)
3179
3180 self._vref.set_int(None)
3181 self._vref.set_sensitive(True)
3182
3183 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
3184 speedUnit = xstr("label" + i18nSpeedUnit)
3185 self._vrefUnit.set_text(speedUnit)
3186
3187 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip" +
3188 i18nSpeedUnit))
3189
3190 self._updateForwardButton()
3191
3192 self._active = True
3193
3194 def flightEnded(self):
3195 """Called when the flight has ended."""
3196 super(LandingPage, self).flightEnded()
3197 self._flightEnded = True
3198 self._updateForwardButton()
3199
3200 def changeMETAR(self, metar):
3201 """Change the METAR as a result of an edit on one of the other
3202 pages."""
3203 if self._active:
3204 print "LandingPage.changeMETAR"
3205 self._updatingMETAR = True
3206 self._metar.get_buffer().set_text(metar, -1)
3207 self._updatingMETAR = False
3208
3209 self._updateForwardButton()
3210
3211 def _updateForwardButton(self):
3212 """Update the sensitivity of the forward button."""
3213 sensitive = self._flightEnded and \
3214 self._metar.get_text()!="" and \
3215 (self.star is not None or
3216 self.transition is not None) and \
3217 self._runway.get_text()!="" and \
3218 self._approachType.get_text()!="" and \
3219 self.vref is not None
3220 self._button.set_sensitive(sensitive)
3221
3222 def _upperChanged(self, entry):
3223 """Called for entry widgets that must be converted to uppercase."""
3224 entry.set_text(entry.get_text().upper())
3225 self._updateForwardButton()
3226
3227 def _upperChangedComboBox(self, comboBox):
3228 """Called for combo box widgets that must be converted to uppercase."""
3229 if comboBox.get_active()==-1:
3230 entry = comboBox.get_child()
3231 entry.set_text(entry.get_text().upper())
3232 self._updateForwardButton()
3233
3234 def _vrefChanged(self, widget, value):
3235 """Called when the Vref has changed."""
3236 self._updateForwardButton()
3237
3238 def _backClicked(self, button):
3239 """Called when the Back button is pressed."""
3240 self.goBack()
3241
3242 def _forwardClicked(self, button):
3243 """Called when the forward button is clicked."""
3244 aircraft = self._wizard.gui.flight.aircraft
3245 aircraft.updateVRef()
3246 aircraft.updateLandingAntiIce()
3247 if self._wizard.gui.config.onlineGateSystem and \
3248 self._wizard.loggedIn and not self._completed and \
3249 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
3250 not self._wizard.entranceExam:
3251 self._wizard.getFleet(callback = self._fleetRetrieved,
3252 force = True)
3253 else:
3254 self._wizard.nextPage()
3255
3256 def _fleetRetrieved(self, fleet):
3257 """Callback for the fleet retrieval."""
3258 self._wizard.nextPage()
3259
3260 def _metarChanged(self, entry):
3261 """Called when the METAR has changed."""
3262 print "LandingPage.metarChanged", self._updatingMETAR
3263 if not self._updatingMETAR:
3264 self._updateForwardButton()
3265 self._wizard.metarChanged(entry.get_text(), self)
3266
3267 def _metarInserted(self, buffer, position, text, length):
3268 """Called when new characters are inserted into the METAR.
3269
3270 It uppercases all characters."""
3271 print "LandingPage.metarInserted", self._updatingMETAR
3272 if not self._updatingMETAR:
3273 self._updatingMETAR = True
3274
3275 buffer.delete_text(position, length)
3276 buffer.insert_text(position, text.upper(), length)
3277
3278 self._updatingMETAR = False
3279
3280#-----------------------------------------------------------------------------
3281
3282class FinishPage(Page):
3283 """Flight finish page."""
3284 _flightTypes = [ ("flighttype_scheduled", const.FLIGHTTYPE_SCHEDULED),
3285 ("flighttype_ot", const.FLIGHTTYPE_OLDTIMER),
3286 ("flighttype_vip", const.FLIGHTTYPE_VIP),
3287 ("flighttype_charter", const.FLIGHTTYPE_CHARTER) ]
3288
3289 def __init__(self, wizard):
3290 """Construct the finish page."""
3291 help = xstr("finish_help") + xstr("finish_help_goodtime")
3292 super(FinishPage, self).__init__(wizard, xstr("finish_title"), help)
3293
3294 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3295 xscale = 0.0, yscale = 0.0)
3296
3297 table = gtk.Table(10, 2)
3298 table.set_row_spacings(4)
3299 table.set_col_spacings(16)
3300 table.set_homogeneous(False)
3301 alignment.add(table)
3302 self.setMainWidget(alignment)
3303
3304 row = 0
3305
3306 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3307 label = gtk.Label(xstr("finish_rating"))
3308 labelAlignment.add(label)
3309 table.attach(labelAlignment, 0, 1, row, row+1)
3310
3311 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3312 self._flightRating = gtk.Label()
3313 self._flightRating.set_width_chars(8)
3314 self._flightRating.set_alignment(0.0, 0.5)
3315 self._flightRating.set_use_markup(True)
3316 labelAlignment.add(self._flightRating)
3317 table.attach(labelAlignment, 1, 2, row, row+1)
3318
3319 row += 1
3320
3321 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3322 label = gtk.Label(xstr("finish_dep_time"))
3323 labelAlignment.add(label)
3324 table.attach(labelAlignment, 0, 1, row, row+1)
3325
3326 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3327 self._depTime = gtk.Label()
3328 self._depTime.set_width_chars(13)
3329 self._depTime.set_alignment(0.0, 0.5)
3330 self._depTime.set_use_markup(True)
3331 self._depTime.set_tooltip_markup(xstr("finish_dep_time_tooltip"))
3332 labelAlignment.add(self._depTime)
3333 table.attach(labelAlignment, 1, 2, row, row+1)
3334
3335 row += 1
3336
3337 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3338 label = gtk.Label(xstr("finish_flight_time"))
3339 labelAlignment.add(label)
3340 table.attach(labelAlignment, 0, 1, row, row+1)
3341
3342 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3343 self._flightTime = gtk.Label()
3344 self._flightTime.set_width_chars(10)
3345 self._flightTime.set_alignment(0.0, 0.5)
3346 self._flightTime.set_use_markup(True)
3347 labelAlignment.add(self._flightTime)
3348 table.attach(labelAlignment, 1, 2, row, row+1)
3349
3350 row += 1
3351
3352 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3353 label = gtk.Label(xstr("finish_block_time"))
3354 labelAlignment.add(label)
3355 table.attach(labelAlignment, 0, 1, row, row+1)
3356
3357 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3358 self._blockTime = gtk.Label()
3359 self._blockTime.set_width_chars(10)
3360 self._blockTime.set_alignment(0.0, 0.5)
3361 self._blockTime.set_use_markup(True)
3362 labelAlignment.add(self._blockTime)
3363 table.attach(labelAlignment, 1, 2, row, row+1)
3364
3365 row += 1
3366
3367 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3368 label = gtk.Label(xstr("finish_arr_time"))
3369 labelAlignment.add(label)
3370 table.attach(labelAlignment, 0, 1, row, row+1)
3371
3372 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3373 self._arrTime = gtk.Label()
3374 self._arrTime.set_width_chars(13)
3375 self._arrTime.set_alignment(0.0, 0.5)
3376 self._arrTime.set_use_markup(True)
3377 self._arrTime.set_tooltip_markup(xstr("finish_arr_time_tooltip"))
3378 labelAlignment.add(self._arrTime)
3379 table.attach(labelAlignment, 1, 2, row, row+1)
3380
3381 row += 1
3382
3383 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3384 label = gtk.Label(xstr("finish_distance"))
3385 labelAlignment.add(label)
3386 table.attach(labelAlignment, 0, 1, row, row+1)
3387
3388 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3389 self._distanceFlown = gtk.Label()
3390 self._distanceFlown.set_width_chars(10)
3391 self._distanceFlown.set_alignment(0.0, 0.5)
3392 self._distanceFlown.set_use_markup(True)
3393 labelAlignment.add(self._distanceFlown)
3394 table.attach(labelAlignment, 1, 2, row, row+1)
3395
3396 row += 1
3397
3398 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
3399 label = gtk.Label(xstr("finish_fuel"))
3400 labelAlignment.add(label)
3401 table.attach(labelAlignment, 0, 1, row, row+1)
3402
3403 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3404 self._fuelUsed = gtk.Label()
3405 self._fuelUsed.set_width_chars(10)
3406 self._fuelUsed.set_alignment(0.0, 0.5)
3407 self._fuelUsed.set_use_markup(True)
3408 labelAlignment.add(self._fuelUsed)
3409 table.attach(labelAlignment, 1, 2, row, row+1)
3410
3411 row += 1
3412
3413 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
3414 yalign = 0.5, yscale = 0.0)
3415 label = gtk.Label(xstr("finish_type"))
3416 label.set_use_underline(True)
3417 labelAlignment.add(label)
3418 table.attach(labelAlignment, 0, 1, row, row+1)
3419
3420 flightTypeModel = gtk.ListStore(str, int)
3421 for (name, type) in FinishPage._flightTypes:
3422 flightTypeModel.append([xstr(name), type])
3423
3424 self._flightType = gtk.ComboBox(model = flightTypeModel)
3425 renderer = gtk.CellRendererText()
3426 self._flightType.pack_start(renderer, True)
3427 self._flightType.add_attribute(renderer, "text", 0)
3428 self._flightType.set_tooltip_text(xstr("finish_type_tooltip"))
3429 self._flightType.set_active(0)
3430 self._flightType.connect("changed", self._flightTypeChanged)
3431 flightTypeAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3432 flightTypeAlignment.add(self._flightType)
3433 table.attach(flightTypeAlignment, 1, 2, row, row+1)
3434 label.set_mnemonic_widget(self._flightType)
3435
3436 row += 1
3437
3438 self._onlineFlight = gtk.CheckButton(xstr("finish_online"))
3439 self._onlineFlight.set_use_underline(True)
3440 self._onlineFlight.set_tooltip_text(xstr("finish_online_tooltip"))
3441 onlineFlightAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
3442 onlineFlightAlignment.add(self._onlineFlight)
3443 table.attach(onlineFlightAlignment, 1, 2, row, row + 1)
3444
3445 row += 1
3446
3447 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
3448 yalign = 0.5, yscale = 0.0)
3449 self._gateLabel = gtk.Label(xstr("finish_gate"))
3450 self._gateLabel.set_use_underline(True)
3451 labelAlignment.add(self._gateLabel)
3452 table.attach(labelAlignment, 0, 1, row, row+1)
3453
3454 self._gatesModel = gtk.ListStore(str)
3455
3456 self._gate = gtk.ComboBox(model = self._gatesModel)
3457 renderer = gtk.CellRendererText()
3458 self._gate.pack_start(renderer, True)
3459 self._gate.add_attribute(renderer, "text", 0)
3460 self._gate.set_tooltip_text(xstr("finish_gate_tooltip"))
3461 self._gate.connect("changed", self._gateChanged)
3462 gateAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
3463 gateAlignment.add(self._gate)
3464 table.attach(gateAlignment, 1, 2, row, row+1)
3465 self._gateLabel.set_mnemonic_widget(self._gate)
3466
3467 self.addButton(xstr("finish_newFlight"),
3468 sensitive = True,
3469 clicked = self._newFlightClicked,
3470 tooltip = xstr("finish_newFlight_tooltip"),
3471 padding = 16)
3472
3473 self.addPreviousButton(clicked = self._backClicked)
3474
3475 self._saveButton = self.addButton(xstr("finish_save"),
3476 sensitive = False,
3477 clicked = self._saveClicked,
3478 tooltip = xstr("finish_save_tooltip"))
3479 self._savePIREPDialog = None
3480 self._lastSavePath = None
3481
3482 self._tooBigTimeDifference = False
3483 self._deferredAutoSave = False
3484 self._pirepSaved = False
3485 self._pirepSent = False
3486
3487 self._sendButton = self.addButton(xstr("sendPIREP"), default = True,
3488 sensitive = False,
3489 clicked = self._sendClicked,
3490 tooltip = xstr("sendPIREP_tooltip"))
3491
3492 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 10*60.0)
3493 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 20*60.0)
3494 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 0*60.0)
3495 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), (23*60.0+50)*60.0)
3496 # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (1*60.0+5)*60.0)
3497 # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (0*60.0+50)*60.0)
3498 # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (0*60.0+5)*60.0)
3499 # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (23*60.0+45)*60.0)
3500
3501 @property
3502 def flightType(self):
3503 """Get the flight type."""
3504 index = self._flightType.get_active()
3505 return None if index<0 else self._flightType.get_model()[index][1]
3506
3507 @property
3508 def online(self):
3509 """Get whether the flight was an online flight or not."""
3510 return self._onlineFlight.get_active()
3511
3512 def activate(self):
3513 """Activate the page."""
3514 self._deferredAutoSave = False
3515 self._pirepSaved = False
3516 self._pirepSent = False
3517
3518 flight = self._wizard.gui._flight
3519 rating = flight.logger.getRating()
3520 if rating<0:
3521 self._flightRating.set_markup('<b><span foreground="red">NO GO</span></b>')
3522 else:
3523 self._flightRating.set_markup("<b>%.1f %%</b>" % (rating,))
3524
3525 flightLength = flight.flightTimeEnd - flight.flightTimeStart
3526 self._flightTime.set_markup("<b>%s</b>" % \
3527 (util.getTimeIntervalString(flightLength),))
3528
3529 blockLength = flight.blockTimeEnd - flight.blockTimeStart
3530 self._blockTime.set_markup("<b>%s</b>" % \
3531 (util.getTimeIntervalString(blockLength),))
3532
3533 self._distanceFlown.set_markup("<b>%.2f NM</b>" % \
3534 (flight.flownDistance,))
3535
3536 self._fuelUsed.set_markup("<b>%.0f kg</b>" % \
3537 (flight.startFuel - flight.endFuel,))
3538
3539 self._flightType.set_active(-1)
3540 self._onlineFlight.set_active(self._wizard.loggedIn)
3541
3542 self._gatesModel.clear()
3543 if self._wizard.gui.config.onlineGateSystem and \
3544 self._wizard.loggedIn and \
3545 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
3546 not self._wizard.entranceExam:
3547 occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
3548 for gate in lhbpGates.gates:
3549 if gate.isAvailable(lhbpGates, occupiedGateNumbers):
3550 self._gatesModel.append([gate.number])
3551 self._gateLabel.set_sensitive(True)
3552 self._gate.set_sensitive(True)
3553 self._gate.set_active(-1)
3554 else:
3555 self._gateLabel.set_sensitive(False)
3556 self._gate.set_sensitive(False)
3557
3558 self._updateTimes()
3559
3560 def updateButtons(self):
3561 """Update the sensitivity state of the buttons."""
3562 gui = self._wizard.gui
3563 faultsExplained = gui.faultsFullyExplained
3564 timesCorrect = self.flightType is None or \
3565 not self._tooBigTimeDifference or \
3566 gui.hasComments or gui.hasDelayCode
3567 sensitive = gui.flight is not None and \
3568 gui.flight.stage==const.STAGE_END and \
3569 self._flightType.get_active()>=0 and \
3570 (self._gatesModel.get_iter_first() is None or
3571 self._gate.get_active()>=0) and \
3572 faultsExplained and timesCorrect
3573
3574 self._updateHelp(faultsExplained, timesCorrect)
3575
3576 wasSensitive = self._saveButton.get_sensitive()
3577
3578 if gui.config.pirepAutoSave and sensitive and not wasSensitive:
3579 if gui.isWizardActive():
3580 self._autoSavePIREP()
3581 else:
3582 self._deferredAutoSave = True
3583
3584 if not sensitive:
3585 self._deferredAutoSave = False
3586
3587 self._saveButton.set_sensitive(sensitive)
3588 self._sendButton.set_sensitive(sensitive and
3589 self._wizard.bookedFlight.id is not None)
3590
3591 def grabDefault(self):
3592 """If the page has a default button, make it the default one."""
3593 super(FinishPage, self).grabDefault()
3594 if self._deferredAutoSave:
3595 self._autoSavePIREP()
3596 self._deferredAutoSave = False
3597
3598 def _autoSavePIREP(self):
3599 """Perform the automatic saving of the PIREP."""
3600 self._lastSavePath = os.path.join(self._wizard.gui.config.pirepDirectory,
3601 self._getDefaultPIREPName())
3602 self._lastSavePath = text2unicode(self._lastSavePath)
3603 self._savePIREP(automatic = True)
3604
3605 def _backClicked(self, button):
3606 """Called when the Back button is pressed."""
3607 self.goBack()
3608
3609 def _flightTypeChanged(self, comboBox):
3610 """Called when the flight type has changed."""
3611 self._updateTimes()
3612
3613 def _gateChanged(self, comboBox):
3614 """Called when the arrival gate has changed."""
3615 self.updateButtons()
3616
3617 def _newFlightClicked(self, button):
3618 """Called when the new flight button is clicked."""
3619 gui = self._wizard.gui
3620 if not self._pirepSent and not self._pirepSaved:
3621 dialog = gtk.MessageDialog(parent = gui.mainWindow,
3622 type = MESSAGETYPE_QUESTION,
3623 message_format = xstr("finish_newFlight_question"))
3624
3625 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
3626 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
3627
3628 dialog.set_title(WINDOW_TITLE_BASE)
3629 result = dialog.run()
3630 dialog.hide()
3631 if result!=RESPONSETYPE_YES:
3632 return
3633
3634 gui.reset()
3635
3636 def _getDefaultPIREPName(self):
3637 """Get the default name of the PIREP."""
3638 gui = self._wizard.gui
3639
3640 bookedFlight = gui.bookedFlight
3641 tm = time.gmtime()
3642
3643 pilotID = self._wizard.pilotID
3644 if pilotID: pilotID += " "
3645 return "%s%s %02d%02d %s-%s.pirep" % \
3646 (pilotID, str(bookedFlight.departureTime.date()),
3647 tm.tm_hour, tm.tm_min,
3648 bookedFlight.departureICAO, bookedFlight.arrivalICAO)
3649
3650
3651 def _saveClicked(self, button):
3652 """Called when the Save PIREP button is clicked."""
3653 gui = self._wizard.gui
3654
3655 fileName = self._getDefaultPIREPName()
3656
3657 dialog = self._getSaveDialog()
3658
3659 if self._lastSavePath is None:
3660 pirepDirectory = gui.config.pirepDirectory
3661 if pirepDirectory is not None:
3662 dialog.set_current_folder(pirepDirectory)
3663 else:
3664 dialog.set_current_folder(os.path.dirname(self._lastSavePath))
3665
3666 dialog.set_current_name(fileName)
3667 result = dialog.run()
3668 dialog.hide()
3669
3670 if result==RESPONSETYPE_OK:
3671 self._lastSavePath = text2unicode(dialog.get_filename())
3672 self._savePIREP()
3673
3674 def _savePIREP(self, automatic = False):
3675 """Perform the saving of the PIREP."""
3676
3677 gui = self._wizard.gui
3678
3679 if automatic:
3680 gui.beginBusy(xstr("finish_autosave_busy"))
3681
3682 pirep = PIREP(gui.flight)
3683 error = pirep.save(self._lastSavePath)
3684
3685 if automatic:
3686 gui.endBusy()
3687
3688 if error:
3689 type = MESSAGETYPE_ERROR
3690 message = xstr("finish_save_failed")
3691 secondary = xstr("finish_save_failed_sec") % (text2unicode(error),)
3692 else:
3693 type = MESSAGETYPE_INFO
3694 message = xstr("finish_save_done")
3695 if automatic:
3696 secondary = xstr("finish_save_done_sec") % (self._lastSavePath,)
3697 else:
3698 secondary = None
3699 self._pirepSaved = True
3700
3701 dialog = gtk.MessageDialog(parent = gui.mainWindow,
3702 type = type, message_format = message)
3703 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
3704 dialog.set_title(WINDOW_TITLE_BASE)
3705 if secondary is not None:
3706 dialog.format_secondary_markup(secondary)
3707
3708 dialog.run()
3709 dialog.hide()
3710
3711 def _getSaveDialog(self):
3712 """Get the PIREP saving dialog.
3713
3714 If it does not exist yet, create it."""
3715 if self._savePIREPDialog is None:
3716 gui = self._wizard.gui
3717 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
3718 xstr("finish_save_title"),
3719 action = FILE_CHOOSER_ACTION_SAVE,
3720 buttons = (gtk.STOCK_CANCEL,
3721 RESPONSETYPE_CANCEL,
3722 gtk.STOCK_OK, RESPONSETYPE_OK),
3723 parent = gui.mainWindow)
3724 dialog.set_modal(True)
3725 dialog.set_do_overwrite_confirmation(True)
3726
3727 filter = gtk.FileFilter()
3728 filter.set_name(xstr("file_filter_pireps"))
3729 filter.add_pattern("*.pirep")
3730 dialog.add_filter(filter)
3731
3732 filter = gtk.FileFilter()
3733 filter.set_name(xstr("file_filter_all"))
3734 filter.add_pattern("*.*")
3735 dialog.add_filter(filter)
3736
3737 self._savePIREPDialog = dialog
3738
3739 return self._savePIREPDialog
3740
3741
3742 def _sendClicked(self, button):
3743 """Called when the Send button is clicked."""
3744 pirep = PIREP(self._wizard.gui.flight)
3745 self._wizard.gui.sendPIREP(pirep,
3746 callback = self._handlePIREPSent)
3747
3748 def _handlePIREPSent(self, returned, result):
3749 """Callback for the PIREP sending result."""
3750 self._pirepSent = returned and result.success
3751 if self._wizard.gui.config.onlineGateSystem and \
3752 self._wizard.loggedIn and not self._wizard.entranceExam and \
3753 returned and result.success:
3754 bookedFlight = self._wizard.bookedFlight
3755 if bookedFlight.arrivalICAO=="LHBP":
3756 iter = self._gate.get_active_iter()
3757 gateNumber = None if iter is None \
3758 else self._gatesModel.get_value(iter, 0)
3759
3760 status = const.PLANE_PARKING if gateNumber is None \
3761 else const.PLANE_HOME
3762 else:
3763 gateNumber = None
3764 status = const.PLANE_AWAY
3765
3766 self._wizard.updatePlane(self._planeUpdated,
3767 bookedFlight.tailNumber,
3768 status, gateNumber = gateNumber)
3769
3770 def _planeUpdated(self, success):
3771 """Callback for the plane updating."""
3772 pass
3773
3774 def _formatTime(self, scheduledTime, realTimestamp, (warning, error)):
3775 """Format the departure or arrival time based on the given data as a
3776 markup for a label."""
3777 realTime = time.gmtime(realTimestamp)
3778
3779 if warning:
3780 colour = "red" if error else "orange"
3781 markupBegin = '<span foreground="%s">' % (colour,)
3782 markupEnd = '</span>'
3783 else:
3784 markupBegin = markupEnd = ""
3785
3786 markup = "<b>%s%02d:%02d [%02d:%02d]%s</b>" % \
3787 (markupBegin,
3788 realTime.tm_hour, realTime.tm_min,
3789 scheduledTime.hour, scheduledTime.minute,
3790 markupEnd)
3791
3792 return markup
3793
3794 def _updateTimes(self):
3795 """Format the flight times and the help text according to the flight
3796 type.
3797
3798 The buttons are also updated.
3799 """
3800 flight = self._wizard.gui._flight
3801 bookedFlight = flight.bookedFlight
3802
3803 (departureWarning, departureError) = flight.blockTimeStartWrong
3804 (arrivalWarning, arrivalError) = flight.blockTimeEndWrong
3805
3806 if self.flightType==const.FLIGHTTYPE_VIP:
3807 departureError = arrivalError = False
3808
3809 self._tooBigTimeDifference = departureError or arrivalError
3810
3811 self._depTime.set_markup(self._formatTime(bookedFlight.departureTime,
3812 flight.blockTimeStart,
3813 (departureWarning,
3814 departureError)))
3815
3816 self._arrTime.set_markup(self._formatTime(bookedFlight.arrivalTime,
3817 flight.blockTimeEnd,
3818 (arrivalWarning,
3819 arrivalError)))
3820
3821 self.updateButtons()
3822
3823 def _updateHelp(self, faultsExplained, timesCorrect):
3824 """Update the help text according to the actual situation."""
3825 if not faultsExplained:
3826 self.setHelp(xstr("finish_help") + xstr("finish_help_faults"))
3827 elif not timesCorrect:
3828 self.setHelp(xstr("finish_help") + xstr("finish_help_wrongtime"))
3829 else:
3830 self.setHelp(xstr("finish_help") + xstr("finish_help_goodtime"))
3831
3832
3833#-----------------------------------------------------------------------------
3834
3835class Wizard(gtk.VBox):
3836 """The flight wizard."""
3837 def __init__(self, gui):
3838 """Construct the wizard."""
3839 super(Wizard, self).__init__()
3840
3841 self.gui = gui
3842
3843 self._pages = []
3844 self._currentPage = None
3845
3846 self._loginPage = LoginPage(self)
3847 self._pages.append(self._loginPage)
3848 self._pages.append(FlightSelectionPage(self))
3849 self._pages.append(GateSelectionPage(self))
3850 self._pages.append(ConnectPage(self))
3851 self._payloadPage = PayloadPage(self)
3852 self._pages.append(self._payloadPage)
3853 self._payloadIndex = len(self._pages)
3854 self._pages.append(TimePage(self))
3855 self._routePage = RoutePage(self)
3856 self._pages.append(self._routePage)
3857 self._simBriefSetupPage = SimBriefSetupPage(self)
3858 self._pages.append(self._simBriefSetupPage)
3859 self._simBriefingPage = SimBriefingPage(self)
3860 self._pages.append(self._simBriefingPage)
3861 self._pages.append(FuelPage(self))
3862 self._departureBriefingPage = BriefingPage(self, True)
3863 self._pages.append(self._departureBriefingPage)
3864 self._arrivalBriefingPage = BriefingPage(self, False)
3865 self._pages.append(self._arrivalBriefingPage)
3866 self._arrivalBriefingIndex = len(self._pages)
3867 self._takeoffPage = TakeoffPage(self)
3868 self._pages.append(self._takeoffPage)
3869 self._cruisePage = CruisePage(self)
3870 self._pages.append(self._cruisePage)
3871 self._landingPage = LandingPage(self)
3872 self._pages.append(self._landingPage)
3873 self._finishPage = FinishPage(self)
3874 self._pages.append(self._finishPage)
3875
3876 self._requestedWidth = None
3877 self._requestedHeight = None
3878
3879 self.connect("size-allocate", self._sizeAllocate)
3880
3881 for page in self._pages:
3882 page.show_all()
3883 page.setStyle()
3884
3885 self._initialize()
3886
3887 def _sizeAllocate(self, widget, allocation):
3888 if self._requestedWidth is not None and \
3889 self._requestedHeight is not None:
3890 return
3891
3892 if self._currentPage is not None:
3893 self.remove(self._pages[self._currentPage])
3894
3895 maxWidth = 0
3896 maxHeight = 0
3897 for page in self._pages:
3898 self.add(page)
3899 self.show_all()
3900 pageSizeRequest = page.size_request()
3901 width = pageSizeRequest.width if pygobject else pageSizeRequest[0]
3902 height = pageSizeRequest.height if pygobject else pageSizeRequest[1]
3903 maxWidth = max(maxWidth, width)
3904 maxHeight = max(maxHeight, height)
3905 self.remove(page)
3906
3907 if self._currentPage is not None:
3908 self.add(self._pages[self._currentPage])
3909
3910 self._requestedWidth = maxWidth
3911 self._requestedHeight = maxHeight
3912 self.set_size_request(maxWidth, maxHeight)
3913
3914 @property
3915 def pilotID(self):
3916 """Get the pilot ID, if given."""
3917 return self._loginPage.pilotID
3918
3919 @property
3920 def entranceExam(self):
3921 """Get whether an entrance exam is about to be taken."""
3922 return self._loginPage.entranceExam
3923
3924 @property
3925 def loggedIn(self):
3926 """Indicate if there was a successful login."""
3927 return self._loginResult is not None
3928
3929 @property
3930 def loginResult(self):
3931 """Get the login result."""
3932 return self._loginResult
3933
3934 def setCurrentPage(self, index, finalize = False):
3935 """Set the current page to the one with the given index."""
3936 assert index < len(self._pages)
3937
3938 fromPage = self._currentPage
3939 if fromPage is not None:
3940 page = self._pages[fromPage]
3941 if finalize and not page._completed:
3942 page.complete()
3943 self.remove(page)
3944
3945 self._currentPage = index
3946 page = self._pages[index]
3947 self.add(page)
3948 if page._fromPage is None:
3949 page._fromPage = fromPage
3950 page.initialize()
3951 self.show_all()
3952 if fromPage is not None:
3953 self.grabDefault()
3954
3955 @property
3956 def bookedFlight(self):
3957 """Get the booked flight selected."""
3958 return self._bookedFlight
3959
3960 @property
3961 def numCrew(self):
3962 """Get the number of crew members."""
3963 return self._payloadPage.numCrew
3964
3965 @property
3966 def numPassengers(self):
3967 """Get the number of passengers."""
3968 return self._payloadPage.numPassengers
3969
3970 @property
3971 def bagWeight(self):
3972 """Get the baggage weight."""
3973 return self._payloadPage.bagWeight
3974
3975 @property
3976 def cargoWeight(self):
3977 """Get the cargo weight."""
3978 return self._payloadPage.cargoWeight
3979
3980 @property
3981 def mailWeight(self):
3982 """Get the mail weight."""
3983 return self._payloadPage.mailWeight
3984
3985 @property
3986 def zfw(self):
3987 """Get the calculated ZFW value."""
3988 return 0 if self._bookedFlight is None \
3989 else self._payloadPage.calculateZFW()
3990
3991 @property
3992 def filedCruiseLevel(self):
3993 """Get the filed cruise level."""
3994 return self._routePage.filedCruiseLevel
3995
3996 @property
3997 def filedCruiseAltitude(self):
3998 """Get the filed cruise altitude."""
3999 return self._routePage.filedCruiseLevel * 100
4000
4001 @property
4002 def cruiseAltitude(self):
4003 """Get the cruise altitude."""
4004 level = self._cruisePage.cruiseLevel if self._cruisePage.activated \
4005 else self._routePage.filedCruiseLevel
4006 return level * 100
4007
4008 @property
4009 def loggableCruiseAltitude(self):
4010 """Get the cruise altitude that can be logged."""
4011 if self._cruisePage.activated:
4012 return self._cruisePage.loggableCruiseLevel * 100
4013 else:
4014 return 0
4015
4016 @property
4017 def route(self):
4018 """Get the route."""
4019 return self._routePage.route
4020
4021 @property
4022 def alternate(self):
4023 """Get the ICAO code of the alternate airport."""
4024 return self._routePage.alternate
4025
4026 @property
4027 def departureMETAR(self):
4028 """Get the METAR of the departure airport."""
4029 return self._departureBriefingPage.metar
4030
4031 @property
4032 def arrivalMETAR(self):
4033 """Get the METAR of the arrival airport."""
4034 return self._arrivalBriefingPage.metar
4035
4036 @property
4037 def departureRunway(self):
4038 """Get the departure runway."""
4039 return self._takeoffPage.runway
4040
4041 @property
4042 def sid(self):
4043 """Get the SID."""
4044 return self._takeoffPage.sid
4045
4046 @property
4047 def v1(self):
4048 """Get the V1 speed."""
4049 return self._takeoffPage.v1
4050
4051 @property
4052 def vr(self):
4053 """Get the Vr speed."""
4054 return self._takeoffPage.vr
4055
4056 @property
4057 def v2(self):
4058 """Get the V2 speed."""
4059 return self._takeoffPage.v2
4060
4061 @property
4062 def derate(self):
4063 """Get the derate value."""
4064 return self._takeoffPage.derate
4065
4066 @property
4067 def takeoffAntiIceOn(self):
4068 """Get whether the anti-ice system was on during take-off."""
4069 return self._takeoffPage.antiIceOn
4070
4071 @takeoffAntiIceOn.setter
4072 def takeoffAntiIceOn(self, value):
4073 """Set anti-ice on indicator."""
4074 self._takeoffPage.antiIceOn = value
4075
4076 @property
4077 def rtoIndicated(self):
4078 """Get whether the pilot has indicated that an RTO has occured."""
4079 return self._takeoffPage.rtoIndicated
4080
4081 @property
4082 def arrivalRunway(self):
4083 """Get the arrival runway."""
4084 return self._landingPage.runway
4085
4086 @property
4087 def star(self):
4088 """Get the STAR."""
4089 return self._landingPage.star
4090
4091 @property
4092 def transition(self):
4093 """Get the transition."""
4094 return self._landingPage.transition
4095
4096 @property
4097 def approachType(self):
4098 """Get the approach type."""
4099 return self._landingPage.approachType
4100
4101 @property
4102 def vref(self):
4103 """Get the Vref speed."""
4104 return self._landingPage.vref
4105
4106 @property
4107 def landingAntiIceOn(self):
4108 """Get whether the anti-ice system was on during landing."""
4109 return self._landingPage.antiIceOn
4110
4111 @landingAntiIceOn.setter
4112 def landingAntiIceOn(self, value):
4113 """Set anti-ice on indicator."""
4114 self._landingPage.antiIceOn = value
4115
4116 @property
4117 def flightType(self):
4118 """Get the flight type."""
4119 return self._finishPage.flightType
4120
4121 @property
4122 def online(self):
4123 """Get whether the flight was online or not."""
4124 return self._finishPage.online
4125
4126 @property
4127 def usingSimBrief(self):
4128 """Indicate if we are using a SimBrief briefing or not."""
4129 return self._usingSimBrief
4130
4131 @usingSimBrief.setter
4132 def usingSimBrief(self, x):
4133 """Set whether we are using a SimBrief briefing or not."""
4134 self._usingSimBrief = x
4135
4136 def nextPage(self, finalize = True):
4137 """Go to the next page."""
4138 self.jumpPage(1, finalize)
4139
4140 def jumpPage(self, count, finalize = True):
4141 """Go to the page which is 'count' pages after the current one."""
4142 self.setCurrentPage(self._currentPage + count, finalize = finalize)
4143
4144 def grabDefault(self):
4145 """Make the default button of the current page the default."""
4146 self._pages[self._currentPage].grabDefault()
4147
4148 def connected(self, fsType, descriptor):
4149 """Called when the connection could be made to the simulator."""
4150 self.nextPage()
4151
4152 def reset(self, loginResult):
4153 """Resets the wizard to go back to the login page."""
4154 self._initialize(keepLoginResult = loginResult is None,
4155 loginResult = loginResult)
4156
4157 def setStage(self, stage):
4158 """Set the flight stage to the given one."""
4159 if stage!=const.STAGE_END:
4160 self._cruisePage.setLoggable(Flight.canLogCruiseAltitude(stage))
4161
4162 if stage==const.STAGE_TAKEOFF:
4163 self._takeoffPage.allowForward()
4164 elif stage==const.STAGE_LANDING:
4165 if not self._arrivalBriefingPage.metarEdited:
4166 print "Downloading arrival METAR again"
4167 self.gui.webHandler.getMETARs(self._arrivalMETARCallback,
4168 [self._bookedFlight.arrivalICAO])
4169
4170 elif stage==const.STAGE_END:
4171 for page in self._pages:
4172 page.flightEnded()
4173
4174 def _initialize(self, keepLoginResult = False, loginResult = None):
4175 """Initialize the wizard."""
4176 if not keepLoginResult:
4177 self._loginResult = loginResult
4178
4179 self._loginCallback = None
4180
4181 self._fleet = None
4182 self._fleetCallback = None
4183
4184 self._bookedFlight = None
4185 self._departureGate = "-"
4186 self._fuelData = None
4187 self._departureNOTAMs = None
4188 self._departureMETAR = None
4189 self._arrivalNOTAMs = None
4190 self._arrivalMETAR = None
4191 self._usingSimBrief = False
4192
4193 firstPage = 0 if self._loginResult is None else 1
4194 for page in self._pages[firstPage:]:
4195 page.reset()
4196
4197 self.setCurrentPage(firstPage)
4198 #self.setCurrentPage(10)
4199
4200 def login(self, callback, pilotID, password, entranceExam):
4201 """Called when the login button was clicked."""
4202 self._loginCallback = callback
4203 if pilotID is None:
4204 loginResult = self._loginResult
4205 assert loginResult is not None and loginResult.loggedIn
4206 pilotID = loginResult.pilotID
4207 password = loginResult.password
4208 entranceExam = loginResult.entranceExam
4209 busyMessage = xstr("reload_busy")
4210 else:
4211 self._loginResult = None
4212 busyMessage = xstr("login_busy")
4213
4214 self.gui.beginBusy(busyMessage)
4215
4216 self.gui.webHandler.login(self._loginResultCallback,
4217 pilotID, password,
4218 entranceExam = entranceExam)
4219
4220 def reloadFlights(self, callback):
4221 """Reload the flights from the MAVA server."""
4222 self.login(callback, None, None, None)
4223
4224 def cruiseLevelChanged(self):
4225 """Called when the cruise level is changed."""
4226 return self.gui.cruiseLevelChanged()
4227
4228 def metarChanged(self, metar, originator):
4229 """Called when a METER is changed on on of the pages.
4230
4231 originator is the page that originated the changed. It will be used to
4232 determine which METAR (departure or arrival) has changed."""
4233 metar = metar.upper()
4234 if originator in [self._departureBriefingPage, self._takeoffPage]:
4235 self._departureMETARChanged(metar, originator)
4236 else:
4237 self._arrivalMETARChanged(metar, originator)
4238
4239 def _departureMETARChanged(self, metar, originator):
4240 """Called when the departure METAR has been edited on one of the
4241 pages.
4242
4243 originator is the page that originated the change. It will not be
4244 called to set the METAR, while others will be."""
4245 for page in [self._departureBriefingPage, self._takeoffPage]:
4246 if page is not originator:
4247 page.changeMETAR(metar)
4248
4249 def _arrivalMETARChanged(self, metar, originator):
4250 """Called when the arrival METAR has been edited on one of the
4251 pages.
4252
4253 originator is the page that originated the change. It will not be
4254 called to set the METAR, while others will be."""
4255 for page in [self._arrivalBriefingPage, self._landingPage]:
4256 if page is not originator:
4257 page.changeMETAR(metar)
4258
4259 def _loginResultCallback(self, returned, result):
4260 """The login result callback, called in the web handler's thread."""
4261 gobject.idle_add(self._handleLoginResult, returned, result)
4262
4263 def _handleLoginResult(self, returned, result):
4264 """Handle the login result."""
4265 self.gui.endBusy()
4266 returned = True
4267 isReload = self._loginResult is not None
4268 if returned:
4269 if result.loggedIn:
4270 self._loginResult = result
4271 else:
4272 if isReload:
4273 message = xstr("reload_failed")
4274 else:
4275 message = xstr("login_entranceExam_invalid"
4276 if self.entranceExam else
4277 xstr("login_invalid"))
4278 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
4279 type = MESSAGETYPE_ERROR,
4280 message_format = message)
4281 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
4282 dialog.set_title(WINDOW_TITLE_BASE)
4283 if isReload:
4284 secondary = xstr("reload_failed_sec")
4285 else:
4286 secondary = xstr("login_entranceExam_invalid_sec"
4287 if self.entranceExam else
4288 xstr("login_invalid_sec"))
4289 dialog.format_secondary_markup(secondary)
4290 dialog.run()
4291 dialog.hide()
4292 else:
4293 message = xstr("reload_failconn") if isReload \
4294 else xstr("login_failconn")
4295 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
4296 type = MESSAGETYPE_ERROR,
4297 message_format = message)
4298 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
4299 dialog.set_title(WINDOW_TITLE_BASE)
4300 secondary = xstr("reload_failconn_sec") if isReload \
4301 else xstr("login_failconn_sec")
4302 dialog.format_secondary_markup(secondary)
4303
4304 dialog.run()
4305 dialog.hide()
4306
4307 callback = self._loginCallback
4308 self._loginCallback = None
4309 callback(returned, result)
4310
4311 def getFleet(self, callback, force = False):
4312 """Get the fleet via the GUI and call the given callback."""
4313 self._fleetCallback = callback
4314 self.gui.getFleet(callback = self._fleetRetrieved, force = force)
4315
4316 def _fleetRetrieved(self, fleet):
4317 """Callback for the fleet retrieval."""
4318 self._fleet = fleet
4319 if self._fleetCallback is not None:
4320 self._fleetCallback(fleet)
4321 self._fleetCallback = None
4322
4323 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
4324 """Update the given plane's gate information."""
4325 self.gui.updatePlane(tailNumber, status, gateNumber = gateNumber,
4326 callback = callback)
4327
4328 def updateRTO(self):
4329 """Update the RTO state.
4330
4331 The RTO checkbox will be enabled if the flight has an RTO state and the
4332 comments field contains some text."""
4333 flight = self.gui.flight
4334 rtoEnabled = flight is not None and flight.hasRTO and \
4335 self.gui.hasComments
4336 self._takeoffPage.setRTOEnabled(rtoEnabled)
4337
4338 def commentsChanged(self):
4339 """Called when the comments have changed."""
4340 self.updateRTO()
4341 self._finishPage.updateButtons()
4342
4343 def delayCodesChanged(self):
4344 """Called when the delay codes have changed."""
4345 self._finishPage.updateButtons()
4346
4347 def faultExplanationsChanged(self):
4348 """Called when the faults and their explanations have changed."""
4349 self._finishPage.updateButtons()
4350
4351 def rtoToggled(self, indicated):
4352 """Called when the RTO indication has changed."""
4353 self.gui.rtoToggled(indicated)
4354
4355 def _connectSimulator(self, simulatorType):
4356 """Connect to the simulator."""
4357 self.gui.connectSimulator(self._bookedFlight.aircraftType,
4358 simulatorType)
4359
4360 def _arrivalMETARCallback(self, returned, result):
4361 """Called when the METAR of the arrival airport is retrieved."""
4362 gobject.idle_add(self._handleArrivalMETAR, returned, result)
4363
4364 def _handleArrivalMETAR(self, returned, result):
4365 """Called when the METAR of the arrival airport is retrieved."""
4366 icao = self._bookedFlight.arrivalICAO
4367 if returned and icao in result.metars:
4368 metar = result.metars[icao]
4369 if metar!="":
4370 self._arrivalBriefingPage.setMETAR(metar)
4371
4372#-----------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.