source: src/mlx/gui/flight.py@ 1008:5a311694b5cb

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

If the number of fuel tanks is less than 5, the old-style, side-by-side display is used (re #347).

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