source: src/mlx/gui/flight.py@ 274:b866c39279e8

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

Reworked fuel handling so that the model can tell what tanks there are

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