source: src/mlx/gui/flight.py@ 393:977efa7a177b

Last change on this file since 393:977efa7a177b was 393:977efa7a177b, checked in by István Váradi <ivaradi@…>, 11 years ago

Added the automatic saving of the PIREP (re #163)

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