source: src/mlx/gui/flight.py@ 502:602814ecde8f

xplane
Last change on this file since 502:602814ecde8f was 501:2fd9b3270f6d, checked in by István Váradi <ivaradi@…>, 12 years ago

Added logic to select the simulator type

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