source: src/mlx/gui/flight.py@ 232:e8102d6ba023

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

Fixed some problems with starting a new flight

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