source: src/mlx/gui/flight.py@ 1115:be19ae8ee7bd

python3
Last change on this file since 1115:be19ae8ee7bd was 1110:67a5ba8a8664, checked in by István Váradi <ivaradi@…>, 14 months ago

The arrival METAR is changed on landing even when SimBrief is used

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