source: src/mlx/gui/flight.py@ 999:e096a5638b87

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

Removed Gtk 2/3 constant definitions (re #347)

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