source: src/mlx/gui/flight.py@ 1097:f2ac841cffec

python3
Last change on this file since 1097:f2ac841cffec was 1097:f2ac841cffec, checked in by István Váradi <ivaradi@…>, 10 months ago

The browser cache can be cleared from the menu (re #368)

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