source: src/mlx/gui/flight.py@ 1002:5e741be6b47c

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

The printing of a briefing works with Gtk 3 (re #347)

File size: 222.6 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) # cr.create_layout()
718 layout.set_text("Malév VA official briefing")
719 font = Pango.FontDescription("sans")
720 font.set_size(int(32 * scale * 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 * scale * 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 * scale * 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 self._tankFigure.connect("scroll-event", self._scrolled)
3260
3261 alignment = Gtk.Alignment(xalign = 0.5, yalign = 0.5,
3262 xscale = 0.0, yscale = 1.0)
3263 alignment.add(self._tankFigure)
3264
3265 self.pack_start(alignment, True, True, 4)
3266
3267 self._expectedButton = Gtk.SpinButton()
3268 self._expectedButton.set_numeric(True)
3269 self._expectedButton.set_range(0, self.capacity)
3270 self._expectedButton.set_increments(10, 100)
3271 self._expectedButton.set_value(currentWeight)
3272 self._expectedButton.set_alignment(1.0)
3273 self._expectedButton.set_width_chars(5)
3274 self._expectedButton.connect("value-changed", self._expectedChanged)
3275
3276 label.set_mnemonic_widget(self._expectedButton)
3277
3278 @property
3279 def label(self):
3280 """Get the label with the caption."""
3281 return self._label
3282
3283 @property
3284 def expectedButton(self):
3285 """Get the button containing the expected value."""
3286 return self._expectedButton
3287
3288 def setCurrent(self, currentWeight):
3289 """Set the current weight."""
3290 self.currentWeight = currentWeight
3291 self._redraw()
3292
3293 def isCorrect(self):
3294 """Determine if the contents of the fuel tank are as expected"""
3295 return abs(self.expectedWeight - self.currentWeight)<=1
3296
3297 def disable(self):
3298 """Disable the fuel tank."""
3299 self._expectedButton.set_sensitive(False)
3300 self._enabled = False
3301
3302 def _redraw(self):
3303 """Redraw the tank figure."""
3304 self._tankFigure.queue_draw()
3305
3306 def _drawTankFigure(self, tankFigure, eventOrContext):
3307 """Draw the tank figure."""
3308 triangleSize = 5
3309
3310 context = eventOrContext
3311 (xOffset, yOffset) = (0, 0)
3312
3313 width = tankFigure.get_allocated_width()
3314 height = tankFigure.get_allocated_height()
3315
3316 rectangleX0 = triangleSize
3317 rectangleY0 = triangleSize
3318 rectangleX1 = width - 1 - triangleSize
3319 rectangleY1 = height - 1 - triangleSize
3320 rectangleLineWidth = 2.0
3321
3322 context.set_source_rgb(0.0, 0.0, 0.0)
3323 context.set_line_width(rectangleLineWidth)
3324 context.rectangle(xOffset + rectangleX0 + rectangleLineWidth/2,
3325 yOffset + rectangleY0 + rectangleLineWidth/2,
3326 rectangleX1 - rectangleX0 - rectangleLineWidth,
3327 rectangleY1 - rectangleY0 - rectangleLineWidth)
3328 context.stroke()
3329
3330 rectangleInnerLeft = rectangleX0 + rectangleLineWidth
3331 rectangleInnerRight = rectangleX1 - rectangleLineWidth
3332 self._rectangleInnerTop = rectangleInnerTop = rectangleY0 + rectangleLineWidth
3333 self._rectangleInnerBottom = rectangleInnerBottom = rectangleY1 - rectangleLineWidth
3334
3335 rectangleInnerWidth = rectangleInnerRight - rectangleInnerLeft
3336 rectangleInnerHeight = rectangleInnerBottom - rectangleInnerTop
3337
3338 context.set_source_rgb(1.0, 0.9, 0.6)
3339 currentHeight = self.currentWeight * rectangleInnerHeight / self.capacity
3340 currentX = rectangleInnerTop + rectangleInnerHeight - currentHeight
3341 context.rectangle(xOffset + rectangleInnerLeft,
3342 yOffset + rectangleInnerTop +
3343 rectangleInnerHeight - currentHeight,
3344 rectangleInnerWidth, currentHeight)
3345 context.fill()
3346
3347 expectedHeight = self.expectedWeight * rectangleInnerHeight / self.capacity
3348 expectedY = rectangleInnerTop + rectangleInnerHeight - expectedHeight
3349
3350 context.set_line_width(1.5)
3351 context.set_source_rgb(0.0, 0.85, 0.85)
3352 context.move_to(xOffset + rectangleX0, yOffset + expectedY)
3353 context.line_to(xOffset + rectangleX1, yOffset + expectedY)
3354 context.stroke()
3355
3356 context.set_line_width(0.0)
3357 context.move_to(xOffset + 0, yOffset + expectedY - triangleSize)
3358 context.line_to(xOffset + 0, yOffset + expectedY + triangleSize)
3359 context.line_to(xOffset + rectangleX0 + 1, yOffset + expectedY)
3360 context.line_to(xOffset + 0, yOffset + expectedY - triangleSize)
3361 context.fill()
3362
3363 context.set_line_width(0.0)
3364 context.move_to(xOffset + width, yOffset + expectedY - triangleSize)
3365 context.line_to(xOffset + width, yOffset + expectedY + triangleSize)
3366 context.line_to(xOffset + rectangleX1 - 1, yOffset + expectedY)
3367 context.line_to(xOffset + width, yOffset + expectedY - triangleSize)
3368 context.fill()
3369
3370 return True
3371
3372 def _setExpectedFromY(self, y):
3373 """Set the expected weight from the given Y-coordinate."""
3374 level = (self._rectangleInnerBottom - y) / \
3375 (self._rectangleInnerBottom - self._rectangleInnerTop)
3376 level = min(1.0, max(0.0, level))
3377 self._expectedButton.set_value(level * self.capacity)
3378
3379 def _buttonPressed(self, tankFigure, event):
3380 """Called when a button is pressed in the figure.
3381
3382 The expected level will be set there."""
3383 if self._enabled and event.button==1:
3384 self._setExpectedFromY(event.y)
3385
3386 def _motionNotify(self, tankFigure, event):
3387 """Called when the mouse pointer moves within the area of a tank figure."""
3388 if self._enabled and event.state==Gdk.ModifierType.BUTTON1_MASK:
3389 self._setExpectedFromY(event.y)
3390
3391 def _scrolled(self, tankFigure, event):
3392 """Called when a scroll event is received."""
3393 if self._enabled:
3394 increment = 1 if event.state==Gdk.ModifierType.CONTROL_MASK \
3395 else 100 if event.state==Gdk.ModifierType.SHIFT_MASK \
3396 else 10 if event.state==0 else 0
3397 if increment!=0:
3398 if event.direction==Gdk.ScrollDirection.DOWN:
3399 increment *= -1
3400 self._expectedButton.spin(Gtk.SpinType.USER_DEFINED, increment)
3401
3402 def _expectedChanged(self, spinButton):
3403 """Called when the expected value has changed."""
3404 self.expectedWeight = spinButton.get_value_as_int()
3405 self._redraw()
3406
3407#-----------------------------------------------------------------------------
3408
3409class FuelPage(Page):
3410 """The page containing the fuel tank filling."""
3411 _pumpStep = 0.02
3412
3413 def __init__(self, wizard):
3414 """Construct the page."""
3415 super(FuelPage, self).__init__(wizard, "fuel",
3416 xstr("fuel_title"),
3417 xstr("fuel_help_pre") +
3418 xstr("fuel_help_post"),
3419 completedHelp = xstr("fuel_chelp"))
3420
3421 self._fuelTanks = []
3422 self._fuelTable = None
3423 self._fuelAlignment = Gtk.Alignment(xalign = 0.5, yalign = 0.5,
3424 xscale = 0.0, yscale = 1.0)
3425 self.setMainWidget(self._fuelAlignment)
3426
3427 tankData = [(tank, 2500, 3900) for tank in acft.mostFuelTanks]
3428 self._setupTanks(tankData)
3429
3430 self.addCancelFlightButton()
3431
3432 self._backButton = self.addPreviousButton(clicked = self._backClicked)
3433 self._button = self.addNextButton(clicked = self._forwardClicked)
3434
3435 self._pumpIndex = 0
3436
3437 def activate(self):
3438 """Activate the page."""
3439 self._setupTanks(self._wizard._fuelData)
3440
3441 aircraft = self._wizard.gui.flight.aircraft
3442 minLandingFuel = aircraft.minLandingFuel
3443 recommendedLandingFuel = aircraft.recommendedLandingFuel
3444
3445 middleHelp = "" if minLandingFuel is None else \
3446 (xstr("fuel_help_min") % (minLandingFuel,)) \
3447 if recommendedLandingFuel is None else \
3448 (xstr("fuel_help_min_rec") % (minLandingFuel,
3449 recommendedLandingFuel))
3450 self.setHelp(xstr("fuel_help_pre") + middleHelp + xstr("fuel_help_post"))
3451
3452 def finalize(self):
3453 """Finalize the page."""
3454 for fuelTank in self._fuelTanks:
3455 fuelTank.disable()
3456
3457 def _backClicked(self, button):
3458 """Called when the Back button is pressed."""
3459 self.goBack()
3460
3461 def _forwardClicked(self, button):
3462 """Called when the forward button is clicked."""
3463 if not self._completed:
3464 self._pumpIndex = 0
3465 self._wizard.gui.beginBusy(xstr("fuel_pump_busy"))
3466 self._pump()
3467 elif self._wizard.usingSimBrief:
3468 self._wizard.jumpPage("takeoff")
3469 else:
3470 self._wizard.jumpPage("briefing1")
3471
3472 def _setupTanks(self, tankData):
3473 """Setup the tanks for the given data."""
3474 numTanks = len(tankData)
3475 if self._fuelTable is not None:
3476 self._fuelAlignment.remove(self._fuelTable)
3477
3478 self._fuelTanks = []
3479 self._fuelTable = Gtk.Grid()
3480 self._fuelTable.set_column_homogeneous(True)
3481 self._fuelTable.set_row_spacing(4)
3482 index = 0
3483 for (tank, current, capacity) in tankData:
3484 fuelTank = FuelTank(tank,
3485 xstr("fuel_tank_" +
3486 const.fuelTank2string(tank)),
3487 capacity, current)
3488 self._fuelTanks.append(fuelTank)
3489
3490 alignment = Gtk.Alignment(xalign = 0.5, yalign = 1.0,
3491 xscale = 1.0, yscale = 0.0)
3492 alignment.add(fuelTank.label)
3493 self._fuelTable.attach(alignment, index*2, 0, 3, 1)
3494
3495 alignment = Gtk.Alignment(xalign = 0.5, yalign = 0.5,
3496 xscale = 0.0, yscale = 1.0)
3497 alignment.add(fuelTank)
3498 self._fuelTable.attach(alignment, index*2+1, 1, 1, 1)
3499
3500
3501 alignment = Gtk.Alignment(xalign = 0.5, yalign = 0.5,
3502 xscale = 1.0, yscale = 0.0)
3503 alignment.add(fuelTank.expectedButton)
3504
3505 self._fuelTable.attach(alignment, index*2,
3506 2 if (index%2)==0 or numTanks==2 else 3,
3507 3, 1)
3508
3509 index += 1
3510
3511 self._fuelAlignment.add(self._fuelTable)
3512 self.show_all()
3513
3514 def _pump(self):
3515 """Perform one step of pumping.
3516
3517 It is checked, if the current tank's contents are of the right
3518 quantity. If not, it is filled one step further to the desired
3519 contents. Otherwise the next tank is started. If all tanks are are
3520 filled, the next page is selected."""
3521 numTanks = len(self._fuelTanks)
3522
3523 fuelTank = None
3524 while self._pumpIndex < numTanks:
3525 fuelTank = self._fuelTanks[self._pumpIndex]
3526 if fuelTank.isCorrect():
3527 self._pumpIndex += 1
3528 fuelTank = None
3529 else:
3530 break
3531
3532 if fuelTank is None:
3533 self._wizard.gui.endBusy()
3534 if self._wizard.usingSimBrief:
3535 self._wizard.gui.startMonitoring()
3536 self._wizard.jumpPage("takeoff")
3537 else:
3538 bookedFlight = self._wizard._bookedFlight
3539 self._wizard.gui.beginBusy(xstr("route_down_notams"))
3540 self._wizard.gui.webHandler.getNOTAMs(self._notamsCallback,
3541 bookedFlight.departureICAO,
3542 bookedFlight.arrivalICAO)
3543 startSound(const.SOUND_NOTAM)
3544 else:
3545 currentLevel = fuelTank.currentWeight / fuelTank.capacity
3546 expectedLevel = fuelTank.expectedWeight / fuelTank.capacity
3547 if currentLevel<expectedLevel:
3548 currentLevel += FuelPage._pumpStep
3549 if currentLevel>expectedLevel: currentLevel = expectedLevel
3550 else:
3551 currentLevel -= FuelPage._pumpStep
3552 if currentLevel<expectedLevel: currentLevel = expectedLevel
3553 fuelTank.setCurrent(currentLevel * fuelTank.capacity)
3554 self._wizard.gui.simulator.setFuelLevel([(fuelTank.fuelTank,
3555 currentLevel)])
3556 GObject.timeout_add(50, self._pump)
3557
3558 def _notamsCallback(self, returned, result):
3559 """Callback for the NOTAMs."""
3560 GObject.idle_add(self._handleNOTAMs, returned, result)
3561
3562 def _handleNOTAMs(self, returned, result):
3563 """Handle the NOTAMs."""
3564 if returned:
3565 self._wizard._departureNOTAMs = result.departureNOTAMs
3566 self._wizard._arrivalNOTAMs = result.arrivalNOTAMs
3567 else:
3568 self._wizard._departureNOTAMs = None
3569 self._wizard._arrivalNOTAMs = None
3570
3571 bookedFlight = self._wizard._bookedFlight
3572 self._wizard.gui.beginBusy(xstr("route_down_metars"))
3573 self._wizard.gui.webHandler.getMETARs(self._metarsCallback,
3574 [bookedFlight.departureICAO,
3575 bookedFlight.arrivalICAO])
3576
3577 def _metarsCallback(self, returned, result):
3578 """Callback for the METARs."""
3579 GObject.idle_add(self._handleMETARs, returned, result)
3580
3581 def _handleMETARs(self, returned, result):
3582 """Handle the METARs."""
3583 self._wizard._departureMETAR = None
3584 self._wizard._arrivalMETAR = None
3585 bookedFlight = self._wizard._bookedFlight
3586 if returned:
3587 if bookedFlight.departureICAO in result.metars:
3588 self._wizard._departureMETAR = result.metars[bookedFlight.departureICAO]
3589 if bookedFlight.arrivalICAO in result.metars:
3590 self._wizard._arrivalMETAR = result.metars[bookedFlight.arrivalICAO]
3591
3592 self._wizard.gui.endBusy()
3593 self._backButton.set_sensitive(True)
3594 self._button.set_sensitive(True)
3595 self._wizard.nextPage()
3596
3597#-----------------------------------------------------------------------------
3598
3599class BriefingPage(Page):
3600 """Page for the briefing."""
3601 def __init__(self, wizard, departure):
3602 """Construct the briefing page."""
3603 self._departure = departure
3604
3605 number = 1 if departure else 2
3606
3607 title = xstr("briefing_title") % (number,
3608 xstr("briefing_departure")
3609 if departure
3610 else xstr("briefing_arrival"))
3611 super(BriefingPage, self).__init__(wizard,
3612 "briefing%d" % (number,),
3613 title, xstr("briefing_help"),
3614 completedHelp = xstr("briefing_chelp"))
3615
3616 alignment = Gtk.Alignment(xalign = 0.5, yalign = 0.5,
3617 xscale = 1.0, yscale = 1.0)
3618
3619 mainBox = Gtk.VBox()
3620 alignment.add(mainBox)
3621 self.setMainWidget(alignment)
3622
3623 self._notamsFrame = Gtk.Frame()
3624 self._notamsFrame.set_label(xstr("briefing_notams_init"))
3625 scrolledWindow = Gtk.ScrolledWindow()
3626 scrolledWindow.set_size_request(-1, 128)
3627 # FIXME: these constants should be in common
3628 scrolledWindow.set_policy(Gtk.PolicyType.AUTOMATIC,
3629 Gtk.PolicyType.AUTOMATIC)
3630 self._notams = Gtk.TextView()
3631 self._notams.set_editable(False)
3632 self._notams.set_accepts_tab(False)
3633 self._notams.set_wrap_mode(Gtk.WrapMode.WORD)
3634 scrolledWindow.add(self._notams)
3635 alignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
3636 xscale = 1.0, yscale = 1.0)
3637 alignment.set_padding(padding_top = 4, padding_bottom = 0,
3638 padding_left = 0, padding_right = 0)
3639 alignment.add(scrolledWindow)
3640 self._notamsFrame.add(alignment)
3641 mainBox.pack_start(self._notamsFrame, True, True, 4)
3642
3643 self._metarFrame = Gtk.Frame()
3644 self._metarFrame.set_label(xstr("briefing_metar_init"))
3645 scrolledWindow = Gtk.ScrolledWindow()
3646 scrolledWindow.set_size_request(-1, 32)
3647 scrolledWindow.set_policy(Gtk.PolicyType.AUTOMATIC,
3648 Gtk.PolicyType.AUTOMATIC)
3649
3650 self._updatingMETAR = False
3651
3652 self._metar = Gtk.TextView()
3653 self._metar.set_accepts_tab(False)
3654 self._metar.set_wrap_mode(Gtk.WrapMode.WORD)
3655 self._metar.get_buffer().connect("changed", self._metarChanged)
3656 self._metar.get_buffer().connect_after("insert-text", self._metarInserted)
3657 scrolledWindow.add(self._metar)
3658 alignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
3659 xscale = 1.0, yscale = 1.0)
3660 alignment.set_padding(padding_top = 4, padding_bottom = 0,
3661 padding_left = 0, padding_right = 0)
3662 alignment.add(scrolledWindow)
3663 self._metarFrame.add(alignment)
3664 mainBox.pack_start(self._metarFrame, True, True, 4)
3665 self.metarEdited = False
3666
3667 self.addCancelFlightButton()
3668
3669 self.addPreviousButton(clicked = self._backClicked)
3670 self._button = self.addNextButton(clicked = self._forwardClicked)
3671
3672 @property
3673 def metar(self):
3674 """Get the METAR on the page."""
3675 buffer = self._metar.get_buffer()
3676 return buffer.get_text(buffer.get_start_iter(),
3677 buffer.get_end_iter(), True)
3678
3679 def setMETAR(self, metar):
3680 """Set the METAR."""
3681 self._metar.get_buffer().set_text(metar)
3682 self.metarEdited = False
3683
3684 def changeMETAR(self, metar):
3685 """Change the METAR as a result of an edit on one of the other
3686 pages."""
3687 self._updatingMETAR = True
3688 self._metar.get_buffer().set_text(metar)
3689 self._updatingMETAR = False
3690
3691 self._updateButton()
3692 self.metarEdited = True
3693
3694 def activate(self):
3695 """Activate the page."""
3696 if not self._departure:
3697 self._button.set_label(xstr("briefing_button"))
3698 self._button.set_has_tooltip(False)
3699 self._button.set_use_stock(False)
3700
3701 bookedFlight = self._wizard._bookedFlight
3702
3703 icao = bookedFlight.departureICAO if self._departure \
3704 else bookedFlight.arrivalICAO
3705 notams = self._wizard._departureNOTAMs if self._departure \
3706 else self._wizard._arrivalNOTAMs
3707 metar = self._wizard._departureMETAR if self._departure \
3708 else self._wizard._arrivalMETAR
3709
3710 self._notamsFrame.set_label(xstr("briefing_notams_template") % (icao,))
3711 buffer = self._notams.get_buffer()
3712 if notams is None:
3713 buffer.set_text(xstr("briefing_notams_failed"))
3714 elif not notams:
3715 buffer.set_text(xstr("briefing_notams_missing"))
3716 else:
3717 s = ""
3718 for notam in notams:
3719 s += str(notam)
3720 s += "-------------------- * --------------------\n"
3721 buffer.set_text(s)
3722
3723 self._metarFrame.set_label(xstr("briefing_metar_template") % (icao,))
3724 buffer = self._metar.get_buffer()
3725 self._updatingMETAR = True
3726 if metar is None:
3727 buffer.set_text("")
3728 self.setHelp(xstr("briefing_help_nometar"))
3729 else:
3730 buffer.set_text(metar)
3731 self._updatingMETAR = False
3732 self._updateButton()
3733
3734 label = self._metarFrame.get_label_widget()
3735 label.set_use_underline(True)
3736 label.set_mnemonic_widget(self._metar)
3737
3738 self.metarEdited = False
3739
3740 def _backClicked(self, button):
3741 """Called when the Back button is pressed."""
3742 self.goBack()
3743
3744 def _forwardClicked(self, button):
3745 """Called when the forward button is clicked."""
3746 if not self._departure:
3747 if not self._completed:
3748 self._wizard.gui.startMonitoring()
3749 self._button.set_label(xstr("button_next"))
3750 self._button.set_tooltip_text(xstr("button_next_tooltip"))
3751 self.complete()
3752
3753 self._wizard.nextPage()
3754
3755 def _metarChanged(self, buffer):
3756 """Called when the METAR has changed."""
3757 print("BriefingPage.metarChanged", self._updatingMETAR)
3758 if not self._updatingMETAR:
3759 self.metarEdited = True
3760 self._updateButton()
3761 metar = buffer.get_text(buffer.get_start_iter(),
3762 buffer.get_end_iter(), True)
3763 self._wizard.metarChanged(metar, self)
3764
3765 def _metarInserted(self, textBuffer, iter, text, length):
3766 """Called when new characters are inserted into the METAR.
3767
3768 It uppercases all characters."""
3769 print("BriefingPage.metarInserted", self._updatingMETAR)
3770 if not self._updatingMETAR:
3771 self._updatingMETAR = True
3772
3773 iter1 = iter.copy()
3774 iter1.backward_chars(length)
3775 textBuffer.delete(iter, iter1)
3776
3777 textBuffer.insert(iter, text.upper())
3778
3779 self._updatingMETAR = False
3780
3781 def _updateButton(self):
3782 """Update the sensitivity of the Next button based on the contents of
3783 the METAR field."""
3784 buffer = self._metar.get_buffer()
3785 self._button.set_sensitive(buffer.get_text(buffer.get_start_iter(),
3786 buffer.get_end_iter(),
3787 True)!="")
3788
3789
3790#-----------------------------------------------------------------------------
3791
3792class TakeoffPage(Page):
3793 """Page for entering the takeoff data."""
3794 def __init__(self, wizard):
3795 """Construct the takeoff page."""
3796 super(TakeoffPage, self).__init__(wizard, "takeoff",
3797 xstr("takeoff_title"),
3798 xstr("takeoff_help"),
3799 completedHelp = xstr("takeoff_chelp"))
3800
3801 self._forwardAllowed = False
3802
3803 alignment = Gtk.Alignment(xalign = 0.5, yalign = 0.5,
3804 xscale = 0.0, yscale = 0.0)
3805
3806 table = Gtk.Table(9, 24)
3807 table.set_row_spacings(4)
3808 table.set_col_spacings(16)
3809 table.set_homogeneous(False)
3810 alignment.add(table)
3811 self.setMainWidget(alignment)
3812
3813 row = 0
3814
3815 label = Gtk.Label(xstr("takeoff_metar"))
3816 label.set_use_underline(True)
3817 label.set_alignment(0.0, 0.5)
3818 table.attach(label, 0, 1, row, row+1)
3819
3820 self._metar = Gtk.Entry()
3821 self._metar.set_width_chars(40)
3822 self._metar.set_tooltip_text(xstr("takeoff_metar_tooltip"))
3823 self._metar.connect("changed", self._metarChanged)
3824 self._metar.get_buffer().connect_after("inserted-text", self._metarInserted)
3825 table.attach(self._metar, 1, 24, row, row+1)
3826 label.set_mnemonic_widget(self._metar)
3827
3828 self._updatingMETAR = False
3829
3830 row += 1
3831
3832 label = Gtk.Label(xstr("takeoff_runway"))
3833 label.set_use_underline(True)
3834 label.set_alignment(0.0, 0.5)
3835 table.attach(label, 0, 1, row, row+1)
3836
3837 self._runway = Gtk.Entry()
3838 self._runway.set_width_chars(10)
3839 self._runway.set_tooltip_text(xstr("takeoff_runway_tooltip"))
3840 self._runway.connect("changed", self._upperChanged)
3841 table.attach(self._runway, 1, 3, row, row+1)
3842 label.set_mnemonic_widget(self._runway)
3843
3844 row += 1
3845
3846 label = Gtk.Label(xstr("takeoff_sid"))
3847 label.set_use_underline(True)
3848 label.set_alignment(0.0, 0.5)
3849 table.attach(label, 0, 1, row, row+1)
3850
3851 self._sid = Gtk.ComboBox.new_with_model_and_entry(comboModel)
3852
3853 self._sid.set_entry_text_column(0)
3854 self._sid.get_child().set_width_chars(10)
3855 self._sid.set_tooltip_text(xstr("takeoff_sid_tooltip"))
3856 self._sid.connect("changed", self._upperChangedComboBox)
3857 table.attach(self._sid, 1, 3, row, row+1)
3858 label.set_mnemonic_widget(self._sid)
3859
3860 row += 1
3861
3862 label = Gtk.Label(xstr("takeoff_v1"))
3863 label.set_use_markup(True)
3864 label.set_use_underline(True)
3865 label.set_alignment(0.0, 0.5)
3866 table.attach(label, 0, 1, row, row+1)
3867
3868 self._v1 = IntegerEntry()
3869 self._v1.set_width_chars(4)
3870 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip_knots"))
3871 self._v1.connect("integer-changed", self._valueChanged)
3872 table.attach(self._v1, 2, 3, row, row+1)
3873 label.set_mnemonic_widget(self._v1)
3874
3875 self._v1Unit = Gtk.Label(xstr("label_knots"))
3876 self._v1Unit.set_alignment(0.0, 0.5)
3877 table.attach(self._v1Unit, 3, 4, row, row+1)
3878
3879 row += 1
3880
3881 label = Gtk.Label(xstr("takeoff_vr"))
3882 label.set_use_markup(True)
3883 label.set_use_underline(True)
3884 label.set_alignment(0.0, 0.5)
3885 table.attach(label, 0, 1, row, row+1)
3886
3887 self._vr = IntegerEntry()
3888 self._vr.set_width_chars(4)
3889 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip_knots"))
3890 self._vr.connect("integer-changed", self._valueChanged)
3891 table.attach(self._vr, 2, 3, row, row+1)
3892 label.set_mnemonic_widget(self._vr)
3893
3894 self._vrUnit = Gtk.Label(xstr("label_knots"))
3895 self._vrUnit.set_alignment(0.0, 0.5)
3896 table.attach(self._vrUnit, 3, 4, row, row+1)
3897
3898 row += 1
3899
3900 label = Gtk.Label(xstr("takeoff_v2"))
3901 label.set_use_markup(True)
3902 label.set_use_underline(True)
3903 label.set_alignment(0.0, 0.5)
3904 table.attach(label, 0, 1, row, row+1)
3905
3906 self._v2 = IntegerEntry()
3907 self._v2.set_width_chars(4)
3908 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip_knots"))
3909 self._v2.connect("integer-changed", self._valueChanged)
3910 table.attach(self._v2, 2, 3, row, row+1)
3911 label.set_mnemonic_widget(self._v2)
3912
3913 self._v2Unit = Gtk.Label(xstr("label_knots"))
3914 self._v2Unit.set_alignment(0.0, 0.5)
3915 table.attach(self._v2Unit, 3, 4, row, row+1)
3916
3917 row += 1
3918
3919 self._derateType = acft.DERATE_NONE
3920
3921 self._derateLabel = Gtk.Label()
3922 self._derateLabel.set_use_underline(True)
3923 self._derateLabel.set_markup(xstr("takeoff_derate_tupolev"))
3924 self._derateLabel.set_alignment(0.0, 0.5)
3925 table.attach(self._derateLabel, 0, 1, row, row+1)
3926
3927 self._derate = Gtk.Alignment()
3928 table.attach(self._derate, 2, 4, row, row+1)
3929 self._derateWidget = None
3930 self._derateEntry = None
3931 self._derateUnit = None
3932 self._derateButtons = None
3933
3934 row += 1
3935
3936 self._antiIceOn = Gtk.CheckButton(xstr("takeoff_antiice"))
3937 self._antiIceOn.set_use_underline(True)
3938 self._antiIceOn.set_tooltip_text(xstr("takeoff_antiice_tooltip"))
3939 table.attach(self._antiIceOn, 2, 4, row, row+1)
3940
3941 row += 1
3942
3943 self._rto = Gtk.CheckButton(xstr("takeoff_rto"))
3944 self._rto.set_use_underline(True)
3945 self._rto.set_tooltip_text(xstr("takeoff_rto_tooltip"))
3946 self._rto.connect("toggled", self._rtoToggled)
3947 table.attach(self._rto, 2, 4, row, row+1, ypadding = 8)
3948
3949 self.addCancelFlightButton()
3950
3951 self.addPreviousButton(clicked = self._backClicked)
3952
3953 self._button = self.addNextButton(clicked = self._forwardClicked)
3954
3955 self._active = False
3956
3957 @property
3958 def runway(self):
3959 """Get the runway."""
3960 return self._runway.get_text()
3961
3962 @property
3963 def sid(self):
3964 """Get the SID."""
3965 text = self._sid.get_child().get_text()
3966 return text if self._sid.get_active()!=0 and text and text!="N/A" \
3967 else None
3968
3969 @property
3970 def v1(self):
3971 """Get the v1 speed."""
3972 return self._v1.get_int()
3973
3974 @property
3975 def vr(self):
3976 """Get the vr speed."""
3977 return self._vr.get_int()
3978
3979 @property
3980 def v2(self):
3981 """Get the v2 speed."""
3982 return self._v2.get_int()
3983
3984 @property
3985 def derate(self):
3986 """Get the derate value, if any."""
3987 if self._derateWidget is None:
3988 return None
3989 if self._derateType==acft.DERATE_BOEING:
3990 derate = self._derateEntry.get_text()
3991 return derate if derate else None
3992 elif self._derateType==acft.DERATE_EPR:
3993 derate = self._derateWidget.get_text()
3994 return derate if derate else None
3995 elif self._derateType==acft.DERATE_TUPOLEV:
3996 return acft.DERATE_TUPOLEV_NOMINAL \
3997 if self._derateButtons[0].get_active() \
3998 else acft.DERATE_TUPOLEV_TAKEOFF
3999 elif self._derateType==acft.DERATE_B462:
4000 return self._derateWidget.get_active()
4001 else:
4002 return None
4003
4004 @property
4005 def antiIceOn(self):
4006 """Get whether the anti-ice system has been turned on."""
4007 return self._antiIceOn.get_active()
4008
4009 @antiIceOn.setter
4010 def antiIceOn(self, value):
4011 """Set the anti-ice indicator."""
4012 self._antiIceOn.set_active(value)
4013
4014 @property
4015 def rtoIndicated(self):
4016 """Get whether the pilot has indicated if there was an RTO."""
4017 return self._rto.get_active()
4018
4019 def activate(self):
4020 """Activate the page."""
4021 print("TakeoffPage.activate")
4022
4023 self._updatingMETAR = True
4024 self._metar.get_buffer().set_text(self._wizard.departureMETAR, -1)
4025 self._updatingMETAR = False
4026
4027 if self._wizard.takeoffRunway is None:
4028 self._runway.set_text("")
4029 else:
4030 self._runway.set_text(self._wizard.takeoffRunway)
4031 self._runway.set_sensitive(True)
4032 self._sid.set_active(0)
4033 self._sid.set_sensitive(True)
4034 self._v1.set_int(None)
4035 self._v1.set_sensitive(True)
4036 self._vr.set_int(None)
4037 self._vr.set_sensitive(True)
4038 self._v2.set_int(None)
4039 self._v2.set_sensitive(True)
4040
4041 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
4042 speedUnit = xstr("label" + i18nSpeedUnit)
4043 self._v1Unit.set_text(speedUnit)
4044 self._vrUnit.set_text(speedUnit)
4045 self._v2Unit.set_text(speedUnit)
4046
4047 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip" + i18nSpeedUnit))
4048 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip" + i18nSpeedUnit))
4049 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip" + i18nSpeedUnit))
4050
4051 self._derateType = self._wizard.gui.flight.aircraft.derateType
4052
4053 self._setupDerateWidget()
4054
4055 self._rto.set_active(False)
4056 self._rto.set_sensitive(False)
4057
4058 self._button.set_sensitive(False)
4059 self._forwardAllowed = False
4060
4061 self._active = True
4062
4063 def allowForward(self):
4064 """Allow going to the next page."""
4065 print("TakeoffPage.allowForward")
4066 self._forwardAllowed = True
4067 self._updateForwardButton()
4068
4069 def reset(self):
4070 """Reset the page if the wizard is reset."""
4071 print("TakeoffPage.reset")
4072
4073 super(TakeoffPage, self).reset()
4074 self._v1.reset()
4075 self._vr.reset()
4076 self._v2.reset()
4077 self._hasDerate = False
4078 self._antiIceOn.set_active(False)
4079 self._active = False
4080
4081 def setRTOEnabled(self, enabled):
4082 """Set the RTO checkbox enabled or disabled."""
4083 if not enabled:
4084 self._rto.set_active(False)
4085 self._rto.set_sensitive(enabled)
4086
4087 def changeMETAR(self, metar):
4088 """Change the METAR as a result of an edit on one of the other
4089 pages."""
4090 if self._active:
4091 print("TakeoffPage.changeMETAR")
4092 self._updatingMETAR = True
4093 self._metar.get_buffer().set_text(metar, -1)
4094 self._updatingMETAR = False
4095
4096 self._updateForwardButton()
4097
4098 def _updateForwardButton(self):
4099 """Update the sensitivity of the forward button based on some conditions."""
4100 sensitive = self._forwardAllowed and \
4101 self._metar.get_text()!="" and \
4102 self._runway.get_text()!="" and \
4103 self.sid is not None and \
4104 self.v1 is not None and \
4105 self.vr is not None and \
4106 self.v2 is not None and \
4107 self.v1 <= self.vr and \
4108 self.vr <= self.v2 and \
4109 (self._derateType==acft.DERATE_NONE or
4110 self.derate is not None)
4111
4112 print("TakeoffPage._updateForwardButton: forwardAllowed:", self._forwardAllowed, ", sensitive:", sensitive)
4113 if self._forwardAllowed:
4114 print(" METAR: ", self._metar.get_text())
4115 print(" runway: ", self._runway.get_text())
4116 print(" SID:", self.sid)
4117 print(" V1:", self.v1)
4118 print(" VR:", self.vr)
4119 print(" V2:", self.v2)
4120 print(" derateType:", self._derateType)
4121 print(" derate:", self.derate)
4122
4123 self._button.set_sensitive(sensitive)
4124
4125 def _valueChanged(self, widget, arg = None):
4126 """Called when the value of some widget has changed."""
4127 print("TakeoffPage._valueChanged")
4128
4129 self._updateForwardButton()
4130
4131 def _upperChanged(self, entry, arg = None):
4132 """Called when the value of some entry widget has changed and the value
4133 should be converted to uppercase."""
4134 print("TakeoffPage._upperChanged")
4135 entry.set_text(entry.get_text().upper())
4136 self._valueChanged(entry, arg)
4137
4138 def _upperChangedComboBox(self, comboBox):
4139 """Called for combo box widgets that must be converted to uppercase."""
4140 entry = comboBox.get_child()
4141 if comboBox.get_active()==-1:
4142 entry.set_text(entry.get_text().upper())
4143 self._valueChanged(entry)
4144
4145 def _derateChanged(self, entry):
4146 """Called when the value of the derate is changed."""
4147 print("TakeoffPage._derateChanged")
4148 self._updateForwardButton()
4149
4150 def _rtoToggled(self, button):
4151 """Called when the RTO check button is toggled."""
4152 self._wizard.rtoToggled(button.get_active())
4153
4154 def _backClicked(self, button):
4155 """Called when the Back button is pressed."""
4156 self.goBack()
4157
4158 def _forwardClicked(self, button):
4159 """Called when the forward button is clicked."""
4160 aircraft = self._wizard.gui.flight.aircraft
4161 aircraft.updateV1R2()
4162 if self.derate is not None:
4163 aircraft.updateDerate()
4164 aircraft.updateTakeoffAntiIce()
4165 self._wizard.nextPage()
4166
4167 def _setupDerateWidget(self):
4168 """Setup the derate widget."""
4169 if self._derateWidget is not None:
4170 self._derate.remove(self._derateWidget)
4171
4172 if self._derateType==acft.DERATE_BOEING:
4173 self._derateLabel.set_text(xstr("takeoff_derate_boeing"))
4174 self._derateLabel.set_use_underline(True)
4175 self._derateLabel.set_sensitive(True)
4176
4177 self._derateEntry = Gtk.Entry()
4178 self._derateEntry.set_width_chars(7)
4179 self._derateEntry.set_tooltip_text(xstr("takeoff_derate_boeing_tooltip"))
4180 self._derateEntry.set_alignment(1.0)
4181 self._derateEntry.connect("changed", self._derateChanged)
4182 self._derateLabel.set_mnemonic_widget(self._derateEntry)
4183
4184 self._derateUnit = Gtk.Label("%")
4185 self._derateUnit.set_alignment(0.0, 0.5)
4186
4187 self._derateWidget = Gtk.Table(3, 1)
4188 self._derateWidget.set_row_spacings(4)
4189 self._derateWidget.set_col_spacings(16)
4190 self._derateWidget.set_homogeneous(False)
4191
4192 self._derateWidget.attach(self._derateEntry, 0, 2, 0, 1)
4193 self._derateWidget.attach(self._derateUnit, 2, 3, 0, 1)
4194
4195 self._derate.add(self._derateWidget)
4196 elif self._derateType==acft.DERATE_EPR:
4197 self._derateLabel.set_text("_EPR:")
4198 self._derateLabel.set_use_underline(True)
4199 self._derateLabel.set_sensitive(True)
4200
4201 self._derateWidget = Gtk.Entry()
4202 self._derateWidget.set_width_chars(7)
4203 self._derateWidget.set_tooltip_text(xstr("takeoff_derate_epr_tooltip"))
4204 self._derateWidget.set_alignment(1.0)
4205 self._derateWidget.connect("changed", self._derateChanged)
4206 self._derateLabel.set_mnemonic_widget(self._derateWidget)
4207
4208 self._derate.add(self._derateWidget)
4209 elif self._derateType==acft.DERATE_TUPOLEV:
4210 self._derateLabel.set_text(xstr("takeoff_derate_tupolev"))
4211 self._derateLabel.set_use_underline(True)
4212 self._derateLabel.set_sensitive(True)
4213
4214 nominal = Gtk.RadioButton.\
4215 new_with_label_from_widget(None,
4216 xstr("takeoff_derate_tupolev_nominal"))
4217 nominal.set_use_underline(True)
4218 nominal.set_tooltip_text(xstr("takeoff_derate_tupolev_nominal_tooltip"))
4219 nominal.connect("toggled", self._derateChanged)
4220
4221 takeoff = Gtk.RadioButton.\
4222 new_with_label_from_widget(nominal,
4223 xstr("takeoff_derate_tupolev_takeoff"))
4224
4225 takeoff.set_use_underline(True)
4226 takeoff.set_tooltip_text(xstr("takeoff_derate_tupolev_takeoff_tooltip"))
4227 takeoff.connect("toggled", self._derateChanged)
4228
4229 self._derateButtons = [nominal, takeoff]
4230
4231 self._derateWidget = Gtk.HBox()
4232 self._derateWidget.pack_start(nominal, False, False, 4)
4233 self._derateWidget.pack_start(takeoff, False, False, 4)
4234
4235 self._derate.add(self._derateWidget)
4236 elif self._derateType==acft.DERATE_B462:
4237 self._derateLabel.set_text("")
4238
4239 self._derateWidget = Gtk.CheckButton(xstr("takeoff_derate_b462"))
4240 self._derateWidget.set_tooltip_text(xstr("takeoff_derate_b462_tooltip"))
4241 self._derateWidget.set_use_underline(True)
4242 self._derate.add(self._derateWidget)
4243 else:
4244 self._derateWidget = None
4245 self._derateLabel.set_text("")
4246 self._derateLabel.set_sensitive(False)
4247
4248 def _metarChanged(self, entry):
4249 """Called when the METAR has changed."""
4250 print("TakeoffPage.metarChanged", self._updatingMETAR)
4251 if not self._updatingMETAR:
4252 self._updateForwardButton()
4253 self._wizard.metarChanged(entry.get_text(), self)
4254
4255 def _metarInserted(self, buffer, position, text, length):
4256 """Called when new characters are inserted into the METAR.
4257
4258 It uppercases all characters."""
4259 print("TakeoffPage.metarInserted", self._updatingMETAR)
4260 if not self._updatingMETAR:
4261 self._updatingMETAR = True
4262
4263 buffer.delete_text(position, length)
4264 buffer.insert_text(position, text.upper(), length)
4265
4266 self._updatingMETAR = False
4267
4268#-----------------------------------------------------------------------------
4269
4270class CruisePage(Page):
4271 """The page containing the flight level that might change during flight."""
4272 def __init__(self, wizard):
4273 """Construct the page."""
4274 super(CruisePage, self).__init__(wizard, "cruise",
4275 xstr("cruise_title"),
4276 xstr("cruise_help"))
4277
4278 self._loggable = False
4279 self._loggedCruiseLevel = 240
4280 self._activated = False
4281
4282 alignment = Gtk.Alignment(xalign = 0.5, yalign = 0.0,
4283 xscale = 0.0, yscale = 1.0)
4284
4285 mainBox = Gtk.VBox()
4286 alignment.add(mainBox)
4287 self.setMainWidget(alignment)
4288
4289 alignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
4290 xscale = 0.0, yscale = 0.0)
4291 mainBox.pack_start(alignment, False, False, 16)
4292
4293 levelBox = Gtk.HBox()
4294
4295 label = Gtk.Label(xstr("route_level"))
4296 label.set_use_underline(True)
4297 levelBox.pack_start(label, True, True, 0)
4298
4299 self._cruiseLevel = Gtk.SpinButton()
4300 self._cruiseLevel.set_increments(step = 10, page = 100)
4301 self._cruiseLevel.set_range(min = 50, max = 500)
4302 self._cruiseLevel.set_tooltip_text(xstr("cruise_route_level_tooltip"))
4303 self._cruiseLevel.set_numeric(True)
4304 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
4305 label.set_mnemonic_widget(self._cruiseLevel)
4306
4307 levelBox.pack_start(self._cruiseLevel, False, False, 8)
4308
4309 self._updateButton = Gtk.Button(xstr("cruise_route_level_update"));
4310 self._updateButton.set_use_underline(True)
4311 self._updateButton.set_tooltip_text(xstr("cruise_route_level_update_tooltip"))
4312 self._updateButton.connect("clicked", self._updateButtonClicked)
4313
4314 levelBox.pack_start(self._updateButton, False, False, 16)
4315
4316 mainBox.pack_start(levelBox, False, False, 0)
4317
4318 alignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
4319 xscale = 0.0, yscale = 1.0)
4320 mainBox.pack_start(alignment, True, True, 0)
4321
4322 self.addCancelFlightButton()
4323
4324 self._backButton = self.addPreviousButton(clicked = self._backClicked)
4325 self._button = self.addNextButton(clicked = self._forwardClicked)
4326
4327 @property
4328 def activated(self):
4329 """Determine if the page is already activated or not."""
4330 return self._activated
4331
4332 @property
4333 def cruiseLevel(self):
4334 """Get the cruise level."""
4335 return self._loggedCruiseLevel
4336
4337 @property
4338 def loggableCruiseLevel(self):
4339 """Get the cruise level which should be logged."""
4340 return self._cruiseLevel.get_value_as_int()
4341
4342 def setLoggable(self, loggable):
4343 """Set whether the cruise altitude can be logged."""
4344 self._loggable = loggable
4345 self._updateButtons()
4346
4347 def activate(self):
4348 """Setup the route from the booked flight."""
4349 self._loggedCruiseLevel = self._wizard.filedCruiseLevel
4350 self._cruiseLevel.set_value(self._loggedCruiseLevel)
4351 self._activated = True
4352
4353 def reset(self):
4354 """Reset the page."""
4355 self._loggable = False
4356 self._activated = False
4357 super(CruisePage, self).reset()
4358
4359 def _updateButtons(self):
4360 """Update the sensitivity of the buttons."""
4361 self._updateButton.set_sensitive(self._loggable and
4362 self.loggableCruiseLevel!=
4363 self._loggedCruiseLevel)
4364
4365 def _cruiseLevelChanged(self, spinButton):
4366 """Called when the cruise level has changed."""
4367 self._updateButtons()
4368
4369 def _updateButtonClicked(self, button):
4370 """Called when the update button is clicked."""
4371 if self._wizard.cruiseLevelChanged():
4372 self._loggedCruiseLevel = self.loggableCruiseLevel
4373 self._updateButtons()
4374
4375 def _backClicked(self, button):
4376 """Called when the Back button is pressed."""
4377 self.goBack()
4378
4379 def _forwardClicked(self, button):
4380 """Called when the Forward button is clicked."""
4381 self._wizard.nextPage()
4382
4383#-----------------------------------------------------------------------------
4384
4385class LandingPage(Page):
4386 """Page for entering landing data."""
4387 def __init__(self, wizard):
4388 """Construct the landing page."""
4389 super(LandingPage, self).__init__(wizard, "landing",
4390 xstr("landing_title"),
4391 xstr("landing_help"),
4392 completedHelp = xstr("landing_chelp"))
4393
4394 self._flightEnded = False
4395
4396 alignment = Gtk.Alignment(xalign = 0.5, yalign = 0.5,
4397 xscale = 0.0, yscale = 0.0)
4398
4399 table = Gtk.Table(7, 24)
4400 table.set_row_spacings(4)
4401 table.set_col_spacings(16)
4402 table.set_homogeneous(False)
4403 alignment.add(table)
4404 self.setMainWidget(alignment)
4405
4406 row = 0
4407
4408 label = Gtk.Label(xstr("landing_metar"))
4409 label.set_use_underline(True)
4410 label.set_alignment(0.0, 0.5)
4411 table.attach(label, 1, 2, row, row+1)
4412
4413 self._metar = Gtk.Entry()
4414 self._metar.set_width_chars(40)
4415 self._metar.set_tooltip_text(xstr("landing_metar_tooltip"))
4416 self._metar.connect("changed", self._metarChanged)
4417 self._metar.get_buffer().connect_after("inserted-text", self._metarInserted)
4418 table.attach(self._metar, 2, 24, row, row+1)
4419 label.set_mnemonic_widget(self._metar)
4420
4421 self._updatingMETAR = False
4422
4423 row += 1
4424
4425 label = Gtk.Label(xstr("landing_star"))
4426 label.set_use_underline(True)
4427 label.set_alignment(0.0, 0.5)
4428 table.attach(label, 1, 2, row, row + 1)
4429
4430 self._star = Gtk.ComboBox.new_with_model_and_entry(comboModel)
4431
4432 self._star.set_entry_text_column(0)
4433 self._star.get_child().set_width_chars(10)
4434 self._star.set_tooltip_text(xstr("landing_star_tooltip"))
4435 self._star.connect("changed", self._upperChangedComboBox)
4436 self._star.set_sensitive(False)
4437 table.attach(self._star, 2, 4, row, row + 1)
4438 label.set_mnemonic_widget(self._star)
4439
4440 row += 1
4441
4442 label = Gtk.Label(xstr("landing_transition"))
4443 label.set_use_underline(True)
4444 label.set_alignment(0.0, 0.5)
4445 table.attach(label, 1, 2, row, row + 1)
4446
4447 self._transition = Gtk.ComboBox.new_with_model_and_entry(comboModel)
4448
4449 self._transition.set_entry_text_column(0)
4450 self._transition.get_child().set_width_chars(10)
4451 self._transition.set_tooltip_text(xstr("landing_transition_tooltip"))
4452 self._transition.connect("changed", self._upperChangedComboBox)
4453 self._transition.set_sensitive(False)
4454 table.attach(self._transition, 2, 4, row, row + 1)
4455 label.set_mnemonic_widget(self._transition)
4456
4457 row += 1
4458
4459 label = Gtk.Label(xstr("landing_runway"))
4460 label.set_use_underline(True)
4461 label.set_alignment(0.0, 0.5)
4462 table.attach(label, 1, 2, row, row + 1)
4463
4464 self._runway = Gtk.Entry()
4465 self._runway.set_width_chars(10)
4466 self._runway.set_tooltip_text(xstr("landing_runway_tooltip"))
4467 self._runway.connect("changed", self._upperChanged)
4468 table.attach(self._runway, 2, 4, row, row + 1)
4469 label.set_mnemonic_widget(self._runway)
4470
4471 row += 1
4472
4473 label = Gtk.Label(xstr("landing_approach"))
4474 label.set_use_underline(True)
4475 label.set_alignment(0.0, 0.5)
4476 table.attach(label, 1, 2, row, row + 1)
4477
4478 self._approachType = Gtk.Entry()
4479 self._approachType.set_width_chars(10)
4480 self._approachType.set_tooltip_text(xstr("landing_approach_tooltip"))
4481 self._approachType.connect("changed", self._upperChanged)
4482 table.attach(self._approachType, 2, 4, row, row + 1)
4483 label.set_mnemonic_widget(self._approachType)
4484
4485 row += 1
4486
4487 label = Gtk.Label(xstr("landing_vref"))
4488 label.set_use_markup(True)
4489 label.set_use_underline(True)
4490 label.set_alignment(0.0, 0.5)
4491 table.attach(label, 1, 2, row, row + 1)
4492
4493 self._vref = IntegerEntry()
4494 self._vref.set_width_chars(5)
4495 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip_knots"))
4496 self._vref.connect("integer-changed", self._vrefChanged)
4497 table.attach(self._vref, 3, 4, row, row + 1)
4498 label.set_mnemonic_widget(self._vref)
4499
4500 self._vrefUnit = Gtk.Label(xstr("label_knots"))
4501 table.attach(self._vrefUnit, 4, 5, row, row + 1)
4502
4503 row += 1
4504
4505 self._antiIceOn = Gtk.CheckButton(xstr("landing_antiice"))
4506 self._antiIceOn.set_use_underline(True)
4507 self._antiIceOn.set_tooltip_text(xstr("landing_antiice_tooltip"))
4508 table.attach(self._antiIceOn, 3, 5, row, row + 1)
4509
4510 self.addCancelFlightButton()
4511
4512 self.addPreviousButton(clicked = self._backClicked)
4513
4514 self._button = self.addNextButton(clicked = self._forwardClicked)
4515
4516 self._active = False
4517
4518 @property
4519 def star(self):
4520 """Get the STAR or None if none entered."""
4521 text = self._star.get_child().get_text()
4522 return text if self._star.get_active()!=0 and text and text!="N/A" \
4523 else None
4524
4525 @property
4526 def transition(self):
4527 """Get the transition or None if none entered."""
4528 text = self._transition.get_child().get_text()
4529 return text if self._transition.get_active()!=0 and text and text!="N/A" \
4530 else None
4531
4532 @property
4533 def approachType(self):
4534 """Get the approach type."""
4535 return self._approachType.get_text()
4536
4537 @property
4538 def runway(self):
4539 """Get the runway."""
4540 return self._runway.get_text()
4541
4542 @property
4543 def vref(self):
4544 """Return the landing reference speed."""
4545 return self._vref.get_int()
4546
4547 @property
4548 def antiIceOn(self):
4549 """Get whether the anti-ice system has been turned on."""
4550 return self._antiIceOn.get_active()
4551
4552 @antiIceOn.setter
4553 def antiIceOn(self, value):
4554 """Set the anti-ice indicator."""
4555 self._antiIceOn.set_active(value)
4556
4557 def reset(self):
4558 """Reset the page if the wizard is reset."""
4559 super(LandingPage, self).reset()
4560 self._vref.reset()
4561 self._antiIceOn.set_active(False)
4562 self._flightEnded = False
4563 self._active = False
4564
4565 def activate(self):
4566 """Called when the page is activated."""
4567 self._updatingMETAR = True
4568 self._metar.get_buffer().set_text(self._wizard.arrivalMETAR, -1)
4569 self._updatingMETAR = False
4570
4571 self._star.set_active(0)
4572 self._star.set_sensitive(True)
4573
4574 self._transition.set_active(0)
4575 self._transition.set_sensitive(True)
4576
4577 if self._wizard.landingRunway is None:
4578 self._runway.set_text("")
4579 else:
4580 self._runway.set_text(self._wizard.landingRunway)
4581 self._runway.set_sensitive(True)
4582
4583 self._approachType.set_text("")
4584 self._approachType.set_sensitive(True)
4585
4586 self._vref.set_int(None)
4587 self._vref.set_sensitive(True)
4588
4589 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
4590 speedUnit = xstr("label" + i18nSpeedUnit)
4591 self._vrefUnit.set_text(speedUnit)
4592
4593 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip" +
4594 i18nSpeedUnit))
4595
4596 self._updateForwardButton()
4597
4598 self._active = True
4599
4600 def flightEnded(self):
4601 """Called when the flight has ended."""
4602 super(LandingPage, self).flightEnded()
4603 self._flightEnded = True
4604 self._updateForwardButton()
4605
4606 def changeMETAR(self, metar):
4607 """Change the METAR as a result of an edit on one of the other
4608 pages."""
4609 if self._active:
4610 print("LandingPage.changeMETAR")
4611 self._updatingMETAR = True
4612 self._metar.get_buffer().set_text(metar, -1)
4613 self._updatingMETAR = False
4614
4615 self._updateForwardButton()
4616
4617 def _updateForwardButton(self):
4618 """Update the sensitivity of the forward button."""
4619 sensitive = self._flightEnded and \
4620 self._metar.get_text()!="" and \
4621 (self.star is not None or
4622 self.transition is not None) and \
4623 self._runway.get_text()!="" and \
4624 self._approachType.get_text()!="" and \
4625 self.vref is not None
4626 self._button.set_sensitive(sensitive)
4627
4628 def _upperChanged(self, entry):
4629 """Called for entry widgets that must be converted to uppercase."""
4630 entry.set_text(entry.get_text().upper())
4631 self._updateForwardButton()
4632
4633 def _upperChangedComboBox(self, comboBox):
4634 """Called for combo box widgets that must be converted to uppercase."""
4635 if comboBox.get_active()==-1:
4636 entry = comboBox.get_child()
4637 entry.set_text(entry.get_text().upper())
4638 self._updateForwardButton()
4639
4640 def _vrefChanged(self, widget, value):
4641 """Called when the Vref has changed."""
4642 self._updateForwardButton()
4643
4644 def _backClicked(self, button):
4645 """Called when the Back button is pressed."""
4646 self.goBack()
4647
4648 def _forwardClicked(self, button):
4649 """Called when the forward button is clicked."""
4650 wizard = self._wizard
4651
4652 aircraft = wizard.gui.flight.aircraft
4653 aircraft.updateVRef()
4654 aircraft.updateLandingAntiIce()
4655 if wizard.gui.config.onlineGateSystem and \
4656 wizard.loggedIn and not self._completed and \
4657 wizard.bookedFlight.arrivalICAO=="LHBP" and \
4658 not wizard.entranceExam:
4659 wizard.getFleet(callback = self._fleetRetrieved, force = True)
4660 elif wizard.entranceExam:
4661 self._handleEntranceExamDone()
4662 else:
4663 wizard.nextPage()
4664
4665 def _fleetRetrieved(self, fleet):
4666 """Callback for the fleet retrieval."""
4667 self._wizard.nextPage()
4668
4669 def _metarChanged(self, entry):
4670 """Called when the METAR has changed."""
4671 print("LandingPage.metarChanged", self._updatingMETAR)
4672 if not self._updatingMETAR:
4673 self._updateForwardButton()
4674 self._wizard.metarChanged(entry.get_text(), self)
4675
4676 def _metarInserted(self, buffer, position, text, length):
4677 """Called when new characters are inserted into the METAR.
4678
4679 It uppercases all characters."""
4680 print("LandingPage.metarInserted", self._updatingMETAR)
4681 if not self._updatingMETAR:
4682 self._updatingMETAR = True
4683
4684 buffer.delete_text(position, length)
4685 buffer.insert_text(position, text.upper(), length)
4686
4687 self._updatingMETAR = False
4688
4689 def _handleEntranceExamDone(self):
4690 """Handle the end of the entrance exam.
4691
4692 If the there was a NO-GO fault, notify the user that exam is a failure
4693 and take them back to the student page. Otherwise congratulate, update
4694 the database to reflect that the exam has been taken and go back to the
4695 student page."""
4696 self._wizard.jumpPage("chkfinish")
4697
4698#-----------------------------------------------------------------------------
4699
4700class PIREPSaveHelper(object):
4701 """A helper to use for saving PIREPs."""
4702 def __init__(self, wizard):
4703 """Construct the helper."""
4704 super(PIREPSaveHelper, self).__init__()
4705
4706 self._wizard = wizard
4707
4708 self._lastSavePath = None
4709 self._savePIREPDialog = None
4710
4711 def addButton(self, page):
4712 """Add a button to save the PIREP to the given page."""
4713 return page.addButton(xstr("finish_save"), sensitive = False,
4714 clicked = self._saveClicked,
4715 tooltip = xstr("finish_save_tooltip"),
4716 clickedArg = page)
4717
4718 def autoSavePIREP(self, page):
4719 """Perform the automatic saving of the PIREP."""
4720 self._lastSavePath = os.path.join(self._wizard.gui.config.pirepDirectory,
4721 self._getDefaultPIREPName())
4722 self._lastSavePath = self._lastSavePath
4723 self._savePIREP(page, automatic = True)
4724
4725 def _getDefaultPIREPName(self):
4726 """Get the default name of the PIREP."""
4727 gui = self._wizard.gui
4728
4729 bookedFlight = gui.bookedFlight
4730 tm = time.gmtime()
4731
4732 pilotID = self._wizard.pilotID
4733 if pilotID: pilotID += " "
4734 return "%s%s %02d%02d %s-%s.pirep" % \
4735 (pilotID, str(bookedFlight.departureTime.date()),
4736 tm.tm_hour, tm.tm_min,
4737 bookedFlight.departureICAO, bookedFlight.arrivalICAO)
4738
4739 def _saveClicked(self, button, page):
4740 """Called when the Save PIREP button is clicked."""
4741 gui = self._wizard.gui
4742
4743 fileName = self._getDefaultPIREPName()
4744
4745 dialog = self._getSaveDialog()
4746
4747 if self._lastSavePath is None:
4748 pirepDirectory = gui.config.pirepDirectory
4749 if pirepDirectory is not None:
4750 dialog.set_current_folder(pirepDirectory)
4751 else:
4752 dialog.set_current_folder(os.path.dirname(self._lastSavePath))
4753
4754 dialog.set_current_name(fileName)
4755 result = dialog.run()
4756 dialog.hide()
4757
4758 if result==Gtk.ResponseType.OK:
4759 self._lastSavePath = dialog.get_filename()
4760 self._savePIREP(page)
4761
4762 def _savePIREP(self, page, automatic = False):
4763 """Perform the saving of the PIREP."""
4764
4765 gui = self._wizard.gui
4766
4767 if automatic:
4768 gui.beginBusy(xstr("finish_autosave_busy"))
4769
4770 pirep = PIREP(gui.flight)
4771 error = pirep.save(self._lastSavePath)
4772
4773 if automatic:
4774 gui.endBusy()
4775
4776 if error:
4777 type = Gtk.MessageType.ERROR
4778 message = xstr("finish_save_failed")
4779 secondary = xstr("finish_save_failed_sec") % (error,)
4780 else:
4781 type = Gtk.MessageType.INFO
4782 message = xstr("finish_save_done")
4783 if automatic:
4784 secondary = xstr("finish_save_done_sec") % (self._lastSavePath,)
4785 else:
4786 secondary = None
4787 page.setPIREPSaved()
4788
4789 dialog = Gtk.MessageDialog(parent = gui.mainWindow,
4790 type = type, message_format = message)
4791 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
4792 dialog.set_title(WINDOW_TITLE_BASE)
4793 if secondary is not None:
4794 dialog.format_secondary_markup(secondary)
4795
4796 dialog.run()
4797 dialog.hide()
4798
4799 def _getSaveDialog(self):
4800 """Get the PIREP saving dialog.
4801
4802 If it does not exist yet, create it."""
4803 if self._savePIREPDialog is None:
4804 gui = self._wizard.gui
4805 dialog = Gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
4806 xstr("finish_save_title"),
4807 action = Gtk.FileChooserAction.SAVE,
4808 buttons = (Gtk.STOCK_CANCEL,
4809 Gtk.ResponseType.CANCEL,
4810 Gtk.STOCK_OK, Gtk.ResponseType.OK),
4811 parent = gui.mainWindow)
4812 dialog.set_modal(True)
4813 dialog.set_do_overwrite_confirmation(True)
4814
4815 filter = Gtk.FileFilter()
4816 filter.set_name(xstr("file_filter_pireps"))
4817 filter.add_pattern("*.pirep")
4818 dialog.add_filter(filter)
4819
4820 filter = Gtk.FileFilter()
4821 filter.set_name(xstr("file_filter_all"))
4822 filter.add_pattern("*.*")
4823 dialog.add_filter(filter)
4824
4825 self._savePIREPDialog = dialog
4826
4827 return self._savePIREPDialog
4828
4829#-----------------------------------------------------------------------------
4830
4831class FinishPage(Page):
4832 """Flight finish page."""
4833 def __init__(self, wizard, saveHelper):
4834 """Construct the finish page."""
4835 help = xstr("finish_help") + xstr("finish_help_goodtime")
4836 super(FinishPage, self).__init__(wizard, "finish",
4837 xstr("finish_title"), help)
4838
4839 alignment = Gtk.Alignment(xalign = 0.5, yalign = 0.5,
4840 xscale = 0.0, yscale = 0.0)
4841
4842 table = Gtk.Table(10, 2)
4843 table.set_row_spacings(4)
4844 table.set_col_spacings(16)
4845 table.set_homogeneous(False)
4846 alignment.add(table)
4847 self.setMainWidget(alignment)
4848
4849 row = 0
4850
4851 labelAlignment = Gtk.Alignment(xalign=1.0, xscale=0.0)
4852 label = Gtk.Label(xstr("finish_rating"))
4853 labelAlignment.add(label)
4854 table.attach(labelAlignment, 0, 1, row, row+1)
4855
4856 labelAlignment = Gtk.Alignment(xalign=0.0, xscale=0.0)
4857 self._flightRating = Gtk.Label()
4858 self._flightRating.set_width_chars(8)
4859 self._flightRating.set_alignment(0.0, 0.5)
4860 self._flightRating.set_use_markup(True)
4861 labelAlignment.add(self._flightRating)
4862 table.attach(labelAlignment, 1, 2, row, row+1)
4863
4864 row += 1
4865
4866 labelAlignment = Gtk.Alignment(xalign=1.0, xscale=0.0)
4867 label = Gtk.Label(xstr("finish_dep_time"))
4868 labelAlignment.add(label)
4869 table.attach(labelAlignment, 0, 1, row, row+1)
4870
4871 labelAlignment = Gtk.Alignment(xalign=0.0, xscale=0.0)
4872 self._depTime = Gtk.Label()
4873 self._depTime.set_width_chars(13)
4874 self._depTime.set_alignment(0.0, 0.5)
4875 self._depTime.set_use_markup(True)
4876 self._depTime.set_tooltip_markup(xstr("finish_dep_time_tooltip"))
4877 labelAlignment.add(self._depTime)
4878 table.attach(labelAlignment, 1, 2, row, row+1)
4879
4880 row += 1
4881
4882 labelAlignment = Gtk.Alignment(xalign=1.0, xscale=0.0)
4883 label = Gtk.Label(xstr("finish_flight_time"))
4884 labelAlignment.add(label)
4885 table.attach(labelAlignment, 0, 1, row, row+1)
4886
4887 labelAlignment = Gtk.Alignment(xalign=0.0, xscale=0.0)
4888 self._flightTime = Gtk.Label()
4889 self._flightTime.set_width_chars(10)
4890 self._flightTime.set_alignment(0.0, 0.5)
4891 self._flightTime.set_use_markup(True)
4892 labelAlignment.add(self._flightTime)
4893 table.attach(labelAlignment, 1, 2, row, row+1)
4894
4895 row += 1
4896
4897 labelAlignment = Gtk.Alignment(xalign=1.0, xscale=0.0)
4898 label = Gtk.Label(xstr("finish_block_time"))
4899 labelAlignment.add(label)
4900 table.attach(labelAlignment, 0, 1, row, row+1)
4901
4902 labelAlignment = Gtk.Alignment(xalign=0.0, xscale=0.0)
4903 self._blockTime = Gtk.Label()
4904 self._blockTime.set_width_chars(10)
4905 self._blockTime.set_alignment(0.0, 0.5)
4906 self._blockTime.set_use_markup(True)
4907 labelAlignment.add(self._blockTime)
4908 table.attach(labelAlignment, 1, 2, row, row+1)
4909
4910 row += 1
4911
4912 labelAlignment = Gtk.Alignment(xalign=1.0, xscale=0.0)
4913 label = Gtk.Label(xstr("finish_arr_time"))
4914 labelAlignment.add(label)
4915 table.attach(labelAlignment, 0, 1, row, row+1)
4916
4917 labelAlignment = Gtk.Alignment(xalign=0.0, xscale=0.0)
4918 self._arrTime = Gtk.Label()
4919 self._arrTime.set_width_chars(13)
4920 self._arrTime.set_alignment(0.0, 0.5)
4921 self._arrTime.set_use_markup(True)
4922 self._arrTime.set_tooltip_markup(xstr("finish_arr_time_tooltip"))
4923 labelAlignment.add(self._arrTime)
4924 table.attach(labelAlignment, 1, 2, row, row+1)
4925
4926 row += 1
4927
4928 labelAlignment = Gtk.Alignment(xalign=1.0, xscale=0.0)
4929 label = Gtk.Label(xstr("finish_distance"))
4930 labelAlignment.add(label)
4931 table.attach(labelAlignment, 0, 1, row, row+1)
4932
4933 labelAlignment = Gtk.Alignment(xalign=0.0, xscale=0.0)
4934 self._distanceFlown = Gtk.Label()
4935 self._distanceFlown.set_width_chars(10)
4936 self._distanceFlown.set_alignment(0.0, 0.5)
4937 self._distanceFlown.set_use_markup(True)
4938 labelAlignment.add(self._distanceFlown)
4939 table.attach(labelAlignment, 1, 2, row, row+1)
4940
4941 row += 1
4942
4943 labelAlignment = Gtk.Alignment(xalign=1.0, xscale=0.0)
4944 label = Gtk.Label(xstr("finish_fuel"))
4945 labelAlignment.add(label)
4946 table.attach(labelAlignment, 0, 1, row, row+1)
4947
4948 labelAlignment = Gtk.Alignment(xalign=0.0, xscale=0.0)
4949 self._fuelUsed = Gtk.Label()
4950 self._fuelUsed.set_width_chars(10)
4951 self._fuelUsed.set_alignment(0.0, 0.5)
4952 self._fuelUsed.set_use_markup(True)
4953 labelAlignment.add(self._fuelUsed)
4954 table.attach(labelAlignment, 1, 2, row, row+1)
4955
4956 row += 1
4957
4958 labelAlignment = Gtk.Alignment(xalign = 1.0, xscale = 0.0,
4959 yalign = 0.5, yscale = 0.0)
4960 label = Gtk.Label(xstr("finish_type"))
4961 label.set_use_underline(True)
4962 labelAlignment.add(label)
4963 table.attach(labelAlignment, 0, 1, row, row+1)
4964
4965 self._flightType = createFlightTypeComboBox()
4966 self._flightType.set_tooltip_text(xstr("finish_type_tooltip"))
4967 self._flightType.set_active(0)
4968 self._flightType.connect("changed", self._flightTypeChanged)
4969 flightTypeAlignment = Gtk.Alignment(xalign=0.0, xscale=0.0)
4970 flightTypeAlignment.add(self._flightType)
4971 table.attach(flightTypeAlignment, 1, 2, row, row+1)
4972 label.set_mnemonic_widget(self._flightType)
4973
4974 row += 1
4975
4976 self._onlineFlight = Gtk.CheckButton(xstr("finish_online"))
4977 self._onlineFlight.set_use_underline(True)
4978 self._onlineFlight.set_tooltip_text(xstr("finish_online_tooltip"))
4979 onlineFlightAlignment = Gtk.Alignment(xalign=0.0, xscale=0.0)
4980 onlineFlightAlignment.add(self._onlineFlight)
4981 table.attach(onlineFlightAlignment, 1, 2, row, row + 1)
4982
4983 row += 1
4984
4985 labelAlignment = Gtk.Alignment(xalign = 1.0, xscale = 0.0,
4986 yalign = 0.5, yscale = 0.0)
4987 self._gateLabel = Gtk.Label(xstr("finish_gate"))
4988 self._gateLabel.set_use_underline(True)
4989 labelAlignment.add(self._gateLabel)
4990 table.attach(labelAlignment, 0, 1, row, row+1)
4991
4992 self._gatesModel = Gtk.ListStore(str)
4993
4994 self._gate = Gtk.ComboBox(model = self._gatesModel)
4995 renderer = Gtk.CellRendererText()
4996 self._gate.pack_start(renderer, True)
4997 self._gate.add_attribute(renderer, "text", 0)
4998 self._gate.set_tooltip_text(xstr("finish_gate_tooltip"))
4999 self._gate.connect("changed", self._gateChanged)
5000 gateAlignment = Gtk.Alignment(xalign=0.0, xscale=1.0)
5001 gateAlignment.add(self._gate)
5002 table.attach(gateAlignment, 1, 2, row, row+1)
5003 self._gateLabel.set_mnemonic_widget(self._gate)
5004
5005 self.addButton(xstr("finish_newFlight"),
5006 sensitive = True,
5007 clicked = self._newFlightClicked,
5008 tooltip = xstr("finish_newFlight_tooltip"),
5009 padding = 16)
5010
5011 self.addPreviousButton(clicked = self._backClicked)
5012
5013 self._saveHelper = saveHelper
5014 self._saveButton = saveHelper.addButton(self)
5015
5016 self._tooBigTimeDifference = False
5017 self._deferredAutoSave = False
5018 self._pirepSaved = False
5019 self._pirepSent = False
5020
5021 self._sendButton = self.addButton(xstr("sendPIREP"), default = True,
5022 sensitive = False,
5023 clicked = self._sendClicked,
5024 tooltip = xstr("sendPIREP_tooltip"))
5025
5026 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 10*60.0)
5027 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 20*60.0)
5028 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 0*60.0)
5029 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), (23*60.0+50)*60.0)
5030 # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (1*60.0+5)*60.0)
5031 # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (0*60.0+50)*60.0)
5032 # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (0*60.0+5)*60.0)
5033 # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (23*60.0+45)*60.0)
5034
5035 @property
5036 def flightType(self):
5037 """Get the flight type."""
5038 index = self._flightType.get_active()
5039 return None if index<0 else self._flightType.get_model()[index][1]
5040
5041 @property
5042 def online(self):
5043 """Get whether the flight was an online flight or not."""
5044 return self._onlineFlight.get_active()
5045
5046 def activate(self):
5047 """Activate the page."""
5048 self._deferredAutoSave = False
5049 self._pirepSaved = False
5050 self._pirepSent = False
5051
5052 flight = self._wizard.gui._flight
5053 rating = flight.logger.getRating()
5054 if rating<0:
5055 self._flightRating.set_markup('<b><span foreground="red">NO GO</span></b>')
5056 else:
5057 self._flightRating.set_markup("<b>%.1f %%</b>" % (rating,))
5058
5059 flightLength = flight.flightTimeEnd - flight.flightTimeStart
5060 self._flightTime.set_markup("<b>%s</b>" % \
5061 (util.getTimeIntervalString(flightLength),))
5062
5063 blockLength = flight.blockTimeEnd - flight.blockTimeStart
5064 self._blockTime.set_markup("<b>%s</b>" % \
5065 (util.getTimeIntervalString(blockLength),))
5066
5067 self._distanceFlown.set_markup("<b>%.2f NM</b>" % \
5068 (flight.flownDistance,))
5069
5070 self._fuelUsed.set_markup("<b>%.0f kg</b>" % \
5071 (flight.startFuel - flight.endFuel,))
5072
5073 self._flightType.set_active(-1)
5074 self._onlineFlight.set_active(self._wizard.loggedIn)
5075
5076 self._gatesModel.clear()
5077 if self._wizard.gui.config.onlineGateSystem and \
5078 self._wizard.loggedIn and \
5079 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
5080 not self._wizard.entranceExam:
5081 occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
5082 for gate in lhbpGates.gates:
5083 if gate.isAvailable(lhbpGates, occupiedGateNumbers):
5084 self._gatesModel.append([gate.number])
5085 self._gateLabel.set_sensitive(True)
5086 self._gate.set_sensitive(True)
5087 self._gate.set_active(-1)
5088 else:
5089 self._gateLabel.set_sensitive(False)
5090 self._gate.set_sensitive(False)
5091
5092 self._updateTimes()
5093
5094 def updateButtons(self):
5095 """Update the sensitivity state of the buttons."""
5096 gui = self._wizard.gui
5097 faultsExplained = gui.faultsFullyExplained
5098 timesCorrect = self.flightType is None or \
5099 not self._tooBigTimeDifference or \
5100 gui.hasComments or gui.hasDelayCode
5101 sensitive = gui.flight is not None and \
5102 gui.flight.stage==const.STAGE_END and \
5103 self._flightType.get_active()>=0 and \
5104 (self._gatesModel.get_iter_first() is None or
5105 self._gate.get_active()>=0) and \
5106 faultsExplained and timesCorrect
5107
5108 self._updateHelp(faultsExplained, timesCorrect)
5109
5110 wasSensitive = self._saveButton.get_sensitive()
5111
5112 if gui.config.pirepAutoSave and sensitive and not wasSensitive:
5113 if gui.isWizardActive():
5114 self._saveHelper.autoSavePIREP(self)
5115 else:
5116 self._deferredAutoSave = True
5117
5118 if not sensitive:
5119 self._deferredAutoSave = False
5120
5121 self._saveButton.set_sensitive(sensitive)
5122 self._sendButton.set_sensitive(sensitive and
5123 self._wizard.bookedFlight.id is not None)
5124
5125 def grabDefault(self):
5126 """If the page has a default button, make it the default one."""
5127 super(FinishPage, self).grabDefault()
5128 if self._deferredAutoSave:
5129 self._saveHelper.autoSavePIREP(self)
5130 self._deferredAutoSave = False
5131
5132 def setPIREPSaved(self):
5133 """Mark the PIREP as saved."""
5134 self._pirepSaved = True
5135
5136 def _backClicked(self, button):
5137 """Called when the Back button is pressed."""
5138 self.goBack()
5139
5140 def _flightTypeChanged(self, comboBox):
5141 """Called when the flight type has changed."""
5142 self._updateTimes()
5143
5144 def _gateChanged(self, comboBox):
5145 """Called when the arrival gate has changed."""
5146 self.updateButtons()
5147
5148 def _newFlightClicked(self, button):
5149 """Called when the new flight button is clicked."""
5150 gui = self._wizard.gui
5151 if not self._pirepSent and not self._pirepSaved:
5152 dialog = Gtk.MessageDialog(parent = gui.mainWindow,
5153 type = Gtk.MessageType.QUESTION,
5154 message_format = xstr("finish_newFlight_question"))
5155
5156 dialog.add_button(xstr("button_no"), Gtk.ResponseType.NO)
5157 dialog.add_button(xstr("button_yes"), Gtk.ResponseType.YES)
5158
5159 dialog.set_title(WINDOW_TITLE_BASE)
5160 result = dialog.run()
5161 dialog.hide()
5162 if result!=Gtk.ResponseType.YES:
5163 return
5164
5165 gui.reset()
5166
5167 def _sendClicked(self, button):
5168 """Called when the Send button is clicked."""
5169 pirep = PIREP(self._wizard.gui.flight)
5170 self._wizard.gui.sendPIREP(pirep,
5171 callback = self._handlePIREPSent)
5172
5173 def _handlePIREPSent(self, returned, result):
5174 """Callback for the PIREP sending result."""
5175 self._pirepSent = returned and result.success
5176 if self._wizard.gui.config.onlineGateSystem and \
5177 self._wizard.loggedIn and not self._wizard.entranceExam and \
5178 returned and result.success:
5179 bookedFlight = self._wizard.bookedFlight
5180 if bookedFlight.arrivalICAO=="LHBP":
5181 iter = self._gate.get_active_iter()
5182 gateNumber = None if iter is None \
5183 else self._gatesModel.get_value(iter, 0)
5184
5185 status = const.PLANE_PARKING if gateNumber is None \
5186 else const.PLANE_HOME
5187 else:
5188 gateNumber = None
5189 status = const.PLANE_AWAY
5190
5191 self._wizard.updatePlane(self._planeUpdated,
5192 bookedFlight.tailNumber,
5193 status, gateNumber = gateNumber)
5194
5195 def _planeUpdated(self, success):
5196 """Callback for the plane updating."""
5197 pass
5198
5199 def _formatTime(self, scheduledTime, realTimestamp, xxx_todo_changeme):
5200 """Format the departure or arrival time based on the given data as a
5201 markup for a label."""
5202 (warning, error) = xxx_todo_changeme
5203 realTime = time.gmtime(realTimestamp)
5204
5205 if warning:
5206 colour = "red" if error else "orange"
5207 markupBegin = '<span foreground="%s">' % (colour,)
5208 markupEnd = '</span>'
5209 else:
5210 markupBegin = markupEnd = ""
5211
5212 markup = "<b>%s%02d:%02d [%02d:%02d]%s</b>" % \
5213 (markupBegin,
5214 realTime.tm_hour, realTime.tm_min,
5215 scheduledTime.hour, scheduledTime.minute,
5216 markupEnd)
5217
5218 return markup
5219
5220 def _updateTimes(self):
5221 """Format the flight times and the help text according to the flight
5222 type.
5223
5224 The buttons are also updated.
5225 """
5226 flight = self._wizard.gui._flight
5227 bookedFlight = flight.bookedFlight
5228
5229 (departureWarning, departureError) = flight.blockTimeStartWrong
5230 (arrivalWarning, arrivalError) = flight.blockTimeEndWrong
5231
5232 if self.flightType==const.FLIGHTTYPE_VIP:
5233 departureError = arrivalError = False
5234
5235 self._tooBigTimeDifference = departureError or arrivalError
5236
5237 self._depTime.set_markup(self._formatTime(bookedFlight.departureTime,
5238 flight.blockTimeStart,
5239 (departureWarning,
5240 departureError)))
5241
5242 self._arrTime.set_markup(self._formatTime(bookedFlight.arrivalTime,
5243 flight.blockTimeEnd,
5244 (arrivalWarning,
5245 arrivalError)))
5246
5247 self.updateButtons()
5248
5249 def _updateHelp(self, faultsExplained, timesCorrect):
5250 """Update the help text according to the actual situation."""
5251 if not faultsExplained:
5252 self.setHelp(xstr("finish_help") + xstr("finish_help_faults"))
5253 elif not timesCorrect:
5254 self.setHelp(xstr("finish_help") + xstr("finish_help_wrongtime"))
5255 else:
5256 self.setHelp(xstr("finish_help") + xstr("finish_help_goodtime"))
5257
5258
5259#-----------------------------------------------------------------------------
5260
5261class CheckFlightFinishPage(Page):
5262 """Finish page for a check flight."""
5263 def __init__(self, wizard, saveHelper):
5264 """Construct the check flight finish page."""
5265 super(CheckFlightFinishPage, self).__init__(wizard,
5266 "chkfinish",
5267 xstr("chkfinish_title"),
5268 "")
5269
5270 alignment = Gtk.Alignment(xalign = 0.5, yalign = 0.5,
5271 xscale = 1.0, yscale = 1.0)
5272 self._label = Gtk.Label()
5273 alignment.add(self._label)
5274
5275 self.setMainWidget(alignment)
5276
5277 self._saveHelper = saveHelper
5278 self._saveButton = saveHelper.addButton(self)
5279
5280 self._button = self.addNextButton(sensitive = False,
5281 clicked = self._forwardClicked)
5282
5283 def activate(self):
5284 """Activate the page."""
5285 wizard = self._wizard
5286 loginResult = wizard.loginResult
5287 gui = wizard.gui
5288 rating = gui.flight.logger.getRating()
5289
5290 if rating>=0:
5291 loginResult.checkFlightStatus = True
5292
5293 firstOfficer = \
5294 loginResult.entryExamPassed and loginResult.checkFlightStatus
5295
5296 if firstOfficer:
5297 loginResult.rank = "FO"
5298
5299 if rating<0:
5300 mainMessage = xstr("chkfinish_failed")
5301 else:
5302 mainMessage = xstr("chkfinish_passed_begin")
5303 if firstOfficer:
5304 mainMessage += xstr("chkfinish_passed_fo")
5305 mainMessage += xstr("chkfinish_passed_end")
5306
5307 if firstOfficer:
5308 nextMessage = xstr("chkfinish_next")
5309 else:
5310 nextMessage = xstr("chkfinish_next_student_begin")
5311 if not loginResult.entryExamPassed and \
5312 not loginResult.checkFlightStatus:
5313 nextMessage += xstr("chkfinish_next_student_nothing")
5314 elif loginResult.entryExamPassed and \
5315 not loginResult.checkFlightStatus:
5316 nextMessage += xstr("chkfinish_next_student_no_flight")
5317 elif not loginResult.entryExamPassed and \
5318 loginResult.checkFlightStatus:
5319 nextMessage += xstr("chkfinish_next_student_no_exam")
5320
5321 self._label.set_text(mainMessage +
5322 xstr("chkfinish_savepirep") +
5323 nextMessage)
5324 self._label.set_use_markup(True)
5325 self._label.set_alignment(0.5, 0.0)
5326
5327 self._saveButton.set_sensitive(True)
5328 self._button.set_sensitive(True)
5329
5330 def _forwardClicked(self, button):
5331 """Jump to the student page if there are some tasks to do,
5332 or to the flight selection page, if the pilot is allowed to perform
5333 MAVA flights."""
5334 wizard = self._wizard
5335 gui = wizard.gui
5336
5337 loginResult = wizard.loginResult
5338 if loginResult.checkFlightStatus:
5339 gui.beginBusy(xstr("chkfinish_updateweb_busy"))
5340 gui.webHandler.setCheckFlightPassed(self._checkFlightPassedSetCallback,
5341 wizard.checkFlightAircraftType)
5342 else:
5343 self._resetGUI()
5344
5345 def _checkFlightPassedSetCallback(self, returned, result):
5346 """Called when the check flight status has been set."""
5347 GObject.idle_add(self._checkFlightPassedSet, returned, result)
5348
5349 def _checkFlightPassedSet(self, returned, result):
5350 """Handle the result of an attempt to set the check flight status."""
5351 gui = self._wizard.gui
5352
5353 gui.endBusy()
5354
5355 if returned:
5356 self._resetGUI()
5357 else:
5358 dialog = Gtk.MessageDialog(parent = gui.mainWindow,
5359 type = Gtk.MessageType.ERROR,
5360 message_format =
5361 xstr("chkfinish_passedset_failed"))
5362 dialog.set_title(WINDOW_TITLE_BASE + " - " +
5363 xstr("chkfinish_passedset_failed_title"))
5364 dialog.format_secondary_markup(xstr("chkfinish_passedset_failed_secondary"))
5365
5366 dialog.add_button(xstr("button_ok"), 0)
5367
5368 dialog.run()
5369 dialog.hide()
5370
5371 def _resetGUI(self):
5372 """Reset the GUI."""
5373 gui = self._wizard.gui
5374 gui.reset()
5375
5376#-----------------------------------------------------------------------------
5377
5378class Wizard(Gtk.VBox):
5379 """The flight wizard."""
5380 def __init__(self, gui):
5381 """Construct the wizard."""
5382 super(Wizard, self).__init__()
5383
5384 self.gui = gui
5385
5386 self._pages = []
5387 self._currentPage = None
5388
5389 self._loginPage = LoginPage(self)
5390 self._pages.append(self._loginPage)
5391 self._flightSelectionPage = FlightSelectionPage(self)
5392 self._pages.append(self._flightSelectionPage)
5393 self._pages.append(GateSelectionPage(self))
5394 self._pages.append(RegisterPage(self))
5395 self._studentPage = StudentPage(self)
5396 self._pages.append(self._studentPage)
5397 self._pages.append(ConnectPage(self))
5398 self._payloadPage = PayloadPage(self)
5399 self._pages.append(self._payloadPage)
5400 self._payloadIndex = len(self._pages)
5401 self._pages.append(TimePage(self))
5402 self._routePage = RoutePage(self)
5403 self._pages.append(self._routePage)
5404 self._simBriefSetupPage = SimBriefSetupPage(self)
5405 self._pages.append(self._simBriefSetupPage)
5406 self._simBriefingPage = SimBriefingPage(self)
5407 self._pages.append(self._simBriefingPage)
5408 self._pages.append(FuelPage(self))
5409 self._departureBriefingPage = BriefingPage(self, True)
5410 self._pages.append(self._departureBriefingPage)
5411 self._arrivalBriefingPage = BriefingPage(self, False)
5412 self._pages.append(self._arrivalBriefingPage)
5413 self._arrivalBriefingIndex = len(self._pages)
5414 self._takeoffPage = TakeoffPage(self)
5415 self._pages.append(self._takeoffPage)
5416 self._cruisePage = CruisePage(self)
5417 self._pages.append(self._cruisePage)
5418 self._landingPage = LandingPage(self)
5419 self._pages.append(self._landingPage)
5420
5421 pirepSaveHelper = PIREPSaveHelper(self)
5422
5423 self._finishPage = FinishPage(self, pirepSaveHelper)
5424 self._pages.append(self._finishPage)
5425 self._pages.append(CheckFlightFinishPage(self, pirepSaveHelper))
5426
5427 self._requestedWidth = None
5428 self._requestedHeight = None
5429
5430 self.connect("size-allocate", self._sizeAllocate)
5431
5432 for page in self._pages:
5433 page.show_all()
5434 page.setStyle()
5435
5436 self._initialize()
5437 self._allocateSize()
5438
5439 def _sizeAllocate(self, widget, allocation):
5440 if self._requestedWidth is not None and \
5441 self._requestedHeight is not None:
5442 return
5443
5444 (maxWidth, maxHeight) = self._allocateSize()
5445
5446 self._requestedWidth = maxWidth
5447 self._requestedHeight = maxHeight
5448
5449 def _allocateSize(self):
5450 """Perform the real size allocation."""
5451
5452 if self._currentPage is not None:
5453 self.remove(self._pages[self._currentPage])
5454
5455 maxWidth = 0
5456 maxHeight = 0
5457 for page in self._pages:
5458 self.add(page)
5459 self.show_all()
5460 pageSizeRequest = page.size_request()
5461 width = pageSizeRequest.width
5462 height = pageSizeRequest.height
5463 maxWidth = max(maxWidth, width)
5464 maxHeight = max(maxHeight, height)
5465 self.remove(page)
5466
5467 if self._currentPage is not None:
5468 self.add(self._pages[self._currentPage])
5469
5470 self.set_size_request(maxWidth, maxHeight)
5471
5472 return (maxWidth, maxHeight)
5473
5474 @property
5475 def pilotID(self):
5476 """Get the pilot ID, if given."""
5477 return self._loginPage.pilotID
5478
5479 @property
5480 def entranceExam(self):
5481 """Get whether an entrance exam is about to be taken."""
5482 return self._loginResult is not None and self._loginResult.rank=="STU"
5483
5484 @property
5485 def loggedIn(self):
5486 """Indicate if there was a successful login."""
5487 return self._loginResult is not None
5488
5489 @property
5490 def loginResult(self):
5491 """Get the login result."""
5492 return self._loginResult
5493
5494 @property
5495 def checkFlightAircraftType(self):
5496 """Get the type of the aircraft used to perform the check flight."""
5497 return self._studentPage.aircraftType
5498
5499 def setCurrentPage(self, index, finalize = False, fromPageShift = None):
5500 """Set the current page to the one with the given index.
5501
5502 @param fromPageShift if given, the relative index of one of the
5503 previous pages that should be used as the from-page of the next
5504 page. E.g. if fromPageShift is 1, the previous page will be the
5505 from-page."""
5506 assert index < len(self._pages)
5507
5508 fromPage = self._currentPage
5509 if fromPage is not None:
5510 page = self._pages[fromPage]
5511 if finalize and not page._completed:
5512 page.complete()
5513 self.remove(page)
5514 if fromPageShift is not None:
5515 fromPage -= fromPageShift
5516
5517 self._currentPage = index
5518 page = self._pages[index]
5519 self.add(page)
5520 if page._fromPage is None:
5521 page._fromPage = fromPage
5522 page.initialize()
5523 self.show_all()
5524 if fromPage is not None:
5525 self.grabDefault()
5526
5527 @property
5528 def bookedFlight(self):
5529 """Get the booked flight selected."""
5530 return self._bookedFlight
5531
5532 @property
5533 def numCrew(self):
5534 """Get the number of crew members."""
5535 return self._payloadPage.numCrew
5536
5537 @property
5538 def numPassengers(self):
5539 """Get the number of passengers."""
5540 return self._payloadPage.numPassengers
5541
5542 @property
5543 def bagWeight(self):
5544 """Get the baggage weight."""
5545 return self._payloadPage.bagWeight
5546
5547 @property
5548 def cargoWeight(self):
5549 """Get the cargo weight."""
5550 return self._payloadPage.cargoWeight
5551
5552 @property
5553 def mailWeight(self):
5554 """Get the mail weight."""
5555 return self._payloadPage.mailWeight
5556
5557 @property
5558 def zfw(self):
5559 """Get the calculated ZFW value."""
5560 return 0 if self._bookedFlight is None \
5561 else self._payloadPage.calculateZFW()
5562
5563 @property
5564 def filedCruiseLevel(self):
5565 """Get the filed cruise level."""
5566 return self._routePage.filedCruiseLevel
5567
5568 @property
5569 def filedCruiseAltitude(self):
5570 """Get the filed cruise altitude."""
5571 return self._routePage.filedCruiseLevel * 100
5572
5573 @property
5574 def cruiseAltitude(self):
5575 """Get the cruise altitude."""
5576 level = self._cruisePage.cruiseLevel if self._cruisePage.activated \
5577 else self._routePage.filedCruiseLevel
5578 return level * 100
5579
5580 @property
5581 def loggableCruiseAltitude(self):
5582 """Get the cruise altitude that can be logged."""
5583 if self._cruisePage.activated:
5584 return self._cruisePage.loggableCruiseLevel * 100
5585 else:
5586 return 0
5587
5588 @property
5589 def route(self):
5590 """Get the route."""
5591 return self._routePage.route
5592
5593 @property
5594 def alternate(self):
5595 """Get the ICAO code of the alternate airport."""
5596 return self._routePage.alternate
5597
5598 @property
5599 def departureMETAR(self):
5600 """Get the METAR of the departure airport."""
5601 return self._departureBriefingPage.metar
5602
5603 @property
5604 def arrivalMETAR(self):
5605 """Get the METAR of the arrival airport."""
5606 return self._arrivalBriefingPage.metar
5607
5608 @property
5609 def departureRunway(self):
5610 """Get the departure runway."""
5611 return self._takeoffPage.runway
5612
5613 @property
5614 def sid(self):
5615 """Get the SID."""
5616 return self._takeoffPage.sid
5617
5618 @property
5619 def v1(self):
5620 """Get the V1 speed."""
5621 return self._takeoffPage.v1
5622
5623 @property
5624 def vr(self):
5625 """Get the Vr speed."""
5626 return self._takeoffPage.vr
5627
5628 @property
5629 def v2(self):
5630 """Get the V2 speed."""
5631 return self._takeoffPage.v2
5632
5633 @property
5634 def derate(self):
5635 """Get the derate value."""
5636 return self._takeoffPage.derate
5637
5638 @property
5639 def takeoffAntiIceOn(self):
5640 """Get whether the anti-ice system was on during take-off."""
5641 return self._takeoffPage.antiIceOn
5642
5643 @takeoffAntiIceOn.setter
5644 def takeoffAntiIceOn(self, value):
5645 """Set anti-ice on indicator."""
5646 self._takeoffPage.antiIceOn = value
5647
5648 @property
5649 def rtoIndicated(self):
5650 """Get whether the pilot has indicated that an RTO has occured."""
5651 return self._takeoffPage.rtoIndicated
5652
5653 @property
5654 def arrivalRunway(self):
5655 """Get the arrival runway."""
5656 return self._landingPage.runway
5657
5658 @property
5659 def star(self):
5660 """Get the STAR."""
5661 return self._landingPage.star
5662
5663 @property
5664 def transition(self):
5665 """Get the transition."""
5666 return self._landingPage.transition
5667
5668 @property
5669 def approachType(self):
5670 """Get the approach type."""
5671 return self._landingPage.approachType
5672
5673 @property
5674 def vref(self):
5675 """Get the Vref speed."""
5676 return self._landingPage.vref
5677
5678 @property
5679 def landingAntiIceOn(self):
5680 """Get whether the anti-ice system was on during landing."""
5681 return self._landingPage.antiIceOn
5682
5683 @landingAntiIceOn.setter
5684 def landingAntiIceOn(self, value):
5685 """Set anti-ice on indicator."""
5686 self._landingPage.antiIceOn = value
5687
5688 @property
5689 def flightType(self):
5690 """Get the flight type."""
5691 return self._finishPage.flightType
5692
5693 @property
5694 def online(self):
5695 """Get whether the flight was online or not."""
5696 return self._finishPage.online
5697
5698 @property
5699 def usingSimBrief(self):
5700 """Indicate if we are using a SimBrief briefing or not."""
5701 return self._usingSimBrief
5702
5703 @usingSimBrief.setter
5704 def usingSimBrief(self, x):
5705 """Set whether we are using a SimBrief briefing or not."""
5706 self._usingSimBrief = x
5707
5708 def nextPage(self, finalize = True):
5709 """Go to the next page."""
5710 nextPageID = self._pages[self._currentPage].nextPageID
5711 self.jumpPage(1 if nextPageID is None else nextPageID, finalize)
5712
5713 def jumpPage(self, countOrID, finalize = True, fromPageShift = None):
5714 """Go to the page which is 'count' pages after the current one."""
5715 if isinstance(countOrID, str):
5716 targetIndex = self._getIndexOf(countOrID)
5717 else:
5718 targetIndex = self._currentPage + countOrID
5719 self.setCurrentPage(targetIndex,
5720 finalize = finalize, fromPageShift = fromPageShift)
5721
5722 def grabDefault(self):
5723 """Make the default button of the current page the default."""
5724 self._pages[self._currentPage].grabDefault()
5725
5726 def connected(self, fsType, descriptor):
5727 """Called when the connection could be made to the simulator."""
5728 self.nextPage()
5729
5730 def reset(self, loginResult):
5731 """Resets the wizard to go back to the login page."""
5732 self._initialize(keepLoginResult = loginResult is None,
5733 loginResult = loginResult)
5734
5735 def setStage(self, stage):
5736 """Set the flight stage to the given one."""
5737 if stage!=const.STAGE_END:
5738 self._cruisePage.setLoggable(Flight.canLogCruiseAltitude(stage))
5739
5740 if stage==const.STAGE_TAKEOFF:
5741 self._takeoffPage.allowForward()
5742 elif stage==const.STAGE_LANDING:
5743 if not self._arrivalBriefingPage.metarEdited:
5744 print("Downloading arrival METAR again")
5745 self.gui.webHandler.getMETARs(self._arrivalMETARCallback,
5746 [self._bookedFlight.arrivalICAO])
5747
5748 elif stage==const.STAGE_END:
5749 for page in self._pages:
5750 page.flightEnded()
5751
5752 def _initialize(self, keepLoginResult = False, loginResult = None):
5753 """Initialize the wizard."""
5754 if not keepLoginResult:
5755 self._loginResult = loginResult
5756
5757 self._loginCallback = None
5758
5759 self._fleet = None
5760 self._fleetCallback = None
5761
5762 self._bookedFlight = None
5763 self._departureGate = "-"
5764 self._fuelData = None
5765 self._departureNOTAMs = None
5766 self._departureMETAR = None
5767 self._arrivalNOTAMs = None
5768 self._arrivalMETAR = None
5769 self._usingSimBrief = None
5770 self.takeoffRunway = None
5771 self.landingRunway = None
5772
5773 firstPage = 0 if self._loginResult is None else 1
5774 for page in self._pages[firstPage:]:
5775 page.reset()
5776
5777 self.setCurrentPage(firstPage)
5778 #self.setCurrentPage(10)
5779
5780 def login(self, callback, pilotID, password):
5781 """Called when the login button was clicked."""
5782 self._loginCallback = callback
5783 if pilotID is None:
5784 loginResult = self._loginResult
5785 assert loginResult is not None and loginResult.loggedIn
5786 pilotID = loginResult.pilotID
5787 password = loginResult.password
5788 busyMessage = xstr("reload_busy")
5789 else:
5790 self._loginResult = None
5791 busyMessage = xstr("login_busy")
5792
5793 self.gui.beginBusy(busyMessage)
5794
5795 self.gui.webHandler.login(self._loginResultCallback,
5796 pilotID, password)
5797
5798 def reloadFlights(self, callback):
5799 """Reload the flights from the MAVA server."""
5800 self.login(callback, None, None)
5801
5802 def addFlight(self, bookedFlight):
5803 """Add the given booked flight to the flight selection page."""
5804 self._flightSelectionPage.addFlight(bookedFlight)
5805
5806 def reflyFlight(self, bookedFlight):
5807 """Add the given booked flight to the flight selection page."""
5808 self._removePendingFlight(bookedFlight)
5809 self._flightSelectionPage._reflyFlight(bookedFlight)
5810
5811 def deleteFlight(self, bookedFlight):
5812 """Remove the given flight from the pending flight list."""
5813 self._removePendingFlight(bookedFlight)
5814 self._flightSelectionPage._updatePending()
5815
5816 def cancelFlight(self, reloadCallback):
5817 """Cancel the flight.
5818
5819 If it is an entry exam flight, we go back to the student page.
5820 Otherwise we reload the flights."""
5821 if self.entranceExam:
5822 self.reset(None)
5823 self.jumpPage("student")
5824 else:
5825 self.reloadFlights(reloadCallback)
5826
5827 def cruiseLevelChanged(self):
5828 """Called when the cruise level is changed."""
5829 return self.gui.cruiseLevelChanged()
5830
5831 def metarChanged(self, metar, originator):
5832 """Called when a METER is changed on on of the pages.
5833
5834 originator is the page that originated the changed. It will be used to
5835 determine which METAR (departure or arrival) has changed."""
5836 metar = metar.upper()
5837 if originator in [self._departureBriefingPage, self._takeoffPage]:
5838 self.departureMETARChanged(metar, originator)
5839 else:
5840 self.arrivalMETARChanged(metar, originator)
5841
5842 def departureMETARChanged(self, metar, originator):
5843 """Called when the departure METAR has been edited on one of the
5844 pages.
5845
5846 originator is the page that originated the change. It will not be
5847 called to set the METAR, while others will be."""
5848 for page in [self._departureBriefingPage, self._takeoffPage]:
5849 if page is not originator:
5850 page.changeMETAR(metar)
5851
5852 def arrivalMETARChanged(self, metar, originator):
5853 """Called when the arrival METAR has been edited on one of the
5854 pages.
5855
5856 originator is the page that originated the change. It will not be
5857 called to set the METAR, while others will be."""
5858 for page in [self._arrivalBriefingPage, self._landingPage]:
5859 if page is not originator:
5860 page.changeMETAR(metar)
5861
5862 def _removePendingFlight(self, flight):
5863 """Remove the given pending flight from the login result."""
5864 for flights in [self._loginResult.reportedFlights,
5865 self._loginResult.rejectedFlights]:
5866 for f in flights:
5867 if f.id==flight.id:
5868 flights.remove(f)
5869 return
5870
5871 def _loginResultCallback(self, returned, result):
5872 """The login result callback, called in the web handler's thread."""
5873 GObject.idle_add(self._handleLoginResult, returned, result)
5874
5875 def _handleLoginResult(self, returned, result):
5876 """Handle the login result."""
5877 self.gui.endBusy()
5878 isReload = self._loginResult is not None
5879 if returned:
5880 if result.loggedIn:
5881 self._loginResult = result
5882 self.gui.loginSuccessful()
5883 else:
5884 if isReload:
5885 message = xstr("reload_failed")
5886 else:
5887 message = xstr("login_entranceExam_invalid"
5888 if self.entranceExam else
5889 xstr("login_invalid"))
5890 dialog = Gtk.MessageDialog(parent = self.gui.mainWindow,
5891 type = Gtk.MessageType.ERROR,
5892 message_format = message)
5893 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
5894 dialog.set_title(WINDOW_TITLE_BASE)
5895 if isReload:
5896 secondary = xstr("reload_failed_sec")
5897 else:
5898 secondary = xstr("login_entranceExam_invalid_sec"
5899 if self.entranceExam else
5900 xstr("login_invalid_sec"))
5901 dialog.format_secondary_markup(secondary)
5902 dialog.run()
5903 dialog.hide()
5904 else:
5905 message = xstr("reload_failconn") if isReload \
5906 else xstr("login_failconn")
5907 dialog = Gtk.MessageDialog(parent = self.gui.mainWindow,
5908 type = Gtk.MessageType.ERROR,
5909 message_format = message)
5910 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
5911 dialog.set_title(WINDOW_TITLE_BASE)
5912 secondary = xstr("reload_failconn_sec") if isReload \
5913 else xstr("login_failconn_sec")
5914 dialog.format_secondary_markup(secondary)
5915
5916 dialog.run()
5917 dialog.hide()
5918
5919 callback = self._loginCallback
5920 self._loginCallback = None
5921 callback(returned, result)
5922
5923 def getFleet(self, callback, force = False):
5924 """Get the fleet via the GUI and call the given callback."""
5925 self._fleetCallback = callback
5926 self.gui.getFleet(callback = self._fleetRetrieved, force = force)
5927
5928 def _fleetRetrieved(self, fleet):
5929 """Callback for the fleet retrieval."""
5930 self._fleet = fleet
5931 if self._fleetCallback is not None:
5932 self._fleetCallback(fleet)
5933 self._fleetCallback = None
5934
5935 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
5936 """Update the given plane's gate information."""
5937 self.gui.updatePlane(tailNumber, status, gateNumber = gateNumber,
5938 callback = callback)
5939
5940 def updateRTO(self):
5941 """Update the RTO state.
5942
5943 The RTO checkbox will be enabled if the flight has an RTO state and the
5944 comments field contains some text."""
5945 flight = self.gui.flight
5946 rtoEnabled = flight is not None and flight.hasRTO and \
5947 self.gui.hasComments
5948 self._takeoffPage.setRTOEnabled(rtoEnabled)
5949
5950 def commentsChanged(self):
5951 """Called when the comments have changed."""
5952 self.updateRTO()
5953 self._finishPage.updateButtons()
5954
5955 def delayCodesChanged(self):
5956 """Called when the delay codes have changed."""
5957 self._finishPage.updateButtons()
5958
5959 def faultExplanationsChanged(self):
5960 """Called when the faults and their explanations have changed."""
5961 self._finishPage.updateButtons()
5962
5963 def rtoToggled(self, indicated):
5964 """Called when the RTO indication has changed."""
5965 self.gui.rtoToggled(indicated)
5966
5967 def _connectSimulator(self, simulatorType):
5968 """Connect to the simulator."""
5969 self.gui.connectSimulator(self._bookedFlight, simulatorType)
5970
5971 def _arrivalMETARCallback(self, returned, result):
5972 """Called when the METAR of the arrival airport is retrieved."""
5973 GObject.idle_add(self._handleArrivalMETAR, returned, result)
5974
5975 def _handleArrivalMETAR(self, returned, result):
5976 """Called when the METAR of the arrival airport is retrieved."""
5977 icao = self._bookedFlight.arrivalICAO
5978 if returned and icao in result.metars:
5979 metar = result.metars[icao]
5980 if metar!="":
5981 self._arrivalBriefingPage.setMETAR(metar)
5982
5983 def _getIndexOf(self, pageID):
5984 """Get the index for the given page ID.
5985
5986 It is an assertion failure if the ID is not found."""
5987 for index in range(0, len(self._pages)):
5988 page = self._pages[index]
5989 if page.id==pageID:
5990 return index
5991 assert False
5992
5993#-----------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.