source: src/mlx/gui/flight.py@ 990:7ba1c9864bbb

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

Further reduced the window size by making the registration page smaller (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, 4)
1280 table.set_row_spacings(4)
1281 table.set_col_spacings(24)
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 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1302 xscale = 0.0, yscale = 0.0)
1303 label = gtk.Label(xstr("register_name2"))
1304 label.set_use_underline(True)
1305 labelAlignment.add(label)
1306 table.attach(labelAlignment, 2, 3, row, row+1)
1307
1308 self._name2 = gtk.Entry()
1309 self._name2.connect("changed", self._updateButtons)
1310 self._name2.set_tooltip_text(xstr("register_name2_tooltip"))
1311 self._name2.set_width_chars(15)
1312 table.attach(self._name2, 3, 4, row, row+1)
1313 label.set_mnemonic_widget(self._name2)
1314
1315 row += 1
1316
1317 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1318 xscale = 0.0, yscale = 0.0)
1319 label = gtk.Label(xstr("register_year_of_birth"))
1320 label.set_use_underline(True)
1321 labelAlignment.add(label)
1322 table.attach(labelAlignment, 0, 1, row, row+1)
1323
1324 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
1325 xscale = 0.0, yscale = 0.0)
1326
1327 self._yearOfBirth = gtk.SpinButton()
1328 self._yearOfBirth.set_increments(step = 1, page = 10)
1329 self._yearOfBirth.set_range(min = RegisterPage._minYearOfBirth,
1330 max = RegisterPage._maxYearOfBirth)
1331 self._yearOfBirth.set_numeric(True)
1332 self._yearOfBirth.set_tooltip_text(xstr("register_year_of_birth_tooltip"))
1333 self._yearOfBirth.set_width_chars(5)
1334 self._yearOfBirth.connect("changed", self._updateButtons)
1335 self._yearOfBirth.connect("value-changed", self._updateButtons)
1336 alignment.add(self._yearOfBirth)
1337 table.attach(alignment, 1, 2, row, row+1)
1338 label.set_mnemonic_widget(self._yearOfBirth)
1339
1340 row += 1
1341
1342 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1343 xscale = 0.0, yscale = 0.0)
1344 label = gtk.Label(xstr("register_email"))
1345 label.set_use_underline(True)
1346 labelAlignment.add(label)
1347 table.attach(labelAlignment, 0, 1, row, row+1)
1348
1349 self._emailAddress = gtk.Entry()
1350 self._emailAddress.connect("changed", self._updateButtons)
1351 self._emailAddress.set_tooltip_text(xstr("register_email_tooltip"))
1352 table.attach(self._emailAddress, 1, 2, row, row+1)
1353 label.set_mnemonic_widget(self._emailAddress)
1354
1355 self._emailAddressPublic = gtk.CheckButton(xstr("register_email_public"))
1356 self._emailAddressPublic.set_use_underline(True)
1357 self._emailAddressPublic.set_tooltip_text(xstr("register_email_public_tooltip"))
1358 table.attach(self._emailAddressPublic, 2, 4, row, row+1)
1359
1360 row += 1
1361
1362 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1363 xscale = 0.0, yscale = 0.0)
1364 label = gtk.Label(xstr("register_vatsim_id"))
1365 label.set_use_underline(True)
1366 labelAlignment.add(label)
1367 table.attach(labelAlignment, 0, 1, row, row+1)
1368
1369 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
1370 xscale = 0.0, yscale = 0.0)
1371 self._vatsimID = IntegerEntry()
1372 self._vatsimID.connect("changed", self._updateButtons)
1373 self._vatsimID.set_tooltip_text(xstr("register_vatsim_id_tooltip"))
1374 self._vatsimID.set_width_chars(7)
1375 alignment.add(self._vatsimID)
1376 table.attach(alignment, 1, 2, row, row+1)
1377 label.set_mnemonic_widget(self._vatsimID)
1378
1379 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1380 xscale = 0.0, yscale = 0.0)
1381 label = gtk.Label(xstr("register_ivao_id"))
1382 label.set_use_underline(True)
1383 labelAlignment.add(label)
1384 table.attach(labelAlignment, 2, 3, row, row+1)
1385
1386 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
1387 xscale = 0.0, yscale = 0.0)
1388 self._ivaoID = IntegerEntry()
1389 self._ivaoID.connect("changed", self._updateButtons)
1390 self._ivaoID.set_tooltip_text(xstr("register_ivao_id_tooltip"))
1391 self._ivaoID.set_width_chars(7)
1392 alignment.add(self._ivaoID)
1393 table.attach(alignment, 3, 4, row, row+1)
1394 label.set_mnemonic_widget(self._ivaoID)
1395
1396 row += 1
1397
1398 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1399 xscale = 0.0, yscale = 0.0)
1400 label = gtk.Label(xstr("register_phone_num"))
1401 label.set_use_underline(True)
1402 label.set_use_markup(True)
1403 labelAlignment.add(label)
1404 table.attach(labelAlignment, 0, 1, row, row+1)
1405
1406 self._phoneNumber = gtk.Entry()
1407 self._phoneNumber.set_tooltip_text(xstr("register_phone_num_tooltip"))
1408 table.attach(self._phoneNumber, 1, 2, row, row+1)
1409 label.set_mnemonic_widget(self._phoneNumber)
1410
1411 row += 1
1412
1413 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1414 xscale = 0.0, yscale = 0.0)
1415 label = gtk.Label(xstr("register_nationality"))
1416 label.set_use_underline(True)
1417 label.set_use_markup(True)
1418 labelAlignment.add(label)
1419 table.attach(labelAlignment, 0, 1, row, row+1)
1420
1421
1422 self._nationality = gtk.Entry()
1423 self._nationality.set_tooltip_text(xstr("register_nationality_tooltip"))
1424 table.attach(self._nationality, 1, 2, row, row+1)
1425 label.set_mnemonic_widget(self._nationality)
1426
1427 placeholder = gtk.Label()
1428 placeholder.set_text(xstr("register_password_mismatch"))
1429 placeholder.set_use_markup(True)
1430 placeholder.set_child_visible(False)
1431 placeholder.hide()
1432 table.attach(placeholder, 2, 4, row, row+1)
1433
1434 row += 1
1435
1436 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1437 xscale = 0.0, yscale = 0.0)
1438 label = gtk.Label(xstr("register_password"))
1439 label.set_use_underline(True)
1440 labelAlignment.add(label)
1441 table.attach(labelAlignment, 0, 1, row, row+1)
1442
1443 self._password = gtk.Entry()
1444 self._password.set_visibility(False)
1445 self._password.connect("changed", self._updateButtons)
1446 self._password.set_tooltip_text(xstr("register_password_tooltip"))
1447 table.attach(self._password, 1, 2, row, row+1)
1448 label.set_mnemonic_widget(self._password)
1449
1450 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1451 xscale = 0.0, yscale = 0.0)
1452 self._passwordStatus = gtk.Label()
1453 alignment.add(self._passwordStatus)
1454 table.attach(alignment, 2, 4, row, row+1)
1455
1456 row += 1
1457
1458 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1459 xscale = 0.0, yscale = 0.0)
1460 label = gtk.Label(xstr("register_password2"))
1461 label.set_use_underline(True)
1462 labelAlignment.add(label)
1463 table.attach(labelAlignment, 0, 1, row, row+1)
1464
1465 self._password2 = gtk.Entry()
1466 self._password2.set_visibility(False)
1467 self._password2.connect("changed", self._updateButtons)
1468 self._password2.set_tooltip_text(xstr("register_password2_tooltip"))
1469 table.attach(self._password2, 1, 2, row, row+1)
1470 label.set_mnemonic_widget(self._password2)
1471
1472 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1473 xscale = 0.0, yscale = 0.0)
1474 self._password2Status = gtk.Label()
1475 alignment.add(self._password2Status)
1476 table.attach(alignment, 2, 4, row, row+1)
1477
1478 row += 1
1479
1480 self._rememberButton = gtk.CheckButton(xstr("remember_password"))
1481 self._rememberButton.set_use_underline(True)
1482 self._rememberButton.set_tooltip_text(xstr("login_remember_tooltip"))
1483 table.attach(self._rememberButton, 1, 3, row, row+1)
1484
1485 cancelButton = \
1486 self.addButton(xstr("button_cancel"))
1487 cancelButton.connect("clicked", self._cancelClicked)
1488
1489 self._registerButton = \
1490 self.addButton(xstr("button_register"), default = True,
1491 tooltip = xstr("button_register_tooltip"))
1492 self._registerButton.connect("clicked", self._registerClicked)
1493
1494 self._updateButtons()
1495
1496 @property
1497 def name1(self):
1498 """Get the first name component entered."""
1499 return self._name1.get_text()
1500
1501 @property
1502 def name2(self):
1503 """Get the second name component entered."""
1504 return self._name2.get_text()
1505
1506 @property
1507 def yearOfBirth(self):
1508 """Get the year of birth."""
1509 yearOfBirthText = self._yearOfBirth.get_text()
1510 return int(yearOfBirthText) if yearOfBirthText else 0
1511
1512 @property
1513 def emailAddress(self):
1514 """Get the e-mail address."""
1515 return self._emailAddress.get_text()
1516
1517 @property
1518 def emailAddressPublic(self):
1519 """Get the whether the e-mail address is public."""
1520 return self._emailAddressPublic.get_active()
1521
1522 @property
1523 def vatsimID(self):
1524 """Get the VATSIM ID."""
1525 return self._vatsimID.get_int()
1526
1527 @property
1528 def ivaoID(self):
1529 """Get the IVAO ID."""
1530 return self._ivaoID.get_int()
1531
1532 @property
1533 def phoneNumber(self):
1534 """Get the phone number."""
1535 return self._phoneNumber.get_text()
1536
1537 @property
1538 def nationality(self):
1539 """Get the nationality."""
1540 return self._nationality.get_text()
1541
1542 @property
1543 def password(self):
1544 """Get the password."""
1545 return self._password.get_text()
1546
1547 @property
1548 def rememberPassword(self):
1549 """Get whether the password should be remembered."""
1550 return self._rememberButton.get_active()
1551
1552 def activate(self):
1553 """Setup the route from the booked flight."""
1554 self._yearOfBirth.set_value(0)
1555 self._yearOfBirth.set_text("")
1556 self._updateButtons()
1557
1558 def _updateButtons(self, widget = None):
1559 """Update the sensitive state of the buttons"""
1560 yearOfBirth = self.yearOfBirth
1561
1562 emailAddress = self.emailAddress
1563 emailAddressMatch = RegisterPage._emailAddressRE.match(emailAddress)
1564
1565 vatsimID = self.vatsimID
1566 ivaoID = self.ivaoID
1567
1568 password = self.password
1569 password2 = self._password2.get_text()
1570 if not password:
1571 self._passwordStatus.set_text("")
1572 elif len(password)<5:
1573 self._passwordStatus.set_text(xstr("register_password_too_short"))
1574 else:
1575 self._passwordStatus.set_text(xstr("register_password_ok"))
1576 self._passwordStatus.set_use_markup(True)
1577
1578 if len(password)<5 or not password2:
1579 self._password2Status.set_text("")
1580 elif password!=password2:
1581 self._password2Status.set_text(xstr("register_password_mismatch"))
1582 else:
1583 self._password2Status.set_text(xstr("register_password_ok"))
1584 self._password2Status.set_use_markup(True)
1585
1586 sensitive = \
1587 len(self.name1)>0 and len(self.name2)>0 and \
1588 yearOfBirth>=RegisterPage._minYearOfBirth and \
1589 yearOfBirth<=RegisterPage._maxYearOfBirth and \
1590 emailAddressMatch is not None and \
1591 (vatsimID>=800000 or ivaoID>=100000) and \
1592 len(password)>=5 and password==password2
1593
1594 self._registerButton.set_sensitive(sensitive)
1595
1596 def _cancelClicked(self, button):
1597 """Called when the Cancel button is clicked."""
1598 self.goBack()
1599
1600 def _registerClicked(self, button):
1601 """Called when the Register button is clicked."""
1602 nameOrder = xstr("register_nameorder")
1603
1604 if nameOrder=="eastern":
1605 surName = self.name1
1606 firstName = self.name2
1607 else:
1608 surName = self.name2
1609 firstName = self.name1
1610
1611 nationality = self.nationality.lower()
1612
1613 if getLanguage().lower()=="hu" or nationality.find("hung")!=-1 or \
1614 nationality.find("magyar")!=-1:
1615 requestedNameOrder = "eastern"
1616 else:
1617 requestedNameOrder = "western"
1618
1619 registrationData = web.Registration(surName, firstName,
1620 requestedNameOrder,
1621 self.yearOfBirth,
1622 self.emailAddress,
1623 self.emailAddressPublic,
1624 self.vatsimID, self.ivaoID,
1625 self.phoneNumber, self.nationality,
1626 self.password)
1627 print("Registering with data:")
1628 print(" name:", self.name1, self.name2, registrationData.firstName, registrationData.surName, requestedNameOrder)
1629 print(" yearOfBirth:", self.yearOfBirth, registrationData.yearOfBirth)
1630 print(" emailAddress:", self.emailAddress, registrationData.emailAddress)
1631 print(" emailAddressPublic:", self.emailAddressPublic, registrationData.emailAddressPublic)
1632 print(" vatsimID:", self.vatsimID, registrationData.vatsimID)
1633 print(" ivaoID:", self.ivaoID, registrationData.ivaoID)
1634 print(" phoneNumber:", self.phoneNumber, registrationData.phoneNumber)
1635 print(" nationality:", self.nationality, registrationData.nationality)
1636
1637 gui = self._wizard.gui
1638 gui.beginBusy(xstr("register_busy"))
1639 gui.webHandler.register(self._registerResultCallback, registrationData)
1640
1641 def _registerResultCallback(self, returned, result):
1642 """Called when the registration result is available."""
1643 gobject.idle_add(self._handleRegisterResult, returned, result)
1644
1645 def _handleRegisterResult(self, returned, result):
1646 """Handle the registration result."""
1647 gui = self._wizard.gui
1648
1649 gui.endBusy()
1650
1651 print("Registration result:")
1652 print(" returned:", returned)
1653 if returned:
1654 print(" registered:", result.registered)
1655 if result.registered:
1656 print(" pilotID", result.pilotID)
1657 print(" loggedIn", result.loggedIn)
1658 print(" emailAlreadyRegistered:", result.emailAlreadyRegistered)
1659 print(" invalidData:", result.invalidData)
1660
1661 registrationOK = returned and result.registered
1662
1663 message = xstr("register_ok") if registrationOK \
1664 else xstr("register_failed")
1665 secondaryMessage = None
1666 if registrationOK:
1667 if result.loggedIn:
1668 secondaryMessage = xstr("register_info") % (result.pilotID,)
1669 else:
1670 secondaryMessage = xstr("register_nologin") % (result.pilotID,)
1671 messageType = MESSAGETYPE_INFO
1672
1673 config = gui.config
1674 config.pilotID = result.pilotID
1675 config.rememberPassword = self.rememberPassword
1676 if config.rememberPassword:
1677 config.password = self.password
1678 else:
1679 config.password = ""
1680
1681 config.save()
1682 elif returned and result.emailAlreadyRegistered:
1683 secondaryMessage = xstr("register_email_already")
1684 messageType = MESSAGETYPE_ERROR
1685 elif returned and result.invalidData:
1686 secondaryMessage = xstr("register_invalid_data")
1687 messageType = MESSAGETYPE_ERROR
1688 else:
1689 secondaryMessage = xstr("register_error")
1690 messageType = MESSAGETYPE_ERROR
1691
1692 dialog = gtk.MessageDialog(parent = gui.mainWindow,
1693 type = messageType,
1694 message_format = message)
1695 dialog.set_title(WINDOW_TITLE_BASE + " - " +
1696 xstr("register_result_title"))
1697 dialog.format_secondary_markup(secondaryMessage)
1698
1699 dialog.add_button(xstr("button_ok"), 0)
1700
1701 dialog.run()
1702 dialog.hide()
1703
1704 if registrationOK:
1705 if result.loggedIn:
1706 self._wizard._loginResult = result
1707 self._wizard.nextPage()
1708 else:
1709 self._wizard.jumpPage("login")
1710
1711#-----------------------------------------------------------------------------
1712
1713class StudentPage(Page):
1714 """A page displayed to students after logging in."""
1715 _entryExamStatusQueryInterval = 60*1000
1716
1717 def __init__(self, wizard):
1718 """Construct the student page."""
1719 super(StudentPage, self).__init__(wizard, "student",
1720 xstr("student_title"),
1721 xstr("student_help"))
1722
1723
1724 self._getEntryExamStatusCancelled = False
1725
1726 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1727 xscale = 0.5, yscale = 0.0)
1728
1729 table = gtk.Table(6, 4)
1730 table.set_row_spacings(4)
1731 table.set_col_spacings(0)
1732 table.set_homogeneous(False)
1733 alignment.add(table)
1734 self.setMainWidget(alignment)
1735
1736 row = 0
1737
1738 labelAlignment = gtk.Alignment(xalign=0.0, yalign = 0.5,
1739 xscale=0.0, yscale = 0.0)
1740 label = gtk.Label(xstr("student_entry_exam_status"))
1741 label.set_alignment(0.0, 0.5)
1742 labelAlignment.add(label)
1743 labelAlignment.resize_children()
1744 table.attach(labelAlignment, 0, 1, row, row + 1, xoptions = FILL)
1745
1746 alignment = gtk.Alignment(xalign=0.0, yalign = 0.5,
1747 xscale=1.0, yscale = 0.0)
1748 self._entryExamStatus = gtk.Label()
1749 self._entryExamStatus.set_use_markup(True)
1750 self._entryExamStatus.set_alignment(0.0, 0.5)
1751 alignment.add(self._entryExamStatus)
1752 alignment.resize_children()
1753 table.attach(alignment, 1, 4, row, row + 1)
1754
1755 row += 1
1756
1757 buttonAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
1758 button = self._entryExamButton = gtk.Button(xstr("student_entry_exam"))
1759 button.set_use_underline(True)
1760 button.connect("clicked", self._entryExamClicked)
1761 button.set_tooltip_text(xstr("student_entry_exam_tooltip"))
1762
1763 buttonAlignment.add(button)
1764 table.attach(buttonAlignment, 0, 4, row, row + 1, xoptions = FILL,
1765 ypadding = 4)
1766
1767 row += 3
1768
1769 labelAlignment = gtk.Alignment(xalign=0.0, yalign = 0.5,
1770 xscale=0.0, yscale = 0.0)
1771 label = gtk.Label(xstr("student_check_flight_status"))
1772 labelAlignment.add(label)
1773 table.attach(labelAlignment, 0, 1, row, row + 1, xoptions = FILL)
1774
1775 alignment = gtk.Alignment(xalign=0.0, yalign = 0.5,
1776 xscale=1.0, yscale = 0.0)
1777 self._checkFlightStatus = gtk.Label()
1778 self._checkFlightStatus.set_use_markup(True)
1779 self._checkFlightStatus.set_alignment(0.0, 0.5)
1780 alignment.add(self._checkFlightStatus)
1781 table.attach(alignment, 1, 4, row, row + 1)
1782
1783 row += 1
1784
1785 alignment = gtk.Alignment(xalign=0.0, xscale=1.0)
1786
1787 hbox = gtk.HBox()
1788 hbox.set_homogeneous(False)
1789 hbox.set_spacing(0)
1790
1791 aircraftTypesModel = gtk.ListStore(str, int)
1792 for aircraftType in web.BookedFlight.checkFlightTypes:
1793 aircraftTypesModel.append([aircraftNames[aircraftType],
1794 aircraftType])
1795
1796 aircraftTypeAlignment = gtk.Alignment(xalign = 0.0, xscale = 1.0)
1797
1798 self._aircraftType = gtk.ComboBox(model = aircraftTypesModel)
1799 renderer = gtk.CellRendererText()
1800 self._aircraftType.pack_start(renderer, True)
1801 self._aircraftType.add_attribute(renderer, "text", 0)
1802 self._aircraftType.set_tooltip_text(xstr("student_check_flight_type_tooltip"))
1803 self._aircraftType.set_active(0)
1804
1805 aircraftTypeAlignment.add(self._aircraftType)
1806
1807 hbox.pack_start(aircraftTypeAlignment, False, False, 0)
1808
1809 buttonAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
1810 button = self._checkFlightButton = gtk.Button(xstr("student_check_flight"))
1811 button.set_use_underline(True)
1812 button.connect("clicked", self._checkFlightClicked)
1813 button.set_tooltip_text(xstr("student_check_flight_tooltip"))
1814
1815 hbox.pack_start(button, True, True, 0)
1816
1817 alignment.add(hbox)
1818 table.attach(alignment, 0, 4, row, row + 1, xoptions = FILL)
1819
1820 @property
1821 def aircraftType(self):
1822 """Get the type of the aircraft used to perform the check flight."""
1823 index = self._aircraftType.get_active()
1824 return self._aircraftType.get_model()[index][1]
1825
1826 def activate(self):
1827 """Activate the student page."""
1828 print("StudentPage.activate")
1829 self._getEntryExamStatusCancelled = False
1830
1831 loginResult = self._wizard.loginResult
1832 self._entryExamLink = loginResult.entryExamLink
1833
1834 self._updateEntryExamStatus(loginResult.entryExamPassed)
1835 self._getEntryExamStatus()
1836
1837 # FIXME: call with real value
1838 self._updateCheckFlightStatus(self._wizard.loginResult.checkFlightStatus)
1839
1840 def finalize(self):
1841 """Finalize the page."""
1842 print("StudentPage.finalize")
1843 self._getEntryExamStatusCancelled = True
1844
1845 def _entryExamClicked(self, button):
1846 """Called when the entry exam button is clicked."""
1847 webbrowser.open(self._entryExamLink)
1848
1849 def _getEntryExamStatus(self):
1850 """Initiate the query of the entry exam status after the interval."""
1851 if not self._getEntryExamStatusCancelled:
1852 gobject.timeout_add(StudentPage._entryExamStatusQueryInterval,
1853 lambda: self._wizard.gui.webHandler. \
1854 getEntryExamStatus(self._entryExamStatusCallback))
1855
1856 def _entryExamStatusCallback(self, returned, result):
1857 """Called when the entry exam status is available."""
1858 gobject.idle_add(self._handleEntryExamStatus, returned, result)
1859
1860 def _handleEntryExamStatus(self, returned, result):
1861 """Called when the entry exam status is availabe."""
1862 print("_handleEntryExamStatus", returned, result)
1863 if returned and not self._getEntryExamStatusCancelled:
1864 self._entryExamLink = result.entryExamLink
1865 self._updateEntryExamStatus(result.entryExamPassed)
1866 if result.madeFO:
1867 self._madeFO()
1868 else:
1869 self._getEntryExamStatus()
1870
1871 def _updateEntryExamStatus(self, passed):
1872 """Update the entry exam status display and button."""
1873 self._entryExamStatus.set_text(xstr("student_entry_exam_passed")
1874 if passed else
1875 xstr("student_entry_exam_not_passed"))
1876 self._entryExamStatus.set_use_markup(True)
1877 self._entryExamButton.set_sensitive(not passed)
1878 self._wizard._loginResult.entryExamPassed = passed
1879
1880 def _checkFlightClicked(self, button):
1881 """Called when the check flight button is clicked."""
1882 aircraftType = self.aircraftType
1883 self._wizard._bookedFlight = \
1884 web.BookedFlight.forCheckFlight(aircraftType)
1885 self._wizard.gui.enableFlightInfo(aircraftType)
1886 self._wizard.jumpPage("connect")
1887
1888 def _updateCheckFlightStatus(self, passed):
1889 """Update the status of the check flight."""
1890 self._aircraftType.set_sensitive(not passed)
1891 self._checkFlightStatus.set_text(xstr("student_check_flight_passed")
1892 if passed else
1893 xstr("student_check_flight_not_passed"))
1894 self._checkFlightStatus.set_use_markup(True)
1895 self._checkFlightButton.set_sensitive(not passed)
1896
1897 def _madeFO(self):
1898 """Handle the event when the pilot has become a first officer."""
1899 wizard = self._wizard
1900 loginResult = wizard.loginResult
1901 loginResult.rank = "FO"
1902
1903 gui = wizard.gui
1904
1905 dialog = gtk.MessageDialog(parent = gui.mainWindow,
1906 type = MESSAGETYPE_INFO,
1907 message_format = xstr("student_fo"))
1908
1909 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1910 dialog.set_title(WINDOW_TITLE_BASE)
1911 secondary = xstr("student_fo_secondary")
1912 dialog.format_secondary_markup(secondary)
1913 dialog.run()
1914 dialog.hide()
1915
1916 gui.reset()
1917
1918#-----------------------------------------------------------------------------
1919
1920class ConnectPage(Page):
1921 """Page which displays the departure airport and gate (if at LHBP)."""
1922 def __init__(self, wizard):
1923 """Construct the connect page."""
1924 help = "Load the aircraft below into the simulator and park it\n" \
1925 "at the given airport, at the gate below, if present.\n\n" \
1926 "Then press the Connect button to connect to the simulator."
1927 completedHelp = "The basic data of your flight can be read below."
1928 super(ConnectPage, self).__init__(wizard, "connect",
1929 xstr("connect_title"),
1930 xstr("connect_help"),
1931 completedHelp = xstr("connect_chelp"))
1932
1933 self._selectSimulator = os.name=="nt" or "FORCE_SELECT_SIM" in os.environ
1934
1935 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1936 xscale = 0.0, yscale = 0.0)
1937
1938 table = gtk.Table(7 if self._selectSimulator else 5, 2)
1939 table.set_row_spacings(4)
1940 table.set_col_spacings(16)
1941 table.set_homogeneous(True)
1942 alignment.add(table)
1943 self.setMainWidget(alignment)
1944
1945 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
1946 label = gtk.Label(xstr("connect_flightno"))
1947 labelAlignment.add(label)
1948 table.attach(labelAlignment, 0, 1, 0, 1)
1949
1950 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
1951 self._flightNumber = gtk.Label()
1952 self._flightNumber.set_width_chars(9)
1953 self._flightNumber.set_alignment(0.0, 0.5)
1954 labelAlignment.add(self._flightNumber)
1955 table.attach(labelAlignment, 1, 2, 0, 1)
1956
1957 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
1958 label = gtk.Label(xstr("connect_acft"))
1959 labelAlignment.add(label)
1960 table.attach(labelAlignment, 0, 1, 1, 2)
1961
1962 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
1963 self._aircraft = gtk.Label()
1964 self._aircraft.set_width_chars(25)
1965 self._aircraft.set_alignment(0.0, 0.5)
1966 labelAlignment.add(self._aircraft)
1967 table.attach(labelAlignment, 1, 2, 1, 2)
1968
1969 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
1970 label = gtk.Label(xstr("connect_tailno"))
1971 labelAlignment.add(label)
1972 table.attach(labelAlignment, 0, 1, 2, 3)
1973
1974 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
1975 self._tailNumber = gtk.Label()
1976 self._tailNumber.set_width_chars(10)
1977 self._tailNumber.set_alignment(0.0, 0.5)
1978 labelAlignment.add(self._tailNumber)
1979 table.attach(labelAlignment, 1, 2, 2, 3)
1980
1981 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
1982 label = gtk.Label(xstr("connect_airport"))
1983 labelAlignment.add(label)
1984 table.attach(labelAlignment, 0, 1, 3, 4)
1985
1986 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
1987 self._departureICAO = gtk.Label()
1988 self._departureICAO.set_width_chars(6)
1989 self._departureICAO.set_alignment(0.0, 0.5)
1990 labelAlignment.add(self._departureICAO)
1991 table.attach(labelAlignment, 1, 2, 3, 4)
1992
1993 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
1994 label = gtk.Label(xstr("connect_gate"))
1995 labelAlignment.add(label)
1996 table.attach(labelAlignment, 0, 1, 4, 5)
1997
1998 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
1999 self._departureGate = gtk.Label()
2000 self._departureGate.set_width_chars(5)
2001 self._departureGate.set_alignment(0.0, 0.5)
2002 labelAlignment.add(self._departureGate)
2003 table.attach(labelAlignment, 1, 2, 4, 5)
2004
2005 if self._selectSimulator:
2006 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0, yalign=0.5)
2007 label = gtk.Label(xstr("connect_sim"))
2008 labelAlignment.add(label)
2009 table.attach(labelAlignment, 0, 1, 5, 7)
2010
2011 selectAlignment = gtk.Alignment(xalign=0.0, xscale=0.0, yalign=0.5)
2012
2013 selectBox = gtk.HBox()
2014 if pygobject:
2015 self._selectMSFS = \
2016 gtk.RadioButton.new_with_mnemonic_from_widget(None,
2017 xstr("connect_sim_msfs"))
2018 else:
2019 self._selectMSFS = gtk.RadioButton(None,
2020 xstr("connect_sim_msfs"))
2021
2022 selectBox.pack_start(self._selectMSFS, False, False, 0);
2023
2024 if pygobject:
2025 self._selectXPlane = \
2026 gtk.RadioButton.new_with_mnemonic_from_widget(self._selectMSFS,
2027 xstr("connect_sim_xplane"))
2028 else:
2029 self._selectXPlane = gtk.RadioButton(self._selectMSFS,
2030 xstr("connect_sim_xplane"))
2031
2032 selectBox.pack_start(self._selectXPlane, False, False, 8);
2033
2034 selectAlignment.add(selectBox)
2035 table.attach(selectAlignment, 1, 2, 5, 7)
2036
2037
2038 self.addCancelFlightButton()
2039
2040 self._previousButton = \
2041 self.addPreviousButton(clicked = self._backClicked)
2042
2043 self._button = self.addButton(xstr("button_connect"), default = True,
2044 tooltip = xstr("button_connect_tooltip"))
2045 self._clickedID = self._button.connect("clicked", self._connectClicked)
2046
2047 def activate(self):
2048 """Setup the departure information."""
2049 self._button.set_label(xstr("button_connect"))
2050 self._button.set_use_underline(True)
2051 self._button.set_tooltip_text(xstr("button_connect_tooltip"))
2052 self._button.disconnect(self._clickedID)
2053 self._clickedID = self._button.connect("clicked", self._connectClicked)
2054
2055 bookedFlight = self._wizard._bookedFlight
2056
2057 self._flightNumber.set_markup("<b>" + bookedFlight.callsign + "</b>")
2058
2059 aircraftType = aircraftNames[bookedFlight.aircraftType]
2060 self._aircraft.set_markup("<b>" + aircraftType + "</b>")
2061
2062 self._tailNumber.set_markup("<b>" + bookedFlight.tailNumber + "</b>")
2063
2064 icao = bookedFlight.departureICAO
2065 self._departureICAO.set_markup("<b>" + icao + "</b>")
2066 gate = self._wizard._departureGate
2067 if gate!="-":
2068 gate = "<b>" + gate + "</b>"
2069 self._departureGate.set_markup(gate)
2070
2071 if self._selectSimulator:
2072 config = self._wizard.gui.config
2073 self._selectMSFS.set_active(config.defaultMSFS)
2074 self._selectXPlane.set_active(not config.defaultMSFS)
2075
2076 self._previousButton.set_sensitive(not self._wizard.entranceExam)
2077
2078 def finalize(self):
2079 """Finalize the page."""
2080 self._button.set_label(xstr("button_next"))
2081 self._button.set_use_underline(True)
2082 self._button.set_tooltip_text(xstr("button_next_tooltip"))
2083 self._button.disconnect(self._clickedID)
2084 self._clickedID = self._button.connect("clicked", self._forwardClicked)
2085
2086 def _backClicked(self, button):
2087 """Called when the Back button is pressed."""
2088 self.goBack()
2089
2090 def _connectClicked(self, button):
2091 """Called when the Connect button is pressed."""
2092 if self._selectSimulator:
2093 simulatorType = const.SIM_MSFS9 if self._selectMSFS.get_active() \
2094 else const.SIM_XPLANE10
2095 else:
2096 simulatorType = const.SIM_MSFS9 if os.name=="nt" \
2097 else const.SIM_XPLANE10
2098
2099 config = self._wizard.gui.config
2100 config.defaultMSFS = simulatorType == const.SIM_MSFS9
2101 config.save()
2102
2103 self._wizard._connectSimulator(simulatorType)
2104
2105 def _forwardClicked(self, button):
2106 """Called when the Forward button is pressed."""
2107 self._wizard.nextPage()
2108
2109#-----------------------------------------------------------------------------
2110
2111class PayloadPage(Page):
2112 """Page to allow setting up the payload."""
2113 def __init__(self, wizard):
2114 """Construct the page."""
2115 super(PayloadPage, self).__init__(wizard, "payload",
2116 xstr("payload_title"),
2117 xstr("payload_help"),
2118 completedHelp = xstr("payload_chelp"))
2119
2120 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2121 xscale = 0.0, yscale = 0.0)
2122
2123 table = gtk.Table(7, 3)
2124 table.set_row_spacings(4)
2125 table.set_col_spacings(16)
2126 table.set_homogeneous(False)
2127 alignment.add(table)
2128 self.setMainWidget(alignment)
2129
2130 label = gtk.Label(xstr("payload_crew"))
2131 label.set_use_underline(True)
2132 label.set_alignment(0.0, 0.5)
2133 table.attach(label, 0, 1, 0, 1)
2134
2135 self._numCrew = IntegerEntry(defaultValue = 0)
2136 self._numCrew.set_width_chars(6)
2137 self._numCrew.connect("integer-changed", self._weightChanged)
2138 self._numCrew.set_tooltip_text(xstr("payload_crew_tooltip"))
2139 table.attach(self._numCrew, 1, 2, 0, 1)
2140 label.set_mnemonic_widget(self._numCrew)
2141
2142 label = gtk.Label(xstr("payload_pax"))
2143 label.set_use_underline(True)
2144 label.set_alignment(0.0, 0.5)
2145 table.attach(label, 0, 1, 1, 2)
2146
2147 self._numPassengers = IntegerEntry(defaultValue = 0)
2148 self._numPassengers.set_width_chars(6)
2149 self._numPassengers.connect("integer-changed", self._weightChanged)
2150 self._numPassengers.set_tooltip_text(xstr("payload_pax_tooltip"))
2151 table.attach(self._numPassengers, 1, 2, 1, 2)
2152 label.set_mnemonic_widget(self._numPassengers)
2153
2154 label = gtk.Label(xstr("payload_bag"))
2155 label.set_use_underline(True)
2156 label.set_alignment(0.0, 0.5)
2157 table.attach(label, 0, 1, 2, 3)
2158
2159 self._bagWeight = IntegerEntry(defaultValue = 0)
2160 self._bagWeight.set_width_chars(6)
2161 self._bagWeight.connect("integer-changed", self._weightChanged)
2162 self._bagWeight.set_tooltip_text(xstr("payload_bag_tooltip"))
2163 table.attach(self._bagWeight, 1, 2, 2, 3)
2164 label.set_mnemonic_widget(self._bagWeight)
2165
2166 table.attach(gtk.Label("kg"), 2, 3, 2, 3)
2167
2168 label = gtk.Label(xstr("payload_cargo"))
2169 label.set_use_underline(True)
2170 label.set_alignment(0.0, 0.5)
2171 table.attach(label, 0, 1, 3, 4)
2172
2173 self._cargoWeight = IntegerEntry(defaultValue = 0)
2174 self._cargoWeight.set_width_chars(6)
2175 self._cargoWeight.connect("integer-changed", self._weightChanged)
2176 self._cargoWeight.set_tooltip_text(xstr("payload_cargo_tooltip"))
2177 table.attach(self._cargoWeight, 1, 2, 3, 4)
2178 label.set_mnemonic_widget(self._cargoWeight)
2179
2180 table.attach(gtk.Label("kg"), 2, 3, 3, 4)
2181
2182 label = gtk.Label(xstr("payload_mail"))
2183 label.set_use_underline(True)
2184 label.set_alignment(0.0, 0.5)
2185 table.attach(label, 0, 1, 4, 5)
2186
2187 self._mailWeight = IntegerEntry(defaultValue = 0)
2188 self._mailWeight.set_width_chars(6)
2189 self._mailWeight.connect("integer-changed", self._weightChanged)
2190 self._mailWeight.set_tooltip_text(xstr("payload_mail_tooltip"))
2191 table.attach(self._mailWeight, 1, 2, 4, 5)
2192 label.set_mnemonic_widget(self._mailWeight)
2193
2194 table.attach(gtk.Label("kg"), 2, 3, 4, 5)
2195
2196 label = gtk.Label("<b>" + xstr("payload_zfw") + "</b>")
2197 label.set_alignment(0.0, 0.5)
2198 label.set_use_markup(True)
2199 table.attach(label, 0, 1, 5, 6)
2200
2201 self._calculatedZFW = gtk.Label()
2202 self._calculatedZFW.set_width_chars(6)
2203 self._calculatedZFW.set_alignment(1.0, 0.5)
2204 table.attach(self._calculatedZFW, 1, 2, 5, 6)
2205
2206 table.attach(gtk.Label("kg"), 2, 3, 5, 6)
2207
2208 self._zfwButton = gtk.Button(xstr("payload_fszfw"))
2209 self._zfwButton.set_use_underline(True)
2210 self._zfwButton.connect("clicked", self._zfwRequested)
2211 self._zfwButton.set_tooltip_text(xstr("payload_fszfw_tooltip"))
2212 table.attach(self._zfwButton, 0, 1, 6, 7)
2213
2214 self._simulatorZFW = gtk.Label("-")
2215 self._simulatorZFW.set_width_chars(6)
2216 self._simulatorZFW.set_alignment(1.0, 0.5)
2217 table.attach(self._simulatorZFW, 1, 2, 6, 7)
2218 self._simulatorZFWValue = None
2219
2220 table.attach(gtk.Label("kg"), 2, 3, 6, 7)
2221
2222 self.addCancelFlightButton()
2223 self._backButton = self.addPreviousButton(clicked = self._backClicked)
2224 self._button = self.addNextButton(clicked = self._forwardClicked)
2225
2226 @property
2227 def numCrew(self):
2228 """The number of the crew members on the flight."""
2229 return self._numCrew.get_int()
2230
2231 @property
2232 def numPassengers(self):
2233 """The number of the passengers on the flight."""
2234 return self._numPassengers.get_int()
2235
2236 @property
2237 def bagWeight(self):
2238 """Get the bag weight entered."""
2239 return self._bagWeight.get_int()
2240
2241 @property
2242 def cargoWeight(self):
2243 """Get the cargo weight entered."""
2244 return self._cargoWeight.get_int()
2245
2246 @property
2247 def mailWeight(self):
2248 """Get the bag weight entered."""
2249 return self._mailWeight.get_int()
2250
2251 def activate(self):
2252 """Setup the information."""
2253 bookedFlight = self._wizard._bookedFlight
2254
2255 self._numCrew.set_int(bookedFlight.numCrew)
2256 self._numCrew.set_sensitive(True)
2257 self._numPassengers.set_int(bookedFlight.numPassengers)
2258 self._numPassengers.set_sensitive(True)
2259
2260 self._bagWeight.set_int(bookedFlight.bagWeight)
2261 self._bagWeight.set_sensitive(True)
2262 self._cargoWeight.set_int(bookedFlight.cargoWeight)
2263 self._cargoWeight.set_sensitive(True)
2264 self._mailWeight.set_int(bookedFlight.mailWeight)
2265 self._mailWeight.set_sensitive(True)
2266
2267 self._simulatorZFW.set_text("-")
2268 self._simulatorZFWValue = None
2269 self._zfwButton.set_sensitive(True)
2270 self._updateCalculatedZFW()
2271
2272 def finalize(self):
2273 """Finalize the payload page."""
2274 self._numCrew.set_sensitive(False)
2275 self._numPassengers.set_sensitive(False)
2276 self._bagWeight.set_sensitive(False)
2277 self._cargoWeight.set_sensitive(False)
2278 self._mailWeight.set_sensitive(False)
2279 self._wizard.gui.initializeWeightHelp()
2280
2281 def calculateZFW(self):
2282 """Calculate the ZFW value."""
2283 zfw = self._wizard.gui._flight.aircraft.dow
2284 zfw += (self._numCrew.get_int() + self._numPassengers.get_int()) * 82
2285 zfw += self._bagWeight.get_int()
2286 zfw += self._cargoWeight.get_int()
2287 zfw += self._mailWeight.get_int()
2288 return zfw
2289
2290 def _updateCalculatedZFW(self):
2291 """Update the calculated ZFW"""
2292 zfw = self.calculateZFW()
2293
2294 markupBegin = "<b>"
2295 markupEnd = "</b>"
2296 if self._simulatorZFWValue is not None and \
2297 PayloadChecker.isZFWFaulty(self._simulatorZFWValue, zfw):
2298 markupBegin += '<span foreground="red">'
2299 markupEnd = "</span>" + markupEnd
2300 self._calculatedZFW.set_markup(markupBegin + str(zfw) + markupEnd)
2301
2302 def _weightChanged(self, entry, weight):
2303 """Called when one of the weight values or humanm counts has changed."""
2304 self._updateCalculatedZFW()
2305
2306 def _zfwRequested(self, button):
2307 """Called when the ZFW is requested from the simulator."""
2308 self._zfwButton.set_sensitive(False)
2309 self._backButton.set_sensitive(False)
2310 self._button.set_sensitive(False)
2311 gui = self._wizard.gui
2312 gui.beginBusy(xstr("payload_zfw_busy"))
2313 gui.simulator.requestZFW(self._handleZFW)
2314
2315 def _handleZFW(self, zfw):
2316 """Called when the ZFW value is retrieved."""
2317 gobject.idle_add(self._processZFW, zfw)
2318
2319 def _processZFW(self, zfw):
2320 """Process the given ZFW value received from the simulator."""
2321 self._wizard.gui.endBusy()
2322 self._zfwButton.set_sensitive(True)
2323 self._backButton.set_sensitive(True)
2324 self._button.set_sensitive(True)
2325 self._simulatorZFWValue = zfw
2326 self._simulatorZFW.set_text("%.0f" % (zfw,))
2327 self._updateCalculatedZFW()
2328
2329 def _forwardClicked(self, button):
2330 """Called when the forward button is clicked."""
2331 self._wizard.nextPage()
2332
2333 def _backClicked(self, button):
2334 """Called when the Back button is pressed."""
2335 self.goBack()
2336
2337#-----------------------------------------------------------------------------
2338
2339class TimePage(Page):
2340 """Page displaying the departure and arrival times and allows querying the
2341 current time from the flight simulator."""
2342 def __init__(self, wizard):
2343 super(TimePage, self).__init__(wizard, "time",
2344 xstr("time_title"),
2345 xstr("time_help"),
2346 completedHelp = xstr("time_chelp"))
2347
2348 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2349 xscale = 0.0, yscale = 0.0)
2350
2351 table = gtk.Table(3, 2)
2352 table.set_row_spacings(4)
2353 table.set_col_spacings(16)
2354 table.set_homogeneous(False)
2355 alignment.add(table)
2356 self.setMainWidget(alignment)
2357
2358 label = gtk.Label(xstr("time_departure"))
2359 label.set_alignment(0.0, 0.5)
2360 table.attach(label, 0, 1, 0, 1)
2361
2362 self._departure = gtk.Label()
2363 self._departure.set_alignment(0.0, 0.5)
2364 table.attach(self._departure, 1, 2, 0, 1)
2365
2366 label = gtk.Label(xstr("time_arrival"))
2367 label.set_alignment(0.0, 0.5)
2368 table.attach(label, 0, 1, 1, 2)
2369
2370 self._arrival = gtk.Label()
2371 self._arrival.set_alignment(0.0, 0.5)
2372 table.attach(self._arrival, 1, 2, 1, 2)
2373
2374 self._timeButton = gtk.Button(xstr("time_fs"))
2375 self._timeButton.set_use_underline(True)
2376 self._timeButton.set_tooltip_text(xstr("time_fs_tooltip"))
2377 self._timeButton.connect("clicked", self._timeRequested)
2378 table.attach(self._timeButton, 0, 1, 2, 3)
2379
2380 self._simulatorTime = gtk.Label("-")
2381 self._simulatorTime.set_alignment(0.0, 0.5)
2382 table.attach(self._simulatorTime, 1, 2, 2, 3)
2383
2384 self.addCancelFlightButton()
2385
2386 self._backButton = self.addPreviousButton(clicked = self._backClicked)
2387 self._button = self.addNextButton(clicked = self._forwardClicked)
2388
2389 def activate(self):
2390 """Activate the page."""
2391 self._timeButton.set_sensitive(True)
2392 bookedFlight = self._wizard._bookedFlight
2393 self._departure.set_text(str(bookedFlight.departureTime.time()))
2394 self._arrival.set_text(str(bookedFlight.arrivalTime.time()))
2395 self._simulatorTime.set_text("-")
2396
2397 def _timeRequested(self, button):
2398 """Request the time from the simulator."""
2399 self._timeButton.set_sensitive(False)
2400 self._backButton.set_sensitive(False)
2401 self._button.set_sensitive(False)
2402 self._wizard.gui.beginBusy(xstr("time_busy"))
2403 self._wizard.gui.simulator.requestTime(self._handleTime)
2404
2405 def _handleTime(self, timestamp):
2406 """Handle the result of a time retrieval."""
2407 gobject.idle_add(self._processTime, timestamp)
2408
2409 def _processTime(self, timestamp):
2410 """Process the given time."""
2411 self._wizard.gui.endBusy()
2412 self._timeButton.set_sensitive(True)
2413 self._backButton.set_sensitive(True)
2414 self._button.set_sensitive(True)
2415 tm = time.gmtime(timestamp)
2416 t = datetime.time(tm.tm_hour, tm.tm_min, tm.tm_sec)
2417 self._simulatorTime.set_text(str(t))
2418
2419 ts = tm.tm_hour * 3600 + tm.tm_min * 60 + tm.tm_sec
2420 dt = self._wizard._bookedFlight.departureTime.time()
2421 dts = dt.hour * 3600 + dt.minute * 60 + dt.second
2422 diff = dts-ts
2423
2424 markupBegin = ""
2425 markupEnd = ""
2426 if diff < 0:
2427 markupBegin = '<b><span foreground="red">'
2428 markupEnd = '</span></b>'
2429 elif diff < 3*60 or diff > 30*60:
2430 markupBegin = '<b><span foreground="orange">'
2431 markupEnd = '</span></b>'
2432
2433 self._departure.set_markup(markupBegin + str(dt) + markupEnd)
2434
2435 def _backClicked(self, button):
2436 """Called when the Back button is pressed."""
2437 self.goBack()
2438
2439 def _forwardClicked(self, button):
2440 """Called when the forward button is clicked."""
2441 if not self._completed:
2442 gui = self._wizard.gui
2443 gui.beginBusy(xstr("fuel_get_busy"))
2444
2445 gui.simulator.getFuel(self._handleFuel)
2446 else:
2447 self._wizard.nextPage()
2448
2449 def _handleFuel(self, fuelData):
2450 """Callback for the fuel query operation."""
2451 gobject.idle_add(self._processFuel, fuelData)
2452
2453 def _processFuel(self, fuelData):
2454 """Process the given fuel data."""
2455 self._wizard.gui.endBusy()
2456 self._wizard._fuelData = fuelData
2457 self._wizard.nextPage()
2458
2459#-----------------------------------------------------------------------------
2460
2461class RoutePage(Page):
2462 """The page containing the route and the flight level."""
2463 def __init__(self, wizard):
2464 """Construct the page."""
2465 super(RoutePage, self).__init__(wizard, "route",
2466 xstr("route_title"),
2467 xstr("route_help"),
2468 completedHelp = xstr("route_chelp"))
2469
2470 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2471 xscale = 0.0, yscale = 0.0)
2472
2473 mainBox = gtk.VBox()
2474 alignment.add(mainBox)
2475 self.setMainWidget(alignment)
2476
2477 levelBox = gtk.HBox()
2478
2479 label = gtk.Label(xstr("route_level"))
2480 label.set_use_underline(True)
2481 levelBox.pack_start(label, True, True, 0)
2482
2483 self._cruiseLevel = gtk.SpinButton()
2484 self._cruiseLevel.set_increments(step = 10, page = 100)
2485 self._cruiseLevel.set_range(min = 0, max = 500)
2486 self._cruiseLevel.set_tooltip_text(xstr("route_level_tooltip"))
2487 self._cruiseLevel.set_numeric(True)
2488 self._cruiseLevel.connect("changed", self._cruiseLevelChanged)
2489 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
2490 label.set_mnemonic_widget(self._cruiseLevel)
2491
2492 levelBox.pack_start(self._cruiseLevel, False, False, 8)
2493
2494 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
2495 xscale = 0.0, yscale = 0.0)
2496 alignment.add(levelBox)
2497
2498 mainBox.pack_start(alignment, False, False, 0)
2499
2500
2501 routeBox = gtk.VBox()
2502
2503 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
2504 xscale = 0.0, yscale = 0.0)
2505 label = gtk.Label(xstr("route_route"))
2506 label.set_use_underline(True)
2507 alignment.add(label)
2508 routeBox.pack_start(alignment, True, True, 0)
2509
2510 routeWindow = gtk.ScrolledWindow()
2511 routeWindow.set_size_request(400, 80)
2512 routeWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
2513 else gtk.SHADOW_IN)
2514 routeWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
2515 else gtk.POLICY_AUTOMATIC,
2516 gtk.PolicyType.AUTOMATIC if pygobject
2517 else gtk.POLICY_AUTOMATIC)
2518
2519 self._uppercasingRoute = False
2520
2521 self._route = gtk.TextView()
2522 self._route.set_tooltip_text(xstr("route_route_tooltip"))
2523 self._route.set_wrap_mode(WRAP_WORD)
2524 self._route.get_buffer().connect("changed", self._routeChanged)
2525 self._route.get_buffer().connect_after("insert-text", self._routeInserted)
2526 routeWindow.add(self._route)
2527
2528 label.set_mnemonic_widget(self._route)
2529 routeBox.pack_start(routeWindow, True, True, 0)
2530
2531 mainBox.pack_start(routeBox, True, True, 8)
2532
2533 alternateBox = gtk.HBox()
2534
2535 label = gtk.Label(xstr("route_altn"))
2536 label.set_use_underline(True)
2537 alternateBox.pack_start(label, True, True, 0)
2538
2539 self._alternate = gtk.Entry()
2540 self._alternate.set_width_chars(6)
2541 self._alternate.connect("changed", self._alternateChanged)
2542 self._alternate.set_tooltip_text(xstr("route_altn_tooltip"))
2543 label.set_mnemonic_widget(self._alternate)
2544
2545 alternateBox.pack_start(self._alternate, False, False, 8)
2546
2547 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
2548 xscale = 0.0, yscale = 0.0)
2549 alignment.add(alternateBox)
2550
2551 mainBox.pack_start(alignment, False, False, 0)
2552
2553 self.addCancelFlightButton()
2554
2555 self._backButton = self.addPreviousButton(clicked = self._backClicked)
2556 self._button = self.addNextButton(clicked = self._forwardClicked)
2557
2558 @property
2559 def filedCruiseLevel(self):
2560 """Get the filed cruise level."""
2561 return self._cruiseLevel.get_value_as_int()
2562
2563 @property
2564 def route(self):
2565 """Get the route."""
2566 return self._getRoute()
2567
2568 @property
2569 def alternate(self):
2570 """Get the ICAO code of the alternate airport."""
2571 return self._alternate.get_text()
2572
2573 def activate(self):
2574 """Setup the route from the booked flight."""
2575 self._cruiseLevel.set_value(0)
2576 self._cruiseLevel.set_text("")
2577 self._route.get_buffer().set_text(self._wizard._bookedFlight.route)
2578 self._alternate.set_text("")
2579 self._updateForwardButton()
2580
2581 def _getRoute(self):
2582 """Get the text of the route."""
2583 buffer = self._route.get_buffer()
2584 return buffer.get_text(buffer.get_start_iter(),
2585 buffer.get_end_iter(), True)
2586
2587 def _updateForwardButton(self):
2588 """Update the sensitivity of the forward button."""
2589 cruiseLevelText = self._cruiseLevel.get_text()
2590 cruiseLevel = int(cruiseLevelText) if cruiseLevelText else 0
2591 alternate = self._alternate.get_text()
2592 self._button.set_sensitive(cruiseLevel>=50 and self._getRoute()!="" and
2593 (len(alternate)==4 or self._wizard.entranceExam))
2594
2595 def _cruiseLevelChanged(self, *arg):
2596 """Called when the cruise level has changed."""
2597 self._updateForwardButton()
2598
2599 def _routeChanged(self, textBuffer):
2600 """Called when the route has changed."""
2601 if not self._uppercasingRoute:
2602 self._updateForwardButton()
2603
2604 def _routeInserted(self, textBuffer, iter, text, length):
2605 """Called when new characters are inserted into the route.
2606
2607 It uppercases all characters."""
2608 if not self._uppercasingRoute:
2609 self._uppercasingRoute = True
2610
2611 iter1 = iter.copy()
2612 iter1.backward_chars(length)
2613 textBuffer.delete(iter, iter1)
2614
2615 textBuffer.insert(iter, text.upper())
2616
2617 self._uppercasingRoute = False
2618
2619 def _alternateChanged(self, entry):
2620 """Called when the alternate airport has changed."""
2621 entry.set_text(entry.get_text().upper())
2622 self._updateForwardButton()
2623
2624 def _backClicked(self, button):
2625 """Called when the Back button is pressed."""
2626 self.goBack()
2627
2628 def _forwardClicked(self, button):
2629 """Called when the Forward button is clicked."""
2630 if self._wizard.gui.flight.aircraft.simBriefData is None:
2631 self._wizard.usingSimBrief = False
2632 if self._wizard.gui.config.useSimBrief and \
2633 self._wizard.usingSimBrief is not False:
2634 self._wizard.jumpPage("simbrief_setup")
2635 else:
2636 self._wizard.usingSimBrief = False
2637 self._wizard.jumpPage("fuel")
2638
2639#-----------------------------------------------------------------------------
2640
2641class SimBriefCredentialsDialog(gtk.Dialog):
2642 """A dialog window to ask for SimBrief credentials."""
2643 def __init__(self, gui, userName, password, rememberPassword):
2644 """Construct the dialog."""
2645 super(SimBriefCredentialsDialog, self).__init__(WINDOW_TITLE_BASE + " - " +
2646 xstr("simbrief_credentials_title"),
2647 gui.mainWindow,
2648 DIALOG_MODAL)
2649 self.add_button(xstr("button_cancel"), RESPONSETYPE_CANCEL)
2650 self.add_button(xstr("button_ok"), RESPONSETYPE_OK)
2651
2652 contentArea = self.get_content_area()
2653
2654 contentAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2655 xscale = 0.0, yscale = 0.0)
2656 contentAlignment.set_padding(padding_top = 4, padding_bottom = 16,
2657 padding_left = 8, padding_right = 8)
2658
2659 contentArea.pack_start(contentAlignment, False, False, 0)
2660
2661 contentVBox = gtk.VBox()
2662 contentAlignment.add(contentVBox)
2663
2664 label = gtk.Label(xstr("simbrief_login_failed"))
2665 label.set_alignment(0.0, 0.0)
2666
2667 contentVBox.pack_start(label, False, False, 0)
2668
2669 tableAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2670 xscale = 0.0, yscale = 0.0)
2671 tableAlignment.set_padding(padding_top = 24, padding_bottom = 0,
2672 padding_left = 0, padding_right = 0)
2673
2674 table = gtk.Table(3, 2)
2675 table.set_row_spacings(4)
2676 table.set_col_spacings(16)
2677 table.set_homogeneous(False)
2678
2679 tableAlignment.add(table)
2680 contentVBox.pack_start(tableAlignment, True, True, 0)
2681
2682 label = gtk.Label(xstr("simbrief_username"))
2683 label.set_use_underline(True)
2684 label.set_alignment(0.0, 0.5)
2685 table.attach(label, 0, 1, 0, 1)
2686
2687 self._userName = gtk.Entry()
2688 self._userName.set_width_chars(16)
2689 #self._userName.connect("changed",
2690 # lambda button: self._updateForwardButton())
2691 self._userName.set_tooltip_text(xstr("simbrief_username_tooltip"))
2692 self._userName.set_text(userName)
2693 table.attach(self._userName, 1, 2, 0, 1)
2694 label.set_mnemonic_widget(self._userName)
2695
2696 label = gtk.Label(xstr("simbrief_password"))
2697 label.set_use_underline(True)
2698 label.set_alignment(0.0, 0.5)
2699 table.attach(label, 0, 1, 1, 2)
2700
2701 self._password = gtk.Entry()
2702 self._password.set_visibility(False)
2703 #self._password.connect("changed",
2704 # lambda button: self._updateForwardButton())
2705 self._password.set_tooltip_text(xstr("simbrief_password_tooltip"))
2706 self._password.set_text(password)
2707 table.attach(self._password, 1, 2, 1, 2)
2708 label.set_mnemonic_widget(self._password)
2709
2710 self._rememberButton = gtk.CheckButton(xstr("simbrief_remember_password"))
2711 self._rememberButton.set_use_underline(True)
2712 self._rememberButton.set_tooltip_text(xstr("simbrief_remember_tooltip"))
2713 self._rememberButton.set_active(rememberPassword)
2714 table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
2715
2716 @property
2717 def userName(self):
2718 """Get the user name entered."""
2719 return self._userName.get_text()
2720
2721 @property
2722 def password(self):
2723 """Get the password entered."""
2724 return self._password.get_text()
2725
2726 @property
2727 def rememberPassword(self):
2728 """Get whether the password is to be remembered."""
2729 return self._rememberButton.get_active()
2730
2731 def run(self):
2732 """Run the dialog."""
2733 self.show_all()
2734
2735 response = super(SimBriefCredentialsDialog, self).run()
2736
2737 self.hide()
2738
2739 return response
2740
2741#-----------------------------------------------------------------------------
2742
2743class SimBriefSetupPage(Page):
2744 """Page for setting up some parameters for SimBrief."""
2745 monthNum2Name = [
2746 "JAN",
2747 "FEB",
2748 "MAR",
2749 "APR",
2750 "MAY",
2751 "JUN",
2752 "JUL",
2753 "AUG",
2754 "SEP",
2755 "OCT",
2756 "NOV",
2757 "DEC"
2758 ]
2759
2760 progress2Message = {
2761 cef.SIMBRIEF_PROGRESS_SEARCHING_BROWSER: "simbrief_progress_searching_browser",
2762 cef.SIMBRIEF_PROGRESS_LOADING_FORM: "simbrief_progress_loading_form",
2763 cef.SIMBRIEF_PROGRESS_FILLING_FORM: "simbrief_progress_filling_form",
2764 cef.SIMBRIEF_PROGRESS_WAITING_LOGIN: "simbrief_progress_waiting_login",
2765 cef.SIMBRIEF_PROGRESS_LOGGING_IN: "simbrief_progress_logging_in",
2766 cef.SIMBRIEF_PROGRESS_WAITING_RESULT: "simbrief_progress_waiting_result",
2767 cef.SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING: "simbrief_progress_retrieving_briefing"
2768 }
2769
2770 result2Message = {
2771 cef.SIMBRIEF_RESULT_ERROR_OTHER: "simbrief_result_error_other",
2772 cef.SIMBRIEF_RESULT_ERROR_NO_FORM: "simbrief_result_error_no_form",
2773 cef.SIMBRIEF_RESULT_ERROR_NO_POPUP: "simbrief_result_error_no_popup",
2774 cef.SIMBRIEF_RESULT_ERROR_LOGIN_FAILED: "simbrief_result_error_login_failed"
2775 }
2776
2777 @staticmethod
2778 def getHTMLFilePath():
2779 """Get the path of the HTML file to contain the generated flight
2780 plan."""
2781 if os.name=="nt":
2782 return os.path.join(tempfile.gettempdir(),
2783 "mlx_simbrief" +
2784 (".secondary" if secondaryInstallation else "") +
2785 ".html")
2786 else:
2787 import pwd
2788 return os.path.join(tempfile.gettempdir(),
2789 "mlx_simbrief." + pwd.getpwuid(os.getuid())[0] + "" +
2790 (".secondary" if secondaryInstallation else "") +
2791 ".html")
2792
2793 def __init__(self, wizard):
2794 """Construct the setup page."""
2795
2796 super(SimBriefSetupPage, self).__init__(wizard, "simbrief_setup",
2797 xstr("simbrief_setup_title"),
2798 xstr("simbrief_setup_help"),
2799 xstr("simbrief_setup_chelp"))
2800
2801 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2802 xscale = 0.0, yscale = 0.0)
2803
2804 table = gtk.Table(9, 3)
2805 table.set_row_spacings(4)
2806 table.set_col_spacings(16)
2807 table.set_homogeneous(False)
2808 alignment.add(table)
2809 self.setMainWidget(alignment)
2810
2811 label = gtk.Label(xstr("simbrief_username"))
2812 label.set_use_underline(True)
2813 label.set_alignment(0.0, 0.5)
2814 table.attach(label, 0, 1, 0, 1)
2815
2816 self._userName = gtk.Entry()
2817 self._userName.set_width_chars(16)
2818 self._userName.connect("changed",
2819 lambda button: self._updateForwardButton())
2820 self._userName.set_tooltip_text(xstr("simbrief_username_tooltip"))
2821 table.attach(self._userName, 1, 2, 0, 1)
2822 label.set_mnemonic_widget(self._userName)
2823
2824 label = gtk.Label(xstr("simbrief_password"))
2825 label.set_use_underline(True)
2826 label.set_alignment(0.0, 0.5)
2827 table.attach(label, 0, 1, 1, 2)
2828
2829 self._password = gtk.Entry()
2830 self._password.set_visibility(False)
2831 self._password.connect("changed",
2832 lambda button: self._updateForwardButton())
2833 self._password.set_tooltip_text(xstr("simbrief_password_tooltip"))
2834 table.attach(self._password, 1, 2, 1, 2)
2835 label.set_mnemonic_widget(self._password)
2836
2837 self._rememberButton = gtk.CheckButton(xstr("simbrief_remember_password"))
2838 self._rememberButton.set_use_underline(True)
2839 self._rememberButton.set_tooltip_text(xstr("simbrief_remember_tooltip"))
2840 table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
2841
2842 label = gtk.Label(xstr("simbrief_extra_fuel"))
2843 label.set_use_underline(True)
2844 label.set_alignment(0.0, 0.5)
2845 table.attach(label, 0, 1, 3, 4)
2846
2847 self._extraFuel = IntegerEntry(defaultValue = 0)
2848 self._extraFuel.set_width_chars(6)
2849 self._extraFuel.set_tooltip_text(xstr("simbrief_extra_fuel_tooltip"))
2850 table.attach(self._extraFuel, 1, 2, 3, 4)
2851 label.set_mnemonic_widget(self._extraFuel)
2852
2853 table.attach(gtk.Label("kg"), 2, 3, 3, 4)
2854
2855 label = gtk.Label(xstr("simbrief_takeoff_runway"))
2856 label.set_use_underline(True)
2857 label.set_alignment(0.0, 0.5)
2858 table.attach(label, 0, 1, 4, 5)
2859
2860 self._takeoffRunway = gtk.Entry()
2861 self._takeoffRunway.set_width_chars(10)
2862 self._takeoffRunway.set_tooltip_text(xstr("simbrief_takeoff_runway_tooltip"))
2863 self._takeoffRunway.connect("changed", self._upperChanged)
2864 table.attach(self._takeoffRunway, 1, 2, 4, 5)
2865 label.set_mnemonic_widget(self._takeoffRunway)
2866
2867 label = gtk.Label(xstr("simbrief_landing_runway"))
2868 label.set_use_underline(True)
2869 label.set_alignment(0.0, 0.5)
2870 table.attach(label, 0, 1, 5, 6)
2871
2872 self._landingRunway = gtk.Entry()
2873 self._landingRunway.set_width_chars(10)
2874 self._landingRunway.set_tooltip_text(xstr("simbrief_takeoff_runway_tooltip"))
2875 self._landingRunway.connect("changed", self._upperChanged)
2876 table.attach(self._landingRunway, 1, 2, 5, 6)
2877 label.set_mnemonic_widget(self._landingRunway)
2878
2879 label = gtk.Label(xstr("simbrief_climb_profile"))
2880 label.set_use_underline(True)
2881 label.set_alignment(0.0, 0.5)
2882 table.attach(label, 0, 1, 6, 7)
2883
2884 self._climbProfile = gtk.ComboBox()
2885 renderer = gtk.CellRendererText()
2886 self._climbProfile.pack_start(renderer, True)
2887 self._climbProfile.add_attribute(renderer, "text", 0)
2888 self._climbProfile.set_tooltip_text(xstr("simbrief_climb_profile_tooltip"))
2889 table.attach(self._climbProfile, 1, 2, 6, 7)
2890 label.set_mnemonic_widget(self._climbProfile)
2891
2892 label = gtk.Label(xstr("simbrief_cruise_profile"))
2893 label.set_use_underline(True)
2894 label.set_alignment(0.0, 0.5)
2895 table.attach(label, 0, 1, 7, 8)
2896
2897 self._cruiseProfile = gtk.ComboBox()
2898 renderer = gtk.CellRendererText()
2899 self._cruiseProfile.pack_start(renderer, True)
2900 self._cruiseProfile.add_attribute(renderer, "text", 0)
2901 self._cruiseProfile.set_tooltip_text(xstr("simbrief_cruise_profile_tooltip"))
2902 table.attach(self._cruiseProfile, 1, 2, 7, 8)
2903 label.set_mnemonic_widget(self._cruiseProfile)
2904
2905 label = gtk.Label(xstr("simbrief_descent_profile"))
2906 label.set_use_underline(True)
2907 label.set_alignment(0.0, 0.5)
2908 table.attach(label, 0, 1, 8, 9)
2909
2910 self._descentProfile = gtk.ComboBox()
2911 renderer = gtk.CellRendererText()
2912 self._descentProfile.pack_start(renderer, True)
2913 self._descentProfile.add_attribute(renderer, "text", 0)
2914 self._descentProfile.set_tooltip_text(xstr("simbrief_descent_profile_tooltip"))
2915 table.attach(self._descentProfile, 1, 2, 8, 9)
2916 label.set_mnemonic_widget(self._descentProfile)
2917
2918 self.addCancelFlightButton()
2919
2920 self._backButton = self.addPreviousButton(clicked = self._backClicked)
2921 self._button = self.addNextButton(clicked = self._forwardClicked)
2922
2923 def activate(self):
2924 """Activate the SimBrief setup page"""
2925 config = self._wizard.gui.config
2926
2927 self._userName.set_text(config.simBriefUserName)
2928 self._userName.set_sensitive(True)
2929
2930 self._password.set_text(config.simBriefPassword)
2931 self._password.set_sensitive(True)
2932
2933 self._rememberButton.set_active(config.rememberSimBriefPassword)
2934 self._rememberButton.set_sensitive(True)
2935
2936 self._extraFuel.set_int(0)
2937 self._extraFuel.set_sensitive(True)
2938
2939 self._takeoffRunway.set_text("")
2940 self._takeoffRunway.set_sensitive(True)
2941
2942 self._landingRunway.set_text("")
2943 self._landingRunway.set_sensitive(True)
2944
2945 simBriefData = self._wizard.gui.flight.aircraft.simBriefData
2946 for (control, profiles) in [(self._climbProfile,
2947 simBriefData.climbProfiles),
2948 (self._cruiseProfile,
2949 simBriefData.cruiseProfiles),
2950 (self._descentProfile,
2951 simBriefData.descentProfiles)]:
2952 model = gtk.ListStore(str)
2953 for profile in profiles:
2954 model.append([profile])
2955 control.set_model(model)
2956 control.set_sensitive(True)
2957
2958 self._climbProfile.set_active(0)
2959 self._cruiseProfile.set_active(0)
2960 self._descentProfile.set_active(0)
2961
2962 self._updateForwardButton()
2963
2964 def _updateForwardButton(self):
2965 """Update the sensitivity of the forward button."""
2966 self._button.set_sensitive(len(self._userName.get_text())>0 and
2967 len(self._password.get_text())>0)
2968
2969 def _backClicked(self, button):
2970 """Called when the Back button is pressed."""
2971 self.goBack()
2972
2973 def _forwardClicked(self, button):
2974 if self._completed:
2975 self._wizard.nextPage()
2976 else:
2977 config = self._wizard.gui.config
2978
2979 config.simBriefUserName = self._userName.get_text()
2980
2981 rememberPassword = self._rememberButton.get_active()
2982 config.simBriefPassword = \
2983 self._password.get_text() if rememberPassword else ""
2984 config.rememberSimBriefPassword = rememberPassword
2985
2986 config.save()
2987
2988 plan = self._getPlan()
2989 print("plan:", plan)
2990
2991 takeoffRunway = self._takeoffRunway.get_text()
2992 if takeoffRunway:
2993 self._wizard.takeoffRunway = takeoffRunway
2994
2995 landingRunway = self._landingRunway.get_text()
2996 if landingRunway:
2997 self._wizard.landingRunway = landingRunway
2998
2999 self._userName.set_sensitive(False)
3000 self._password.set_sensitive(False)
3001 self._rememberButton.set_sensitive(False)
3002 self._extraFuel.set_sensitive(False)
3003 self._takeoffRunway.set_sensitive(False)
3004 self._landingRunway.set_sensitive(False)
3005
3006 self._climbProfile.set_sensitive(False)
3007 self._cruiseProfile.set_sensitive(False)
3008 self._descentProfile.set_sensitive(False)
3009
3010 self._wizard.gui.beginBusy(xstr("simbrief_calling"))
3011
3012 cef.callSimBrief(plan,
3013 self._getCredentials,
3014 self._simBriefProgress,
3015 SimBriefSetupPage.getHTMLFilePath())
3016
3017 startSound(const.SOUND_NOTAM)
3018
3019 def _getCredentials(self, count):
3020 """Get the credentials.
3021
3022 If count is 0, the user name and password entered into the setup page
3023 are returned. Otherwise a dialog box is displayed informing the user of
3024 invalid credentials and requesting another set of them."""
3025 print("_getCredentials", count)
3026 if count==0:
3027 return (self._userName.get_text(), self._password.get_text())
3028 else:
3029 gui = self._wizard.gui
3030 config = gui.config
3031
3032 dialog = SimBriefCredentialsDialog(gui,
3033 config.simBriefUserName,
3034 config.simBriefPassword,
3035 config.rememberSimBriefPassword)
3036 response = dialog.run()
3037
3038 if response==RESPONSETYPE_OK:
3039 userName = dialog.userName
3040 self._userName.set_text(userName)
3041 password = dialog.password
3042 self._password.set_text(password)
3043 rememberPassword = dialog.rememberPassword
3044
3045 config.simBriefUserName = userName
3046
3047 config.simBriefPassword = \
3048 password if rememberPassword else ""
3049 config.rememberSimBriefPassword = rememberPassword
3050
3051 config.save()
3052
3053 return (userName, password)
3054 else:
3055 return (None, None)
3056
3057 def _simBriefProgress(self, progress, result, flightInfo):
3058 """The real SimBrief progress handler."""
3059 print("_simBriefProgress", progress, result, flightInfo)
3060 if result==cef.SIMBRIEF_RESULT_NONE:
3061 message = SimBriefSetupPage.progress2Message.get(progress,
3062 "simbrief_progress_unknown")
3063 self._wizard.gui.updateBusyState(xstr(message))
3064 else:
3065 self._wizard.gui.endBusy()
3066
3067 if result==cef.SIMBRIEF_RESULT_OK:
3068 self._wizard.departureMETARChanged(flightInfo["orig_metar"],
3069 self)
3070 self._wizard.arrivalMETARChanged(flightInfo["dest_metar"], self)
3071 self._wizard.nextPage()
3072 else:
3073 message = SimBriefSetupPage.result2Message.get(result,
3074 "simbrief_result_unknown")
3075 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
3076 type = MESSAGETYPE_ERROR,
3077 message_format =
3078 xstr(message) + "\n"+
3079 xstr("simbrief_cancelled"))
3080
3081 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
3082 dialog.set_title(WINDOW_TITLE_BASE)
3083 secondary = xstr("flightsel_save_failed_sec")
3084 dialog.format_secondary_markup(secondary)
3085 dialog.run()
3086 dialog.hide()
3087
3088 self._wizard.usingSimBrief = False
3089 self._wizard.jumpPage("fuel", fromPageShift = 1)
3090
3091 def _getPlan(self):
3092 """Get the flight plan data for SimBrief."""
3093 plan = {
3094 "airline": "MAH",
3095 "selcal": "XXXX",
3096 "fuelfactor": "P000",
3097 "contpct": "0.05",
3098 "resvrule": "45",
3099 "taxiout": "10",
3100 "taxiin": "10",
3101 "civalue": "AUTO"
3102 }
3103
3104 wizard = self._wizard
3105 gui = wizard.gui
3106
3107 loginResult = wizard.loginResult
3108 plan["cpt"] = loginResult.pilotName
3109 plan["pid"] = loginResult.pilotID
3110
3111 bookedFlight = wizard.bookedFlight
3112 plan["fltnum"] = wizard.bookedFlight.callsign[2:]
3113 plan["type"] = const.icaoCodes[bookedFlight.aircraftType]
3114 plan["orig"] = bookedFlight.departureICAO
3115 plan["dest"] = bookedFlight.arrivalICAO
3116 plan["reg"] = bookedFlight.tailNumber
3117 plan["fin"] = bookedFlight.tailNumber[3:]
3118 plan["pax"] = str(bookedFlight.numPassengers)
3119
3120 departureTime = bookedFlight.departureTime
3121 plan["date"] = "%d%s%d" % (departureTime.day,
3122 SimBriefSetupPage.monthNum2Name[departureTime.month-1],
3123 departureTime.year%100)
3124 plan["deph"] = str(departureTime.hour)
3125 plan["depm"] = str(departureTime.minute)
3126
3127 arrivalTime = bookedFlight.arrivalTime
3128 plan["steh"] = str(arrivalTime.hour)
3129 plan["stem"] = str(arrivalTime.minute)
3130
3131 plan["manualzfw"] = str(wizard.zfw / 1000.0)
3132 plan["cargo"] = str((wizard.bagWeight + wizard.cargoWeight + wizard.mailWeight)/1000.0)
3133
3134 plan["route"] = wizard.route
3135 plan["fl"] = str(wizard.filedCruiseAltitude)
3136 plan["altn"] = wizard.alternate
3137
3138 plan["addedfuel"] = str(self._extraFuel.get_int() / 1000.0)
3139 plan["origrwy"] = self._takeoffRunway.get_text()
3140 plan["destrwy"] = self._landingRunway.get_text()
3141
3142 for (key, control) in [("climb", self._climbProfile),
3143 ("cruise", self._cruiseProfile),
3144 ("descent", self._descentProfile)]:
3145 model = control.get_model()
3146 active = control.get_active_iter()
3147 value = model.get_value(active, 0)
3148 plan[key] = value
3149
3150 return plan
3151
3152 def _upperChanged(self, entry, arg = None):
3153 """Called when the value of some entry widget has changed and the value
3154 should be converted to uppercase."""
3155 entry.set_text(entry.get_text().upper())
3156
3157#-----------------------------------------------------------------------------
3158
3159class SimBriefingPage(Page):
3160 """Page to display the SimBrief HTML briefing."""
3161 class BrowserLifeSpanHandler(object):
3162 """The life-span handler of a browser."""
3163 def __init__(self, simBriefingPage):
3164 """Construct the life-span handler for the given page."""
3165 self._simBriefingPage = simBriefingPage
3166
3167 def OnBeforeClose(self, browser):
3168 """Called before closing the browser."""
3169 self._simBriefingPage._invalidateBrowser()
3170
3171 def __init__(self, wizard):
3172 """Construct the setup page."""
3173
3174 super(SimBriefingPage, self).__init__(wizard, "simbrief_result",
3175 xstr("simbrief_result_title"), "")
3176
3177 self._alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3178 xscale = 1.0, yscale = 1.0)
3179
3180 self._container = cef.getContainer()
3181 self._alignment.add(self._container)
3182
3183 self.setMainWidget(self._alignment)
3184
3185 self._browser = None
3186
3187 self.addCancelFlightButton()
3188
3189 self.addPreviousButton(clicked = self._backClicked)
3190
3191 self._button = self.addNextButton(clicked = self._forwardClicked)
3192 self._button.set_label(xstr("briefing_button"))
3193 self._button.set_has_tooltip(False)
3194 self._button.set_use_stock(False)
3195
3196 def activate(self):
3197 """Activate the SimBrief flight plan page"""
3198 if self._browser is None:
3199 self._startBrowser()
3200 else:
3201 self._browser.Reload()
3202
3203 def grabDefault(self):
3204 """If the page has a default button, make it the default one."""
3205 super(SimBriefingPage, self).grabDefault()
3206
3207 if self._browser is None:
3208 self._startBrowser()
3209
3210 def _backClicked(self, button):
3211 """Called when the Back button has been pressed."""
3212 self.goBack()
3213
3214 def _forwardClicked(self, button):
3215 """Called when the Forward button has been pressed."""
3216 if not self._completed:
3217 self._button.set_label(xstr("button_next"))
3218 self._button.set_tooltip_text(xstr("button_next_tooltip"))
3219 self._wizard.usingSimBrief = True
3220 self.complete()
3221
3222 self._wizard.nextPage()
3223
3224 def _startBrowser(self):
3225 """Start the browser.
3226
3227 If a container is needed, create one."""
3228 if self._container is None:
3229 self._container = cef.getContainer()
3230 self._alignment.add(self._container)
3231
3232 url = "file://" + SimBriefSetupPage.getHTMLFilePath()
3233 self._browser = cef.startInContainer(self._container, url)
3234
3235 lifeSpanHandler = SimBriefingPage.BrowserLifeSpanHandler(self)
3236 self._browser.SetClientHandler(lifeSpanHandler)
3237
3238 def _invalidateBrowser(self):
3239 """Invalidate the browser (and associated stuff)."""
3240 self._alignment.remove(self._container)
3241 self._container = None
3242 self._browser = None
3243
3244#-----------------------------------------------------------------------------
3245
3246class FuelTank(gtk.VBox):
3247 """Widget for the fuel tank."""
3248 def __init__(self, fuelTank, name, capacity, currentWeight):
3249 """Construct the widget for the tank with the given name."""
3250 super(FuelTank, self).__init__()
3251
3252 self._enabled = True
3253 self.fuelTank = fuelTank
3254 self.capacity = capacity
3255 self.currentWeight = currentWeight
3256 self.expectedWeight = currentWeight
3257
3258 self._label = label = gtk.Label("<b>" + name + "</b>")
3259 label.set_use_markup(True)
3260 label.set_use_underline(True)
3261 label.set_justify(JUSTIFY_CENTER)
3262 label.set_alignment(0.5, 1.0)
3263
3264 self._tankFigure = gtk.EventBox()
3265 self._tankFigure.set_size_request(38, 200)
3266 self._tankFigure.set_visible_window(False)
3267 self._tankFigure.set_tooltip_markup(xstr("fuel_tank_tooltip"))
3268
3269 if pygobject:
3270 self._tankFigure.connect("draw", self._drawTankFigure)
3271 else:
3272 self._tankFigure.connect("expose_event", self._drawTankFigure)
3273 self._tankFigure.connect("button_press_event", self._buttonPressed)
3274 self._tankFigure.connect("motion_notify_event", self._motionNotify)
3275 self._tankFigure.connect("scroll-event", self._scrolled)
3276
3277 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3278 xscale = 0.0, yscale = 1.0)
3279 alignment.add(self._tankFigure)
3280
3281 self.pack_start(alignment, True, True, 4)
3282
3283 self._expectedButton = gtk.SpinButton()
3284 self._expectedButton.set_numeric(True)
3285 self._expectedButton.set_range(0, self.capacity)
3286 self._expectedButton.set_increments(10, 100)
3287 self._expectedButton.set_value(currentWeight)
3288 self._expectedButton.set_alignment(1.0)
3289 self._expectedButton.set_width_chars(5)
3290 self._expectedButton.connect("value-changed", self._expectedChanged)
3291
3292 label.set_mnemonic_widget(self._expectedButton)
3293
3294 @property
3295 def label(self):
3296 """Get the label with the caption."""
3297 return self._label
3298
3299 @property
3300 def expectedButton(self):
3301 """Get the button containing the expected value."""
3302 return self._expectedButton
3303
3304 def setCurrent(self, currentWeight):
3305 """Set the current weight."""
3306 self.currentWeight = currentWeight
3307 self._redraw()
3308
3309 def isCorrect(self):
3310 """Determine if the contents of the fuel tank are as expected"""
3311 return abs(self.expectedWeight - self.currentWeight)<=1
3312
3313 def disable(self):
3314 """Disable the fuel tank."""
3315 self._expectedButton.set_sensitive(False)
3316 self._enabled = False
3317
3318 def _redraw(self):
3319 """Redraw the tank figure."""
3320 self._tankFigure.queue_draw()
3321
3322 def _drawTankFigure(self, tankFigure, eventOrContext):
3323 """Draw the tank figure."""
3324 triangleSize = 5
3325
3326 context = eventOrContext if pygobject else tankFigure.window.cairo_create()
3327 (xOffset, yOffset) = (0, 0) if pygobject \
3328 else (tankFigure.allocation.x, tankFigure.allocation.y)
3329
3330 width = tankFigure.get_allocated_width() if pygobject \
3331 else tankFigure.allocation.width
3332 height = tankFigure.get_allocated_height() if pygobject \
3333 else tankFigure.allocation.height
3334
3335 rectangleX0 = triangleSize
3336 rectangleY0 = triangleSize
3337 rectangleX1 = width - 1 - triangleSize
3338 rectangleY1 = height - 1 - triangleSize
3339 rectangleLineWidth = 2.0
3340
3341 context.set_source_rgb(0.0, 0.0, 0.0)
3342 context.set_line_width(rectangleLineWidth)
3343 context.rectangle(xOffset + rectangleX0 + rectangleLineWidth/2,
3344 yOffset + rectangleY0 + rectangleLineWidth/2,
3345 rectangleX1 - rectangleX0 - rectangleLineWidth,
3346 rectangleY1 - rectangleY0 - rectangleLineWidth)
3347 context.stroke()
3348
3349 rectangleInnerLeft = rectangleX0 + rectangleLineWidth
3350 rectangleInnerRight = rectangleX1 - rectangleLineWidth
3351 self._rectangleInnerTop = rectangleInnerTop = rectangleY0 + rectangleLineWidth
3352 self._rectangleInnerBottom = rectangleInnerBottom = rectangleY1 - rectangleLineWidth
3353
3354 rectangleInnerWidth = rectangleInnerRight - rectangleInnerLeft
3355 rectangleInnerHeight = rectangleInnerBottom - rectangleInnerTop
3356
3357 context.set_source_rgb(1.0, 0.9, 0.6)
3358 currentHeight = self.currentWeight * rectangleInnerHeight / self.capacity
3359 currentX = rectangleInnerTop + rectangleInnerHeight - currentHeight
3360 context.rectangle(xOffset + rectangleInnerLeft,
3361 yOffset + rectangleInnerTop +
3362 rectangleInnerHeight - currentHeight,
3363 rectangleInnerWidth, currentHeight)
3364 context.fill()
3365
3366 expectedHeight = self.expectedWeight * rectangleInnerHeight / self.capacity
3367 expectedY = rectangleInnerTop + rectangleInnerHeight - expectedHeight
3368
3369 context.set_line_width(1.5)
3370 context.set_source_rgb(0.0, 0.85, 0.85)
3371 context.move_to(xOffset + rectangleX0, yOffset + expectedY)
3372 context.line_to(xOffset + rectangleX1, yOffset + expectedY)
3373 context.stroke()
3374
3375 context.set_line_width(0.0)
3376 context.move_to(xOffset + 0, yOffset + expectedY - triangleSize)
3377 context.line_to(xOffset + 0, yOffset + expectedY + triangleSize)
3378 context.line_to(xOffset + rectangleX0 + 1, yOffset + expectedY)
3379 context.line_to(xOffset + 0, yOffset + expectedY - triangleSize)
3380 context.fill()
3381
3382 context.set_line_width(0.0)
3383 context.move_to(xOffset + width, yOffset + expectedY - triangleSize)
3384 context.line_to(xOffset + width, yOffset + expectedY + triangleSize)
3385 context.line_to(xOffset + rectangleX1 - 1, yOffset + expectedY)
3386 context.line_to(xOffset + width, yOffset + expectedY - triangleSize)
3387 context.fill()
3388
3389 return True
3390
3391 def _setExpectedFromY(self, y):
3392 """Set the expected weight from the given Y-coordinate."""
3393 level = (self._rectangleInnerBottom - y) / \
3394 (self._rectangleInnerBottom - self._rectangleInnerTop)
3395 level = min(1.0, max(0.0, level))
3396 self._expectedButton.set_value(level * self.capacity)
3397
3398 def _buttonPressed(self, tankFigure, event):
3399 """Called when a button is pressed in the figure.
3400
3401 The expected level will be set there."""
3402 if self._enabled and event.button==1:
3403 self._setExpectedFromY(event.y)
3404
3405 def _motionNotify(self, tankFigure, event):
3406 """Called when the mouse pointer moves within the area of a tank figure."""
3407 if self._enabled and event.state==BUTTON1_MASK:
3408 self._setExpectedFromY(event.y)
3409
3410 def _scrolled(self, tankFigure, event):
3411 """Called when a scroll event is received."""
3412 if self._enabled:
3413 increment = 1 if event.state==CONTROL_MASK \
3414 else 100 if event.state==SHIFT_MASK \
3415 else 10 if event.state==0 else 0
3416 if increment!=0:
3417 if event.direction==SCROLL_DOWN:
3418 increment *= -1
3419 self._expectedButton.spin(SPIN_USER_DEFINED, increment)
3420
3421 def _expectedChanged(self, spinButton):
3422 """Called when the expected value has changed."""
3423 self.expectedWeight = spinButton.get_value_as_int()
3424 self._redraw()
3425
3426#-----------------------------------------------------------------------------
3427
3428class FuelPage(Page):
3429 """The page containing the fuel tank filling."""
3430 _pumpStep = 0.02
3431
3432 def __init__(self, wizard):
3433 """Construct the page."""
3434 super(FuelPage, self).__init__(wizard, "fuel",
3435 xstr("fuel_title"),
3436 xstr("fuel_help_pre") +
3437 xstr("fuel_help_post"),
3438 completedHelp = xstr("fuel_chelp"))
3439
3440 self._fuelTanks = []
3441 self._fuelTable = None
3442 self._fuelAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3443 xscale = 0.0, yscale = 1.0)
3444 self.setMainWidget(self._fuelAlignment)
3445
3446 tankData = [(tank, 2500, 3900) for tank in acft.mostFuelTanks]
3447 self._setupTanks(tankData)
3448
3449 self.addCancelFlightButton()
3450
3451 self._backButton = self.addPreviousButton(clicked = self._backClicked)
3452 self._button = self.addNextButton(clicked = self._forwardClicked)
3453
3454 self._pumpIndex = 0
3455
3456 def activate(self):
3457 """Activate the page."""
3458 self._setupTanks(self._wizard._fuelData)
3459
3460 aircraft = self._wizard.gui.flight.aircraft
3461 minLandingFuel = aircraft.minLandingFuel
3462 recommendedLandingFuel = aircraft.recommendedLandingFuel
3463
3464 middleHelp = "" if minLandingFuel is None else \
3465 (xstr("fuel_help_min") % (minLandingFuel,)) \
3466 if recommendedLandingFuel is None else \
3467 (xstr("fuel_help_min_rec") % (minLandingFuel,
3468 recommendedLandingFuel))
3469 self.setHelp(xstr("fuel_help_pre") + middleHelp + xstr("fuel_help_post"))
3470
3471 def finalize(self):
3472 """Finalize the page."""
3473 for fuelTank in self._fuelTanks:
3474 fuelTank.disable()
3475
3476 def _backClicked(self, button):
3477 """Called when the Back button is pressed."""
3478 self.goBack()
3479
3480 def _forwardClicked(self, button):
3481 """Called when the forward button is clicked."""
3482 if not self._completed:
3483 self._pumpIndex = 0
3484 self._wizard.gui.beginBusy(xstr("fuel_pump_busy"))
3485 self._pump()
3486 elif self._wizard.usingSimBrief:
3487 self._wizard.jumpPage("takeoff")
3488 else:
3489 self._wizard.jumpPage("briefing1")
3490
3491 def _setupTanks(self, tankData):
3492 """Setup the tanks for the given data."""
3493 numTanks = len(tankData)
3494 if self._fuelTable is not None:
3495 self._fuelAlignment.remove(self._fuelTable)
3496
3497 self._fuelTanks = []
3498 self._fuelTable = gtk.Grid()
3499 self._fuelTable.set_column_homogeneous(True)
3500 self._fuelTable.set_row_spacing(4)
3501 index = 0
3502 for (tank, current, capacity) in tankData:
3503 fuelTank = FuelTank(tank,
3504 xstr("fuel_tank_" +
3505 const.fuelTank2string(tank)),
3506 capacity, current)
3507 self._fuelTanks.append(fuelTank)
3508
3509 alignment = gtk.Alignment(xalign = 0.5, yalign = 1.0,
3510 xscale = 1.0, yscale = 0.0)
3511 alignment.add(fuelTank.label)
3512 self._fuelTable.attach(alignment, index*2, 0, 3, 1)
3513
3514 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3515 xscale = 0.0, yscale = 1.0)
3516 alignment.add(fuelTank)
3517 self._fuelTable.attach(alignment, index*2+1, 1, 1, 1)
3518
3519
3520 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3521 xscale = 1.0, yscale = 0.0)
3522 alignment.add(fuelTank.expectedButton)
3523
3524 self._fuelTable.attach(alignment, index*2,
3525 2 if (index%2)==0 or numTanks==2 else 3,
3526 3, 1)
3527
3528 index += 1
3529
3530 self._fuelAlignment.add(self._fuelTable)
3531 self.show_all()
3532
3533 def _pump(self):
3534 """Perform one step of pumping.
3535
3536 It is checked, if the current tank's contents are of the right
3537 quantity. If not, it is filled one step further to the desired
3538 contents. Otherwise the next tank is started. If all tanks are are
3539 filled, the next page is selected."""
3540 numTanks = len(self._fuelTanks)
3541
3542 fuelTank = None
3543 while self._pumpIndex < numTanks:
3544 fuelTank = self._fuelTanks[self._pumpIndex]
3545 if fuelTank.isCorrect():
3546 self._pumpIndex += 1
3547 fuelTank = None
3548 else:
3549 break
3550
3551 if fuelTank is None:
3552 self._wizard.gui.endBusy()
3553 if self._wizard.usingSimBrief:
3554 self._wizard.gui.startMonitoring()
3555 self._wizard.jumpPage("takeoff")
3556 else:
3557 bookedFlight = self._wizard._bookedFlight
3558 self._wizard.gui.beginBusy(xstr("route_down_notams"))
3559 self._wizard.gui.webHandler.getNOTAMs(self._notamsCallback,
3560 bookedFlight.departureICAO,
3561 bookedFlight.arrivalICAO)
3562 startSound(const.SOUND_NOTAM)
3563 else:
3564 currentLevel = fuelTank.currentWeight / fuelTank.capacity
3565 expectedLevel = fuelTank.expectedWeight / fuelTank.capacity
3566 if currentLevel<expectedLevel:
3567 currentLevel += FuelPage._pumpStep
3568 if currentLevel>expectedLevel: currentLevel = expectedLevel
3569 else:
3570 currentLevel -= FuelPage._pumpStep
3571 if currentLevel<expectedLevel: currentLevel = expectedLevel
3572 fuelTank.setCurrent(currentLevel * fuelTank.capacity)
3573 self._wizard.gui.simulator.setFuelLevel([(fuelTank.fuelTank,
3574 currentLevel)])
3575 gobject.timeout_add(50, self._pump)
3576
3577 def _notamsCallback(self, returned, result):
3578 """Callback for the NOTAMs."""
3579 gobject.idle_add(self._handleNOTAMs, returned, result)
3580
3581 def _handleNOTAMs(self, returned, result):
3582 """Handle the NOTAMs."""
3583 if returned:
3584 self._wizard._departureNOTAMs = result.departureNOTAMs
3585 self._wizard._arrivalNOTAMs = result.arrivalNOTAMs
3586 else:
3587 self._wizard._departureNOTAMs = None
3588 self._wizard._arrivalNOTAMs = None
3589
3590 bookedFlight = self._wizard._bookedFlight
3591 self._wizard.gui.beginBusy(xstr("route_down_metars"))
3592 self._wizard.gui.webHandler.getMETARs(self._metarsCallback,
3593 [bookedFlight.departureICAO,
3594 bookedFlight.arrivalICAO])
3595
3596 def _metarsCallback(self, returned, result):
3597 """Callback for the METARs."""
3598 gobject.idle_add(self._handleMETARs, returned, result)
3599
3600 def _handleMETARs(self, returned, result):
3601 """Handle the METARs."""
3602 self._wizard._departureMETAR = None
3603 self._wizard._arrivalMETAR = None
3604 bookedFlight = self._wizard._bookedFlight
3605 if returned:
3606 if bookedFlight.departureICAO in result.metars:
3607 self._wizard._departureMETAR = result.metars[bookedFlight.departureICAO]
3608 if bookedFlight.arrivalICAO in result.metars:
3609 self._wizard._arrivalMETAR = result.metars[bookedFlight.arrivalICAO]
3610
3611 self._wizard.gui.endBusy()
3612 self._backButton.set_sensitive(True)
3613 self._button.set_sensitive(True)
3614 self._wizard.nextPage()
3615
3616#-----------------------------------------------------------------------------
3617
3618class BriefingPage(Page):
3619 """Page for the briefing."""
3620 def __init__(self, wizard, departure):
3621 """Construct the briefing page."""
3622 self._departure = departure
3623
3624 number = 1 if departure else 2
3625
3626 title = xstr("briefing_title") % (number,
3627 xstr("briefing_departure")
3628 if departure
3629 else xstr("briefing_arrival"))
3630 super(BriefingPage, self).__init__(wizard,
3631 "briefing%d" % (number,),
3632 title, xstr("briefing_help"),
3633 completedHelp = xstr("briefing_chelp"))
3634
3635 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3636 xscale = 1.0, yscale = 1.0)
3637
3638 mainBox = gtk.VBox()
3639 alignment.add(mainBox)
3640 self.setMainWidget(alignment)
3641
3642 self._notamsFrame = gtk.Frame()
3643 self._notamsFrame.set_label(xstr("briefing_notams_init"))
3644 scrolledWindow = gtk.ScrolledWindow()
3645 scrolledWindow.set_size_request(-1, 128)
3646 # FIXME: these constants should be in common
3647 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
3648 else gtk.POLICY_AUTOMATIC,
3649 gtk.PolicyType.AUTOMATIC if pygobject
3650 else gtk.POLICY_AUTOMATIC)
3651 self._notams = gtk.TextView()
3652 self._notams.set_editable(False)
3653 self._notams.set_accepts_tab(False)
3654 self._notams.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
3655 scrolledWindow.add(self._notams)
3656 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
3657 xscale = 1.0, yscale = 1.0)
3658 alignment.set_padding(padding_top = 4, padding_bottom = 0,
3659 padding_left = 0, padding_right = 0)
3660 alignment.add(scrolledWindow)
3661 self._notamsFrame.add(alignment)
3662 mainBox.pack_start(self._notamsFrame, True, True, 4)
3663
3664 self._metarFrame = gtk.Frame()
3665 self._metarFrame.set_label(xstr("briefing_metar_init"))
3666 scrolledWindow = gtk.ScrolledWindow()
3667 scrolledWindow.set_size_request(-1, 32)
3668 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
3669 else gtk.POLICY_AUTOMATIC,
3670 gtk.PolicyType.AUTOMATIC if pygobject
3671 else gtk.POLICY_AUTOMATIC)
3672
3673 self._updatingMETAR = False
3674
3675 self._metar = gtk.TextView()
3676 self._metar.set_accepts_tab(False)
3677 self._metar.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
3678 self._metar.get_buffer().connect("changed", self._metarChanged)
3679 self._metar.get_buffer().connect_after("insert-text", self._metarInserted)
3680 scrolledWindow.add(self._metar)
3681 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
3682 xscale = 1.0, yscale = 1.0)
3683 alignment.set_padding(padding_top = 4, padding_bottom = 0,
3684 padding_left = 0, padding_right = 0)
3685 alignment.add(scrolledWindow)
3686 self._metarFrame.add(alignment)
3687 mainBox.pack_start(self._metarFrame, True, True, 4)
3688 self.metarEdited = False
3689
3690 self.addCancelFlightButton()
3691
3692 self.addPreviousButton(clicked = self._backClicked)
3693 self._button = self.addNextButton(clicked = self._forwardClicked)
3694
3695 @property
3696 def metar(self):
3697 """Get the METAR on the page."""
3698 buffer = self._metar.get_buffer()
3699 return buffer.get_text(buffer.get_start_iter(),
3700 buffer.get_end_iter(), True)
3701
3702 def setMETAR(self, metar):
3703 """Set the METAR."""
3704 self._metar.get_buffer().set_text(metar)
3705 self.metarEdited = False
3706
3707 def changeMETAR(self, metar):
3708 """Change the METAR as a result of an edit on one of the other
3709 pages."""
3710 self._updatingMETAR = True
3711 self._metar.get_buffer().set_text(metar)
3712 self._updatingMETAR = False
3713
3714 self._updateButton()
3715 self.metarEdited = True
3716
3717 def activate(self):
3718 """Activate the page."""
3719 if not self._departure:
3720 self._button.set_label(xstr("briefing_button"))
3721 self._button.set_has_tooltip(False)
3722 self._button.set_use_stock(False)
3723
3724 bookedFlight = self._wizard._bookedFlight
3725
3726 icao = bookedFlight.departureICAO if self._departure \
3727 else bookedFlight.arrivalICAO
3728 notams = self._wizard._departureNOTAMs if self._departure \
3729 else self._wizard._arrivalNOTAMs
3730 metar = self._wizard._departureMETAR if self._departure \
3731 else self._wizard._arrivalMETAR
3732
3733 self._notamsFrame.set_label(xstr("briefing_notams_template") % (icao,))
3734 buffer = self._notams.get_buffer()
3735 if notams is None:
3736 buffer.set_text(xstr("briefing_notams_failed"))
3737 elif not notams:
3738 buffer.set_text(xstr("briefing_notams_missing"))
3739 else:
3740 s = ""
3741 for notam in notams:
3742 s += str(notam)
3743 s += "-------------------- * --------------------\n"
3744 buffer.set_text(s)
3745
3746 self._metarFrame.set_label(xstr("briefing_metar_template") % (icao,))
3747 buffer = self._metar.get_buffer()
3748 self._updatingMETAR = True
3749 if metar is None:
3750 buffer.set_text("")
3751 self.setHelp(xstr("briefing_help_nometar"))
3752 else:
3753 buffer.set_text(metar)
3754 self._updatingMETAR = False
3755 self._updateButton()
3756
3757 label = self._metarFrame.get_label_widget()
3758 label.set_use_underline(True)
3759 label.set_mnemonic_widget(self._metar)
3760
3761 self.metarEdited = False
3762
3763 def _backClicked(self, button):
3764 """Called when the Back button is pressed."""
3765 self.goBack()
3766
3767 def _forwardClicked(self, button):
3768 """Called when the forward button is clicked."""
3769 if not self._departure:
3770 if not self._completed:
3771 self._wizard.gui.startMonitoring()
3772 self._button.set_label(xstr("button_next"))
3773 self._button.set_tooltip_text(xstr("button_next_tooltip"))
3774 self.complete()
3775
3776 self._wizard.nextPage()
3777
3778 def _metarChanged(self, buffer):
3779 """Called when the METAR has changed."""
3780 print("BriefingPage.metarChanged", self._updatingMETAR)
3781 if not self._updatingMETAR:
3782 self.metarEdited = True
3783 self._updateButton()
3784 metar = buffer.get_text(buffer.get_start_iter(),
3785 buffer.get_end_iter(), True)
3786 self._wizard.metarChanged(metar, self)
3787
3788 def _metarInserted(self, textBuffer, iter, text, length):
3789 """Called when new characters are inserted into the METAR.
3790
3791 It uppercases all characters."""
3792 print("BriefingPage.metarInserted", self._updatingMETAR)
3793 if not self._updatingMETAR:
3794 self._updatingMETAR = True
3795
3796 iter1 = iter.copy()
3797 iter1.backward_chars(length)
3798 textBuffer.delete(iter, iter1)
3799
3800 textBuffer.insert(iter, text.upper())
3801
3802 self._updatingMETAR = False
3803
3804 def _updateButton(self):
3805 """Update the sensitivity of the Next button based on the contents of
3806 the METAR field."""
3807 buffer = self._metar.get_buffer()
3808 self._button.set_sensitive(buffer.get_text(buffer.get_start_iter(),
3809 buffer.get_end_iter(),
3810 True)!="")
3811
3812
3813#-----------------------------------------------------------------------------
3814
3815class TakeoffPage(Page):
3816 """Page for entering the takeoff data."""
3817 def __init__(self, wizard):
3818 """Construct the takeoff page."""
3819 super(TakeoffPage, self).__init__(wizard, "takeoff",
3820 xstr("takeoff_title"),
3821 xstr("takeoff_help"),
3822 completedHelp = xstr("takeoff_chelp"))
3823
3824 self._forwardAllowed = False
3825
3826 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3827 xscale = 0.0, yscale = 0.0)
3828
3829 table = gtk.Table(9, 24)
3830 table.set_row_spacings(4)
3831 table.set_col_spacings(16)
3832 table.set_homogeneous(False)
3833 alignment.add(table)
3834 self.setMainWidget(alignment)
3835
3836 row = 0
3837
3838 label = gtk.Label(xstr("takeoff_metar"))
3839 label.set_use_underline(True)
3840 label.set_alignment(0.0, 0.5)
3841 table.attach(label, 0, 1, row, row+1)
3842
3843 self._metar = gtk.Entry()
3844 self._metar.set_width_chars(40)
3845 self._metar.set_tooltip_text(xstr("takeoff_metar_tooltip"))
3846 self._metar.connect("changed", self._metarChanged)
3847 self._metar.get_buffer().connect_after("inserted-text", self._metarInserted)
3848 table.attach(self._metar, 1, 24, row, row+1)
3849 label.set_mnemonic_widget(self._metar)
3850
3851 self._updatingMETAR = False
3852
3853 row += 1
3854
3855 label = gtk.Label(xstr("takeoff_runway"))
3856 label.set_use_underline(True)
3857 label.set_alignment(0.0, 0.5)
3858 table.attach(label, 0, 1, row, row+1)
3859
3860 self._runway = gtk.Entry()
3861 self._runway.set_width_chars(10)
3862 self._runway.set_tooltip_text(xstr("takeoff_runway_tooltip"))
3863 self._runway.connect("changed", self._upperChanged)
3864 table.attach(self._runway, 1, 3, row, row+1)
3865 label.set_mnemonic_widget(self._runway)
3866
3867 row += 1
3868
3869 label = gtk.Label(xstr("takeoff_sid"))
3870 label.set_use_underline(True)
3871 label.set_alignment(0.0, 0.5)
3872 table.attach(label, 0, 1, row, row+1)
3873
3874 if pygobject:
3875 self._sid = gtk.ComboBox.new_with_model_and_entry(comboModel)
3876 else:
3877 self._sid = gtk.ComboBoxEntry(comboModel)
3878
3879 self._sid.set_entry_text_column(0)
3880 self._sid.get_child().set_width_chars(10)
3881 self._sid.set_tooltip_text(xstr("takeoff_sid_tooltip"))
3882 self._sid.connect("changed", self._upperChangedComboBox)
3883 table.attach(self._sid, 1, 3, row, row+1)
3884 label.set_mnemonic_widget(self._sid)
3885
3886 row += 1
3887
3888 label = gtk.Label(xstr("takeoff_v1"))
3889 label.set_use_markup(True)
3890 label.set_use_underline(True)
3891 label.set_alignment(0.0, 0.5)
3892 table.attach(label, 0, 1, row, row+1)
3893
3894 self._v1 = IntegerEntry()
3895 self._v1.set_width_chars(4)
3896 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip_knots"))
3897 self._v1.connect("integer-changed", self._valueChanged)
3898 table.attach(self._v1, 2, 3, row, row+1)
3899 label.set_mnemonic_widget(self._v1)
3900
3901 self._v1Unit = gtk.Label(xstr("label_knots"))
3902 self._v1Unit.set_alignment(0.0, 0.5)
3903 table.attach(self._v1Unit, 3, 4, row, row+1)
3904
3905 row += 1
3906
3907 label = gtk.Label(xstr("takeoff_vr"))
3908 label.set_use_markup(True)
3909 label.set_use_underline(True)
3910 label.set_alignment(0.0, 0.5)
3911 table.attach(label, 0, 1, row, row+1)
3912
3913 self._vr = IntegerEntry()
3914 self._vr.set_width_chars(4)
3915 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip_knots"))
3916 self._vr.connect("integer-changed", self._valueChanged)
3917 table.attach(self._vr, 2, 3, row, row+1)
3918 label.set_mnemonic_widget(self._vr)
3919
3920 self._vrUnit = gtk.Label(xstr("label_knots"))
3921 self._vrUnit.set_alignment(0.0, 0.5)
3922 table.attach(self._vrUnit, 3, 4, row, row+1)
3923
3924 row += 1
3925
3926 label = gtk.Label(xstr("takeoff_v2"))
3927 label.set_use_markup(True)
3928 label.set_use_underline(True)
3929 label.set_alignment(0.0, 0.5)
3930 table.attach(label, 0, 1, row, row+1)
3931
3932 self._v2 = IntegerEntry()
3933 self._v2.set_width_chars(4)
3934 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip_knots"))
3935 self._v2.connect("integer-changed", self._valueChanged)
3936 table.attach(self._v2, 2, 3, row, row+1)
3937 label.set_mnemonic_widget(self._v2)
3938
3939 self._v2Unit = gtk.Label(xstr("label_knots"))
3940 self._v2Unit.set_alignment(0.0, 0.5)
3941 table.attach(self._v2Unit, 3, 4, row, row+1)
3942
3943 row += 1
3944
3945 self._derateType = acft.DERATE_NONE
3946
3947 self._derateLabel = gtk.Label()
3948 self._derateLabel.set_use_underline(True)
3949 self._derateLabel.set_markup(xstr("takeoff_derate_tupolev"))
3950 self._derateLabel.set_alignment(0.0, 0.5)
3951 table.attach(self._derateLabel, 0, 1, row, row+1)
3952
3953 self._derate = gtk.Alignment()
3954 table.attach(self._derate, 2, 4, row, row+1)
3955 self._derateWidget = None
3956 self._derateEntry = None
3957 self._derateUnit = None
3958 self._derateButtons = None
3959
3960 row += 1
3961
3962 self._antiIceOn = gtk.CheckButton(xstr("takeoff_antiice"))
3963 self._antiIceOn.set_use_underline(True)
3964 self._antiIceOn.set_tooltip_text(xstr("takeoff_antiice_tooltip"))
3965 table.attach(self._antiIceOn, 2, 4, row, row+1)
3966
3967 row += 1
3968
3969 self._rto = gtk.CheckButton(xstr("takeoff_rto"))
3970 self._rto.set_use_underline(True)
3971 self._rto.set_tooltip_text(xstr("takeoff_rto_tooltip"))
3972 self._rto.connect("toggled", self._rtoToggled)
3973 table.attach(self._rto, 2, 4, row, row+1, ypadding = 8)
3974
3975 self.addCancelFlightButton()
3976
3977 self.addPreviousButton(clicked = self._backClicked)
3978
3979 self._button = self.addNextButton(clicked = self._forwardClicked)
3980
3981 self._active = False
3982
3983 @property
3984 def runway(self):
3985 """Get the runway."""
3986 return self._runway.get_text()
3987
3988 @property
3989 def sid(self):
3990 """Get the SID."""
3991 text = self._sid.get_child().get_text()
3992 return text if self._sid.get_active()!=0 and text and text!="N/A" \
3993 else None
3994
3995 @property
3996 def v1(self):
3997 """Get the v1 speed."""
3998 return self._v1.get_int()
3999
4000 @property
4001 def vr(self):
4002 """Get the vr speed."""
4003 return self._vr.get_int()
4004
4005 @property
4006 def v2(self):
4007 """Get the v2 speed."""
4008 return self._v2.get_int()
4009
4010 @property
4011 def derate(self):
4012 """Get the derate value, if any."""
4013 if self._derateWidget is None:
4014 return None
4015 if self._derateType==acft.DERATE_BOEING:
4016 derate = self._derateEntry.get_text()
4017 return derate if derate else None
4018 elif self._derateType==acft.DERATE_EPR:
4019 derate = self._derateWidget.get_text()
4020 return derate if derate else None
4021 elif self._derateType==acft.DERATE_TUPOLEV:
4022 return acft.DERATE_TUPOLEV_NOMINAL \
4023 if self._derateButtons[0].get_active() \
4024 else acft.DERATE_TUPOLEV_TAKEOFF
4025 elif self._derateType==acft.DERATE_B462:
4026 return self._derateWidget.get_active()
4027 else:
4028 return None
4029
4030 @property
4031 def antiIceOn(self):
4032 """Get whether the anti-ice system has been turned on."""
4033 return self._antiIceOn.get_active()
4034
4035 @antiIceOn.setter
4036 def antiIceOn(self, value):
4037 """Set the anti-ice indicator."""
4038 self._antiIceOn.set_active(value)
4039
4040 @property
4041 def rtoIndicated(self):
4042 """Get whether the pilot has indicated if there was an RTO."""
4043 return self._rto.get_active()
4044
4045 def activate(self):
4046 """Activate the page."""
4047 print("TakeoffPage.activate")
4048
4049 self._updatingMETAR = True
4050 self._metar.get_buffer().set_text(self._wizard.departureMETAR, -1)
4051 self._updatingMETAR = False
4052
4053 if self._wizard.takeoffRunway is None:
4054 self._runway.set_text("")
4055 else:
4056 self._runway.set_text(self._wizard.takeoffRunway)
4057 self._runway.set_sensitive(True)
4058 self._sid.set_active(0)
4059 self._sid.set_sensitive(True)
4060 self._v1.set_int(None)
4061 self._v1.set_sensitive(True)
4062 self._vr.set_int(None)
4063 self._vr.set_sensitive(True)
4064 self._v2.set_int(None)
4065 self._v2.set_sensitive(True)
4066
4067 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
4068 speedUnit = xstr("label" + i18nSpeedUnit)
4069 self._v1Unit.set_text(speedUnit)
4070 self._vrUnit.set_text(speedUnit)
4071 self._v2Unit.set_text(speedUnit)
4072
4073 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip" + i18nSpeedUnit))
4074 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip" + i18nSpeedUnit))
4075 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip" + i18nSpeedUnit))
4076
4077 self._derateType = self._wizard.gui.flight.aircraft.derateType
4078
4079 self._setupDerateWidget()
4080
4081 self._rto.set_active(False)
4082 self._rto.set_sensitive(False)
4083
4084 self._button.set_sensitive(False)
4085 self._forwardAllowed = False
4086
4087 self._active = True
4088
4089 def allowForward(self):
4090 """Allow going to the next page."""
4091 print("TakeoffPage.allowForward")
4092 self._forwardAllowed = True
4093 self._updateForwardButton()
4094
4095 def reset(self):
4096 """Reset the page if the wizard is reset."""
4097 print("TakeoffPage.reset")
4098
4099 super(TakeoffPage, self).reset()
4100 self._v1.reset()
4101 self._vr.reset()
4102 self._v2.reset()
4103 self._hasDerate = False
4104 self._antiIceOn.set_active(False)
4105 self._active = False
4106
4107 def setRTOEnabled(self, enabled):
4108 """Set the RTO checkbox enabled or disabled."""
4109 if not enabled:
4110 self._rto.set_active(False)
4111 self._rto.set_sensitive(enabled)
4112
4113 def changeMETAR(self, metar):
4114 """Change the METAR as a result of an edit on one of the other
4115 pages."""
4116 if self._active:
4117 print("TakeoffPage.changeMETAR")
4118 self._updatingMETAR = True
4119 self._metar.get_buffer().set_text(metar, -1)
4120 self._updatingMETAR = False
4121
4122 self._updateForwardButton()
4123
4124 def _updateForwardButton(self):
4125 """Update the sensitivity of the forward button based on some conditions."""
4126 sensitive = self._forwardAllowed and \
4127 self._metar.get_text()!="" and \
4128 self._runway.get_text()!="" and \
4129 self.sid is not None and \
4130 self.v1 is not None and \
4131 self.vr is not None and \
4132 self.v2 is not None and \
4133 self.v1 <= self.vr and \
4134 self.vr <= self.v2 and \
4135 (self._derateType==acft.DERATE_NONE or
4136 self.derate is not None)
4137
4138 print("TakeoffPage._updateForwardButton: forwardAllowed:", self._forwardAllowed, ", sensitive:", sensitive)
4139 if self._forwardAllowed:
4140 print(" METAR: ", self._metar.get_text())
4141 print(" runway: ", self._runway.get_text())
4142 print(" SID:", self.sid)
4143 print(" V1:", self.v1)
4144 print(" VR:", self.vr)
4145 print(" V2:", self.v2)
4146 print(" derateType:", self._derateType)
4147 print(" derate:", self.derate)
4148
4149 self._button.set_sensitive(sensitive)
4150
4151 def _valueChanged(self, widget, arg = None):
4152 """Called when the value of some widget has changed."""
4153 print("TakeoffPage._valueChanged")
4154
4155 self._updateForwardButton()
4156
4157 def _upperChanged(self, entry, arg = None):
4158 """Called when the value of some entry widget has changed and the value
4159 should be converted to uppercase."""
4160 print("TakeoffPage._upperChanged")
4161 entry.set_text(entry.get_text().upper())
4162 self._valueChanged(entry, arg)
4163
4164 def _upperChangedComboBox(self, comboBox):
4165 """Called for combo box widgets that must be converted to uppercase."""
4166 entry = comboBox.get_child()
4167 if comboBox.get_active()==-1:
4168 entry.set_text(entry.get_text().upper())
4169 self._valueChanged(entry)
4170
4171 def _derateChanged(self, entry):
4172 """Called when the value of the derate is changed."""
4173 print("TakeoffPage._derateChanged")
4174 self._updateForwardButton()
4175
4176 def _rtoToggled(self, button):
4177 """Called when the RTO check button is toggled."""
4178 self._wizard.rtoToggled(button.get_active())
4179
4180 def _backClicked(self, button):
4181 """Called when the Back button is pressed."""
4182 self.goBack()
4183
4184 def _forwardClicked(self, button):
4185 """Called when the forward button is clicked."""
4186 aircraft = self._wizard.gui.flight.aircraft
4187 aircraft.updateV1R2()
4188 if self.derate is not None:
4189 aircraft.updateDerate()
4190 aircraft.updateTakeoffAntiIce()
4191 self._wizard.nextPage()
4192
4193 def _setupDerateWidget(self):
4194 """Setup the derate widget."""
4195 if self._derateWidget is not None:
4196 self._derate.remove(self._derateWidget)
4197
4198 if self._derateType==acft.DERATE_BOEING:
4199 self._derateLabel.set_text(xstr("takeoff_derate_boeing"))
4200 self._derateLabel.set_use_underline(True)
4201 self._derateLabel.set_sensitive(True)
4202
4203 self._derateEntry = gtk.Entry()
4204 self._derateEntry.set_width_chars(7)
4205 self._derateEntry.set_tooltip_text(xstr("takeoff_derate_boeing_tooltip"))
4206 self._derateEntry.set_alignment(1.0)
4207 self._derateEntry.connect("changed", self._derateChanged)
4208 self._derateLabel.set_mnemonic_widget(self._derateEntry)
4209
4210 self._derateUnit = gtk.Label("%")
4211 self._derateUnit.set_alignment(0.0, 0.5)
4212
4213 self._derateWidget = gtk.Table(3, 1)
4214 self._derateWidget.set_row_spacings(4)
4215 self._derateWidget.set_col_spacings(16)
4216 self._derateWidget.set_homogeneous(False)
4217
4218 self._derateWidget.attach(self._derateEntry, 0, 2, 0, 1)
4219 self._derateWidget.attach(self._derateUnit, 2, 3, 0, 1)
4220
4221 self._derate.add(self._derateWidget)
4222 elif self._derateType==acft.DERATE_EPR:
4223 self._derateLabel.set_text("_EPR:")
4224 self._derateLabel.set_use_underline(True)
4225 self._derateLabel.set_sensitive(True)
4226
4227 self._derateWidget = gtk.Entry()
4228 self._derateWidget.set_width_chars(7)
4229 self._derateWidget.set_tooltip_text(xstr("takeoff_derate_epr_tooltip"))
4230 self._derateWidget.set_alignment(1.0)
4231 self._derateWidget.connect("changed", self._derateChanged)
4232 self._derateLabel.set_mnemonic_widget(self._derateWidget)
4233
4234 self._derate.add(self._derateWidget)
4235 elif self._derateType==acft.DERATE_TUPOLEV:
4236 self._derateLabel.set_text(xstr("takeoff_derate_tupolev"))
4237 self._derateLabel.set_use_underline(True)
4238 self._derateLabel.set_sensitive(True)
4239
4240 if pygobject:
4241 nominal = gtk.RadioButton.\
4242 new_with_label_from_widget(None,
4243 xstr("takeoff_derate_tupolev_nominal"))
4244 else:
4245 nominal = gtk.RadioButton(None,
4246 xstr("takeoff_derate_tupolev_nominal"))
4247 nominal.set_use_underline(True)
4248 nominal.set_tooltip_text(xstr("takeoff_derate_tupolev_nominal_tooltip"))
4249 nominal.connect("toggled", self._derateChanged)
4250
4251 if pygobject:
4252 takeoff = gtk.RadioButton.\
4253 new_with_label_from_widget(nominal,
4254 xstr("takeoff_derate_tupolev_takeoff"))
4255 else:
4256 takeoff = gtk.RadioButton(nominal,
4257 xstr("takeoff_derate_tupolev_takeoff"))
4258
4259 takeoff.set_use_underline(True)
4260 takeoff.set_tooltip_text(xstr("takeoff_derate_tupolev_takeoff_tooltip"))
4261 takeoff.connect("toggled", self._derateChanged)
4262
4263 self._derateButtons = [nominal, takeoff]
4264
4265 self._derateWidget = gtk.HBox()
4266 self._derateWidget.pack_start(nominal, False, False, 4)
4267 self._derateWidget.pack_start(takeoff, False, False, 4)
4268
4269 self._derate.add(self._derateWidget)
4270 elif self._derateType==acft.DERATE_B462:
4271 self._derateLabel.set_text("")
4272
4273 self._derateWidget = gtk.CheckButton(xstr("takeoff_derate_b462"))
4274 self._derateWidget.set_tooltip_text(xstr("takeoff_derate_b462_tooltip"))
4275 self._derateWidget.set_use_underline(True)
4276 self._derate.add(self._derateWidget)
4277 else:
4278 self._derateWidget = None
4279 self._derateLabel.set_text("")
4280 self._derateLabel.set_sensitive(False)
4281
4282 def _metarChanged(self, entry):
4283 """Called when the METAR has changed."""
4284 print("TakeoffPage.metarChanged", self._updatingMETAR)
4285 if not self._updatingMETAR:
4286 self._updateForwardButton()
4287 self._wizard.metarChanged(entry.get_text(), self)
4288
4289 def _metarInserted(self, buffer, position, text, length):
4290 """Called when new characters are inserted into the METAR.
4291
4292 It uppercases all characters."""
4293 print("TakeoffPage.metarInserted", self._updatingMETAR)
4294 if not self._updatingMETAR:
4295 self._updatingMETAR = True
4296
4297 buffer.delete_text(position, length)
4298 buffer.insert_text(position, text.upper(), length)
4299
4300 self._updatingMETAR = False
4301
4302#-----------------------------------------------------------------------------
4303
4304class CruisePage(Page):
4305 """The page containing the flight level that might change during flight."""
4306 def __init__(self, wizard):
4307 """Construct the page."""
4308 super(CruisePage, self).__init__(wizard, "cruise",
4309 xstr("cruise_title"),
4310 xstr("cruise_help"))
4311
4312 self._loggable = False
4313 self._loggedCruiseLevel = 240
4314 self._activated = False
4315
4316 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
4317 xscale = 0.0, yscale = 1.0)
4318
4319 mainBox = gtk.VBox()
4320 alignment.add(mainBox)
4321 self.setMainWidget(alignment)
4322
4323 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
4324 xscale = 0.0, yscale = 0.0)
4325 mainBox.pack_start(alignment, False, False, 16)
4326
4327 levelBox = gtk.HBox()
4328
4329 label = gtk.Label(xstr("route_level"))
4330 label.set_use_underline(True)
4331 levelBox.pack_start(label, True, True, 0)
4332
4333 self._cruiseLevel = gtk.SpinButton()
4334 self._cruiseLevel.set_increments(step = 10, page = 100)
4335 self._cruiseLevel.set_range(min = 50, max = 500)
4336 self._cruiseLevel.set_tooltip_text(xstr("cruise_route_level_tooltip"))
4337 self._cruiseLevel.set_numeric(True)
4338 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
4339 label.set_mnemonic_widget(self._cruiseLevel)
4340
4341 levelBox.pack_start(self._cruiseLevel, False, False, 8)
4342
4343 self._updateButton = gtk.Button(xstr("cruise_route_level_update"));
4344 self._updateButton.set_use_underline(True)
4345 self._updateButton.set_tooltip_text(xstr("cruise_route_level_update_tooltip"))
4346 self._updateButton.connect("clicked", self._updateButtonClicked)
4347
4348 levelBox.pack_start(self._updateButton, False, False, 16)
4349
4350 mainBox.pack_start(levelBox, False, False, 0)
4351
4352 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
4353 xscale = 0.0, yscale = 1.0)
4354 mainBox.pack_start(alignment, True, True, 0)
4355
4356 self.addCancelFlightButton()
4357
4358 self._backButton = self.addPreviousButton(clicked = self._backClicked)
4359 self._button = self.addNextButton(clicked = self._forwardClicked)
4360
4361 @property
4362 def activated(self):
4363 """Determine if the page is already activated or not."""
4364 return self._activated
4365
4366 @property
4367 def cruiseLevel(self):
4368 """Get the cruise level."""
4369 return self._loggedCruiseLevel
4370
4371 @property
4372 def loggableCruiseLevel(self):
4373 """Get the cruise level which should be logged."""
4374 return self._cruiseLevel.get_value_as_int()
4375
4376 def setLoggable(self, loggable):
4377 """Set whether the cruise altitude can be logged."""
4378 self._loggable = loggable
4379 self._updateButtons()
4380
4381 def activate(self):
4382 """Setup the route from the booked flight."""
4383 self._loggedCruiseLevel = self._wizard.filedCruiseLevel
4384 self._cruiseLevel.set_value(self._loggedCruiseLevel)
4385 self._activated = True
4386
4387 def reset(self):
4388 """Reset the page."""
4389 self._loggable = False
4390 self._activated = False
4391 super(CruisePage, self).reset()
4392
4393 def _updateButtons(self):
4394 """Update the sensitivity of the buttons."""
4395 self._updateButton.set_sensitive(self._loggable and
4396 self.loggableCruiseLevel!=
4397 self._loggedCruiseLevel)
4398
4399 def _cruiseLevelChanged(self, spinButton):
4400 """Called when the cruise level has changed."""
4401 self._updateButtons()
4402
4403 def _updateButtonClicked(self, button):
4404 """Called when the update button is clicked."""
4405 if self._wizard.cruiseLevelChanged():
4406 self._loggedCruiseLevel = self.loggableCruiseLevel
4407 self._updateButtons()
4408
4409 def _backClicked(self, button):
4410 """Called when the Back button is pressed."""
4411 self.goBack()
4412
4413 def _forwardClicked(self, button):
4414 """Called when the Forward button is clicked."""
4415 self._wizard.nextPage()
4416
4417#-----------------------------------------------------------------------------
4418
4419class LandingPage(Page):
4420 """Page for entering landing data."""
4421 def __init__(self, wizard):
4422 """Construct the landing page."""
4423 super(LandingPage, self).__init__(wizard, "landing",
4424 xstr("landing_title"),
4425 xstr("landing_help"),
4426 completedHelp = xstr("landing_chelp"))
4427
4428 self._flightEnded = False
4429
4430 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
4431 xscale = 0.0, yscale = 0.0)
4432
4433 table = gtk.Table(7, 24)
4434 table.set_row_spacings(4)
4435 table.set_col_spacings(16)
4436 table.set_homogeneous(False)
4437 alignment.add(table)
4438 self.setMainWidget(alignment)
4439
4440 row = 0
4441
4442 label = gtk.Label(xstr("landing_metar"))
4443 label.set_use_underline(True)
4444 label.set_alignment(0.0, 0.5)
4445 table.attach(label, 1, 2, row, row+1)
4446
4447 self._metar = gtk.Entry()
4448 self._metar.set_width_chars(40)
4449 self._metar.set_tooltip_text(xstr("landing_metar_tooltip"))
4450 self._metar.connect("changed", self._metarChanged)
4451 self._metar.get_buffer().connect_after("inserted-text", self._metarInserted)
4452 table.attach(self._metar, 2, 24, row, row+1)
4453 label.set_mnemonic_widget(self._metar)
4454
4455 self._updatingMETAR = False
4456
4457 row += 1
4458
4459 label = gtk.Label(xstr("landing_star"))
4460 label.set_use_underline(True)
4461 label.set_alignment(0.0, 0.5)
4462 table.attach(label, 1, 2, row, row + 1)
4463
4464 if pygobject:
4465 self._star = gtk.ComboBox.new_with_model_and_entry(comboModel)
4466 else:
4467 self._star = gtk.ComboBoxEntry(comboModel)
4468
4469 self._star.set_entry_text_column(0)
4470 self._star.get_child().set_width_chars(10)
4471 self._star.set_tooltip_text(xstr("landing_star_tooltip"))
4472 self._star.connect("changed", self._upperChangedComboBox)
4473 self._star.set_sensitive(False)
4474 table.attach(self._star, 2, 4, row, row + 1)
4475 label.set_mnemonic_widget(self._star)
4476
4477 row += 1
4478
4479 label = gtk.Label(xstr("landing_transition"))
4480 label.set_use_underline(True)
4481 label.set_alignment(0.0, 0.5)
4482 table.attach(label, 1, 2, row, row + 1)
4483
4484 if pygobject:
4485 self._transition = gtk.ComboBox.new_with_model_and_entry(comboModel)
4486 else:
4487 self._transition = gtk.ComboBoxEntry(comboModel)
4488
4489 self._transition.set_entry_text_column(0)
4490 self._transition.get_child().set_width_chars(10)
4491 self._transition.set_tooltip_text(xstr("landing_transition_tooltip"))
4492 self._transition.connect("changed", self._upperChangedComboBox)
4493 self._transition.set_sensitive(False)
4494 table.attach(self._transition, 2, 4, row, row + 1)
4495 label.set_mnemonic_widget(self._transition)
4496
4497 row += 1
4498
4499 label = gtk.Label(xstr("landing_runway"))
4500 label.set_use_underline(True)
4501 label.set_alignment(0.0, 0.5)
4502 table.attach(label, 1, 2, row, row + 1)
4503
4504 self._runway = gtk.Entry()
4505 self._runway.set_width_chars(10)
4506 self._runway.set_tooltip_text(xstr("landing_runway_tooltip"))
4507 self._runway.connect("changed", self._upperChanged)
4508 table.attach(self._runway, 2, 4, row, row + 1)
4509 label.set_mnemonic_widget(self._runway)
4510
4511 row += 1
4512
4513 label = gtk.Label(xstr("landing_approach"))
4514 label.set_use_underline(True)
4515 label.set_alignment(0.0, 0.5)
4516 table.attach(label, 1, 2, row, row + 1)
4517
4518 self._approachType = gtk.Entry()
4519 self._approachType.set_width_chars(10)
4520 self._approachType.set_tooltip_text(xstr("landing_approach_tooltip"))
4521 self._approachType.connect("changed", self._upperChanged)
4522 table.attach(self._approachType, 2, 4, row, row + 1)
4523 label.set_mnemonic_widget(self._approachType)
4524
4525 row += 1
4526
4527 label = gtk.Label(xstr("landing_vref"))
4528 label.set_use_markup(True)
4529 label.set_use_underline(True)
4530 label.set_alignment(0.0, 0.5)
4531 table.attach(label, 1, 2, row, row + 1)
4532
4533 self._vref = IntegerEntry()
4534 self._vref.set_width_chars(5)
4535 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip_knots"))
4536 self._vref.connect("integer-changed", self._vrefChanged)
4537 table.attach(self._vref, 3, 4, row, row + 1)
4538 label.set_mnemonic_widget(self._vref)
4539
4540 self._vrefUnit = gtk.Label(xstr("label_knots"))
4541 table.attach(self._vrefUnit, 4, 5, row, row + 1)
4542
4543 row += 1
4544
4545 self._antiIceOn = gtk.CheckButton(xstr("landing_antiice"))
4546 self._antiIceOn.set_use_underline(True)
4547 self._antiIceOn.set_tooltip_text(xstr("landing_antiice_tooltip"))
4548 table.attach(self._antiIceOn, 3, 5, row, row + 1)
4549
4550 self.addCancelFlightButton()
4551
4552 self.addPreviousButton(clicked = self._backClicked)
4553
4554 self._button = self.addNextButton(clicked = self._forwardClicked)
4555
4556 self._active = False
4557
4558 @property
4559 def star(self):
4560 """Get the STAR or None if none entered."""
4561 text = self._star.get_child().get_text()
4562 return text if self._star.get_active()!=0 and text and text!="N/A" \
4563 else None
4564
4565 @property
4566 def transition(self):
4567 """Get the transition or None if none entered."""
4568 text = self._transition.get_child().get_text()
4569 return text if self._transition.get_active()!=0 and text and text!="N/A" \
4570 else None
4571
4572 @property
4573 def approachType(self):
4574 """Get the approach type."""
4575 return self._approachType.get_text()
4576
4577 @property
4578 def runway(self):
4579 """Get the runway."""
4580 return self._runway.get_text()
4581
4582 @property
4583 def vref(self):
4584 """Return the landing reference speed."""
4585 return self._vref.get_int()
4586
4587 @property
4588 def antiIceOn(self):
4589 """Get whether the anti-ice system has been turned on."""
4590 return self._antiIceOn.get_active()
4591
4592 @antiIceOn.setter
4593 def antiIceOn(self, value):
4594 """Set the anti-ice indicator."""
4595 self._antiIceOn.set_active(value)
4596
4597 def reset(self):
4598 """Reset the page if the wizard is reset."""
4599 super(LandingPage, self).reset()
4600 self._vref.reset()
4601 self._antiIceOn.set_active(False)
4602 self._flightEnded = False
4603 self._active = False
4604
4605 def activate(self):
4606 """Called when the page is activated."""
4607 self._updatingMETAR = True
4608 self._metar.get_buffer().set_text(self._wizard.arrivalMETAR, -1)
4609 self._updatingMETAR = False
4610
4611 self._star.set_active(0)
4612 self._star.set_sensitive(True)
4613
4614 self._transition.set_active(0)
4615 self._transition.set_sensitive(True)
4616
4617 if self._wizard.landingRunway is None:
4618 self._runway.set_text("")
4619 else:
4620 self._runway.set_text(self._wizard.landingRunway)
4621 self._runway.set_sensitive(True)
4622
4623 self._approachType.set_text("")
4624 self._approachType.set_sensitive(True)
4625
4626 self._vref.set_int(None)
4627 self._vref.set_sensitive(True)
4628
4629 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
4630 speedUnit = xstr("label" + i18nSpeedUnit)
4631 self._vrefUnit.set_text(speedUnit)
4632
4633 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip" +
4634 i18nSpeedUnit))
4635
4636 self._updateForwardButton()
4637
4638 self._active = True
4639
4640 def flightEnded(self):
4641 """Called when the flight has ended."""
4642 super(LandingPage, self).flightEnded()
4643 self._flightEnded = True
4644 self._updateForwardButton()
4645
4646 def changeMETAR(self, metar):
4647 """Change the METAR as a result of an edit on one of the other
4648 pages."""
4649 if self._active:
4650 print("LandingPage.changeMETAR")
4651 self._updatingMETAR = True
4652 self._metar.get_buffer().set_text(metar, -1)
4653 self._updatingMETAR = False
4654
4655 self._updateForwardButton()
4656
4657 def _updateForwardButton(self):
4658 """Update the sensitivity of the forward button."""
4659 sensitive = self._flightEnded and \
4660 self._metar.get_text()!="" and \
4661 (self.star is not None or
4662 self.transition is not None) and \
4663 self._runway.get_text()!="" and \
4664 self._approachType.get_text()!="" and \
4665 self.vref is not None
4666 self._button.set_sensitive(sensitive)
4667
4668 def _upperChanged(self, entry):
4669 """Called for entry widgets that must be converted to uppercase."""
4670 entry.set_text(entry.get_text().upper())
4671 self._updateForwardButton()
4672
4673 def _upperChangedComboBox(self, comboBox):
4674 """Called for combo box widgets that must be converted to uppercase."""
4675 if comboBox.get_active()==-1:
4676 entry = comboBox.get_child()
4677 entry.set_text(entry.get_text().upper())
4678 self._updateForwardButton()
4679
4680 def _vrefChanged(self, widget, value):
4681 """Called when the Vref has changed."""
4682 self._updateForwardButton()
4683
4684 def _backClicked(self, button):
4685 """Called when the Back button is pressed."""
4686 self.goBack()
4687
4688 def _forwardClicked(self, button):
4689 """Called when the forward button is clicked."""
4690 wizard = self._wizard
4691
4692 aircraft = wizard.gui.flight.aircraft
4693 aircraft.updateVRef()
4694 aircraft.updateLandingAntiIce()
4695 if wizard.gui.config.onlineGateSystem and \
4696 wizard.loggedIn and not self._completed and \
4697 wizard.bookedFlight.arrivalICAO=="LHBP" and \
4698 not wizard.entranceExam:
4699 wizard.getFleet(callback = self._fleetRetrieved, force = True)
4700 elif wizard.entranceExam:
4701 self._handleEntranceExamDone()
4702 else:
4703 wizard.nextPage()
4704
4705 def _fleetRetrieved(self, fleet):
4706 """Callback for the fleet retrieval."""
4707 self._wizard.nextPage()
4708
4709 def _metarChanged(self, entry):
4710 """Called when the METAR has changed."""
4711 print("LandingPage.metarChanged", self._updatingMETAR)
4712 if not self._updatingMETAR:
4713 self._updateForwardButton()
4714 self._wizard.metarChanged(entry.get_text(), self)
4715
4716 def _metarInserted(self, buffer, position, text, length):
4717 """Called when new characters are inserted into the METAR.
4718
4719 It uppercases all characters."""
4720 print("LandingPage.metarInserted", self._updatingMETAR)
4721 if not self._updatingMETAR:
4722 self._updatingMETAR = True
4723
4724 buffer.delete_text(position, length)
4725 buffer.insert_text(position, text.upper(), length)
4726
4727 self._updatingMETAR = False
4728
4729 def _handleEntranceExamDone(self):
4730 """Handle the end of the entrance exam.
4731
4732 If the there was a NO-GO fault, notify the user that exam is a failure
4733 and take them back to the student page. Otherwise congratulate, update
4734 the database to reflect that the exam has been taken and go back to the
4735 student page."""
4736 self._wizard.jumpPage("chkfinish")
4737
4738#-----------------------------------------------------------------------------
4739
4740class PIREPSaveHelper(object):
4741 """A helper to use for saving PIREPs."""
4742 def __init__(self, wizard):
4743 """Construct the helper."""
4744 super(PIREPSaveHelper, self).__init__()
4745
4746 self._wizard = wizard
4747
4748 self._lastSavePath = None
4749 self._savePIREPDialog = None
4750
4751 def addButton(self, page):
4752 """Add a button to save the PIREP to the given page."""
4753 return page.addButton(xstr("finish_save"), sensitive = False,
4754 clicked = self._saveClicked,
4755 tooltip = xstr("finish_save_tooltip"),
4756 clickedArg = page)
4757
4758 def autoSavePIREP(self, page):
4759 """Perform the automatic saving of the PIREP."""
4760 self._lastSavePath = os.path.join(self._wizard.gui.config.pirepDirectory,
4761 self._getDefaultPIREPName())
4762 self._lastSavePath = self._lastSavePath
4763 self._savePIREP(page, automatic = True)
4764
4765 def _getDefaultPIREPName(self):
4766 """Get the default name of the PIREP."""
4767 gui = self._wizard.gui
4768
4769 bookedFlight = gui.bookedFlight
4770 tm = time.gmtime()
4771
4772 pilotID = self._wizard.pilotID
4773 if pilotID: pilotID += " "
4774 return "%s%s %02d%02d %s-%s.pirep" % \
4775 (pilotID, str(bookedFlight.departureTime.date()),
4776 tm.tm_hour, tm.tm_min,
4777 bookedFlight.departureICAO, bookedFlight.arrivalICAO)
4778
4779 def _saveClicked(self, button, page):
4780 """Called when the Save PIREP button is clicked."""
4781 gui = self._wizard.gui
4782
4783 fileName = self._getDefaultPIREPName()
4784
4785 dialog = self._getSaveDialog()
4786
4787 if self._lastSavePath is None:
4788 pirepDirectory = gui.config.pirepDirectory
4789 if pirepDirectory is not None:
4790 dialog.set_current_folder(pirepDirectory)
4791 else:
4792 dialog.set_current_folder(os.path.dirname(self._lastSavePath))
4793
4794 dialog.set_current_name(fileName)
4795 result = dialog.run()
4796 dialog.hide()
4797
4798 if result==RESPONSETYPE_OK:
4799 self._lastSavePath = dialog.get_filename()
4800 self._savePIREP(page)
4801
4802 def _savePIREP(self, page, automatic = False):
4803 """Perform the saving of the PIREP."""
4804
4805 gui = self._wizard.gui
4806
4807 if automatic:
4808 gui.beginBusy(xstr("finish_autosave_busy"))
4809
4810 pirep = PIREP(gui.flight)
4811 error = pirep.save(self._lastSavePath)
4812
4813 if automatic:
4814 gui.endBusy()
4815
4816 if error:
4817 type = MESSAGETYPE_ERROR
4818 message = xstr("finish_save_failed")
4819 secondary = xstr("finish_save_failed_sec") % (error,)
4820 else:
4821 type = MESSAGETYPE_INFO
4822 message = xstr("finish_save_done")
4823 if automatic:
4824 secondary = xstr("finish_save_done_sec") % (self._lastSavePath,)
4825 else:
4826 secondary = None
4827 page.setPIREPSaved()
4828
4829 dialog = gtk.MessageDialog(parent = gui.mainWindow,
4830 type = type, message_format = message)
4831 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
4832 dialog.set_title(WINDOW_TITLE_BASE)
4833 if secondary is not None:
4834 dialog.format_secondary_markup(secondary)
4835
4836 dialog.run()
4837 dialog.hide()
4838
4839 def _getSaveDialog(self):
4840 """Get the PIREP saving dialog.
4841
4842 If it does not exist yet, create it."""
4843 if self._savePIREPDialog is None:
4844 gui = self._wizard.gui
4845 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
4846 xstr("finish_save_title"),
4847 action = FILE_CHOOSER_ACTION_SAVE,
4848 buttons = (gtk.STOCK_CANCEL,
4849 RESPONSETYPE_CANCEL,
4850 gtk.STOCK_OK, RESPONSETYPE_OK),
4851 parent = gui.mainWindow)
4852 dialog.set_modal(True)
4853 dialog.set_do_overwrite_confirmation(True)
4854
4855 filter = gtk.FileFilter()
4856 filter.set_name(xstr("file_filter_pireps"))
4857 filter.add_pattern("*.pirep")
4858 dialog.add_filter(filter)
4859
4860 filter = gtk.FileFilter()
4861 filter.set_name(xstr("file_filter_all"))
4862 filter.add_pattern("*.*")
4863 dialog.add_filter(filter)
4864
4865 self._savePIREPDialog = dialog
4866
4867 return self._savePIREPDialog
4868
4869#-----------------------------------------------------------------------------
4870
4871class FinishPage(Page):
4872 """Flight finish page."""
4873 def __init__(self, wizard, saveHelper):
4874 """Construct the finish page."""
4875 help = xstr("finish_help") + xstr("finish_help_goodtime")
4876 super(FinishPage, self).__init__(wizard, "finish",
4877 xstr("finish_title"), help)
4878
4879 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
4880 xscale = 0.0, yscale = 0.0)
4881
4882 table = gtk.Table(10, 2)
4883 table.set_row_spacings(4)
4884 table.set_col_spacings(16)
4885 table.set_homogeneous(False)
4886 alignment.add(table)
4887 self.setMainWidget(alignment)
4888
4889 row = 0
4890
4891 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4892 label = gtk.Label(xstr("finish_rating"))
4893 labelAlignment.add(label)
4894 table.attach(labelAlignment, 0, 1, row, row+1)
4895
4896 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4897 self._flightRating = gtk.Label()
4898 self._flightRating.set_width_chars(8)
4899 self._flightRating.set_alignment(0.0, 0.5)
4900 self._flightRating.set_use_markup(True)
4901 labelAlignment.add(self._flightRating)
4902 table.attach(labelAlignment, 1, 2, row, row+1)
4903
4904 row += 1
4905
4906 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4907 label = gtk.Label(xstr("finish_dep_time"))
4908 labelAlignment.add(label)
4909 table.attach(labelAlignment, 0, 1, row, row+1)
4910
4911 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4912 self._depTime = gtk.Label()
4913 self._depTime.set_width_chars(13)
4914 self._depTime.set_alignment(0.0, 0.5)
4915 self._depTime.set_use_markup(True)
4916 self._depTime.set_tooltip_markup(xstr("finish_dep_time_tooltip"))
4917 labelAlignment.add(self._depTime)
4918 table.attach(labelAlignment, 1, 2, row, row+1)
4919
4920 row += 1
4921
4922 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4923 label = gtk.Label(xstr("finish_flight_time"))
4924 labelAlignment.add(label)
4925 table.attach(labelAlignment, 0, 1, row, row+1)
4926
4927 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4928 self._flightTime = gtk.Label()
4929 self._flightTime.set_width_chars(10)
4930 self._flightTime.set_alignment(0.0, 0.5)
4931 self._flightTime.set_use_markup(True)
4932 labelAlignment.add(self._flightTime)
4933 table.attach(labelAlignment, 1, 2, row, row+1)
4934
4935 row += 1
4936
4937 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4938 label = gtk.Label(xstr("finish_block_time"))
4939 labelAlignment.add(label)
4940 table.attach(labelAlignment, 0, 1, row, row+1)
4941
4942 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4943 self._blockTime = gtk.Label()
4944 self._blockTime.set_width_chars(10)
4945 self._blockTime.set_alignment(0.0, 0.5)
4946 self._blockTime.set_use_markup(True)
4947 labelAlignment.add(self._blockTime)
4948 table.attach(labelAlignment, 1, 2, row, row+1)
4949
4950 row += 1
4951
4952 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4953 label = gtk.Label(xstr("finish_arr_time"))
4954 labelAlignment.add(label)
4955 table.attach(labelAlignment, 0, 1, row, row+1)
4956
4957 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4958 self._arrTime = gtk.Label()
4959 self._arrTime.set_width_chars(13)
4960 self._arrTime.set_alignment(0.0, 0.5)
4961 self._arrTime.set_use_markup(True)
4962 self._arrTime.set_tooltip_markup(xstr("finish_arr_time_tooltip"))
4963 labelAlignment.add(self._arrTime)
4964 table.attach(labelAlignment, 1, 2, row, row+1)
4965
4966 row += 1
4967
4968 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4969 label = gtk.Label(xstr("finish_distance"))
4970 labelAlignment.add(label)
4971 table.attach(labelAlignment, 0, 1, row, row+1)
4972
4973 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4974 self._distanceFlown = gtk.Label()
4975 self._distanceFlown.set_width_chars(10)
4976 self._distanceFlown.set_alignment(0.0, 0.5)
4977 self._distanceFlown.set_use_markup(True)
4978 labelAlignment.add(self._distanceFlown)
4979 table.attach(labelAlignment, 1, 2, row, row+1)
4980
4981 row += 1
4982
4983 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4984 label = gtk.Label(xstr("finish_fuel"))
4985 labelAlignment.add(label)
4986 table.attach(labelAlignment, 0, 1, row, row+1)
4987
4988 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4989 self._fuelUsed = gtk.Label()
4990 self._fuelUsed.set_width_chars(10)
4991 self._fuelUsed.set_alignment(0.0, 0.5)
4992 self._fuelUsed.set_use_markup(True)
4993 labelAlignment.add(self._fuelUsed)
4994 table.attach(labelAlignment, 1, 2, row, row+1)
4995
4996 row += 1
4997
4998 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
4999 yalign = 0.5, yscale = 0.0)
5000 label = gtk.Label(xstr("finish_type"))
5001 label.set_use_underline(True)
5002 labelAlignment.add(label)
5003 table.attach(labelAlignment, 0, 1, row, row+1)
5004
5005 self._flightType = createFlightTypeComboBox()
5006 self._flightType.set_tooltip_text(xstr("finish_type_tooltip"))
5007 self._flightType.set_active(0)
5008 self._flightType.connect("changed", self._flightTypeChanged)
5009 flightTypeAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
5010 flightTypeAlignment.add(self._flightType)
5011 table.attach(flightTypeAlignment, 1, 2, row, row+1)
5012 label.set_mnemonic_widget(self._flightType)
5013
5014 row += 1
5015
5016 self._onlineFlight = gtk.CheckButton(xstr("finish_online"))
5017 self._onlineFlight.set_use_underline(True)
5018 self._onlineFlight.set_tooltip_text(xstr("finish_online_tooltip"))
5019 onlineFlightAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
5020 onlineFlightAlignment.add(self._onlineFlight)
5021 table.attach(onlineFlightAlignment, 1, 2, row, row + 1)
5022
5023 row += 1
5024
5025 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
5026 yalign = 0.5, yscale = 0.0)
5027 self._gateLabel = gtk.Label(xstr("finish_gate"))
5028 self._gateLabel.set_use_underline(True)
5029 labelAlignment.add(self._gateLabel)
5030 table.attach(labelAlignment, 0, 1, row, row+1)
5031
5032 self._gatesModel = gtk.ListStore(str)
5033
5034 self._gate = gtk.ComboBox(model = self._gatesModel)
5035 renderer = gtk.CellRendererText()
5036 self._gate.pack_start(renderer, True)
5037 self._gate.add_attribute(renderer, "text", 0)
5038 self._gate.set_tooltip_text(xstr("finish_gate_tooltip"))
5039 self._gate.connect("changed", self._gateChanged)
5040 gateAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
5041 gateAlignment.add(self._gate)
5042 table.attach(gateAlignment, 1, 2, row, row+1)
5043 self._gateLabel.set_mnemonic_widget(self._gate)
5044
5045 self.addButton(xstr("finish_newFlight"),
5046 sensitive = True,
5047 clicked = self._newFlightClicked,
5048 tooltip = xstr("finish_newFlight_tooltip"),
5049 padding = 16)
5050
5051 self.addPreviousButton(clicked = self._backClicked)
5052
5053 self._saveHelper = saveHelper
5054 self._saveButton = saveHelper.addButton(self)
5055
5056 self._tooBigTimeDifference = False
5057 self._deferredAutoSave = False
5058 self._pirepSaved = False
5059 self._pirepSent = False
5060
5061 self._sendButton = self.addButton(xstr("sendPIREP"), default = True,
5062 sensitive = False,
5063 clicked = self._sendClicked,
5064 tooltip = xstr("sendPIREP_tooltip"))
5065
5066 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 10*60.0)
5067 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 20*60.0)
5068 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 0*60.0)
5069 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), (23*60.0+50)*60.0)
5070 # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (1*60.0+5)*60.0)
5071 # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (0*60.0+50)*60.0)
5072 # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (0*60.0+5)*60.0)
5073 # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (23*60.0+45)*60.0)
5074
5075 @property
5076 def flightType(self):
5077 """Get the flight type."""
5078 index = self._flightType.get_active()
5079 return None if index<0 else self._flightType.get_model()[index][1]
5080
5081 @property
5082 def online(self):
5083 """Get whether the flight was an online flight or not."""
5084 return self._onlineFlight.get_active()
5085
5086 def activate(self):
5087 """Activate the page."""
5088 self._deferredAutoSave = False
5089 self._pirepSaved = False
5090 self._pirepSent = False
5091
5092 flight = self._wizard.gui._flight
5093 rating = flight.logger.getRating()
5094 if rating<0:
5095 self._flightRating.set_markup('<b><span foreground="red">NO GO</span></b>')
5096 else:
5097 self._flightRating.set_markup("<b>%.1f %%</b>" % (rating,))
5098
5099 flightLength = flight.flightTimeEnd - flight.flightTimeStart
5100 self._flightTime.set_markup("<b>%s</b>" % \
5101 (util.getTimeIntervalString(flightLength),))
5102
5103 blockLength = flight.blockTimeEnd - flight.blockTimeStart
5104 self._blockTime.set_markup("<b>%s</b>" % \
5105 (util.getTimeIntervalString(blockLength),))
5106
5107 self._distanceFlown.set_markup("<b>%.2f NM</b>" % \
5108 (flight.flownDistance,))
5109
5110 self._fuelUsed.set_markup("<b>%.0f kg</b>" % \
5111 (flight.startFuel - flight.endFuel,))
5112
5113 self._flightType.set_active(-1)
5114 self._onlineFlight.set_active(self._wizard.loggedIn)
5115
5116 self._gatesModel.clear()
5117 if self._wizard.gui.config.onlineGateSystem and \
5118 self._wizard.loggedIn and \
5119 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
5120 not self._wizard.entranceExam:
5121 occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
5122 for gate in lhbpGates.gates:
5123 if gate.isAvailable(lhbpGates, occupiedGateNumbers):
5124 self._gatesModel.append([gate.number])
5125 self._gateLabel.set_sensitive(True)
5126 self._gate.set_sensitive(True)
5127 self._gate.set_active(-1)
5128 else:
5129 self._gateLabel.set_sensitive(False)
5130 self._gate.set_sensitive(False)
5131
5132 self._updateTimes()
5133
5134 def updateButtons(self):
5135 """Update the sensitivity state of the buttons."""
5136 gui = self._wizard.gui
5137 faultsExplained = gui.faultsFullyExplained
5138 timesCorrect = self.flightType is None or \
5139 not self._tooBigTimeDifference or \
5140 gui.hasComments or gui.hasDelayCode
5141 sensitive = gui.flight is not None and \
5142 gui.flight.stage==const.STAGE_END and \
5143 self._flightType.get_active()>=0 and \
5144 (self._gatesModel.get_iter_first() is None or
5145 self._gate.get_active()>=0) and \
5146 faultsExplained and timesCorrect
5147
5148 self._updateHelp(faultsExplained, timesCorrect)
5149
5150 wasSensitive = self._saveButton.get_sensitive()
5151
5152 if gui.config.pirepAutoSave and sensitive and not wasSensitive:
5153 if gui.isWizardActive():
5154 self._saveHelper.autoSavePIREP(self)
5155 else:
5156 self._deferredAutoSave = True
5157
5158 if not sensitive:
5159 self._deferredAutoSave = False
5160
5161 self._saveButton.set_sensitive(sensitive)
5162 self._sendButton.set_sensitive(sensitive and
5163 self._wizard.bookedFlight.id is not None)
5164
5165 def grabDefault(self):
5166 """If the page has a default button, make it the default one."""
5167 super(FinishPage, self).grabDefault()
5168 if self._deferredAutoSave:
5169 self._saveHelper.autoSavePIREP(self)
5170 self._deferredAutoSave = False
5171
5172 def setPIREPSaved(self):
5173 """Mark the PIREP as saved."""
5174 self._pirepSaved = True
5175
5176 def _backClicked(self, button):
5177 """Called when the Back button is pressed."""
5178 self.goBack()
5179
5180 def _flightTypeChanged(self, comboBox):
5181 """Called when the flight type has changed."""
5182 self._updateTimes()
5183
5184 def _gateChanged(self, comboBox):
5185 """Called when the arrival gate has changed."""
5186 self.updateButtons()
5187
5188 def _newFlightClicked(self, button):
5189 """Called when the new flight button is clicked."""
5190 gui = self._wizard.gui
5191 if not self._pirepSent and not self._pirepSaved:
5192 dialog = gtk.MessageDialog(parent = gui.mainWindow,
5193 type = MESSAGETYPE_QUESTION,
5194 message_format = xstr("finish_newFlight_question"))
5195
5196 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
5197 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
5198
5199 dialog.set_title(WINDOW_TITLE_BASE)
5200 result = dialog.run()
5201 dialog.hide()
5202 if result!=RESPONSETYPE_YES:
5203 return
5204
5205 gui.reset()
5206
5207 def _sendClicked(self, button):
5208 """Called when the Send button is clicked."""
5209 pirep = PIREP(self._wizard.gui.flight)
5210 self._wizard.gui.sendPIREP(pirep,
5211 callback = self._handlePIREPSent)
5212
5213 def _handlePIREPSent(self, returned, result):
5214 """Callback for the PIREP sending result."""
5215 self._pirepSent = returned and result.success
5216 if self._wizard.gui.config.onlineGateSystem and \
5217 self._wizard.loggedIn and not self._wizard.entranceExam and \
5218 returned and result.success:
5219 bookedFlight = self._wizard.bookedFlight
5220 if bookedFlight.arrivalICAO=="LHBP":
5221 iter = self._gate.get_active_iter()
5222 gateNumber = None if iter is None \
5223 else self._gatesModel.get_value(iter, 0)
5224
5225 status = const.PLANE_PARKING if gateNumber is None \
5226 else const.PLANE_HOME
5227 else:
5228 gateNumber = None
5229 status = const.PLANE_AWAY
5230
5231 self._wizard.updatePlane(self._planeUpdated,
5232 bookedFlight.tailNumber,
5233 status, gateNumber = gateNumber)
5234
5235 def _planeUpdated(self, success):
5236 """Callback for the plane updating."""
5237 pass
5238
5239 def _formatTime(self, scheduledTime, realTimestamp, xxx_todo_changeme):
5240 """Format the departure or arrival time based on the given data as a
5241 markup for a label."""
5242 (warning, error) = xxx_todo_changeme
5243 realTime = time.gmtime(realTimestamp)
5244
5245 if warning:
5246 colour = "red" if error else "orange"
5247 markupBegin = '<span foreground="%s">' % (colour,)
5248 markupEnd = '</span>'
5249 else:
5250 markupBegin = markupEnd = ""
5251
5252 markup = "<b>%s%02d:%02d [%02d:%02d]%s</b>" % \
5253 (markupBegin,
5254 realTime.tm_hour, realTime.tm_min,
5255 scheduledTime.hour, scheduledTime.minute,
5256 markupEnd)
5257
5258 return markup
5259
5260 def _updateTimes(self):
5261 """Format the flight times and the help text according to the flight
5262 type.
5263
5264 The buttons are also updated.
5265 """
5266 flight = self._wizard.gui._flight
5267 bookedFlight = flight.bookedFlight
5268
5269 (departureWarning, departureError) = flight.blockTimeStartWrong
5270 (arrivalWarning, arrivalError) = flight.blockTimeEndWrong
5271
5272 if self.flightType==const.FLIGHTTYPE_VIP:
5273 departureError = arrivalError = False
5274
5275 self._tooBigTimeDifference = departureError or arrivalError
5276
5277 self._depTime.set_markup(self._formatTime(bookedFlight.departureTime,
5278 flight.blockTimeStart,
5279 (departureWarning,
5280 departureError)))
5281
5282 self._arrTime.set_markup(self._formatTime(bookedFlight.arrivalTime,
5283 flight.blockTimeEnd,
5284 (arrivalWarning,
5285 arrivalError)))
5286
5287 self.updateButtons()
5288
5289 def _updateHelp(self, faultsExplained, timesCorrect):
5290 """Update the help text according to the actual situation."""
5291 if not faultsExplained:
5292 self.setHelp(xstr("finish_help") + xstr("finish_help_faults"))
5293 elif not timesCorrect:
5294 self.setHelp(xstr("finish_help") + xstr("finish_help_wrongtime"))
5295 else:
5296 self.setHelp(xstr("finish_help") + xstr("finish_help_goodtime"))
5297
5298
5299#-----------------------------------------------------------------------------
5300
5301class CheckFlightFinishPage(Page):
5302 """Finish page for a check flight."""
5303 def __init__(self, wizard, saveHelper):
5304 """Construct the check flight finish page."""
5305 super(CheckFlightFinishPage, self).__init__(wizard,
5306 "chkfinish",
5307 xstr("chkfinish_title"),
5308 "")
5309
5310 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
5311 xscale = 1.0, yscale = 1.0)
5312 self._label = gtk.Label()
5313 alignment.add(self._label)
5314
5315 self.setMainWidget(alignment)
5316
5317 self._saveHelper = saveHelper
5318 self._saveButton = saveHelper.addButton(self)
5319
5320 self._button = self.addNextButton(sensitive = False,
5321 clicked = self._forwardClicked)
5322
5323 def activate(self):
5324 """Activate the page."""
5325 wizard = self._wizard
5326 loginResult = wizard.loginResult
5327 gui = wizard.gui
5328 rating = gui.flight.logger.getRating()
5329
5330 if rating>=0:
5331 loginResult.checkFlightStatus = True
5332
5333 firstOfficer = \
5334 loginResult.entryExamPassed and loginResult.checkFlightStatus
5335
5336 if firstOfficer:
5337 loginResult.rank = "FO"
5338
5339 if rating<0:
5340 mainMessage = xstr("chkfinish_failed")
5341 else:
5342 mainMessage = xstr("chkfinish_passed_begin")
5343 if firstOfficer:
5344 mainMessage += xstr("chkfinish_passed_fo")
5345 mainMessage += xstr("chkfinish_passed_end")
5346
5347 if firstOfficer:
5348 nextMessage = xstr("chkfinish_next")
5349 else:
5350 nextMessage = xstr("chkfinish_next_student_begin")
5351 if not loginResult.entryExamPassed and \
5352 not loginResult.checkFlightStatus:
5353 nextMessage += xstr("chkfinish_next_student_nothing")
5354 elif loginResult.entryExamPassed and \
5355 not loginResult.checkFlightStatus:
5356 nextMessage += xstr("chkfinish_next_student_no_flight")
5357 elif not loginResult.entryExamPassed and \
5358 loginResult.checkFlightStatus:
5359 nextMessage += xstr("chkfinish_next_student_no_exam")
5360
5361 self._label.set_text(mainMessage +
5362 xstr("chkfinish_savepirep") +
5363 nextMessage)
5364 self._label.set_use_markup(True)
5365 self._label.set_alignment(0.5, 0.0)
5366
5367 self._saveButton.set_sensitive(True)
5368 self._button.set_sensitive(True)
5369
5370 def _forwardClicked(self, button):
5371 """Jump to the student page if there are some tasks to do,
5372 or to the flight selection page, if the pilot is allowed to perform
5373 MAVA flights."""
5374 wizard = self._wizard
5375 gui = wizard.gui
5376
5377 loginResult = wizard.loginResult
5378 if loginResult.checkFlightStatus:
5379 gui.beginBusy(xstr("chkfinish_updateweb_busy"))
5380 gui.webHandler.setCheckFlightPassed(self._checkFlightPassedSetCallback,
5381 wizard.checkFlightAircraftType)
5382 else:
5383 self._resetGUI()
5384
5385 def _checkFlightPassedSetCallback(self, returned, result):
5386 """Called when the check flight status has been set."""
5387 gobject.idle_add(self._checkFlightPassedSet, returned, result)
5388
5389 def _checkFlightPassedSet(self, returned, result):
5390 """Handle the result of an attempt to set the check flight status."""
5391 gui = self._wizard.gui
5392
5393 gui.endBusy()
5394
5395 if returned:
5396 self._resetGUI()
5397 else:
5398 dialog = gtk.MessageDialog(parent = gui.mainWindow,
5399 type = MESSAGETYPE_ERROR,
5400 message_format =
5401 xstr("chkfinish_passedset_failed"))
5402 dialog.set_title(WINDOW_TITLE_BASE + " - " +
5403 xstr("chkfinish_passedset_failed_title"))
5404 dialog.format_secondary_markup(xstr("chkfinish_passedset_failed_secondary"))
5405
5406 dialog.add_button(xstr("button_ok"), 0)
5407
5408 dialog.run()
5409 dialog.hide()
5410
5411 def _resetGUI(self):
5412 """Reset the GUI."""
5413 gui = self._wizard.gui
5414 gui.reset()
5415
5416#-----------------------------------------------------------------------------
5417
5418class Wizard(gtk.VBox):
5419 """The flight wizard."""
5420 def __init__(self, gui):
5421 """Construct the wizard."""
5422 super(Wizard, self).__init__()
5423
5424 self.gui = gui
5425
5426 self._pages = []
5427 self._currentPage = None
5428
5429 self._loginPage = LoginPage(self)
5430 self._pages.append(self._loginPage)
5431 self._flightSelectionPage = FlightSelectionPage(self)
5432 self._pages.append(self._flightSelectionPage)
5433 self._pages.append(GateSelectionPage(self))
5434 self._pages.append(RegisterPage(self))
5435 self._studentPage = StudentPage(self)
5436 self._pages.append(self._studentPage)
5437 self._pages.append(ConnectPage(self))
5438 self._payloadPage = PayloadPage(self)
5439 self._pages.append(self._payloadPage)
5440 self._payloadIndex = len(self._pages)
5441 self._pages.append(TimePage(self))
5442 self._routePage = RoutePage(self)
5443 self._pages.append(self._routePage)
5444 self._simBriefSetupPage = SimBriefSetupPage(self)
5445 self._pages.append(self._simBriefSetupPage)
5446 self._simBriefingPage = SimBriefingPage(self)
5447 self._pages.append(self._simBriefingPage)
5448 self._pages.append(FuelPage(self))
5449 self._departureBriefingPage = BriefingPage(self, True)
5450 self._pages.append(self._departureBriefingPage)
5451 self._arrivalBriefingPage = BriefingPage(self, False)
5452 self._pages.append(self._arrivalBriefingPage)
5453 self._arrivalBriefingIndex = len(self._pages)
5454 self._takeoffPage = TakeoffPage(self)
5455 self._pages.append(self._takeoffPage)
5456 self._cruisePage = CruisePage(self)
5457 self._pages.append(self._cruisePage)
5458 self._landingPage = LandingPage(self)
5459 self._pages.append(self._landingPage)
5460
5461 pirepSaveHelper = PIREPSaveHelper(self)
5462
5463 self._finishPage = FinishPage(self, pirepSaveHelper)
5464 self._pages.append(self._finishPage)
5465 self._pages.append(CheckFlightFinishPage(self, pirepSaveHelper))
5466
5467 self._requestedWidth = None
5468 self._requestedHeight = None
5469
5470 self.connect("size-allocate", self._sizeAllocate)
5471
5472 for page in self._pages:
5473 page.show_all()
5474 page.setStyle()
5475
5476 self._initialize()
5477
5478 def _sizeAllocate(self, widget, allocation):
5479 if self._requestedWidth is not None and \
5480 self._requestedHeight is not None:
5481 return
5482
5483 if self._currentPage is not None:
5484 self.remove(self._pages[self._currentPage])
5485
5486 maxWidth = 0
5487 maxHeight = 0
5488 for page in self._pages:
5489 self.add(page)
5490 self.show_all()
5491 pageSizeRequest = page.size_request()
5492 width = pageSizeRequest.width if pygobject else pageSizeRequest[0]
5493 height = pageSizeRequest.height if pygobject else pageSizeRequest[1]
5494 maxWidth = max(maxWidth, width)
5495 maxHeight = max(maxHeight, height)
5496 self.remove(page)
5497
5498 if self._currentPage is not None:
5499 self.add(self._pages[self._currentPage])
5500
5501 self._requestedWidth = maxWidth
5502 self._requestedHeight = maxHeight
5503 self.set_size_request(maxWidth, maxHeight)
5504
5505 @property
5506 def pilotID(self):
5507 """Get the pilot ID, if given."""
5508 return self._loginPage.pilotID
5509
5510 @property
5511 def entranceExam(self):
5512 """Get whether an entrance exam is about to be taken."""
5513 return self._loginResult is not None and self._loginResult.rank=="STU"
5514
5515 @property
5516 def loggedIn(self):
5517 """Indicate if there was a successful login."""
5518 return self._loginResult is not None
5519
5520 @property
5521 def loginResult(self):
5522 """Get the login result."""
5523 return self._loginResult
5524
5525 @property
5526 def checkFlightAircraftType(self):
5527 """Get the type of the aircraft used to perform the check flight."""
5528 return self._studentPage.aircraftType
5529
5530 def setCurrentPage(self, index, finalize = False, fromPageShift = None):
5531 """Set the current page to the one with the given index.
5532
5533 @param fromPageShift if given, the relative index of one of the
5534 previous pages that should be used as the from-page of the next
5535 page. E.g. if fromPageShift is 1, the previous page will be the
5536 from-page."""
5537 assert index < len(self._pages)
5538
5539 fromPage = self._currentPage
5540 if fromPage is not None:
5541 page = self._pages[fromPage]
5542 if finalize and not page._completed:
5543 page.complete()
5544 self.remove(page)
5545 if fromPageShift is not None:
5546 fromPage -= fromPageShift
5547
5548 self._currentPage = index
5549 page = self._pages[index]
5550 self.add(page)
5551 if page._fromPage is None:
5552 page._fromPage = fromPage
5553 page.initialize()
5554 self.show_all()
5555 if fromPage is not None:
5556 self.grabDefault()
5557
5558 @property
5559 def bookedFlight(self):
5560 """Get the booked flight selected."""
5561 return self._bookedFlight
5562
5563 @property
5564 def numCrew(self):
5565 """Get the number of crew members."""
5566 return self._payloadPage.numCrew
5567
5568 @property
5569 def numPassengers(self):
5570 """Get the number of passengers."""
5571 return self._payloadPage.numPassengers
5572
5573 @property
5574 def bagWeight(self):
5575 """Get the baggage weight."""
5576 return self._payloadPage.bagWeight
5577
5578 @property
5579 def cargoWeight(self):
5580 """Get the cargo weight."""
5581 return self._payloadPage.cargoWeight
5582
5583 @property
5584 def mailWeight(self):
5585 """Get the mail weight."""
5586 return self._payloadPage.mailWeight
5587
5588 @property
5589 def zfw(self):
5590 """Get the calculated ZFW value."""
5591 return 0 if self._bookedFlight is None \
5592 else self._payloadPage.calculateZFW()
5593
5594 @property
5595 def filedCruiseLevel(self):
5596 """Get the filed cruise level."""
5597 return self._routePage.filedCruiseLevel
5598
5599 @property
5600 def filedCruiseAltitude(self):
5601 """Get the filed cruise altitude."""
5602 return self._routePage.filedCruiseLevel * 100
5603
5604 @property
5605 def cruiseAltitude(self):
5606 """Get the cruise altitude."""
5607 level = self._cruisePage.cruiseLevel if self._cruisePage.activated \
5608 else self._routePage.filedCruiseLevel
5609 return level * 100
5610
5611 @property
5612 def loggableCruiseAltitude(self):
5613 """Get the cruise altitude that can be logged."""
5614 if self._cruisePage.activated:
5615 return self._cruisePage.loggableCruiseLevel * 100
5616 else:
5617 return 0
5618
5619 @property
5620 def route(self):
5621 """Get the route."""
5622 return self._routePage.route
5623
5624 @property
5625 def alternate(self):
5626 """Get the ICAO code of the alternate airport."""
5627 return self._routePage.alternate
5628
5629 @property
5630 def departureMETAR(self):
5631 """Get the METAR of the departure airport."""
5632 return self._departureBriefingPage.metar
5633
5634 @property
5635 def arrivalMETAR(self):
5636 """Get the METAR of the arrival airport."""
5637 return self._arrivalBriefingPage.metar
5638
5639 @property
5640 def departureRunway(self):
5641 """Get the departure runway."""
5642 return self._takeoffPage.runway
5643
5644 @property
5645 def sid(self):
5646 """Get the SID."""
5647 return self._takeoffPage.sid
5648
5649 @property
5650 def v1(self):
5651 """Get the V1 speed."""
5652 return self._takeoffPage.v1
5653
5654 @property
5655 def vr(self):
5656 """Get the Vr speed."""
5657 return self._takeoffPage.vr
5658
5659 @property
5660 def v2(self):
5661 """Get the V2 speed."""
5662 return self._takeoffPage.v2
5663
5664 @property
5665 def derate(self):
5666 """Get the derate value."""
5667 return self._takeoffPage.derate
5668
5669 @property
5670 def takeoffAntiIceOn(self):
5671 """Get whether the anti-ice system was on during take-off."""
5672 return self._takeoffPage.antiIceOn
5673
5674 @takeoffAntiIceOn.setter
5675 def takeoffAntiIceOn(self, value):
5676 """Set anti-ice on indicator."""
5677 self._takeoffPage.antiIceOn = value
5678
5679 @property
5680 def rtoIndicated(self):
5681 """Get whether the pilot has indicated that an RTO has occured."""
5682 return self._takeoffPage.rtoIndicated
5683
5684 @property
5685 def arrivalRunway(self):
5686 """Get the arrival runway."""
5687 return self._landingPage.runway
5688
5689 @property
5690 def star(self):
5691 """Get the STAR."""
5692 return self._landingPage.star
5693
5694 @property
5695 def transition(self):
5696 """Get the transition."""
5697 return self._landingPage.transition
5698
5699 @property
5700 def approachType(self):
5701 """Get the approach type."""
5702 return self._landingPage.approachType
5703
5704 @property
5705 def vref(self):
5706 """Get the Vref speed."""
5707 return self._landingPage.vref
5708
5709 @property
5710 def landingAntiIceOn(self):
5711 """Get whether the anti-ice system was on during landing."""
5712 return self._landingPage.antiIceOn
5713
5714 @landingAntiIceOn.setter
5715 def landingAntiIceOn(self, value):
5716 """Set anti-ice on indicator."""
5717 self._landingPage.antiIceOn = value
5718
5719 @property
5720 def flightType(self):
5721 """Get the flight type."""
5722 return self._finishPage.flightType
5723
5724 @property
5725 def online(self):
5726 """Get whether the flight was online or not."""
5727 return self._finishPage.online
5728
5729 @property
5730 def usingSimBrief(self):
5731 """Indicate if we are using a SimBrief briefing or not."""
5732 return self._usingSimBrief
5733
5734 @usingSimBrief.setter
5735 def usingSimBrief(self, x):
5736 """Set whether we are using a SimBrief briefing or not."""
5737 self._usingSimBrief = x
5738
5739 def nextPage(self, finalize = True):
5740 """Go to the next page."""
5741 nextPageID = self._pages[self._currentPage].nextPageID
5742 self.jumpPage(1 if nextPageID is None else nextPageID, finalize)
5743
5744 def jumpPage(self, countOrID, finalize = True, fromPageShift = None):
5745 """Go to the page which is 'count' pages after the current one."""
5746 if isinstance(countOrID, str):
5747 targetIndex = self._getIndexOf(countOrID)
5748 else:
5749 targetIndex = self._currentPage + countOrID
5750 self.setCurrentPage(targetIndex,
5751 finalize = finalize, fromPageShift = fromPageShift)
5752
5753 def grabDefault(self):
5754 """Make the default button of the current page the default."""
5755 self._pages[self._currentPage].grabDefault()
5756
5757 def connected(self, fsType, descriptor):
5758 """Called when the connection could be made to the simulator."""
5759 self.nextPage()
5760
5761 def reset(self, loginResult):
5762 """Resets the wizard to go back to the login page."""
5763 self._initialize(keepLoginResult = loginResult is None,
5764 loginResult = loginResult)
5765
5766 def setStage(self, stage):
5767 """Set the flight stage to the given one."""
5768 if stage!=const.STAGE_END:
5769 self._cruisePage.setLoggable(Flight.canLogCruiseAltitude(stage))
5770
5771 if stage==const.STAGE_TAKEOFF:
5772 self._takeoffPage.allowForward()
5773 elif stage==const.STAGE_LANDING:
5774 if not self._arrivalBriefingPage.metarEdited:
5775 print("Downloading arrival METAR again")
5776 self.gui.webHandler.getMETARs(self._arrivalMETARCallback,
5777 [self._bookedFlight.arrivalICAO])
5778
5779 elif stage==const.STAGE_END:
5780 for page in self._pages:
5781 page.flightEnded()
5782
5783 def _initialize(self, keepLoginResult = False, loginResult = None):
5784 """Initialize the wizard."""
5785 if not keepLoginResult:
5786 self._loginResult = loginResult
5787
5788 self._loginCallback = None
5789
5790 self._fleet = None
5791 self._fleetCallback = None
5792
5793 self._bookedFlight = None
5794 self._departureGate = "-"
5795 self._fuelData = None
5796 self._departureNOTAMs = None
5797 self._departureMETAR = None
5798 self._arrivalNOTAMs = None
5799 self._arrivalMETAR = None
5800 self._usingSimBrief = None
5801 self.takeoffRunway = None
5802 self.landingRunway = None
5803
5804 firstPage = 0 if self._loginResult is None else 1
5805 for page in self._pages[firstPage:]:
5806 page.reset()
5807
5808 self.setCurrentPage(firstPage)
5809 #self.setCurrentPage(10)
5810
5811 def login(self, callback, pilotID, password):
5812 """Called when the login button was clicked."""
5813 self._loginCallback = callback
5814 if pilotID is None:
5815 loginResult = self._loginResult
5816 assert loginResult is not None and loginResult.loggedIn
5817 pilotID = loginResult.pilotID
5818 password = loginResult.password
5819 busyMessage = xstr("reload_busy")
5820 else:
5821 self._loginResult = None
5822 busyMessage = xstr("login_busy")
5823
5824 self.gui.beginBusy(busyMessage)
5825
5826 self.gui.webHandler.login(self._loginResultCallback,
5827 pilotID, password)
5828
5829 def reloadFlights(self, callback):
5830 """Reload the flights from the MAVA server."""
5831 self.login(callback, None, None)
5832
5833 def addFlight(self, bookedFlight):
5834 """Add the given booked flight to the flight selection page."""
5835 self._flightSelectionPage.addFlight(bookedFlight)
5836
5837 def reflyFlight(self, bookedFlight):
5838 """Add the given booked flight to the flight selection page."""
5839 self._removePendingFlight(bookedFlight)
5840 self._flightSelectionPage._reflyFlight(bookedFlight)
5841
5842 def deleteFlight(self, bookedFlight):
5843 """Remove the given flight from the pending flight list."""
5844 self._removePendingFlight(bookedFlight)
5845 self._flightSelectionPage._updatePending()
5846
5847 def cancelFlight(self, reloadCallback):
5848 """Cancel the flight.
5849
5850 If it is an entry exam flight, we go back to the student page.
5851 Otherwise we reload the flights."""
5852 if self.entranceExam:
5853 self.reset(None)
5854 self.jumpPage("student")
5855 else:
5856 self.reloadFlights(reloadCallback)
5857
5858 def cruiseLevelChanged(self):
5859 """Called when the cruise level is changed."""
5860 return self.gui.cruiseLevelChanged()
5861
5862 def metarChanged(self, metar, originator):
5863 """Called when a METER is changed on on of the pages.
5864
5865 originator is the page that originated the changed. It will be used to
5866 determine which METAR (departure or arrival) has changed."""
5867 metar = metar.upper()
5868 if originator in [self._departureBriefingPage, self._takeoffPage]:
5869 self.departureMETARChanged(metar, originator)
5870 else:
5871 self.arrivalMETARChanged(metar, originator)
5872
5873 def departureMETARChanged(self, metar, originator):
5874 """Called when the departure METAR has been edited on one of the
5875 pages.
5876
5877 originator is the page that originated the change. It will not be
5878 called to set the METAR, while others will be."""
5879 for page in [self._departureBriefingPage, self._takeoffPage]:
5880 if page is not originator:
5881 page.changeMETAR(metar)
5882
5883 def arrivalMETARChanged(self, metar, originator):
5884 """Called when the arrival METAR has been edited on one of the
5885 pages.
5886
5887 originator is the page that originated the change. It will not be
5888 called to set the METAR, while others will be."""
5889 for page in [self._arrivalBriefingPage, self._landingPage]:
5890 if page is not originator:
5891 page.changeMETAR(metar)
5892
5893 def _removePendingFlight(self, flight):
5894 """Remove the given pending flight from the login result."""
5895 for flights in [self._loginResult.reportedFlights,
5896 self._loginResult.rejectedFlights]:
5897 for f in flights:
5898 if f.id==flight.id:
5899 flights.remove(f)
5900 return
5901
5902 def _loginResultCallback(self, returned, result):
5903 """The login result callback, called in the web handler's thread."""
5904 gobject.idle_add(self._handleLoginResult, returned, result)
5905
5906 def _handleLoginResult(self, returned, result):
5907 """Handle the login result."""
5908 self.gui.endBusy()
5909 isReload = self._loginResult is not None
5910 if returned:
5911 if result.loggedIn:
5912 self._loginResult = result
5913 self.gui.loginSuccessful()
5914 else:
5915 if isReload:
5916 message = xstr("reload_failed")
5917 else:
5918 message = xstr("login_entranceExam_invalid"
5919 if self.entranceExam else
5920 xstr("login_invalid"))
5921 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
5922 type = MESSAGETYPE_ERROR,
5923 message_format = message)
5924 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
5925 dialog.set_title(WINDOW_TITLE_BASE)
5926 if isReload:
5927 secondary = xstr("reload_failed_sec")
5928 else:
5929 secondary = xstr("login_entranceExam_invalid_sec"
5930 if self.entranceExam else
5931 xstr("login_invalid_sec"))
5932 dialog.format_secondary_markup(secondary)
5933 dialog.run()
5934 dialog.hide()
5935 else:
5936 message = xstr("reload_failconn") if isReload \
5937 else xstr("login_failconn")
5938 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
5939 type = MESSAGETYPE_ERROR,
5940 message_format = message)
5941 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
5942 dialog.set_title(WINDOW_TITLE_BASE)
5943 secondary = xstr("reload_failconn_sec") if isReload \
5944 else xstr("login_failconn_sec")
5945 dialog.format_secondary_markup(secondary)
5946
5947 dialog.run()
5948 dialog.hide()
5949
5950 callback = self._loginCallback
5951 self._loginCallback = None
5952 callback(returned, result)
5953
5954 def getFleet(self, callback, force = False):
5955 """Get the fleet via the GUI and call the given callback."""
5956 self._fleetCallback = callback
5957 self.gui.getFleet(callback = self._fleetRetrieved, force = force)
5958
5959 def _fleetRetrieved(self, fleet):
5960 """Callback for the fleet retrieval."""
5961 self._fleet = fleet
5962 if self._fleetCallback is not None:
5963 self._fleetCallback(fleet)
5964 self._fleetCallback = None
5965
5966 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
5967 """Update the given plane's gate information."""
5968 self.gui.updatePlane(tailNumber, status, gateNumber = gateNumber,
5969 callback = callback)
5970
5971 def updateRTO(self):
5972 """Update the RTO state.
5973
5974 The RTO checkbox will be enabled if the flight has an RTO state and the
5975 comments field contains some text."""
5976 flight = self.gui.flight
5977 rtoEnabled = flight is not None and flight.hasRTO and \
5978 self.gui.hasComments
5979 self._takeoffPage.setRTOEnabled(rtoEnabled)
5980
5981 def commentsChanged(self):
5982 """Called when the comments have changed."""
5983 self.updateRTO()
5984 self._finishPage.updateButtons()
5985
5986 def delayCodesChanged(self):
5987 """Called when the delay codes have changed."""
5988 self._finishPage.updateButtons()
5989
5990 def faultExplanationsChanged(self):
5991 """Called when the faults and their explanations have changed."""
5992 self._finishPage.updateButtons()
5993
5994 def rtoToggled(self, indicated):
5995 """Called when the RTO indication has changed."""
5996 self.gui.rtoToggled(indicated)
5997
5998 def _connectSimulator(self, simulatorType):
5999 """Connect to the simulator."""
6000 self.gui.connectSimulator(self._bookedFlight, simulatorType)
6001
6002 def _arrivalMETARCallback(self, returned, result):
6003 """Called when the METAR of the arrival airport is retrieved."""
6004 gobject.idle_add(self._handleArrivalMETAR, returned, result)
6005
6006 def _handleArrivalMETAR(self, returned, result):
6007 """Called when the METAR of the arrival airport is retrieved."""
6008 icao = self._bookedFlight.arrivalICAO
6009 if returned and icao in result.metars:
6010 metar = result.metars[icao]
6011 if metar!="":
6012 self._arrivalBriefingPage.setMETAR(metar)
6013
6014 def _getIndexOf(self, pageID):
6015 """Get the index for the given page ID.
6016
6017 It is an assertion failure if the ID is not found."""
6018 for index in range(0, len(self._pages)):
6019 page = self._pages[index]
6020 if page.id==pageID:
6021 return index
6022 assert False
6023
6024#-----------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.