source: src/mlx/gui/flight.py@ 989:d60ebbdef36c

python3
Last change on this file since 989:d60ebbdef36c was 989:d60ebbdef36c, checked in by István Váradi <ivaradi@…>, 5 years ago

The width of the fuel page has been significantly reduced (re #347).

File size: 224.5 KB
Line 
1# -*- encoding: utf-8 -*-
2
3from mlx.gui.common import *
4import mlx.gui.cef as cef
5from mlx.gui.flightlist import ColumnDescriptor, FlightList, PendingFlightsWindow
6
7import mlx.const as const
8import mlx.fs as fs
9import mlx.acft as acft
10from mlx.flight import Flight
11from mlx.checks import PayloadChecker
12from mlx.gates import lhbpGates
13import mlx.util as util
14from mlx.pirep import PIREP
15from mlx.i18n import xstr, getLanguage
16from mlx.sound import startSound
17import mlx.web as web
18
19import datetime
20import time
21import os
22import tempfile
23import threading
24import re
25import webbrowser
26
27#-----------------------------------------------------------------------------
28
29## @package mlx.gui.flight
30#
31# The flight "wizard".
32#
33# This module implements the main tab of the application, the flight
34# wizard. The wizard consists of \ref Page "pages", that come one after the
35# other. As some pages might be skipped, the pages dynamically store the index
36# of the previous page so that going back to it is simpler. The \ref
37# Page.activate "activate" function is called before a page is first shown
38# during a flight. This function should initialize the page's controls and fill
39# it with initial data. When a page is left for the first time, its \ref
40# Page.finalize "finalize" function is called. It should set those controls
41# insensitive, that will not be available if the user comes back to this page.
42#
43# Each page has a title at the top displayed in inverted colors and a big
44# font. There is a help text below it centered, that shortly describes what is
45# expected on the page. There can be two help texts: one shown when the page is
46# first displayed during a flight, another shown when the user goes back to the
47# page. The main content area is below this, also centered. Finally, there are
48# some buttons at the bottom on the right. As some buttons occur very
49# frequently, there are functions to add them (\ref Page.addCancelFlightButton
50# "addCancelFlightButton", \ref Page.addPreviousButton "addPreviousButton" and
51# \ref Page.addNextButton "addNextButton".
52#
53# The \ref Wizard class is the main class to collect the pages. It also stores
54# some data passed from one page to another and provides properties to access
55# data items set via the wizard pages.
56
57#-----------------------------------------------------------------------------
58
59comboModel = gtk.ListStore(gobject.TYPE_STRING)
60comboModel.append(("N/A",))
61comboModel.append(("VECTORS",))
62
63#-----------------------------------------------------------------------------
64
65class Page(gtk.Alignment):
66 """A page in the flight wizard."""
67 def __init__(self, wizard, id, title, help, completedHelp = None):
68 """Construct the page."""
69 super(Page, self).__init__(xalign = 0.0, yalign = 0.0,
70 xscale = 1.0, yscale = 1.0)
71 self.set_padding(padding_top = 4, padding_bottom = 4,
72 padding_left = 12, padding_right = 12)
73
74 frame = gtk.Frame()
75 self.add(frame)
76
77 self._vbox = gtk.VBox()
78 self._vbox.set_homogeneous(False)
79 frame.add(self._vbox)
80
81 eventBox = gtk.EventBox()
82
83 alignment = gtk.Alignment(xalign = 0.0, xscale = 0.0)
84
85 titleLabel = gtk.Label(title)
86 titleLabel.modify_font(pango.FontDescription("bold 24"))
87 alignment.set_padding(padding_top = 4, padding_bottom = 4,
88 padding_left = 6, padding_right = 0)
89
90 alignment.add(titleLabel)
91 eventBox.add(alignment)
92
93 self._vbox.pack_start(eventBox, False, False, 0)
94
95 self._titleEventBox = eventBox
96 self._titleLabel = titleLabel
97
98 mainBox = gtk.VBox()
99
100 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
101 xscale = 1.0, yscale = 1.0)
102 alignment.set_padding(padding_top = 16, padding_bottom = 16,
103 padding_left = 16, padding_right = 16)
104 alignment.add(mainBox)
105 self._vbox.pack_start(alignment, True, True, 0)
106
107 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
108 xscale = 0.0, yscale = 0.0)
109 alignment.set_padding(padding_top = 0, padding_bottom = 16,
110 padding_left = 0, padding_right = 0)
111
112 self._help = help
113 self._completedHelp = completedHelp
114
115 if self._completedHelp is None or \
116 len(help.splitlines())>=len(completedHelp.splitlines()):
117 longerHelp = help
118 else:
119 longerHelp = completedHelp
120
121 self._helpLabel = gtk.Label(longerHelp)
122 # FIXME: should be a constant in common
123 self._helpLabel.set_justify(gtk.Justification.CENTER if pygobject
124 else gtk.JUSTIFY_CENTER)
125 self._helpLabel.set_use_markup(True)
126 alignment.add(self._helpLabel)
127 mainBox.pack_start(alignment, False, False, 0)
128
129 self._mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
130 xscale = 1.0, yscale = 1.0)
131 mainBox.pack_start(self._mainAlignment, True, True, 0)
132
133 buttonAlignment = gtk.Alignment(xalign = 1.0, xscale=0.0, yscale = 0.0)
134 buttonAlignment.set_padding(padding_top = 4, padding_bottom = 10,
135 padding_left = 16, padding_right = 16)
136
137 self._buttonBox = gtk.HBox()
138 self._buttonBox.set_homogeneous(False)
139 self._defaultButton = None
140 buttonAlignment.add(self._buttonBox)
141
142 self._vbox.pack_start(buttonAlignment, False, False, 0)
143
144 self._wizard = wizard
145 self._id = id
146 self._nextPageID = None
147
148 self._cancelFlightButton = None
149
150 self._completed = False
151 self._fromPage = None
152
153 @property
154 def id(self):
155 """Get the identifier of the page."""
156 return self._id
157
158 @property
159 def nextPageID(self):
160 """Get the identifier of the next page, if set."""
161 return self._nextPageID
162
163 @nextPageID.setter
164 def nextPageID(self, nextPageID):
165 """Set the identifier of the next page."""
166 self._nextPageID = nextPageID
167
168 def setMainWidget(self, widget):
169 """Set the given widget as the main one."""
170 self._mainAlignment.add(widget)
171
172 def addButton(self, label, default = False, sensitive = True,
173 tooltip = None, clicked = None, padding = 4,
174 clickedArg = None):
175 """Add a button with the given label.
176
177 Return the button object created."""
178 button = gtk.Button(label)
179 self._buttonBox.pack_start(button, False, False, padding)
180 button.set_use_underline(True)
181 if default:
182 button.set_can_default(True)
183 self._defaultButton = button
184 button.set_sensitive(sensitive)
185 if tooltip is not None:
186 button.set_tooltip_text(tooltip)
187 if clicked is not None:
188 if clickedArg is None:
189 button.connect("clicked", clicked)
190 else:
191 button.connect("clicked", clicked, clickedArg)
192 return button
193
194 def addCancelFlightButton(self):
195 """Add the 'Cancel flight' button to the page."""
196 self._cancelFlightButton = \
197 self.addButton(xstr("button_cancelFlight"),
198 sensitive = True,
199 tooltip = xstr("button_cancelFlight_tooltip"),
200 clicked = self._cancelFlight,
201 padding = 16)
202 return self._cancelFlightButton
203
204 def addPreviousButton(self, sensitive = True, clicked = None):
205 """Add the 'Next' button to the page."""
206 return self.addButton(xstr("button_previous"),
207 sensitive = sensitive,
208 tooltip = xstr("button_previous_tooltip"),
209 clicked = clicked)
210
211 def addNextButton(self, default = True, sensitive = True,
212 clicked = None):
213 """Add the 'Next' button to the page."""
214 return self.addButton(xstr("button_next"),
215 default = default,
216 sensitive = sensitive,
217 tooltip = xstr("button_next_tooltip"),
218 clicked = clicked)
219
220 def setStyle(self):
221 """Set the styles of some of the items on the page."""
222 if pygobject:
223 context = self.get_style_context()
224 color = context.get_background_color(gtk.StateFlags.SELECTED)
225 self._titleEventBox.modify_bg(0, color.to_color())
226 color = context.get_color(gtk.StateFlags.SELECTED)
227 self._titleLabel.modify_fg(0, color.to_color())
228 else:
229 style = self.rc_get_style()
230 self._titleEventBox.modify_bg(0, style.bg[3])
231 self._titleLabel.modify_fg(0, style.fg[3])
232
233 def initialize(self):
234 """Initialize the page.
235
236 It sets up the primary help, and calls the activate() function."""
237 self._helpLabel.set_markup(self._help)
238 self._helpLabel.set_sensitive(True)
239 self.activate()
240
241 def activate(self):
242 """Called when this page becomes active.
243
244 This default implementation does nothing."""
245 pass
246
247 def setHelp(self, help):
248 """Set the help string."""
249 self._help = help
250 if not self._completed:
251 self._helpLabel.set_markup(self._help)
252 self._helpLabel.set_sensitive(True)
253
254 def complete(self):
255 """Called when the page is completed.
256
257 It greys out/changes the help text and then calls finalize()."""
258 self.finalize()
259 if self._completedHelp is None:
260 self._helpLabel.set_sensitive(False)
261 else:
262 self._helpLabel.set_markup(self._completedHelp)
263 self._completed = True
264
265 def finalize(self):
266 """Called when the page is finalized."""
267 pass
268
269 def grabDefault(self):
270 """If the page has a default button, make it the default one."""
271 if self._defaultButton is not None:
272 self._defaultButton.grab_default()
273
274 def reset(self):
275 """Reset the page if the wizard is reset."""
276 self._completed = False
277 self._fromPage = None
278 if self._cancelFlightButton is not None:
279 self._cancelFlightButton.set_sensitive(True)
280
281 def goBack(self):
282 """Go to the page we were invoked from."""
283 assert self._fromPage is not None
284
285 self._wizard.setCurrentPage(self._fromPage, finalize = False)
286
287 def flightEnded(self):
288 """Called when the flight has ended.
289
290 This default implementation disables the cancel flight button."""
291 if self._cancelFlightButton is not None:
292 self._cancelFlightButton.set_sensitive(False)
293
294 def _cancelFlight(self, button):
295 """Called when the Cancel flight button is clicked."""
296 self._wizard.gui.cancelFlight()
297
298#-----------------------------------------------------------------------------
299
300class LoginPage(Page):
301 """The login page."""
302 def __init__(self, wizard):
303 """Construct the login page."""
304 super(LoginPage, self).__init__(wizard, "login",
305 xstr("login"), xstr("loginHelp"))
306
307 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
308 xscale = 0.0, yscale = 0.0)
309
310 table = gtk.Table(3, 2)
311 table.set_row_spacings(4)
312 table.set_col_spacings(32)
313 alignment.add(table)
314 self.setMainWidget(alignment)
315
316 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
317 xscale = 0.0, yscale = 0.0)
318 label = gtk.Label(xstr("label_pilotID"))
319 label.set_use_underline(True)
320 labelAlignment.add(label)
321 table.attach(labelAlignment, 0, 1, 0, 1)
322
323 self._pilotID = gtk.Entry()
324 self._pilotID.connect("changed", self._pilotIDChanged)
325 self._pilotID.set_tooltip_text(xstr("login_pilotID_tooltip"))
326 table.attach(self._pilotID, 1, 2, 0, 1)
327 label.set_mnemonic_widget(self._pilotID)
328
329 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
330 xscale = 0.0, yscale = 0.0)
331 label = gtk.Label(xstr("label_password"))
332 label.set_use_underline(True)
333 labelAlignment.add(label)
334 table.attach(labelAlignment, 0, 1, 1, 2)
335
336 self._password = gtk.Entry()
337 self._password.set_visibility(False)
338 self._password.connect("changed", self._setControls)
339 self._password.set_tooltip_text(xstr("login_password_tooltip"))
340 table.attach(self._password, 1, 2, 1, 2)
341 label.set_mnemonic_widget(self._password)
342
343 self._rememberButton = gtk.CheckButton(xstr("remember_password"))
344 self._rememberButton.set_use_underline(True)
345 self._rememberButton.set_tooltip_text(xstr("login_remember_tooltip"))
346 table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
347
348 self.addButton(xstr("button_login_register"),
349 clicked = self._registerClicked,
350 tooltip = xstr("button_login_register_tooltip"))
351
352 self.addButton(xstr("button_offline"),
353 clicked = self._offlineClicked,
354 tooltip = xstr("button_offline_tooltip"))
355
356 self._loginButton = self.addButton(xstr("button_login"), default = True)
357 self._loginButton.connect("clicked", self._loginClicked)
358 self._loginButton.set_tooltip_text(xstr("login_button_tooltip"))
359
360
361 @property
362 def pilotID(self):
363 """Get the pilot ID, if given."""
364 return self._pilotID.get_text()
365
366 def activate(self):
367 """Activate the page."""
368 config = self._wizard.gui.config
369 self._pilotID.set_text(config.pilotID)
370 self._password.set_text(config.password)
371 self._rememberButton.set_active(config.rememberPassword)
372 self._setControls(None)
373
374 def _pilotIDChanged(self, entry):
375 """Called when the pilot ID has changed.
376
377 It sets the text to upper-case and calls _setControls to update other
378 stuff."""
379 entry.set_text(entry.get_text().upper())
380 self._setControls(entry)
381
382 def _setControls(self, entry = None):
383 """Set the sensitivity of the various controls.
384
385 The login button is sensitive only if both the pilot ID and the
386 password fields contain values.
387
388 The password field is sensitive only, if the entrance exam checkbox is
389 not selected.
390
391 The remember password checkbox is sensitive only, if the password field
392 contains text.
393
394 The entrance exam checkbox is sensitive only, if the pilot ID is not
395 empty."""
396 pilotID = self._pilotID.get_text()
397 password = self._password.get_text()
398 self._rememberButton.set_sensitive(password!="")
399 self._loginButton.set_sensitive(pilotID!="" and password!="")
400
401 def _registerClicked(self, button):
402 """Called when the Register button was clicked."""
403 self._wizard.jumpPage("register")
404
405 def _offlineClicked(self, button):
406 """Called when the offline button was clicked."""
407 print("mlx.flight.LoginPage: offline flight selected")
408 self._wizard.nextPage()
409
410 def _loginClicked(self, button):
411 """Called when the login button was clicked."""
412 print("mlx.flight.LoginPage: logging in")
413 self._wizard.login(self._handleLoginResult,
414 self._pilotID.get_text(),
415 self._password.get_text())
416
417 def _handleLoginResult(self, returned, result):
418 """Handle the login result."""
419 self._loginButton.set_sensitive(True)
420 if returned and result.loggedIn:
421 config = self._wizard.gui.config
422
423 config.pilotID = self._pilotID.get_text()
424
425 rememberPassword = self._rememberButton.get_active()
426 config.password = result.password if rememberPassword else ""
427
428 config.rememberPassword = rememberPassword
429
430 config.save()
431 if result.rank=="STU":
432 self._wizard.jumpPage("student")
433 else:
434 self._wizard.nextPage()
435
436#-----------------------------------------------------------------------------
437
438class FlightSelectionPage(Page):
439 """The page to select the flight."""
440 def __init__(self, wizard):
441 """Construct the flight selection page."""
442 help = xstr("flightsel_help")
443 completedHelp = xstr("flightsel_chelp")
444 super(FlightSelectionPage, self).__init__(wizard, "flightsel",
445 xstr("flightsel_title"),
446 help, completedHelp = completedHelp)
447
448 mainBox = gtk.HBox()
449 mainBox.set_homogeneous(False)
450
451 leftVBox = gtk.VBox()
452
453 alignment = gtk.Alignment(xscale = 1.0)
454 alignment.set_size_request(100, 0)
455
456 leftVBox.pack_start(alignment, False, False, 0)
457
458 mainBox.pack_start(leftVBox, True, True, 0)
459
460 self._flightList = FlightList(popupMenuProducer =
461 self._createListPopupMenu,
462 widthRequest = 400)
463 self._flightList.connect("row-activated", self._rowActivated)
464 self._flightList.connect("selection-changed", self._selectionChanged)
465
466 mainBox.pack_start(self._flightList, False, False, 8)
467
468 flightButtonBox = gtk.VBox()
469
470 alignment = gtk.Alignment(xscale = 1.0)
471 alignment.set_size_request(100, 0)
472 flightButtonBox.pack_start(alignment, False, False, 0)
473
474 flightButtonWidthAlignment = gtk.Alignment(xscale=1.0, yscale=0.0,
475 xalign=0.0, yalign=0.0)
476 flightButtonWidthAlignment.set_padding(padding_top = 0,
477 padding_bottom = 0,
478 padding_left = 8,
479 padding_right = 0)
480 flightButtonWidthBox = gtk.VBox()
481
482 self._saveButton = gtk.Button(xstr("flightsel_save"))
483 self._saveButton.set_use_underline(True)
484 self._saveButton.set_sensitive(False)
485 self._saveButton.set_tooltip_text(xstr("flightsel_save_tooltip"))
486 self._saveButton.connect("clicked", self._saveClicked)
487
488 flightButtonWidthBox.pack_start(self._saveButton, True, True, 4)
489
490 self._printButton = gtk.Button(xstr("flightsel_print"))
491 self._printButton.set_use_underline(True)
492 self._printButton.set_sensitive(False)
493 self._printButton.set_tooltip_text(xstr("flightsel_print_tooltip"))
494 self._printButton.connect("clicked", self._printClicked)
495
496 flightButtonWidthBox.pack_start(self._printButton, True, True, 4)
497
498 self._deleteButton = gtk.Button(xstr("flightsel_delete"))
499 self._deleteButton.set_use_underline(True)
500 self._deleteButton.set_sensitive(False)
501 self._deleteButton.set_tooltip_text(xstr("flightsel_delete_tooltip"))
502 self._deleteButton.connect("clicked", self._deleteClicked)
503
504 flightButtonWidthBox.pack_start(self._deleteButton, True, True, 4)
505
506 flightButtonWidthAlignment.add(flightButtonWidthBox)
507 flightButtonBox.pack_start(flightButtonWidthAlignment, False, False, 0)
508
509 mainBox.pack_start(flightButtonBox, True, True, 0)
510
511 self.setMainWidget(mainBox)
512
513 self._bookButton = self.addButton(xstr("flightsel_book"),
514 sensitive = True,
515 clicked = self._bookClicked,
516 tooltip = xstr("flightsel_book_tooltip"))
517
518 self._pendingButton = self.addButton(xstr("flightsel_pending"),
519 sensitive = False,
520 clicked = self._pendingClicked,
521 tooltip = xstr("flightsel_pending_tooltip"))
522
523 self._saveDialog = None
524
525 self._refreshButton = self.addButton(xstr("flightsel_refresh"),
526 sensitive = True,
527 clicked = self._refreshClicked,
528 tooltip = xstr("flightsel_refresh_tooltip"))
529
530 self._loadButton = self.addButton(xstr("flightsel_load"),
531 sensitive = True,
532 tooltip = xstr("flightsel_load_tooltip"))
533 self._loadButton.connect("clicked", self._loadButtonClicked)
534 self._loadDialog = None
535
536 self._button = self.addNextButton(sensitive = False,
537 clicked = self._forwardClicked)
538
539 self._flights = []
540
541 self._pendingFlightsWindow = PendingFlightsWindow(self._wizard)
542 self._pendingFlightsWindowShown = False
543 self._pendingFlightsWindow.connect("delete-event",
544 self._deletePendingFlightsWindow)
545
546 self._printSettings = None
547
548 def activate(self):
549 """Fill the flight list."""
550 self._setupHelp()
551 self._flightList.set_sensitive(True)
552 self._loadButton.set_sensitive(True)
553 self._refreshButton.set_sensitive(self._wizard.loggedIn)
554 self._buildFlights()
555
556 def finalize(self):
557 """Finalize the page."""
558 self._flightList.set_sensitive(False)
559 self._loadButton.set_sensitive(False)
560 self._refreshButton.set_sensitive(False)
561
562 def _setupHelp(self):
563 """Setup the help string"""
564 help = ""
565
566 if self._wizard.loggedIn:
567 numReported = len(self._wizard.loginResult.reportedFlights)
568 numRejected = len(self._wizard.loginResult.rejectedFlights)
569 if numReported==0 and numRejected==0:
570 help = xstr("flightsel_prehelp_nopending")
571 elif numReported>0 and numRejected==0:
572 help = xstr("flightsel_prehelp_rep_0rej") % (numReported,)
573 elif numReported==0 and numRejected>0:
574 help = xstr("flightsel_prehelp_0rep_rej") % (numRejected,)
575 else:
576 help = xstr("flightsel_prehelp_rep_rej") % \
577 (numReported, numRejected)
578
579 help += xstr("flightsel_help")
580
581 self.setHelp(help)
582
583 def _buildFlights(self):
584 """Rebuild the flights from the login result."""
585 self._flights = []
586 self._flightList.clear()
587 self._pendingFlightsWindow.clear()
588 loginResult = self._wizard.loginResult
589 if self._wizard.loggedIn:
590 for flight in loginResult.flights:
591 self.addFlight(flight)
592 for flight in loginResult.reportedFlights:
593 self._pendingFlightsWindow.addReportedFlight(flight)
594 for flight in loginResult.rejectedFlights:
595 self._pendingFlightsWindow.addRejectedFlight(flight)
596
597 self._updatePendingButton()
598
599 def addFlight(self, flight):
600 """Add the given file to the list of flights."""
601 self._flights.append(flight)
602 self._flightList.addFlight(flight)
603
604 def _reflyFlight(self, flight):
605 """Refly the given flight."""
606 self.addFlight(flight)
607 self._updatePending()
608
609 def _updatePending(self):
610 """Update the stuff depending on the set of pending flights."""
611 self._setupHelp()
612 self._updatePendingButton()
613
614 def _bookClicked(self, button):
615 """Called when the Book flights button is clicked."""
616 self._wizard.gui.showTimetable()
617
618 def _pendingClicked(self, button):
619 """Called when the Pending flights button is clicked."""
620 self._pendingFlightsWindow.show_all()
621 self._pendingFlightsWindowShown = True
622 self._updateNextButton()
623
624 def _deletePendingFlightsWindow(self, window, event):
625 """Called when the pending flights window is closed."""
626 self._pendingFlightsWindow.hide()
627 self._pendingFlightsWindowShown = False
628 self._updateNextButton()
629 return True
630
631 def _saveClicked(self, button):
632 """Called when the Save flight button is clicked."""
633 self._saveSelected()
634
635 def _saveSelected(self):
636 """Save the selected flight."""
637 flight = self._getSelectedFlight()
638 date = flight.departureTime.date()
639 name = "%04d-%02d-%02d %s %s-%s.vaflight" % \
640 (date.year, date.month, date.day, flight.callsign,
641 flight.departureICAO, flight.arrivalICAO)
642
643 dialog = self._getSaveDialog()
644 dialog.set_current_name(name)
645 dialog.show_all()
646 response = dialog.run()
647 dialog.hide()
648
649 if response==RESPONSETYPE_OK:
650 fileName = dialog.get_filename()
651 print("Saving", fileName)
652 try:
653 with open(fileName, "wt") as f:
654 flight.writeIntoFile(f)
655 except Exception as e:
656 print("Failed to save flight:", util.utf2unicode(str(e)))
657 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
658 type = MESSAGETYPE_ERROR,
659 message_format =
660 xstr("flightsel_save_failed"))
661 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
662 dialog.set_title(WINDOW_TITLE_BASE)
663 secondary = xstr("flightsel_save_failed_sec")
664 dialog.format_secondary_markup(secondary)
665 dialog.run()
666 dialog.hide()
667
668 def _printClicked(self, button):
669 """Called when the Print briefing button is clicked."""
670 self._printSelected()
671
672 def _printSelected(self):
673 """Print the briefing for the selected flight."""
674 wizard = self._wizard
675 flight = self._getSelectedFlight()
676
677 printOperation = gtk.PrintOperation()
678 if self._printSettings is not None:
679 printOperation.set_print_settings(self._printSettings)
680
681 printOperation.set_n_pages(1)
682 printOperation.set_show_progress(True)
683 printOperation.connect("draw_page", self._drawBriefing)
684
685 name = "MAVA Briefing %s %s %s" % (wizard.loginResult.pilotID,
686 flight.callsign,
687 flight.departureTime.strftime("%Y-%m-%d %H:%M"))
688 printOperation.set_job_name(name)
689 printOperation.set_export_filename(name)
690 printOperation.set_use_full_page(False)
691
692 result = printOperation.run(gtk.PRINT_OPERATION_ACTION_PRINT_DIALOG,
693 wizard.gui.mainWindow)
694
695 if result == gtk.PRINT_OPERATION_RESULT_APPLY:
696 self._printSettings = printOperation.get_print_settings()
697 elif result == gtk.PRINT_OPERATION_RESULT_ERROR:
698 errorDialog(xstr("flightsel_print_failed",
699 wizard.gui.mainWindow,
700 secondary = printOperation.get_error()))
701
702 def _drawBriefing(self, printOperation, context, pageNumber):
703 """Draw the briefing."""
704 wizard = self._wizard
705 loginResult = wizard.loginResult
706 flight=self._getSelectedFlight()
707
708 print("DPI", context.get_dpi_x(), context.get_dpi_y())
709
710 scale = context.get_dpi_x() / 72.0
711
712 cr = context.get_cairo_context()
713 cr.set_antialias(cairo.ANTIALIAS_GRAY)
714
715 cr.set_source_rgb(0.0, 0.0, 0.0)
716 cr.set_line_width(2.0 * scale)
717 cr.rectangle(0, 0, context.get_width(), context.get_height())
718 cr.stroke()
719
720 layout = cr.create_layout()
721 layout.set_text("Malév VA official briefing")
722 font = pango.FontDescription("sans")
723 font.set_size(int(32 * scale * pango.SCALE))
724 font.set_weight(pango.WEIGHT_NORMAL)
725 layout.set_font_description(font)
726
727 (_ink, (x0, y0, x1, y1)) = layout.get_extents()
728 width = float(x1 + 1 - x0) / pango.SCALE
729
730 y = 25 * scale
731
732 cr.move_to((context.get_width() - width)/2.0, y)
733 cr.set_line_width(0.1 * scale)
734 cr.layout_path(layout)
735 cr.stroke_preserve()
736 cr.fill()
737
738 y += float(y1 + 1 - y0) / pango.SCALE
739 y += 6 * scale
740
741 layout = cr.create_layout()
742 layout.set_text("%s (%s) részére" %
743 (loginResult.pilotName, loginResult.pilotID))
744 font = pango.FontDescription("sans")
745 font.set_size(int(16 * scale * pango.SCALE))
746 font.set_weight(450)
747 layout.set_font_description(font)
748 (_ink, (x0, y0, x1, y1)) = layout.get_extents()
749 width = float(x1 + 1 - x0) / pango.SCALE
750
751 cr.move_to((context.get_width() - width)/2.0, y)
752 cr.set_line_width(0.1 * scale)
753 cr.layout_path(layout)
754 cr.stroke_preserve()
755 cr.fill()
756
757 y += float(y1 + 1 - y0) / pango.SCALE
758 y += 4 * scale
759
760 cr.move_to(0, y)
761 cr.line_to(context.get_width(), y)
762 cr.set_line_width(1.0 * scale)
763 cr.stroke()
764
765 y += 20 * scale
766
767 font = pango.FontDescription("sans")
768 font.set_size(int(7 * scale * pango.SCALE))
769 font.set_weight(150)
770
771 table = []
772 table.append(("Flight", flight.callsign))
773 table.append(("Date", flight.date))
774 table.append(("Aircraft",
775 aircraftNames[flight.aircraftType] + ", Lajstrom: " +
776 flight.tailNumber))
777 table.append(("DOW",
778 str(acft.getClass(flight.aircraftType).dow) + " kgs"))
779 table.append(("From", flight.departureICAO))
780 table.append(("To", flight.arrivalICAO))
781 table.append(("ETD (UTC)", flight.departureTime.strftime("%H:%M:%S")))
782 table.append(("ETA (UTC)", flight.arrivalTime.strftime("%H:%M:%S")))
783 table.append(("Crew", str(flight.numCrew)))
784 table.append(("Pass", str(flight.numPassengers)))
785 table.append(("Bag", str(flight.bagWeight)))
786 table.append(("Mail", str(flight.mailWeight)))
787 table.append(("Route", flight.route))
788
789 tableY = y
790 tableX = 15 * scale
791 labelFill = 5 * scale
792 labelValueFill = 25 * scale
793 valueFill = 5 * scale
794 labelX = tableX + labelFill
795 tableWidth = context.get_width() * 55 / 100 - tableX
796
797 labelLayouts = []
798 maxLabelWidth = 0
799 totalHeight = 0
800 for (label, value) in table:
801 labelLayout = cr.create_layout()
802 labelLayout.set_text(label)
803 labelLayout.set_font_description(font)
804
805 (_ink, (x0, y0, x1, y1)) = labelLayout.get_extents()
806 maxLabelWidth = max(maxLabelWidth, x1 + 1 - x0)
807 labelHeight = y1 + 1 - y0
808
809 valueLayout = cr.create_layout()
810 valueLayout.set_text(value)
811 valueLayout.set_font_description(font)
812
813 labelLayouts.append((labelLayout, valueLayout, labelHeight))
814
815 maxLabelWidth = maxLabelWidth / pango.SCALE
816
817 valueX = labelX + labelValueFill + maxLabelWidth
818
819 layouts = []
820 valueWidth = tableWidth - \
821 (labelFill + maxLabelWidth + labelValueFill + valueFill)
822 for (labelLayout, valueLayout, labelHeight) in labelLayouts:
823 valueLayout.set_width(int(valueWidth * pango.SCALE))
824
825 (_ink, (x0, y0, x1, y1)) = valueLayout.get_extents()
826 valueHeight = y1 + 1 - y0
827
828 height = float(max(labelHeight, valueHeight))/pango.SCALE
829 layouts.append((labelLayout, valueLayout, height))
830
831 rowIndex = 0
832 for (labelLayout, valueLayout, height) in layouts:
833 if (rowIndex%2)==0:
834 cr.set_source_rgb(0.85, 0.85, 0.85)
835 else:
836 cr.set_source_rgb(0.9, 0.9, 0.9)
837
838 cr.rectangle(tableX, y-2*scale, tableWidth, height + 4 * scale)
839 cr.fill()
840
841 cr.set_source_rgb(0.0, 0.0, 0.0)
842
843 cr.move_to(labelX, y)
844 cr.set_line_width(0.1)
845 cr.layout_path(labelLayout)
846 cr.stroke_preserve()
847 cr.fill()
848
849 cr.move_to(valueX, y)
850 cr.set_line_width(0.1)
851 cr.layout_path(valueLayout)
852 cr.stroke_preserve()
853 cr.fill()
854
855 y += height
856 y += 4 * scale
857
858 rowIndex += 1
859
860 cr.set_source_rgb(0.0, 0.0, 0.0)
861 cr.set_line_width(1.0 * scale)
862 cr.rectangle(tableX, tableY - 2 * scale, tableWidth, y - tableY)
863 cr.stroke()
864
865 cr.move_to(valueX - 5 * scale, tableY - 2 * scale)
866 cr.line_to(valueX - 5 * scale, y - 2 * scale)
867 cr.stroke()
868
869 def _deleteClicked(self, button):
870 """Called when the Delete flight button is clicked."""
871 self._deleteSelected()
872
873 def _deleteSelected(self):
874 """Delete the selected flight."""
875 if askYesNo(xstr("flightsel_delete_confirm"),
876 parent = self._wizard.gui.mainWindow):
877 flight = self._getSelectedFlight()
878 gui = self._wizard.gui
879 gui.beginBusy(xstr("flightsel_delete_busy"))
880
881 gui.webHandler.deleteFlights(self._deleteResultCallback,
882 [flight.id])
883
884 def _deleteResultCallback(self, returned, result):
885 """Called when the deletion result is available."""
886 gobject.idle_add(self._handleDeleteResult, returned, result)
887
888 def _handleDeleteResult(self, returned, result):
889 """Handle the delete result."""
890 gui = self._wizard.gui
891 gui.endBusy()
892
893 if returned:
894 indexes = self._flightList.selectedIndexes
895
896 flights = [self._flights[index] for index in indexes]
897
898 self._flightList.removeFlights(indexes)
899 for index in indexes[::-1]:
900 del self._flights[index]
901 else:
902 communicationErrorDialog()
903
904
905 def _refreshClicked(self, button):
906 """Called when the refresh button is clicked."""
907 self._wizard.reloadFlights(self._refreshCallback)
908
909 def _refreshCallback(self, returned, result):
910 """Callback for the refresh."""
911 if returned:
912 self._setupHelp()
913 if result.loggedIn:
914 self._buildFlights()
915
916 def _selectionChanged(self, flightList, indexes):
917 """Called when the selection is changed."""
918 self._saveButton.set_sensitive(len(indexes)==1)
919 self._printButton.set_sensitive(len(indexes)==1)
920 self._deleteButton.set_sensitive(len(indexes)==1)
921 self._updateNextButton()
922
923 def _updatePendingButton(self):
924 """Update the senstivity of the Pending button."""
925 self._pendingButton.set_sensitive(self._pendingFlightsWindow.hasFlights)
926
927 def _updateNextButton(self):
928 """Update the sensitivity of the Next button."""
929 sensitive = len(self._flightList.selectedIndexes)==1 and \
930 not self._pendingFlightsWindowShown
931 self._button.set_sensitive(sensitive)
932
933 def _loadButtonClicked(self, loadButton):
934 """Called when the load a flight button is clicked."""
935 dialog = self._getLoadDialog()
936 dialog.show_all()
937 response = dialog.run()
938 dialog.hide()
939
940 if response==RESPONSETYPE_OK:
941 fileName = dialog.get_filename()
942 print("Loading", fileName)
943 bookedFlight = web.BookedFlight()
944 try:
945 with open(fileName, "rt") as f:
946 bookedFlight.readFromFile(f)
947 self.addFlight(bookedFlight)
948 except Exception as e:
949 print("Failed to load flight:", util.utf2unicode(str(e)))
950 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
951 type = MESSAGETYPE_ERROR,
952 message_format =
953 xstr("flightsel_load_failed"))
954 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
955 dialog.set_title(WINDOW_TITLE_BASE)
956 secondary = xstr("flightsel_load_failed_sec")
957 dialog.format_secondary_markup(secondary)
958 dialog.run()
959 dialog.hide()
960
961 def _forwardClicked(self, button):
962 """Called when the forward button was clicked."""
963 if self._completed:
964 self._wizard.jumpPage(self._nextID, finalize = False)
965 else:
966 self._flightSelected()
967
968 def _rowActivated(self, flightList, index):
969 """Called when a row is activated."""
970 if not self._completed:
971 self._flightSelected()
972
973 def _flightSelected(self):
974 """Called when a flight has been selected."""
975 flight = self._getSelectedFlight()
976 self._wizard._bookedFlight = flight
977 self._wizard.gui.enableFlightInfo(flight.aircraftType)
978
979 self._updateDepartureGate()
980
981 def _getSelectedFlight(self):
982 """Get the currently selected flight."""
983 indexes = self._flightList.selectedIndexes
984 assert(len(indexes)==1)
985 return self._flights[indexes[0]]
986
987 def _updateDepartureGate(self):
988 """Update the departure gate for the booked flight."""
989 flight = self._wizard._bookedFlight
990 if self._wizard.gui.config.onlineGateSystem and \
991 self._wizard.loggedIn and not self._wizard.entranceExam:
992 if flight.departureICAO=="LHBP":
993 self._wizard.getFleet(self._fleetRetrieved)
994 else:
995 self._wizard.updatePlane(self._planeUpdated,
996 flight.tailNumber,
997 const.PLANE_AWAY)
998 else:
999 self._nextID = "connect"
1000 self._wizard.jumpPage("connect")
1001
1002 def _fleetRetrieved(self, fleet):
1003 """Called when the fleet has been retrieved."""
1004 if fleet is None:
1005 self._nextID = "connect"
1006 self._wizard.jumpPage("connect")
1007 else:
1008 plane = fleet[self._wizard._bookedFlight.tailNumber]
1009 if plane is None:
1010 self._nextID = "connect"
1011 self._wizard.jumpPage("connect")
1012 elif plane.gateNumber is not None and \
1013 not fleet.isGateConflicting(plane):
1014 self._wizard._departureGate = plane.gateNumber
1015 self._nextID = "connect"
1016 self._wizard.jumpPage("connect")
1017 else:
1018 self._nextID = "gatesel"
1019 self._wizard.jumpPage("gatesel")
1020
1021 def _planeUpdated(self, success):
1022 """Callback for the plane updating."""
1023 self._nextID = "connect"
1024 self._wizard.jumpPage("connect")
1025
1026 def _getSaveDialog(self):
1027 """Get the dialog to load a flight file."""
1028 if self._saveDialog is not None:
1029 return self._saveDialog
1030
1031 gui = self._wizard.gui
1032 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
1033 xstr("flightsel_save_title"),
1034 action = FILE_CHOOSER_ACTION_SAVE,
1035 buttons = (gtk.STOCK_CANCEL,
1036 RESPONSETYPE_CANCEL,
1037 gtk.STOCK_OK, RESPONSETYPE_OK),
1038 parent = gui.mainWindow)
1039 dialog.set_modal(True)
1040 dialog.set_do_overwrite_confirmation(True)
1041
1042 filter = gtk.FileFilter()
1043 filter.set_name(xstr("flightsel_filter_flights"))
1044 filter.add_pattern("*.vaflight")
1045 dialog.add_filter(filter)
1046
1047 filter = gtk.FileFilter()
1048 filter.set_name(xstr("file_filter_all"))
1049 filter.add_pattern("*.*")
1050 dialog.add_filter(filter)
1051
1052 self._saveDialog = dialog
1053
1054 return dialog
1055
1056 def _getLoadDialog(self):
1057 """Get the dialog to load a flight file."""
1058 if self._loadDialog is not None:
1059 return self._loadDialog
1060
1061 gui = self._wizard.gui
1062 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
1063 xstr("flightsel_load_title"),
1064 action = FILE_CHOOSER_ACTION_OPEN,
1065 buttons = (gtk.STOCK_CANCEL,
1066 RESPONSETYPE_CANCEL,
1067 gtk.STOCK_OK, RESPONSETYPE_OK),
1068 parent = gui.mainWindow)
1069 dialog.set_modal(True)
1070
1071 filter = gtk.FileFilter()
1072 filter.set_name(xstr("flightsel_filter_flights"))
1073 filter.add_pattern("*.vaflight")
1074 dialog.add_filter(filter)
1075
1076 filter = gtk.FileFilter()
1077 filter.set_name(xstr("file_filter_all"))
1078 filter.add_pattern("*.*")
1079 dialog.add_filter(filter)
1080
1081 self._loadDialog = dialog
1082
1083 return dialog
1084
1085 def _createListPopupMenu(self):
1086 """Get the flight list popup menu."""
1087 menu = gtk.Menu()
1088
1089 menuItem = gtk.MenuItem()
1090 menuItem.set_label(xstr("flightsel_popup_select"))
1091 menuItem.set_use_underline(True)
1092 menuItem.connect("activate", self._popupSelect)
1093 menuItem.show()
1094
1095 menu.append(menuItem)
1096
1097 menuItem = gtk.MenuItem()
1098 menuItem.set_label(xstr("flightsel_popup_save"))
1099 menuItem.set_use_underline(True)
1100 menuItem.connect("activate", self._popupSave)
1101 menuItem.show()
1102
1103 menu.append(menuItem)
1104
1105 menuItem = gtk.MenuItem()
1106 menuItem.set_label(xstr("flightsel_popup_print"))
1107 menuItem.set_use_underline(True)
1108 menuItem.connect("activate", self._popupPrint)
1109 menuItem.show()
1110
1111 menu.append(menuItem)
1112
1113 menuItem = gtk.MenuItem()
1114 menuItem.set_label(xstr("flightsel_popup_delete"))
1115 menuItem.set_use_underline(True)
1116 menuItem.connect("activate", self._popupDelete)
1117 menuItem.show()
1118
1119 menu.append(menuItem)
1120
1121 return menu
1122
1123 def _popupSelect(self, menuItem):
1124 """Called when the Select menu item is activated in the popup menu."""
1125 if not self._completed:
1126 self._flightSelected()
1127
1128 def _popupSave(self, menuItem):
1129 """Called when the Save menu item is activated in the popup menu."""
1130 if not self._completed:
1131 self._saveSelected()
1132
1133 def _popupPrint(self, menuItem):
1134 """Called when the Print briefing menu item is activated in the popup menu."""
1135 if not self._completed:
1136 self._printSelected()
1137
1138 def _popupDelete(self, menuItem):
1139 """Called when the Delete menu item is activated in the popup menu."""
1140 if not self._completed:
1141 self._deleteSelected()
1142
1143#-----------------------------------------------------------------------------
1144
1145class GateSelectionPage(Page):
1146 """Page to select a free gate at LHBP.
1147 This page should be displayed only if we have fleet information!."""
1148 def __init__(self, wizard):
1149 """Construct the gate selection page."""
1150 super(GateSelectionPage, self).__init__(wizard, "gatesel",
1151 xstr("gatesel_title"),
1152 xstr("gatesel_help"))
1153
1154 self._listStore = gtk.ListStore(str)
1155 self._gateList = gtk.TreeView(self._listStore)
1156 column = gtk.TreeViewColumn(None, gtk.CellRendererText(),
1157 text = 0)
1158 column.set_expand(True)
1159 self._gateList.append_column(column)
1160 self._gateList.set_headers_visible(False)
1161 self._gateList.connect("row-activated", self._rowActivated)
1162
1163 gateSelection = self._gateList.get_selection()
1164 gateSelection.connect("changed", self._selectionChanged)
1165
1166 scrolledWindow = gtk.ScrolledWindow()
1167 scrolledWindow.add(self._gateList)
1168 scrolledWindow.set_size_request(50, -1)
1169 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1170 else gtk.POLICY_AUTOMATIC,
1171 gtk.PolicyType.AUTOMATIC if pygobject
1172 else gtk.POLICY_AUTOMATIC)
1173 scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
1174 else gtk.SHADOW_IN)
1175
1176 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 0.0, yscale = 1.0)
1177 alignment.add(scrolledWindow)
1178
1179 self.setMainWidget(alignment)
1180
1181 self.addCancelFlightButton()
1182
1183 self.addPreviousButton(clicked = self._backClicked)
1184
1185 self._button = self.addNextButton(sensitive = False,
1186 clicked = self._forwardClicked)
1187
1188 def activate(self):
1189 """Fill the gate list."""
1190 self._listStore.clear()
1191 self._gateList.set_sensitive(True)
1192 occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
1193 for gate in lhbpGates.gates:
1194 if gate.isAvailable(lhbpGates, occupiedGateNumbers):
1195 self._listStore.append([gate.number])
1196
1197 def finalize(self):
1198 """Finalize the page."""
1199 self._gateList.set_sensitive(False)
1200
1201 def _selectionChanged(self, selection):
1202 """Called when the selection is changed."""
1203 self._button.set_sensitive(selection.count_selected_rows()==1)
1204
1205 def _backClicked(self, button):
1206 """Called when the Back button is pressed."""
1207 self.goBack()
1208
1209 def _forwardClicked(self, button):
1210 """Called when the forward button is clicked."""
1211 if not self._completed:
1212 self._gateSelected()
1213 else:
1214 self._wizard.jumpPage("connect")
1215
1216 def _rowActivated(self, flightList, path, column):
1217 """Called when a row is activated."""
1218 if not self._completed:
1219 self._gateSelected()
1220
1221 def _gateSelected(self):
1222 """Called when a gate has been selected."""
1223 selection = self._gateList.get_selection()
1224 (listStore, iter) = selection.get_selected()
1225 (gateNumber,) = listStore.get(iter, 0)
1226
1227 self._wizard._departureGate = gateNumber
1228
1229 self._wizard.updatePlane(self._planeUpdated,
1230 self._wizard._bookedFlight.tailNumber,
1231 const.PLANE_HOME, gateNumber)
1232
1233 def _planeUpdated(self, success):
1234 """Callback for the plane updating call."""
1235 if success is None or success:
1236 self._wizard.jumpPage("connect")
1237 else:
1238 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
1239 type = MESSAGETYPE_ERROR,
1240 message_format = xstr("gatesel_conflict"))
1241 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1242 dialog.set_title(WINDOW_TITLE_BASE)
1243 dialog.format_secondary_markup(xstr("gatesel_conflict_sec"))
1244 dialog.run()
1245 dialog.hide()
1246
1247 self._wizard.getFleet(self._fleetRetrieved)
1248
1249 def _fleetRetrieved(self, fleet):
1250 """Called when the fleet has been retrieved."""
1251 if fleet is None:
1252 self._wizard.jumpPage("connect")
1253 else:
1254 self.activate()
1255
1256#-----------------------------------------------------------------------------
1257
1258class RegisterPage(Page):
1259 """A page to enter the registration data."""
1260
1261 # The minimal year of birth
1262 _minYearOfBirth = 1900
1263
1264 # The maximal year of birth
1265 _maxYearOfBirth = datetime.date.today().year
1266
1267 # The regular expression to check the e-mail address with
1268 _emailAddressRE = re.compile("[^@]+@[^@]+\.[^@]+")
1269
1270 def __init__(self, wizard):
1271 """Construct the registration page."""
1272 super(RegisterPage, self).__init__(wizard, "register",
1273 xstr("register_title"),
1274 xstr("register_help"))
1275
1276 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1277 xscale = 0.0, yscale = 0.0)
1278
1279 table = gtk.Table(12, 3)
1280 table.set_row_spacings(4)
1281 table.set_col_spacings(32)
1282 alignment.add(table)
1283 self.setMainWidget(alignment)
1284
1285 row = 0
1286
1287 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1288 xscale = 0.0, yscale = 0.0)
1289 label = gtk.Label(xstr("register_name1"))
1290 label.set_use_underline(True)
1291 labelAlignment.add(label)
1292 table.attach(labelAlignment, 0, 1, row, row+1)
1293
1294 self._name1 = gtk.Entry()
1295 self._name1.connect("changed", self._updateButtons)
1296 self._name1.set_tooltip_text(xstr("register_name1_tooltip"))
1297 self._name1.set_width_chars(15)
1298 table.attach(self._name1, 1, 2, row, row+1)
1299 label.set_mnemonic_widget(self._name1)
1300
1301 row += 1
1302
1303 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1304 xscale = 0.0, yscale = 0.0)
1305 label = gtk.Label(xstr("register_name2"))
1306 label.set_use_underline(True)
1307 labelAlignment.add(label)
1308 table.attach(labelAlignment, 0, 1, row, row+1)
1309
1310 self._name2 = gtk.Entry()
1311 self._name2.connect("changed", self._updateButtons)
1312 self._name2.set_tooltip_text(xstr("register_name2_tooltip"))
1313 self._name2.set_width_chars(15)
1314 table.attach(self._name2, 1, 2, row, row+1)
1315 label.set_mnemonic_widget(self._name2)
1316
1317 row += 1
1318
1319 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1320 xscale = 0.0, yscale = 0.0)
1321 label = gtk.Label(xstr("register_year_of_birth"))
1322 label.set_use_underline(True)
1323 labelAlignment.add(label)
1324 table.attach(labelAlignment, 0, 1, row, row+1)
1325
1326 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
1327 xscale = 0.0, yscale = 0.0)
1328
1329 self._yearOfBirth = gtk.SpinButton()
1330 self._yearOfBirth.set_increments(step = 1, page = 10)
1331 self._yearOfBirth.set_range(min = RegisterPage._minYearOfBirth,
1332 max = RegisterPage._maxYearOfBirth)
1333 self._yearOfBirth.set_numeric(True)
1334 self._yearOfBirth.set_tooltip_text(xstr("register_year_of_birth_tooltip"))
1335 self._yearOfBirth.set_width_chars(5)
1336 self._yearOfBirth.connect("changed", self._updateButtons)
1337 self._yearOfBirth.connect("value-changed", self._updateButtons)
1338 alignment.add(self._yearOfBirth)
1339 table.attach(alignment, 1, 2, row, row+1)
1340 label.set_mnemonic_widget(self._yearOfBirth)
1341
1342 row += 1
1343
1344 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1345 xscale = 0.0, yscale = 0.0)
1346 label = gtk.Label(xstr("register_email"))
1347 label.set_use_underline(True)
1348 labelAlignment.add(label)
1349 table.attach(labelAlignment, 0, 1, row, row+1)
1350
1351 self._emailAddress = gtk.Entry()
1352 self._emailAddress.connect("changed", self._updateButtons)
1353 self._emailAddress.set_tooltip_text(xstr("register_email_tooltip"))
1354 table.attach(self._emailAddress, 1, 2, row, row+1)
1355 label.set_mnemonic_widget(self._emailAddress)
1356
1357 self._emailAddressPublic = gtk.CheckButton(xstr("register_email_public"))
1358 self._emailAddressPublic.set_use_underline(True)
1359 self._emailAddressPublic.set_tooltip_text(xstr("register_email_public_tooltip"))
1360 table.attach(self._emailAddressPublic, 2, 3, row, row+1)
1361
1362 row += 1
1363
1364 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1365 xscale = 0.0, yscale = 0.0)
1366 label = gtk.Label(xstr("register_vatsim_id"))
1367 label.set_use_underline(True)
1368 labelAlignment.add(label)
1369 table.attach(labelAlignment, 0, 1, row, row+1)
1370
1371 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
1372 xscale = 0.0, yscale = 0.0)
1373 self._vatsimID = IntegerEntry()
1374 self._vatsimID.connect("changed", self._updateButtons)
1375 self._vatsimID.set_tooltip_text(xstr("register_vatsim_id_tooltip"))
1376 self._vatsimID.set_width_chars(7)
1377 alignment.add(self._vatsimID)
1378 table.attach(alignment, 1, 2, row, row+1)
1379 label.set_mnemonic_widget(self._vatsimID)
1380
1381 row += 1
1382
1383 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1384 xscale = 0.0, yscale = 0.0)
1385 label = gtk.Label(xstr("register_ivao_id"))
1386 label.set_use_underline(True)
1387 labelAlignment.add(label)
1388 table.attach(labelAlignment, 0, 1, row, row+1)
1389
1390 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
1391 xscale = 0.0, yscale = 0.0)
1392 self._ivaoID = IntegerEntry()
1393 self._ivaoID.connect("changed", self._updateButtons)
1394 self._ivaoID.set_tooltip_text(xstr("register_ivao_id_tooltip"))
1395 self._ivaoID.set_width_chars(7)
1396 alignment.add(self._ivaoID)
1397 table.attach(alignment, 1, 2, row, row+1)
1398 label.set_mnemonic_widget(self._ivaoID)
1399
1400 row += 1
1401
1402 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1403 xscale = 0.0, yscale = 0.0)
1404 label = gtk.Label(xstr("register_phone_num"))
1405 label.set_use_underline(True)
1406 label.set_use_markup(True)
1407 labelAlignment.add(label)
1408 table.attach(labelAlignment, 0, 1, row, row+1)
1409
1410 self._phoneNumber = gtk.Entry()
1411 self._phoneNumber.set_tooltip_text(xstr("register_phone_num_tooltip"))
1412 table.attach(self._phoneNumber, 1, 2, row, row+1)
1413 label.set_mnemonic_widget(self._phoneNumber)
1414
1415 row += 1
1416
1417 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1418 xscale = 0.0, yscale = 0.0)
1419 label = gtk.Label(xstr("register_nationality"))
1420 label.set_use_underline(True)
1421 label.set_use_markup(True)
1422 labelAlignment.add(label)
1423 table.attach(labelAlignment, 0, 1, row, row+1)
1424
1425
1426 self._nationality = gtk.Entry()
1427 self._nationality.set_tooltip_text(xstr("register_nationality_tooltip"))
1428 table.attach(self._nationality, 1, 2, row, row+1)
1429 label.set_mnemonic_widget(self._nationality)
1430
1431 placeholder = gtk.Label()
1432 placeholder.set_text(xstr("register_password_mismatch"))
1433 placeholder.set_use_markup(True)
1434 placeholder.set_child_visible(False)
1435 placeholder.hide()
1436 table.attach(placeholder, 2, 3, row, row+1)
1437
1438 row += 1
1439
1440 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1441 xscale = 0.0, yscale = 0.0)
1442 label = gtk.Label(xstr("register_password"))
1443 label.set_use_underline(True)
1444 labelAlignment.add(label)
1445 table.attach(labelAlignment, 0, 1, row, row+1)
1446
1447 self._password = gtk.Entry()
1448 self._password.set_visibility(False)
1449 self._password.connect("changed", self._updateButtons)
1450 self._password.set_tooltip_text(xstr("register_password_tooltip"))
1451 table.attach(self._password, 1, 2, row, row+1)
1452 label.set_mnemonic_widget(self._password)
1453
1454 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1455 xscale = 0.0, yscale = 0.0)
1456 self._passwordStatus = gtk.Label()
1457 alignment.add(self._passwordStatus)
1458 table.attach(alignment, 2, 3, row, row+1)
1459
1460 row += 1
1461
1462 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1463 xscale = 0.0, yscale = 0.0)
1464 label = gtk.Label(xstr("register_password2"))
1465 label.set_use_underline(True)
1466 labelAlignment.add(label)
1467 table.attach(labelAlignment, 0, 1, row, row+1)
1468
1469 self._password2 = gtk.Entry()
1470 self._password2.set_visibility(False)
1471 self._password2.connect("changed", self._updateButtons)
1472 self._password2.set_tooltip_text(xstr("register_password2_tooltip"))
1473 table.attach(self._password2, 1, 2, row, row+1)
1474 label.set_mnemonic_widget(self._password2)
1475
1476 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1477 xscale = 0.0, yscale = 0.0)
1478 self._password2Status = gtk.Label()
1479 alignment.add(self._password2Status)
1480 table.attach(alignment, 2, 3, row, row+1)
1481
1482 row += 1
1483
1484 self._rememberButton = gtk.CheckButton(xstr("remember_password"))
1485 self._rememberButton.set_use_underline(True)
1486 self._rememberButton.set_tooltip_text(xstr("login_remember_tooltip"))
1487 table.attach(self._rememberButton, 1, 2, row, row+1)
1488
1489 cancelButton = \
1490 self.addButton(xstr("button_cancel"))
1491 cancelButton.connect("clicked", self._cancelClicked)
1492
1493 self._registerButton = \
1494 self.addButton(xstr("button_register"), default = True,
1495 tooltip = xstr("button_register_tooltip"))
1496 self._registerButton.connect("clicked", self._registerClicked)
1497
1498 self._updateButtons()
1499
1500 @property
1501 def name1(self):
1502 """Get the first name component entered."""
1503 return self._name1.get_text()
1504
1505 @property
1506 def name2(self):
1507 """Get the second name component entered."""
1508 return self._name2.get_text()
1509
1510 @property
1511 def yearOfBirth(self):
1512 """Get the year of birth."""
1513 yearOfBirthText = self._yearOfBirth.get_text()
1514 return int(yearOfBirthText) if yearOfBirthText else 0
1515
1516 @property
1517 def emailAddress(self):
1518 """Get the e-mail address."""
1519 return self._emailAddress.get_text()
1520
1521 @property
1522 def emailAddressPublic(self):
1523 """Get the whether the e-mail address is public."""
1524 return self._emailAddressPublic.get_active()
1525
1526 @property
1527 def vatsimID(self):
1528 """Get the VATSIM ID."""
1529 return self._vatsimID.get_int()
1530
1531 @property
1532 def ivaoID(self):
1533 """Get the IVAO ID."""
1534 return self._ivaoID.get_int()
1535
1536 @property
1537 def phoneNumber(self):
1538 """Get the phone number."""
1539 return self._phoneNumber.get_text()
1540
1541 @property
1542 def nationality(self):
1543 """Get the nationality."""
1544 return self._nationality.get_text()
1545
1546 @property
1547 def password(self):
1548 """Get the password."""
1549 return self._password.get_text()
1550
1551 @property
1552 def rememberPassword(self):
1553 """Get whether the password should be remembered."""
1554 return self._rememberButton.get_active()
1555
1556 def activate(self):
1557 """Setup the route from the booked flight."""
1558 self._yearOfBirth.set_value(0)
1559 self._yearOfBirth.set_text("")
1560 self._updateButtons()
1561
1562 def _updateButtons(self, widget = None):
1563 """Update the sensitive state of the buttons"""
1564 yearOfBirth = self.yearOfBirth
1565
1566 emailAddress = self.emailAddress
1567 emailAddressMatch = RegisterPage._emailAddressRE.match(emailAddress)
1568
1569 vatsimID = self.vatsimID
1570 ivaoID = self.ivaoID
1571
1572 password = self.password
1573 password2 = self._password2.get_text()
1574 if not password:
1575 self._passwordStatus.set_text("")
1576 elif len(password)<5:
1577 self._passwordStatus.set_text(xstr("register_password_too_short"))
1578 else:
1579 self._passwordStatus.set_text(xstr("register_password_ok"))
1580 self._passwordStatus.set_use_markup(True)
1581
1582 if len(password)<5 or not password2:
1583 self._password2Status.set_text("")
1584 elif password!=password2:
1585 self._password2Status.set_text(xstr("register_password_mismatch"))
1586 else:
1587 self._password2Status.set_text(xstr("register_password_ok"))
1588 self._password2Status.set_use_markup(True)
1589
1590 sensitive = \
1591 len(self.name1)>0 and len(self.name2)>0 and \
1592 yearOfBirth>=RegisterPage._minYearOfBirth and \
1593 yearOfBirth<=RegisterPage._maxYearOfBirth and \
1594 emailAddressMatch is not None and \
1595 (vatsimID>=800000 or ivaoID>=100000) and \
1596 len(password)>=5 and password==password2
1597
1598 self._registerButton.set_sensitive(sensitive)
1599
1600 def _cancelClicked(self, button):
1601 """Called when the Cancel button is clicked."""
1602 self.goBack()
1603
1604 def _registerClicked(self, button):
1605 """Called when the Register button is clicked."""
1606 nameOrder = xstr("register_nameorder")
1607
1608 if nameOrder=="eastern":
1609 surName = self.name1
1610 firstName = self.name2
1611 else:
1612 surName = self.name2
1613 firstName = self.name1
1614
1615 nationality = self.nationality.lower()
1616
1617 if getLanguage().lower()=="hu" or nationality.find("hung")!=-1 or \
1618 nationality.find("magyar")!=-1:
1619 requestedNameOrder = "eastern"
1620 else:
1621 requestedNameOrder = "western"
1622
1623 registrationData = web.Registration(surName, firstName,
1624 requestedNameOrder,
1625 self.yearOfBirth,
1626 self.emailAddress,
1627 self.emailAddressPublic,
1628 self.vatsimID, self.ivaoID,
1629 self.phoneNumber, self.nationality,
1630 self.password)
1631 print("Registering with data:")
1632 print(" name:", self.name1, self.name2, registrationData.firstName, registrationData.surName, requestedNameOrder)
1633 print(" yearOfBirth:", self.yearOfBirth, registrationData.yearOfBirth)
1634 print(" emailAddress:", self.emailAddress, registrationData.emailAddress)
1635 print(" emailAddressPublic:", self.emailAddressPublic, registrationData.emailAddressPublic)
1636 print(" vatsimID:", self.vatsimID, registrationData.vatsimID)
1637 print(" ivaoID:", self.ivaoID, registrationData.ivaoID)
1638 print(" phoneNumber:", self.phoneNumber, registrationData.phoneNumber)
1639 print(" nationality:", self.nationality, registrationData.nationality)
1640
1641 gui = self._wizard.gui
1642 gui.beginBusy(xstr("register_busy"))
1643 gui.webHandler.register(self._registerResultCallback, registrationData)
1644
1645 def _registerResultCallback(self, returned, result):
1646 """Called when the registration result is available."""
1647 gobject.idle_add(self._handleRegisterResult, returned, result)
1648
1649 def _handleRegisterResult(self, returned, result):
1650 """Handle the registration result."""
1651 gui = self._wizard.gui
1652
1653 gui.endBusy()
1654
1655 print("Registration result:")
1656 print(" returned:", returned)
1657 if returned:
1658 print(" registered:", result.registered)
1659 if result.registered:
1660 print(" pilotID", result.pilotID)
1661 print(" loggedIn", result.loggedIn)
1662 print(" emailAlreadyRegistered:", result.emailAlreadyRegistered)
1663 print(" invalidData:", result.invalidData)
1664
1665 registrationOK = returned and result.registered
1666
1667 message = xstr("register_ok") if registrationOK \
1668 else xstr("register_failed")
1669 secondaryMessage = None
1670 if registrationOK:
1671 if result.loggedIn:
1672 secondaryMessage = xstr("register_info") % (result.pilotID,)
1673 else:
1674 secondaryMessage = xstr("register_nologin") % (result.pilotID,)
1675 messageType = MESSAGETYPE_INFO
1676
1677 config = gui.config
1678 config.pilotID = result.pilotID
1679 config.rememberPassword = self.rememberPassword
1680 if config.rememberPassword:
1681 config.password = self.password
1682 else:
1683 config.password = ""
1684
1685 config.save()
1686 elif returned and result.emailAlreadyRegistered:
1687 secondaryMessage = xstr("register_email_already")
1688 messageType = MESSAGETYPE_ERROR
1689 elif returned and result.invalidData:
1690 secondaryMessage = xstr("register_invalid_data")
1691 messageType = MESSAGETYPE_ERROR
1692 else:
1693 secondaryMessage = xstr("register_error")
1694 messageType = MESSAGETYPE_ERROR
1695
1696 dialog = gtk.MessageDialog(parent = gui.mainWindow,
1697 type = messageType,
1698 message_format = message)
1699 dialog.set_title(WINDOW_TITLE_BASE + " - " +
1700 xstr("register_result_title"))
1701 dialog.format_secondary_markup(secondaryMessage)
1702
1703 dialog.add_button(xstr("button_ok"), 0)
1704
1705 dialog.run()
1706 dialog.hide()
1707
1708 if registrationOK:
1709 if result.loggedIn:
1710 self._wizard._loginResult = result
1711 self._wizard.nextPage()
1712 else:
1713 self._wizard.jumpPage("login")
1714
1715#-----------------------------------------------------------------------------
1716
1717class StudentPage(Page):
1718 """A page displayed to students after logging in."""
1719 _entryExamStatusQueryInterval = 60*1000
1720
1721 def __init__(self, wizard):
1722 """Construct the student page."""
1723 super(StudentPage, self).__init__(wizard, "student",
1724 xstr("student_title"),
1725 xstr("student_help"))
1726
1727
1728 self._getEntryExamStatusCancelled = False
1729
1730 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1731 xscale = 0.5, yscale = 0.0)
1732
1733 table = gtk.Table(6, 4)
1734 table.set_row_spacings(4)
1735 table.set_col_spacings(0)
1736 table.set_homogeneous(False)
1737 alignment.add(table)
1738 self.setMainWidget(alignment)
1739
1740 row = 0
1741
1742 labelAlignment = gtk.Alignment(xalign=0.0, yalign = 0.5,
1743 xscale=0.0, yscale = 0.0)
1744 label = gtk.Label(xstr("student_entry_exam_status"))
1745 label.set_alignment(0.0, 0.5)
1746 labelAlignment.add(label)
1747 labelAlignment.resize_children()
1748 table.attach(labelAlignment, 0, 1, row, row + 1, xoptions = FILL)
1749
1750 alignment = gtk.Alignment(xalign=0.0, yalign = 0.5,
1751 xscale=1.0, yscale = 0.0)
1752 self._entryExamStatus = gtk.Label()
1753 self._entryExamStatus.set_use_markup(True)
1754 self._entryExamStatus.set_alignment(0.0, 0.5)
1755 alignment.add(self._entryExamStatus)
1756 alignment.resize_children()
1757 table.attach(alignment, 1, 4, row, row + 1)
1758
1759 row += 1
1760
1761 buttonAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
1762 button = self._entryExamButton = gtk.Button(xstr("student_entry_exam"))
1763 button.set_use_underline(True)
1764 button.connect("clicked", self._entryExamClicked)
1765 button.set_tooltip_text(xstr("student_entry_exam_tooltip"))
1766
1767 buttonAlignment.add(button)
1768 table.attach(buttonAlignment, 0, 4, row, row + 1, xoptions = FILL,
1769 ypadding = 4)
1770
1771 row += 3
1772
1773 labelAlignment = gtk.Alignment(xalign=0.0, yalign = 0.5,
1774 xscale=0.0, yscale = 0.0)
1775 label = gtk.Label(xstr("student_check_flight_status"))
1776 labelAlignment.add(label)
1777 table.attach(labelAlignment, 0, 1, row, row + 1, xoptions = FILL)
1778
1779 alignment = gtk.Alignment(xalign=0.0, yalign = 0.5,
1780 xscale=1.0, yscale = 0.0)
1781 self._checkFlightStatus = gtk.Label()
1782 self._checkFlightStatus.set_use_markup(True)
1783 self._checkFlightStatus.set_alignment(0.0, 0.5)
1784 alignment.add(self._checkFlightStatus)
1785 table.attach(alignment, 1, 4, row, row + 1)
1786
1787 row += 1
1788
1789 alignment = gtk.Alignment(xalign=0.0, xscale=1.0)
1790
1791 hbox = gtk.HBox()
1792 hbox.set_homogeneous(False)
1793 hbox.set_spacing(0)
1794
1795 aircraftTypesModel = gtk.ListStore(str, int)
1796 for aircraftType in web.BookedFlight.checkFlightTypes:
1797 aircraftTypesModel.append([aircraftNames[aircraftType],
1798 aircraftType])
1799
1800 aircraftTypeAlignment = gtk.Alignment(xalign = 0.0, xscale = 1.0)
1801
1802 self._aircraftType = gtk.ComboBox(model = aircraftTypesModel)
1803 renderer = gtk.CellRendererText()
1804 self._aircraftType.pack_start(renderer, True)
1805 self._aircraftType.add_attribute(renderer, "text", 0)
1806 self._aircraftType.set_tooltip_text(xstr("student_check_flight_type_tooltip"))
1807 self._aircraftType.set_active(0)
1808
1809 aircraftTypeAlignment.add(self._aircraftType)
1810
1811 hbox.pack_start(aircraftTypeAlignment, False, False, 0)
1812
1813 buttonAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
1814 button = self._checkFlightButton = gtk.Button(xstr("student_check_flight"))
1815 button.set_use_underline(True)
1816 button.connect("clicked", self._checkFlightClicked)
1817 button.set_tooltip_text(xstr("student_check_flight_tooltip"))
1818
1819 hbox.pack_start(button, True, True, 0)
1820
1821 alignment.add(hbox)
1822 table.attach(alignment, 0, 4, row, row + 1, xoptions = FILL)
1823
1824 @property
1825 def aircraftType(self):
1826 """Get the type of the aircraft used to perform the check flight."""
1827 index = self._aircraftType.get_active()
1828 return self._aircraftType.get_model()[index][1]
1829
1830 def activate(self):
1831 """Activate the student page."""
1832 print("StudentPage.activate")
1833 self._getEntryExamStatusCancelled = False
1834
1835 loginResult = self._wizard.loginResult
1836 self._entryExamLink = loginResult.entryExamLink
1837
1838 self._updateEntryExamStatus(loginResult.entryExamPassed)
1839 self._getEntryExamStatus()
1840
1841 # FIXME: call with real value
1842 self._updateCheckFlightStatus(self._wizard.loginResult.checkFlightStatus)
1843
1844 def finalize(self):
1845 """Finalize the page."""
1846 print("StudentPage.finalize")
1847 self._getEntryExamStatusCancelled = True
1848
1849 def _entryExamClicked(self, button):
1850 """Called when the entry exam button is clicked."""
1851 webbrowser.open(self._entryExamLink)
1852
1853 def _getEntryExamStatus(self):
1854 """Initiate the query of the entry exam status after the interval."""
1855 if not self._getEntryExamStatusCancelled:
1856 gobject.timeout_add(StudentPage._entryExamStatusQueryInterval,
1857 lambda: self._wizard.gui.webHandler. \
1858 getEntryExamStatus(self._entryExamStatusCallback))
1859
1860 def _entryExamStatusCallback(self, returned, result):
1861 """Called when the entry exam status is available."""
1862 gobject.idle_add(self._handleEntryExamStatus, returned, result)
1863
1864 def _handleEntryExamStatus(self, returned, result):
1865 """Called when the entry exam status is availabe."""
1866 print("_handleEntryExamStatus", returned, result)
1867 if returned and not self._getEntryExamStatusCancelled:
1868 self._entryExamLink = result.entryExamLink
1869 self._updateEntryExamStatus(result.entryExamPassed)
1870 if result.madeFO:
1871 self._madeFO()
1872 else:
1873 self._getEntryExamStatus()
1874
1875 def _updateEntryExamStatus(self, passed):
1876 """Update the entry exam status display and button."""
1877 self._entryExamStatus.set_text(xstr("student_entry_exam_passed")
1878 if passed else
1879 xstr("student_entry_exam_not_passed"))
1880 self._entryExamStatus.set_use_markup(True)
1881 self._entryExamButton.set_sensitive(not passed)
1882 self._wizard._loginResult.entryExamPassed = passed
1883
1884 def _checkFlightClicked(self, button):
1885 """Called when the check flight button is clicked."""
1886 aircraftType = self.aircraftType
1887 self._wizard._bookedFlight = \
1888 web.BookedFlight.forCheckFlight(aircraftType)
1889 self._wizard.gui.enableFlightInfo(aircraftType)
1890 self._wizard.jumpPage("connect")
1891
1892 def _updateCheckFlightStatus(self, passed):
1893 """Update the status of the check flight."""
1894 self._aircraftType.set_sensitive(not passed)
1895 self._checkFlightStatus.set_text(xstr("student_check_flight_passed")
1896 if passed else
1897 xstr("student_check_flight_not_passed"))
1898 self._checkFlightStatus.set_use_markup(True)
1899 self._checkFlightButton.set_sensitive(not passed)
1900
1901 def _madeFO(self):
1902 """Handle the event when the pilot has become a first officer."""
1903 wizard = self._wizard
1904 loginResult = wizard.loginResult
1905 loginResult.rank = "FO"
1906
1907 gui = wizard.gui
1908
1909 dialog = gtk.MessageDialog(parent = gui.mainWindow,
1910 type = MESSAGETYPE_INFO,
1911 message_format = xstr("student_fo"))
1912
1913 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1914 dialog.set_title(WINDOW_TITLE_BASE)
1915 secondary = xstr("student_fo_secondary")
1916 dialog.format_secondary_markup(secondary)
1917 dialog.run()
1918 dialog.hide()
1919
1920 gui.reset()
1921
1922#-----------------------------------------------------------------------------
1923
1924class ConnectPage(Page):
1925 """Page which displays the departure airport and gate (if at LHBP)."""
1926 def __init__(self, wizard):
1927 """Construct the connect page."""
1928 help = "Load the aircraft below into the simulator and park it\n" \
1929 "at the given airport, at the gate below, if present.\n\n" \
1930 "Then press the Connect button to connect to the simulator."
1931 completedHelp = "The basic data of your flight can be read below."
1932 super(ConnectPage, self).__init__(wizard, "connect",
1933 xstr("connect_title"),
1934 xstr("connect_help"),
1935 completedHelp = xstr("connect_chelp"))
1936
1937 self._selectSimulator = os.name=="nt" or "FORCE_SELECT_SIM" in os.environ
1938
1939 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1940 xscale = 0.0, yscale = 0.0)
1941
1942 table = gtk.Table(7 if self._selectSimulator else 5, 2)
1943 table.set_row_spacings(4)
1944 table.set_col_spacings(16)
1945 table.set_homogeneous(True)
1946 alignment.add(table)
1947 self.setMainWidget(alignment)
1948
1949 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
1950 label = gtk.Label(xstr("connect_flightno"))
1951 labelAlignment.add(label)
1952 table.attach(labelAlignment, 0, 1, 0, 1)
1953
1954 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
1955 self._flightNumber = gtk.Label()
1956 self._flightNumber.set_width_chars(9)
1957 self._flightNumber.set_alignment(0.0, 0.5)
1958 labelAlignment.add(self._flightNumber)
1959 table.attach(labelAlignment, 1, 2, 0, 1)
1960
1961 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
1962 label = gtk.Label(xstr("connect_acft"))
1963 labelAlignment.add(label)
1964 table.attach(labelAlignment, 0, 1, 1, 2)
1965
1966 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
1967 self._aircraft = gtk.Label()
1968 self._aircraft.set_width_chars(25)
1969 self._aircraft.set_alignment(0.0, 0.5)
1970 labelAlignment.add(self._aircraft)
1971 table.attach(labelAlignment, 1, 2, 1, 2)
1972
1973 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
1974 label = gtk.Label(xstr("connect_tailno"))
1975 labelAlignment.add(label)
1976 table.attach(labelAlignment, 0, 1, 2, 3)
1977
1978 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
1979 self._tailNumber = gtk.Label()
1980 self._tailNumber.set_width_chars(10)
1981 self._tailNumber.set_alignment(0.0, 0.5)
1982 labelAlignment.add(self._tailNumber)
1983 table.attach(labelAlignment, 1, 2, 2, 3)
1984
1985 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
1986 label = gtk.Label(xstr("connect_airport"))
1987 labelAlignment.add(label)
1988 table.attach(labelAlignment, 0, 1, 3, 4)
1989
1990 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
1991 self._departureICAO = gtk.Label()
1992 self._departureICAO.set_width_chars(6)
1993 self._departureICAO.set_alignment(0.0, 0.5)
1994 labelAlignment.add(self._departureICAO)
1995 table.attach(labelAlignment, 1, 2, 3, 4)
1996
1997 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
1998 label = gtk.Label(xstr("connect_gate"))
1999 labelAlignment.add(label)
2000 table.attach(labelAlignment, 0, 1, 4, 5)
2001
2002 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2003 self._departureGate = gtk.Label()
2004 self._departureGate.set_width_chars(5)
2005 self._departureGate.set_alignment(0.0, 0.5)
2006 labelAlignment.add(self._departureGate)
2007 table.attach(labelAlignment, 1, 2, 4, 5)
2008
2009 if self._selectSimulator:
2010 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0, yalign=0.5)
2011 label = gtk.Label(xstr("connect_sim"))
2012 labelAlignment.add(label)
2013 table.attach(labelAlignment, 0, 1, 5, 7)
2014
2015 selectAlignment = gtk.Alignment(xalign=0.0, xscale=0.0, yalign=0.5)
2016
2017 selectBox = gtk.HBox()
2018 if pygobject:
2019 self._selectMSFS = \
2020 gtk.RadioButton.new_with_mnemonic_from_widget(None,
2021 xstr("connect_sim_msfs"))
2022 else:
2023 self._selectMSFS = gtk.RadioButton(None,
2024 xstr("connect_sim_msfs"))
2025
2026 selectBox.pack_start(self._selectMSFS, False, False, 0);
2027
2028 if pygobject:
2029 self._selectXPlane = \
2030 gtk.RadioButton.new_with_mnemonic_from_widget(self._selectMSFS,
2031 xstr("connect_sim_xplane"))
2032 else:
2033 self._selectXPlane = gtk.RadioButton(self._selectMSFS,
2034 xstr("connect_sim_xplane"))
2035
2036 selectBox.pack_start(self._selectXPlane, False, False, 8);
2037
2038 selectAlignment.add(selectBox)
2039 table.attach(selectAlignment, 1, 2, 5, 7)
2040
2041
2042 self.addCancelFlightButton()
2043
2044 self._previousButton = \
2045 self.addPreviousButton(clicked = self._backClicked)
2046
2047 self._button = self.addButton(xstr("button_connect"), default = True,
2048 tooltip = xstr("button_connect_tooltip"))
2049 self._clickedID = self._button.connect("clicked", self._connectClicked)
2050
2051 def activate(self):
2052 """Setup the departure information."""
2053 self._button.set_label(xstr("button_connect"))
2054 self._button.set_use_underline(True)
2055 self._button.set_tooltip_text(xstr("button_connect_tooltip"))
2056 self._button.disconnect(self._clickedID)
2057 self._clickedID = self._button.connect("clicked", self._connectClicked)
2058
2059 bookedFlight = self._wizard._bookedFlight
2060
2061 self._flightNumber.set_markup("<b>" + bookedFlight.callsign + "</b>")
2062
2063 aircraftType = aircraftNames[bookedFlight.aircraftType]
2064 self._aircraft.set_markup("<b>" + aircraftType + "</b>")
2065
2066 self._tailNumber.set_markup("<b>" + bookedFlight.tailNumber + "</b>")
2067
2068 icao = bookedFlight.departureICAO
2069 self._departureICAO.set_markup("<b>" + icao + "</b>")
2070 gate = self._wizard._departureGate
2071 if gate!="-":
2072 gate = "<b>" + gate + "</b>"
2073 self._departureGate.set_markup(gate)
2074
2075 if self._selectSimulator:
2076 config = self._wizard.gui.config
2077 self._selectMSFS.set_active(config.defaultMSFS)
2078 self._selectXPlane.set_active(not config.defaultMSFS)
2079
2080 self._previousButton.set_sensitive(not self._wizard.entranceExam)
2081
2082 def finalize(self):
2083 """Finalize the page."""
2084 self._button.set_label(xstr("button_next"))
2085 self._button.set_use_underline(True)
2086 self._button.set_tooltip_text(xstr("button_next_tooltip"))
2087 self._button.disconnect(self._clickedID)
2088 self._clickedID = self._button.connect("clicked", self._forwardClicked)
2089
2090 def _backClicked(self, button):
2091 """Called when the Back button is pressed."""
2092 self.goBack()
2093
2094 def _connectClicked(self, button):
2095 """Called when the Connect button is pressed."""
2096 if self._selectSimulator:
2097 simulatorType = const.SIM_MSFS9 if self._selectMSFS.get_active() \
2098 else const.SIM_XPLANE10
2099 else:
2100 simulatorType = const.SIM_MSFS9 if os.name=="nt" \
2101 else const.SIM_XPLANE10
2102
2103 config = self._wizard.gui.config
2104 config.defaultMSFS = simulatorType == const.SIM_MSFS9
2105 config.save()
2106
2107 self._wizard._connectSimulator(simulatorType)
2108
2109 def _forwardClicked(self, button):
2110 """Called when the Forward button is pressed."""
2111 self._wizard.nextPage()
2112
2113#-----------------------------------------------------------------------------
2114
2115class PayloadPage(Page):
2116 """Page to allow setting up the payload."""
2117 def __init__(self, wizard):
2118 """Construct the page."""
2119 super(PayloadPage, self).__init__(wizard, "payload",
2120 xstr("payload_title"),
2121 xstr("payload_help"),
2122 completedHelp = xstr("payload_chelp"))
2123
2124 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2125 xscale = 0.0, yscale = 0.0)
2126
2127 table = gtk.Table(7, 3)
2128 table.set_row_spacings(4)
2129 table.set_col_spacings(16)
2130 table.set_homogeneous(False)
2131 alignment.add(table)
2132 self.setMainWidget(alignment)
2133
2134 label = gtk.Label(xstr("payload_crew"))
2135 label.set_use_underline(True)
2136 label.set_alignment(0.0, 0.5)
2137 table.attach(label, 0, 1, 0, 1)
2138
2139 self._numCrew = IntegerEntry(defaultValue = 0)
2140 self._numCrew.set_width_chars(6)
2141 self._numCrew.connect("integer-changed", self._weightChanged)
2142 self._numCrew.set_tooltip_text(xstr("payload_crew_tooltip"))
2143 table.attach(self._numCrew, 1, 2, 0, 1)
2144 label.set_mnemonic_widget(self._numCrew)
2145
2146 label = gtk.Label(xstr("payload_pax"))
2147 label.set_use_underline(True)
2148 label.set_alignment(0.0, 0.5)
2149 table.attach(label, 0, 1, 1, 2)
2150
2151 self._numPassengers = IntegerEntry(defaultValue = 0)
2152 self._numPassengers.set_width_chars(6)
2153 self._numPassengers.connect("integer-changed", self._weightChanged)
2154 self._numPassengers.set_tooltip_text(xstr("payload_pax_tooltip"))
2155 table.attach(self._numPassengers, 1, 2, 1, 2)
2156 label.set_mnemonic_widget(self._numPassengers)
2157
2158 label = gtk.Label(xstr("payload_bag"))
2159 label.set_use_underline(True)
2160 label.set_alignment(0.0, 0.5)
2161 table.attach(label, 0, 1, 2, 3)
2162
2163 self._bagWeight = IntegerEntry(defaultValue = 0)
2164 self._bagWeight.set_width_chars(6)
2165 self._bagWeight.connect("integer-changed", self._weightChanged)
2166 self._bagWeight.set_tooltip_text(xstr("payload_bag_tooltip"))
2167 table.attach(self._bagWeight, 1, 2, 2, 3)
2168 label.set_mnemonic_widget(self._bagWeight)
2169
2170 table.attach(gtk.Label("kg"), 2, 3, 2, 3)
2171
2172 label = gtk.Label(xstr("payload_cargo"))
2173 label.set_use_underline(True)
2174 label.set_alignment(0.0, 0.5)
2175 table.attach(label, 0, 1, 3, 4)
2176
2177 self._cargoWeight = IntegerEntry(defaultValue = 0)
2178 self._cargoWeight.set_width_chars(6)
2179 self._cargoWeight.connect("integer-changed", self._weightChanged)
2180 self._cargoWeight.set_tooltip_text(xstr("payload_cargo_tooltip"))
2181 table.attach(self._cargoWeight, 1, 2, 3, 4)
2182 label.set_mnemonic_widget(self._cargoWeight)
2183
2184 table.attach(gtk.Label("kg"), 2, 3, 3, 4)
2185
2186 label = gtk.Label(xstr("payload_mail"))
2187 label.set_use_underline(True)
2188 label.set_alignment(0.0, 0.5)
2189 table.attach(label, 0, 1, 4, 5)
2190
2191 self._mailWeight = IntegerEntry(defaultValue = 0)
2192 self._mailWeight.set_width_chars(6)
2193 self._mailWeight.connect("integer-changed", self._weightChanged)
2194 self._mailWeight.set_tooltip_text(xstr("payload_mail_tooltip"))
2195 table.attach(self._mailWeight, 1, 2, 4, 5)
2196 label.set_mnemonic_widget(self._mailWeight)
2197
2198 table.attach(gtk.Label("kg"), 2, 3, 4, 5)
2199
2200 label = gtk.Label("<b>" + xstr("payload_zfw") + "</b>")
2201 label.set_alignment(0.0, 0.5)
2202 label.set_use_markup(True)
2203 table.attach(label, 0, 1, 5, 6)
2204
2205 self._calculatedZFW = gtk.Label()
2206 self._calculatedZFW.set_width_chars(6)
2207 self._calculatedZFW.set_alignment(1.0, 0.5)
2208 table.attach(self._calculatedZFW, 1, 2, 5, 6)
2209
2210 table.attach(gtk.Label("kg"), 2, 3, 5, 6)
2211
2212 self._zfwButton = gtk.Button(xstr("payload_fszfw"))
2213 self._zfwButton.set_use_underline(True)
2214 self._zfwButton.connect("clicked", self._zfwRequested)
2215 self._zfwButton.set_tooltip_text(xstr("payload_fszfw_tooltip"))
2216 table.attach(self._zfwButton, 0, 1, 6, 7)
2217
2218 self._simulatorZFW = gtk.Label("-")
2219 self._simulatorZFW.set_width_chars(6)
2220 self._simulatorZFW.set_alignment(1.0, 0.5)
2221 table.attach(self._simulatorZFW, 1, 2, 6, 7)
2222 self._simulatorZFWValue = None
2223
2224 table.attach(gtk.Label("kg"), 2, 3, 6, 7)
2225
2226 self.addCancelFlightButton()
2227 self._backButton = self.addPreviousButton(clicked = self._backClicked)
2228 self._button = self.addNextButton(clicked = self._forwardClicked)
2229
2230 @property
2231 def numCrew(self):
2232 """The number of the crew members on the flight."""
2233 return self._numCrew.get_int()
2234
2235 @property
2236 def numPassengers(self):
2237 """The number of the passengers on the flight."""
2238 return self._numPassengers.get_int()
2239
2240 @property
2241 def bagWeight(self):
2242 """Get the bag weight entered."""
2243 return self._bagWeight.get_int()
2244
2245 @property
2246 def cargoWeight(self):
2247 """Get the cargo weight entered."""
2248 return self._cargoWeight.get_int()
2249
2250 @property
2251 def mailWeight(self):
2252 """Get the bag weight entered."""
2253 return self._mailWeight.get_int()
2254
2255 def activate(self):
2256 """Setup the information."""
2257 bookedFlight = self._wizard._bookedFlight
2258
2259 self._numCrew.set_int(bookedFlight.numCrew)
2260 self._numCrew.set_sensitive(True)
2261 self._numPassengers.set_int(bookedFlight.numPassengers)
2262 self._numPassengers.set_sensitive(True)
2263
2264 self._bagWeight.set_int(bookedFlight.bagWeight)
2265 self._bagWeight.set_sensitive(True)
2266 self._cargoWeight.set_int(bookedFlight.cargoWeight)
2267 self._cargoWeight.set_sensitive(True)
2268 self._mailWeight.set_int(bookedFlight.mailWeight)
2269 self._mailWeight.set_sensitive(True)
2270
2271 self._simulatorZFW.set_text("-")
2272 self._simulatorZFWValue = None
2273 self._zfwButton.set_sensitive(True)
2274 self._updateCalculatedZFW()
2275
2276 def finalize(self):
2277 """Finalize the payload page."""
2278 self._numCrew.set_sensitive(False)
2279 self._numPassengers.set_sensitive(False)
2280 self._bagWeight.set_sensitive(False)
2281 self._cargoWeight.set_sensitive(False)
2282 self._mailWeight.set_sensitive(False)
2283 self._wizard.gui.initializeWeightHelp()
2284
2285 def calculateZFW(self):
2286 """Calculate the ZFW value."""
2287 zfw = self._wizard.gui._flight.aircraft.dow
2288 zfw += (self._numCrew.get_int() + self._numPassengers.get_int()) * 82
2289 zfw += self._bagWeight.get_int()
2290 zfw += self._cargoWeight.get_int()
2291 zfw += self._mailWeight.get_int()
2292 return zfw
2293
2294 def _updateCalculatedZFW(self):
2295 """Update the calculated ZFW"""
2296 zfw = self.calculateZFW()
2297
2298 markupBegin = "<b>"
2299 markupEnd = "</b>"
2300 if self._simulatorZFWValue is not None and \
2301 PayloadChecker.isZFWFaulty(self._simulatorZFWValue, zfw):
2302 markupBegin += '<span foreground="red">'
2303 markupEnd = "</span>" + markupEnd
2304 self._calculatedZFW.set_markup(markupBegin + str(zfw) + markupEnd)
2305
2306 def _weightChanged(self, entry, weight):
2307 """Called when one of the weight values or humanm counts has changed."""
2308 self._updateCalculatedZFW()
2309
2310 def _zfwRequested(self, button):
2311 """Called when the ZFW is requested from the simulator."""
2312 self._zfwButton.set_sensitive(False)
2313 self._backButton.set_sensitive(False)
2314 self._button.set_sensitive(False)
2315 gui = self._wizard.gui
2316 gui.beginBusy(xstr("payload_zfw_busy"))
2317 gui.simulator.requestZFW(self._handleZFW)
2318
2319 def _handleZFW(self, zfw):
2320 """Called when the ZFW value is retrieved."""
2321 gobject.idle_add(self._processZFW, zfw)
2322
2323 def _processZFW(self, zfw):
2324 """Process the given ZFW value received from the simulator."""
2325 self._wizard.gui.endBusy()
2326 self._zfwButton.set_sensitive(True)
2327 self._backButton.set_sensitive(True)
2328 self._button.set_sensitive(True)
2329 self._simulatorZFWValue = zfw
2330 self._simulatorZFW.set_text("%.0f" % (zfw,))
2331 self._updateCalculatedZFW()
2332
2333 def _forwardClicked(self, button):
2334 """Called when the forward button is clicked."""
2335 self._wizard.nextPage()
2336
2337 def _backClicked(self, button):
2338 """Called when the Back button is pressed."""
2339 self.goBack()
2340
2341#-----------------------------------------------------------------------------
2342
2343class TimePage(Page):
2344 """Page displaying the departure and arrival times and allows querying the
2345 current time from the flight simulator."""
2346 def __init__(self, wizard):
2347 super(TimePage, self).__init__(wizard, "time",
2348 xstr("time_title"),
2349 xstr("time_help"),
2350 completedHelp = xstr("time_chelp"))
2351
2352 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2353 xscale = 0.0, yscale = 0.0)
2354
2355 table = gtk.Table(3, 2)
2356 table.set_row_spacings(4)
2357 table.set_col_spacings(16)
2358 table.set_homogeneous(False)
2359 alignment.add(table)
2360 self.setMainWidget(alignment)
2361
2362 label = gtk.Label(xstr("time_departure"))
2363 label.set_alignment(0.0, 0.5)
2364 table.attach(label, 0, 1, 0, 1)
2365
2366 self._departure = gtk.Label()
2367 self._departure.set_alignment(0.0, 0.5)
2368 table.attach(self._departure, 1, 2, 0, 1)
2369
2370 label = gtk.Label(xstr("time_arrival"))
2371 label.set_alignment(0.0, 0.5)
2372 table.attach(label, 0, 1, 1, 2)
2373
2374 self._arrival = gtk.Label()
2375 self._arrival.set_alignment(0.0, 0.5)
2376 table.attach(self._arrival, 1, 2, 1, 2)
2377
2378 self._timeButton = gtk.Button(xstr("time_fs"))
2379 self._timeButton.set_use_underline(True)
2380 self._timeButton.set_tooltip_text(xstr("time_fs_tooltip"))
2381 self._timeButton.connect("clicked", self._timeRequested)
2382 table.attach(self._timeButton, 0, 1, 2, 3)
2383
2384 self._simulatorTime = gtk.Label("-")
2385 self._simulatorTime.set_alignment(0.0, 0.5)
2386 table.attach(self._simulatorTime, 1, 2, 2, 3)
2387
2388 self.addCancelFlightButton()
2389
2390 self._backButton = self.addPreviousButton(clicked = self._backClicked)
2391 self._button = self.addNextButton(clicked = self._forwardClicked)
2392
2393 def activate(self):
2394 """Activate the page."""
2395 self._timeButton.set_sensitive(True)
2396 bookedFlight = self._wizard._bookedFlight
2397 self._departure.set_text(str(bookedFlight.departureTime.time()))
2398 self._arrival.set_text(str(bookedFlight.arrivalTime.time()))
2399 self._simulatorTime.set_text("-")
2400
2401 def _timeRequested(self, button):
2402 """Request the time from the simulator."""
2403 self._timeButton.set_sensitive(False)
2404 self._backButton.set_sensitive(False)
2405 self._button.set_sensitive(False)
2406 self._wizard.gui.beginBusy(xstr("time_busy"))
2407 self._wizard.gui.simulator.requestTime(self._handleTime)
2408
2409 def _handleTime(self, timestamp):
2410 """Handle the result of a time retrieval."""
2411 gobject.idle_add(self._processTime, timestamp)
2412
2413 def _processTime(self, timestamp):
2414 """Process the given time."""
2415 self._wizard.gui.endBusy()
2416 self._timeButton.set_sensitive(True)
2417 self._backButton.set_sensitive(True)
2418 self._button.set_sensitive(True)
2419 tm = time.gmtime(timestamp)
2420 t = datetime.time(tm.tm_hour, tm.tm_min, tm.tm_sec)
2421 self._simulatorTime.set_text(str(t))
2422
2423 ts = tm.tm_hour * 3600 + tm.tm_min * 60 + tm.tm_sec
2424 dt = self._wizard._bookedFlight.departureTime.time()
2425 dts = dt.hour * 3600 + dt.minute * 60 + dt.second
2426 diff = dts-ts
2427
2428 markupBegin = ""
2429 markupEnd = ""
2430 if diff < 0:
2431 markupBegin = '<b><span foreground="red">'
2432 markupEnd = '</span></b>'
2433 elif diff < 3*60 or diff > 30*60:
2434 markupBegin = '<b><span foreground="orange">'
2435 markupEnd = '</span></b>'
2436
2437 self._departure.set_markup(markupBegin + str(dt) + markupEnd)
2438
2439 def _backClicked(self, button):
2440 """Called when the Back button is pressed."""
2441 self.goBack()
2442
2443 def _forwardClicked(self, button):
2444 """Called when the forward button is clicked."""
2445 if not self._completed:
2446 gui = self._wizard.gui
2447 gui.beginBusy(xstr("fuel_get_busy"))
2448
2449 gui.simulator.getFuel(self._handleFuel)
2450 else:
2451 self._wizard.nextPage()
2452
2453 def _handleFuel(self, fuelData):
2454 """Callback for the fuel query operation."""
2455 gobject.idle_add(self._processFuel, fuelData)
2456
2457 def _processFuel(self, fuelData):
2458 """Process the given fuel data."""
2459 self._wizard.gui.endBusy()
2460 self._wizard._fuelData = fuelData
2461 self._wizard.nextPage()
2462
2463#-----------------------------------------------------------------------------
2464
2465class RoutePage(Page):
2466 """The page containing the route and the flight level."""
2467 def __init__(self, wizard):
2468 """Construct the page."""
2469 super(RoutePage, self).__init__(wizard, "route",
2470 xstr("route_title"),
2471 xstr("route_help"),
2472 completedHelp = xstr("route_chelp"))
2473
2474 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2475 xscale = 0.0, yscale = 0.0)
2476
2477 mainBox = gtk.VBox()
2478 alignment.add(mainBox)
2479 self.setMainWidget(alignment)
2480
2481 levelBox = gtk.HBox()
2482
2483 label = gtk.Label(xstr("route_level"))
2484 label.set_use_underline(True)
2485 levelBox.pack_start(label, True, True, 0)
2486
2487 self._cruiseLevel = gtk.SpinButton()
2488 self._cruiseLevel.set_increments(step = 10, page = 100)
2489 self._cruiseLevel.set_range(min = 0, max = 500)
2490 self._cruiseLevel.set_tooltip_text(xstr("route_level_tooltip"))
2491 self._cruiseLevel.set_numeric(True)
2492 self._cruiseLevel.connect("changed", self._cruiseLevelChanged)
2493 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
2494 label.set_mnemonic_widget(self._cruiseLevel)
2495
2496 levelBox.pack_start(self._cruiseLevel, False, False, 8)
2497
2498 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
2499 xscale = 0.0, yscale = 0.0)
2500 alignment.add(levelBox)
2501
2502 mainBox.pack_start(alignment, False, False, 0)
2503
2504
2505 routeBox = gtk.VBox()
2506
2507 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
2508 xscale = 0.0, yscale = 0.0)
2509 label = gtk.Label(xstr("route_route"))
2510 label.set_use_underline(True)
2511 alignment.add(label)
2512 routeBox.pack_start(alignment, True, True, 0)
2513
2514 routeWindow = gtk.ScrolledWindow()
2515 routeWindow.set_size_request(400, 80)
2516 routeWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
2517 else gtk.SHADOW_IN)
2518 routeWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
2519 else gtk.POLICY_AUTOMATIC,
2520 gtk.PolicyType.AUTOMATIC if pygobject
2521 else gtk.POLICY_AUTOMATIC)
2522
2523 self._uppercasingRoute = False
2524
2525 self._route = gtk.TextView()
2526 self._route.set_tooltip_text(xstr("route_route_tooltip"))
2527 self._route.set_wrap_mode(WRAP_WORD)
2528 self._route.get_buffer().connect("changed", self._routeChanged)
2529 self._route.get_buffer().connect_after("insert-text", self._routeInserted)
2530 routeWindow.add(self._route)
2531
2532 label.set_mnemonic_widget(self._route)
2533 routeBox.pack_start(routeWindow, True, True, 0)
2534
2535 mainBox.pack_start(routeBox, True, True, 8)
2536
2537 alternateBox = gtk.HBox()
2538
2539 label = gtk.Label(xstr("route_altn"))
2540 label.set_use_underline(True)
2541 alternateBox.pack_start(label, True, True, 0)
2542
2543 self._alternate = gtk.Entry()
2544 self._alternate.set_width_chars(6)
2545 self._alternate.connect("changed", self._alternateChanged)
2546 self._alternate.set_tooltip_text(xstr("route_altn_tooltip"))
2547 label.set_mnemonic_widget(self._alternate)
2548
2549 alternateBox.pack_start(self._alternate, False, False, 8)
2550
2551 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
2552 xscale = 0.0, yscale = 0.0)
2553 alignment.add(alternateBox)
2554
2555 mainBox.pack_start(alignment, False, False, 0)
2556
2557 self.addCancelFlightButton()
2558
2559 self._backButton = self.addPreviousButton(clicked = self._backClicked)
2560 self._button = self.addNextButton(clicked = self._forwardClicked)
2561
2562 @property
2563 def filedCruiseLevel(self):
2564 """Get the filed cruise level."""
2565 return self._cruiseLevel.get_value_as_int()
2566
2567 @property
2568 def route(self):
2569 """Get the route."""
2570 return self._getRoute()
2571
2572 @property
2573 def alternate(self):
2574 """Get the ICAO code of the alternate airport."""
2575 return self._alternate.get_text()
2576
2577 def activate(self):
2578 """Setup the route from the booked flight."""
2579 self._cruiseLevel.set_value(0)
2580 self._cruiseLevel.set_text("")
2581 self._route.get_buffer().set_text(self._wizard._bookedFlight.route)
2582 self._alternate.set_text("")
2583 self._updateForwardButton()
2584
2585 def _getRoute(self):
2586 """Get the text of the route."""
2587 buffer = self._route.get_buffer()
2588 return buffer.get_text(buffer.get_start_iter(),
2589 buffer.get_end_iter(), True)
2590
2591 def _updateForwardButton(self):
2592 """Update the sensitivity of the forward button."""
2593 cruiseLevelText = self._cruiseLevel.get_text()
2594 cruiseLevel = int(cruiseLevelText) if cruiseLevelText else 0
2595 alternate = self._alternate.get_text()
2596 self._button.set_sensitive(cruiseLevel>=50 and self._getRoute()!="" and
2597 (len(alternate)==4 or self._wizard.entranceExam))
2598
2599 def _cruiseLevelChanged(self, *arg):
2600 """Called when the cruise level has changed."""
2601 self._updateForwardButton()
2602
2603 def _routeChanged(self, textBuffer):
2604 """Called when the route has changed."""
2605 if not self._uppercasingRoute:
2606 self._updateForwardButton()
2607
2608 def _routeInserted(self, textBuffer, iter, text, length):
2609 """Called when new characters are inserted into the route.
2610
2611 It uppercases all characters."""
2612 if not self._uppercasingRoute:
2613 self._uppercasingRoute = True
2614
2615 iter1 = iter.copy()
2616 iter1.backward_chars(length)
2617 textBuffer.delete(iter, iter1)
2618
2619 textBuffer.insert(iter, text.upper())
2620
2621 self._uppercasingRoute = False
2622
2623 def _alternateChanged(self, entry):
2624 """Called when the alternate airport has changed."""
2625 entry.set_text(entry.get_text().upper())
2626 self._updateForwardButton()
2627
2628 def _backClicked(self, button):
2629 """Called when the Back button is pressed."""
2630 self.goBack()
2631
2632 def _forwardClicked(self, button):
2633 """Called when the Forward button is clicked."""
2634 if self._wizard.gui.flight.aircraft.simBriefData is None:
2635 self._wizard.usingSimBrief = False
2636 if self._wizard.gui.config.useSimBrief and \
2637 self._wizard.usingSimBrief is not False:
2638 self._wizard.jumpPage("simbrief_setup")
2639 else:
2640 self._wizard.usingSimBrief = False
2641 self._wizard.jumpPage("fuel")
2642
2643#-----------------------------------------------------------------------------
2644
2645class SimBriefCredentialsDialog(gtk.Dialog):
2646 """A dialog window to ask for SimBrief credentials."""
2647 def __init__(self, gui, userName, password, rememberPassword):
2648 """Construct the dialog."""
2649 super(SimBriefCredentialsDialog, self).__init__(WINDOW_TITLE_BASE + " - " +
2650 xstr("simbrief_credentials_title"),
2651 gui.mainWindow,
2652 DIALOG_MODAL)
2653 self.add_button(xstr("button_cancel"), RESPONSETYPE_CANCEL)
2654 self.add_button(xstr("button_ok"), RESPONSETYPE_OK)
2655
2656 contentArea = self.get_content_area()
2657
2658 contentAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2659 xscale = 0.0, yscale = 0.0)
2660 contentAlignment.set_padding(padding_top = 4, padding_bottom = 16,
2661 padding_left = 8, padding_right = 8)
2662
2663 contentArea.pack_start(contentAlignment, False, False, 0)
2664
2665 contentVBox = gtk.VBox()
2666 contentAlignment.add(contentVBox)
2667
2668 label = gtk.Label(xstr("simbrief_login_failed"))
2669 label.set_alignment(0.0, 0.0)
2670
2671 contentVBox.pack_start(label, False, False, 0)
2672
2673 tableAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2674 xscale = 0.0, yscale = 0.0)
2675 tableAlignment.set_padding(padding_top = 24, padding_bottom = 0,
2676 padding_left = 0, padding_right = 0)
2677
2678 table = gtk.Table(3, 2)
2679 table.set_row_spacings(4)
2680 table.set_col_spacings(16)
2681 table.set_homogeneous(False)
2682
2683 tableAlignment.add(table)
2684 contentVBox.pack_start(tableAlignment, True, True, 0)
2685
2686 label = gtk.Label(xstr("simbrief_username"))
2687 label.set_use_underline(True)
2688 label.set_alignment(0.0, 0.5)
2689 table.attach(label, 0, 1, 0, 1)
2690
2691 self._userName = gtk.Entry()
2692 self._userName.set_width_chars(16)
2693 #self._userName.connect("changed",
2694 # lambda button: self._updateForwardButton())
2695 self._userName.set_tooltip_text(xstr("simbrief_username_tooltip"))
2696 self._userName.set_text(userName)
2697 table.attach(self._userName, 1, 2, 0, 1)
2698 label.set_mnemonic_widget(self._userName)
2699
2700 label = gtk.Label(xstr("simbrief_password"))
2701 label.set_use_underline(True)
2702 label.set_alignment(0.0, 0.5)
2703 table.attach(label, 0, 1, 1, 2)
2704
2705 self._password = gtk.Entry()
2706 self._password.set_visibility(False)
2707 #self._password.connect("changed",
2708 # lambda button: self._updateForwardButton())
2709 self._password.set_tooltip_text(xstr("simbrief_password_tooltip"))
2710 self._password.set_text(password)
2711 table.attach(self._password, 1, 2, 1, 2)
2712 label.set_mnemonic_widget(self._password)
2713
2714 self._rememberButton = gtk.CheckButton(xstr("simbrief_remember_password"))
2715 self._rememberButton.set_use_underline(True)
2716 self._rememberButton.set_tooltip_text(xstr("simbrief_remember_tooltip"))
2717 self._rememberButton.set_active(rememberPassword)
2718 table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
2719
2720 @property
2721 def userName(self):
2722 """Get the user name entered."""
2723 return self._userName.get_text()
2724
2725 @property
2726 def password(self):
2727 """Get the password entered."""
2728 return self._password.get_text()
2729
2730 @property
2731 def rememberPassword(self):
2732 """Get whether the password is to be remembered."""
2733 return self._rememberButton.get_active()
2734
2735 def run(self):
2736 """Run the dialog."""
2737 self.show_all()
2738
2739 response = super(SimBriefCredentialsDialog, self).run()
2740
2741 self.hide()
2742
2743 return response
2744
2745#-----------------------------------------------------------------------------
2746
2747class SimBriefSetupPage(Page):
2748 """Page for setting up some parameters for SimBrief."""
2749 monthNum2Name = [
2750 "JAN",
2751 "FEB",
2752 "MAR",
2753 "APR",
2754 "MAY",
2755 "JUN",
2756 "JUL",
2757 "AUG",
2758 "SEP",
2759 "OCT",
2760 "NOV",
2761 "DEC"
2762 ]
2763
2764 progress2Message = {
2765 cef.SIMBRIEF_PROGRESS_SEARCHING_BROWSER: "simbrief_progress_searching_browser",
2766 cef.SIMBRIEF_PROGRESS_LOADING_FORM: "simbrief_progress_loading_form",
2767 cef.SIMBRIEF_PROGRESS_FILLING_FORM: "simbrief_progress_filling_form",
2768 cef.SIMBRIEF_PROGRESS_WAITING_LOGIN: "simbrief_progress_waiting_login",
2769 cef.SIMBRIEF_PROGRESS_LOGGING_IN: "simbrief_progress_logging_in",
2770 cef.SIMBRIEF_PROGRESS_WAITING_RESULT: "simbrief_progress_waiting_result",
2771 cef.SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING: "simbrief_progress_retrieving_briefing"
2772 }
2773
2774 result2Message = {
2775 cef.SIMBRIEF_RESULT_ERROR_OTHER: "simbrief_result_error_other",
2776 cef.SIMBRIEF_RESULT_ERROR_NO_FORM: "simbrief_result_error_no_form",
2777 cef.SIMBRIEF_RESULT_ERROR_NO_POPUP: "simbrief_result_error_no_popup",
2778 cef.SIMBRIEF_RESULT_ERROR_LOGIN_FAILED: "simbrief_result_error_login_failed"
2779 }
2780
2781 @staticmethod
2782 def getHTMLFilePath():
2783 """Get the path of the HTML file to contain the generated flight
2784 plan."""
2785 if os.name=="nt":
2786 return os.path.join(tempfile.gettempdir(),
2787 "mlx_simbrief" +
2788 (".secondary" if secondaryInstallation else "") +
2789 ".html")
2790 else:
2791 import pwd
2792 return os.path.join(tempfile.gettempdir(),
2793 "mlx_simbrief." + pwd.getpwuid(os.getuid())[0] + "" +
2794 (".secondary" if secondaryInstallation else "") +
2795 ".html")
2796
2797 def __init__(self, wizard):
2798 """Construct the setup page."""
2799
2800 super(SimBriefSetupPage, self).__init__(wizard, "simbrief_setup",
2801 xstr("simbrief_setup_title"),
2802 xstr("simbrief_setup_help"),
2803 xstr("simbrief_setup_chelp"))
2804
2805 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2806 xscale = 0.0, yscale = 0.0)
2807
2808 table = gtk.Table(9, 3)
2809 table.set_row_spacings(4)
2810 table.set_col_spacings(16)
2811 table.set_homogeneous(False)
2812 alignment.add(table)
2813 self.setMainWidget(alignment)
2814
2815 label = gtk.Label(xstr("simbrief_username"))
2816 label.set_use_underline(True)
2817 label.set_alignment(0.0, 0.5)
2818 table.attach(label, 0, 1, 0, 1)
2819
2820 self._userName = gtk.Entry()
2821 self._userName.set_width_chars(16)
2822 self._userName.connect("changed",
2823 lambda button: self._updateForwardButton())
2824 self._userName.set_tooltip_text(xstr("simbrief_username_tooltip"))
2825 table.attach(self._userName, 1, 2, 0, 1)
2826 label.set_mnemonic_widget(self._userName)
2827
2828 label = gtk.Label(xstr("simbrief_password"))
2829 label.set_use_underline(True)
2830 label.set_alignment(0.0, 0.5)
2831 table.attach(label, 0, 1, 1, 2)
2832
2833 self._password = gtk.Entry()
2834 self._password.set_visibility(False)
2835 self._password.connect("changed",
2836 lambda button: self._updateForwardButton())
2837 self._password.set_tooltip_text(xstr("simbrief_password_tooltip"))
2838 table.attach(self._password, 1, 2, 1, 2)
2839 label.set_mnemonic_widget(self._password)
2840
2841 self._rememberButton = gtk.CheckButton(xstr("simbrief_remember_password"))
2842 self._rememberButton.set_use_underline(True)
2843 self._rememberButton.set_tooltip_text(xstr("simbrief_remember_tooltip"))
2844 table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
2845
2846 label = gtk.Label(xstr("simbrief_extra_fuel"))
2847 label.set_use_underline(True)
2848 label.set_alignment(0.0, 0.5)
2849 table.attach(label, 0, 1, 3, 4)
2850
2851 self._extraFuel = IntegerEntry(defaultValue = 0)
2852 self._extraFuel.set_width_chars(6)
2853 self._extraFuel.set_tooltip_text(xstr("simbrief_extra_fuel_tooltip"))
2854 table.attach(self._extraFuel, 1, 2, 3, 4)
2855 label.set_mnemonic_widget(self._extraFuel)
2856
2857 table.attach(gtk.Label("kg"), 2, 3, 3, 4)
2858
2859 label = gtk.Label(xstr("simbrief_takeoff_runway"))
2860 label.set_use_underline(True)
2861 label.set_alignment(0.0, 0.5)
2862 table.attach(label, 0, 1, 4, 5)
2863
2864 self._takeoffRunway = gtk.Entry()
2865 self._takeoffRunway.set_width_chars(10)
2866 self._takeoffRunway.set_tooltip_text(xstr("simbrief_takeoff_runway_tooltip"))
2867 self._takeoffRunway.connect("changed", self._upperChanged)
2868 table.attach(self._takeoffRunway, 1, 2, 4, 5)
2869 label.set_mnemonic_widget(self._takeoffRunway)
2870
2871 label = gtk.Label(xstr("simbrief_landing_runway"))
2872 label.set_use_underline(True)
2873 label.set_alignment(0.0, 0.5)
2874 table.attach(label, 0, 1, 5, 6)
2875
2876 self._landingRunway = gtk.Entry()
2877 self._landingRunway.set_width_chars(10)
2878 self._landingRunway.set_tooltip_text(xstr("simbrief_takeoff_runway_tooltip"))
2879 self._landingRunway.connect("changed", self._upperChanged)
2880 table.attach(self._landingRunway, 1, 2, 5, 6)
2881 label.set_mnemonic_widget(self._landingRunway)
2882
2883 label = gtk.Label(xstr("simbrief_climb_profile"))
2884 label.set_use_underline(True)
2885 label.set_alignment(0.0, 0.5)
2886 table.attach(label, 0, 1, 6, 7)
2887
2888 self._climbProfile = gtk.ComboBox()
2889 renderer = gtk.CellRendererText()
2890 self._climbProfile.pack_start(renderer, True)
2891 self._climbProfile.add_attribute(renderer, "text", 0)
2892 self._climbProfile.set_tooltip_text(xstr("simbrief_climb_profile_tooltip"))
2893 table.attach(self._climbProfile, 1, 2, 6, 7)
2894 label.set_mnemonic_widget(self._climbProfile)
2895
2896 label = gtk.Label(xstr("simbrief_cruise_profile"))
2897 label.set_use_underline(True)
2898 label.set_alignment(0.0, 0.5)
2899 table.attach(label, 0, 1, 7, 8)
2900
2901 self._cruiseProfile = gtk.ComboBox()
2902 renderer = gtk.CellRendererText()
2903 self._cruiseProfile.pack_start(renderer, True)
2904 self._cruiseProfile.add_attribute(renderer, "text", 0)
2905 self._cruiseProfile.set_tooltip_text(xstr("simbrief_cruise_profile_tooltip"))
2906 table.attach(self._cruiseProfile, 1, 2, 7, 8)
2907 label.set_mnemonic_widget(self._cruiseProfile)
2908
2909 label = gtk.Label(xstr("simbrief_descent_profile"))
2910 label.set_use_underline(True)
2911 label.set_alignment(0.0, 0.5)
2912 table.attach(label, 0, 1, 8, 9)
2913
2914 self._descentProfile = gtk.ComboBox()
2915 renderer = gtk.CellRendererText()
2916 self._descentProfile.pack_start(renderer, True)
2917 self._descentProfile.add_attribute(renderer, "text", 0)
2918 self._descentProfile.set_tooltip_text(xstr("simbrief_descent_profile_tooltip"))
2919 table.attach(self._descentProfile, 1, 2, 8, 9)
2920 label.set_mnemonic_widget(self._descentProfile)
2921
2922 self.addCancelFlightButton()
2923
2924 self._backButton = self.addPreviousButton(clicked = self._backClicked)
2925 self._button = self.addNextButton(clicked = self._forwardClicked)
2926
2927 def activate(self):
2928 """Activate the SimBrief setup page"""
2929 config = self._wizard.gui.config
2930
2931 self._userName.set_text(config.simBriefUserName)
2932 self._userName.set_sensitive(True)
2933
2934 self._password.set_text(config.simBriefPassword)
2935 self._password.set_sensitive(True)
2936
2937 self._rememberButton.set_active(config.rememberSimBriefPassword)
2938 self._rememberButton.set_sensitive(True)
2939
2940 self._extraFuel.set_int(0)
2941 self._extraFuel.set_sensitive(True)
2942
2943 self._takeoffRunway.set_text("")
2944 self._takeoffRunway.set_sensitive(True)
2945
2946 self._landingRunway.set_text("")
2947 self._landingRunway.set_sensitive(True)
2948
2949 simBriefData = self._wizard.gui.flight.aircraft.simBriefData
2950 for (control, profiles) in [(self._climbProfile,
2951 simBriefData.climbProfiles),
2952 (self._cruiseProfile,
2953 simBriefData.cruiseProfiles),
2954 (self._descentProfile,
2955 simBriefData.descentProfiles)]:
2956 model = gtk.ListStore(str)
2957 for profile in profiles:
2958 model.append([profile])
2959 control.set_model(model)
2960 control.set_sensitive(True)
2961
2962 self._climbProfile.set_active(0)
2963 self._cruiseProfile.set_active(0)
2964 self._descentProfile.set_active(0)
2965
2966 self._updateForwardButton()
2967
2968 def _updateForwardButton(self):
2969 """Update the sensitivity of the forward button."""
2970 self._button.set_sensitive(len(self._userName.get_text())>0 and
2971 len(self._password.get_text())>0)
2972
2973 def _backClicked(self, button):
2974 """Called when the Back button is pressed."""
2975 self.goBack()
2976
2977 def _forwardClicked(self, button):
2978 if self._completed:
2979 self._wizard.nextPage()
2980 else:
2981 config = self._wizard.gui.config
2982
2983 config.simBriefUserName = self._userName.get_text()
2984
2985 rememberPassword = self._rememberButton.get_active()
2986 config.simBriefPassword = \
2987 self._password.get_text() if rememberPassword else ""
2988 config.rememberSimBriefPassword = rememberPassword
2989
2990 config.save()
2991
2992 plan = self._getPlan()
2993 print("plan:", plan)
2994
2995 takeoffRunway = self._takeoffRunway.get_text()
2996 if takeoffRunway:
2997 self._wizard.takeoffRunway = takeoffRunway
2998
2999 landingRunway = self._landingRunway.get_text()
3000 if landingRunway:
3001 self._wizard.landingRunway = landingRunway
3002
3003 self._userName.set_sensitive(False)
3004 self._password.set_sensitive(False)
3005 self._rememberButton.set_sensitive(False)
3006 self._extraFuel.set_sensitive(False)
3007 self._takeoffRunway.set_sensitive(False)
3008 self._landingRunway.set_sensitive(False)
3009
3010 self._climbProfile.set_sensitive(False)
3011 self._cruiseProfile.set_sensitive(False)
3012 self._descentProfile.set_sensitive(False)
3013
3014 self._wizard.gui.beginBusy(xstr("simbrief_calling"))
3015
3016 cef.callSimBrief(plan,
3017 self._getCredentials,
3018 self._simBriefProgress,
3019 SimBriefSetupPage.getHTMLFilePath())
3020
3021 startSound(const.SOUND_NOTAM)
3022
3023 def _getCredentials(self, count):
3024 """Get the credentials.
3025
3026 If count is 0, the user name and password entered into the setup page
3027 are returned. Otherwise a dialog box is displayed informing the user of
3028 invalid credentials and requesting another set of them."""
3029 print("_getCredentials", count)
3030 if count==0:
3031 return (self._userName.get_text(), self._password.get_text())
3032 else:
3033 gui = self._wizard.gui
3034 config = gui.config
3035
3036 dialog = SimBriefCredentialsDialog(gui,
3037 config.simBriefUserName,
3038 config.simBriefPassword,
3039 config.rememberSimBriefPassword)
3040 response = dialog.run()
3041
3042 if response==RESPONSETYPE_OK:
3043 userName = dialog.userName
3044 self._userName.set_text(userName)
3045 password = dialog.password
3046 self._password.set_text(password)
3047 rememberPassword = dialog.rememberPassword
3048
3049 config.simBriefUserName = userName
3050
3051 config.simBriefPassword = \
3052 password if rememberPassword else ""
3053 config.rememberSimBriefPassword = rememberPassword
3054
3055 config.save()
3056
3057 return (userName, password)
3058 else:
3059 return (None, None)
3060
3061 def _simBriefProgress(self, progress, result, flightInfo):
3062 """The real SimBrief progress handler."""
3063 print("_simBriefProgress", progress, result, flightInfo)
3064 if result==cef.SIMBRIEF_RESULT_NONE:
3065 message = SimBriefSetupPage.progress2Message.get(progress,
3066 "simbrief_progress_unknown")
3067 self._wizard.gui.updateBusyState(xstr(message))
3068 else:
3069 self._wizard.gui.endBusy()
3070
3071 if result==cef.SIMBRIEF_RESULT_OK:
3072 self._wizard.departureMETARChanged(flightInfo["orig_metar"],
3073 self)
3074 self._wizard.arrivalMETARChanged(flightInfo["dest_metar"], self)
3075 self._wizard.nextPage()
3076 else:
3077 message = SimBriefSetupPage.result2Message.get(result,
3078 "simbrief_result_unknown")
3079 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
3080 type = MESSAGETYPE_ERROR,
3081 message_format =
3082 xstr(message) + "\n"+
3083 xstr("simbrief_cancelled"))
3084
3085 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
3086 dialog.set_title(WINDOW_TITLE_BASE)
3087 secondary = xstr("flightsel_save_failed_sec")
3088 dialog.format_secondary_markup(secondary)
3089 dialog.run()
3090 dialog.hide()
3091
3092 self._wizard.usingSimBrief = False
3093 self._wizard.jumpPage("fuel", fromPageShift = 1)
3094
3095 def _getPlan(self):
3096 """Get the flight plan data for SimBrief."""
3097 plan = {
3098 "airline": "MAH",
3099 "selcal": "XXXX",
3100 "fuelfactor": "P000",
3101 "contpct": "0.05",
3102 "resvrule": "45",
3103 "taxiout": "10",
3104 "taxiin": "10",
3105 "civalue": "AUTO"
3106 }
3107
3108 wizard = self._wizard
3109 gui = wizard.gui
3110
3111 loginResult = wizard.loginResult
3112 plan["cpt"] = loginResult.pilotName
3113 plan["pid"] = loginResult.pilotID
3114
3115 bookedFlight = wizard.bookedFlight
3116 plan["fltnum"] = wizard.bookedFlight.callsign[2:]
3117 plan["type"] = const.icaoCodes[bookedFlight.aircraftType]
3118 plan["orig"] = bookedFlight.departureICAO
3119 plan["dest"] = bookedFlight.arrivalICAO
3120 plan["reg"] = bookedFlight.tailNumber
3121 plan["fin"] = bookedFlight.tailNumber[3:]
3122 plan["pax"] = str(bookedFlight.numPassengers)
3123
3124 departureTime = bookedFlight.departureTime
3125 plan["date"] = "%d%s%d" % (departureTime.day,
3126 SimBriefSetupPage.monthNum2Name[departureTime.month-1],
3127 departureTime.year%100)
3128 plan["deph"] = str(departureTime.hour)
3129 plan["depm"] = str(departureTime.minute)
3130
3131 arrivalTime = bookedFlight.arrivalTime
3132 plan["steh"] = str(arrivalTime.hour)
3133 plan["stem"] = str(arrivalTime.minute)
3134
3135 plan["manualzfw"] = str(wizard.zfw / 1000.0)
3136 plan["cargo"] = str((wizard.bagWeight + wizard.cargoWeight + wizard.mailWeight)/1000.0)
3137
3138 plan["route"] = wizard.route
3139 plan["fl"] = str(wizard.filedCruiseAltitude)
3140 plan["altn"] = wizard.alternate
3141
3142 plan["addedfuel"] = str(self._extraFuel.get_int() / 1000.0)
3143 plan["origrwy"] = self._takeoffRunway.get_text()
3144 plan["destrwy"] = self._landingRunway.get_text()
3145
3146 for (key, control) in [("climb", self._climbProfile),
3147 ("cruise", self._cruiseProfile),
3148 ("descent", self._descentProfile)]:
3149 model = control.get_model()
3150 active = control.get_active_iter()
3151 value = model.get_value(active, 0)
3152 plan[key] = value
3153
3154 return plan
3155
3156 def _upperChanged(self, entry, arg = None):
3157 """Called when the value of some entry widget has changed and the value
3158 should be converted to uppercase."""
3159 entry.set_text(entry.get_text().upper())
3160
3161#-----------------------------------------------------------------------------
3162
3163class SimBriefingPage(Page):
3164 """Page to display the SimBrief HTML briefing."""
3165 class BrowserLifeSpanHandler(object):
3166 """The life-span handler of a browser."""
3167 def __init__(self, simBriefingPage):
3168 """Construct the life-span handler for the given page."""
3169 self._simBriefingPage = simBriefingPage
3170
3171 def OnBeforeClose(self, browser):
3172 """Called before closing the browser."""
3173 self._simBriefingPage._invalidateBrowser()
3174
3175 def __init__(self, wizard):
3176 """Construct the setup page."""
3177
3178 super(SimBriefingPage, self).__init__(wizard, "simbrief_result",
3179 xstr("simbrief_result_title"), "")
3180
3181 self._alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3182 xscale = 1.0, yscale = 1.0)
3183
3184 self._container = cef.getContainer()
3185 self._alignment.add(self._container)
3186
3187 self.setMainWidget(self._alignment)
3188
3189 self._browser = None
3190
3191 self.addCancelFlightButton()
3192
3193 self.addPreviousButton(clicked = self._backClicked)
3194
3195 self._button = self.addNextButton(clicked = self._forwardClicked)
3196 self._button.set_label(xstr("briefing_button"))
3197 self._button.set_has_tooltip(False)
3198 self._button.set_use_stock(False)
3199
3200 def activate(self):
3201 """Activate the SimBrief flight plan page"""
3202 if self._browser is None:
3203 self._startBrowser()
3204 else:
3205 self._browser.Reload()
3206
3207 def grabDefault(self):
3208 """If the page has a default button, make it the default one."""
3209 super(SimBriefingPage, self).grabDefault()
3210
3211 if self._browser is None:
3212 self._startBrowser()
3213
3214 def _backClicked(self, button):
3215 """Called when the Back button has been pressed."""
3216 self.goBack()
3217
3218 def _forwardClicked(self, button):
3219 """Called when the Forward button has been pressed."""
3220 if not self._completed:
3221 self._button.set_label(xstr("button_next"))
3222 self._button.set_tooltip_text(xstr("button_next_tooltip"))
3223 self._wizard.usingSimBrief = True
3224 self.complete()
3225
3226 self._wizard.nextPage()
3227
3228 def _startBrowser(self):
3229 """Start the browser.
3230
3231 If a container is needed, create one."""
3232 if self._container is None:
3233 self._container = cef.getContainer()
3234 self._alignment.add(self._container)
3235
3236 url = "file://" + SimBriefSetupPage.getHTMLFilePath()
3237 self._browser = cef.startInContainer(self._container, url)
3238
3239 lifeSpanHandler = SimBriefingPage.BrowserLifeSpanHandler(self)
3240 self._browser.SetClientHandler(lifeSpanHandler)
3241
3242 def _invalidateBrowser(self):
3243 """Invalidate the browser (and associated stuff)."""
3244 self._alignment.remove(self._container)
3245 self._container = None
3246 self._browser = None
3247
3248#-----------------------------------------------------------------------------
3249
3250class FuelTank(gtk.VBox):
3251 """Widget for the fuel tank."""
3252 def __init__(self, fuelTank, name, capacity, currentWeight):
3253 """Construct the widget for the tank with the given name."""
3254 super(FuelTank, self).__init__()
3255
3256 self._enabled = True
3257 self.fuelTank = fuelTank
3258 self.capacity = capacity
3259 self.currentWeight = currentWeight
3260 self.expectedWeight = currentWeight
3261
3262 self._label = label = gtk.Label("<b>" + name + "</b>")
3263 label.set_use_markup(True)
3264 label.set_use_underline(True)
3265 label.set_justify(JUSTIFY_CENTER)
3266 label.set_alignment(0.5, 1.0)
3267
3268 self._tankFigure = gtk.EventBox()
3269 self._tankFigure.set_size_request(38, 200)
3270 self._tankFigure.set_visible_window(False)
3271 self._tankFigure.set_tooltip_markup(xstr("fuel_tank_tooltip"))
3272
3273 if pygobject:
3274 self._tankFigure.connect("draw", self._drawTankFigure)
3275 else:
3276 self._tankFigure.connect("expose_event", self._drawTankFigure)
3277 self._tankFigure.connect("button_press_event", self._buttonPressed)
3278 self._tankFigure.connect("motion_notify_event", self._motionNotify)
3279 self._tankFigure.connect("scroll-event", self._scrolled)
3280
3281 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3282 xscale = 0.0, yscale = 1.0)
3283 alignment.add(self._tankFigure)
3284
3285 self.pack_start(alignment, True, True, 4)
3286
3287 self._expectedButton = gtk.SpinButton()
3288 self._expectedButton.set_numeric(True)
3289 self._expectedButton.set_range(0, self.capacity)
3290 self._expectedButton.set_increments(10, 100)
3291 self._expectedButton.set_value(currentWeight)
3292 self._expectedButton.set_alignment(1.0)
3293 self._expectedButton.set_width_chars(5)
3294 self._expectedButton.connect("value-changed", self._expectedChanged)
3295
3296 label.set_mnemonic_widget(self._expectedButton)
3297
3298 @property
3299 def label(self):
3300 """Get the label with the caption."""
3301 return self._label
3302
3303 @property
3304 def expectedButton(self):
3305 """Get the button containing the expected value."""
3306 return self._expectedButton
3307
3308 def setCurrent(self, currentWeight):
3309 """Set the current weight."""
3310 self.currentWeight = currentWeight
3311 self._redraw()
3312
3313 def isCorrect(self):
3314 """Determine if the contents of the fuel tank are as expected"""
3315 return abs(self.expectedWeight - self.currentWeight)<=1
3316
3317 def disable(self):
3318 """Disable the fuel tank."""
3319 self._expectedButton.set_sensitive(False)
3320 self._enabled = False
3321
3322 def _redraw(self):
3323 """Redraw the tank figure."""
3324 self._tankFigure.queue_draw()
3325
3326 def _drawTankFigure(self, tankFigure, eventOrContext):
3327 """Draw the tank figure."""
3328 triangleSize = 5
3329
3330 context = eventOrContext if pygobject else tankFigure.window.cairo_create()
3331 (xOffset, yOffset) = (0, 0) if pygobject \
3332 else (tankFigure.allocation.x, tankFigure.allocation.y)
3333
3334 width = tankFigure.get_allocated_width() if pygobject \
3335 else tankFigure.allocation.width
3336 height = tankFigure.get_allocated_height() if pygobject \
3337 else tankFigure.allocation.height
3338
3339 rectangleX0 = triangleSize
3340 rectangleY0 = triangleSize
3341 rectangleX1 = width - 1 - triangleSize
3342 rectangleY1 = height - 1 - triangleSize
3343 rectangleLineWidth = 2.0
3344
3345 context.set_source_rgb(0.0, 0.0, 0.0)
3346 context.set_line_width(rectangleLineWidth)
3347 context.rectangle(xOffset + rectangleX0 + rectangleLineWidth/2,
3348 yOffset + rectangleY0 + rectangleLineWidth/2,
3349 rectangleX1 - rectangleX0 - rectangleLineWidth,
3350 rectangleY1 - rectangleY0 - rectangleLineWidth)
3351 context.stroke()
3352
3353 rectangleInnerLeft = rectangleX0 + rectangleLineWidth
3354 rectangleInnerRight = rectangleX1 - rectangleLineWidth
3355 self._rectangleInnerTop = rectangleInnerTop = rectangleY0 + rectangleLineWidth
3356 self._rectangleInnerBottom = rectangleInnerBottom = rectangleY1 - rectangleLineWidth
3357
3358 rectangleInnerWidth = rectangleInnerRight - rectangleInnerLeft
3359 rectangleInnerHeight = rectangleInnerBottom - rectangleInnerTop
3360
3361 context.set_source_rgb(1.0, 0.9, 0.6)
3362 currentHeight = self.currentWeight * rectangleInnerHeight / self.capacity
3363 currentX = rectangleInnerTop + rectangleInnerHeight - currentHeight
3364 context.rectangle(xOffset + rectangleInnerLeft,
3365 yOffset + rectangleInnerTop +
3366 rectangleInnerHeight - currentHeight,
3367 rectangleInnerWidth, currentHeight)
3368 context.fill()
3369
3370 expectedHeight = self.expectedWeight * rectangleInnerHeight / self.capacity
3371 expectedY = rectangleInnerTop + rectangleInnerHeight - expectedHeight
3372
3373 context.set_line_width(1.5)
3374 context.set_source_rgb(0.0, 0.85, 0.85)
3375 context.move_to(xOffset + rectangleX0, yOffset + expectedY)
3376 context.line_to(xOffset + rectangleX1, yOffset + expectedY)
3377 context.stroke()
3378
3379 context.set_line_width(0.0)
3380 context.move_to(xOffset + 0, yOffset + expectedY - triangleSize)
3381 context.line_to(xOffset + 0, yOffset + expectedY + triangleSize)
3382 context.line_to(xOffset + rectangleX0 + 1, yOffset + expectedY)
3383 context.line_to(xOffset + 0, yOffset + expectedY - triangleSize)
3384 context.fill()
3385
3386 context.set_line_width(0.0)
3387 context.move_to(xOffset + width, yOffset + expectedY - triangleSize)
3388 context.line_to(xOffset + width, yOffset + expectedY + triangleSize)
3389 context.line_to(xOffset + rectangleX1 - 1, yOffset + expectedY)
3390 context.line_to(xOffset + width, yOffset + expectedY - triangleSize)
3391 context.fill()
3392
3393 return True
3394
3395 def _setExpectedFromY(self, y):
3396 """Set the expected weight from the given Y-coordinate."""
3397 level = (self._rectangleInnerBottom - y) / \
3398 (self._rectangleInnerBottom - self._rectangleInnerTop)
3399 level = min(1.0, max(0.0, level))
3400 self._expectedButton.set_value(level * self.capacity)
3401
3402 def _buttonPressed(self, tankFigure, event):
3403 """Called when a button is pressed in the figure.
3404
3405 The expected level will be set there."""
3406 if self._enabled and event.button==1:
3407 self._setExpectedFromY(event.y)
3408
3409 def _motionNotify(self, tankFigure, event):
3410 """Called when the mouse pointer moves within the area of a tank figure."""
3411 if self._enabled and event.state==BUTTON1_MASK:
3412 self._setExpectedFromY(event.y)
3413
3414 def _scrolled(self, tankFigure, event):
3415 """Called when a scroll event is received."""
3416 if self._enabled:
3417 increment = 1 if event.state==CONTROL_MASK \
3418 else 100 if event.state==SHIFT_MASK \
3419 else 10 if event.state==0 else 0
3420 if increment!=0:
3421 if event.direction==SCROLL_DOWN:
3422 increment *= -1
3423 self._expectedButton.spin(SPIN_USER_DEFINED, increment)
3424
3425 def _expectedChanged(self, spinButton):
3426 """Called when the expected value has changed."""
3427 self.expectedWeight = spinButton.get_value_as_int()
3428 self._redraw()
3429
3430#-----------------------------------------------------------------------------
3431
3432class FuelPage(Page):
3433 """The page containing the fuel tank filling."""
3434 _pumpStep = 0.02
3435
3436 def __init__(self, wizard):
3437 """Construct the page."""
3438 super(FuelPage, self).__init__(wizard, "fuel",
3439 xstr("fuel_title"),
3440 xstr("fuel_help_pre") +
3441 xstr("fuel_help_post"),
3442 completedHelp = xstr("fuel_chelp"))
3443
3444 self._fuelTanks = []
3445 self._fuelTable = None
3446 self._fuelAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3447 xscale = 0.0, yscale = 1.0)
3448 self.setMainWidget(self._fuelAlignment)
3449
3450 tankData = [(tank, 2500, 3900) for tank in acft.mostFuelTanks]
3451 self._setupTanks(tankData)
3452
3453 self.addCancelFlightButton()
3454
3455 self._backButton = self.addPreviousButton(clicked = self._backClicked)
3456 self._button = self.addNextButton(clicked = self._forwardClicked)
3457
3458 self._pumpIndex = 0
3459
3460 def activate(self):
3461 """Activate the page."""
3462 self._setupTanks(self._wizard._fuelData)
3463
3464 aircraft = self._wizard.gui.flight.aircraft
3465 minLandingFuel = aircraft.minLandingFuel
3466 recommendedLandingFuel = aircraft.recommendedLandingFuel
3467
3468 middleHelp = "" if minLandingFuel is None else \
3469 (xstr("fuel_help_min") % (minLandingFuel,)) \
3470 if recommendedLandingFuel is None else \
3471 (xstr("fuel_help_min_rec") % (minLandingFuel,
3472 recommendedLandingFuel))
3473 self.setHelp(xstr("fuel_help_pre") + middleHelp + xstr("fuel_help_post"))
3474
3475 def finalize(self):
3476 """Finalize the page."""
3477 for fuelTank in self._fuelTanks:
3478 fuelTank.disable()
3479
3480 def _backClicked(self, button):
3481 """Called when the Back button is pressed."""
3482 self.goBack()
3483
3484 def _forwardClicked(self, button):
3485 """Called when the forward button is clicked."""
3486 if not self._completed:
3487 self._pumpIndex = 0
3488 self._wizard.gui.beginBusy(xstr("fuel_pump_busy"))
3489 self._pump()
3490 elif self._wizard.usingSimBrief:
3491 self._wizard.jumpPage("takeoff")
3492 else:
3493 self._wizard.jumpPage("briefing1")
3494
3495 def _setupTanks(self, tankData):
3496 """Setup the tanks for the given data."""
3497 numTanks = len(tankData)
3498 if self._fuelTable is not None:
3499 self._fuelAlignment.remove(self._fuelTable)
3500
3501 self._fuelTanks = []
3502 self._fuelTable = gtk.Grid()
3503 self._fuelTable.set_column_homogeneous(True)
3504 self._fuelTable.set_row_spacing(4)
3505 index = 0
3506 for (tank, current, capacity) in tankData:
3507 fuelTank = FuelTank(tank,
3508 xstr("fuel_tank_" +
3509 const.fuelTank2string(tank)),
3510 capacity, current)
3511 self._fuelTanks.append(fuelTank)
3512
3513 alignment = gtk.Alignment(xalign = 0.5, yalign = 1.0,
3514 xscale = 1.0, yscale = 0.0)
3515 alignment.add(fuelTank.label)
3516 self._fuelTable.attach(alignment, index*2, 0, 3, 1)
3517
3518 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3519 xscale = 0.0, yscale = 1.0)
3520 alignment.add(fuelTank)
3521 self._fuelTable.attach(alignment, index*2+1, 1, 1, 1)
3522
3523
3524 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3525 xscale = 1.0, yscale = 0.0)
3526 alignment.add(fuelTank.expectedButton)
3527
3528 self._fuelTable.attach(alignment, index*2,
3529 2 if (index%2)==0 or numTanks==2 else 3,
3530 3, 1)
3531
3532 index += 1
3533
3534 self._fuelAlignment.add(self._fuelTable)
3535 self.show_all()
3536
3537 def _pump(self):
3538 """Perform one step of pumping.
3539
3540 It is checked, if the current tank's contents are of the right
3541 quantity. If not, it is filled one step further to the desired
3542 contents. Otherwise the next tank is started. If all tanks are are
3543 filled, the next page is selected."""
3544 numTanks = len(self._fuelTanks)
3545
3546 fuelTank = None
3547 while self._pumpIndex < numTanks:
3548 fuelTank = self._fuelTanks[self._pumpIndex]
3549 if fuelTank.isCorrect():
3550 self._pumpIndex += 1
3551 fuelTank = None
3552 else:
3553 break
3554
3555 if fuelTank is None:
3556 self._wizard.gui.endBusy()
3557 if self._wizard.usingSimBrief:
3558 self._wizard.gui.startMonitoring()
3559 self._wizard.jumpPage("takeoff")
3560 else:
3561 bookedFlight = self._wizard._bookedFlight
3562 self._wizard.gui.beginBusy(xstr("route_down_notams"))
3563 self._wizard.gui.webHandler.getNOTAMs(self._notamsCallback,
3564 bookedFlight.departureICAO,
3565 bookedFlight.arrivalICAO)
3566 startSound(const.SOUND_NOTAM)
3567 else:
3568 currentLevel = fuelTank.currentWeight / fuelTank.capacity
3569 expectedLevel = fuelTank.expectedWeight / fuelTank.capacity
3570 if currentLevel<expectedLevel:
3571 currentLevel += FuelPage._pumpStep
3572 if currentLevel>expectedLevel: currentLevel = expectedLevel
3573 else:
3574 currentLevel -= FuelPage._pumpStep
3575 if currentLevel<expectedLevel: currentLevel = expectedLevel
3576 fuelTank.setCurrent(currentLevel * fuelTank.capacity)
3577 self._wizard.gui.simulator.setFuelLevel([(fuelTank.fuelTank,
3578 currentLevel)])
3579 gobject.timeout_add(50, self._pump)
3580
3581 def _notamsCallback(self, returned, result):
3582 """Callback for the NOTAMs."""
3583 gobject.idle_add(self._handleNOTAMs, returned, result)
3584
3585 def _handleNOTAMs(self, returned, result):
3586 """Handle the NOTAMs."""
3587 if returned:
3588 self._wizard._departureNOTAMs = result.departureNOTAMs
3589 self._wizard._arrivalNOTAMs = result.arrivalNOTAMs
3590 else:
3591 self._wizard._departureNOTAMs = None
3592 self._wizard._arrivalNOTAMs = None
3593
3594 bookedFlight = self._wizard._bookedFlight
3595 self._wizard.gui.beginBusy(xstr("route_down_metars"))
3596 self._wizard.gui.webHandler.getMETARs(self._metarsCallback,
3597 [bookedFlight.departureICAO,
3598 bookedFlight.arrivalICAO])
3599
3600 def _metarsCallback(self, returned, result):
3601 """Callback for the METARs."""
3602 gobject.idle_add(self._handleMETARs, returned, result)
3603
3604 def _handleMETARs(self, returned, result):
3605 """Handle the METARs."""
3606 self._wizard._departureMETAR = None
3607 self._wizard._arrivalMETAR = None
3608 bookedFlight = self._wizard._bookedFlight
3609 if returned:
3610 if bookedFlight.departureICAO in result.metars:
3611 self._wizard._departureMETAR = result.metars[bookedFlight.departureICAO]
3612 if bookedFlight.arrivalICAO in result.metars:
3613 self._wizard._arrivalMETAR = result.metars[bookedFlight.arrivalICAO]
3614
3615 self._wizard.gui.endBusy()
3616 self._backButton.set_sensitive(True)
3617 self._button.set_sensitive(True)
3618 self._wizard.nextPage()
3619
3620#-----------------------------------------------------------------------------
3621
3622class BriefingPage(Page):
3623 """Page for the briefing."""
3624 def __init__(self, wizard, departure):
3625 """Construct the briefing page."""
3626 self._departure = departure
3627
3628 number = 1 if departure else 2
3629
3630 title = xstr("briefing_title") % (number,
3631 xstr("briefing_departure")
3632 if departure
3633 else xstr("briefing_arrival"))
3634 super(BriefingPage, self).__init__(wizard,
3635 "briefing%d" % (number,),
3636 title, xstr("briefing_help"),
3637 completedHelp = xstr("briefing_chelp"))
3638
3639 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3640 xscale = 1.0, yscale = 1.0)
3641
3642 mainBox = gtk.VBox()
3643 alignment.add(mainBox)
3644 self.setMainWidget(alignment)
3645
3646 self._notamsFrame = gtk.Frame()
3647 self._notamsFrame.set_label(xstr("briefing_notams_init"))
3648 scrolledWindow = gtk.ScrolledWindow()
3649 scrolledWindow.set_size_request(-1, 128)
3650 # FIXME: these constants should be in common
3651 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
3652 else gtk.POLICY_AUTOMATIC,
3653 gtk.PolicyType.AUTOMATIC if pygobject
3654 else gtk.POLICY_AUTOMATIC)
3655 self._notams = gtk.TextView()
3656 self._notams.set_editable(False)
3657 self._notams.set_accepts_tab(False)
3658 self._notams.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
3659 scrolledWindow.add(self._notams)
3660 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
3661 xscale = 1.0, yscale = 1.0)
3662 alignment.set_padding(padding_top = 4, padding_bottom = 0,
3663 padding_left = 0, padding_right = 0)
3664 alignment.add(scrolledWindow)
3665 self._notamsFrame.add(alignment)
3666 mainBox.pack_start(self._notamsFrame, True, True, 4)
3667
3668 self._metarFrame = gtk.Frame()
3669 self._metarFrame.set_label(xstr("briefing_metar_init"))
3670 scrolledWindow = gtk.ScrolledWindow()
3671 scrolledWindow.set_size_request(-1, 32)
3672 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
3673 else gtk.POLICY_AUTOMATIC,
3674 gtk.PolicyType.AUTOMATIC if pygobject
3675 else gtk.POLICY_AUTOMATIC)
3676
3677 self._updatingMETAR = False
3678
3679 self._metar = gtk.TextView()
3680 self._metar.set_accepts_tab(False)
3681 self._metar.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
3682 self._metar.get_buffer().connect("changed", self._metarChanged)
3683 self._metar.get_buffer().connect_after("insert-text", self._metarInserted)
3684 scrolledWindow.add(self._metar)
3685 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
3686 xscale = 1.0, yscale = 1.0)
3687 alignment.set_padding(padding_top = 4, padding_bottom = 0,
3688 padding_left = 0, padding_right = 0)
3689 alignment.add(scrolledWindow)
3690 self._metarFrame.add(alignment)
3691 mainBox.pack_start(self._metarFrame, True, True, 4)
3692 self.metarEdited = False
3693
3694 self.addCancelFlightButton()
3695
3696 self.addPreviousButton(clicked = self._backClicked)
3697 self._button = self.addNextButton(clicked = self._forwardClicked)
3698
3699 @property
3700 def metar(self):
3701 """Get the METAR on the page."""
3702 buffer = self._metar.get_buffer()
3703 return buffer.get_text(buffer.get_start_iter(),
3704 buffer.get_end_iter(), True)
3705
3706 def setMETAR(self, metar):
3707 """Set the METAR."""
3708 self._metar.get_buffer().set_text(metar)
3709 self.metarEdited = False
3710
3711 def changeMETAR(self, metar):
3712 """Change the METAR as a result of an edit on one of the other
3713 pages."""
3714 self._updatingMETAR = True
3715 self._metar.get_buffer().set_text(metar)
3716 self._updatingMETAR = False
3717
3718 self._updateButton()
3719 self.metarEdited = True
3720
3721 def activate(self):
3722 """Activate the page."""
3723 if not self._departure:
3724 self._button.set_label(xstr("briefing_button"))
3725 self._button.set_has_tooltip(False)
3726 self._button.set_use_stock(False)
3727
3728 bookedFlight = self._wizard._bookedFlight
3729
3730 icao = bookedFlight.departureICAO if self._departure \
3731 else bookedFlight.arrivalICAO
3732 notams = self._wizard._departureNOTAMs if self._departure \
3733 else self._wizard._arrivalNOTAMs
3734 metar = self._wizard._departureMETAR if self._departure \
3735 else self._wizard._arrivalMETAR
3736
3737 self._notamsFrame.set_label(xstr("briefing_notams_template") % (icao,))
3738 buffer = self._notams.get_buffer()
3739 if notams is None:
3740 buffer.set_text(xstr("briefing_notams_failed"))
3741 elif not notams:
3742 buffer.set_text(xstr("briefing_notams_missing"))
3743 else:
3744 s = ""
3745 for notam in notams:
3746 s += str(notam)
3747 s += "-------------------- * --------------------\n"
3748 buffer.set_text(s)
3749
3750 self._metarFrame.set_label(xstr("briefing_metar_template") % (icao,))
3751 buffer = self._metar.get_buffer()
3752 self._updatingMETAR = True
3753 if metar is None:
3754 buffer.set_text("")
3755 self.setHelp(xstr("briefing_help_nometar"))
3756 else:
3757 buffer.set_text(metar)
3758 self._updatingMETAR = False
3759 self._updateButton()
3760
3761 label = self._metarFrame.get_label_widget()
3762 label.set_use_underline(True)
3763 label.set_mnemonic_widget(self._metar)
3764
3765 self.metarEdited = False
3766
3767 def _backClicked(self, button):
3768 """Called when the Back button is pressed."""
3769 self.goBack()
3770
3771 def _forwardClicked(self, button):
3772 """Called when the forward button is clicked."""
3773 if not self._departure:
3774 if not self._completed:
3775 self._wizard.gui.startMonitoring()
3776 self._button.set_label(xstr("button_next"))
3777 self._button.set_tooltip_text(xstr("button_next_tooltip"))
3778 self.complete()
3779
3780 self._wizard.nextPage()
3781
3782 def _metarChanged(self, buffer):
3783 """Called when the METAR has changed."""
3784 print("BriefingPage.metarChanged", self._updatingMETAR)
3785 if not self._updatingMETAR:
3786 self.metarEdited = True
3787 self._updateButton()
3788 metar = buffer.get_text(buffer.get_start_iter(),
3789 buffer.get_end_iter(), True)
3790 self._wizard.metarChanged(metar, self)
3791
3792 def _metarInserted(self, textBuffer, iter, text, length):
3793 """Called when new characters are inserted into the METAR.
3794
3795 It uppercases all characters."""
3796 print("BriefingPage.metarInserted", self._updatingMETAR)
3797 if not self._updatingMETAR:
3798 self._updatingMETAR = True
3799
3800 iter1 = iter.copy()
3801 iter1.backward_chars(length)
3802 textBuffer.delete(iter, iter1)
3803
3804 textBuffer.insert(iter, text.upper())
3805
3806 self._updatingMETAR = False
3807
3808 def _updateButton(self):
3809 """Update the sensitivity of the Next button based on the contents of
3810 the METAR field."""
3811 buffer = self._metar.get_buffer()
3812 self._button.set_sensitive(buffer.get_text(buffer.get_start_iter(),
3813 buffer.get_end_iter(),
3814 True)!="")
3815
3816
3817#-----------------------------------------------------------------------------
3818
3819class TakeoffPage(Page):
3820 """Page for entering the takeoff data."""
3821 def __init__(self, wizard):
3822 """Construct the takeoff page."""
3823 super(TakeoffPage, self).__init__(wizard, "takeoff",
3824 xstr("takeoff_title"),
3825 xstr("takeoff_help"),
3826 completedHelp = xstr("takeoff_chelp"))
3827
3828 self._forwardAllowed = False
3829
3830 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3831 xscale = 0.0, yscale = 0.0)
3832
3833 table = gtk.Table(9, 24)
3834 table.set_row_spacings(4)
3835 table.set_col_spacings(16)
3836 table.set_homogeneous(False)
3837 alignment.add(table)
3838 self.setMainWidget(alignment)
3839
3840 row = 0
3841
3842 label = gtk.Label(xstr("takeoff_metar"))
3843 label.set_use_underline(True)
3844 label.set_alignment(0.0, 0.5)
3845 table.attach(label, 0, 1, row, row+1)
3846
3847 self._metar = gtk.Entry()
3848 self._metar.set_width_chars(40)
3849 self._metar.set_tooltip_text(xstr("takeoff_metar_tooltip"))
3850 self._metar.connect("changed", self._metarChanged)
3851 self._metar.get_buffer().connect_after("inserted-text", self._metarInserted)
3852 table.attach(self._metar, 1, 24, row, row+1)
3853 label.set_mnemonic_widget(self._metar)
3854
3855 self._updatingMETAR = False
3856
3857 row += 1
3858
3859 label = gtk.Label(xstr("takeoff_runway"))
3860 label.set_use_underline(True)
3861 label.set_alignment(0.0, 0.5)
3862 table.attach(label, 0, 1, row, row+1)
3863
3864 self._runway = gtk.Entry()
3865 self._runway.set_width_chars(10)
3866 self._runway.set_tooltip_text(xstr("takeoff_runway_tooltip"))
3867 self._runway.connect("changed", self._upperChanged)
3868 table.attach(self._runway, 1, 3, row, row+1)
3869 label.set_mnemonic_widget(self._runway)
3870
3871 row += 1
3872
3873 label = gtk.Label(xstr("takeoff_sid"))
3874 label.set_use_underline(True)
3875 label.set_alignment(0.0, 0.5)
3876 table.attach(label, 0, 1, row, row+1)
3877
3878 if pygobject:
3879 self._sid = gtk.ComboBox.new_with_model_and_entry(comboModel)
3880 else:
3881 self._sid = gtk.ComboBoxEntry(comboModel)
3882
3883 self._sid.set_entry_text_column(0)
3884 self._sid.get_child().set_width_chars(10)
3885 self._sid.set_tooltip_text(xstr("takeoff_sid_tooltip"))
3886 self._sid.connect("changed", self._upperChangedComboBox)
3887 table.attach(self._sid, 1, 3, row, row+1)
3888 label.set_mnemonic_widget(self._sid)
3889
3890 row += 1
3891
3892 label = gtk.Label(xstr("takeoff_v1"))
3893 label.set_use_markup(True)
3894 label.set_use_underline(True)
3895 label.set_alignment(0.0, 0.5)
3896 table.attach(label, 0, 1, row, row+1)
3897
3898 self._v1 = IntegerEntry()
3899 self._v1.set_width_chars(4)
3900 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip_knots"))
3901 self._v1.connect("integer-changed", self._valueChanged)
3902 table.attach(self._v1, 2, 3, row, row+1)
3903 label.set_mnemonic_widget(self._v1)
3904
3905 self._v1Unit = gtk.Label(xstr("label_knots"))
3906 self._v1Unit.set_alignment(0.0, 0.5)
3907 table.attach(self._v1Unit, 3, 4, row, row+1)
3908
3909 row += 1
3910
3911 label = gtk.Label(xstr("takeoff_vr"))
3912 label.set_use_markup(True)
3913 label.set_use_underline(True)
3914 label.set_alignment(0.0, 0.5)
3915 table.attach(label, 0, 1, row, row+1)
3916
3917 self._vr = IntegerEntry()
3918 self._vr.set_width_chars(4)
3919 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip_knots"))
3920 self._vr.connect("integer-changed", self._valueChanged)
3921 table.attach(self._vr, 2, 3, row, row+1)
3922 label.set_mnemonic_widget(self._vr)
3923
3924 self._vrUnit = gtk.Label(xstr("label_knots"))
3925 self._vrUnit.set_alignment(0.0, 0.5)
3926 table.attach(self._vrUnit, 3, 4, row, row+1)
3927
3928 row += 1
3929
3930 label = gtk.Label(xstr("takeoff_v2"))
3931 label.set_use_markup(True)
3932 label.set_use_underline(True)
3933 label.set_alignment(0.0, 0.5)
3934 table.attach(label, 0, 1, row, row+1)
3935
3936 self._v2 = IntegerEntry()
3937 self._v2.set_width_chars(4)
3938 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip_knots"))
3939 self._v2.connect("integer-changed", self._valueChanged)
3940 table.attach(self._v2, 2, 3, row, row+1)
3941 label.set_mnemonic_widget(self._v2)
3942
3943 self._v2Unit = gtk.Label(xstr("label_knots"))
3944 self._v2Unit.set_alignment(0.0, 0.5)
3945 table.attach(self._v2Unit, 3, 4, row, row+1)
3946
3947 row += 1
3948
3949 self._derateType = acft.DERATE_NONE
3950
3951 self._derateLabel = gtk.Label()
3952 self._derateLabel.set_use_underline(True)
3953 self._derateLabel.set_markup(xstr("takeoff_derate_tupolev"))
3954 self._derateLabel.set_alignment(0.0, 0.5)
3955 table.attach(self._derateLabel, 0, 1, row, row+1)
3956
3957 self._derate = gtk.Alignment()
3958 table.attach(self._derate, 2, 4, row, row+1)
3959 self._derateWidget = None
3960 self._derateEntry = None
3961 self._derateUnit = None
3962 self._derateButtons = None
3963
3964 row += 1
3965
3966 self._antiIceOn = gtk.CheckButton(xstr("takeoff_antiice"))
3967 self._antiIceOn.set_use_underline(True)
3968 self._antiIceOn.set_tooltip_text(xstr("takeoff_antiice_tooltip"))
3969 table.attach(self._antiIceOn, 2, 4, row, row+1)
3970
3971 row += 1
3972
3973 self._rto = gtk.CheckButton(xstr("takeoff_rto"))
3974 self._rto.set_use_underline(True)
3975 self._rto.set_tooltip_text(xstr("takeoff_rto_tooltip"))
3976 self._rto.connect("toggled", self._rtoToggled)
3977 table.attach(self._rto, 2, 4, row, row+1, ypadding = 8)
3978
3979 self.addCancelFlightButton()
3980
3981 self.addPreviousButton(clicked = self._backClicked)
3982
3983 self._button = self.addNextButton(clicked = self._forwardClicked)
3984
3985 self._active = False
3986
3987 @property
3988 def runway(self):
3989 """Get the runway."""
3990 return self._runway.get_text()
3991
3992 @property
3993 def sid(self):
3994 """Get the SID."""
3995 text = self._sid.get_child().get_text()
3996 return text if self._sid.get_active()!=0 and text and text!="N/A" \
3997 else None
3998
3999 @property
4000 def v1(self):
4001 """Get the v1 speed."""
4002 return self._v1.get_int()
4003
4004 @property
4005 def vr(self):
4006 """Get the vr speed."""
4007 return self._vr.get_int()
4008
4009 @property
4010 def v2(self):
4011 """Get the v2 speed."""
4012 return self._v2.get_int()
4013
4014 @property
4015 def derate(self):
4016 """Get the derate value, if any."""
4017 if self._derateWidget is None:
4018 return None
4019 if self._derateType==acft.DERATE_BOEING:
4020 derate = self._derateEntry.get_text()
4021 return derate if derate else None
4022 elif self._derateType==acft.DERATE_EPR:
4023 derate = self._derateWidget.get_text()
4024 return derate if derate else None
4025 elif self._derateType==acft.DERATE_TUPOLEV:
4026 return acft.DERATE_TUPOLEV_NOMINAL \
4027 if self._derateButtons[0].get_active() \
4028 else acft.DERATE_TUPOLEV_TAKEOFF
4029 elif self._derateType==acft.DERATE_B462:
4030 return self._derateWidget.get_active()
4031 else:
4032 return None
4033
4034 @property
4035 def antiIceOn(self):
4036 """Get whether the anti-ice system has been turned on."""
4037 return self._antiIceOn.get_active()
4038
4039 @antiIceOn.setter
4040 def antiIceOn(self, value):
4041 """Set the anti-ice indicator."""
4042 self._antiIceOn.set_active(value)
4043
4044 @property
4045 def rtoIndicated(self):
4046 """Get whether the pilot has indicated if there was an RTO."""
4047 return self._rto.get_active()
4048
4049 def activate(self):
4050 """Activate the page."""
4051 print("TakeoffPage.activate")
4052
4053 self._updatingMETAR = True
4054 self._metar.get_buffer().set_text(self._wizard.departureMETAR, -1)
4055 self._updatingMETAR = False
4056
4057 if self._wizard.takeoffRunway is None:
4058 self._runway.set_text("")
4059 else:
4060 self._runway.set_text(self._wizard.takeoffRunway)
4061 self._runway.set_sensitive(True)
4062 self._sid.set_active(0)
4063 self._sid.set_sensitive(True)
4064 self._v1.set_int(None)
4065 self._v1.set_sensitive(True)
4066 self._vr.set_int(None)
4067 self._vr.set_sensitive(True)
4068 self._v2.set_int(None)
4069 self._v2.set_sensitive(True)
4070
4071 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
4072 speedUnit = xstr("label" + i18nSpeedUnit)
4073 self._v1Unit.set_text(speedUnit)
4074 self._vrUnit.set_text(speedUnit)
4075 self._v2Unit.set_text(speedUnit)
4076
4077 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip" + i18nSpeedUnit))
4078 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip" + i18nSpeedUnit))
4079 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip" + i18nSpeedUnit))
4080
4081 self._derateType = self._wizard.gui.flight.aircraft.derateType
4082
4083 self._setupDerateWidget()
4084
4085 self._rto.set_active(False)
4086 self._rto.set_sensitive(False)
4087
4088 self._button.set_sensitive(False)
4089 self._forwardAllowed = False
4090
4091 self._active = True
4092
4093 def allowForward(self):
4094 """Allow going to the next page."""
4095 print("TakeoffPage.allowForward")
4096 self._forwardAllowed = True
4097 self._updateForwardButton()
4098
4099 def reset(self):
4100 """Reset the page if the wizard is reset."""
4101 print("TakeoffPage.reset")
4102
4103 super(TakeoffPage, self).reset()
4104 self._v1.reset()
4105 self._vr.reset()
4106 self._v2.reset()
4107 self._hasDerate = False
4108 self._antiIceOn.set_active(False)
4109 self._active = False
4110
4111 def setRTOEnabled(self, enabled):
4112 """Set the RTO checkbox enabled or disabled."""
4113 if not enabled:
4114 self._rto.set_active(False)
4115 self._rto.set_sensitive(enabled)
4116
4117 def changeMETAR(self, metar):
4118 """Change the METAR as a result of an edit on one of the other
4119 pages."""
4120 if self._active:
4121 print("TakeoffPage.changeMETAR")
4122 self._updatingMETAR = True
4123 self._metar.get_buffer().set_text(metar, -1)
4124 self._updatingMETAR = False
4125
4126 self._updateForwardButton()
4127
4128 def _updateForwardButton(self):
4129 """Update the sensitivity of the forward button based on some conditions."""
4130 sensitive = self._forwardAllowed and \
4131 self._metar.get_text()!="" and \
4132 self._runway.get_text()!="" and \
4133 self.sid is not None and \
4134 self.v1 is not None and \
4135 self.vr is not None and \
4136 self.v2 is not None and \
4137 self.v1 <= self.vr and \
4138 self.vr <= self.v2 and \
4139 (self._derateType==acft.DERATE_NONE or
4140 self.derate is not None)
4141
4142 print("TakeoffPage._updateForwardButton: forwardAllowed:", self._forwardAllowed, ", sensitive:", sensitive)
4143 if self._forwardAllowed:
4144 print(" METAR: ", self._metar.get_text())
4145 print(" runway: ", self._runway.get_text())
4146 print(" SID:", self.sid)
4147 print(" V1:", self.v1)
4148 print(" VR:", self.vr)
4149 print(" V2:", self.v2)
4150 print(" derateType:", self._derateType)
4151 print(" derate:", self.derate)
4152
4153 self._button.set_sensitive(sensitive)
4154
4155 def _valueChanged(self, widget, arg = None):
4156 """Called when the value of some widget has changed."""
4157 print("TakeoffPage._valueChanged")
4158
4159 self._updateForwardButton()
4160
4161 def _upperChanged(self, entry, arg = None):
4162 """Called when the value of some entry widget has changed and the value
4163 should be converted to uppercase."""
4164 print("TakeoffPage._upperChanged")
4165 entry.set_text(entry.get_text().upper())
4166 self._valueChanged(entry, arg)
4167
4168 def _upperChangedComboBox(self, comboBox):
4169 """Called for combo box widgets that must be converted to uppercase."""
4170 entry = comboBox.get_child()
4171 if comboBox.get_active()==-1:
4172 entry.set_text(entry.get_text().upper())
4173 self._valueChanged(entry)
4174
4175 def _derateChanged(self, entry):
4176 """Called when the value of the derate is changed."""
4177 print("TakeoffPage._derateChanged")
4178 self._updateForwardButton()
4179
4180 def _rtoToggled(self, button):
4181 """Called when the RTO check button is toggled."""
4182 self._wizard.rtoToggled(button.get_active())
4183
4184 def _backClicked(self, button):
4185 """Called when the Back button is pressed."""
4186 self.goBack()
4187
4188 def _forwardClicked(self, button):
4189 """Called when the forward button is clicked."""
4190 aircraft = self._wizard.gui.flight.aircraft
4191 aircraft.updateV1R2()
4192 if self.derate is not None:
4193 aircraft.updateDerate()
4194 aircraft.updateTakeoffAntiIce()
4195 self._wizard.nextPage()
4196
4197 def _setupDerateWidget(self):
4198 """Setup the derate widget."""
4199 if self._derateWidget is not None:
4200 self._derate.remove(self._derateWidget)
4201
4202 if self._derateType==acft.DERATE_BOEING:
4203 self._derateLabel.set_text(xstr("takeoff_derate_boeing"))
4204 self._derateLabel.set_use_underline(True)
4205 self._derateLabel.set_sensitive(True)
4206
4207 self._derateEntry = gtk.Entry()
4208 self._derateEntry.set_width_chars(7)
4209 self._derateEntry.set_tooltip_text(xstr("takeoff_derate_boeing_tooltip"))
4210 self._derateEntry.set_alignment(1.0)
4211 self._derateEntry.connect("changed", self._derateChanged)
4212 self._derateLabel.set_mnemonic_widget(self._derateEntry)
4213
4214 self._derateUnit = gtk.Label("%")
4215 self._derateUnit.set_alignment(0.0, 0.5)
4216
4217 self._derateWidget = gtk.Table(3, 1)
4218 self._derateWidget.set_row_spacings(4)
4219 self._derateWidget.set_col_spacings(16)
4220 self._derateWidget.set_homogeneous(False)
4221
4222 self._derateWidget.attach(self._derateEntry, 0, 2, 0, 1)
4223 self._derateWidget.attach(self._derateUnit, 2, 3, 0, 1)
4224
4225 self._derate.add(self._derateWidget)
4226 elif self._derateType==acft.DERATE_EPR:
4227 self._derateLabel.set_text("_EPR:")
4228 self._derateLabel.set_use_underline(True)
4229 self._derateLabel.set_sensitive(True)
4230
4231 self._derateWidget = gtk.Entry()
4232 self._derateWidget.set_width_chars(7)
4233 self._derateWidget.set_tooltip_text(xstr("takeoff_derate_epr_tooltip"))
4234 self._derateWidget.set_alignment(1.0)
4235 self._derateWidget.connect("changed", self._derateChanged)
4236 self._derateLabel.set_mnemonic_widget(self._derateWidget)
4237
4238 self._derate.add(self._derateWidget)
4239 elif self._derateType==acft.DERATE_TUPOLEV:
4240 self._derateLabel.set_text(xstr("takeoff_derate_tupolev"))
4241 self._derateLabel.set_use_underline(True)
4242 self._derateLabel.set_sensitive(True)
4243
4244 if pygobject:
4245 nominal = gtk.RadioButton.\
4246 new_with_label_from_widget(None,
4247 xstr("takeoff_derate_tupolev_nominal"))
4248 else:
4249 nominal = gtk.RadioButton(None,
4250 xstr("takeoff_derate_tupolev_nominal"))
4251 nominal.set_use_underline(True)
4252 nominal.set_tooltip_text(xstr("takeoff_derate_tupolev_nominal_tooltip"))
4253 nominal.connect("toggled", self._derateChanged)
4254
4255 if pygobject:
4256 takeoff = gtk.RadioButton.\
4257 new_with_label_from_widget(nominal,
4258 xstr("takeoff_derate_tupolev_takeoff"))
4259 else:
4260 takeoff = gtk.RadioButton(nominal,
4261 xstr("takeoff_derate_tupolev_takeoff"))
4262
4263 takeoff.set_use_underline(True)
4264 takeoff.set_tooltip_text(xstr("takeoff_derate_tupolev_takeoff_tooltip"))
4265 takeoff.connect("toggled", self._derateChanged)
4266
4267 self._derateButtons = [nominal, takeoff]
4268
4269 self._derateWidget = gtk.HBox()
4270 self._derateWidget.pack_start(nominal, False, False, 4)
4271 self._derateWidget.pack_start(takeoff, False, False, 4)
4272
4273 self._derate.add(self._derateWidget)
4274 elif self._derateType==acft.DERATE_B462:
4275 self._derateLabel.set_text("")
4276
4277 self._derateWidget = gtk.CheckButton(xstr("takeoff_derate_b462"))
4278 self._derateWidget.set_tooltip_text(xstr("takeoff_derate_b462_tooltip"))
4279 self._derateWidget.set_use_underline(True)
4280 self._derate.add(self._derateWidget)
4281 else:
4282 self._derateWidget = None
4283 self._derateLabel.set_text("")
4284 self._derateLabel.set_sensitive(False)
4285
4286 def _metarChanged(self, entry):
4287 """Called when the METAR has changed."""
4288 print("TakeoffPage.metarChanged", self._updatingMETAR)
4289 if not self._updatingMETAR:
4290 self._updateForwardButton()
4291 self._wizard.metarChanged(entry.get_text(), self)
4292
4293 def _metarInserted(self, buffer, position, text, length):
4294 """Called when new characters are inserted into the METAR.
4295
4296 It uppercases all characters."""
4297 print("TakeoffPage.metarInserted", self._updatingMETAR)
4298 if not self._updatingMETAR:
4299 self._updatingMETAR = True
4300
4301 buffer.delete_text(position, length)
4302 buffer.insert_text(position, text.upper(), length)
4303
4304 self._updatingMETAR = False
4305
4306#-----------------------------------------------------------------------------
4307
4308class CruisePage(Page):
4309 """The page containing the flight level that might change during flight."""
4310 def __init__(self, wizard):
4311 """Construct the page."""
4312 super(CruisePage, self).__init__(wizard, "cruise",
4313 xstr("cruise_title"),
4314 xstr("cruise_help"))
4315
4316 self._loggable = False
4317 self._loggedCruiseLevel = 240
4318 self._activated = False
4319
4320 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
4321 xscale = 0.0, yscale = 1.0)
4322
4323 mainBox = gtk.VBox()
4324 alignment.add(mainBox)
4325 self.setMainWidget(alignment)
4326
4327 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
4328 xscale = 0.0, yscale = 0.0)
4329 mainBox.pack_start(alignment, False, False, 16)
4330
4331 levelBox = gtk.HBox()
4332
4333 label = gtk.Label(xstr("route_level"))
4334 label.set_use_underline(True)
4335 levelBox.pack_start(label, True, True, 0)
4336
4337 self._cruiseLevel = gtk.SpinButton()
4338 self._cruiseLevel.set_increments(step = 10, page = 100)
4339 self._cruiseLevel.set_range(min = 50, max = 500)
4340 self._cruiseLevel.set_tooltip_text(xstr("cruise_route_level_tooltip"))
4341 self._cruiseLevel.set_numeric(True)
4342 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
4343 label.set_mnemonic_widget(self._cruiseLevel)
4344
4345 levelBox.pack_start(self._cruiseLevel, False, False, 8)
4346
4347 self._updateButton = gtk.Button(xstr("cruise_route_level_update"));
4348 self._updateButton.set_use_underline(True)
4349 self._updateButton.set_tooltip_text(xstr("cruise_route_level_update_tooltip"))
4350 self._updateButton.connect("clicked", self._updateButtonClicked)
4351
4352 levelBox.pack_start(self._updateButton, False, False, 16)
4353
4354 mainBox.pack_start(levelBox, False, False, 0)
4355
4356 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
4357 xscale = 0.0, yscale = 1.0)
4358 mainBox.pack_start(alignment, True, True, 0)
4359
4360 self.addCancelFlightButton()
4361
4362 self._backButton = self.addPreviousButton(clicked = self._backClicked)
4363 self._button = self.addNextButton(clicked = self._forwardClicked)
4364
4365 @property
4366 def activated(self):
4367 """Determine if the page is already activated or not."""
4368 return self._activated
4369
4370 @property
4371 def cruiseLevel(self):
4372 """Get the cruise level."""
4373 return self._loggedCruiseLevel
4374
4375 @property
4376 def loggableCruiseLevel(self):
4377 """Get the cruise level which should be logged."""
4378 return self._cruiseLevel.get_value_as_int()
4379
4380 def setLoggable(self, loggable):
4381 """Set whether the cruise altitude can be logged."""
4382 self._loggable = loggable
4383 self._updateButtons()
4384
4385 def activate(self):
4386 """Setup the route from the booked flight."""
4387 self._loggedCruiseLevel = self._wizard.filedCruiseLevel
4388 self._cruiseLevel.set_value(self._loggedCruiseLevel)
4389 self._activated = True
4390
4391 def reset(self):
4392 """Reset the page."""
4393 self._loggable = False
4394 self._activated = False
4395 super(CruisePage, self).reset()
4396
4397 def _updateButtons(self):
4398 """Update the sensitivity of the buttons."""
4399 self._updateButton.set_sensitive(self._loggable and
4400 self.loggableCruiseLevel!=
4401 self._loggedCruiseLevel)
4402
4403 def _cruiseLevelChanged(self, spinButton):
4404 """Called when the cruise level has changed."""
4405 self._updateButtons()
4406
4407 def _updateButtonClicked(self, button):
4408 """Called when the update button is clicked."""
4409 if self._wizard.cruiseLevelChanged():
4410 self._loggedCruiseLevel = self.loggableCruiseLevel
4411 self._updateButtons()
4412
4413 def _backClicked(self, button):
4414 """Called when the Back button is pressed."""
4415 self.goBack()
4416
4417 def _forwardClicked(self, button):
4418 """Called when the Forward button is clicked."""
4419 self._wizard.nextPage()
4420
4421#-----------------------------------------------------------------------------
4422
4423class LandingPage(Page):
4424 """Page for entering landing data."""
4425 def __init__(self, wizard):
4426 """Construct the landing page."""
4427 super(LandingPage, self).__init__(wizard, "landing",
4428 xstr("landing_title"),
4429 xstr("landing_help"),
4430 completedHelp = xstr("landing_chelp"))
4431
4432 self._flightEnded = False
4433
4434 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
4435 xscale = 0.0, yscale = 0.0)
4436
4437 table = gtk.Table(7, 24)
4438 table.set_row_spacings(4)
4439 table.set_col_spacings(16)
4440 table.set_homogeneous(False)
4441 alignment.add(table)
4442 self.setMainWidget(alignment)
4443
4444 row = 0
4445
4446 label = gtk.Label(xstr("landing_metar"))
4447 label.set_use_underline(True)
4448 label.set_alignment(0.0, 0.5)
4449 table.attach(label, 1, 2, row, row+1)
4450
4451 self._metar = gtk.Entry()
4452 self._metar.set_width_chars(40)
4453 self._metar.set_tooltip_text(xstr("landing_metar_tooltip"))
4454 self._metar.connect("changed", self._metarChanged)
4455 self._metar.get_buffer().connect_after("inserted-text", self._metarInserted)
4456 table.attach(self._metar, 2, 24, row, row+1)
4457 label.set_mnemonic_widget(self._metar)
4458
4459 self._updatingMETAR = False
4460
4461 row += 1
4462
4463 label = gtk.Label(xstr("landing_star"))
4464 label.set_use_underline(True)
4465 label.set_alignment(0.0, 0.5)
4466 table.attach(label, 1, 2, row, row + 1)
4467
4468 if pygobject:
4469 self._star = gtk.ComboBox.new_with_model_and_entry(comboModel)
4470 else:
4471 self._star = gtk.ComboBoxEntry(comboModel)
4472
4473 self._star.set_entry_text_column(0)
4474 self._star.get_child().set_width_chars(10)
4475 self._star.set_tooltip_text(xstr("landing_star_tooltip"))
4476 self._star.connect("changed", self._upperChangedComboBox)
4477 self._star.set_sensitive(False)
4478 table.attach(self._star, 2, 4, row, row + 1)
4479 label.set_mnemonic_widget(self._star)
4480
4481 row += 1
4482
4483 label = gtk.Label(xstr("landing_transition"))
4484 label.set_use_underline(True)
4485 label.set_alignment(0.0, 0.5)
4486 table.attach(label, 1, 2, row, row + 1)
4487
4488 if pygobject:
4489 self._transition = gtk.ComboBox.new_with_model_and_entry(comboModel)
4490 else:
4491 self._transition = gtk.ComboBoxEntry(comboModel)
4492
4493 self._transition.set_entry_text_column(0)
4494 self._transition.get_child().set_width_chars(10)
4495 self._transition.set_tooltip_text(xstr("landing_transition_tooltip"))
4496 self._transition.connect("changed", self._upperChangedComboBox)
4497 self._transition.set_sensitive(False)
4498 table.attach(self._transition, 2, 4, row, row + 1)
4499 label.set_mnemonic_widget(self._transition)
4500
4501 row += 1
4502
4503 label = gtk.Label(xstr("landing_runway"))
4504 label.set_use_underline(True)
4505 label.set_alignment(0.0, 0.5)
4506 table.attach(label, 1, 2, row, row + 1)
4507
4508 self._runway = gtk.Entry()
4509 self._runway.set_width_chars(10)
4510 self._runway.set_tooltip_text(xstr("landing_runway_tooltip"))
4511 self._runway.connect("changed", self._upperChanged)
4512 table.attach(self._runway, 2, 4, row, row + 1)
4513 label.set_mnemonic_widget(self._runway)
4514
4515 row += 1
4516
4517 label = gtk.Label(xstr("landing_approach"))
4518 label.set_use_underline(True)
4519 label.set_alignment(0.0, 0.5)
4520 table.attach(label, 1, 2, row, row + 1)
4521
4522 self._approachType = gtk.Entry()
4523 self._approachType.set_width_chars(10)
4524 self._approachType.set_tooltip_text(xstr("landing_approach_tooltip"))
4525 self._approachType.connect("changed", self._upperChanged)
4526 table.attach(self._approachType, 2, 4, row, row + 1)
4527 label.set_mnemonic_widget(self._approachType)
4528
4529 row += 1
4530
4531 label = gtk.Label(xstr("landing_vref"))
4532 label.set_use_markup(True)
4533 label.set_use_underline(True)
4534 label.set_alignment(0.0, 0.5)
4535 table.attach(label, 1, 2, row, row + 1)
4536
4537 self._vref = IntegerEntry()
4538 self._vref.set_width_chars(5)
4539 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip_knots"))
4540 self._vref.connect("integer-changed", self._vrefChanged)
4541 table.attach(self._vref, 3, 4, row, row + 1)
4542 label.set_mnemonic_widget(self._vref)
4543
4544 self._vrefUnit = gtk.Label(xstr("label_knots"))
4545 table.attach(self._vrefUnit, 4, 5, row, row + 1)
4546
4547 row += 1
4548
4549 self._antiIceOn = gtk.CheckButton(xstr("landing_antiice"))
4550 self._antiIceOn.set_use_underline(True)
4551 self._antiIceOn.set_tooltip_text(xstr("landing_antiice_tooltip"))
4552 table.attach(self._antiIceOn, 3, 5, row, row + 1)
4553
4554 self.addCancelFlightButton()
4555
4556 self.addPreviousButton(clicked = self._backClicked)
4557
4558 self._button = self.addNextButton(clicked = self._forwardClicked)
4559
4560 self._active = False
4561
4562 @property
4563 def star(self):
4564 """Get the STAR or None if none entered."""
4565 text = self._star.get_child().get_text()
4566 return text if self._star.get_active()!=0 and text and text!="N/A" \
4567 else None
4568
4569 @property
4570 def transition(self):
4571 """Get the transition or None if none entered."""
4572 text = self._transition.get_child().get_text()
4573 return text if self._transition.get_active()!=0 and text and text!="N/A" \
4574 else None
4575
4576 @property
4577 def approachType(self):
4578 """Get the approach type."""
4579 return self._approachType.get_text()
4580
4581 @property
4582 def runway(self):
4583 """Get the runway."""
4584 return self._runway.get_text()
4585
4586 @property
4587 def vref(self):
4588 """Return the landing reference speed."""
4589 return self._vref.get_int()
4590
4591 @property
4592 def antiIceOn(self):
4593 """Get whether the anti-ice system has been turned on."""
4594 return self._antiIceOn.get_active()
4595
4596 @antiIceOn.setter
4597 def antiIceOn(self, value):
4598 """Set the anti-ice indicator."""
4599 self._antiIceOn.set_active(value)
4600
4601 def reset(self):
4602 """Reset the page if the wizard is reset."""
4603 super(LandingPage, self).reset()
4604 self._vref.reset()
4605 self._antiIceOn.set_active(False)
4606 self._flightEnded = False
4607 self._active = False
4608
4609 def activate(self):
4610 """Called when the page is activated."""
4611 self._updatingMETAR = True
4612 self._metar.get_buffer().set_text(self._wizard.arrivalMETAR, -1)
4613 self._updatingMETAR = False
4614
4615 self._star.set_active(0)
4616 self._star.set_sensitive(True)
4617
4618 self._transition.set_active(0)
4619 self._transition.set_sensitive(True)
4620
4621 if self._wizard.landingRunway is None:
4622 self._runway.set_text("")
4623 else:
4624 self._runway.set_text(self._wizard.landingRunway)
4625 self._runway.set_sensitive(True)
4626
4627 self._approachType.set_text("")
4628 self._approachType.set_sensitive(True)
4629
4630 self._vref.set_int(None)
4631 self._vref.set_sensitive(True)
4632
4633 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
4634 speedUnit = xstr("label" + i18nSpeedUnit)
4635 self._vrefUnit.set_text(speedUnit)
4636
4637 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip" +
4638 i18nSpeedUnit))
4639
4640 self._updateForwardButton()
4641
4642 self._active = True
4643
4644 def flightEnded(self):
4645 """Called when the flight has ended."""
4646 super(LandingPage, self).flightEnded()
4647 self._flightEnded = True
4648 self._updateForwardButton()
4649
4650 def changeMETAR(self, metar):
4651 """Change the METAR as a result of an edit on one of the other
4652 pages."""
4653 if self._active:
4654 print("LandingPage.changeMETAR")
4655 self._updatingMETAR = True
4656 self._metar.get_buffer().set_text(metar, -1)
4657 self._updatingMETAR = False
4658
4659 self._updateForwardButton()
4660
4661 def _updateForwardButton(self):
4662 """Update the sensitivity of the forward button."""
4663 sensitive = self._flightEnded and \
4664 self._metar.get_text()!="" and \
4665 (self.star is not None or
4666 self.transition is not None) and \
4667 self._runway.get_text()!="" and \
4668 self._approachType.get_text()!="" and \
4669 self.vref is not None
4670 self._button.set_sensitive(sensitive)
4671
4672 def _upperChanged(self, entry):
4673 """Called for entry widgets that must be converted to uppercase."""
4674 entry.set_text(entry.get_text().upper())
4675 self._updateForwardButton()
4676
4677 def _upperChangedComboBox(self, comboBox):
4678 """Called for combo box widgets that must be converted to uppercase."""
4679 if comboBox.get_active()==-1:
4680 entry = comboBox.get_child()
4681 entry.set_text(entry.get_text().upper())
4682 self._updateForwardButton()
4683
4684 def _vrefChanged(self, widget, value):
4685 """Called when the Vref has changed."""
4686 self._updateForwardButton()
4687
4688 def _backClicked(self, button):
4689 """Called when the Back button is pressed."""
4690 self.goBack()
4691
4692 def _forwardClicked(self, button):
4693 """Called when the forward button is clicked."""
4694 wizard = self._wizard
4695
4696 aircraft = wizard.gui.flight.aircraft
4697 aircraft.updateVRef()
4698 aircraft.updateLandingAntiIce()
4699 if wizard.gui.config.onlineGateSystem and \
4700 wizard.loggedIn and not self._completed and \
4701 wizard.bookedFlight.arrivalICAO=="LHBP" and \
4702 not wizard.entranceExam:
4703 wizard.getFleet(callback = self._fleetRetrieved, force = True)
4704 elif wizard.entranceExam:
4705 self._handleEntranceExamDone()
4706 else:
4707 wizard.nextPage()
4708
4709 def _fleetRetrieved(self, fleet):
4710 """Callback for the fleet retrieval."""
4711 self._wizard.nextPage()
4712
4713 def _metarChanged(self, entry):
4714 """Called when the METAR has changed."""
4715 print("LandingPage.metarChanged", self._updatingMETAR)
4716 if not self._updatingMETAR:
4717 self._updateForwardButton()
4718 self._wizard.metarChanged(entry.get_text(), self)
4719
4720 def _metarInserted(self, buffer, position, text, length):
4721 """Called when new characters are inserted into the METAR.
4722
4723 It uppercases all characters."""
4724 print("LandingPage.metarInserted", self._updatingMETAR)
4725 if not self._updatingMETAR:
4726 self._updatingMETAR = True
4727
4728 buffer.delete_text(position, length)
4729 buffer.insert_text(position, text.upper(), length)
4730
4731 self._updatingMETAR = False
4732
4733 def _handleEntranceExamDone(self):
4734 """Handle the end of the entrance exam.
4735
4736 If the there was a NO-GO fault, notify the user that exam is a failure
4737 and take them back to the student page. Otherwise congratulate, update
4738 the database to reflect that the exam has been taken and go back to the
4739 student page."""
4740 self._wizard.jumpPage("chkfinish")
4741
4742#-----------------------------------------------------------------------------
4743
4744class PIREPSaveHelper(object):
4745 """A helper to use for saving PIREPs."""
4746 def __init__(self, wizard):
4747 """Construct the helper."""
4748 super(PIREPSaveHelper, self).__init__()
4749
4750 self._wizard = wizard
4751
4752 self._lastSavePath = None
4753 self._savePIREPDialog = None
4754
4755 def addButton(self, page):
4756 """Add a button to save the PIREP to the given page."""
4757 return page.addButton(xstr("finish_save"), sensitive = False,
4758 clicked = self._saveClicked,
4759 tooltip = xstr("finish_save_tooltip"),
4760 clickedArg = page)
4761
4762 def autoSavePIREP(self, page):
4763 """Perform the automatic saving of the PIREP."""
4764 self._lastSavePath = os.path.join(self._wizard.gui.config.pirepDirectory,
4765 self._getDefaultPIREPName())
4766 self._lastSavePath = self._lastSavePath
4767 self._savePIREP(page, automatic = True)
4768
4769 def _getDefaultPIREPName(self):
4770 """Get the default name of the PIREP."""
4771 gui = self._wizard.gui
4772
4773 bookedFlight = gui.bookedFlight
4774 tm = time.gmtime()
4775
4776 pilotID = self._wizard.pilotID
4777 if pilotID: pilotID += " "
4778 return "%s%s %02d%02d %s-%s.pirep" % \
4779 (pilotID, str(bookedFlight.departureTime.date()),
4780 tm.tm_hour, tm.tm_min,
4781 bookedFlight.departureICAO, bookedFlight.arrivalICAO)
4782
4783 def _saveClicked(self, button, page):
4784 """Called when the Save PIREP button is clicked."""
4785 gui = self._wizard.gui
4786
4787 fileName = self._getDefaultPIREPName()
4788
4789 dialog = self._getSaveDialog()
4790
4791 if self._lastSavePath is None:
4792 pirepDirectory = gui.config.pirepDirectory
4793 if pirepDirectory is not None:
4794 dialog.set_current_folder(pirepDirectory)
4795 else:
4796 dialog.set_current_folder(os.path.dirname(self._lastSavePath))
4797
4798 dialog.set_current_name(fileName)
4799 result = dialog.run()
4800 dialog.hide()
4801
4802 if result==RESPONSETYPE_OK:
4803 self._lastSavePath = dialog.get_filename()
4804 self._savePIREP(page)
4805
4806 def _savePIREP(self, page, automatic = False):
4807 """Perform the saving of the PIREP."""
4808
4809 gui = self._wizard.gui
4810
4811 if automatic:
4812 gui.beginBusy(xstr("finish_autosave_busy"))
4813
4814 pirep = PIREP(gui.flight)
4815 error = pirep.save(self._lastSavePath)
4816
4817 if automatic:
4818 gui.endBusy()
4819
4820 if error:
4821 type = MESSAGETYPE_ERROR
4822 message = xstr("finish_save_failed")
4823 secondary = xstr("finish_save_failed_sec") % (error,)
4824 else:
4825 type = MESSAGETYPE_INFO
4826 message = xstr("finish_save_done")
4827 if automatic:
4828 secondary = xstr("finish_save_done_sec") % (self._lastSavePath,)
4829 else:
4830 secondary = None
4831 page.setPIREPSaved()
4832
4833 dialog = gtk.MessageDialog(parent = gui.mainWindow,
4834 type = type, message_format = message)
4835 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
4836 dialog.set_title(WINDOW_TITLE_BASE)
4837 if secondary is not None:
4838 dialog.format_secondary_markup(secondary)
4839
4840 dialog.run()
4841 dialog.hide()
4842
4843 def _getSaveDialog(self):
4844 """Get the PIREP saving dialog.
4845
4846 If it does not exist yet, create it."""
4847 if self._savePIREPDialog is None:
4848 gui = self._wizard.gui
4849 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
4850 xstr("finish_save_title"),
4851 action = FILE_CHOOSER_ACTION_SAVE,
4852 buttons = (gtk.STOCK_CANCEL,
4853 RESPONSETYPE_CANCEL,
4854 gtk.STOCK_OK, RESPONSETYPE_OK),
4855 parent = gui.mainWindow)
4856 dialog.set_modal(True)
4857 dialog.set_do_overwrite_confirmation(True)
4858
4859 filter = gtk.FileFilter()
4860 filter.set_name(xstr("file_filter_pireps"))
4861 filter.add_pattern("*.pirep")
4862 dialog.add_filter(filter)
4863
4864 filter = gtk.FileFilter()
4865 filter.set_name(xstr("file_filter_all"))
4866 filter.add_pattern("*.*")
4867 dialog.add_filter(filter)
4868
4869 self._savePIREPDialog = dialog
4870
4871 return self._savePIREPDialog
4872
4873#-----------------------------------------------------------------------------
4874
4875class FinishPage(Page):
4876 """Flight finish page."""
4877 def __init__(self, wizard, saveHelper):
4878 """Construct the finish page."""
4879 help = xstr("finish_help") + xstr("finish_help_goodtime")
4880 super(FinishPage, self).__init__(wizard, "finish",
4881 xstr("finish_title"), help)
4882
4883 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
4884 xscale = 0.0, yscale = 0.0)
4885
4886 table = gtk.Table(10, 2)
4887 table.set_row_spacings(4)
4888 table.set_col_spacings(16)
4889 table.set_homogeneous(False)
4890 alignment.add(table)
4891 self.setMainWidget(alignment)
4892
4893 row = 0
4894
4895 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4896 label = gtk.Label(xstr("finish_rating"))
4897 labelAlignment.add(label)
4898 table.attach(labelAlignment, 0, 1, row, row+1)
4899
4900 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4901 self._flightRating = gtk.Label()
4902 self._flightRating.set_width_chars(8)
4903 self._flightRating.set_alignment(0.0, 0.5)
4904 self._flightRating.set_use_markup(True)
4905 labelAlignment.add(self._flightRating)
4906 table.attach(labelAlignment, 1, 2, row, row+1)
4907
4908 row += 1
4909
4910 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4911 label = gtk.Label(xstr("finish_dep_time"))
4912 labelAlignment.add(label)
4913 table.attach(labelAlignment, 0, 1, row, row+1)
4914
4915 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4916 self._depTime = gtk.Label()
4917 self._depTime.set_width_chars(13)
4918 self._depTime.set_alignment(0.0, 0.5)
4919 self._depTime.set_use_markup(True)
4920 self._depTime.set_tooltip_markup(xstr("finish_dep_time_tooltip"))
4921 labelAlignment.add(self._depTime)
4922 table.attach(labelAlignment, 1, 2, row, row+1)
4923
4924 row += 1
4925
4926 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4927 label = gtk.Label(xstr("finish_flight_time"))
4928 labelAlignment.add(label)
4929 table.attach(labelAlignment, 0, 1, row, row+1)
4930
4931 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4932 self._flightTime = gtk.Label()
4933 self._flightTime.set_width_chars(10)
4934 self._flightTime.set_alignment(0.0, 0.5)
4935 self._flightTime.set_use_markup(True)
4936 labelAlignment.add(self._flightTime)
4937 table.attach(labelAlignment, 1, 2, row, row+1)
4938
4939 row += 1
4940
4941 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4942 label = gtk.Label(xstr("finish_block_time"))
4943 labelAlignment.add(label)
4944 table.attach(labelAlignment, 0, 1, row, row+1)
4945
4946 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4947 self._blockTime = gtk.Label()
4948 self._blockTime.set_width_chars(10)
4949 self._blockTime.set_alignment(0.0, 0.5)
4950 self._blockTime.set_use_markup(True)
4951 labelAlignment.add(self._blockTime)
4952 table.attach(labelAlignment, 1, 2, row, row+1)
4953
4954 row += 1
4955
4956 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4957 label = gtk.Label(xstr("finish_arr_time"))
4958 labelAlignment.add(label)
4959 table.attach(labelAlignment, 0, 1, row, row+1)
4960
4961 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4962 self._arrTime = gtk.Label()
4963 self._arrTime.set_width_chars(13)
4964 self._arrTime.set_alignment(0.0, 0.5)
4965 self._arrTime.set_use_markup(True)
4966 self._arrTime.set_tooltip_markup(xstr("finish_arr_time_tooltip"))
4967 labelAlignment.add(self._arrTime)
4968 table.attach(labelAlignment, 1, 2, row, row+1)
4969
4970 row += 1
4971
4972 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4973 label = gtk.Label(xstr("finish_distance"))
4974 labelAlignment.add(label)
4975 table.attach(labelAlignment, 0, 1, row, row+1)
4976
4977 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4978 self._distanceFlown = gtk.Label()
4979 self._distanceFlown.set_width_chars(10)
4980 self._distanceFlown.set_alignment(0.0, 0.5)
4981 self._distanceFlown.set_use_markup(True)
4982 labelAlignment.add(self._distanceFlown)
4983 table.attach(labelAlignment, 1, 2, row, row+1)
4984
4985 row += 1
4986
4987 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4988 label = gtk.Label(xstr("finish_fuel"))
4989 labelAlignment.add(label)
4990 table.attach(labelAlignment, 0, 1, row, row+1)
4991
4992 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4993 self._fuelUsed = gtk.Label()
4994 self._fuelUsed.set_width_chars(10)
4995 self._fuelUsed.set_alignment(0.0, 0.5)
4996 self._fuelUsed.set_use_markup(True)
4997 labelAlignment.add(self._fuelUsed)
4998 table.attach(labelAlignment, 1, 2, row, row+1)
4999
5000 row += 1
5001
5002 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
5003 yalign = 0.5, yscale = 0.0)
5004 label = gtk.Label(xstr("finish_type"))
5005 label.set_use_underline(True)
5006 labelAlignment.add(label)
5007 table.attach(labelAlignment, 0, 1, row, row+1)
5008
5009 self._flightType = createFlightTypeComboBox()
5010 self._flightType.set_tooltip_text(xstr("finish_type_tooltip"))
5011 self._flightType.set_active(0)
5012 self._flightType.connect("changed", self._flightTypeChanged)
5013 flightTypeAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
5014 flightTypeAlignment.add(self._flightType)
5015 table.attach(flightTypeAlignment, 1, 2, row, row+1)
5016 label.set_mnemonic_widget(self._flightType)
5017
5018 row += 1
5019
5020 self._onlineFlight = gtk.CheckButton(xstr("finish_online"))
5021 self._onlineFlight.set_use_underline(True)
5022 self._onlineFlight.set_tooltip_text(xstr("finish_online_tooltip"))
5023 onlineFlightAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
5024 onlineFlightAlignment.add(self._onlineFlight)
5025 table.attach(onlineFlightAlignment, 1, 2, row, row + 1)
5026
5027 row += 1
5028
5029 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
5030 yalign = 0.5, yscale = 0.0)
5031 self._gateLabel = gtk.Label(xstr("finish_gate"))
5032 self._gateLabel.set_use_underline(True)
5033 labelAlignment.add(self._gateLabel)
5034 table.attach(labelAlignment, 0, 1, row, row+1)
5035
5036 self._gatesModel = gtk.ListStore(str)
5037
5038 self._gate = gtk.ComboBox(model = self._gatesModel)
5039 renderer = gtk.CellRendererText()
5040 self._gate.pack_start(renderer, True)
5041 self._gate.add_attribute(renderer, "text", 0)
5042 self._gate.set_tooltip_text(xstr("finish_gate_tooltip"))
5043 self._gate.connect("changed", self._gateChanged)
5044 gateAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
5045 gateAlignment.add(self._gate)
5046 table.attach(gateAlignment, 1, 2, row, row+1)
5047 self._gateLabel.set_mnemonic_widget(self._gate)
5048
5049 self.addButton(xstr("finish_newFlight"),
5050 sensitive = True,
5051 clicked = self._newFlightClicked,
5052 tooltip = xstr("finish_newFlight_tooltip"),
5053 padding = 16)
5054
5055 self.addPreviousButton(clicked = self._backClicked)
5056
5057 self._saveHelper = saveHelper
5058 self._saveButton = saveHelper.addButton(self)
5059
5060 self._tooBigTimeDifference = False
5061 self._deferredAutoSave = False
5062 self._pirepSaved = False
5063 self._pirepSent = False
5064
5065 self._sendButton = self.addButton(xstr("sendPIREP"), default = True,
5066 sensitive = False,
5067 clicked = self._sendClicked,
5068 tooltip = xstr("sendPIREP_tooltip"))
5069
5070 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 10*60.0)
5071 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 20*60.0)
5072 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 0*60.0)
5073 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), (23*60.0+50)*60.0)
5074 # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (1*60.0+5)*60.0)
5075 # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (0*60.0+50)*60.0)
5076 # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (0*60.0+5)*60.0)
5077 # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (23*60.0+45)*60.0)
5078
5079 @property
5080 def flightType(self):
5081 """Get the flight type."""
5082 index = self._flightType.get_active()
5083 return None if index<0 else self._flightType.get_model()[index][1]
5084
5085 @property
5086 def online(self):
5087 """Get whether the flight was an online flight or not."""
5088 return self._onlineFlight.get_active()
5089
5090 def activate(self):
5091 """Activate the page."""
5092 self._deferredAutoSave = False
5093 self._pirepSaved = False
5094 self._pirepSent = False
5095
5096 flight = self._wizard.gui._flight
5097 rating = flight.logger.getRating()
5098 if rating<0:
5099 self._flightRating.set_markup('<b><span foreground="red">NO GO</span></b>')
5100 else:
5101 self._flightRating.set_markup("<b>%.1f %%</b>" % (rating,))
5102
5103 flightLength = flight.flightTimeEnd - flight.flightTimeStart
5104 self._flightTime.set_markup("<b>%s</b>" % \
5105 (util.getTimeIntervalString(flightLength),))
5106
5107 blockLength = flight.blockTimeEnd - flight.blockTimeStart
5108 self._blockTime.set_markup("<b>%s</b>" % \
5109 (util.getTimeIntervalString(blockLength),))
5110
5111 self._distanceFlown.set_markup("<b>%.2f NM</b>" % \
5112 (flight.flownDistance,))
5113
5114 self._fuelUsed.set_markup("<b>%.0f kg</b>" % \
5115 (flight.startFuel - flight.endFuel,))
5116
5117 self._flightType.set_active(-1)
5118 self._onlineFlight.set_active(self._wizard.loggedIn)
5119
5120 self._gatesModel.clear()
5121 if self._wizard.gui.config.onlineGateSystem and \
5122 self._wizard.loggedIn and \
5123 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
5124 not self._wizard.entranceExam:
5125 occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
5126 for gate in lhbpGates.gates:
5127 if gate.isAvailable(lhbpGates, occupiedGateNumbers):
5128 self._gatesModel.append([gate.number])
5129 self._gateLabel.set_sensitive(True)
5130 self._gate.set_sensitive(True)
5131 self._gate.set_active(-1)
5132 else:
5133 self._gateLabel.set_sensitive(False)
5134 self._gate.set_sensitive(False)
5135
5136 self._updateTimes()
5137
5138 def updateButtons(self):
5139 """Update the sensitivity state of the buttons."""
5140 gui = self._wizard.gui
5141 faultsExplained = gui.faultsFullyExplained
5142 timesCorrect = self.flightType is None or \
5143 not self._tooBigTimeDifference or \
5144 gui.hasComments or gui.hasDelayCode
5145 sensitive = gui.flight is not None and \
5146 gui.flight.stage==const.STAGE_END and \
5147 self._flightType.get_active()>=0 and \
5148 (self._gatesModel.get_iter_first() is None or
5149 self._gate.get_active()>=0) and \
5150 faultsExplained and timesCorrect
5151
5152 self._updateHelp(faultsExplained, timesCorrect)
5153
5154 wasSensitive = self._saveButton.get_sensitive()
5155
5156 if gui.config.pirepAutoSave and sensitive and not wasSensitive:
5157 if gui.isWizardActive():
5158 self._saveHelper.autoSavePIREP(self)
5159 else:
5160 self._deferredAutoSave = True
5161
5162 if not sensitive:
5163 self._deferredAutoSave = False
5164
5165 self._saveButton.set_sensitive(sensitive)
5166 self._sendButton.set_sensitive(sensitive and
5167 self._wizard.bookedFlight.id is not None)
5168
5169 def grabDefault(self):
5170 """If the page has a default button, make it the default one."""
5171 super(FinishPage, self).grabDefault()
5172 if self._deferredAutoSave:
5173 self._saveHelper.autoSavePIREP(self)
5174 self._deferredAutoSave = False
5175
5176 def setPIREPSaved(self):
5177 """Mark the PIREP as saved."""
5178 self._pirepSaved = True
5179
5180 def _backClicked(self, button):
5181 """Called when the Back button is pressed."""
5182 self.goBack()
5183
5184 def _flightTypeChanged(self, comboBox):
5185 """Called when the flight type has changed."""
5186 self._updateTimes()
5187
5188 def _gateChanged(self, comboBox):
5189 """Called when the arrival gate has changed."""
5190 self.updateButtons()
5191
5192 def _newFlightClicked(self, button):
5193 """Called when the new flight button is clicked."""
5194 gui = self._wizard.gui
5195 if not self._pirepSent and not self._pirepSaved:
5196 dialog = gtk.MessageDialog(parent = gui.mainWindow,
5197 type = MESSAGETYPE_QUESTION,
5198 message_format = xstr("finish_newFlight_question"))
5199
5200 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
5201 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
5202
5203 dialog.set_title(WINDOW_TITLE_BASE)
5204 result = dialog.run()
5205 dialog.hide()
5206 if result!=RESPONSETYPE_YES:
5207 return
5208
5209 gui.reset()
5210
5211 def _sendClicked(self, button):
5212 """Called when the Send button is clicked."""
5213 pirep = PIREP(self._wizard.gui.flight)
5214 self._wizard.gui.sendPIREP(pirep,
5215 callback = self._handlePIREPSent)
5216
5217 def _handlePIREPSent(self, returned, result):
5218 """Callback for the PIREP sending result."""
5219 self._pirepSent = returned and result.success
5220 if self._wizard.gui.config.onlineGateSystem and \
5221 self._wizard.loggedIn and not self._wizard.entranceExam and \
5222 returned and result.success:
5223 bookedFlight = self._wizard.bookedFlight
5224 if bookedFlight.arrivalICAO=="LHBP":
5225 iter = self._gate.get_active_iter()
5226 gateNumber = None if iter is None \
5227 else self._gatesModel.get_value(iter, 0)
5228
5229 status = const.PLANE_PARKING if gateNumber is None \
5230 else const.PLANE_HOME
5231 else:
5232 gateNumber = None
5233 status = const.PLANE_AWAY
5234
5235 self._wizard.updatePlane(self._planeUpdated,
5236 bookedFlight.tailNumber,
5237 status, gateNumber = gateNumber)
5238
5239 def _planeUpdated(self, success):
5240 """Callback for the plane updating."""
5241 pass
5242
5243 def _formatTime(self, scheduledTime, realTimestamp, xxx_todo_changeme):
5244 """Format the departure or arrival time based on the given data as a
5245 markup for a label."""
5246 (warning, error) = xxx_todo_changeme
5247 realTime = time.gmtime(realTimestamp)
5248
5249 if warning:
5250 colour = "red" if error else "orange"
5251 markupBegin = '<span foreground="%s">' % (colour,)
5252 markupEnd = '</span>'
5253 else:
5254 markupBegin = markupEnd = ""
5255
5256 markup = "<b>%s%02d:%02d [%02d:%02d]%s</b>" % \
5257 (markupBegin,
5258 realTime.tm_hour, realTime.tm_min,
5259 scheduledTime.hour, scheduledTime.minute,
5260 markupEnd)
5261
5262 return markup
5263
5264 def _updateTimes(self):
5265 """Format the flight times and the help text according to the flight
5266 type.
5267
5268 The buttons are also updated.
5269 """
5270 flight = self._wizard.gui._flight
5271 bookedFlight = flight.bookedFlight
5272
5273 (departureWarning, departureError) = flight.blockTimeStartWrong
5274 (arrivalWarning, arrivalError) = flight.blockTimeEndWrong
5275
5276 if self.flightType==const.FLIGHTTYPE_VIP:
5277 departureError = arrivalError = False
5278
5279 self._tooBigTimeDifference = departureError or arrivalError
5280
5281 self._depTime.set_markup(self._formatTime(bookedFlight.departureTime,
5282 flight.blockTimeStart,
5283 (departureWarning,
5284 departureError)))
5285
5286 self._arrTime.set_markup(self._formatTime(bookedFlight.arrivalTime,
5287 flight.blockTimeEnd,
5288 (arrivalWarning,
5289 arrivalError)))
5290
5291 self.updateButtons()
5292
5293 def _updateHelp(self, faultsExplained, timesCorrect):
5294 """Update the help text according to the actual situation."""
5295 if not faultsExplained:
5296 self.setHelp(xstr("finish_help") + xstr("finish_help_faults"))
5297 elif not timesCorrect:
5298 self.setHelp(xstr("finish_help") + xstr("finish_help_wrongtime"))
5299 else:
5300 self.setHelp(xstr("finish_help") + xstr("finish_help_goodtime"))
5301
5302
5303#-----------------------------------------------------------------------------
5304
5305class CheckFlightFinishPage(Page):
5306 """Finish page for a check flight."""
5307 def __init__(self, wizard, saveHelper):
5308 """Construct the check flight finish page."""
5309 super(CheckFlightFinishPage, self).__init__(wizard,
5310 "chkfinish",
5311 xstr("chkfinish_title"),
5312 "")
5313
5314 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
5315 xscale = 1.0, yscale = 1.0)
5316 self._label = gtk.Label()
5317 alignment.add(self._label)
5318
5319 self.setMainWidget(alignment)
5320
5321 self._saveHelper = saveHelper
5322 self._saveButton = saveHelper.addButton(self)
5323
5324 self._button = self.addNextButton(sensitive = False,
5325 clicked = self._forwardClicked)
5326
5327 def activate(self):
5328 """Activate the page."""
5329 wizard = self._wizard
5330 loginResult = wizard.loginResult
5331 gui = wizard.gui
5332 rating = gui.flight.logger.getRating()
5333
5334 if rating>=0:
5335 loginResult.checkFlightStatus = True
5336
5337 firstOfficer = \
5338 loginResult.entryExamPassed and loginResult.checkFlightStatus
5339
5340 if firstOfficer:
5341 loginResult.rank = "FO"
5342
5343 if rating<0:
5344 mainMessage = xstr("chkfinish_failed")
5345 else:
5346 mainMessage = xstr("chkfinish_passed_begin")
5347 if firstOfficer:
5348 mainMessage += xstr("chkfinish_passed_fo")
5349 mainMessage += xstr("chkfinish_passed_end")
5350
5351 if firstOfficer:
5352 nextMessage = xstr("chkfinish_next")
5353 else:
5354 nextMessage = xstr("chkfinish_next_student_begin")
5355 if not loginResult.entryExamPassed and \
5356 not loginResult.checkFlightStatus:
5357 nextMessage += xstr("chkfinish_next_student_nothing")
5358 elif loginResult.entryExamPassed and \
5359 not loginResult.checkFlightStatus:
5360 nextMessage += xstr("chkfinish_next_student_no_flight")
5361 elif not loginResult.entryExamPassed and \
5362 loginResult.checkFlightStatus:
5363 nextMessage += xstr("chkfinish_next_student_no_exam")
5364
5365 self._label.set_text(mainMessage +
5366 xstr("chkfinish_savepirep") +
5367 nextMessage)
5368 self._label.set_use_markup(True)
5369 self._label.set_alignment(0.5, 0.0)
5370
5371 self._saveButton.set_sensitive(True)
5372 self._button.set_sensitive(True)
5373
5374 def _forwardClicked(self, button):
5375 """Jump to the student page if there are some tasks to do,
5376 or to the flight selection page, if the pilot is allowed to perform
5377 MAVA flights."""
5378 wizard = self._wizard
5379 gui = wizard.gui
5380
5381 loginResult = wizard.loginResult
5382 if loginResult.checkFlightStatus:
5383 gui.beginBusy(xstr("chkfinish_updateweb_busy"))
5384 gui.webHandler.setCheckFlightPassed(self._checkFlightPassedSetCallback,
5385 wizard.checkFlightAircraftType)
5386 else:
5387 self._resetGUI()
5388
5389 def _checkFlightPassedSetCallback(self, returned, result):
5390 """Called when the check flight status has been set."""
5391 gobject.idle_add(self._checkFlightPassedSet, returned, result)
5392
5393 def _checkFlightPassedSet(self, returned, result):
5394 """Handle the result of an attempt to set the check flight status."""
5395 gui = self._wizard.gui
5396
5397 gui.endBusy()
5398
5399 if returned:
5400 self._resetGUI()
5401 else:
5402 dialog = gtk.MessageDialog(parent = gui.mainWindow,
5403 type = MESSAGETYPE_ERROR,
5404 message_format =
5405 xstr("chkfinish_passedset_failed"))
5406 dialog.set_title(WINDOW_TITLE_BASE + " - " +
5407 xstr("chkfinish_passedset_failed_title"))
5408 dialog.format_secondary_markup(xstr("chkfinish_passedset_failed_secondary"))
5409
5410 dialog.add_button(xstr("button_ok"), 0)
5411
5412 dialog.run()
5413 dialog.hide()
5414
5415 def _resetGUI(self):
5416 """Reset the GUI."""
5417 gui = self._wizard.gui
5418 gui.reset()
5419
5420#-----------------------------------------------------------------------------
5421
5422class Wizard(gtk.VBox):
5423 """The flight wizard."""
5424 def __init__(self, gui):
5425 """Construct the wizard."""
5426 super(Wizard, self).__init__()
5427
5428 self.gui = gui
5429
5430 self._pages = []
5431 self._currentPage = None
5432
5433 self._loginPage = LoginPage(self)
5434 self._pages.append(self._loginPage)
5435 self._flightSelectionPage = FlightSelectionPage(self)
5436 self._pages.append(self._flightSelectionPage)
5437 self._pages.append(GateSelectionPage(self))
5438 self._pages.append(RegisterPage(self))
5439 self._studentPage = StudentPage(self)
5440 self._pages.append(self._studentPage)
5441 self._pages.append(ConnectPage(self))
5442 self._payloadPage = PayloadPage(self)
5443 self._pages.append(self._payloadPage)
5444 self._payloadIndex = len(self._pages)
5445 self._pages.append(TimePage(self))
5446 self._routePage = RoutePage(self)
5447 self._pages.append(self._routePage)
5448 self._simBriefSetupPage = SimBriefSetupPage(self)
5449 self._pages.append(self._simBriefSetupPage)
5450 self._simBriefingPage = SimBriefingPage(self)
5451 self._pages.append(self._simBriefingPage)
5452 self._pages.append(FuelPage(self))
5453 self._departureBriefingPage = BriefingPage(self, True)
5454 self._pages.append(self._departureBriefingPage)
5455 self._arrivalBriefingPage = BriefingPage(self, False)
5456 self._pages.append(self._arrivalBriefingPage)
5457 self._arrivalBriefingIndex = len(self._pages)
5458 self._takeoffPage = TakeoffPage(self)
5459 self._pages.append(self._takeoffPage)
5460 self._cruisePage = CruisePage(self)
5461 self._pages.append(self._cruisePage)
5462 self._landingPage = LandingPage(self)
5463 self._pages.append(self._landingPage)
5464
5465 pirepSaveHelper = PIREPSaveHelper(self)
5466
5467 self._finishPage = FinishPage(self, pirepSaveHelper)
5468 self._pages.append(self._finishPage)
5469 self._pages.append(CheckFlightFinishPage(self, pirepSaveHelper))
5470
5471 self._requestedWidth = None
5472 self._requestedHeight = None
5473
5474 self.connect("size-allocate", self._sizeAllocate)
5475
5476 for page in self._pages:
5477 page.show_all()
5478 page.setStyle()
5479
5480 self._initialize()
5481
5482 def _sizeAllocate(self, widget, allocation):
5483 if self._requestedWidth is not None and \
5484 self._requestedHeight is not None:
5485 return
5486
5487 if self._currentPage is not None:
5488 self.remove(self._pages[self._currentPage])
5489
5490 maxWidth = 0
5491 maxHeight = 0
5492 for page in self._pages:
5493 self.add(page)
5494 self.show_all()
5495 pageSizeRequest = page.size_request()
5496 width = pageSizeRequest.width if pygobject else pageSizeRequest[0]
5497 height = pageSizeRequest.height if pygobject else pageSizeRequest[1]
5498 maxWidth = max(maxWidth, width)
5499 maxHeight = max(maxHeight, height)
5500 self.remove(page)
5501
5502 if self._currentPage is not None:
5503 self.add(self._pages[self._currentPage])
5504
5505 self._requestedWidth = maxWidth
5506 self._requestedHeight = maxHeight
5507 self.set_size_request(maxWidth, maxHeight)
5508
5509 @property
5510 def pilotID(self):
5511 """Get the pilot ID, if given."""
5512 return self._loginPage.pilotID
5513
5514 @property
5515 def entranceExam(self):
5516 """Get whether an entrance exam is about to be taken."""
5517 return self._loginResult is not None and self._loginResult.rank=="STU"
5518
5519 @property
5520 def loggedIn(self):
5521 """Indicate if there was a successful login."""
5522 return self._loginResult is not None
5523
5524 @property
5525 def loginResult(self):
5526 """Get the login result."""
5527 return self._loginResult
5528
5529 @property
5530 def checkFlightAircraftType(self):
5531 """Get the type of the aircraft used to perform the check flight."""
5532 return self._studentPage.aircraftType
5533
5534 def setCurrentPage(self, index, finalize = False, fromPageShift = None):
5535 """Set the current page to the one with the given index.
5536
5537 @param fromPageShift if given, the relative index of one of the
5538 previous pages that should be used as the from-page of the next
5539 page. E.g. if fromPageShift is 1, the previous page will be the
5540 from-page."""
5541 assert index < len(self._pages)
5542
5543 fromPage = self._currentPage
5544 if fromPage is not None:
5545 page = self._pages[fromPage]
5546 if finalize and not page._completed:
5547 page.complete()
5548 self.remove(page)
5549 if fromPageShift is not None:
5550 fromPage -= fromPageShift
5551
5552 self._currentPage = index
5553 page = self._pages[index]
5554 self.add(page)
5555 if page._fromPage is None:
5556 page._fromPage = fromPage
5557 page.initialize()
5558 self.show_all()
5559 if fromPage is not None:
5560 self.grabDefault()
5561
5562 @property
5563 def bookedFlight(self):
5564 """Get the booked flight selected."""
5565 return self._bookedFlight
5566
5567 @property
5568 def numCrew(self):
5569 """Get the number of crew members."""
5570 return self._payloadPage.numCrew
5571
5572 @property
5573 def numPassengers(self):
5574 """Get the number of passengers."""
5575 return self._payloadPage.numPassengers
5576
5577 @property
5578 def bagWeight(self):
5579 """Get the baggage weight."""
5580 return self._payloadPage.bagWeight
5581
5582 @property
5583 def cargoWeight(self):
5584 """Get the cargo weight."""
5585 return self._payloadPage.cargoWeight
5586
5587 @property
5588 def mailWeight(self):
5589 """Get the mail weight."""
5590 return self._payloadPage.mailWeight
5591
5592 @property
5593 def zfw(self):
5594 """Get the calculated ZFW value."""
5595 return 0 if self._bookedFlight is None \
5596 else self._payloadPage.calculateZFW()
5597
5598 @property
5599 def filedCruiseLevel(self):
5600 """Get the filed cruise level."""
5601 return self._routePage.filedCruiseLevel
5602
5603 @property
5604 def filedCruiseAltitude(self):
5605 """Get the filed cruise altitude."""
5606 return self._routePage.filedCruiseLevel * 100
5607
5608 @property
5609 def cruiseAltitude(self):
5610 """Get the cruise altitude."""
5611 level = self._cruisePage.cruiseLevel if self._cruisePage.activated \
5612 else self._routePage.filedCruiseLevel
5613 return level * 100
5614
5615 @property
5616 def loggableCruiseAltitude(self):
5617 """Get the cruise altitude that can be logged."""
5618 if self._cruisePage.activated:
5619 return self._cruisePage.loggableCruiseLevel * 100
5620 else:
5621 return 0
5622
5623 @property
5624 def route(self):
5625 """Get the route."""
5626 return self._routePage.route
5627
5628 @property
5629 def alternate(self):
5630 """Get the ICAO code of the alternate airport."""
5631 return self._routePage.alternate
5632
5633 @property
5634 def departureMETAR(self):
5635 """Get the METAR of the departure airport."""
5636 return self._departureBriefingPage.metar
5637
5638 @property
5639 def arrivalMETAR(self):
5640 """Get the METAR of the arrival airport."""
5641 return self._arrivalBriefingPage.metar
5642
5643 @property
5644 def departureRunway(self):
5645 """Get the departure runway."""
5646 return self._takeoffPage.runway
5647
5648 @property
5649 def sid(self):
5650 """Get the SID."""
5651 return self._takeoffPage.sid
5652
5653 @property
5654 def v1(self):
5655 """Get the V1 speed."""
5656 return self._takeoffPage.v1
5657
5658 @property
5659 def vr(self):
5660 """Get the Vr speed."""
5661 return self._takeoffPage.vr
5662
5663 @property
5664 def v2(self):
5665 """Get the V2 speed."""
5666 return self._takeoffPage.v2
5667
5668 @property
5669 def derate(self):
5670 """Get the derate value."""
5671 return self._takeoffPage.derate
5672
5673 @property
5674 def takeoffAntiIceOn(self):
5675 """Get whether the anti-ice system was on during take-off."""
5676 return self._takeoffPage.antiIceOn
5677
5678 @takeoffAntiIceOn.setter
5679 def takeoffAntiIceOn(self, value):
5680 """Set anti-ice on indicator."""
5681 self._takeoffPage.antiIceOn = value
5682
5683 @property
5684 def rtoIndicated(self):
5685 """Get whether the pilot has indicated that an RTO has occured."""
5686 return self._takeoffPage.rtoIndicated
5687
5688 @property
5689 def arrivalRunway(self):
5690 """Get the arrival runway."""
5691 return self._landingPage.runway
5692
5693 @property
5694 def star(self):
5695 """Get the STAR."""
5696 return self._landingPage.star
5697
5698 @property
5699 def transition(self):
5700 """Get the transition."""
5701 return self._landingPage.transition
5702
5703 @property
5704 def approachType(self):
5705 """Get the approach type."""
5706 return self._landingPage.approachType
5707
5708 @property
5709 def vref(self):
5710 """Get the Vref speed."""
5711 return self._landingPage.vref
5712
5713 @property
5714 def landingAntiIceOn(self):
5715 """Get whether the anti-ice system was on during landing."""
5716 return self._landingPage.antiIceOn
5717
5718 @landingAntiIceOn.setter
5719 def landingAntiIceOn(self, value):
5720 """Set anti-ice on indicator."""
5721 self._landingPage.antiIceOn = value
5722
5723 @property
5724 def flightType(self):
5725 """Get the flight type."""
5726 return self._finishPage.flightType
5727
5728 @property
5729 def online(self):
5730 """Get whether the flight was online or not."""
5731 return self._finishPage.online
5732
5733 @property
5734 def usingSimBrief(self):
5735 """Indicate if we are using a SimBrief briefing or not."""
5736 return self._usingSimBrief
5737
5738 @usingSimBrief.setter
5739 def usingSimBrief(self, x):
5740 """Set whether we are using a SimBrief briefing or not."""
5741 self._usingSimBrief = x
5742
5743 def nextPage(self, finalize = True):
5744 """Go to the next page."""
5745 nextPageID = self._pages[self._currentPage].nextPageID
5746 self.jumpPage(1 if nextPageID is None else nextPageID, finalize)
5747
5748 def jumpPage(self, countOrID, finalize = True, fromPageShift = None):
5749 """Go to the page which is 'count' pages after the current one."""
5750 if isinstance(countOrID, str):
5751 targetIndex = self._getIndexOf(countOrID)
5752 else:
5753 targetIndex = self._currentPage + countOrID
5754 self.setCurrentPage(targetIndex,
5755 finalize = finalize, fromPageShift = fromPageShift)
5756
5757 def grabDefault(self):
5758 """Make the default button of the current page the default."""
5759 self._pages[self._currentPage].grabDefault()
5760
5761 def connected(self, fsType, descriptor):
5762 """Called when the connection could be made to the simulator."""
5763 self.nextPage()
5764
5765 def reset(self, loginResult):
5766 """Resets the wizard to go back to the login page."""
5767 self._initialize(keepLoginResult = loginResult is None,
5768 loginResult = loginResult)
5769
5770 def setStage(self, stage):
5771 """Set the flight stage to the given one."""
5772 if stage!=const.STAGE_END:
5773 self._cruisePage.setLoggable(Flight.canLogCruiseAltitude(stage))
5774
5775 if stage==const.STAGE_TAKEOFF:
5776 self._takeoffPage.allowForward()
5777 elif stage==const.STAGE_LANDING:
5778 if not self._arrivalBriefingPage.metarEdited:
5779 print("Downloading arrival METAR again")
5780 self.gui.webHandler.getMETARs(self._arrivalMETARCallback,
5781 [self._bookedFlight.arrivalICAO])
5782
5783 elif stage==const.STAGE_END:
5784 for page in self._pages:
5785 page.flightEnded()
5786
5787 def _initialize(self, keepLoginResult = False, loginResult = None):
5788 """Initialize the wizard."""
5789 if not keepLoginResult:
5790 self._loginResult = loginResult
5791
5792 self._loginCallback = None
5793
5794 self._fleet = None
5795 self._fleetCallback = None
5796
5797 self._bookedFlight = None
5798 self._departureGate = "-"
5799 self._fuelData = None
5800 self._departureNOTAMs = None
5801 self._departureMETAR = None
5802 self._arrivalNOTAMs = None
5803 self._arrivalMETAR = None
5804 self._usingSimBrief = None
5805 self.takeoffRunway = None
5806 self.landingRunway = None
5807
5808 firstPage = 0 if self._loginResult is None else 1
5809 for page in self._pages[firstPage:]:
5810 page.reset()
5811
5812 self.setCurrentPage(firstPage)
5813 #self.setCurrentPage(10)
5814
5815 def login(self, callback, pilotID, password):
5816 """Called when the login button was clicked."""
5817 self._loginCallback = callback
5818 if pilotID is None:
5819 loginResult = self._loginResult
5820 assert loginResult is not None and loginResult.loggedIn
5821 pilotID = loginResult.pilotID
5822 password = loginResult.password
5823 busyMessage = xstr("reload_busy")
5824 else:
5825 self._loginResult = None
5826 busyMessage = xstr("login_busy")
5827
5828 self.gui.beginBusy(busyMessage)
5829
5830 self.gui.webHandler.login(self._loginResultCallback,
5831 pilotID, password)
5832
5833 def reloadFlights(self, callback):
5834 """Reload the flights from the MAVA server."""
5835 self.login(callback, None, None)
5836
5837 def addFlight(self, bookedFlight):
5838 """Add the given booked flight to the flight selection page."""
5839 self._flightSelectionPage.addFlight(bookedFlight)
5840
5841 def reflyFlight(self, bookedFlight):
5842 """Add the given booked flight to the flight selection page."""
5843 self._removePendingFlight(bookedFlight)
5844 self._flightSelectionPage._reflyFlight(bookedFlight)
5845
5846 def deleteFlight(self, bookedFlight):
5847 """Remove the given flight from the pending flight list."""
5848 self._removePendingFlight(bookedFlight)
5849 self._flightSelectionPage._updatePending()
5850
5851 def cancelFlight(self, reloadCallback):
5852 """Cancel the flight.
5853
5854 If it is an entry exam flight, we go back to the student page.
5855 Otherwise we reload the flights."""
5856 if self.entranceExam:
5857 self.reset(None)
5858 self.jumpPage("student")
5859 else:
5860 self.reloadFlights(reloadCallback)
5861
5862 def cruiseLevelChanged(self):
5863 """Called when the cruise level is changed."""
5864 return self.gui.cruiseLevelChanged()
5865
5866 def metarChanged(self, metar, originator):
5867 """Called when a METER is changed on on of the pages.
5868
5869 originator is the page that originated the changed. It will be used to
5870 determine which METAR (departure or arrival) has changed."""
5871 metar = metar.upper()
5872 if originator in [self._departureBriefingPage, self._takeoffPage]:
5873 self.departureMETARChanged(metar, originator)
5874 else:
5875 self.arrivalMETARChanged(metar, originator)
5876
5877 def departureMETARChanged(self, metar, originator):
5878 """Called when the departure METAR has been edited on one of the
5879 pages.
5880
5881 originator is the page that originated the change. It will not be
5882 called to set the METAR, while others will be."""
5883 for page in [self._departureBriefingPage, self._takeoffPage]:
5884 if page is not originator:
5885 page.changeMETAR(metar)
5886
5887 def arrivalMETARChanged(self, metar, originator):
5888 """Called when the arrival METAR has been edited on one of the
5889 pages.
5890
5891 originator is the page that originated the change. It will not be
5892 called to set the METAR, while others will be."""
5893 for page in [self._arrivalBriefingPage, self._landingPage]:
5894 if page is not originator:
5895 page.changeMETAR(metar)
5896
5897 def _removePendingFlight(self, flight):
5898 """Remove the given pending flight from the login result."""
5899 for flights in [self._loginResult.reportedFlights,
5900 self._loginResult.rejectedFlights]:
5901 for f in flights:
5902 if f.id==flight.id:
5903 flights.remove(f)
5904 return
5905
5906 def _loginResultCallback(self, returned, result):
5907 """The login result callback, called in the web handler's thread."""
5908 gobject.idle_add(self._handleLoginResult, returned, result)
5909
5910 def _handleLoginResult(self, returned, result):
5911 """Handle the login result."""
5912 self.gui.endBusy()
5913 isReload = self._loginResult is not None
5914 if returned:
5915 if result.loggedIn:
5916 self._loginResult = result
5917 self.gui.loginSuccessful()
5918 else:
5919 if isReload:
5920 message = xstr("reload_failed")
5921 else:
5922 message = xstr("login_entranceExam_invalid"
5923 if self.entranceExam else
5924 xstr("login_invalid"))
5925 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
5926 type = MESSAGETYPE_ERROR,
5927 message_format = message)
5928 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
5929 dialog.set_title(WINDOW_TITLE_BASE)
5930 if isReload:
5931 secondary = xstr("reload_failed_sec")
5932 else:
5933 secondary = xstr("login_entranceExam_invalid_sec"
5934 if self.entranceExam else
5935 xstr("login_invalid_sec"))
5936 dialog.format_secondary_markup(secondary)
5937 dialog.run()
5938 dialog.hide()
5939 else:
5940 message = xstr("reload_failconn") if isReload \
5941 else xstr("login_failconn")
5942 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
5943 type = MESSAGETYPE_ERROR,
5944 message_format = message)
5945 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
5946 dialog.set_title(WINDOW_TITLE_BASE)
5947 secondary = xstr("reload_failconn_sec") if isReload \
5948 else xstr("login_failconn_sec")
5949 dialog.format_secondary_markup(secondary)
5950
5951 dialog.run()
5952 dialog.hide()
5953
5954 callback = self._loginCallback
5955 self._loginCallback = None
5956 callback(returned, result)
5957
5958 def getFleet(self, callback, force = False):
5959 """Get the fleet via the GUI and call the given callback."""
5960 self._fleetCallback = callback
5961 self.gui.getFleet(callback = self._fleetRetrieved, force = force)
5962
5963 def _fleetRetrieved(self, fleet):
5964 """Callback for the fleet retrieval."""
5965 self._fleet = fleet
5966 if self._fleetCallback is not None:
5967 self._fleetCallback(fleet)
5968 self._fleetCallback = None
5969
5970 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
5971 """Update the given plane's gate information."""
5972 self.gui.updatePlane(tailNumber, status, gateNumber = gateNumber,
5973 callback = callback)
5974
5975 def updateRTO(self):
5976 """Update the RTO state.
5977
5978 The RTO checkbox will be enabled if the flight has an RTO state and the
5979 comments field contains some text."""
5980 flight = self.gui.flight
5981 rtoEnabled = flight is not None and flight.hasRTO and \
5982 self.gui.hasComments
5983 self._takeoffPage.setRTOEnabled(rtoEnabled)
5984
5985 def commentsChanged(self):
5986 """Called when the comments have changed."""
5987 self.updateRTO()
5988 self._finishPage.updateButtons()
5989
5990 def delayCodesChanged(self):
5991 """Called when the delay codes have changed."""
5992 self._finishPage.updateButtons()
5993
5994 def faultExplanationsChanged(self):
5995 """Called when the faults and their explanations have changed."""
5996 self._finishPage.updateButtons()
5997
5998 def rtoToggled(self, indicated):
5999 """Called when the RTO indication has changed."""
6000 self.gui.rtoToggled(indicated)
6001
6002 def _connectSimulator(self, simulatorType):
6003 """Connect to the simulator."""
6004 self.gui.connectSimulator(self._bookedFlight, simulatorType)
6005
6006 def _arrivalMETARCallback(self, returned, result):
6007 """Called when the METAR of the arrival airport is retrieved."""
6008 gobject.idle_add(self._handleArrivalMETAR, returned, result)
6009
6010 def _handleArrivalMETAR(self, returned, result):
6011 """Called when the METAR of the arrival airport is retrieved."""
6012 icao = self._bookedFlight.arrivalICAO
6013 if returned and icao in result.metars:
6014 metar = result.metars[icao]
6015 if metar!="":
6016 self._arrivalBriefingPage.setMETAR(metar)
6017
6018 def _getIndexOf(self, pageID):
6019 """Get the index for the given page ID.
6020
6021 It is an assertion failure if the ID is not found."""
6022 for index in range(0, len(self._pages)):
6023 page = self._pages[index]
6024 if page.id==pageID:
6025 return index
6026 assert False
6027
6028#-----------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.