source: src/mlx/gui/flight.py@ 1006:9a97fc27f9d8

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

Fonts are not scaled with the DPI when producing the briefing (re #347).

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