source: src/mlx/gui/flight.py@ 1044:988bfea3e8db

python3
Last change on this file since 1044:988bfea3e8db was 1044:988bfea3e8db, checked in by István Váradi <ivaradi@…>, 8 months ago

RPC is the only communication method (re #357)

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