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

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

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

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