source: src/mlx/gui/flight.py@ 1070:6df48aba546b

python3
Last change on this file since 1070:6df48aba546b was 1055:e55d18232dcf, checked in by István Váradi <ivaradi@…>, 23 months ago

All booked flights are displayed, but those that are too much in the future cannot be started

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