source: src/mlx/gui/flight.py@ 208:22ff615383e9

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

It is now possible to cancel a flight and to start a new one at the end and also to refresh the list of flights.

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