source: src/mlx/gui/flight.py@ 214:19fb20505b1a

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

Added support for saving a flight into a file

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