source: src/mlx/gui/flight.py@ 1008:5a311694b5cb

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

If the number of fuel tanks is less than 5, the old-style, side-by-side display is used (re #347).

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