source: src/mlx/gui/flight.py@ 402:b668511b4ac8

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

Made the debug logging more detailed by adding the printout of the configuration and a few other stuff (re #170)

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