source: src/mlx/gui/flight.py@ 967:843cc4f0f5d5

python3
Last change on this file since 967:843cc4f0f5d5 was 954:ad190b3a88c7, checked in by István Váradi <ivaradi@…>, 6 years ago

Removed text2unicode (re #347).

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