source: src/mlx/gui/flight.py@ 713:64f54381f834

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

Added support for a fast timeout handler to speed up operations driver via Selenium (re #279).

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