source: src/mlx/gui/flight.py@ 302:a4e4cda5d89a

Last change on this file since 302:a4e4cda5d89a was 300:f101bd18f39d, checked in by István Váradi <ivaradi@…>, 12 years ago

Added the module comments for the GUI

File size: 118.2 KB
Line 
1
2from mlx.gui.common import *
3
4import mlx.const as const
5import mlx.fs as fs
6import mlx.acft as acft
7from mlx.checks import PayloadChecker
8import mlx.util as util
9from mlx.pirep import PIREP
10from mlx.i18n import xstr
11from mlx.sound import startSound
12import mlx.web as web
13
14import datetime
15import time
16
17#-----------------------------------------------------------------------------
18
19## @package mlx.gui.flight
20#
21# The flight "wizard".
22#
23# This module implements the main tab of the application, the flight
24# wizard. The wizard consists of \ref Page "pages", that come one after the
25# other. As some pages might be skipped, the pages dynamically store the index
26# of the previous page so that going back to it is simpler. The \ref
27# Page.activate "activate" function is called before a page is first shown
28# during a flight. This function should initialize the page's controls and fill
29# it with initial data. When a page is left for the first time, its \ref
30# Page.finalize "finalize" function is called. It should set those controls
31# insensitive, that will not be available if the user comes back to this page.
32#
33# Each page has a title at the top displayed in inverted colors and a big
34# font. There is a help text below it centered, that shortly describes what is
35# expected on the page. There can be two help texts: one shown when the page is
36# first displayed during a flight, another shown when the user goes back to the
37# page. The main content area is below this, also centered. Finally, there are
38# some buttons at the bottom on the right. As some buttons occur very
39# frequently, there are functions to add them (\ref Page.addCancelFlightButton
40# "addCancelFlightButton", \ref Page.addPreviousButton "addPreviousButton" and
41# \ref Page.addNextButton "addNextButton".
42#
43# The \ref Wizard class is the main class to collect the pages. It also stores
44# some data passed from one page to another and provides properties to access
45# data items set via the wizard pages.
46
47#-----------------------------------------------------------------------------
48
49class Page(gtk.Alignment):
50 """A page in the flight wizard."""
51 def __init__(self, wizard, title, help, completedHelp = None):
52 """Construct the page."""
53 super(Page, self).__init__(xalign = 0.0, yalign = 0.0,
54 xscale = 1.0, yscale = 1.0)
55 self.set_padding(padding_top = 4, padding_bottom = 4,
56 padding_left = 12, padding_right = 12)
57
58 frame = gtk.Frame()
59 self.add(frame)
60
61 self._vbox = gtk.VBox()
62 self._vbox.set_homogeneous(False)
63 frame.add(self._vbox)
64
65 eventBox = gtk.EventBox()
66
67 alignment = gtk.Alignment(xalign = 0.0, xscale = 0.0)
68
69 titleLabel = gtk.Label(title)
70 titleLabel.modify_font(pango.FontDescription("bold 24"))
71 alignment.set_padding(padding_top = 4, padding_bottom = 4,
72 padding_left = 6, padding_right = 0)
73
74 alignment.add(titleLabel)
75 eventBox.add(alignment)
76
77 self._vbox.pack_start(eventBox, False, False, 0)
78
79 self._titleEventBox = eventBox
80 self._titleLabel = titleLabel
81
82 mainBox = gtk.VBox()
83
84 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
85 xscale = 1.0, yscale = 1.0)
86 alignment.set_padding(padding_top = 16, padding_bottom = 16,
87 padding_left = 16, padding_right = 16)
88 alignment.add(mainBox)
89 self._vbox.pack_start(alignment, True, True, 0)
90
91 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
92 xscale = 0.0, yscale = 0.0)
93 alignment.set_padding(padding_top = 0, padding_bottom = 16,
94 padding_left = 0, padding_right = 0)
95
96 self._help = help
97 self._completedHelp = completedHelp
98
99 if self._completedHelp is None or \
100 len(help.splitlines())>=len(completedHelp.splitlines()):
101 longerHelp = help
102 else:
103 longerHelp = completedHelp
104
105 self._helpLabel = gtk.Label(completedHelp)
106 # FIXME: should be a constant in common
107 self._helpLabel.set_justify(gtk.Justification.CENTER if pygobject
108 else gtk.JUSTIFY_CENTER)
109 self._helpLabel.set_use_markup(True)
110 alignment.add(self._helpLabel)
111 mainBox.pack_start(alignment, False, False, 0)
112
113 self._mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
114 xscale = 1.0, yscale = 1.0)
115 mainBox.pack_start(self._mainAlignment, True, True, 0)
116
117 buttonAlignment = gtk.Alignment(xalign = 1.0, xscale=0.0, yscale = 0.0)
118 buttonAlignment.set_padding(padding_top = 4, padding_bottom = 10,
119 padding_left = 16, padding_right = 16)
120
121 self._buttonBox = gtk.HBox()
122 self._buttonBox.set_homogeneous(False)
123 self._defaultButton = None
124 buttonAlignment.add(self._buttonBox)
125
126 self._vbox.pack_start(buttonAlignment, False, False, 0)
127
128 self._wizard = wizard
129
130 self._cancelFlightButton = None
131
132 self._completed = False
133 self._fromPage = None
134
135 def setMainWidget(self, widget):
136 """Set the given widget as the main one."""
137 self._mainAlignment.add(widget)
138
139 def addButton(self, label, default = False, sensitive = True,
140 tooltip = None, clicked = None, padding = 4):
141 """Add a button with the given label.
142
143 Return the button object created."""
144 button = gtk.Button(label)
145 self._buttonBox.pack_start(button, False, False, padding)
146 button.set_use_underline(True)
147 if default:
148 button.set_can_default(True)
149 self._defaultButton = button
150 button.set_sensitive(sensitive)
151 if tooltip is not None:
152 button.set_tooltip_text(tooltip)
153 if clicked is not None:
154 button.connect("clicked", clicked)
155 return button
156
157 def addCancelFlightButton(self):
158 """Add the 'Cancel flight' button to the page."""
159 self._cancelFlightButton = \
160 self.addButton(xstr("button_cancelFlight"),
161 sensitive = True,
162 tooltip = xstr("button_cancelFlight_tooltip"),
163 clicked = self._cancelFlight,
164 padding = 16)
165 return self._cancelFlightButton
166
167 def addPreviousButton(self, sensitive = True, clicked = None):
168 """Add the 'Next' button to the page."""
169 return self.addButton(xstr("button_previous"),
170 sensitive = sensitive,
171 tooltip = xstr("button_previous_tooltip"),
172 clicked = clicked)
173
174 def addNextButton(self, default = True, sensitive = True,
175 clicked = None):
176 """Add the 'Next' button to the page."""
177 return self.addButton(xstr("button_next"),
178 default = default,
179 sensitive = sensitive,
180 tooltip = xstr("button_next_tooltip"),
181 clicked = clicked)
182
183 def setStyle(self):
184 """Set the styles of some of the items on the page."""
185 style = self.get_style() if pygobject else self.rc_get_style()
186
187 self._titleEventBox.modify_bg(0, style.bg[3])
188 self._titleLabel.modify_fg(0, style.fg[3])
189
190 def initialize(self):
191 """Initialize the page.
192
193 It sets up the primary help, and calls the activate() function."""
194 self._helpLabel.set_markup(self._help)
195 self._helpLabel.set_sensitive(True)
196 self.activate()
197
198 def activate(self):
199 """Called when this page becomes active.
200
201 This default implementation does nothing."""
202 pass
203
204 def complete(self):
205 """Called when the page is completed.
206
207 It greys out/changes the help text and then calls finalize()."""
208 self.finalize()
209 if self._completedHelp is None:
210 self._helpLabel.set_sensitive(False)
211 else:
212 self._helpLabel.set_markup(self._completedHelp)
213 self._completed = True
214
215 def finalize(self):
216 """Called when the page is finalized."""
217 pass
218
219 def grabDefault(self):
220 """If the page has a default button, make it the default one."""
221 if self._defaultButton is not None:
222 self._defaultButton.grab_default()
223
224 def reset(self):
225 """Reset the page if the wizard is reset."""
226 self._completed = False
227 self._fromPage = None
228 if self._cancelFlightButton is not None:
229 self._cancelFlightButton.set_sensitive(True)
230
231 def goBack(self):
232 """Go to the page we were invoked from."""
233 assert self._fromPage is not None
234
235 self._wizard.setCurrentPage(self._fromPage, finalize = False)
236
237 def flightEnded(self):
238 """Called when the flight has ended.
239
240 This default implementation disables the cancel flight button."""
241 if self._cancelFlightButton is not None:
242 self._cancelFlightButton.set_sensitive(False)
243
244 def _cancelFlight(self, button):
245 """Called when the Cancel flight button is clicked."""
246 self._wizard.gui.cancelFlight()
247
248#-----------------------------------------------------------------------------
249
250class LoginPage(Page):
251 """The login page."""
252 def __init__(self, wizard):
253 """Construct the login page."""
254 super(LoginPage, self).__init__(wizard, xstr("login"),
255 xstr("loginHelp"))
256
257 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
258 xscale = 0.0, yscale = 0.0)
259
260 table = gtk.Table(4, 2)
261 table.set_row_spacings(4)
262 table.set_col_spacings(32)
263 alignment.add(table)
264 self.setMainWidget(alignment)
265
266 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
267 xscale = 0.0, yscale = 0.0)
268 label = gtk.Label(xstr("label_pilotID"))
269 label.set_use_underline(True)
270 labelAlignment.add(label)
271 table.attach(labelAlignment, 0, 1, 0, 1)
272
273 self._pilotID = gtk.Entry()
274 self._pilotID.connect("changed", self._setControls)
275 self._pilotID.set_tooltip_text(xstr("login_pilotID_tooltip"))
276 table.attach(self._pilotID, 1, 2, 0, 1)
277 label.set_mnemonic_widget(self._pilotID)
278
279 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
280 xscale = 0.0, yscale = 0.0)
281 label = gtk.Label(xstr("label_password"))
282 label.set_use_underline(True)
283 labelAlignment.add(label)
284 table.attach(labelAlignment, 0, 1, 1, 2)
285
286 self._password = gtk.Entry()
287 self._password.set_visibility(False)
288 self._password.connect("changed", self._setControls)
289 self._password.set_tooltip_text(xstr("login_password_tooltip"))
290 table.attach(self._password, 1, 2, 1, 2)
291 label.set_mnemonic_widget(self._password)
292
293 self._rememberButton = gtk.CheckButton(xstr("remember_password"))
294 self._rememberButton.set_use_underline(True)
295 self._rememberButton.set_tooltip_text(xstr("login_remember_tooltip"))
296 table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
297
298 self._entranceExam = gtk.CheckButton(xstr("login_entranceExam"))
299 self._entranceExam.set_use_underline(True)
300 self._entranceExam.set_tooltip_text(xstr("login_entranceExam_tooltip"))
301 self._entranceExam.connect("toggled", self._setControls)
302 table.attach(self._entranceExam, 1, 2, 3, 4, ypadding = 12)
303
304 self.addButton(xstr("button_offline"),
305 clicked = self._offlineClicked,
306 tooltip = xstr("button_offline_tooltip"))
307
308 self._loginButton = self.addButton(xstr("button_login"), default = True)
309 self._loginButton.connect("clicked", self._loginClicked)
310 self._loginButton.set_tooltip_text(xstr("login_button_tooltip"))
311
312
313 @property
314 def entranceExam(self):
315 """Get whether an entrance exam is being performed."""
316 return self._entranceExam.get_active() and \
317 self._pilotID.get_text()!=""
318
319 @property
320 def pilotID(self):
321 """Get the pilot ID, if given."""
322 return self._pilotID.get_text()
323
324 def activate(self):
325 """Activate the page."""
326 config = self._wizard.gui.config
327 self._pilotID.set_text(config.pilotID)
328 self._password.set_text(config.password)
329 self._rememberButton.set_active(config.rememberPassword)
330 self._setControls(None)
331
332 def _setControls(self, entry = None):
333 """Set the sensitivity of the various controls.
334
335 The login button is sensitive only if both the pilot ID and the
336 password fields contain values.
337
338 The password field is sensitive only, if the entrance exam checkbox is
339 not selected.
340
341 The remember password checkbox is sensitive only, if the password field
342 contains text.
343
344 The entrance exam checkbox is sensitive only, if the pilot ID is not
345 empty."""
346 pilotID = self._pilotID.get_text()
347 password = self._password.get_text()
348 entranceExam = self._entranceExam.get_active()
349 self._password.set_sensitive(not entranceExam)
350 self._rememberButton.set_sensitive(password!="" and not entranceExam)
351 self._entranceExam.set_sensitive(pilotID!="")
352 self._loginButton.set_sensitive(pilotID!="" and
353 (password!="" or entranceExam))
354
355 def _offlineClicked(self, button):
356 """Called when the offline button was clicked."""
357 self._wizard.nextPage()
358
359 def _loginClicked(self, button):
360 """Called when the login button was clicked."""
361 self._wizard.login(self._handleLoginResult,
362 self._pilotID.get_text(),
363 self._password.get_text(),
364 self.entranceExam)
365
366 def _handleLoginResult(self, returned, result):
367 """Handle the login result."""
368 self._loginButton.set_sensitive(True)
369 if returned and result.loggedIn:
370 config = self._wizard.gui.config
371
372 config.pilotID = self._pilotID.get_text()
373
374 rememberPassword = self._rememberButton.get_active()
375 config.password = result.password if rememberPassword else ""
376
377 config.rememberPassword = rememberPassword
378
379 config.save()
380 self._wizard.nextPage()
381
382#-----------------------------------------------------------------------------
383
384class FlightSelectionPage(Page):
385 """The page to select the flight."""
386 def __init__(self, wizard):
387 """Construct the flight selection page."""
388 help = xstr("flightsel_help")
389 completedHelp = xstr("flightsel_chelp")
390 super(FlightSelectionPage, self).__init__(wizard, xstr("flightsel_title"),
391 help, completedHelp = completedHelp)
392
393
394 self._listStore = gtk.ListStore(str, str, str, str)
395 self._flightList = gtk.TreeView(self._listStore)
396 column = gtk.TreeViewColumn(xstr("flightsel_no"), gtk.CellRendererText(),
397 text = 1)
398 column.set_expand(True)
399 self._flightList.append_column(column)
400 column = gtk.TreeViewColumn(xstr("flightsel_deptime"), gtk.CellRendererText(),
401 text = 0)
402 column.set_expand(True)
403 self._flightList.append_column(column)
404 column = gtk.TreeViewColumn(xstr("flightsel_from"), gtk.CellRendererText(),
405 text = 2)
406 column.set_expand(True)
407 self._flightList.append_column(column)
408 column = gtk.TreeViewColumn(xstr("flightsel_to"), gtk.CellRendererText(),
409 text = 3)
410 column.set_expand(True)
411 self._flightList.append_column(column)
412 self._flightList.connect("row-activated", self._rowActivated)
413 self._flightList.connect("button-press-event", self._listButtonPressed)
414
415 self._flightListPopupMenu = None
416
417 flightSelection = self._flightList.get_selection()
418 flightSelection.connect("changed", self._selectionChanged)
419
420 scrolledWindow = gtk.ScrolledWindow()
421 scrolledWindow.add(self._flightList)
422 scrolledWindow.set_size_request(400, -1)
423 # FIXME: these should be constants in common.py
424 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
425 else gtk.POLICY_AUTOMATIC,
426 gtk.PolicyType.AUTOMATIC if pygobject
427 else gtk.POLICY_AUTOMATIC)
428 scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
429 else gtk.SHADOW_IN)
430
431 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 0.0, yscale = 1.0)
432 alignment.add(scrolledWindow)
433
434 self.setMainWidget(alignment)
435
436 self._saveButton = self.addButton(xstr("flightsel_save"),
437 sensitive = False,
438 clicked = self._saveClicked,
439 tooltip = xstr("flightsel_save_tooltip"))
440 self._saveDialog = None
441
442 self._refreshButton = self.addButton(xstr("flightsel_refresh"),
443 sensitive = True,
444 clicked = self._refreshClicked,
445 tooltip = xstr("flightsel_refresh_tooltip"))
446
447 self._loadButton = self.addButton(xstr("flightsel_load"),
448 sensitive = True,
449 tooltip = xstr("flightsel_load_tooltip"))
450 self._loadButton.connect("clicked", self._loadButtonClicked)
451 self._loadDialog = None
452
453 self._button = self.addNextButton(sensitive = False,
454 clicked = self._forwardClicked)
455
456 self._flights = []
457
458 def activate(self):
459 """Fill the flight list."""
460 self._flightList.set_sensitive(True)
461 self._loadButton.set_sensitive(True)
462 self._refreshButton.set_sensitive(self._wizard.loggedIn)
463 self._buildFlights()
464
465 def finalize(self):
466 """Finalize the page."""
467 self._flightList.set_sensitive(False)
468 self._loadButton.set_sensitive(False)
469 self._refreshButton.set_sensitive(False)
470
471 def _buildFlights(self):
472 """Rebuild the flights from the login result."""
473 self._flights = []
474 self._listStore.clear()
475 if self._wizard.loggedIn:
476 for flight in self._wizard.loginResult.flights:
477 self._addFlight(flight)
478
479 def _addFlight(self, flight):
480 """Add the given file to the list of flights."""
481 self._flights.append(flight)
482 self._listStore.append([str(flight.departureTime),
483 flight.callsign,
484 flight.departureICAO,
485 flight.arrivalICAO])
486
487 def _saveClicked(self, button):
488 """Called when the Save flight button is clicked."""
489 self._saveSelected()
490
491 def _saveSelected(self):
492 """Save the selected flight."""
493 flight = self._getSelectedFlight()
494 date = flight.departureTime.date()
495 name = "%04d-%02d-%02d %s %s-%s.vaflight" % \
496 (date.year, date.month, date.day, flight.callsign,
497 flight.departureICAO, flight.arrivalICAO)
498
499 dialog = self._getSaveDialog()
500 dialog.set_current_name(name)
501 dialog.show_all()
502 response = dialog.run()
503 dialog.hide()
504
505 if response==RESPONSETYPE_OK:
506 fileName = text2unicode(dialog.get_filename())
507 print "Saving", fileName
508 try:
509 with open(fileName, "wt") as f:
510 flight.writeIntoFile(f)
511 except Exception, e:
512 print "Failed to save flight:", str(e)
513 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
514 type = MESSAGETYPE_ERROR,
515 message_format =
516 xstr("flightsel_save_failed"))
517 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
518 dialog.set_title(WINDOW_TITLE_BASE)
519 secondary = xstr("flightsel_save_failed_sec")
520 dialog.format_secondary_markup(secondary)
521 dialog.run()
522 dialog.hide()
523
524 def _refreshClicked(self, button):
525 """Called when the refresh button is clicked."""
526 self._wizard.reloadFlights(self._refreshCallback)
527
528 def _refreshCallback(self, returned, result):
529 """Callback for the refresh."""
530 if returned and result.loggedIn:
531 self._buildFlights()
532
533 def _selectionChanged(self, selection):
534 """Called when the selection is changed."""
535 selected = selection.count_selected_rows()==1
536 self._saveButton.set_sensitive(selected)
537 self._button.set_sensitive(selected)
538
539 def _loadButtonClicked(self, loadButton):
540 """Called when the load a flight button is clicked."""
541 dialog = self._getLoadDialog()
542 dialog.show_all()
543 response = dialog.run()
544 dialog.hide()
545
546 if response==RESPONSETYPE_OK:
547 fileName = text2unicode(dialog.get_filename())
548 print "Loading", fileName
549 bookedFlight = web.BookedFlight()
550 try:
551 with open(fileName, "rt") as f:
552 bookedFlight.readFromFile(f)
553 self._addFlight(bookedFlight)
554 except Exception, e:
555 print "Failed to load flight:", str(e)
556 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
557 type = MESSAGETYPE_ERROR,
558 message_format =
559 xstr("flightsel_load_failed"))
560 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
561 dialog.set_title(WINDOW_TITLE_BASE)
562 secondary = xstr("flightsel_load_failed_sec")
563 dialog.format_secondary_markup(secondary)
564 dialog.run()
565 dialog.hide()
566
567 def _forwardClicked(self, button):
568 """Called when the forward button was clicked."""
569 if self._completed:
570 self._wizard.jumpPage(self._nextDistance, finalize = False)
571 else:
572 self._flightSelected()
573
574 def _rowActivated(self, flightList, path, column):
575 """Called when a row is activated."""
576 if not self._completed:
577 self._flightSelected()
578
579 def _flightSelected(self):
580 """Called when a flight has been selected."""
581 flight = self._getSelectedFlight()
582 self._wizard._bookedFlight = flight
583 self._wizard.gui.enableFlightInfo()
584
585 self._updateDepartureGate()
586
587 def _getSelectedFlight(self):
588 """Get the currently selected flight."""
589 selection = self._flightList.get_selection()
590 (listStore, iter) = selection.get_selected()
591 path = listStore.get_path(iter)
592 [index] = path.get_indices() if pygobject else path
593
594 return self._flights[index]
595
596 def _listButtonPressed(self, widget, event):
597 """Called when a mouse button is pressed on the flight list."""
598 if event.type!=EVENT_BUTTON_PRESS or event.button!=3:
599 return
600
601 (path, _, _, _) = self._flightList.get_path_at_pos(int(event.x),
602 int(event.y))
603 selection = self._flightList.get_selection()
604 selection.unselect_all()
605 selection.select_path(path)
606
607 menu = self._getListPopupMenu()
608 if pygobject:
609 menu.popup(None, None, None, None, event.button, event.time)
610 else:
611 menu.popup(None, None, None, event.button, event.time)
612
613 def _updateDepartureGate(self):
614 """Update the departure gate for the booked flight."""
615 flight = self._wizard._bookedFlight
616 if self._wizard.gui.config.onlineGateSystem and \
617 self._wizard.loggedIn and not self._wizard.entranceExam:
618 if flight.departureICAO=="LHBP":
619 self._wizard.getFleet(self._fleetRetrieved)
620 else:
621 self._wizard.updatePlane(self._planeUpdated,
622 flight.tailNumber,
623 const.PLANE_AWAY)
624 else:
625 self._nextDistance = 2
626 self._wizard.jumpPage(2)
627
628 def _fleetRetrieved(self, fleet):
629 """Called when the fleet has been retrieved."""
630 if fleet is None:
631 self._nextDistance = 2
632 self._wizard.jumpPage(2)
633 else:
634 plane = fleet[self._wizard._bookedFlight.tailNumber]
635 if plane is None:
636 self._nextDistance = 2
637 self._wizard.jumpPage(2)
638 elif plane.gateNumber is not None and \
639 not fleet.isGateConflicting(plane):
640 self._wizard._departureGate = plane.gateNumber
641 self._nextDistance = 2
642 self._wizard.jumpPage(2)
643 else:
644 self._nextDistance = 1
645 self._wizard.nextPage()
646
647 def _planeUpdated(self, success):
648 """Callback for the plane updating."""
649 self._nextDistance = 2
650 self._wizard.jumpPage(2)
651
652 def _getSaveDialog(self):
653 """Get the dialog to load a flight file."""
654 if self._saveDialog is not None:
655 return self._saveDialog
656
657 gui = self._wizard.gui
658 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
659 xstr("flightsel_save_title"),
660 action = FILE_CHOOSER_ACTION_SAVE,
661 buttons = (gtk.STOCK_CANCEL,
662 RESPONSETYPE_CANCEL,
663 gtk.STOCK_OK, RESPONSETYPE_OK),
664 parent = gui.mainWindow)
665 dialog.set_modal(True)
666 dialog.set_do_overwrite_confirmation(True)
667
668 filter = gtk.FileFilter()
669 filter.set_name(xstr("flightsel_filter_flights"))
670 filter.add_pattern("*.vaflight")
671 dialog.add_filter(filter)
672
673 filter = gtk.FileFilter()
674 filter.set_name(xstr("file_filter_all"))
675 filter.add_pattern("*.*")
676 dialog.add_filter(filter)
677
678 self._saveDialog = dialog
679
680 return dialog
681
682 def _getLoadDialog(self):
683 """Get the dialog to load a flight file."""
684 if self._loadDialog is not None:
685 return self._loadDialog
686
687 gui = self._wizard.gui
688 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
689 xstr("flightsel_load_title"),
690 action = FILE_CHOOSER_ACTION_OPEN,
691 buttons = (gtk.STOCK_CANCEL,
692 RESPONSETYPE_CANCEL,
693 gtk.STOCK_OK, RESPONSETYPE_OK),
694 parent = gui.mainWindow)
695 dialog.set_modal(True)
696
697 filter = gtk.FileFilter()
698 filter.set_name(xstr("flightsel_filter_flights"))
699 filter.add_pattern("*.vaflight")
700 dialog.add_filter(filter)
701
702 filter = gtk.FileFilter()
703 filter.set_name(xstr("file_filter_all"))
704 filter.add_pattern("*.*")
705 dialog.add_filter(filter)
706
707 self._loadDialog = dialog
708
709 return dialog
710
711 def _getListPopupMenu(self):
712 """Get the flight list popup menu."""
713 if self._flightListPopupMenu is None:
714 menu = gtk.Menu()
715
716 menuItem = gtk.MenuItem()
717 menuItem.set_label(xstr("flightsel_popup_select"))
718 menuItem.set_use_underline(True)
719 menuItem.connect("activate", self._popupSelect)
720 menuItem.show()
721
722 menu.append(menuItem)
723
724 menuItem = gtk.MenuItem()
725 menuItem.set_label(xstr("flightsel_popup_save"))
726 menuItem.set_use_underline(True)
727 menuItem.connect("activate", self._popupSave)
728 menuItem.show()
729
730 menu.append(menuItem)
731
732 self._flightListPopupMenu = menu
733
734 return self._flightListPopupMenu
735
736 def _popupSelect(self, menuItem):
737 """Called when the Select menu item is activated in the popup menu."""
738 if not self._completed:
739 self._flightSelected()
740
741 def _popupSave(self, menuItem):
742 """Called when the Save menu item is activated in the popup menu."""
743 if not self._completed:
744 self._saveSelected()
745
746#-----------------------------------------------------------------------------
747
748class GateSelectionPage(Page):
749 """Page to select a free gate at LHBP.
750 This page should be displayed only if we have fleet information!."""
751 def __init__(self, wizard):
752 """Construct the gate selection page."""
753 super(GateSelectionPage, self).__init__(wizard, xstr("gatesel_title"),
754 xstr("gatesel_help"))
755
756 self._listStore = gtk.ListStore(str)
757 self._gateList = gtk.TreeView(self._listStore)
758 column = gtk.TreeViewColumn(None, gtk.CellRendererText(),
759 text = 0)
760 column.set_expand(True)
761 self._gateList.append_column(column)
762 self._gateList.set_headers_visible(False)
763 self._gateList.connect("row-activated", self._rowActivated)
764
765 gateSelection = self._gateList.get_selection()
766 gateSelection.connect("changed", self._selectionChanged)
767
768 scrolledWindow = gtk.ScrolledWindow()
769 scrolledWindow.add(self._gateList)
770 scrolledWindow.set_size_request(50, -1)
771 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
772 else gtk.POLICY_AUTOMATIC,
773 gtk.PolicyType.AUTOMATIC if pygobject
774 else gtk.POLICY_AUTOMATIC)
775 scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
776 else gtk.SHADOW_IN)
777
778 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 0.0, yscale = 1.0)
779 alignment.add(scrolledWindow)
780
781 self.setMainWidget(alignment)
782
783 self.addCancelFlightButton()
784
785 self.addPreviousButton(clicked = self._backClicked)
786
787 self._button = self.addNextButton(sensitive = False,
788 clicked = self._forwardClicked)
789
790 def activate(self):
791 """Fill the gate list."""
792 self._listStore.clear()
793 self._gateList.set_sensitive(True)
794 occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
795 for gateNumber in const.lhbpGateNumbers:
796 if gateNumber not in occupiedGateNumbers:
797 self._listStore.append([gateNumber])
798
799 def finalize(self):
800 """Finalize the page."""
801 self._gateList.set_sensitive(False)
802
803 def _selectionChanged(self, selection):
804 """Called when the selection is changed."""
805 self._button.set_sensitive(selection.count_selected_rows()==1)
806
807 def _backClicked(self, button):
808 """Called when the Back button is pressed."""
809 self.goBack()
810
811 def _forwardClicked(self, button):
812 """Called when the forward button is clicked."""
813 if not self._completed:
814 self._gateSelected()
815 else:
816 self._wizard.nextPage()
817
818 def _rowActivated(self, flightList, path, column):
819 """Called when a row is activated."""
820 if not self._completed:
821 self._gateSelected()
822
823 def _gateSelected(self):
824 """Called when a gate has been selected."""
825 selection = self._gateList.get_selection()
826 (listStore, iter) = selection.get_selected()
827 (gateNumber,) = listStore.get(iter, 0)
828
829 self._wizard._departureGate = gateNumber
830
831 self._wizard.updatePlane(self._planeUpdated,
832 self._wizard._bookedFlight.tailNumber,
833 const.PLANE_HOME, gateNumber)
834
835 def _planeUpdated(self, success):
836 """Callback for the plane updating call."""
837 if success is None or success:
838 self._wizard.nextPage()
839 else:
840 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
841 type = MESSAGETYPE_ERROR,
842 message_format = xstr("gatesel_conflict"))
843 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
844 dialog.set_title(WINDOW_TITLE_BASE)
845 dialog.format_secondary_markup(xstr("gatesel_conflict_sec"))
846 dialog.run()
847 dialog.hide()
848
849 self._wizard.getFleet(self._fleetRetrieved)
850
851 def _fleetRetrieved(self, fleet):
852 """Called when the fleet has been retrieved."""
853 if fleet is None:
854 self._wizard.nextPage()
855 else:
856 self.activate()
857
858#-----------------------------------------------------------------------------
859
860class ConnectPage(Page):
861 """Page which displays the departure airport and gate (if at LHBP)."""
862 def __init__(self, wizard):
863 """Construct the connect page."""
864 help = "Load the aircraft below into the simulator and park it\n" \
865 "at the given airport, at the gate below, if present.\n\n" \
866 "Then press the Connect button to connect to the simulator."
867 completedHelp = "The basic data of your flight can be read below."
868 super(ConnectPage, self).__init__(wizard, xstr("connect_title"),
869 xstr("connect_help"),
870 completedHelp = xstr("connect_chelp"))
871
872 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
873 xscale = 0.0, yscale = 0.0)
874
875 table = gtk.Table(5, 2)
876 table.set_row_spacings(4)
877 table.set_col_spacings(16)
878 table.set_homogeneous(True)
879 alignment.add(table)
880 self.setMainWidget(alignment)
881
882 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
883 label = gtk.Label(xstr("connect_flightno"))
884 labelAlignment.add(label)
885 table.attach(labelAlignment, 0, 1, 0, 1)
886
887 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
888 self._flightNumber = gtk.Label()
889 self._flightNumber.set_width_chars(9)
890 self._flightNumber.set_alignment(0.0, 0.5)
891 labelAlignment.add(self._flightNumber)
892 table.attach(labelAlignment, 1, 2, 0, 1)
893
894 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
895 label = gtk.Label(xstr("connect_acft"))
896 labelAlignment.add(label)
897 table.attach(labelAlignment, 0, 1, 1, 2)
898
899 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
900 self._aircraft = gtk.Label()
901 self._aircraft.set_width_chars(25)
902 self._aircraft.set_alignment(0.0, 0.5)
903 labelAlignment.add(self._aircraft)
904 table.attach(labelAlignment, 1, 2, 1, 2)
905
906 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
907 label = gtk.Label(xstr("connect_tailno"))
908 labelAlignment.add(label)
909 table.attach(labelAlignment, 0, 1, 2, 3)
910
911 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
912 self._tailNumber = gtk.Label()
913 self._tailNumber.set_width_chars(10)
914 self._tailNumber.set_alignment(0.0, 0.5)
915 labelAlignment.add(self._tailNumber)
916 table.attach(labelAlignment, 1, 2, 2, 3)
917
918 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
919 label = gtk.Label(xstr("connect_airport"))
920 labelAlignment.add(label)
921 table.attach(labelAlignment, 0, 1, 3, 4)
922
923 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
924 self._departureICAO = gtk.Label()
925 self._departureICAO.set_width_chars(6)
926 self._departureICAO.set_alignment(0.0, 0.5)
927 labelAlignment.add(self._departureICAO)
928 table.attach(labelAlignment, 1, 2, 3, 4)
929
930 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
931 label = gtk.Label(xstr("connect_gate"))
932 labelAlignment.add(label)
933 table.attach(labelAlignment, 0, 1, 4, 5)
934
935 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
936 self._departureGate = gtk.Label()
937 self._departureGate.set_width_chars(5)
938 self._departureGate.set_alignment(0.0, 0.5)
939 labelAlignment.add(self._departureGate)
940 table.attach(labelAlignment, 1, 2, 4, 5)
941
942 self.addCancelFlightButton()
943
944 self.addPreviousButton(clicked = self._backClicked)
945
946 self._button = self.addButton(xstr("button_connect"), default = True,
947 tooltip = xstr("button_connect_tooltip"))
948 self._clickedID = self._button.connect("clicked", self._connectClicked)
949
950 def activate(self):
951 """Setup the departure information."""
952 self._button.set_label(xstr("button_connect"))
953 self._button.set_use_underline(True)
954 self._button.set_tooltip_text(xstr("button_connect_tooltip"))
955 self._button.disconnect(self._clickedID)
956 self._clickedID = self._button.connect("clicked", self._connectClicked)
957
958 bookedFlight = self._wizard._bookedFlight
959
960 self._flightNumber.set_markup("<b>" + bookedFlight.callsign + "</b>")
961
962 aircraftType = aircraftNames[bookedFlight.aircraftType]
963 self._aircraft.set_markup("<b>" + aircraftType + "</b>")
964
965 self._tailNumber.set_markup("<b>" + bookedFlight.tailNumber + "</b>")
966
967 icao = bookedFlight.departureICAO
968 self._departureICAO.set_markup("<b>" + icao + "</b>")
969 gate = self._wizard._departureGate
970 if gate!="-":
971 gate = "<b>" + gate + "</b>"
972 self._departureGate.set_markup(gate)
973
974 def finalize(self):
975 """Finalize the page."""
976 self._button.set_label(xstr("button_next"))
977 self._button.set_use_underline(True)
978 self._button.set_tooltip_text(xstr("button_next_tooltip"))
979 self._button.disconnect(self._clickedID)
980 self._clickedID = self._button.connect("clicked", self._forwardClicked)
981
982 def _backClicked(self, button):
983 """Called when the Back button is pressed."""
984 self.goBack()
985
986 def _connectClicked(self, button):
987 """Called when the Connect button is pressed."""
988 self._wizard._connectSimulator()
989
990 def _forwardClicked(self, button):
991 """Called when the Forward button is pressed."""
992 self._wizard.nextPage()
993
994#-----------------------------------------------------------------------------
995
996class PayloadPage(Page):
997 """Page to allow setting up the payload."""
998 def __init__(self, wizard):
999 """Construct the page."""
1000 super(PayloadPage, self).__init__(wizard, xstr("payload_title"),
1001 xstr("payload_help"),
1002 completedHelp = xstr("payload_chelp"))
1003
1004 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1005 xscale = 0.0, yscale = 0.0)
1006
1007 table = gtk.Table(7, 3)
1008 table.set_row_spacings(4)
1009 table.set_col_spacings(16)
1010 table.set_homogeneous(False)
1011 alignment.add(table)
1012 self.setMainWidget(alignment)
1013
1014 label = gtk.Label(xstr("payload_crew"))
1015 label.set_alignment(0.0, 0.5)
1016 table.attach(label, 0, 1, 0, 1)
1017
1018 self._numCrew = gtk.Label()
1019 self._numCrew.set_width_chars(6)
1020 self._numCrew.set_alignment(1.0, 0.5)
1021 table.attach(self._numCrew, 1, 2, 0, 1)
1022
1023 label = gtk.Label(xstr("payload_pax"))
1024 label.set_alignment(0.0, 0.5)
1025 table.attach(label, 0, 1, 1, 2)
1026
1027 self._numPassengers = gtk.Label()
1028 self._numPassengers.set_width_chars(6)
1029 self._numPassengers.set_alignment(1.0, 0.5)
1030 table.attach(self._numPassengers, 1, 2, 1, 2)
1031
1032 label = gtk.Label(xstr("payload_bag"))
1033 label.set_alignment(0.0, 0.5)
1034 table.attach(label, 0, 1, 2, 3)
1035
1036 self._bagWeight = gtk.Label()
1037 self._bagWeight.set_width_chars(6)
1038 self._bagWeight.set_alignment(1.0, 0.5)
1039 table.attach(self._bagWeight, 1, 2, 2, 3)
1040
1041 table.attach(gtk.Label("kg"), 2, 3, 2, 3)
1042
1043 label = gtk.Label(xstr("payload_cargo"))
1044 label.set_use_underline(True)
1045 label.set_alignment(0.0, 0.5)
1046 table.attach(label, 0, 1, 3, 4)
1047
1048 self._cargoWeight = IntegerEntry(defaultValue = 0)
1049 self._cargoWeight.set_width_chars(6)
1050 self._cargoWeight.connect("integer-changed", self._cargoWeightChanged)
1051 self._cargoWeight.set_tooltip_text(xstr("payload_cargo_tooltip"))
1052 table.attach(self._cargoWeight, 1, 2, 3, 4)
1053 label.set_mnemonic_widget(self._cargoWeight)
1054
1055 table.attach(gtk.Label("kg"), 2, 3, 3, 4)
1056
1057 label = gtk.Label(xstr("payload_mail"))
1058 label.set_alignment(0.0, 0.5)
1059 table.attach(label, 0, 1, 4, 5)
1060
1061 self._mailWeight = gtk.Label()
1062 self._mailWeight.set_width_chars(6)
1063 self._mailWeight.set_alignment(1.0, 0.5)
1064 table.attach(self._mailWeight, 1, 2, 4, 5)
1065
1066 table.attach(gtk.Label("kg"), 2, 3, 4, 5)
1067
1068 label = gtk.Label("<b>" + xstr("payload_zfw") + "</b>")
1069 label.set_alignment(0.0, 0.5)
1070 label.set_use_markup(True)
1071 table.attach(label, 0, 1, 5, 6)
1072
1073 self._calculatedZFW = gtk.Label()
1074 self._calculatedZFW.set_width_chars(6)
1075 self._calculatedZFW.set_alignment(1.0, 0.5)
1076 table.attach(self._calculatedZFW, 1, 2, 5, 6)
1077
1078 table.attach(gtk.Label("kg"), 2, 3, 5, 6)
1079
1080 self._zfwButton = gtk.Button(xstr("payload_fszfw"))
1081 self._zfwButton.set_use_underline(True)
1082 self._zfwButton.connect("clicked", self._zfwRequested)
1083 self._zfwButton.set_tooltip_text(xstr("payload_fszfw_tooltip"))
1084 table.attach(self._zfwButton, 0, 1, 6, 7)
1085
1086 self._simulatorZFW = gtk.Label("-")
1087 self._simulatorZFW.set_width_chars(6)
1088 self._simulatorZFW.set_alignment(1.0, 0.5)
1089 table.attach(self._simulatorZFW, 1, 2, 6, 7)
1090 self._simulatorZFWValue = None
1091
1092 table.attach(gtk.Label("kg"), 2, 3, 6, 7)
1093
1094 self.addCancelFlightButton()
1095 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1096 self._button = self.addNextButton(clicked = self._forwardClicked)
1097
1098 @property
1099 def cargoWeight(self):
1100 """Get the cargo weight entered."""
1101 return self._cargoWeight.get_int()
1102
1103 def activate(self):
1104 """Setup the information."""
1105 bookedFlight = self._wizard._bookedFlight
1106 self._numCrew.set_text(str(bookedFlight.numCrew))
1107 self._numPassengers.set_text(str(bookedFlight.numPassengers))
1108 self._bagWeight.set_text(str(bookedFlight.bagWeight))
1109 self._cargoWeight.set_int(bookedFlight.cargoWeight)
1110 self._cargoWeight.set_sensitive(True)
1111 self._mailWeight.set_text(str(bookedFlight.mailWeight))
1112 self._simulatorZFW.set_text("-")
1113 self._simulatorZFWValue = None
1114 self._zfwButton.set_sensitive(True)
1115 self._updateCalculatedZFW()
1116
1117 def finalize(self):
1118 """Finalize the payload page."""
1119 self._cargoWeight.set_sensitive(False)
1120 self._wizard.gui.initializeWeightHelp()
1121
1122 def calculateZFW(self):
1123 """Calculate the ZFW value."""
1124 zfw = self._wizard.gui._flight.aircraft.dow
1125 bookedFlight = self._wizard._bookedFlight
1126 zfw += (bookedFlight.numCrew + bookedFlight.numPassengers) * 82
1127 zfw += bookedFlight.bagWeight
1128 zfw += self._cargoWeight.get_int()
1129 zfw += bookedFlight.mailWeight
1130 return zfw
1131
1132 def _updateCalculatedZFW(self):
1133 """Update the calculated ZFW"""
1134 zfw = self.calculateZFW()
1135
1136 markupBegin = "<b>"
1137 markupEnd = "</b>"
1138 if self._simulatorZFWValue is not None and \
1139 PayloadChecker.isZFWFaulty(self._simulatorZFWValue, zfw):
1140 markupBegin += '<span foreground="red">'
1141 markupEnd = "</span>" + markupEnd
1142 self._calculatedZFW.set_markup(markupBegin + str(zfw) + markupEnd)
1143
1144 def _cargoWeightChanged(self, entry, weight):
1145 """Called when the cargo weight has changed."""
1146 self._updateCalculatedZFW()
1147
1148 def _zfwRequested(self, button):
1149 """Called when the ZFW is requested from the simulator."""
1150 self._zfwButton.set_sensitive(False)
1151 self._backButton.set_sensitive(False)
1152 self._button.set_sensitive(False)
1153 gui = self._wizard.gui
1154 gui.beginBusy(xstr("payload_zfw_busy"))
1155 gui.simulator.requestZFW(self._handleZFW)
1156
1157 def _handleZFW(self, zfw):
1158 """Called when the ZFW value is retrieved."""
1159 gobject.idle_add(self._processZFW, zfw)
1160
1161 def _processZFW(self, zfw):
1162 """Process the given ZFW value received from the simulator."""
1163 self._wizard.gui.endBusy()
1164 self._zfwButton.set_sensitive(True)
1165 self._backButton.set_sensitive(True)
1166 self._button.set_sensitive(True)
1167 self._simulatorZFWValue = zfw
1168 self._simulatorZFW.set_text("%.0f" % (zfw,))
1169 self._updateCalculatedZFW()
1170
1171 def _forwardClicked(self, button):
1172 """Called when the forward button is clicked."""
1173 self._wizard.nextPage()
1174
1175 def _backClicked(self, button):
1176 """Called when the Back button is pressed."""
1177 self.goBack()
1178
1179#-----------------------------------------------------------------------------
1180
1181class TimePage(Page):
1182 """Page displaying the departure and arrival times and allows querying the
1183 current time from the flight simulator."""
1184 def __init__(self, wizard):
1185 super(TimePage, self).__init__(wizard, xstr("time_title"),
1186 xstr("time_help"),
1187 completedHelp = xstr("time_chelp"))
1188
1189 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1190 xscale = 0.0, yscale = 0.0)
1191
1192 table = gtk.Table(3, 2)
1193 table.set_row_spacings(4)
1194 table.set_col_spacings(16)
1195 table.set_homogeneous(False)
1196 alignment.add(table)
1197 self.setMainWidget(alignment)
1198
1199 label = gtk.Label(xstr("time_departure"))
1200 label.set_alignment(0.0, 0.5)
1201 table.attach(label, 0, 1, 0, 1)
1202
1203 self._departure = gtk.Label()
1204 self._departure.set_alignment(0.0, 0.5)
1205 table.attach(self._departure, 1, 2, 0, 1)
1206
1207 label = gtk.Label(xstr("time_arrival"))
1208 label.set_alignment(0.0, 0.5)
1209 table.attach(label, 0, 1, 1, 2)
1210
1211 self._arrival = gtk.Label()
1212 self._arrival.set_alignment(0.0, 0.5)
1213 table.attach(self._arrival, 1, 2, 1, 2)
1214
1215 self._timeButton = gtk.Button(xstr("time_fs"))
1216 self._timeButton.set_use_underline(True)
1217 self._timeButton.set_tooltip_text(xstr("time_fs_tooltip"))
1218 self._timeButton.connect("clicked", self._timeRequested)
1219 table.attach(self._timeButton, 0, 1, 2, 3)
1220
1221 self._simulatorTime = gtk.Label("-")
1222 self._simulatorTime.set_alignment(0.0, 0.5)
1223 table.attach(self._simulatorTime, 1, 2, 2, 3)
1224
1225 self.addCancelFlightButton()
1226
1227 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1228 self._button = self.addNextButton(clicked = self._forwardClicked)
1229
1230 def activate(self):
1231 """Activate the page."""
1232 self._timeButton.set_sensitive(True)
1233 bookedFlight = self._wizard._bookedFlight
1234 self._departure.set_text(str(bookedFlight.departureTime.time()))
1235 self._arrival.set_text(str(bookedFlight.arrivalTime.time()))
1236 self._simulatorTime.set_text("-")
1237
1238 def _timeRequested(self, button):
1239 """Request the time from the simulator."""
1240 self._timeButton.set_sensitive(False)
1241 self._backButton.set_sensitive(False)
1242 self._button.set_sensitive(False)
1243 self._wizard.gui.beginBusy(xstr("time_busy"))
1244 self._wizard.gui.simulator.requestTime(self._handleTime)
1245
1246 def _handleTime(self, timestamp):
1247 """Handle the result of a time retrieval."""
1248 gobject.idle_add(self._processTime, timestamp)
1249
1250 def _processTime(self, timestamp):
1251 """Process the given time."""
1252 self._wizard.gui.endBusy()
1253 self._timeButton.set_sensitive(True)
1254 self._backButton.set_sensitive(True)
1255 self._button.set_sensitive(True)
1256 tm = time.gmtime(timestamp)
1257 t = datetime.time(tm.tm_hour, tm.tm_min, tm.tm_sec)
1258 self._simulatorTime.set_text(str(t))
1259
1260 ts = tm.tm_hour * 3600 + tm.tm_min * 60 + tm.tm_sec
1261 dt = self._wizard._bookedFlight.departureTime.time()
1262 dts = dt.hour * 3600 + dt.minute * 60 + dt.second
1263 diff = dts-ts
1264
1265 markupBegin = ""
1266 markupEnd = ""
1267 if diff < 0:
1268 markupBegin = '<b><span foreground="red">'
1269 markupEnd = '</span></b>'
1270 elif diff < 3*60 or diff > 30*60:
1271 markupBegin = '<b><span foreground="orange">'
1272 markupEnd = '</span></b>'
1273
1274 self._departure.set_markup(markupBegin + str(dt) + markupEnd)
1275
1276 def _backClicked(self, button):
1277 """Called when the Back button is pressed."""
1278 self.goBack()
1279
1280 def _forwardClicked(self, button):
1281 """Called when the forward button is clicked."""
1282 if not self._completed:
1283 gui = self._wizard.gui
1284 gui.beginBusy(xstr("fuel_get_busy"))
1285
1286 gui.simulator.getFuel(self._handleFuel)
1287 else:
1288 self._wizard.nextPage()
1289
1290 def _handleFuel(self, fuelData):
1291 """Callback for the fuel query operation."""
1292 gobject.idle_add(self._processFuel, fuelData)
1293
1294 def _processFuel(self, fuelData):
1295 """Process the given fuel data."""
1296 self._wizard.gui.endBusy()
1297 self._wizard._fuelData = fuelData
1298 self._wizard.nextPage()
1299
1300#-----------------------------------------------------------------------------
1301
1302class FuelTank(gtk.VBox):
1303 """Widget for the fuel tank."""
1304 def __init__(self, fuelTank, name, capacity, currentWeight):
1305 """Construct the widget for the tank with the given name."""
1306 super(FuelTank, self).__init__()
1307
1308 self._enabled = True
1309 self.fuelTank = fuelTank
1310 self.capacity = capacity
1311 self.currentWeight = currentWeight
1312 self.expectedWeight = currentWeight
1313
1314 label = gtk.Label("<b>" + name + "</b>")
1315 label.set_use_markup(True)
1316 label.set_use_underline(True)
1317 label.set_justify(JUSTIFY_CENTER)
1318 label.set_alignment(0.5, 1.0)
1319 self.pack_start(label, False, False, 4)
1320
1321 self._tankFigure = gtk.EventBox()
1322 self._tankFigure.set_size_request(38, -1)
1323 self._tankFigure.set_visible_window(False)
1324 self._tankFigure.set_tooltip_markup(xstr("fuel_tank_tooltip"))
1325
1326 if pygobject:
1327 self._tankFigure.connect("draw", self._drawTankFigure)
1328 else:
1329 self._tankFigure.connect("expose_event", self._drawTankFigure)
1330 self._tankFigure.connect("button_press_event", self._buttonPressed)
1331 self._tankFigure.connect("motion_notify_event", self._motionNotify)
1332 self._tankFigure.connect("scroll-event", self._scrolled)
1333
1334 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1335 xscale = 0.0, yscale = 1.0)
1336 alignment.add(self._tankFigure)
1337
1338 self.pack_start(alignment, True, True, 4)
1339
1340 self._expectedButton = gtk.SpinButton()
1341 self._expectedButton.set_numeric(True)
1342 self._expectedButton.set_range(0, self.capacity)
1343 self._expectedButton.set_increments(10, 100)
1344 self._expectedButton.set_value(currentWeight)
1345 self._expectedButton.set_alignment(1.0)
1346 self._expectedButton.set_width_chars(5)
1347 self._expectedButton.connect("value-changed", self._expectedChanged)
1348
1349 label.set_mnemonic_widget(self._expectedButton)
1350
1351 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1352 xscale = 0.0, yscale = 1.0)
1353 alignment.add(self._expectedButton)
1354 self.pack_start(alignment, False, False, 4)
1355
1356 def setCurrent(self, currentWeight):
1357 """Set the current weight."""
1358 self.currentWeight = currentWeight
1359 self._redraw()
1360
1361 def isCorrect(self):
1362 """Determine if the contents of the fuel tank are as expected"""
1363 return abs(self.expectedWeight - self.currentWeight)<=1
1364
1365 def disable(self):
1366 """Disable the fuel tank."""
1367 self._expectedButton.set_sensitive(False)
1368 self._enabled = False
1369
1370 def _redraw(self):
1371 """Redraw the tank figure."""
1372 self._tankFigure.queue_draw()
1373
1374 def _drawTankFigure(self, tankFigure, eventOrContext):
1375 """Draw the tank figure."""
1376 triangleSize = 5
1377
1378 context = eventOrContext if pygobject else tankFigure.window.cairo_create()
1379 (xOffset, yOffset) = (0, 0) if pygobject \
1380 else (tankFigure.allocation.x, tankFigure.allocation.y)
1381
1382 width = tankFigure.get_allocated_width() if pygobject \
1383 else tankFigure.allocation.width
1384 height = tankFigure.get_allocated_height() if pygobject \
1385 else tankFigure.allocation.height
1386
1387 rectangleX0 = triangleSize
1388 rectangleY0 = triangleSize
1389 rectangleX1 = width - 1 - triangleSize
1390 rectangleY1 = height - 1 - triangleSize
1391 rectangleLineWidth = 2.0
1392
1393 context.set_source_rgb(0.0, 0.0, 0.0)
1394 context.set_line_width(rectangleLineWidth)
1395 context.rectangle(xOffset + rectangleX0 + rectangleLineWidth/2,
1396 yOffset + rectangleY0 + rectangleLineWidth/2,
1397 rectangleX1 - rectangleX0 - rectangleLineWidth,
1398 rectangleY1 - rectangleY0 - rectangleLineWidth)
1399 context.stroke()
1400
1401 rectangleInnerLeft = rectangleX0 + rectangleLineWidth
1402 rectangleInnerRight = rectangleX1 - rectangleLineWidth
1403 self._rectangleInnerTop = rectangleInnerTop = rectangleY0 + rectangleLineWidth
1404 self._rectangleInnerBottom = rectangleInnerBottom = rectangleY1 - rectangleLineWidth
1405
1406 rectangleInnerWidth = rectangleInnerRight - rectangleInnerLeft
1407 rectangleInnerHeight = rectangleInnerBottom - rectangleInnerTop
1408
1409 context.set_source_rgb(1.0, 0.9, 0.6)
1410 currentHeight = self.currentWeight * rectangleInnerHeight / self.capacity
1411 currentX = rectangleInnerTop + rectangleInnerHeight - currentHeight
1412 context.rectangle(xOffset + rectangleInnerLeft,
1413 yOffset + rectangleInnerTop +
1414 rectangleInnerHeight - currentHeight,
1415 rectangleInnerWidth, currentHeight)
1416 context.fill()
1417
1418 expectedHeight = self.expectedWeight * rectangleInnerHeight / self.capacity
1419 expectedY = rectangleInnerTop + rectangleInnerHeight - expectedHeight
1420
1421 context.set_line_width(1.5)
1422 context.set_source_rgb(0.0, 0.85, 0.85)
1423 context.move_to(xOffset + rectangleX0, yOffset + expectedY)
1424 context.line_to(xOffset + rectangleX1, yOffset + expectedY)
1425 context.stroke()
1426
1427 context.set_line_width(0.0)
1428 context.move_to(xOffset + 0, yOffset + expectedY - triangleSize)
1429 context.line_to(xOffset + 0, yOffset + expectedY + triangleSize)
1430 context.line_to(xOffset + rectangleX0 + 1, yOffset + expectedY)
1431 context.line_to(xOffset + 0, yOffset + expectedY - triangleSize)
1432 context.fill()
1433
1434 context.set_line_width(0.0)
1435 context.move_to(xOffset + width, yOffset + expectedY - triangleSize)
1436 context.line_to(xOffset + width, yOffset + expectedY + triangleSize)
1437 context.line_to(xOffset + rectangleX1 - 1, yOffset + expectedY)
1438 context.line_to(xOffset + width, yOffset + expectedY - triangleSize)
1439 context.fill()
1440
1441 return True
1442
1443 def _setExpectedFromY(self, y):
1444 """Set the expected weight from the given Y-coordinate."""
1445 level = (self._rectangleInnerBottom - y) / \
1446 (self._rectangleInnerBottom - self._rectangleInnerTop)
1447 level = min(1.0, max(0.0, level))
1448 self._expectedButton.set_value(level * self.capacity)
1449
1450 def _buttonPressed(self, tankFigure, event):
1451 """Called when a button is pressed in the figure.
1452
1453 The expected level will be set there."""
1454 if self._enabled and event.button==1:
1455 self._setExpectedFromY(event.y)
1456
1457 def _motionNotify(self, tankFigure, event):
1458 """Called when the mouse pointer moves within the area of a tank figure."""
1459 if self._enabled and event.state==BUTTON1_MASK:
1460 self._setExpectedFromY(event.y)
1461
1462 def _scrolled(self, tankFigure, event):
1463 """Called when a scroll event is received."""
1464 if self._enabled:
1465 increment = 1 if event.state==CONTROL_MASK \
1466 else 100 if event.state==SHIFT_MASK \
1467 else 10 if event.state==0 else 0
1468 if increment!=0:
1469 if event.direction==SCROLL_DOWN:
1470 increment *= -1
1471 self._expectedButton.spin(SPIN_USER_DEFINED, increment)
1472
1473 def _expectedChanged(self, spinButton):
1474 """Called when the expected value has changed."""
1475 self.expectedWeight = spinButton.get_value_as_int()
1476 self._redraw()
1477
1478#-----------------------------------------------------------------------------
1479
1480class FuelPage(Page):
1481 """The page containing the fuel tank filling."""
1482 _pumpStep = 0.02
1483
1484 def __init__(self, wizard):
1485 """Construct the page."""
1486 super(FuelPage, self).__init__(wizard, xstr("fuel_title"),
1487 xstr("fuel_help"),
1488 completedHelp = xstr("fuel_chelp"))
1489
1490 self._fuelTanks = []
1491 self._fuelTable = None
1492 self._fuelAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1493 xscale = 0.0, yscale = 1.0)
1494 self.setMainWidget(self._fuelAlignment)
1495
1496 tankData = [(tank, 2500, 3900) for tank in acft.mostFuelTanks]
1497 self._setupTanks(tankData)
1498
1499 self.addCancelFlightButton()
1500
1501 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1502 self._button = self.addNextButton(clicked = self._forwardClicked)
1503
1504 self._pumpIndex = 0
1505
1506 def activate(self):
1507 """Activate the page."""
1508 self._setupTanks(self._wizard._fuelData)
1509
1510 def finalize(self):
1511 """Finalize the page."""
1512 for fuelTank in self._fuelTanks:
1513 fuelTank.disable()
1514
1515 def _backClicked(self, button):
1516 """Called when the Back button is pressed."""
1517 self.goBack()
1518
1519 def _forwardClicked(self, button):
1520 """Called when the forward button is clicked."""
1521 if not self._completed:
1522 self._pumpIndex = 0
1523 self._wizard.gui.beginBusy(xstr("fuel_pump_busy"))
1524 self._pump()
1525 else:
1526 self._wizard.nextPage()
1527
1528 def _setupTanks(self, tankData):
1529 """Setup the tanks for the given data."""
1530 numTanks = len(tankData)
1531 if self._fuelTable is not None:
1532 self._fuelAlignment.remove(self._fuelTable)
1533
1534 self._fuelTanks = []
1535 self._fuelTable = gtk.Table(numTanks, 1)
1536 self._fuelTable.set_col_spacings(16)
1537 index = 0
1538 for (tank, current, capacity) in tankData:
1539 fuelTank = FuelTank(tank,
1540 xstr("fuel_tank_" +
1541 const.fuelTank2string(tank)),
1542 capacity, current)
1543 self._fuelTable.attach(fuelTank, index, index+1, 0, 1)
1544 self._fuelTanks.append(fuelTank)
1545 index += 1
1546
1547 self._fuelAlignment.add(self._fuelTable)
1548 self.show_all()
1549
1550 def _pump(self):
1551 """Perform one step of pumping.
1552
1553 It is checked, if the current tank's contents are of the right
1554 quantity. If not, it is filled one step further to the desired
1555 contents. Otherwise the next tank is started. If all tanks are are
1556 filled, the next page is selected."""
1557 numTanks = len(self._fuelTanks)
1558
1559 fuelTank = None
1560 while self._pumpIndex < numTanks:
1561 fuelTank = self._fuelTanks[self._pumpIndex]
1562 if fuelTank.isCorrect():
1563 self._pumpIndex += 1
1564 fuelTank = None
1565 else:
1566 break
1567
1568 if fuelTank is None:
1569 self._wizard.gui.endBusy()
1570 self._wizard.nextPage()
1571 else:
1572 currentLevel = fuelTank.currentWeight / fuelTank.capacity
1573 expectedLevel = fuelTank.expectedWeight / fuelTank.capacity
1574 if currentLevel<expectedLevel:
1575 currentLevel += FuelPage._pumpStep
1576 if currentLevel>expectedLevel: currentLevel = expectedLevel
1577 else:
1578 currentLevel -= FuelPage._pumpStep
1579 if currentLevel<expectedLevel: currentLevel = expectedLevel
1580 fuelTank.setCurrent(currentLevel * fuelTank.capacity)
1581 self._wizard.gui.simulator.setFuelLevel([(fuelTank.fuelTank,
1582 currentLevel)])
1583 gobject.timeout_add(50, self._pump)
1584
1585#-----------------------------------------------------------------------------
1586
1587class RoutePage(Page):
1588 """The page containing the route and the flight level."""
1589 def __init__(self, wizard):
1590 """Construct the page."""
1591 super(RoutePage, self).__init__(wizard, xstr("route_title"),
1592 xstr("route_help"),
1593 completedHelp = xstr("route_chelp"))
1594
1595 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1596 xscale = 0.0, yscale = 0.0)
1597
1598 mainBox = gtk.VBox()
1599 alignment.add(mainBox)
1600 self.setMainWidget(alignment)
1601
1602 levelBox = gtk.HBox()
1603
1604 label = gtk.Label(xstr("route_level"))
1605 label.set_use_underline(True)
1606 levelBox.pack_start(label, True, True, 0)
1607
1608 self._cruiseLevel = gtk.SpinButton()
1609 self._cruiseLevel.set_increments(step = 10, page = 100)
1610 self._cruiseLevel.set_range(min = 50, max = 500)
1611 self._cruiseLevel.set_tooltip_text(xstr("route_level_tooltip"))
1612 self._cruiseLevel.set_numeric(True)
1613 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
1614 label.set_mnemonic_widget(self._cruiseLevel)
1615 self._filedCruiseLevel = 240
1616
1617 levelBox.pack_start(self._cruiseLevel, False, False, 8)
1618
1619 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1620 xscale = 0.0, yscale = 0.0)
1621 alignment.add(levelBox)
1622
1623 mainBox.pack_start(alignment, False, False, 0)
1624
1625
1626 routeBox = gtk.VBox()
1627
1628 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1629 xscale = 0.0, yscale = 0.0)
1630 label = gtk.Label(xstr("route_route"))
1631 label.set_use_underline(True)
1632 alignment.add(label)
1633 routeBox.pack_start(alignment, True, True, 0)
1634
1635 routeWindow = gtk.ScrolledWindow()
1636 routeWindow.set_size_request(400, 80)
1637 routeWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
1638 else gtk.SHADOW_IN)
1639 routeWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1640 else gtk.POLICY_AUTOMATIC,
1641 gtk.PolicyType.AUTOMATIC if pygobject
1642 else gtk.POLICY_AUTOMATIC)
1643
1644 self._uppercasingRoute = False
1645
1646 self._route = gtk.TextView()
1647 self._route.set_tooltip_text(xstr("route_route_tooltip"))
1648 self._route.set_wrap_mode(WRAP_WORD)
1649 self._route.get_buffer().connect("changed", self._routeChanged)
1650 self._route.get_buffer().connect_after("insert-text", self._routeInserted)
1651 routeWindow.add(self._route)
1652
1653 label.set_mnemonic_widget(self._route)
1654 routeBox.pack_start(routeWindow, True, True, 0)
1655
1656 mainBox.pack_start(routeBox, True, True, 8)
1657
1658 self.addCancelFlightButton()
1659
1660 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1661 self._button = self.addNextButton(clicked = self._forwardClicked)
1662
1663 @property
1664 def filedCruiseLevel(self):
1665 """Get the filed cruise level."""
1666 return self._filedCruiseLevel
1667
1668 @property
1669 def cruiseLevel(self):
1670 """Get the cruise level."""
1671 return self._cruiseLevel.get_value_as_int()
1672
1673 @property
1674 def route(self):
1675 """Get the route."""
1676 return self._getRoute()
1677
1678 def activate(self):
1679 """Setup the route from the booked flight."""
1680 self._cruiseLevel.set_value(240)
1681 self._filedCruiseLevel = 240
1682 self._route.get_buffer().set_text(self._wizard._bookedFlight.route)
1683 self._updateForwardButton()
1684
1685 def _getRoute(self):
1686 """Get the text of the route."""
1687 buffer = self._route.get_buffer()
1688 return buffer.get_text(buffer.get_start_iter(),
1689 buffer.get_end_iter(), True)
1690
1691 def _updateForwardButton(self):
1692 """Update the sensitivity of the forward button."""
1693 self._button.set_sensitive(self._cruiseLevel.get_value_as_int()>=50 and \
1694 self._getRoute()!="")
1695
1696 def _cruiseLevelChanged(self, spinButton):
1697 """Called when the cruise level has changed."""
1698 self._updateForwardButton()
1699
1700 def _routeChanged(self, textBuffer):
1701 """Called when the route has changed."""
1702 if not self._uppercasingRoute:
1703 self._updateForwardButton()
1704
1705 def _routeInserted(self, textBuffer, iter, text, length):
1706 """Called when new characters are inserted into the route.
1707
1708 It uppercases all characters."""
1709 if not self._uppercasingRoute:
1710 self._uppercasingRoute = True
1711
1712 iter1 = iter.copy()
1713 iter1.backward_chars(length)
1714 textBuffer.delete(iter, iter1)
1715
1716 textBuffer.insert(iter, text.upper())
1717
1718 self._uppercasingRoute = False
1719
1720 def _backClicked(self, button):
1721 """Called when the Back button is pressed."""
1722 self.goBack()
1723
1724 def _forwardClicked(self, button):
1725 """Called when the Forward button is clicked."""
1726 if self._completed:
1727 self._wizard.nextPage()
1728 else:
1729 bookedFlight = self._wizard._bookedFlight
1730 self._filedCruiseLevel = self.cruiseLevel
1731 self._wizard.gui.beginBusy(xstr("route_down_notams"))
1732 self._wizard.gui.webHandler.getNOTAMs(self._notamsCallback,
1733 bookedFlight.departureICAO,
1734 bookedFlight.arrivalICAO)
1735 startSound(const.SOUND_NOTAM)
1736
1737 def _notamsCallback(self, returned, result):
1738 """Callback for the NOTAMs."""
1739 gobject.idle_add(self._handleNOTAMs, returned, result)
1740
1741 def _handleNOTAMs(self, returned, result):
1742 """Handle the NOTAMs."""
1743 if returned:
1744 self._wizard._departureNOTAMs = result.departureNOTAMs
1745 self._wizard._arrivalNOTAMs = result.arrivalNOTAMs
1746 else:
1747 self._wizard._departureNOTAMs = None
1748 self._wizard._arrivalNOTAMs = None
1749
1750 bookedFlight = self._wizard._bookedFlight
1751 self._wizard.gui.beginBusy(xstr("route_down_metars"))
1752 self._wizard.gui.webHandler.getMETARs(self._metarsCallback,
1753 [bookedFlight.departureICAO,
1754 bookedFlight.arrivalICAO])
1755
1756 def _metarsCallback(self, returned, result):
1757 """Callback for the METARs."""
1758 gobject.idle_add(self._handleMETARs, returned, result)
1759
1760 def _handleMETARs(self, returned, result):
1761 """Handle the METARs."""
1762 self._wizard._departureMETAR = None
1763 self._wizard._arrivalMETAR = None
1764 bookedFlight = self._wizard._bookedFlight
1765 if returned:
1766 if bookedFlight.departureICAO in result.metars:
1767 self._wizard._departureMETAR = result.metars[bookedFlight.departureICAO]
1768 if bookedFlight.arrivalICAO in result.metars:
1769 self._wizard._arrivalMETAR = result.metars[bookedFlight.arrivalICAO]
1770
1771 self._wizard.gui.endBusy()
1772 self._backButton.set_sensitive(True)
1773 self._button.set_sensitive(True)
1774 self._wizard.nextPage()
1775
1776#-----------------------------------------------------------------------------
1777
1778class BriefingPage(Page):
1779 """Page for the briefing."""
1780 def __init__(self, wizard, departure):
1781 """Construct the briefing page."""
1782 self._departure = departure
1783
1784 title = xstr("briefing_title") % (1 if departure else 2,
1785 xstr("briefing_departure")
1786 if departure
1787 else xstr("briefing_arrival"))
1788 super(BriefingPage, self).__init__(wizard, title, xstr("briefing_help"),
1789 completedHelp = xstr("briefing_chelp"))
1790
1791 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1792 xscale = 1.0, yscale = 1.0)
1793
1794 mainBox = gtk.VBox()
1795 alignment.add(mainBox)
1796 self.setMainWidget(alignment)
1797
1798 self._notamsFrame = gtk.Frame()
1799 self._notamsFrame.set_label(xstr("briefing_notams_init"))
1800 scrolledWindow = gtk.ScrolledWindow()
1801 scrolledWindow.set_size_request(-1, 128)
1802 # FIXME: these constants should be in common
1803 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1804 else gtk.POLICY_AUTOMATIC,
1805 gtk.PolicyType.AUTOMATIC if pygobject
1806 else gtk.POLICY_AUTOMATIC)
1807 self._notams = gtk.TextView()
1808 self._notams.set_editable(False)
1809 self._notams.set_accepts_tab(False)
1810 self._notams.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
1811 scrolledWindow.add(self._notams)
1812 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
1813 xscale = 1.0, yscale = 1.0)
1814 alignment.set_padding(padding_top = 4, padding_bottom = 0,
1815 padding_left = 0, padding_right = 0)
1816 alignment.add(scrolledWindow)
1817 self._notamsFrame.add(alignment)
1818 mainBox.pack_start(self._notamsFrame, True, True, 4)
1819
1820 self._metarFrame = gtk.Frame()
1821 self._metarFrame.set_label(xstr("briefing_metar_init"))
1822 scrolledWindow = gtk.ScrolledWindow()
1823 scrolledWindow.set_size_request(-1, 32)
1824 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1825 else gtk.POLICY_AUTOMATIC,
1826 gtk.PolicyType.AUTOMATIC if pygobject
1827 else gtk.POLICY_AUTOMATIC)
1828
1829 self._uppercasingMETAR = False
1830
1831 self._metar = gtk.TextView()
1832 self._metar.set_accepts_tab(False)
1833 self._metar.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
1834 self._metar.get_buffer().connect("changed", self._metarChanged)
1835 self._metar.get_buffer().connect_after("insert-text", self._metarInserted)
1836 scrolledWindow.add(self._metar)
1837 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
1838 xscale = 1.0, yscale = 1.0)
1839 alignment.set_padding(padding_top = 4, padding_bottom = 0,
1840 padding_left = 0, padding_right = 0)
1841 alignment.add(scrolledWindow)
1842 self._metarFrame.add(alignment)
1843 mainBox.pack_start(self._metarFrame, True, True, 4)
1844 self.metarEdited = False
1845
1846 self.addCancelFlightButton()
1847
1848 self.addPreviousButton(clicked = self._backClicked)
1849 self._button = self.addNextButton(clicked = self._forwardClicked)
1850
1851 @property
1852 def metar(self):
1853 """Get the METAR on the page."""
1854 buffer = self._metar.get_buffer()
1855 return buffer.get_text(buffer.get_start_iter(),
1856 buffer.get_end_iter(), True)
1857
1858 def setMETAR(self, metar):
1859 """Set the metar."""
1860 self._metar.get_buffer().set_text(metar)
1861 self.metarEdited = False
1862
1863 def activate(self):
1864 """Activate the page."""
1865 if not self._departure:
1866 self._button.set_label(xstr("briefing_button"))
1867 self._button.set_has_tooltip(False)
1868 self._button.set_use_stock(False)
1869
1870 bookedFlight = self._wizard._bookedFlight
1871
1872 icao = bookedFlight.departureICAO if self._departure \
1873 else bookedFlight.arrivalICAO
1874 notams = self._wizard._departureNOTAMs if self._departure \
1875 else self._wizard._arrivalNOTAMs
1876 metar = self._wizard._departureMETAR if self._departure \
1877 else self._wizard._arrivalMETAR
1878
1879 self._notamsFrame.set_label(xstr("briefing_notams_template") % (icao,))
1880 buffer = self._notams.get_buffer()
1881 if notams is None:
1882 buffer.set_text(xstr("briefing_notams_failed"))
1883 elif not notams:
1884 buffer.set_text(xstr("briefing_notams_missing"))
1885 else:
1886 s = ""
1887 for notam in notams:
1888 s += str(notam.begin)
1889 if notam.end is not None:
1890 s += " - " + str(notam.end)
1891 elif notam.permanent:
1892 s += " - PERMANENT"
1893 s += "\n"
1894 if notam.repeatCycle:
1895 s += "Repeat cycle: " + notam.repeatCycle + "\n"
1896 s += notam.notice + "\n"
1897 s += "-------------------- * --------------------\n"
1898 buffer.set_text(s)
1899
1900 self._metarFrame.set_label(xstr("briefing_metar_template") % (icao,))
1901 buffer = self._metar.get_buffer()
1902 if metar is None:
1903 buffer.set_text(xstr("briefing_metar_failed"))
1904 else:
1905 buffer.set_text(metar)
1906
1907 label = self._metarFrame.get_label_widget()
1908 label.set_use_underline(True)
1909 label.set_mnemonic_widget(self._metar)
1910
1911 self.metarEdited = False
1912
1913 def _backClicked(self, button):
1914 """Called when the Back button is pressed."""
1915 self.goBack()
1916
1917 def _forwardClicked(self, button):
1918 """Called when the forward button is clicked."""
1919 if not self._departure:
1920 if not self._completed:
1921 self._wizard.gui.startMonitoring()
1922 self._button.set_label(xstr("button_next"))
1923 self._button.set_tooltip_text(xstr("button_next_tooltip"))
1924 self.complete()
1925
1926 self._wizard.nextPage()
1927
1928 def _metarChanged(self, buffer):
1929 """Called when the METAR has changed."""
1930 if not self._uppercasingMETAR:
1931 self.metarEdited = True
1932 self._button.set_sensitive(buffer.get_text(buffer.get_start_iter(),
1933 buffer.get_end_iter(),
1934 True)!="")
1935
1936 def _metarInserted(self, textBuffer, iter, text, length):
1937 """Called when new characters are inserted into the METAR.
1938
1939 It uppercases all characters."""
1940 if not self._uppercasingMETAR:
1941 self._uppercasingMETAR = True
1942
1943 iter1 = iter.copy()
1944 iter1.backward_chars(length)
1945 textBuffer.delete(iter, iter1)
1946
1947 textBuffer.insert(iter, text.upper())
1948
1949 self._uppercasingMETAR = False
1950
1951#-----------------------------------------------------------------------------
1952
1953class TakeoffPage(Page):
1954 """Page for entering the takeoff data."""
1955 def __init__(self, wizard):
1956 """Construct the takeoff page."""
1957 super(TakeoffPage, self).__init__(wizard, xstr("takeoff_title"),
1958 xstr("takeoff_help"),
1959 completedHelp = xstr("takeoff_chelp"))
1960
1961 self._forwardAllowed = False
1962
1963 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1964 xscale = 0.0, yscale = 0.0)
1965
1966 table = gtk.Table(5, 4)
1967 table.set_row_spacings(4)
1968 table.set_col_spacings(16)
1969 table.set_homogeneous(False)
1970 alignment.add(table)
1971 self.setMainWidget(alignment)
1972
1973 label = gtk.Label(xstr("takeoff_runway"))
1974 label.set_use_underline(True)
1975 label.set_alignment(0.0, 0.5)
1976 table.attach(label, 0, 1, 0, 1)
1977
1978 self._runway = gtk.Entry()
1979 self._runway.set_width_chars(10)
1980 self._runway.set_tooltip_text(xstr("takeoff_runway_tooltip"))
1981 self._runway.connect("changed", self._upperChanged)
1982 table.attach(self._runway, 1, 3, 0, 1)
1983 label.set_mnemonic_widget(self._runway)
1984
1985 label = gtk.Label(xstr("takeoff_sid"))
1986 label.set_use_underline(True)
1987 label.set_alignment(0.0, 0.5)
1988 table.attach(label, 0, 1, 1, 2)
1989
1990 self._sid = gtk.Entry()
1991 self._sid.set_width_chars(10)
1992 self._sid.set_tooltip_text(xstr("takeoff_sid_tooltip"))
1993 self._sid.connect("changed", self._upperChanged)
1994 table.attach(self._sid, 1, 3, 1, 2)
1995 label.set_mnemonic_widget(self._sid)
1996
1997 label = gtk.Label(xstr("takeoff_v1"))
1998 label.set_use_markup(True)
1999 label.set_use_underline(True)
2000 label.set_alignment(0.0, 0.5)
2001 table.attach(label, 0, 1, 2, 3)
2002
2003 self._v1 = IntegerEntry()
2004 self._v1.set_width_chars(4)
2005 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip_knots"))
2006 self._v1.connect("integer-changed", self._valueChanged)
2007 table.attach(self._v1, 2, 3, 2, 3)
2008 label.set_mnemonic_widget(self._v1)
2009
2010 self._v1Unit = gtk.Label(xstr("label_knots"))
2011 table.attach(self._v1Unit, 3, 4, 2, 3)
2012
2013 label = gtk.Label(xstr("takeoff_vr"))
2014 label.set_use_markup(True)
2015 label.set_use_underline(True)
2016 label.set_alignment(0.0, 0.5)
2017 table.attach(label, 0, 1, 3, 4)
2018
2019 self._vr = IntegerEntry()
2020 self._vr.set_width_chars(4)
2021 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip_knots"))
2022 self._vr.connect("integer-changed", self._valueChanged)
2023 table.attach(self._vr, 2, 3, 3, 4)
2024 label.set_mnemonic_widget(self._vr)
2025
2026 self._vrUnit = gtk.Label(xstr("label_knots"))
2027 table.attach(self._vrUnit, 3, 4, 3, 4)
2028
2029 label = gtk.Label(xstr("takeoff_v2"))
2030 label.set_use_markup(True)
2031 label.set_use_underline(True)
2032 label.set_alignment(0.0, 0.5)
2033 table.attach(label, 0, 1, 4, 5)
2034
2035 self._v2 = IntegerEntry()
2036 self._v2.set_width_chars(4)
2037 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip_knots"))
2038 self._v2.connect("integer-changed", self._valueChanged)
2039 table.attach(self._v2, 2, 3, 4, 5)
2040 label.set_mnemonic_widget(self._v2)
2041
2042 self._v2Unit = gtk.Label(xstr("label_knots"))
2043 table.attach(self._v2Unit, 3, 4, 4, 5)
2044
2045 self.addCancelFlightButton()
2046
2047 self.addPreviousButton(clicked = self._backClicked)
2048
2049 self._button = self.addNextButton(clicked = self._forwardClicked)
2050
2051 @property
2052 def runway(self):
2053 """Get the runway."""
2054 return self._runway.get_text()
2055
2056 @property
2057 def sid(self):
2058 """Get the SID."""
2059 return self._sid.get_text()
2060
2061 @property
2062 def v1(self):
2063 """Get the v1 speed."""
2064 return self._v1.get_int()
2065
2066 @property
2067 def vr(self):
2068 """Get the vr speed."""
2069 return self._vr.get_int()
2070
2071 @property
2072 def v2(self):
2073 """Get the v2 speed."""
2074 return self._v2.get_int()
2075
2076 def activate(self):
2077 """Activate the page."""
2078 self._runway.set_text("")
2079 self._runway.set_sensitive(True)
2080 self._sid.set_text("")
2081 self._sid.set_sensitive(True)
2082 self._v1.set_int(None)
2083 self._v1.set_sensitive(True)
2084 self._vr.set_int(None)
2085 self._vr.set_sensitive(True)
2086 self._v2.set_int(None)
2087 self._v2.set_sensitive(True)
2088
2089 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
2090 speedUnit = xstr("label" + i18nSpeedUnit)
2091 self._v1Unit.set_text(speedUnit)
2092 self._vrUnit.set_text(speedUnit)
2093 self._v2Unit.set_text(speedUnit)
2094
2095 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip" + i18nSpeedUnit))
2096 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip" + i18nSpeedUnit))
2097 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip" + i18nSpeedUnit))
2098
2099 self._button.set_sensitive(False)
2100 self._forwardAllowed = False
2101
2102 def allowForward(self):
2103 """Allow going to the next page."""
2104 self._forwardAllowed = True
2105 self._updateForwardButton()
2106
2107 def reset(self):
2108 """Reset the page if the wizard is reset."""
2109 super(TakeoffPage, self).reset()
2110 self._v1.reset()
2111 self._vr.reset()
2112 self._v2.reset()
2113
2114 def _updateForwardButton(self):
2115 """Update the sensitivity of the forward button based on some conditions."""
2116 sensitive = self._forwardAllowed and \
2117 self._runway.get_text()!="" and \
2118 self._sid.get_text()!="" and \
2119 self.v1 is not None and \
2120 self.vr is not None and \
2121 self.v2 is not None and \
2122 self.v1 <= self.vr and \
2123 self.vr <= self.v2
2124 self._button.set_sensitive(sensitive)
2125
2126 def _valueChanged(self, widget, arg = None):
2127 """Called when the value of some widget has changed."""
2128 self._updateForwardButton()
2129
2130 def _upperChanged(self, entry, arg = None):
2131 """Called when the value of some entry widget has changed and the value
2132 should be converted to uppercase."""
2133 entry.set_text(entry.get_text().upper())
2134 self._valueChanged(entry, arg)
2135
2136 def _backClicked(self, button):
2137 """Called when the Back button is pressed."""
2138 self.goBack()
2139
2140 def _forwardClicked(self, button):
2141 """Called when the forward button is clicked."""
2142 self._wizard.gui.flight.aircraft.updateV1R2()
2143 self._wizard.nextPage()
2144
2145#-----------------------------------------------------------------------------
2146
2147class LandingPage(Page):
2148 """Page for entering landing data."""
2149 def __init__(self, wizard):
2150 """Construct the landing page."""
2151 super(LandingPage, self).__init__(wizard, xstr("landing_title"),
2152 xstr("landing_help"),
2153 completedHelp = xstr("landing_chelp"))
2154
2155 self._flightEnded = False
2156
2157 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2158 xscale = 0.0, yscale = 0.0)
2159
2160 table = gtk.Table(5, 5)
2161 table.set_row_spacings(4)
2162 table.set_col_spacings(16)
2163 table.set_homogeneous(False)
2164 alignment.add(table)
2165 self.setMainWidget(alignment)
2166
2167 self._starButton = gtk.CheckButton()
2168 self._starButton.connect("clicked", self._starButtonClicked)
2169 table.attach(self._starButton, 0, 1, 0, 1)
2170
2171 label = gtk.Label(xstr("landing_star"))
2172 label.set_use_underline(True)
2173 label.set_alignment(0.0, 0.5)
2174 table.attach(label, 1, 2, 0, 1)
2175
2176 self._star = gtk.Entry()
2177 self._star.set_width_chars(10)
2178 self._star.set_tooltip_text(xstr("landing_star_tooltip"))
2179 self._star.connect("changed", self._upperChanged)
2180 self._star.set_sensitive(False)
2181 table.attach(self._star, 2, 4, 0, 1)
2182 label.set_mnemonic_widget(self._starButton)
2183
2184 self._transitionButton = gtk.CheckButton()
2185 self._transitionButton.connect("clicked", self._transitionButtonClicked)
2186 table.attach(self._transitionButton, 0, 1, 1, 2)
2187
2188 label = gtk.Label(xstr("landing_transition"))
2189 label.set_use_underline(True)
2190 label.set_alignment(0.0, 0.5)
2191 table.attach(label, 1, 2, 1, 2)
2192
2193 self._transition = gtk.Entry()
2194 self._transition.set_width_chars(10)
2195 self._transition.set_tooltip_text(xstr("landing_transition_tooltip"))
2196 self._transition.connect("changed", self._upperChanged)
2197 self._transition.set_sensitive(False)
2198 table.attach(self._transition, 2, 4, 1, 2)
2199 label.set_mnemonic_widget(self._transitionButton)
2200
2201 label = gtk.Label(xstr("landing_runway"))
2202 label.set_use_underline(True)
2203 label.set_alignment(0.0, 0.5)
2204 table.attach(label, 1, 2, 2, 3)
2205
2206 self._runway = gtk.Entry()
2207 self._runway.set_width_chars(10)
2208 self._runway.set_tooltip_text(xstr("landing_runway_tooltip"))
2209 self._runway.connect("changed", self._upperChanged)
2210 table.attach(self._runway, 2, 4, 2, 3)
2211 label.set_mnemonic_widget(self._runway)
2212
2213 label = gtk.Label(xstr("landing_approach"))
2214 label.set_use_underline(True)
2215 label.set_alignment(0.0, 0.5)
2216 table.attach(label, 1, 2, 3, 4)
2217
2218 self._approachType = gtk.Entry()
2219 self._approachType.set_width_chars(10)
2220 self._approachType.set_tooltip_text(xstr("landing_approach_tooltip"))
2221 self._approachType.connect("changed", self._upperChanged)
2222 table.attach(self._approachType, 2, 4, 3, 4)
2223 label.set_mnemonic_widget(self._approachType)
2224
2225 label = gtk.Label(xstr("landing_vref"))
2226 label.set_use_markup(True)
2227 label.set_use_underline(True)
2228 label.set_alignment(0.0, 0.5)
2229 table.attach(label, 1, 2, 5, 6)
2230
2231 self._vref = IntegerEntry()
2232 self._vref.set_width_chars(5)
2233 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip_knots"))
2234 self._vref.connect("integer-changed", self._vrefChanged)
2235 table.attach(self._vref, 3, 4, 5, 6)
2236 label.set_mnemonic_widget(self._vref)
2237
2238 self._vrefUnit = gtk.Label(xstr("label_knots"))
2239 table.attach(self._vrefUnit, 4, 5, 5, 6)
2240
2241 self.addCancelFlightButton()
2242
2243 self.addPreviousButton(clicked = self._backClicked)
2244
2245 self._button = self.addNextButton(clicked = self._forwardClicked)
2246
2247 # These are needed for correct size calculations
2248 self._starButton.set_active(True)
2249 self._transitionButton.set_active(True)
2250
2251 @property
2252 def star(self):
2253 """Get the STAR or None if none entered."""
2254 return self._star.get_text() if self._starButton.get_active() else None
2255
2256 @property
2257 def transition(self):
2258 """Get the transition or None if none entered."""
2259 return self._transition.get_text() \
2260 if self._transitionButton.get_active() else None
2261
2262 @property
2263 def approachType(self):
2264 """Get the approach type."""
2265 return self._approachType.get_text()
2266
2267 @property
2268 def runway(self):
2269 """Get the runway."""
2270 return self._runway.get_text()
2271
2272 @property
2273 def vref(self):
2274 """Return the landing reference speed."""
2275 return self._vref.get_int()
2276
2277 def reset(self):
2278 """Reset the page if the wizard is reset."""
2279 super(LandingPage, self).reset()
2280 self._vref.reset()
2281 self._flightEnded = False
2282
2283 def activate(self):
2284 """Called when the page is activated."""
2285 self._starButton.set_sensitive(True)
2286 self._starButton.set_active(False)
2287 self._star.set_text("")
2288
2289 self._transitionButton.set_sensitive(True)
2290 self._transitionButton.set_active(False)
2291 self._transition.set_text("")
2292
2293 self._runway.set_text("")
2294 self._runway.set_sensitive(True)
2295
2296 self._approachType.set_text("")
2297 self._approachType.set_sensitive(True)
2298
2299 self._vref.set_int(None)
2300 self._vref.set_sensitive(True)
2301
2302 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
2303 speedUnit = xstr("label" + i18nSpeedUnit)
2304 self._vrefUnit.set_text(speedUnit)
2305
2306 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip" +
2307 i18nSpeedUnit))
2308
2309 self._updateForwardButton()
2310
2311 def flightEnded(self):
2312 """Called when the flight has ended."""
2313 super(LandingPage, self).flightEnded()
2314 self._flightEnded = True
2315 self._updateForwardButton()
2316
2317 def _starButtonClicked(self, button):
2318 """Called when the STAR button is clicked."""
2319 active = button.get_active()
2320 self._star.set_sensitive(active)
2321 if active:
2322 self._star.grab_focus()
2323 self._updateForwardButton()
2324
2325 def _transitionButtonClicked(self, button):
2326 """Called when the Transition button is clicked."""
2327 active = button.get_active()
2328 self._transition.set_sensitive(active)
2329 if active:
2330 self._transition.grab_focus()
2331 self._updateForwardButton()
2332
2333 def _updateForwardButton(self):
2334 """Update the sensitivity of the forward button."""
2335 sensitive = self._flightEnded and \
2336 (self._starButton.get_active() or \
2337 self._transitionButton.get_active()) and \
2338 (self._star.get_text()!="" or
2339 not self._starButton.get_active()) and \
2340 (self._transition.get_text()!="" or
2341 not self._transitionButton.get_active()) and \
2342 self._runway.get_text()!="" and \
2343 self._approachType.get_text()!="" and \
2344 self.vref is not None
2345 self._button.set_sensitive(sensitive)
2346
2347 def _upperChanged(self, entry):
2348 """Called for entry widgets that must be converted to uppercase."""
2349 entry.set_text(entry.get_text().upper())
2350 self._updateForwardButton()
2351
2352 def _vrefChanged(self, widget, value):
2353 """Called when the Vref has changed."""
2354 self._updateForwardButton()
2355
2356 def _backClicked(self, button):
2357 """Called when the Back button is pressed."""
2358 self.goBack()
2359
2360 def _forwardClicked(self, button):
2361 """Called when the forward button is clicked."""
2362 self._wizard.gui.flight.aircraft.updateVRef()
2363 if self._wizard.gui.config.onlineGateSystem and \
2364 self._wizard.loggedIn and not self._completed and \
2365 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
2366 not self._wizard.entranceExam:
2367 self._wizard.getFleet(callback = self._fleetRetrieved,
2368 force = True)
2369 else:
2370 self._wizard.nextPage()
2371
2372 def _fleetRetrieved(self, fleet):
2373 """Callback for the fleet retrieval."""
2374 self._wizard.nextPage()
2375
2376#-----------------------------------------------------------------------------
2377
2378class FinishPage(Page):
2379 """Flight finish page."""
2380 _flightTypes = [ ("flighttype_scheduled", const.FLIGHTTYPE_SCHEDULED),
2381 ("flighttype_ot", const.FLIGHTTYPE_OLDTIMER),
2382 ("flighttype_vip", const.FLIGHTTYPE_VIP),
2383 ("flighttype_charter", const.FLIGHTTYPE_CHARTER) ]
2384
2385 def __init__(self, wizard):
2386 """Construct the finish page."""
2387 super(FinishPage, self).__init__(wizard, xstr("finish_title"),
2388 xstr("finish_help"))
2389
2390 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2391 xscale = 0.0, yscale = 0.0)
2392
2393 table = gtk.Table(8, 2)
2394 table.set_row_spacings(4)
2395 table.set_col_spacings(16)
2396 table.set_homogeneous(False)
2397 alignment.add(table)
2398 self.setMainWidget(alignment)
2399
2400 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2401 label = gtk.Label(xstr("finish_rating"))
2402 labelAlignment.add(label)
2403 table.attach(labelAlignment, 0, 1, 0, 1)
2404
2405 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2406 self._flightRating = gtk.Label()
2407 self._flightRating.set_width_chars(8)
2408 self._flightRating.set_alignment(0.0, 0.5)
2409 self._flightRating.set_use_markup(True)
2410 labelAlignment.add(self._flightRating)
2411 table.attach(labelAlignment, 1, 2, 0, 1)
2412
2413 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2414 label = gtk.Label(xstr("finish_flight_time"))
2415 labelAlignment.add(label)
2416 table.attach(labelAlignment, 0, 1, 1, 2)
2417
2418 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2419 self._flightTime = gtk.Label()
2420 self._flightTime.set_width_chars(10)
2421 self._flightTime.set_alignment(0.0, 0.5)
2422 self._flightTime.set_use_markup(True)
2423 labelAlignment.add(self._flightTime)
2424 table.attach(labelAlignment, 1, 2, 1, 2)
2425
2426 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2427 label = gtk.Label(xstr("finish_block_time"))
2428 labelAlignment.add(label)
2429 table.attach(labelAlignment, 0, 1, 2, 3)
2430
2431 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2432 self._blockTime = gtk.Label()
2433 self._blockTime.set_width_chars(10)
2434 self._blockTime.set_alignment(0.0, 0.5)
2435 self._blockTime.set_use_markup(True)
2436 labelAlignment.add(self._blockTime)
2437 table.attach(labelAlignment, 1, 2, 2, 3)
2438
2439 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2440 label = gtk.Label(xstr("finish_distance"))
2441 labelAlignment.add(label)
2442 table.attach(labelAlignment, 0, 1, 3, 4)
2443
2444 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2445 self._distanceFlown = gtk.Label()
2446 self._distanceFlown.set_width_chars(10)
2447 self._distanceFlown.set_alignment(0.0, 0.5)
2448 self._distanceFlown.set_use_markup(True)
2449 labelAlignment.add(self._distanceFlown)
2450 table.attach(labelAlignment, 1, 2, 3, 4)
2451
2452 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2453 label = gtk.Label(xstr("finish_fuel"))
2454 labelAlignment.add(label)
2455 table.attach(labelAlignment, 0, 1, 4, 5)
2456
2457 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2458 self._fuelUsed = gtk.Label()
2459 self._fuelUsed.set_width_chars(10)
2460 self._fuelUsed.set_alignment(0.0, 0.5)
2461 self._fuelUsed.set_use_markup(True)
2462 labelAlignment.add(self._fuelUsed)
2463 table.attach(labelAlignment, 1, 2, 4, 5)
2464
2465 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
2466 yalign = 0.5, yscale = 0.0)
2467 label = gtk.Label(xstr("finish_type"))
2468 label.set_use_underline(True)
2469 labelAlignment.add(label)
2470 table.attach(labelAlignment, 0, 1, 5, 6)
2471
2472 flightTypeModel = gtk.ListStore(str, int)
2473 for (name, type) in FinishPage._flightTypes:
2474 flightTypeModel.append([xstr(name), type])
2475
2476 self._flightType = gtk.ComboBox(model = flightTypeModel)
2477 renderer = gtk.CellRendererText()
2478 self._flightType.pack_start(renderer, True)
2479 self._flightType.add_attribute(renderer, "text", 0)
2480 self._flightType.set_tooltip_text(xstr("finish_type_tooltip"))
2481 self._flightType.set_active(0)
2482 self._flightType.connect("changed", self._flightTypeChanged)
2483 flightTypeAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2484 flightTypeAlignment.add(self._flightType)
2485 table.attach(flightTypeAlignment, 1, 2, 5, 6)
2486 label.set_mnemonic_widget(self._flightType)
2487
2488 self._onlineFlight = gtk.CheckButton(xstr("finish_online"))
2489 self._onlineFlight.set_use_underline(True)
2490 self._onlineFlight.set_tooltip_text(xstr("finish_online_tooltip"))
2491 onlineFlightAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2492 onlineFlightAlignment.add(self._onlineFlight)
2493 table.attach(onlineFlightAlignment, 1, 2, 6, 7)
2494
2495 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
2496 yalign = 0.5, yscale = 0.0)
2497 self._gateLabel = gtk.Label(xstr("finish_gate"))
2498 self._gateLabel.set_use_underline(True)
2499 labelAlignment.add(self._gateLabel)
2500 table.attach(labelAlignment, 0, 1, 7, 8)
2501
2502 self._gatesModel = gtk.ListStore(str)
2503
2504 self._gate = gtk.ComboBox(model = self._gatesModel)
2505 renderer = gtk.CellRendererText()
2506 self._gate.pack_start(renderer, True)
2507 self._gate.add_attribute(renderer, "text", 0)
2508 self._gate.set_tooltip_text(xstr("finish_gate_tooltip"))
2509 self._gate.connect("changed", self._gateChanged)
2510 gateAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
2511 gateAlignment.add(self._gate)
2512 table.attach(gateAlignment, 1, 2, 7, 8)
2513 self._gateLabel.set_mnemonic_widget(self._gate)
2514
2515 self.addButton(xstr("finish_newFlight"),
2516 sensitive = True,
2517 clicked = self._newFlightClicked,
2518 tooltip = xstr("finish_newFlight_tooltip"),
2519 padding = 16)
2520
2521 self.addPreviousButton(clicked = self._backClicked)
2522
2523 self._saveButton = self.addButton(xstr("finish_save"),
2524 sensitive = False,
2525 clicked = self._saveClicked,
2526 tooltip = xstr("finish_save_tooltip"))
2527 self._savePIREPDialog = None
2528 self._lastSavePath = None
2529
2530 self._pirepSaved = False
2531 self._pirepSent = False
2532
2533 self._sendButton = self.addButton(xstr("sendPIREP"), default = True,
2534 sensitive = False,
2535 clicked = self._sendClicked,
2536 tooltip = xstr("sendPIREP_tooltip"))
2537
2538 @property
2539 def flightType(self):
2540 """Get the flight type."""
2541 index = self._flightType.get_active()
2542 return None if index<0 else self._flightType.get_model()[index][1]
2543
2544 @property
2545 def online(self):
2546 """Get whether the flight was an online flight or not."""
2547 return self._onlineFlight.get_active()
2548
2549 def activate(self):
2550 """Activate the page."""
2551 self._pirepSaved = False
2552 self._pirepSent = False
2553
2554 flight = self._wizard.gui._flight
2555 rating = flight.logger.getRating()
2556 if rating<0:
2557 self._flightRating.set_markup('<b><span foreground="red">NO GO</span></b>')
2558 else:
2559 self._flightRating.set_markup("<b>%.1f %%</b>" % (rating,))
2560
2561 flightLength = flight.flightTimeEnd - flight.flightTimeStart
2562 self._flightTime.set_markup("<b>%s</b>" % \
2563 (util.getTimeIntervalString(flightLength),))
2564
2565 blockLength = flight.blockTimeEnd - flight.blockTimeStart
2566 self._blockTime.set_markup("<b>%s</b>" % \
2567 (util.getTimeIntervalString(blockLength),))
2568
2569 self._distanceFlown.set_markup("<b>%.2f NM</b>" % \
2570 (flight.flownDistance,))
2571
2572 self._fuelUsed.set_markup("<b>%.0f kg</b>" % \
2573 (flight.startFuel - flight.endFuel,))
2574
2575 self._flightType.set_active(-1)
2576 self._onlineFlight.set_active(self._wizard.loggedIn)
2577
2578 self._gatesModel.clear()
2579 if self._wizard.gui.config.onlineGateSystem and \
2580 self._wizard.loggedIn and \
2581 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
2582 not self._wizard.entranceExam:
2583 occupiedGates = self._wizard._fleet.getOccupiedGateNumbers()
2584 for gateNumber in const.lhbpGateNumbers:
2585 if gateNumber not in occupiedGates:
2586 self._gatesModel.append([gateNumber])
2587 self._gateLabel.set_sensitive(True)
2588 self._gate.set_sensitive(True)
2589 self._gate.set_active(-1)
2590 else:
2591 self._gateLabel.set_sensitive(False)
2592 self._gate.set_sensitive(False)
2593
2594 def _backClicked(self, button):
2595 """Called when the Back button is pressed."""
2596 self.goBack()
2597
2598 def _updateButtons(self):
2599 """Update the sensitivity state of the buttons."""
2600 sensitive = self._flightType.get_active()>=0 and \
2601 (self._gatesModel.get_iter_first() is None or
2602 self._gate.get_active()>=0)
2603
2604 self._saveButton.set_sensitive(sensitive)
2605 self._sendButton.set_sensitive(sensitive and
2606 self._wizard.bookedFlight.id is not None)
2607
2608 def _flightTypeChanged(self, comboBox):
2609 """Called when the flight type has changed."""
2610 self._updateButtons()
2611
2612 def _gateChanged(self, comboBox):
2613 """Called when the arrival gate has changed."""
2614 self._updateButtons()
2615
2616 def _newFlightClicked(self, button):
2617 """Called when the new flight button is clicked."""
2618 gui = self._wizard.gui
2619 if not self._pirepSent and not self._pirepSaved:
2620 dialog = gtk.MessageDialog(parent = gui.mainWindow,
2621 type = MESSAGETYPE_QUESTION,
2622 message_format = xstr("finish_newFlight_question"))
2623
2624 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
2625 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
2626
2627 dialog.set_title(WINDOW_TITLE_BASE)
2628 result = dialog.run()
2629 dialog.hide()
2630 if result!=RESPONSETYPE_YES:
2631 return
2632
2633 gui.reset()
2634
2635 def _saveClicked(self, button):
2636 """Called when the Save PIREP button is clicked."""
2637 gui = self._wizard.gui
2638
2639 bookedFlight = gui.bookedFlight
2640 tm = time.gmtime()
2641
2642 pilotID = self._wizard.pilotID
2643 if pilotID: pilotID += " "
2644 fileName = "%s%s %02d%02d %s-%s.pirep" % \
2645 (pilotID, str(bookedFlight.departureTime.date()),
2646 tm.tm_hour, tm.tm_min,
2647 bookedFlight.departureICAO,
2648 bookedFlight.arrivalICAO)
2649
2650 dialog = self._getSaveDialog()
2651
2652 if self._lastSavePath is None:
2653 pirepDirectory = gui.config.pirepDirectory
2654 if pirepDirectory is not None:
2655 dialog.set_current_folder(pirepDirectory)
2656 else:
2657 dialog.set_current_folder(os.path.dirname(self._lastSavePath))
2658
2659 dialog.set_current_name(fileName)
2660 result = dialog.run()
2661 dialog.hide()
2662
2663 if result==RESPONSETYPE_OK:
2664 pirep = PIREP(gui.flight)
2665
2666 self._lastSavePath = text2unicode(dialog.get_filename())
2667
2668 if pirep.save(self._lastSavePath):
2669 type = MESSAGETYPE_INFO
2670 message = xstr("finish_save_done")
2671 secondary = None
2672 self._pirepSaved = True
2673 else:
2674 type = MESSAGETYPE_ERROR
2675 message = xstr("finish_save_failed")
2676 secondary = xstr("finish_save_failed_sec")
2677
2678 dialog = gtk.MessageDialog(parent = gui.mainWindow,
2679 type = type, message_format = message)
2680 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
2681 dialog.set_title(WINDOW_TITLE_BASE)
2682 if secondary is not None:
2683 dialog.format_secondary_markup(secondary)
2684
2685 dialog.run()
2686 dialog.hide()
2687
2688 def _getSaveDialog(self):
2689 """Get the PIREP saving dialog.
2690
2691 If it does not exist yet, create it."""
2692 if self._savePIREPDialog is None:
2693 gui = self._wizard.gui
2694 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
2695 xstr("finish_save_title"),
2696 action = FILE_CHOOSER_ACTION_SAVE,
2697 buttons = (gtk.STOCK_CANCEL,
2698 RESPONSETYPE_CANCEL,
2699 gtk.STOCK_OK, RESPONSETYPE_OK),
2700 parent = gui.mainWindow)
2701 dialog.set_modal(True)
2702 dialog.set_do_overwrite_confirmation(True)
2703
2704 filter = gtk.FileFilter()
2705 filter.set_name(xstr("file_filter_pireps"))
2706 filter.add_pattern("*.pirep")
2707 dialog.add_filter(filter)
2708
2709 filter = gtk.FileFilter()
2710 filter.set_name(xstr("file_filter_all"))
2711 filter.add_pattern("*.*")
2712 dialog.add_filter(filter)
2713
2714 self._savePIREPDialog = dialog
2715
2716 return self._savePIREPDialog
2717
2718
2719 def _sendClicked(self, button):
2720 """Called when the Send button is clicked."""
2721 pirep = PIREP(self._wizard.gui.flight)
2722 self._wizard.gui.sendPIREP(pirep,
2723 callback = self._handlePIREPSent)
2724
2725 def _handlePIREPSent(self, returned, result):
2726 """Callback for the PIREP sending result."""
2727 self._pirepSent = returned and result.success
2728 if self._wizard.gui.config.onlineGateSystem and \
2729 self._wizard.loggedIn and not self._wizard.entranceExam and \
2730 returned and result.success:
2731 bookedFlight = self._wizard.bookedFlight
2732 if bookedFlight.arrivalICAO=="LHBP":
2733 iter = self._gate.get_active_iter()
2734 gateNumber = None if iter is None \
2735 else self._gatesModel.get_value(iter, 0)
2736
2737 status = const.PLANE_PARKING if gateNumber is None \
2738 else const.PLANE_HOME
2739 else:
2740 gateNumber = None
2741 status = const.PLANE_AWAY
2742
2743 self._wizard.updatePlane(self._planeUpdated,
2744 bookedFlight.tailNumber,
2745 status, gateNumber = gateNumber)
2746
2747 def _planeUpdated(self, success):
2748 """Callback for the plane updating."""
2749 pass
2750
2751#-----------------------------------------------------------------------------
2752
2753class Wizard(gtk.VBox):
2754 """The flight wizard."""
2755 def __init__(self, gui):
2756 """Construct the wizard."""
2757 super(Wizard, self).__init__()
2758
2759 self.gui = gui
2760
2761 self._pages = []
2762 self._currentPage = None
2763
2764 self._loginPage = LoginPage(self)
2765 self._pages.append(self._loginPage)
2766 self._pages.append(FlightSelectionPage(self))
2767 self._pages.append(GateSelectionPage(self))
2768 self._pages.append(ConnectPage(self))
2769 self._payloadPage = PayloadPage(self)
2770 self._pages.append(self._payloadPage)
2771 self._payloadIndex = len(self._pages)
2772 self._pages.append(TimePage(self))
2773 self._pages.append(FuelPage(self))
2774 self._routePage = RoutePage(self)
2775 self._pages.append(self._routePage)
2776 self._departureBriefingPage = BriefingPage(self, True)
2777 self._pages.append(self._departureBriefingPage)
2778 self._arrivalBriefingPage = BriefingPage(self, False)
2779 self._pages.append(self._arrivalBriefingPage)
2780 self._arrivalBriefingIndex = len(self._pages)
2781 self._takeoffPage = TakeoffPage(self)
2782 self._pages.append(self._takeoffPage)
2783 self._landingPage = LandingPage(self)
2784 self._pages.append(self._landingPage)
2785 self._finishPage = FinishPage(self)
2786 self._pages.append(self._finishPage)
2787
2788 maxWidth = 0
2789 maxHeight = 0
2790 for page in self._pages:
2791 page.show_all()
2792 pageSizeRequest = page.size_request()
2793 width = pageSizeRequest.width if pygobject else pageSizeRequest[0]
2794 height = pageSizeRequest.height if pygobject else pageSizeRequest[1]
2795 maxWidth = max(maxWidth, width)
2796 maxHeight = max(maxHeight, height)
2797 page.setStyle()
2798 maxWidth += 16
2799 maxHeight += 32
2800 self.set_size_request(maxWidth, maxHeight)
2801
2802 self._initialize()
2803
2804 @property
2805 def pilotID(self):
2806 """Get the pilot ID, if given."""
2807 return self._loginPage.pilotID
2808
2809 @property
2810 def entranceExam(self):
2811 """Get whether an entrance exam is about to be taken."""
2812 return self._loginPage.entranceExam
2813
2814 @property
2815 def loggedIn(self):
2816 """Indicate if there was a successful login."""
2817 return self._loginResult is not None
2818
2819 @property
2820 def loginResult(self):
2821 """Get the login result."""
2822 return self._loginResult
2823
2824 def setCurrentPage(self, index, finalize = False):
2825 """Set the current page to the one with the given index."""
2826 assert index < len(self._pages)
2827
2828 fromPage = self._currentPage
2829 if fromPage is not None:
2830 page = self._pages[fromPage]
2831 if finalize and not page._completed:
2832 page.complete()
2833 self.remove(page)
2834
2835 self._currentPage = index
2836 page = self._pages[index]
2837 self.add(page)
2838 if page._fromPage is None:
2839 page._fromPage = fromPage
2840 page.initialize()
2841 self.show_all()
2842 if fromPage is not None:
2843 self.grabDefault()
2844
2845 @property
2846 def bookedFlight(self):
2847 """Get the booked flight selected."""
2848 return self._bookedFlight
2849
2850 @property
2851 def cargoWeight(self):
2852 """Get the calculated ZFW value."""
2853 return self._payloadPage.cargoWeight
2854
2855 @property
2856 def zfw(self):
2857 """Get the calculated ZFW value."""
2858 return 0 if self._bookedFlight is None \
2859 else self._payloadPage.calculateZFW()
2860
2861 @property
2862 def filedCruiseAltitude(self):
2863 """Get the filed cruise altitude."""
2864 return self._routePage.filedCruiseLevel * 100
2865
2866 @property
2867 def cruiseAltitude(self):
2868 """Get the cruise altitude."""
2869 return self._routePage.cruiseLevel * 100
2870
2871 @property
2872 def route(self):
2873 """Get the route."""
2874 return self._routePage.route
2875
2876 @property
2877 def departureMETAR(self):
2878 """Get the METAR of the departure airport."""
2879 return self._departureBriefingPage.metar
2880
2881 @property
2882 def arrivalMETAR(self):
2883 """Get the METAR of the arrival airport."""
2884 return self._arrivalBriefingPage.metar
2885
2886 @property
2887 def departureRunway(self):
2888 """Get the departure runway."""
2889 return self._takeoffPage.runway
2890
2891 @property
2892 def sid(self):
2893 """Get the SID."""
2894 return self._takeoffPage.sid
2895
2896 @property
2897 def v1(self):
2898 """Get the V1 speed."""
2899 return self._takeoffPage.v1
2900
2901 @property
2902 def vr(self):
2903 """Get the Vr speed."""
2904 return self._takeoffPage.vr
2905
2906 @property
2907 def v2(self):
2908 """Get the V2 speed."""
2909 return self._takeoffPage.v2
2910
2911 @property
2912 def arrivalRunway(self):
2913 """Get the arrival runway."""
2914 return self._landingPage.runway
2915
2916 @property
2917 def star(self):
2918 """Get the STAR."""
2919 return self._landingPage.star
2920
2921 @property
2922 def transition(self):
2923 """Get the transition."""
2924 return self._landingPage.transition
2925
2926 @property
2927 def approachType(self):
2928 """Get the approach type."""
2929 return self._landingPage.approachType
2930
2931 @property
2932 def vref(self):
2933 """Get the Vref speed."""
2934 return self._landingPage.vref
2935
2936 @property
2937 def flightType(self):
2938 """Get the flight type."""
2939 return self._finishPage.flightType
2940
2941 @property
2942 def online(self):
2943 """Get whether the flight was online or not."""
2944 return self._finishPage.online
2945
2946 def nextPage(self, finalize = True):
2947 """Go to the next page."""
2948 self.jumpPage(1, finalize)
2949
2950 def jumpPage(self, count, finalize = True):
2951 """Go to the page which is 'count' pages after the current one."""
2952 self.setCurrentPage(self._currentPage + count, finalize = finalize)
2953
2954 def grabDefault(self):
2955 """Make the default button of the current page the default."""
2956 self._pages[self._currentPage].grabDefault()
2957
2958 def connected(self, fsType, descriptor):
2959 """Called when the connection could be made to the simulator."""
2960 self.nextPage()
2961
2962 def reset(self, loginResult):
2963 """Resets the wizard to go back to the login page."""
2964 self._initialize(keepLoginResult = loginResult is None,
2965 loginResult = loginResult)
2966
2967 def setStage(self, stage):
2968 """Set the flight stage to the given one."""
2969 if stage==const.STAGE_TAKEOFF:
2970 self._takeoffPage.allowForward()
2971 elif stage==const.STAGE_LANDING:
2972 if not self._arrivalBriefingPage.metarEdited:
2973 print "Downloading arrival METAR again"
2974 self.gui.webHandler.getMETARs(self._arrivalMETARCallback,
2975 [self._bookedFlight.arrivalICAO])
2976
2977 elif stage==const.STAGE_END:
2978 for page in self._pages:
2979 page.flightEnded()
2980
2981 def _initialize(self, keepLoginResult = False, loginResult = None):
2982 """Initialize the wizard."""
2983 if not keepLoginResult:
2984 self._loginResult = loginResult
2985
2986 self._loginCallback = None
2987
2988 self._fleet = None
2989 self._fleetCallback = None
2990
2991 self._bookedFlight = None
2992 self._departureGate = "-"
2993 self._fuelData = None
2994 self._departureNOTAMs = None
2995 self._departureMETAR = None
2996 self._arrivalNOTAMs = None
2997 self._arrivalMETAR = None
2998
2999 firstPage = 0 if self._loginResult is None else 1
3000 for page in self._pages[firstPage:]:
3001 page.reset()
3002
3003 self.setCurrentPage(firstPage)
3004
3005 def login(self, callback, pilotID, password, entranceExam):
3006 """Called when the login button was clicked."""
3007 self._loginCallback = callback
3008 if pilotID is None:
3009 loginResult = self._loginResult
3010 assert loginResult is not None and loginResult.loggedIn
3011 pilotID = loginResult.pilotID
3012 password = loginResult.password
3013 entranceExam = loginResult.entranceExam
3014 busyMessage = xstr("reload_busy")
3015 else:
3016 self._loginResult = None
3017 busyMessage = xstr("login_busy")
3018
3019 self.gui.beginBusy(busyMessage)
3020
3021 self.gui.webHandler.login(self._loginResultCallback,
3022 pilotID, password,
3023 entranceExam = entranceExam)
3024
3025 def reloadFlights(self, callback):
3026 """Reload the flights from the MAVA server."""
3027 self.login(callback, None, None, None)
3028
3029 def _loginResultCallback(self, returned, result):
3030 """The login result callback, called in the web handler's thread."""
3031 gobject.idle_add(self._handleLoginResult, returned, result)
3032
3033 def _handleLoginResult(self, returned, result):
3034 """Handle the login result."""
3035 self.gui.endBusy()
3036 isReload = self._loginResult is not None
3037 if returned:
3038 if result.loggedIn:
3039 self._loginResult = result
3040 else:
3041 if isReload:
3042 message = xstr("reload_failed")
3043 else:
3044 message = xstr("login_entranceExam_invalid"
3045 if self.entranceExam else
3046 xstr("login_invalid"))
3047 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
3048 type = MESSAGETYPE_ERROR,
3049 message_format = message)
3050 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
3051 dialog.set_title(WINDOW_TITLE_BASE)
3052 if isReload:
3053 secondary = xstr("reload_failed_sec")
3054 else:
3055 secondary = xstr("login_entranceExam_invalid_sec"
3056 if self.entranceExam else
3057 xstr("login_invalid_sec"))
3058 dialog.format_secondary_markup(secondary)
3059 dialog.run()
3060 dialog.hide()
3061 else:
3062 message = xstr("reload_failconn") if isReload \
3063 else xstr("login_failconn")
3064 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
3065 type = MESSAGETYPE_ERROR,
3066 message_format = message)
3067 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
3068 dialog.set_title(WINDOW_TITLE_BASE)
3069 secondary = xstr("reload_failconn_sec") if isReload \
3070 else xstr("login_failconn_sec")
3071 dialog.format_secondary_markup(secondary)
3072
3073 dialog.run()
3074 dialog.hide()
3075
3076 callback = self._loginCallback
3077 self._loginCallback = None
3078 callback(returned, result)
3079
3080 def getFleet(self, callback, force = False):
3081 """Get the fleet via the GUI and call the given callback."""
3082 self._fleetCallback = callback
3083 self.gui.getFleet(callback = self._fleetRetrieved, force = force)
3084
3085 def _fleetRetrieved(self, fleet):
3086 """Callback for the fleet retrieval."""
3087 self._fleet = fleet
3088 if self._fleetCallback is not None:
3089 self._fleetCallback(fleet)
3090 self._fleetCallback = None
3091
3092 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
3093 """Update the given plane's gate information."""
3094 self.gui.updatePlane(tailNumber, status, gateNumber = gateNumber,
3095 callback = callback)
3096
3097 def _connectSimulator(self):
3098 """Connect to the simulator."""
3099 self.gui.connectSimulator(self._bookedFlight.aircraftType)
3100
3101 def _arrivalMETARCallback(self, returned, result):
3102 """Called when the METAR of the arrival airport is retrieved."""
3103 gobject.idle_add(self._handleArrivalMETAR, returned, result)
3104
3105 def _handleArrivalMETAR(self, returned, result):
3106 """Called when the METAR of the arrival airport is retrieved."""
3107 icao = self._bookedFlight.arrivalICAO
3108 if returned and icao in result.metars:
3109 metar = result.metars[icao]
3110 if metar!="":
3111 self._arrivalBriefingPage.setMETAR(metar)
3112
3113#-----------------------------------------------------------------------------
3114
Note: See TracBrowser for help on using the repository browser.