source: src/mlx/gui/flight.py@ 248:d1a42111f067

Last change on this file since 248:d1a42111f067 was 242:63061800b9fb, checked in by István Váradi <ivaradi@…>, 12 years ago

The landing and takeoff pages remain editable after leaving them

File size: 113.6 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(gui.flight.aircraft.fuelTanks,
1166 self._handleFuel)
1167 else:
1168 self._wizard.nextPage()
1169
1170 def _handleFuel(self, fuelData):
1171 """Callback for the fuel query operation."""
1172 gobject.idle_add(self._processFuel, fuelData)
1173
1174 def _processFuel(self, fuelData):
1175 """Process the given fuel data."""
1176 self._wizard.gui.endBusy()
1177 self._wizard._fuelData = fuelData
1178 self._wizard.nextPage()
1179
1180#-----------------------------------------------------------------------------
1181
1182class FuelTank(gtk.VBox):
1183 """Widget for the fuel tank."""
1184 def __init__(self, fuelTank, name, capacity, currentWeight):
1185 """Construct the widget for the tank with the given name."""
1186 super(FuelTank, self).__init__()
1187
1188 self._enabled = True
1189 self.fuelTank = fuelTank
1190 self.capacity = capacity
1191 self.currentWeight = currentWeight
1192 self.expectedWeight = currentWeight
1193
1194 label = gtk.Label("<b>" + name + "</b>")
1195 label.set_use_markup(True)
1196 label.set_use_underline(True)
1197 label.set_justify(JUSTIFY_CENTER)
1198 label.set_alignment(0.5, 1.0)
1199 self.pack_start(label, False, False, 4)
1200
1201 self._tankFigure = gtk.EventBox()
1202 self._tankFigure.set_size_request(38, -1)
1203 self._tankFigure.set_visible_window(False)
1204 self._tankFigure.set_tooltip_markup(xstr("fuel_tank_tooltip"))
1205
1206 if pygobject:
1207 self._tankFigure.connect("draw", self._drawTankFigure)
1208 else:
1209 self._tankFigure.connect("expose_event", self._drawTankFigure)
1210 self._tankFigure.connect("button_press_event", self._buttonPressed)
1211 self._tankFigure.connect("motion_notify_event", self._motionNotify)
1212 self._tankFigure.connect("scroll-event", self._scrolled)
1213
1214 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1215 xscale = 0.0, yscale = 1.0)
1216 alignment.add(self._tankFigure)
1217
1218 self.pack_start(alignment, True, True, 4)
1219
1220 self._expectedButton = gtk.SpinButton()
1221 self._expectedButton.set_numeric(True)
1222 self._expectedButton.set_range(0, self.capacity)
1223 self._expectedButton.set_increments(10, 100)
1224 self._expectedButton.set_value(currentWeight)
1225 self._expectedButton.set_alignment(1.0)
1226 self._expectedButton.set_width_chars(5)
1227 self._expectedButton.connect("value-changed", self._expectedChanged)
1228
1229 label.set_mnemonic_widget(self._expectedButton)
1230
1231 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1232 xscale = 0.0, yscale = 1.0)
1233 alignment.add(self._expectedButton)
1234 self.pack_start(alignment, False, False, 4)
1235
1236 def setCurrent(self, currentWeight):
1237 """Set the current weight."""
1238 self.currentWeight = currentWeight
1239 self._redraw()
1240
1241 def isCorrect(self):
1242 """Determine if the contents of the fuel tank are as expected"""
1243 return abs(self.expectedWeight - self.currentWeight)<=1
1244
1245 def disable(self):
1246 """Disable the fuel tank."""
1247 self._expectedButton.set_sensitive(False)
1248 self._enabled = False
1249
1250 def _redraw(self):
1251 """Redraw the tank figure."""
1252 self._tankFigure.queue_draw()
1253
1254 def _drawTankFigure(self, tankFigure, eventOrContext):
1255 """Draw the tank figure."""
1256 triangleSize = 5
1257
1258 context = eventOrContext if pygobject else tankFigure.window.cairo_create()
1259 (xOffset, yOffset) = (0, 0) if pygobject \
1260 else (tankFigure.allocation.x, tankFigure.allocation.y)
1261
1262 width = tankFigure.get_allocated_width() if pygobject \
1263 else tankFigure.allocation.width
1264 height = tankFigure.get_allocated_height() if pygobject \
1265 else tankFigure.allocation.height
1266
1267 rectangleX0 = triangleSize
1268 rectangleY0 = triangleSize
1269 rectangleX1 = width - 1 - triangleSize
1270 rectangleY1 = height - 1 - triangleSize
1271 rectangleLineWidth = 2.0
1272
1273 context.set_source_rgb(0.0, 0.0, 0.0)
1274 context.set_line_width(rectangleLineWidth)
1275 context.rectangle(xOffset + rectangleX0 + rectangleLineWidth/2,
1276 yOffset + rectangleY0 + rectangleLineWidth/2,
1277 rectangleX1 - rectangleX0 - rectangleLineWidth,
1278 rectangleY1 - rectangleY0 - rectangleLineWidth)
1279 context.stroke()
1280
1281 rectangleInnerLeft = rectangleX0 + rectangleLineWidth
1282 rectangleInnerRight = rectangleX1 - rectangleLineWidth
1283 self._rectangleInnerTop = rectangleInnerTop = rectangleY0 + rectangleLineWidth
1284 self._rectangleInnerBottom = rectangleInnerBottom = rectangleY1 - rectangleLineWidth
1285
1286 rectangleInnerWidth = rectangleInnerRight - rectangleInnerLeft
1287 rectangleInnerHeight = rectangleInnerBottom - rectangleInnerTop
1288
1289 context.set_source_rgb(1.0, 0.9, 0.6)
1290 currentHeight = self.currentWeight * rectangleInnerHeight / self.capacity
1291 currentX = rectangleInnerTop + rectangleInnerHeight - currentHeight
1292 context.rectangle(xOffset + rectangleInnerLeft,
1293 yOffset + rectangleInnerTop +
1294 rectangleInnerHeight - currentHeight,
1295 rectangleInnerWidth, currentHeight)
1296 context.fill()
1297
1298 expectedHeight = self.expectedWeight * rectangleInnerHeight / self.capacity
1299 expectedY = rectangleInnerTop + rectangleInnerHeight - expectedHeight
1300
1301 context.set_line_width(1.5)
1302 context.set_source_rgb(0.0, 0.85, 0.85)
1303 context.move_to(xOffset + rectangleX0, yOffset + expectedY)
1304 context.line_to(xOffset + rectangleX1, yOffset + expectedY)
1305 context.stroke()
1306
1307 context.set_line_width(0.0)
1308 context.move_to(xOffset + 0, yOffset + expectedY - triangleSize)
1309 context.line_to(xOffset + 0, yOffset + expectedY + triangleSize)
1310 context.line_to(xOffset + rectangleX0 + 1, yOffset + expectedY)
1311 context.line_to(xOffset + 0, yOffset + expectedY - triangleSize)
1312 context.fill()
1313
1314 context.set_line_width(0.0)
1315 context.move_to(xOffset + width, yOffset + expectedY - triangleSize)
1316 context.line_to(xOffset + width, yOffset + expectedY + triangleSize)
1317 context.line_to(xOffset + rectangleX1 - 1, yOffset + expectedY)
1318 context.line_to(xOffset + width, yOffset + expectedY - triangleSize)
1319 context.fill()
1320
1321 return True
1322
1323 def _setExpectedFromY(self, y):
1324 """Set the expected weight from the given Y-coordinate."""
1325 level = (self._rectangleInnerBottom - y) / \
1326 (self._rectangleInnerBottom - self._rectangleInnerTop)
1327 level = min(1.0, max(0.0, level))
1328 self._expectedButton.set_value(level * self.capacity)
1329
1330 def _buttonPressed(self, tankFigure, event):
1331 """Called when a button is pressed in the figure.
1332
1333 The expected level will be set there."""
1334 if self._enabled and event.button==1:
1335 self._setExpectedFromY(event.y)
1336
1337 def _motionNotify(self, tankFigure, event):
1338 """Called when the mouse pointer moves within the area of a tank figure."""
1339 if self._enabled and event.state==BUTTON1_MASK:
1340 self._setExpectedFromY(event.y)
1341
1342 def _scrolled(self, tankFigure, event):
1343 """Called when a scroll event is received."""
1344 if self._enabled:
1345 increment = 1 if event.state==CONTROL_MASK \
1346 else 100 if event.state==SHIFT_MASK \
1347 else 10 if event.state==0 else 0
1348 if increment!=0:
1349 if event.direction==SCROLL_DOWN:
1350 increment *= -1
1351 self._expectedButton.spin(SPIN_USER_DEFINED, increment)
1352
1353 def _expectedChanged(self, spinButton):
1354 """Called when the expected value has changed."""
1355 self.expectedWeight = spinButton.get_value_as_int()
1356 self._redraw()
1357
1358#-----------------------------------------------------------------------------
1359
1360class FuelPage(Page):
1361 """The page containing the fuel tank filling."""
1362 _pumpStep = 0.02
1363
1364 def __init__(self, wizard):
1365 """Construct the page."""
1366 super(FuelPage, self).__init__(wizard, xstr("fuel_title"),
1367 xstr("fuel_help"),
1368 completedHelp = xstr("fuel_chelp"))
1369
1370 self._fuelTanks = []
1371 self._fuelTable = None
1372 self._fuelAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1373 xscale = 0.0, yscale = 1.0)
1374 self.setMainWidget(self._fuelAlignment)
1375
1376 tanks = acft.MostFuelTankAircraft.fuelTanks
1377 tankData = ((2500, 3900),) * len(tanks)
1378 self._setupTanks(tanks, tankData)
1379
1380 self.addCancelFlightButton()
1381
1382 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1383 self._button = self.addNextButton(clicked = self._forwardClicked)
1384
1385 self._pumpIndex = 0
1386
1387 def activate(self):
1388 """Activate the page."""
1389 gui = self._wizard.gui
1390
1391 self._setupTanks(gui.flight.aircraft.fuelTanks,
1392 self._wizard._fuelData)
1393
1394 def finalize(self):
1395 """Finalize the page."""
1396 for fuelTank in self._fuelTanks:
1397 fuelTank.disable()
1398
1399 def _backClicked(self, button):
1400 """Called when the Back button is pressed."""
1401 self.goBack()
1402
1403 def _forwardClicked(self, button):
1404 """Called when the forward button is clicked."""
1405 if not self._completed:
1406 self._pumpIndex = 0
1407 self._wizard.gui.beginBusy(xstr("fuel_pump_busy"))
1408 self._pump()
1409 else:
1410 self._wizard.nextPage()
1411
1412 def _setupTanks(self, tanks, tankData):
1413 """Setup the tanks for the given data."""
1414 numTanks = len(tanks)
1415 if self._fuelTable is not None:
1416 self._fuelAlignment.remove(self._fuelTable)
1417
1418 self._fuelTanks = []
1419 self._fuelTable = gtk.Table(numTanks, 1)
1420 self._fuelTable.set_col_spacings(16)
1421 for i in range(0, numTanks):
1422 tank = tanks[i]
1423 (current, capacity) = tankData[i]
1424
1425 fuelTank = FuelTank(tank,
1426 xstr("fuel_tank_" +
1427 const.fuelTank2string(tank)),
1428 capacity, current)
1429 self._fuelTable.attach(fuelTank, i, i+1, 0, 1)
1430 self._fuelTanks.append(fuelTank)
1431
1432 self._fuelAlignment.add(self._fuelTable)
1433 self.show_all()
1434
1435 def _pump(self):
1436 """Perform one step of pumping.
1437
1438 It is checked, if the current tank's contents are of the right
1439 quantity. If not, it is filled one step further to the desired
1440 contents. Otherwise the next tank is started. If all tanks are are
1441 filled, the next page is selected."""
1442 numTanks = len(self._fuelTanks)
1443
1444 fuelTank = None
1445 while self._pumpIndex < numTanks:
1446 fuelTank = self._fuelTanks[self._pumpIndex]
1447 if fuelTank.isCorrect():
1448 self._pumpIndex += 1
1449 fuelTank = None
1450 else:
1451 break
1452
1453 if fuelTank is None:
1454 self._wizard.gui.endBusy()
1455 self._wizard.nextPage()
1456 else:
1457 currentLevel = fuelTank.currentWeight / fuelTank.capacity
1458 expectedLevel = fuelTank.expectedWeight / fuelTank.capacity
1459 if currentLevel<expectedLevel:
1460 currentLevel += FuelPage._pumpStep
1461 if currentLevel>expectedLevel: currentLevel = expectedLevel
1462 else:
1463 currentLevel -= FuelPage._pumpStep
1464 if currentLevel<expectedLevel: currentLevel = expectedLevel
1465 fuelTank.setCurrent(currentLevel * fuelTank.capacity)
1466 self._wizard.gui.simulator.setFuelLevel([(fuelTank.fuelTank,
1467 currentLevel)])
1468 gobject.timeout_add(50, self._pump)
1469
1470#-----------------------------------------------------------------------------
1471
1472class RoutePage(Page):
1473 """The page containing the route and the flight level."""
1474 def __init__(self, wizard):
1475 """Construct the page."""
1476 super(RoutePage, self).__init__(wizard, xstr("route_title"),
1477 xstr("route_help"),
1478 completedHelp = xstr("route_chelp"))
1479
1480 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1481 xscale = 0.0, yscale = 0.0)
1482
1483 mainBox = gtk.VBox()
1484 alignment.add(mainBox)
1485 self.setMainWidget(alignment)
1486
1487 levelBox = gtk.HBox()
1488
1489 label = gtk.Label(xstr("route_level"))
1490 label.set_use_underline(True)
1491 levelBox.pack_start(label, True, True, 0)
1492
1493 self._cruiseLevel = gtk.SpinButton()
1494 self._cruiseLevel.set_increments(step = 10, page = 100)
1495 self._cruiseLevel.set_range(min = 50, max = 500)
1496 self._cruiseLevel.set_tooltip_text(xstr("route_level_tooltip"))
1497 self._cruiseLevel.set_numeric(True)
1498 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
1499 label.set_mnemonic_widget(self._cruiseLevel)
1500 self._filedCruiseLevel = 240
1501
1502 levelBox.pack_start(self._cruiseLevel, False, False, 8)
1503
1504 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1505 xscale = 0.0, yscale = 0.0)
1506 alignment.add(levelBox)
1507
1508 mainBox.pack_start(alignment, False, False, 0)
1509
1510
1511 routeBox = gtk.VBox()
1512
1513 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1514 xscale = 0.0, yscale = 0.0)
1515 label = gtk.Label(xstr("route_route"))
1516 label.set_use_underline(True)
1517 alignment.add(label)
1518 routeBox.pack_start(alignment, True, True, 0)
1519
1520 routeWindow = gtk.ScrolledWindow()
1521 routeWindow.set_size_request(400, 80)
1522 routeWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
1523 else gtk.SHADOW_IN)
1524 routeWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1525 else gtk.POLICY_AUTOMATIC,
1526 gtk.PolicyType.AUTOMATIC if pygobject
1527 else gtk.POLICY_AUTOMATIC)
1528
1529 self._uppercasingRoute = False
1530
1531 self._route = gtk.TextView()
1532 self._route.set_tooltip_text(xstr("route_route_tooltip"))
1533 self._route.set_wrap_mode(WRAP_WORD)
1534 self._route.get_buffer().connect("changed", self._routeChanged)
1535 self._route.get_buffer().connect_after("insert-text", self._routeInserted)
1536 routeWindow.add(self._route)
1537
1538 label.set_mnemonic_widget(self._route)
1539 routeBox.pack_start(routeWindow, True, True, 0)
1540
1541 mainBox.pack_start(routeBox, True, True, 8)
1542
1543 self.addCancelFlightButton()
1544
1545 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1546 self._button = self.addNextButton(clicked = self._forwardClicked)
1547
1548 @property
1549 def filedCruiseLevel(self):
1550 """Get the filed cruise level."""
1551 return self._filedCruiseLevel
1552
1553 @property
1554 def cruiseLevel(self):
1555 """Get the cruise level."""
1556 return self._cruiseLevel.get_value_as_int()
1557
1558 @property
1559 def route(self):
1560 """Get the route."""
1561 return self._getRoute()
1562
1563 def activate(self):
1564 """Setup the route from the booked flight."""
1565 self._cruiseLevel.set_value(240)
1566 self._filedCruiseLevel = 240
1567 self._route.get_buffer().set_text(self._wizard._bookedFlight.route)
1568 self._updateForwardButton()
1569
1570 def _getRoute(self):
1571 """Get the text of the route."""
1572 buffer = self._route.get_buffer()
1573 return buffer.get_text(buffer.get_start_iter(),
1574 buffer.get_end_iter(), True)
1575
1576 def _updateForwardButton(self):
1577 """Update the sensitivity of the forward button."""
1578 self._button.set_sensitive(self._cruiseLevel.get_value_as_int()>=50 and \
1579 self._getRoute()!="")
1580
1581 def _cruiseLevelChanged(self, spinButton):
1582 """Called when the cruise level has changed."""
1583 self._updateForwardButton()
1584
1585 def _routeChanged(self, textBuffer):
1586 """Called when the route has changed."""
1587 if not self._uppercasingRoute:
1588 self._updateForwardButton()
1589
1590 def _routeInserted(self, textBuffer, iter, text, length):
1591 """Called when new characters are inserted into the route.
1592
1593 It uppercases all characters."""
1594 if not self._uppercasingRoute:
1595 self._uppercasingRoute = True
1596
1597 iter1 = iter.copy()
1598 iter1.backward_chars(length)
1599 textBuffer.delete(iter, iter1)
1600
1601 textBuffer.insert(iter, text.upper())
1602
1603 self._uppercasingRoute = False
1604
1605 def _backClicked(self, button):
1606 """Called when the Back button is pressed."""
1607 self.goBack()
1608
1609 def _forwardClicked(self, button):
1610 """Called when the Forward button is clicked."""
1611 if self._completed:
1612 self._wizard.nextPage()
1613 else:
1614 bookedFlight = self._wizard._bookedFlight
1615 self._filedCruiseLevel = self.cruiseLevel
1616 self._wizard.gui.beginBusy(xstr("route_down_notams"))
1617 self._wizard.gui.webHandler.getNOTAMs(self._notamsCallback,
1618 bookedFlight.departureICAO,
1619 bookedFlight.arrivalICAO)
1620 startSound(const.SOUND_NOTAM)
1621
1622 def _notamsCallback(self, returned, result):
1623 """Callback for the NOTAMs."""
1624 gobject.idle_add(self._handleNOTAMs, returned, result)
1625
1626 def _handleNOTAMs(self, returned, result):
1627 """Handle the NOTAMs."""
1628 if returned:
1629 self._wizard._departureNOTAMs = result.departureNOTAMs
1630 self._wizard._arrivalNOTAMs = result.arrivalNOTAMs
1631 else:
1632 self._wizard._departureNOTAMs = None
1633 self._wizard._arrivalNOTAMs = None
1634
1635 bookedFlight = self._wizard._bookedFlight
1636 self._wizard.gui.beginBusy(xstr("route_down_metars"))
1637 self._wizard.gui.webHandler.getMETARs(self._metarsCallback,
1638 [bookedFlight.departureICAO,
1639 bookedFlight.arrivalICAO])
1640
1641 def _metarsCallback(self, returned, result):
1642 """Callback for the METARs."""
1643 gobject.idle_add(self._handleMETARs, returned, result)
1644
1645 def _handleMETARs(self, returned, result):
1646 """Handle the METARs."""
1647 self._wizard._departureMETAR = None
1648 self._wizard._arrivalMETAR = None
1649 bookedFlight = self._wizard._bookedFlight
1650 if returned:
1651 if bookedFlight.departureICAO in result.metars:
1652 self._wizard._departureMETAR = result.metars[bookedFlight.departureICAO]
1653 if bookedFlight.arrivalICAO in result.metars:
1654 self._wizard._arrivalMETAR = result.metars[bookedFlight.arrivalICAO]
1655
1656 self._wizard.gui.endBusy()
1657 self._backButton.set_sensitive(True)
1658 self._button.set_sensitive(True)
1659 self._wizard.nextPage()
1660
1661#-----------------------------------------------------------------------------
1662
1663class BriefingPage(Page):
1664 """Page for the briefing."""
1665 def __init__(self, wizard, departure):
1666 """Construct the briefing page."""
1667 self._departure = departure
1668
1669 title = xstr("briefing_title") % (1 if departure else 2,
1670 xstr("briefing_departure")
1671 if departure
1672 else xstr("briefing_arrival"))
1673 super(BriefingPage, self).__init__(wizard, title, xstr("briefing_help"),
1674 completedHelp = xstr("briefing_chelp"))
1675
1676 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1677 xscale = 1.0, yscale = 1.0)
1678
1679 mainBox = gtk.VBox()
1680 alignment.add(mainBox)
1681 self.setMainWidget(alignment)
1682
1683 self._notamsFrame = gtk.Frame()
1684 self._notamsFrame.set_label(xstr("briefing_notams_init"))
1685 scrolledWindow = gtk.ScrolledWindow()
1686 scrolledWindow.set_size_request(-1, 128)
1687 # FIXME: these constants should be in common
1688 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1689 else gtk.POLICY_AUTOMATIC,
1690 gtk.PolicyType.AUTOMATIC if pygobject
1691 else gtk.POLICY_AUTOMATIC)
1692 self._notams = gtk.TextView()
1693 self._notams.set_editable(False)
1694 self._notams.set_accepts_tab(False)
1695 self._notams.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
1696 scrolledWindow.add(self._notams)
1697 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
1698 xscale = 1.0, yscale = 1.0)
1699 alignment.set_padding(padding_top = 4, padding_bottom = 0,
1700 padding_left = 0, padding_right = 0)
1701 alignment.add(scrolledWindow)
1702 self._notamsFrame.add(alignment)
1703 mainBox.pack_start(self._notamsFrame, True, True, 4)
1704
1705 self._metarFrame = gtk.Frame()
1706 self._metarFrame.set_label(xstr("briefing_metar_init"))
1707 scrolledWindow = gtk.ScrolledWindow()
1708 scrolledWindow.set_size_request(-1, 32)
1709 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1710 else gtk.POLICY_AUTOMATIC,
1711 gtk.PolicyType.AUTOMATIC if pygobject
1712 else gtk.POLICY_AUTOMATIC)
1713
1714 self._uppercasingMETAR = False
1715
1716 self._metar = gtk.TextView()
1717 self._metar.set_accepts_tab(False)
1718 self._metar.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
1719 self._metar.get_buffer().connect("changed", self._metarChanged)
1720 self._metar.get_buffer().connect_after("insert-text", self._metarInserted)
1721 scrolledWindow.add(self._metar)
1722 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
1723 xscale = 1.0, yscale = 1.0)
1724 alignment.set_padding(padding_top = 4, padding_bottom = 0,
1725 padding_left = 0, padding_right = 0)
1726 alignment.add(scrolledWindow)
1727 self._metarFrame.add(alignment)
1728 mainBox.pack_start(self._metarFrame, True, True, 4)
1729 self.metarEdited = False
1730
1731 self.addCancelFlightButton()
1732
1733 self.addPreviousButton(clicked = self._backClicked)
1734 self._button = self.addNextButton(clicked = self._forwardClicked)
1735
1736 @property
1737 def metar(self):
1738 """Get the METAR on the page."""
1739 buffer = self._metar.get_buffer()
1740 return buffer.get_text(buffer.get_start_iter(),
1741 buffer.get_end_iter(), True)
1742
1743 def setMETAR(self, metar):
1744 """Set the metar."""
1745 self._metar.get_buffer().set_text(metar)
1746 self.metarEdited = False
1747
1748 def activate(self):
1749 """Activate the page."""
1750 if not self._departure:
1751 self._button.set_label(xstr("briefing_button"))
1752 self._button.set_has_tooltip(False)
1753 self._button.set_use_stock(False)
1754
1755 bookedFlight = self._wizard._bookedFlight
1756
1757 icao = bookedFlight.departureICAO if self._departure \
1758 else bookedFlight.arrivalICAO
1759 notams = self._wizard._departureNOTAMs if self._departure \
1760 else self._wizard._arrivalNOTAMs
1761 metar = self._wizard._departureMETAR if self._departure \
1762 else self._wizard._arrivalMETAR
1763
1764 self._notamsFrame.set_label(xstr("briefing_notams_template") % (icao,))
1765 buffer = self._notams.get_buffer()
1766 if notams is None:
1767 buffer.set_text(xstr("briefing_notams_failed"))
1768 elif not notams:
1769 buffer.set_text(xstr("briefing_notams_missing"))
1770 else:
1771 s = ""
1772 for notam in notams:
1773 s += str(notam.begin)
1774 if notam.end is not None:
1775 s += " - " + str(notam.end)
1776 elif notam.permanent:
1777 s += " - PERMANENT"
1778 s += "\n"
1779 if notam.repeatCycle:
1780 s += "Repeat cycle: " + notam.repeatCycle + "\n"
1781 s += notam.notice + "\n"
1782 s += "-------------------- * --------------------\n"
1783 buffer.set_text(s)
1784
1785 self._metarFrame.set_label(xstr("briefing_metar_template") % (icao,))
1786 buffer = self._metar.get_buffer()
1787 if metar is None:
1788 buffer.set_text(xstr("briefing_metar_failed"))
1789 else:
1790 buffer.set_text(metar)
1791
1792 label = self._metarFrame.get_label_widget()
1793 label.set_use_underline(True)
1794 label.set_mnemonic_widget(self._metar)
1795
1796 self.metarEdited = False
1797
1798 def _backClicked(self, button):
1799 """Called when the Back button is pressed."""
1800 self.goBack()
1801
1802 def _forwardClicked(self, button):
1803 """Called when the forward button is clicked."""
1804 if not self._departure:
1805 if not self._completed:
1806 self._wizard.gui.startMonitoring()
1807 self._button.set_label(xstr("button_next"))
1808 self._button.set_tooltip_text(xstr("button_next_tooltip"))
1809 self.complete()
1810
1811 self._wizard.nextPage()
1812
1813 def _metarChanged(self, buffer):
1814 """Called when the METAR has changed."""
1815 if not self._uppercasingMETAR:
1816 self.metarEdited = True
1817 self._button.set_sensitive(buffer.get_text(buffer.get_start_iter(),
1818 buffer.get_end_iter(),
1819 True)!="")
1820
1821 def _metarInserted(self, textBuffer, iter, text, length):
1822 """Called when new characters are inserted into the METAR.
1823
1824 It uppercases all characters."""
1825 if not self._uppercasingMETAR:
1826 self._uppercasingMETAR = True
1827
1828 iter1 = iter.copy()
1829 iter1.backward_chars(length)
1830 textBuffer.delete(iter, iter1)
1831
1832 textBuffer.insert(iter, text.upper())
1833
1834 self._uppercasingMETAR = False
1835
1836#-----------------------------------------------------------------------------
1837
1838class TakeoffPage(Page):
1839 """Page for entering the takeoff data."""
1840 def __init__(self, wizard):
1841 """Construct the takeoff page."""
1842 super(TakeoffPage, self).__init__(wizard, xstr("takeoff_title"),
1843 xstr("takeoff_help"),
1844 completedHelp = xstr("takeoff_chelp"))
1845
1846 self._forwardAllowed = False
1847
1848 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1849 xscale = 0.0, yscale = 0.0)
1850
1851 table = gtk.Table(5, 4)
1852 table.set_row_spacings(4)
1853 table.set_col_spacings(16)
1854 table.set_homogeneous(False)
1855 alignment.add(table)
1856 self.setMainWidget(alignment)
1857
1858 label = gtk.Label(xstr("takeoff_runway"))
1859 label.set_use_underline(True)
1860 label.set_alignment(0.0, 0.5)
1861 table.attach(label, 0, 1, 0, 1)
1862
1863 self._runway = gtk.Entry()
1864 self._runway.set_width_chars(10)
1865 self._runway.set_tooltip_text(xstr("takeoff_runway_tooltip"))
1866 self._runway.connect("changed", self._upperChanged)
1867 table.attach(self._runway, 1, 3, 0, 1)
1868 label.set_mnemonic_widget(self._runway)
1869
1870 label = gtk.Label(xstr("takeoff_sid"))
1871 label.set_use_underline(True)
1872 label.set_alignment(0.0, 0.5)
1873 table.attach(label, 0, 1, 1, 2)
1874
1875 self._sid = gtk.Entry()
1876 self._sid.set_width_chars(10)
1877 self._sid.set_tooltip_text(xstr("takeoff_sid_tooltip"))
1878 self._sid.connect("changed", self._upperChanged)
1879 table.attach(self._sid, 1, 3, 1, 2)
1880 label.set_mnemonic_widget(self._sid)
1881
1882 label = gtk.Label(xstr("takeoff_v1"))
1883 label.set_use_markup(True)
1884 label.set_use_underline(True)
1885 label.set_alignment(0.0, 0.5)
1886 table.attach(label, 0, 1, 2, 3)
1887
1888 self._v1 = IntegerEntry()
1889 self._v1.set_width_chars(4)
1890 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip_knots"))
1891 self._v1.connect("integer-changed", self._valueChanged)
1892 table.attach(self._v1, 2, 3, 2, 3)
1893 label.set_mnemonic_widget(self._v1)
1894
1895 self._v1Unit = gtk.Label(xstr("label_knots"))
1896 table.attach(self._v1Unit, 3, 4, 2, 3)
1897
1898 label = gtk.Label(xstr("takeoff_vr"))
1899 label.set_use_markup(True)
1900 label.set_use_underline(True)
1901 label.set_alignment(0.0, 0.5)
1902 table.attach(label, 0, 1, 3, 4)
1903
1904 self._vr = IntegerEntry()
1905 self._vr.set_width_chars(4)
1906 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip_knots"))
1907 self._vr.connect("integer-changed", self._valueChanged)
1908 table.attach(self._vr, 2, 3, 3, 4)
1909 label.set_mnemonic_widget(self._vr)
1910
1911 self._vrUnit = gtk.Label(xstr("label_knots"))
1912 table.attach(self._vrUnit, 3, 4, 3, 4)
1913
1914 label = gtk.Label(xstr("takeoff_v2"))
1915 label.set_use_markup(True)
1916 label.set_use_underline(True)
1917 label.set_alignment(0.0, 0.5)
1918 table.attach(label, 0, 1, 4, 5)
1919
1920 self._v2 = IntegerEntry()
1921 self._v2.set_width_chars(4)
1922 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip_knots"))
1923 self._v2.connect("integer-changed", self._valueChanged)
1924 table.attach(self._v2, 2, 3, 4, 5)
1925 label.set_mnemonic_widget(self._v2)
1926
1927 self._v2Unit = gtk.Label(xstr("label_knots"))
1928 table.attach(self._v2Unit, 3, 4, 4, 5)
1929
1930 self.addCancelFlightButton()
1931
1932 self.addPreviousButton(clicked = self._backClicked)
1933
1934 self._button = self.addNextButton(clicked = self._forwardClicked)
1935
1936 @property
1937 def runway(self):
1938 """Get the runway."""
1939 return self._runway.get_text()
1940
1941 @property
1942 def sid(self):
1943 """Get the SID."""
1944 return self._sid.get_text()
1945
1946 @property
1947 def v1(self):
1948 """Get the v1 speed."""
1949 return self._v1.get_int()
1950
1951 @property
1952 def vr(self):
1953 """Get the vr speed."""
1954 return self._vr.get_int()
1955
1956 @property
1957 def v2(self):
1958 """Get the v2 speed."""
1959 return self._v2.get_int()
1960
1961 def activate(self):
1962 """Activate the page."""
1963 self._runway.set_text("")
1964 self._runway.set_sensitive(True)
1965 self._sid.set_text("")
1966 self._sid.set_sensitive(True)
1967 self._v1.set_int(None)
1968 self._v1.set_sensitive(True)
1969 self._vr.set_int(None)
1970 self._vr.set_sensitive(True)
1971 self._v2.set_int(None)
1972 self._v2.set_sensitive(True)
1973
1974 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
1975 speedUnit = xstr("label" + i18nSpeedUnit)
1976 self._v1Unit.set_text(speedUnit)
1977 self._vrUnit.set_text(speedUnit)
1978 self._v2Unit.set_text(speedUnit)
1979
1980 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip" + i18nSpeedUnit))
1981 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip" + i18nSpeedUnit))
1982 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip" + i18nSpeedUnit))
1983
1984 self._button.set_sensitive(False)
1985 self._forwardAllowed = False
1986
1987 def allowForward(self):
1988 """Allow going to the next page."""
1989 self._forwardAllowed = True
1990 self._updateForwardButton()
1991
1992 def reset(self):
1993 """Reset the page if the wizard is reset."""
1994 super(TakeoffPage, self).reset()
1995 self._v1.reset()
1996 self._vr.reset()
1997 self._v2.reset()
1998
1999 def _updateForwardButton(self):
2000 """Update the sensitivity of the forward button based on some conditions."""
2001 sensitive = self._forwardAllowed and \
2002 self._runway.get_text()!="" and \
2003 self._sid.get_text()!="" and \
2004 self.v1 is not None and \
2005 self.vr is not None and \
2006 self.v2 is not None and \
2007 self.v1 <= self.vr and \
2008 self.vr <= self.v2
2009 self._button.set_sensitive(sensitive)
2010
2011 def _valueChanged(self, widget, arg = None):
2012 """Called when the value of some widget has changed."""
2013 self._updateForwardButton()
2014
2015 def _upperChanged(self, entry, arg = None):
2016 """Called when the value of some entry widget has changed and the value
2017 should be converted to uppercase."""
2018 entry.set_text(entry.get_text().upper())
2019 self._valueChanged(entry, arg)
2020
2021 def _backClicked(self, button):
2022 """Called when the Back button is pressed."""
2023 self.goBack()
2024
2025 def _forwardClicked(self, button):
2026 """Called when the forward button is clicked."""
2027 self._wizard.gui.flight.aircraft.updateV1R2()
2028 self._wizard.nextPage()
2029
2030#-----------------------------------------------------------------------------
2031
2032class LandingPage(Page):
2033 """Page for entering landing data."""
2034 def __init__(self, wizard):
2035 """Construct the landing page."""
2036 super(LandingPage, self).__init__(wizard, xstr("landing_title"),
2037 xstr("landing_help"),
2038 completedHelp = xstr("landing_chelp"))
2039
2040 self._flightEnded = False
2041
2042 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2043 xscale = 0.0, yscale = 0.0)
2044
2045 table = gtk.Table(5, 5)
2046 table.set_row_spacings(4)
2047 table.set_col_spacings(16)
2048 table.set_homogeneous(False)
2049 alignment.add(table)
2050 self.setMainWidget(alignment)
2051
2052 self._starButton = gtk.CheckButton()
2053 self._starButton.connect("clicked", self._starButtonClicked)
2054 table.attach(self._starButton, 0, 1, 0, 1)
2055
2056 label = gtk.Label(xstr("landing_star"))
2057 label.set_use_underline(True)
2058 label.set_alignment(0.0, 0.5)
2059 table.attach(label, 1, 2, 0, 1)
2060
2061 self._star = gtk.Entry()
2062 self._star.set_width_chars(10)
2063 self._star.set_tooltip_text(xstr("landing_star_tooltip"))
2064 self._star.connect("changed", self._upperChanged)
2065 self._star.set_sensitive(False)
2066 table.attach(self._star, 2, 4, 0, 1)
2067 label.set_mnemonic_widget(self._starButton)
2068
2069 self._transitionButton = gtk.CheckButton()
2070 self._transitionButton.connect("clicked", self._transitionButtonClicked)
2071 table.attach(self._transitionButton, 0, 1, 1, 2)
2072
2073 label = gtk.Label(xstr("landing_transition"))
2074 label.set_use_underline(True)
2075 label.set_alignment(0.0, 0.5)
2076 table.attach(label, 1, 2, 1, 2)
2077
2078 self._transition = gtk.Entry()
2079 self._transition.set_width_chars(10)
2080 self._transition.set_tooltip_text(xstr("landing_transition_tooltip"))
2081 self._transition.connect("changed", self._upperChanged)
2082 self._transition.set_sensitive(False)
2083 table.attach(self._transition, 2, 4, 1, 2)
2084 label.set_mnemonic_widget(self._transitionButton)
2085
2086 label = gtk.Label(xstr("landing_runway"))
2087 label.set_use_underline(True)
2088 label.set_alignment(0.0, 0.5)
2089 table.attach(label, 1, 2, 2, 3)
2090
2091 self._runway = gtk.Entry()
2092 self._runway.set_width_chars(10)
2093 self._runway.set_tooltip_text(xstr("landing_runway_tooltip"))
2094 self._runway.connect("changed", self._upperChanged)
2095 table.attach(self._runway, 2, 4, 2, 3)
2096 label.set_mnemonic_widget(self._runway)
2097
2098 label = gtk.Label(xstr("landing_approach"))
2099 label.set_use_underline(True)
2100 label.set_alignment(0.0, 0.5)
2101 table.attach(label, 1, 2, 3, 4)
2102
2103 self._approachType = gtk.Entry()
2104 self._approachType.set_width_chars(10)
2105 self._approachType.set_tooltip_text(xstr("landing_approach_tooltip"))
2106 self._approachType.connect("changed", self._upperChanged)
2107 table.attach(self._approachType, 2, 4, 3, 4)
2108 label.set_mnemonic_widget(self._approachType)
2109
2110 label = gtk.Label(xstr("landing_vref"))
2111 label.set_use_markup(True)
2112 label.set_use_underline(True)
2113 label.set_alignment(0.0, 0.5)
2114 table.attach(label, 1, 2, 5, 6)
2115
2116 self._vref = IntegerEntry()
2117 self._vref.set_width_chars(5)
2118 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip_knots"))
2119 self._vref.connect("integer-changed", self._vrefChanged)
2120 table.attach(self._vref, 3, 4, 5, 6)
2121 label.set_mnemonic_widget(self._vref)
2122
2123 self._vrefUnit = gtk.Label(xstr("label_knots"))
2124 table.attach(self._vrefUnit, 4, 5, 5, 6)
2125
2126 self.addCancelFlightButton()
2127
2128 self.addPreviousButton(clicked = self._backClicked)
2129
2130 self._button = self.addNextButton(clicked = self._forwardClicked)
2131
2132 # These are needed for correct size calculations
2133 self._starButton.set_active(True)
2134 self._transitionButton.set_active(True)
2135
2136 @property
2137 def star(self):
2138 """Get the STAR or None if none entered."""
2139 return self._star.get_text() if self._starButton.get_active() else None
2140
2141 @property
2142 def transition(self):
2143 """Get the transition or None if none entered."""
2144 return self._transition.get_text() \
2145 if self._transitionButton.get_active() else None
2146
2147 @property
2148 def approachType(self):
2149 """Get the approach type."""
2150 return self._approachType.get_text()
2151
2152 @property
2153 def runway(self):
2154 """Get the runway."""
2155 return self._runway.get_text()
2156
2157 @property
2158 def vref(self):
2159 """Return the landing reference speed."""
2160 return self._vref.get_int()
2161
2162 def reset(self):
2163 """Reset the page if the wizard is reset."""
2164 super(LandingPage, self).reset()
2165 self._vref.reset()
2166 self._flightEnded = False
2167
2168 def activate(self):
2169 """Called when the page is activated."""
2170 self._starButton.set_sensitive(True)
2171 self._starButton.set_active(False)
2172 self._star.set_text("")
2173
2174 self._transitionButton.set_sensitive(True)
2175 self._transitionButton.set_active(False)
2176 self._transition.set_text("")
2177
2178 self._runway.set_text("")
2179 self._runway.set_sensitive(True)
2180
2181 self._approachType.set_text("")
2182 self._approachType.set_sensitive(True)
2183
2184 self._vref.set_int(None)
2185 self._vref.set_sensitive(True)
2186
2187 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
2188 speedUnit = xstr("label" + i18nSpeedUnit)
2189 self._vrefUnit.set_text(speedUnit)
2190
2191 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip" +
2192 i18nSpeedUnit))
2193
2194 self._updateForwardButton()
2195
2196 def flightEnded(self):
2197 """Called when the flight has ended."""
2198 self._flightEnded = True
2199 self._updateForwardButton()
2200
2201 def _starButtonClicked(self, button):
2202 """Called when the STAR button is clicked."""
2203 active = button.get_active()
2204 self._star.set_sensitive(active)
2205 if active:
2206 self._star.grab_focus()
2207 self._updateForwardButton()
2208
2209 def _transitionButtonClicked(self, button):
2210 """Called when the Transition button is clicked."""
2211 active = button.get_active()
2212 self._transition.set_sensitive(active)
2213 if active:
2214 self._transition.grab_focus()
2215 self._updateForwardButton()
2216
2217 def _updateForwardButton(self):
2218 """Update the sensitivity of the forward button."""
2219 sensitive = self._flightEnded and \
2220 (self._starButton.get_active() or \
2221 self._transitionButton.get_active()) and \
2222 (self._star.get_text()!="" or
2223 not self._starButton.get_active()) and \
2224 (self._transition.get_text()!="" or
2225 not self._transitionButton.get_active()) and \
2226 self._runway.get_text()!="" and \
2227 self._approachType.get_text()!="" and \
2228 self.vref is not None
2229 self._button.set_sensitive(sensitive)
2230
2231 def _upperChanged(self, entry):
2232 """Called for entry widgets that must be converted to uppercase."""
2233 entry.set_text(entry.get_text().upper())
2234 self._updateForwardButton()
2235
2236 def _vrefChanged(self, widget, value):
2237 """Called when the Vref has changed."""
2238 self._updateForwardButton()
2239
2240 def _backClicked(self, button):
2241 """Called when the Back button is pressed."""
2242 self.goBack()
2243
2244 def _forwardClicked(self, button):
2245 """Called when the forward button is clicked."""
2246 self._wizard.gui.flight.aircraft.updateVRef()
2247 if self._wizard.gui.config.onlineGateSystem and \
2248 self._wizard.loggedIn and not self._completed and \
2249 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
2250 not self._wizard.entranceExam:
2251 self._wizard.getFleet(callback = self._fleetRetrieved,
2252 force = True)
2253 else:
2254 self._wizard.nextPage()
2255
2256 def _fleetRetrieved(self, fleet):
2257 """Callback for the fleet retrieval."""
2258 self._wizard.nextPage()
2259
2260#-----------------------------------------------------------------------------
2261
2262class FinishPage(Page):
2263 """Flight finish page."""
2264 _flightTypes = [ ("flighttype_scheduled", const.FLIGHTTYPE_SCHEDULED),
2265 ("flighttype_ot", const.FLIGHTTYPE_OLDTIMER),
2266 ("flighttype_vip", const.FLIGHTTYPE_VIP),
2267 ("flighttype_charter", const.FLIGHTTYPE_CHARTER) ]
2268
2269 def __init__(self, wizard):
2270 """Construct the finish page."""
2271 super(FinishPage, self).__init__(wizard, xstr("finish_title"),
2272 xstr("finish_help"))
2273
2274 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2275 xscale = 0.0, yscale = 0.0)
2276
2277 table = gtk.Table(8, 2)
2278 table.set_row_spacings(4)
2279 table.set_col_spacings(16)
2280 table.set_homogeneous(False)
2281 alignment.add(table)
2282 self.setMainWidget(alignment)
2283
2284 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2285 label = gtk.Label(xstr("finish_rating"))
2286 labelAlignment.add(label)
2287 table.attach(labelAlignment, 0, 1, 0, 1)
2288
2289 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2290 self._flightRating = gtk.Label()
2291 self._flightRating.set_width_chars(8)
2292 self._flightRating.set_alignment(0.0, 0.5)
2293 self._flightRating.set_use_markup(True)
2294 labelAlignment.add(self._flightRating)
2295 table.attach(labelAlignment, 1, 2, 0, 1)
2296
2297 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2298 label = gtk.Label(xstr("finish_flight_time"))
2299 labelAlignment.add(label)
2300 table.attach(labelAlignment, 0, 1, 1, 2)
2301
2302 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2303 self._flightTime = gtk.Label()
2304 self._flightTime.set_width_chars(10)
2305 self._flightTime.set_alignment(0.0, 0.5)
2306 self._flightTime.set_use_markup(True)
2307 labelAlignment.add(self._flightTime)
2308 table.attach(labelAlignment, 1, 2, 1, 2)
2309
2310 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2311 label = gtk.Label(xstr("finish_block_time"))
2312 labelAlignment.add(label)
2313 table.attach(labelAlignment, 0, 1, 2, 3)
2314
2315 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2316 self._blockTime = gtk.Label()
2317 self._blockTime.set_width_chars(10)
2318 self._blockTime.set_alignment(0.0, 0.5)
2319 self._blockTime.set_use_markup(True)
2320 labelAlignment.add(self._blockTime)
2321 table.attach(labelAlignment, 1, 2, 2, 3)
2322
2323 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2324 label = gtk.Label(xstr("finish_distance"))
2325 labelAlignment.add(label)
2326 table.attach(labelAlignment, 0, 1, 3, 4)
2327
2328 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2329 self._distanceFlown = gtk.Label()
2330 self._distanceFlown.set_width_chars(10)
2331 self._distanceFlown.set_alignment(0.0, 0.5)
2332 self._distanceFlown.set_use_markup(True)
2333 labelAlignment.add(self._distanceFlown)
2334 table.attach(labelAlignment, 1, 2, 3, 4)
2335
2336 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2337 label = gtk.Label(xstr("finish_fuel"))
2338 labelAlignment.add(label)
2339 table.attach(labelAlignment, 0, 1, 4, 5)
2340
2341 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2342 self._fuelUsed = gtk.Label()
2343 self._fuelUsed.set_width_chars(10)
2344 self._fuelUsed.set_alignment(0.0, 0.5)
2345 self._fuelUsed.set_use_markup(True)
2346 labelAlignment.add(self._fuelUsed)
2347 table.attach(labelAlignment, 1, 2, 4, 5)
2348
2349 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
2350 yalign = 0.5, yscale = 0.0)
2351 label = gtk.Label(xstr("finish_type"))
2352 label.set_use_underline(True)
2353 labelAlignment.add(label)
2354 table.attach(labelAlignment, 0, 1, 5, 6)
2355
2356 flightTypeModel = gtk.ListStore(str, int)
2357 for (name, type) in FinishPage._flightTypes:
2358 flightTypeModel.append([xstr(name), type])
2359
2360 self._flightType = gtk.ComboBox(model = flightTypeModel)
2361 renderer = gtk.CellRendererText()
2362 self._flightType.pack_start(renderer, True)
2363 self._flightType.add_attribute(renderer, "text", 0)
2364 self._flightType.set_tooltip_text(xstr("finish_type_tooltip"))
2365 self._flightType.set_active(0)
2366 self._flightType.connect("changed", self._flightTypeChanged)
2367 flightTypeAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2368 flightTypeAlignment.add(self._flightType)
2369 table.attach(flightTypeAlignment, 1, 2, 5, 6)
2370 label.set_mnemonic_widget(self._flightType)
2371
2372 self._onlineFlight = gtk.CheckButton(xstr("finish_online"))
2373 self._onlineFlight.set_use_underline(True)
2374 self._onlineFlight.set_tooltip_text(xstr("finish_online_tooltip"))
2375 onlineFlightAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2376 onlineFlightAlignment.add(self._onlineFlight)
2377 table.attach(onlineFlightAlignment, 1, 2, 6, 7)
2378
2379 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
2380 yalign = 0.5, yscale = 0.0)
2381 self._gateLabel = gtk.Label(xstr("finish_gate"))
2382 self._gateLabel.set_use_underline(True)
2383 labelAlignment.add(self._gateLabel)
2384 table.attach(labelAlignment, 0, 1, 7, 8)
2385
2386 self._gatesModel = gtk.ListStore(str)
2387
2388 self._gate = gtk.ComboBox(model = self._gatesModel)
2389 renderer = gtk.CellRendererText()
2390 self._gate.pack_start(renderer, True)
2391 self._gate.add_attribute(renderer, "text", 0)
2392 self._gate.set_tooltip_text(xstr("finish_gate_tooltip"))
2393 self._gate.connect("changed", self._gateChanged)
2394 gateAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
2395 gateAlignment.add(self._gate)
2396 table.attach(gateAlignment, 1, 2, 7, 8)
2397 self._gateLabel.set_mnemonic_widget(self._gate)
2398
2399 self.addButton(xstr("finish_newFlight"),
2400 sensitive = True,
2401 clicked = self._newFlightClicked,
2402 tooltip = xstr("finish_newFlight_tooltip"),
2403 padding = 16)
2404
2405 self.addPreviousButton(clicked = self._backClicked)
2406
2407 self._saveButton = self.addButton(xstr("finish_save"),
2408 sensitive = False,
2409 clicked = self._saveClicked,
2410 tooltip = xstr("finish_save_tooltip"))
2411 self._savePIREPDialog = None
2412 self._lastSavePath = None
2413
2414 self._pirepSaved = False
2415 self._pirepSent = False
2416
2417 self._sendButton = self.addButton(xstr("sendPIREP"), default = True,
2418 sensitive = False,
2419 clicked = self._sendClicked,
2420 tooltip = xstr("sendPIREP_tooltip"))
2421
2422 @property
2423 def flightType(self):
2424 """Get the flight type."""
2425 index = self._flightType.get_active()
2426 return None if index<0 else self._flightType.get_model()[index][1]
2427
2428 @property
2429 def online(self):
2430 """Get whether the flight was an online flight or not."""
2431 return self._onlineFlight.get_active()
2432
2433 def activate(self):
2434 """Activate the page."""
2435 self._pirepSaved = False
2436 self._pirepSent = False
2437
2438 flight = self._wizard.gui._flight
2439 rating = flight.logger.getRating()
2440 if rating<0:
2441 self._flightRating.set_markup('<b><span foreground="red">NO GO</span></b>')
2442 else:
2443 self._flightRating.set_markup("<b>%.1f %%</b>" % (rating,))
2444
2445 flightLength = flight.flightTimeEnd - flight.flightTimeStart
2446 self._flightTime.set_markup("<b>%s</b>" % \
2447 (util.getTimeIntervalString(flightLength),))
2448
2449 blockLength = flight.blockTimeEnd - flight.blockTimeStart
2450 self._blockTime.set_markup("<b>%s</b>" % \
2451 (util.getTimeIntervalString(blockLength),))
2452
2453 self._distanceFlown.set_markup("<b>%.2f NM</b>" % \
2454 (flight.flownDistance,))
2455
2456 self._fuelUsed.set_markup("<b>%.0f kg</b>" % \
2457 (flight.startFuel - flight.endFuel,))
2458
2459 self._flightType.set_active(-1)
2460 self._onlineFlight.set_active(self._wizard.loggedIn)
2461
2462 self._gatesModel.clear()
2463 if self._wizard.gui.config.onlineGateSystem and \
2464 self._wizard.loggedIn and \
2465 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
2466 not self._wizard.entranceExam:
2467 occupiedGates = self._wizard._fleet.getOccupiedGateNumbers()
2468 for gateNumber in const.lhbpGateNumbers:
2469 if gateNumber not in occupiedGates:
2470 self._gatesModel.append([gateNumber])
2471 self._gateLabel.set_sensitive(True)
2472 self._gate.set_sensitive(True)
2473 self._gate.set_active(-1)
2474 else:
2475 self._gateLabel.set_sensitive(False)
2476 self._gate.set_sensitive(False)
2477
2478 def _backClicked(self, button):
2479 """Called when the Back button is pressed."""
2480 self.goBack()
2481
2482 def _updateButtons(self):
2483 """Update the sensitivity state of the buttons."""
2484 sensitive = self._flightType.get_active()>=0 and \
2485 (self._gatesModel.get_iter_first() is None or
2486 self._gate.get_active()>=0)
2487
2488 self._saveButton.set_sensitive(sensitive)
2489 self._sendButton.set_sensitive(sensitive and
2490 self._wizard.bookedFlight.id is not None)
2491
2492 def _flightTypeChanged(self, comboBox):
2493 """Called when the flight type has changed."""
2494 self._updateButtons()
2495
2496 def _gateChanged(self, comboBox):
2497 """Called when the arrival gate has changed."""
2498 self._updateButtons()
2499
2500 def _newFlightClicked(self, button):
2501 """Called when the new flight button is clicked."""
2502 gui = self._wizard.gui
2503 if not self._pirepSent and not self._pirepSaved:
2504 dialog = gtk.MessageDialog(parent = gui.mainWindow,
2505 type = MESSAGETYPE_QUESTION,
2506 message_format = xstr("finish_newFlight_question"))
2507
2508 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
2509 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
2510
2511 dialog.set_title(WINDOW_TITLE_BASE)
2512 result = dialog.run()
2513 dialog.hide()
2514 if result!=RESPONSETYPE_YES:
2515 return
2516
2517 gui.reset()
2518
2519 def _saveClicked(self, button):
2520 """Called when the Save PIREP button is clicked."""
2521 gui = self._wizard.gui
2522
2523 bookedFlight = gui.bookedFlight
2524 tm = time.gmtime()
2525
2526 pilotID = self._wizard.pilotID
2527 if pilotID: pilotID += " "
2528 fileName = "%s%s %02d%02d %s-%s.pirep" % \
2529 (pilotID, str(bookedFlight.departureTime.date()),
2530 tm.tm_hour, tm.tm_min,
2531 bookedFlight.departureICAO,
2532 bookedFlight.arrivalICAO)
2533
2534 dialog = self._getSaveDialog()
2535
2536 if self._lastSavePath is None:
2537 pirepDirectory = gui.config.pirepDirectory
2538 if pirepDirectory is not None:
2539 dialog.set_current_folder(pirepDirectory)
2540 else:
2541 dialog.set_current_folder(os.path.dirname(self._lastSavePath))
2542
2543 dialog.set_current_name(fileName)
2544 result = dialog.run()
2545 dialog.hide()
2546
2547 if result==RESPONSETYPE_OK:
2548 pirep = PIREP(gui)
2549
2550 self._lastSavePath = text2unicode(dialog.get_filename())
2551
2552 if pirep.save(self._lastSavePath):
2553 type = MESSAGETYPE_INFO
2554 message = xstr("finish_save_done")
2555 secondary = None
2556 self._pirepSaved = True
2557 else:
2558 type = MESSAGETYPE_ERROR
2559 message = xstr("finish_save_failed")
2560 secondary = xstr("finish_save_failed_sec")
2561
2562 dialog = gtk.MessageDialog(parent = gui.mainWindow,
2563 type = type, message_format = message)
2564 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
2565 dialog.set_title(WINDOW_TITLE_BASE)
2566 if secondary is not None:
2567 dialog.format_secondary_markup(secondary)
2568
2569 dialog.run()
2570 dialog.hide()
2571
2572 def _getSaveDialog(self):
2573 """Get the PIREP saving dialog.
2574
2575 If it does not exist yet, create it."""
2576 if self._savePIREPDialog is None:
2577 gui = self._wizard.gui
2578 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
2579 xstr("finish_save_title"),
2580 action = FILE_CHOOSER_ACTION_SAVE,
2581 buttons = (gtk.STOCK_CANCEL,
2582 RESPONSETYPE_CANCEL,
2583 gtk.STOCK_OK, RESPONSETYPE_OK),
2584 parent = gui.mainWindow)
2585 dialog.set_modal(True)
2586 dialog.set_do_overwrite_confirmation(True)
2587
2588 filter = gtk.FileFilter()
2589 filter.set_name(xstr("file_filter_pireps"))
2590 filter.add_pattern("*.pirep")
2591 dialog.add_filter(filter)
2592
2593 filter = gtk.FileFilter()
2594 filter.set_name(xstr("file_filter_all"))
2595 filter.add_pattern("*.*")
2596 dialog.add_filter(filter)
2597
2598 self._savePIREPDialog = dialog
2599
2600 return self._savePIREPDialog
2601
2602
2603 def _sendClicked(self, button):
2604 """Called when the Send button is clicked."""
2605 pirep = PIREP(self._wizard.gui)
2606 self._wizard.gui.sendPIREP(pirep,
2607 callback = self._handlePIREPSent)
2608
2609 def _handlePIREPSent(self, returned, result):
2610 """Callback for the PIREP sending result."""
2611 self._pirepSent = returned and result.success
2612 if self._wizard.gui.config.onlineGateSystem and \
2613 self._wizard.loggedIn and not self._wizard.entranceExam and \
2614 returned and result.success:
2615 bookedFlight = self._wizard.bookedFlight
2616 if bookedFlight.arrivalICAO=="LHBP":
2617 iter = self._gate.get_active_iter()
2618 gateNumber = None if iter is None \
2619 else self._gatesModel.get_value(iter, 0)
2620
2621 status = const.PLANE_PARKING if gateNumber is None \
2622 else const.PLANE_HOME
2623 else:
2624 gateNumber = None
2625 status = const.PLANE_AWAY
2626
2627 self._wizard.updatePlane(self._planeUpdated,
2628 bookedFlight.tailNumber,
2629 status, gateNumber = gateNumber)
2630
2631 def _planeUpdated(self, success):
2632 """Callback for the plane updating."""
2633 pass
2634
2635#-----------------------------------------------------------------------------
2636
2637class Wizard(gtk.VBox):
2638 """The flight wizard."""
2639 def __init__(self, gui):
2640 """Construct the wizard."""
2641 super(Wizard, self).__init__()
2642
2643 self.gui = gui
2644
2645 self._pages = []
2646 self._currentPage = None
2647
2648 self._loginPage = LoginPage(self)
2649 self._pages.append(self._loginPage)
2650 self._pages.append(FlightSelectionPage(self))
2651 self._pages.append(GateSelectionPage(self))
2652 self._pages.append(ConnectPage(self))
2653 self._payloadPage = PayloadPage(self)
2654 self._pages.append(self._payloadPage)
2655 self._payloadIndex = len(self._pages)
2656 self._pages.append(TimePage(self))
2657 self._pages.append(FuelPage(self))
2658 self._routePage = RoutePage(self)
2659 self._pages.append(self._routePage)
2660 self._departureBriefingPage = BriefingPage(self, True)
2661 self._pages.append(self._departureBriefingPage)
2662 self._arrivalBriefingPage = BriefingPage(self, False)
2663 self._pages.append(self._arrivalBriefingPage)
2664 self._arrivalBriefingIndex = len(self._pages)
2665 self._takeoffPage = TakeoffPage(self)
2666 self._pages.append(self._takeoffPage)
2667 self._landingPage = LandingPage(self)
2668 self._pages.append(self._landingPage)
2669 self._finishPage = FinishPage(self)
2670 self._pages.append(self._finishPage)
2671
2672 maxWidth = 0
2673 maxHeight = 0
2674 for page in self._pages:
2675 page.show_all()
2676 pageSizeRequest = page.size_request()
2677 width = pageSizeRequest.width if pygobject else pageSizeRequest[0]
2678 height = pageSizeRequest.height if pygobject else pageSizeRequest[1]
2679 maxWidth = max(maxWidth, width)
2680 maxHeight = max(maxHeight, height)
2681 page.setStyle()
2682 maxWidth += 16
2683 maxHeight += 32
2684 self.set_size_request(maxWidth, maxHeight)
2685
2686 self._initialize()
2687
2688 @property
2689 def pilotID(self):
2690 """Get the pilot ID, if given."""
2691 return self._loginPage.pilotID
2692
2693 @property
2694 def entranceExam(self):
2695 """Get whether an entrance exam is about to be taken."""
2696 return self._loginPage.entranceExam
2697
2698 @property
2699 def loggedIn(self):
2700 """Indicate if there was a successful login."""
2701 return self._loginResult is not None
2702
2703 @property
2704 def loginResult(self):
2705 """Get the login result."""
2706 return self._loginResult
2707
2708 def setCurrentPage(self, index, finalize = False):
2709 """Set the current page to the one with the given index."""
2710 assert index < len(self._pages)
2711
2712 fromPage = self._currentPage
2713 if fromPage is not None:
2714 page = self._pages[fromPage]
2715 if finalize and not page._completed:
2716 page.complete()
2717 self.remove(page)
2718
2719 self._currentPage = index
2720 page = self._pages[index]
2721 self.add(page)
2722 if page._fromPage is None:
2723 page._fromPage = fromPage
2724 page.initialize()
2725 self.show_all()
2726 if fromPage is not None:
2727 self.grabDefault()
2728
2729 @property
2730 def bookedFlight(self):
2731 """Get the booked flight selected."""
2732 return self._bookedFlight
2733
2734 @property
2735 def cargoWeight(self):
2736 """Get the calculated ZFW value."""
2737 return self._payloadPage.cargoWeight
2738
2739 @property
2740 def zfw(self):
2741 """Get the calculated ZFW value."""
2742 return 0 if self._bookedFlight is None \
2743 else self._payloadPage.calculateZFW()
2744
2745 @property
2746 def filedCruiseAltitude(self):
2747 """Get the filed cruise altitude."""
2748 return self._routePage.filedCruiseLevel * 100
2749
2750 @property
2751 def cruiseAltitude(self):
2752 """Get the cruise altitude."""
2753 return self._routePage.cruiseLevel * 100
2754
2755 @property
2756 def route(self):
2757 """Get the route."""
2758 return self._routePage.route
2759
2760 @property
2761 def departureMETAR(self):
2762 """Get the METAR of the departure airport."""
2763 return self._departureBriefingPage.metar
2764
2765 @property
2766 def arrivalMETAR(self):
2767 """Get the METAR of the arrival airport."""
2768 return self._arrivalBriefingPage.metar
2769
2770 @property
2771 def departureRunway(self):
2772 """Get the departure runway."""
2773 return self._takeoffPage.runway
2774
2775 @property
2776 def sid(self):
2777 """Get the SID."""
2778 return self._takeoffPage.sid
2779
2780 @property
2781 def v1(self):
2782 """Get the V1 speed."""
2783 return self._takeoffPage.v1
2784
2785 @property
2786 def vr(self):
2787 """Get the Vr speed."""
2788 return self._takeoffPage.vr
2789
2790 @property
2791 def v2(self):
2792 """Get the V2 speed."""
2793 return self._takeoffPage.v2
2794
2795 @property
2796 def arrivalRunway(self):
2797 """Get the arrival runway."""
2798 return self._landingPage.runway
2799
2800 @property
2801 def star(self):
2802 """Get the STAR."""
2803 return self._landingPage.star
2804
2805 @property
2806 def transition(self):
2807 """Get the transition."""
2808 return self._landingPage.transition
2809
2810 @property
2811 def approachType(self):
2812 """Get the approach type."""
2813 return self._landingPage.approachType
2814
2815 @property
2816 def vref(self):
2817 """Get the Vref speed."""
2818 return self._landingPage.vref
2819
2820 @property
2821 def flightType(self):
2822 """Get the flight type."""
2823 return self._finishPage.flightType
2824
2825 @property
2826 def online(self):
2827 """Get whether the flight was online or not."""
2828 return self._finishPage.online
2829
2830 def nextPage(self, finalize = True):
2831 """Go to the next page."""
2832 self.jumpPage(1, finalize)
2833
2834 def jumpPage(self, count, finalize = True):
2835 """Go to the page which is 'count' pages after the current one."""
2836 self.setCurrentPage(self._currentPage + count, finalize = finalize)
2837
2838 def grabDefault(self):
2839 """Make the default button of the current page the default."""
2840 self._pages[self._currentPage].grabDefault()
2841
2842 def connected(self, fsType, descriptor):
2843 """Called when the connection could be made to the simulator."""
2844 self.nextPage()
2845
2846 def reset(self, loginResult):
2847 """Resets the wizard to go back to the login page."""
2848 self._initialize(keepLoginResult = loginResult is None,
2849 loginResult = loginResult)
2850
2851 def setStage(self, stage):
2852 """Set the flight stage to the given one."""
2853 if stage==const.STAGE_TAKEOFF:
2854 self._takeoffPage.allowForward()
2855 elif stage==const.STAGE_LANDING:
2856 if not self._arrivalBriefingPage.metarEdited:
2857 print "Downloading arrival METAR again"
2858 self.gui.webHandler.getMETARs(self._arrivalMETARCallback,
2859 [self._bookedFlight.arrivalICAO])
2860
2861 elif stage==const.STAGE_END:
2862 self._landingPage.flightEnded()
2863
2864 def _initialize(self, keepLoginResult = False, loginResult = None):
2865 """Initialize the wizard."""
2866 if not keepLoginResult:
2867 self._loginResult = loginResult
2868
2869 self._loginCallback = None
2870
2871 self._fleet = None
2872 self._fleetCallback = None
2873
2874 self._bookedFlight = None
2875 self._departureGate = "-"
2876 self._fuelData = None
2877 self._departureNOTAMs = None
2878 self._departureMETAR = None
2879 self._arrivalNOTAMs = None
2880 self._arrivalMETAR = None
2881
2882 firstPage = 0 if self._loginResult is None else 1
2883 for page in self._pages[firstPage:]:
2884 page.reset()
2885
2886 self.setCurrentPage(firstPage)
2887
2888 def login(self, callback, pilotID, password, entranceExam):
2889 """Called when the login button was clicked."""
2890 self._loginCallback = callback
2891 if pilotID is None:
2892 loginResult = self._loginResult
2893 assert loginResult is not None and loginResult.loggedIn
2894 pilotID = loginResult.pilotID
2895 password = loginResult.password
2896 entranceExam = loginResult.entranceExam
2897 busyMessage = xstr("reload_busy")
2898 else:
2899 self._loginResult = None
2900 busyMessage = xstr("login_busy")
2901
2902 self.gui.beginBusy(busyMessage)
2903
2904 self.gui.webHandler.login(self._loginResultCallback,
2905 pilotID, password,
2906 entranceExam = entranceExam)
2907
2908 def reloadFlights(self, callback):
2909 """Reload the flights from the MAVA server."""
2910 self.login(callback, None, None, None)
2911
2912 def _loginResultCallback(self, returned, result):
2913 """The login result callback, called in the web handler's thread."""
2914 gobject.idle_add(self._handleLoginResult, returned, result)
2915
2916 def _handleLoginResult(self, returned, result):
2917 """Handle the login result."""
2918 self.gui.endBusy()
2919 isReload = self._loginResult is not None
2920 if returned:
2921 if result.loggedIn:
2922 self._loginResult = result
2923 else:
2924 if isReload:
2925 message = xstr("reload_failed")
2926 else:
2927 message = xstr("login_entranceExam_invalid"
2928 if self.entranceExam else
2929 xstr("login_invalid"))
2930 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
2931 type = MESSAGETYPE_ERROR,
2932 message_format = message)
2933 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
2934 dialog.set_title(WINDOW_TITLE_BASE)
2935 if isReload:
2936 secondary = xstr("reload_failed_sec")
2937 else:
2938 secondary = xstr("login_entranceExam_invalid_sec"
2939 if self.entranceExam else
2940 xstr("login_invalid_sec"))
2941 dialog.format_secondary_markup(secondary)
2942 dialog.run()
2943 dialog.hide()
2944 else:
2945 message = xstr("reload_failconn") if isReload \
2946 else xstr("login_failconn")
2947 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
2948 type = MESSAGETYPE_ERROR,
2949 message_format = message)
2950 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
2951 dialog.set_title(WINDOW_TITLE_BASE)
2952 secondary = xstr("reload_failconn_sec") if isReload \
2953 else xstr("login_failconn_sec")
2954 dialog.format_secondary_markup(secondary)
2955
2956 dialog.run()
2957 dialog.hide()
2958
2959 callback = self._loginCallback
2960 self._loginCallback = None
2961 callback(returned, result)
2962
2963 def getFleet(self, callback, force = False):
2964 """Get the fleet via the GUI and call the given callback."""
2965 self._fleetCallback = callback
2966 self.gui.getFleet(callback = self._fleetRetrieved, force = force)
2967
2968 def _fleetRetrieved(self, fleet):
2969 """Callback for the fleet retrieval."""
2970 self._fleet = fleet
2971 if self._fleetCallback is not None:
2972 self._fleetCallback(fleet)
2973 self._fleetCallback = None
2974
2975 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
2976 """Update the given plane's gate information."""
2977 self.gui.updatePlane(tailNumber, status, gateNumber = gateNumber,
2978 callback = callback)
2979
2980 def _connectSimulator(self):
2981 """Connect to the simulator."""
2982 self.gui.connectSimulator(self._bookedFlight.aircraftType)
2983
2984 def _arrivalMETARCallback(self, returned, result):
2985 """Called when the METAR of the arrival airport is retrieved."""
2986 gobject.idle_add(self._handleArrivalMETAR, returned, result)
2987
2988 def _handleArrivalMETAR(self, returned, result):
2989 """Called when the METAR of the arrival airport is retrieved."""
2990 icao = self._bookedFlight.arrivalICAO
2991 if returned and icao in result.metars:
2992 metar = result.metars[icao]
2993 if metar!="":
2994 self._arrivalBriefingPage.setMETAR(metar)
2995
2996#-----------------------------------------------------------------------------
2997
Note: See TracBrowser for help on using the repository browser.