source: src/mlx/gui/flight.py@ 84:40b2d74e74f4

Last change on this file since 84:40b2d74e74f4 was 84:40b2d74e74f4, checked in by István Váradi <ivaradi@…>, 12 years ago

Created an integer entry widget and made use of it for entering the cargo weight and the takeoff speeds.

File size: 66.4 KB
Line 
1# The flight handling "wizard"
2
3from mlx.gui.common import *
4
5import mlx.const as const
6import mlx.fs as fs
7from mlx.checks import PayloadChecker
8
9import datetime
10import time
11
12#------------------------------------------------------------------------------
13
14acftTypeNames = { const.AIRCRAFT_B736: "Boeing 737-600",
15 const.AIRCRAFT_B737: "Boeing 737-700",
16 const.AIRCRAFT_B738: "Boeing 737-800",
17 const.AIRCRAFT_DH8D: "Bombardier Dash 8-Q400",
18 const.AIRCRAFT_B733: "Boeing 737-300",
19 const.AIRCRAFT_B734: "Boeing 737-400",
20 const.AIRCRAFT_B735: "Boeing 737-500",
21 const.AIRCRAFT_B762: "Boeing 767-200",
22 const.AIRCRAFT_B763: "Boeing 767-300",
23 const.AIRCRAFT_CRJ2: "Bombardier CRJ200",
24 const.AIRCRAFT_F70: "Fokker 70",
25 const.AIRCRAFT_DC3: "Lisunov Li-2",
26 const.AIRCRAFT_T134: "Tupolev Tu-134",
27 const.AIRCRAFT_T154: "Tupolev Tu-154",
28 const.AIRCRAFT_YK40: "Yakovlev Yak-40" }
29
30#-----------------------------------------------------------------------------
31
32class Page(gtk.Alignment):
33 """A page in the flight wizard."""
34 def __init__(self, wizard, title, help):
35 """Construct the page."""
36 super(Page, self).__init__(xalign = 0.0, yalign = 0.0,
37 xscale = 1.0, yscale = 1.0)
38 self.set_padding(padding_top = 4, padding_bottom = 4,
39 padding_left = 12, padding_right = 12)
40
41 frame = gtk.Frame()
42 self.add(frame)
43
44 style = self.get_style() if pygobject else self.rc_get_style()
45
46 self._vbox = gtk.VBox()
47 self._vbox.set_homogeneous(False)
48 frame.add(self._vbox)
49
50 eventBox = gtk.EventBox()
51 eventBox.modify_bg(0, style.bg[3])
52
53 alignment = gtk.Alignment(xalign = 0.0, xscale = 0.0)
54
55 label = gtk.Label(title)
56 label.modify_fg(0, style.fg[3])
57 label.modify_font(pango.FontDescription("bold 24"))
58 alignment.set_padding(padding_top = 4, padding_bottom = 4,
59 padding_left = 6, padding_right = 0)
60
61 alignment.add(label)
62 eventBox.add(alignment)
63
64 self._vbox.pack_start(eventBox, False, False, 0)
65
66 mainBox = gtk.VBox()
67
68 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
69 xscale = 1.0, yscale = 1.0)
70 alignment.set_padding(padding_top = 16, padding_bottom = 16,
71 padding_left = 16, padding_right = 16)
72 alignment.add(mainBox)
73 self._vbox.pack_start(alignment, True, True, 0)
74
75 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
76 xscale = 0, yscale = 0.0)
77 alignment.set_padding(padding_top = 0, padding_bottom = 16,
78 padding_left = 0, padding_right = 0)
79
80 label = gtk.Label(help)
81 label.set_justify(gtk.Justification.CENTER if pygobject
82 else gtk.JUSTIFY_CENTER)
83 label.set_use_markup(True)
84 alignment.add(label)
85 mainBox.pack_start(alignment, False, False, 0)
86
87 self._mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
88 xscale = 1.0, yscale = 1.0)
89 mainBox.pack_start(self._mainAlignment, True, True, 0)
90
91 buttonAlignment = gtk.Alignment(xalign = 1.0, xscale=0.0, yscale = 0.0)
92 buttonAlignment.set_padding(padding_top = 4, padding_bottom = 10,
93 padding_left = 16, padding_right = 16)
94
95 self._buttonBox = gtk.HBox()
96 self._buttonBox.set_homogeneous(False)
97 self._defaultButton = None
98 buttonAlignment.add(self._buttonBox)
99
100 self._vbox.pack_start(buttonAlignment, False, False, 0)
101
102 self._wizard = wizard
103
104 self._finalized = False
105 self._fromPage = None
106
107 def setMainWidget(self, widget):
108 """Set the given widget as the main one."""
109 self._mainAlignment.add(widget)
110
111 def addButton(self, label, default = False):
112 """Add a button with the given label.
113
114 Return the button object created."""
115 button = gtk.Button(label)
116 self._buttonBox.pack_start(button, False, False, 4)
117 button.set_use_underline(True)
118 if default:
119 button.set_can_default(True)
120 self._defaultButton = button
121 return button
122
123 def activate(self):
124 """Called when this page becomes active.
125
126 This default implementation does nothing."""
127 pass
128
129 def finalize(self):
130 """Called when the page is finalized."""
131 pass
132
133 def grabDefault(self):
134 """If the page has a default button, make it the default one."""
135 if self._defaultButton is not None:
136 self._defaultButton.grab_default()
137
138 def reset(self):
139 """Reset the page if the wizard is reset."""
140 self._finalized = False
141 self._fromPage = None
142
143 def goBack(self):
144 """Go to the page we were invoked from."""
145 assert self._fromPage is not None
146
147 self._wizard.setCurrentPage(self._fromPage, finalize = False)
148
149#-----------------------------------------------------------------------------
150
151class LoginPage(Page):
152 """The login page."""
153 def __init__(self, wizard):
154 """Construct the login page."""
155 help = "Enter your MAVA pilot's ID and password to\n" \
156 "log in to the MAVA website and download\n" \
157 "your booked flights."
158 super(LoginPage, self).__init__(wizard, "Login", help)
159
160 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
161 xscale = 0.0, yscale = 0.0)
162
163 table = gtk.Table(2, 3)
164 table.set_row_spacings(4)
165 table.set_col_spacings(32)
166 alignment.add(table)
167 self.setMainWidget(alignment)
168
169 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
170 label = gtk.Label("Pilot _ID:")
171 label.set_use_underline(True)
172 labelAlignment.add(label)
173 table.attach(labelAlignment, 0, 1, 0, 1)
174
175 self._pilotID = gtk.Entry()
176 self._pilotID.connect("changed", self._setLoginButton)
177 self._pilotID.set_tooltip_text("Enter your MAVA pilot's ID. This "
178 "usually starts with a "
179 "'P' followed by 3 digits.")
180 table.attach(self._pilotID, 1, 2, 0, 1)
181 label.set_mnemonic_widget(self._pilotID)
182
183 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
184 label = gtk.Label("_Password:")
185 label.set_use_underline(True)
186 labelAlignment.add(label)
187 table.attach(labelAlignment, 0, 1, 1, 2)
188
189 self._password = gtk.Entry()
190 self._password.set_visibility(False)
191 self._password.connect("changed", self._setLoginButton)
192 self._password.set_tooltip_text("Enter the password for your pilot's ID")
193 table.attach(self._password, 1, 2, 1, 2)
194 label.set_mnemonic_widget(self._password)
195
196 self._rememberButton = gtk.CheckButton("_Remember password")
197 self._rememberButton.set_use_underline(True)
198 self._rememberButton.set_tooltip_text("If checked, your password will "
199 "be stored, so that you should "
200 "not have to enter it every time. "
201 "Note, however, that the password "
202 "is stored as text, and anybody "
203 "who can access your files will "
204 "be able to read it.")
205 table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
206
207 self._loginButton = self.addButton("_Login", default = True)
208 self._loginButton.set_sensitive(False)
209 self._loginButton.connect("clicked", self._loginClicked)
210 self._loginButton.set_tooltip_text("Click to log in.")
211
212 config = self._wizard.gui.config
213 self._pilotID.set_text(config.pilotID)
214 self._password.set_text(config.password)
215 self._rememberButton.set_active(config.rememberPassword)
216
217 def _setLoginButton(self, entry):
218 """Set the login button's sensitivity.
219
220 The button is sensitive only if both the pilot ID and the password
221 fields contain values."""
222 self._loginButton.set_sensitive(self._pilotID.get_text()!="" and
223 self._password.get_text()!="")
224
225 def _loginClicked(self, button):
226 """Called when the login button was clicked."""
227 self._loginButton.set_sensitive(False)
228 gui = self._wizard.gui
229 gui.beginBusy("Logging in...")
230 gui.webHandler.login(self._loginResultCallback,
231 self._pilotID.get_text(),
232 self._password.get_text())
233
234 def _loginResultCallback(self, returned, result):
235 """The login result callback, called in the web handler's thread."""
236 gobject.idle_add(self._handleLoginResult, returned, result)
237
238 def _handleLoginResult(self, returned, result):
239 """Handle the login result."""
240 self._wizard.gui.endBusy()
241 self._loginButton.set_sensitive(True)
242 if returned:
243 if result.loggedIn:
244 config = self._wizard.gui.config
245
246 config.pilotID = self._pilotID.get_text()
247
248 rememberPassword = self._rememberButton.get_active()
249 config.password = self._password.get_text() if rememberPassword \
250 else ""
251
252 config.rememberPassword = rememberPassword
253
254 config.save()
255 self._wizard._loginResult = result
256 self._wizard.nextPage()
257 else:
258 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
259 buttons = BUTTONSTYPE_OK,
260 message_format =
261 "Invalid pilot's ID or password.")
262 dialog.format_secondary_markup("Check the ID and try to reenter"
263 " the password.")
264 dialog.run()
265 dialog.hide()
266 else:
267 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
268 buttons = BUTTONSTYPE_OK,
269 message_format =
270 "Failed to connect to the MAVA website.")
271 dialog.format_secondary_markup("Try again in a few minutes.")
272 dialog.run()
273 dialog.hide()
274
275#-----------------------------------------------------------------------------
276
277class FlightSelectionPage(Page):
278 """The page to select the flight."""
279 def __init__(self, wizard):
280 """Construct the flight selection page."""
281 super(FlightSelectionPage, self).__init__(wizard, "Flight selection",
282 "Select the flight you want "
283 "to perform.")
284
285
286 self._listStore = gtk.ListStore(str, str, str, str)
287 self._flightList = gtk.TreeView(self._listStore)
288 column = gtk.TreeViewColumn("Flight no.", gtk.CellRendererText(),
289 text = 1)
290 column.set_expand(True)
291 self._flightList.append_column(column)
292 column = gtk.TreeViewColumn("Departure time [UTC]", gtk.CellRendererText(),
293 text = 0)
294 column.set_expand(True)
295 self._flightList.append_column(column)
296 column = gtk.TreeViewColumn("From", gtk.CellRendererText(),
297 text = 2)
298 column.set_expand(True)
299 self._flightList.append_column(column)
300 column = gtk.TreeViewColumn("To", gtk.CellRendererText(),
301 text = 3)
302 column.set_expand(True)
303 self._flightList.append_column(column)
304
305 flightSelection = self._flightList.get_selection()
306 flightSelection.connect("changed", self._selectionChanged)
307
308 scrolledWindow = gtk.ScrolledWindow()
309 scrolledWindow.add(self._flightList)
310 scrolledWindow.set_size_request(400, -1)
311 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
312 else gtk.POLICY_AUTOMATIC,
313 gtk.PolicyType.AUTOMATIC if pygobject
314 else gtk.POLICY_AUTOMATIC)
315 scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
316 else gtk.SHADOW_IN)
317
318 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 0.0, yscale = 1.0)
319 alignment.add(scrolledWindow)
320
321 self.setMainWidget(alignment)
322
323 self._button = self.addButton(gtk.STOCK_GO_FORWARD, default = True)
324 self._button.set_use_stock(True)
325 self._button.set_sensitive(False)
326 self._button.connect("clicked", self._forwardClicked)
327
328 def activate(self):
329 """Fill the flight list."""
330 self._flightList.set_sensitive(True)
331 self._listStore.clear()
332 for flight in self._wizard.loginResult.flights:
333 self._listStore.append([str(flight.departureTime),
334 flight.callsign,
335 flight.departureICAO,
336 flight.arrivalICAO])
337
338 def finalize(self):
339 """Finalize the page."""
340 self._flightList.set_sensitive(False)
341
342 def _selectionChanged(self, selection):
343 """Called when the selection is changed."""
344 self._button.set_sensitive(selection.count_selected_rows()==1)
345
346 def _forwardClicked(self, button):
347 """Called when the forward button was clicked."""
348 if self._finalized:
349 self._wizard.jumpPage(self._nextDistance, finalize = False)
350 else:
351 selection = self._flightList.get_selection()
352 (listStore, iter) = selection.get_selected()
353 path = listStore.get_path(iter)
354 [index] = path.get_indices() if pygobject else path
355
356 flight = self._wizard.loginResult.flights[index]
357 self._wizard._bookedFlight = flight
358
359 self._updateDepartureGate()
360
361 def _updateDepartureGate(self):
362 """Update the departure gate for the booked flight."""
363 flight = self._wizard._bookedFlight
364 if flight.departureICAO=="LHBP":
365 self._wizard._getFleet(self._fleetRetrieved)
366 else:
367 self._nextDistance = 2
368 self._wizard.jumpPage(2)
369
370 def _fleetRetrieved(self, fleet):
371 """Called when the fleet has been retrieved."""
372 if fleet is None:
373 self._nextDistance = 2
374 self._wizard.jumpPage(2)
375 else:
376 plane = fleet[self._wizard._bookedFlight.tailNumber]
377 if plane is None:
378 self._nextDistance = 2
379 self._wizard.jumpPage(2)
380 elif plane.gateNumber is not None and \
381 not fleet.isGateConflicting(plane):
382 self._wizard._departureGate = plane.gateNumber
383 self._nextDistance = 2
384 self._wizard.jumpPage(2)
385 else:
386 self._nextDistance = 1
387 self._wizard.nextPage()
388
389#-----------------------------------------------------------------------------
390
391class GateSelectionPage(Page):
392 """Page to select a free gate at LHBP.
393
394 This page should be displayed only if we have fleet information!."""
395 def __init__(self, wizard):
396 """Construct the gate selection page."""
397 help = "The airplane's gate position is invalid.\n\n" \
398 "Select the gate from which you\n" \
399 "would like to begin the flight."
400 super(GateSelectionPage, self).__init__(wizard,
401 "LHBP gate selection",
402 help)
403
404 self._listStore = gtk.ListStore(str)
405 self._gateList = gtk.TreeView(self._listStore)
406 column = gtk.TreeViewColumn(None, gtk.CellRendererText(),
407 text = 0)
408 column.set_expand(True)
409 self._gateList.append_column(column)
410 self._gateList.set_headers_visible(False)
411
412 gateSelection = self._gateList.get_selection()
413 gateSelection.connect("changed", self._selectionChanged)
414
415 scrolledWindow = gtk.ScrolledWindow()
416 scrolledWindow.add(self._gateList)
417 scrolledWindow.set_size_request(50, -1)
418 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
419 else gtk.POLICY_AUTOMATIC,
420 gtk.PolicyType.AUTOMATIC if pygobject
421 else gtk.POLICY_AUTOMATIC)
422 scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
423 else gtk.SHADOW_IN)
424
425 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 0.0, yscale = 1.0)
426 alignment.add(scrolledWindow)
427
428 self.setMainWidget(alignment)
429
430 button = self.addButton(gtk.STOCK_GO_BACK)
431 button.set_use_stock(True)
432 button.connect("clicked", self._backClicked)
433
434 self._button = self.addButton(gtk.STOCK_GO_FORWARD, default = True)
435 self._button.set_use_stock(True)
436 self._button.set_sensitive(False)
437 self._button.connect("clicked", self._forwardClicked)
438
439 def activate(self):
440 """Fill the gate list."""
441 self._listStore.clear()
442 self._gateList.set_sensitive(True)
443 occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
444 for gateNumber in const.lhbpGateNumbers:
445 if gateNumber not in occupiedGateNumbers:
446 self._listStore.append([gateNumber])
447
448 def finalize(self):
449 """Finalize the page."""
450 self._gateList.set_sensitive(False)
451
452 def _selectionChanged(self, selection):
453 """Called when the selection is changed."""
454 self._button.set_sensitive(selection.count_selected_rows()==1)
455
456 def _backClicked(self, button):
457 """Called when the Back button is pressed."""
458 self.goBack()
459
460 def _forwardClicked(self, button):
461 """Called when the forward button is clicked."""
462 if not self._finalized:
463 selection = self._gateList.get_selection()
464 (listStore, iter) = selection.get_selected()
465 (gateNumber,) = listStore.get(iter, 0)
466
467 self._wizard._departureGate = gateNumber
468
469 #self._wizard._updatePlane(self._planeUpdated,
470 # self._wizard._bookedFlight.tailNumber,
471 # const.PLANE_HOME,
472 # gateNumber)
473
474 self._wizard.nextPage()
475
476 def _planeUpdated(self, success):
477 """Callback for the plane updating call."""
478 if success is None or success:
479 self._wizard.nextPage()
480 else:
481 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
482 buttons = BUTTONSTYPE_OK,
483 message_format = "Gate conflict detected again")
484 dialog.format_secondary_markup("Try to select a different gate.")
485 dialog.run()
486 dialog.hide()
487
488 self._wizard._getFleet(self._fleetRetrieved)
489
490 def _fleetRetrieved(self, fleet):
491 """Called when the fleet has been retrieved."""
492 if fleet is None:
493 self._wizard.nextPage()
494 else:
495 self.activate()
496
497#-----------------------------------------------------------------------------
498
499class ConnectPage(Page):
500 """Page which displays the departure airport and gate (if at LHBP)."""
501 def __init__(self, wizard):
502 """Construct the connect page."""
503 help = "Load the aircraft below into the simulator and park it\n" \
504 "at the given airport, at the gate below, if present.\n\n" \
505 "Then press the Connect button to connect to the simulator."
506 super(ConnectPage, self).__init__(wizard,
507 "Connect to the simulator",
508 help)
509
510 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
511 xscale = 0.0, yscale = 0.0)
512
513 table = gtk.Table(5, 2)
514 table.set_row_spacings(4)
515 table.set_col_spacings(16)
516 table.set_homogeneous(True)
517 alignment.add(table)
518 self.setMainWidget(alignment)
519
520 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
521 label = gtk.Label("Flight number:")
522 labelAlignment.add(label)
523 table.attach(labelAlignment, 0, 1, 0, 1)
524
525 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
526 self._flightNumber = gtk.Label()
527 self._flightNumber.set_width_chars(7)
528 self._flightNumber.set_alignment(0.0, 0.5)
529 labelAlignment.add(self._flightNumber)
530 table.attach(labelAlignment, 1, 2, 0, 1)
531
532 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
533 label = gtk.Label("Aircraft:")
534 labelAlignment.add(label)
535 table.attach(labelAlignment, 0, 1, 1, 2)
536
537 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
538 self._aircraft = gtk.Label()
539 self._aircraft.set_width_chars(25)
540 self._aircraft.set_alignment(0.0, 0.5)
541 labelAlignment.add(self._aircraft)
542 table.attach(labelAlignment, 1, 2, 1, 2)
543
544 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
545 label = gtk.Label("Tail number:")
546 labelAlignment.add(label)
547 table.attach(labelAlignment, 0, 1, 2, 3)
548
549 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
550 self._tailNumber = gtk.Label()
551 self._tailNumber.set_width_chars(10)
552 self._tailNumber.set_alignment(0.0, 0.5)
553 labelAlignment.add(self._tailNumber)
554 table.attach(labelAlignment, 1, 2, 2, 3)
555
556 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
557 label = gtk.Label("Airport:")
558 labelAlignment.add(label)
559 table.attach(labelAlignment, 0, 1, 3, 4)
560
561 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
562 self._departureICAO = gtk.Label()
563 self._departureICAO.set_width_chars(6)
564 self._departureICAO.set_alignment(0.0, 0.5)
565 labelAlignment.add(self._departureICAO)
566 table.attach(labelAlignment, 1, 2, 3, 4)
567
568 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
569 label = gtk.Label("Gate:")
570 labelAlignment.add(label)
571 table.attach(labelAlignment, 0, 1, 4, 5)
572
573 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
574 self._departureGate = gtk.Label()
575 self._departureGate.set_width_chars(5)
576 self._departureGate.set_alignment(0.0, 0.5)
577 labelAlignment.add(self._departureGate)
578 table.attach(labelAlignment, 1, 2, 4, 5)
579
580 button = self.addButton(gtk.STOCK_GO_BACK)
581 button.set_use_stock(True)
582 button.connect("clicked", self._backClicked)
583
584 self._button = self.addButton("_Connect", default = True)
585 self._button.set_use_underline(True)
586 self._clickedID = self._button.connect("clicked", self._connectClicked)
587
588 def activate(self):
589 """Setup the departure information."""
590 self._button.set_label("_Connect")
591 self._button.set_use_underline(True)
592 self._button.disconnect(self._clickedID)
593 self._clickedID = self._button.connect("clicked", self._connectClicked)
594
595 bookedFlight = self._wizard._bookedFlight
596
597 self._flightNumber.set_markup("<b>" + bookedFlight.callsign + "</b>")
598
599 aircraftType = acftTypeNames[bookedFlight.aircraftType]
600 self._aircraft.set_markup("<b>" + aircraftType + "</b>")
601
602 self._tailNumber.set_markup("<b>" + bookedFlight.tailNumber + "</b>")
603
604 icao = bookedFlight.departureICAO
605 self._departureICAO.set_markup("<b>" + icao + "</b>")
606 gate = self._wizard._departureGate
607 if gate!="-":
608 gate = "<b>" + gate + "</b>"
609 self._departureGate.set_markup(gate)
610
611 def finalize(self):
612 """Finalize the page."""
613 self._button.set_label(gtk.STOCK_GO_FORWARD)
614 self._button.set_use_stock(True)
615 self._button.disconnect(self._clickedID)
616 self._clickedID = self._button.connect("clicked", self._forwardClicked)
617
618 def _backClicked(self, button):
619 """Called when the Back button is pressed."""
620 self.goBack()
621
622 def _connectClicked(self, button):
623 """Called when the Connect button is pressed."""
624 self._wizard._connectSimulator()
625
626 def _forwardClicked(self, button):
627 """Called when the Forward button is pressed."""
628 self._wizard.nextPage()
629
630#-----------------------------------------------------------------------------
631
632class PayloadPage(Page):
633 """Page to allow setting up the payload."""
634 def __init__(self, wizard):
635 """Construct the page."""
636 help = "The briefing contains the weights below.\n" \
637 "Setup the cargo weight here and the payload weight in the simulator.\n\n" \
638 "You can also check here what the simulator reports as ZFW."
639
640 super(PayloadPage, self).__init__(wizard, "Payload", help)
641
642 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
643 xscale = 0.0, yscale = 0.0)
644
645 table = gtk.Table(7, 3)
646 table.set_row_spacings(4)
647 table.set_col_spacings(16)
648 table.set_homogeneous(False)
649 alignment.add(table)
650 self.setMainWidget(alignment)
651
652 label = gtk.Label("Crew:")
653 label.set_alignment(0.0, 0.5)
654 table.attach(label, 0, 1, 0, 1)
655
656 self._numCrew = gtk.Label()
657 self._numCrew.set_width_chars(6)
658 self._numCrew.set_alignment(1.0, 0.5)
659 table.attach(self._numCrew, 1, 2, 0, 1)
660
661 label = gtk.Label("Passengers:")
662 label.set_alignment(0.0, 0.5)
663 table.attach(label, 0, 1, 1, 2)
664
665 self._numPassengers = gtk.Label()
666 self._numPassengers.set_width_chars(6)
667 self._numPassengers.set_alignment(1.0, 0.5)
668 table.attach(self._numPassengers, 1, 2, 1, 2)
669
670 label = gtk.Label("Baggage:")
671 label.set_alignment(0.0, 0.5)
672 table.attach(label, 0, 1, 2, 3)
673
674 self._bagWeight = gtk.Label()
675 self._bagWeight.set_width_chars(6)
676 self._bagWeight.set_alignment(1.0, 0.5)
677 table.attach(self._bagWeight, 1, 2, 2, 3)
678
679 table.attach(gtk.Label("kg"), 2, 3, 2, 3)
680
681 label = gtk.Label("_Cargo:")
682 label.set_use_underline(True)
683 label.set_alignment(0.0, 0.5)
684 table.attach(label, 0, 1, 3, 4)
685
686 self._cargoWeight = IntegerEntry(defaultValue = 0)
687 self._cargoWeight.set_width_chars(6)
688 self._cargoWeight.set_alignment(1.0)
689 self._cargoWeight.connect("integer-changed", self._cargoWeightChanged)
690 self._cargoWeight.set_tooltip_text("The weight of the cargo for your flight.")
691 table.attach(self._cargoWeight, 1, 2, 3, 4)
692 label.set_mnemonic_widget(self._cargoWeight)
693
694 table.attach(gtk.Label("kg"), 2, 3, 3, 4)
695
696 label = gtk.Label("Mail:")
697 label.set_alignment(0.0, 0.5)
698 table.attach(label, 0, 1, 4, 5)
699
700 self._mailWeight = gtk.Label()
701 self._mailWeight.set_width_chars(6)
702 self._mailWeight.set_alignment(1.0, 0.5)
703 table.attach(self._mailWeight, 1, 2, 4, 5)
704
705 table.attach(gtk.Label("kg"), 2, 3, 4, 5)
706
707 label = gtk.Label("<b>Calculated ZFW:</b>")
708 label.set_alignment(0.0, 0.5)
709 label.set_use_markup(True)
710 table.attach(label, 0, 1, 5, 6)
711
712 self._calculatedZFW = gtk.Label()
713 self._calculatedZFW.set_width_chars(6)
714 self._calculatedZFW.set_alignment(1.0, 0.5)
715 table.attach(self._calculatedZFW, 1, 2, 5, 6)
716
717 table.attach(gtk.Label("kg"), 2, 3, 5, 6)
718
719 self._zfwButton = gtk.Button("_ZFW from FS:")
720 self._zfwButton.set_use_underline(True)
721 self._zfwButton.connect("clicked", self._zfwRequested)
722 table.attach(self._zfwButton, 0, 1, 6, 7)
723
724 self._simulatorZFW = gtk.Label("-")
725 self._simulatorZFW.set_width_chars(6)
726 self._simulatorZFW.set_alignment(1.0, 0.5)
727 table.attach(self._simulatorZFW, 1, 2, 6, 7)
728 self._simulatorZFWValue = None
729
730 table.attach(gtk.Label("kg"), 2, 3, 6, 7)
731
732 self._backButton = self.addButton(gtk.STOCK_GO_BACK)
733 self._backButton.set_use_stock(True)
734 self._backButton.connect("clicked", self._backClicked)
735
736 self._button = self.addButton(gtk.STOCK_GO_FORWARD, default = True)
737 self._button.set_use_stock(True)
738 self._button.connect("clicked", self._forwardClicked)
739
740 def activate(self):
741 """Setup the information."""
742 bookedFlight = self._wizard._bookedFlight
743 self._numCrew.set_text(str(bookedFlight.numCrew))
744 self._numPassengers.set_text(str(bookedFlight.numPassengers))
745 self._bagWeight.set_text(str(bookedFlight.bagWeight))
746 self._cargoWeight.set_int(bookedFlight.cargoWeight)
747 self._cargoWeight.set_sensitive(True)
748 self._mailWeight.set_text(str(bookedFlight.mailWeight))
749 self._zfwButton.set_sensitive(True)
750 self._updateCalculatedZFW()
751
752 def finalize(self):
753 """Finalize the payload page."""
754 self._cargoWeight.set_sensitive(False)
755 self._zfwButton.set_sensitive(False)
756
757 def calculateZFW(self):
758 """Calculate the ZFW value."""
759 zfw = self._wizard.gui._flight.aircraft.dow
760 bookedFlight = self._wizard._bookedFlight
761 zfw += (bookedFlight.numCrew + bookedFlight.numPassengers) * 82
762 zfw += bookedFlight.bagWeight
763 zfw += self._cargoWeight.get_int()
764 zfw += bookedFlight.mailWeight
765 return zfw
766
767 def _updateCalculatedZFW(self):
768 """Update the calculated ZFW"""
769 zfw = self.calculateZFW()
770
771 markupBegin = "<b>"
772 markupEnd = "</b>"
773 if self._simulatorZFWValue is not None and \
774 PayloadChecker.isZFWFaulty(self._simulatorZFWValue, zfw):
775 markupBegin += '<span foreground="red">'
776 markupEnd = "</span>" + markupEnd
777 self._calculatedZFW.set_markup(markupBegin + str(zfw) + markupEnd)
778
779 def _cargoWeightChanged(self, entry, weight):
780 """Called when the cargo weight has changed."""
781 self._updateCalculatedZFW()
782
783 def _zfwRequested(self, button):
784 """Called when the ZFW is requested from the simulator."""
785 self._zfwButton.set_sensitive(False)
786 self._backButton.set_sensitive(False)
787 self._button.set_sensitive(False)
788 gui = self._wizard.gui
789 gui.beginBusy("Querying ZFW...")
790 gui.simulator.requestZFW(self._handleZFW)
791
792 def _handleZFW(self, zfw):
793 """Called when the ZFW value is retrieved."""
794 gobject.idle_add(self._processZFW, zfw)
795
796 def _processZFW(self, zfw):
797 """Process the given ZFW value received from the simulator."""
798 self._wizard.gui.endBusy()
799 self._zfwButton.set_sensitive(True)
800 self._backButton.set_sensitive(True)
801 self._button.set_sensitive(True)
802 self._simulatorZFWValue = zfw
803 self._simulatorZFW.set_text("%.0f" % (zfw,))
804 self._updateCalculatedZFW()
805
806 def _forwardClicked(self, button):
807 """Called when the forward button is clicked."""
808 self._wizard.nextPage()
809
810 def _backClicked(self, button):
811 """Called when the Back button is pressed."""
812 self.goBack()
813
814#-----------------------------------------------------------------------------
815
816class TimePage(Page):
817 """Page displaying the departure and arrival times and allows querying the
818 current time from the flight simulator."""
819 def __init__(self, wizard):
820 help = "The departure and arrival times are displayed below in UTC.\n\n" \
821 "You can also query the current UTC time from the simulator.\n" \
822 "Ensure that you have enough time to properly prepare for the flight."
823
824 super(TimePage, self).__init__(wizard, "Time", help)
825
826 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
827 xscale = 0.0, yscale = 0.0)
828
829 table = gtk.Table(3, 2)
830 table.set_row_spacings(4)
831 table.set_col_spacings(16)
832 table.set_homogeneous(False)
833 alignment.add(table)
834 self.setMainWidget(alignment)
835
836 label = gtk.Label("Departure:")
837 label.set_alignment(0.0, 0.5)
838 table.attach(label, 0, 1, 0, 1)
839
840 self._departure = gtk.Label()
841 self._departure.set_alignment(0.0, 0.5)
842 table.attach(self._departure, 1, 2, 0, 1)
843
844 label = gtk.Label("Arrival:")
845 label.set_alignment(0.0, 0.5)
846 table.attach(label, 0, 1, 1, 2)
847
848 self._arrival = gtk.Label()
849 self._arrival.set_alignment(0.0, 0.5)
850 table.attach(self._arrival, 1, 2, 1, 2)
851
852 self._timeButton = gtk.Button("_Time from FS:")
853 self._timeButton.set_use_underline(True)
854 self._timeButton.connect("clicked", self._timeRequested)
855 table.attach(self._timeButton, 0, 1, 2, 3)
856
857 self._simulatorTime = gtk.Label("-")
858 self._simulatorTime.set_alignment(0.0, 0.5)
859 table.attach(self._simulatorTime, 1, 2, 2, 3)
860
861 self._backButton = self.addButton(gtk.STOCK_GO_BACK)
862 self._backButton.set_use_stock(True)
863 self._backButton.connect("clicked", self._backClicked)
864
865 self._button = self.addButton(gtk.STOCK_GO_FORWARD, default = True)
866 self._button.set_use_stock(True)
867 self._button.connect("clicked", self._forwardClicked)
868
869 def activate(self):
870 """Activate the page."""
871 self._timeButton.set_sensitive(True)
872 bookedFlight = self._wizard._bookedFlight
873 self._departure.set_text(str(bookedFlight.departureTime.time()))
874 self._arrival.set_text(str(bookedFlight.arrivalTime.time()))
875
876 def finalize(self):
877 """Finalize the page."""
878 self._timeButton.set_sensitive(False)
879
880 def _timeRequested(self, button):
881 """Request the time from the simulator."""
882 self._timeButton.set_sensitive(False)
883 self._backButton.set_sensitive(False)
884 self._button.set_sensitive(False)
885 self._wizard.gui.beginBusy("Querying time...")
886 self._wizard.gui.simulator.requestTime(self._handleTime)
887
888 def _handleTime(self, timestamp):
889 """Handle the result of a time retrieval."""
890 gobject.idle_add(self._processTime, timestamp)
891
892 def _processTime(self, timestamp):
893 """Process the given time."""
894 self._wizard.gui.endBusy()
895 self._timeButton.set_sensitive(True)
896 self._backButton.set_sensitive(True)
897 self._button.set_sensitive(True)
898 tm = time.gmtime(timestamp)
899 t = datetime.time(tm.tm_hour, tm.tm_min, tm.tm_sec)
900 self._simulatorTime.set_text(str(t))
901
902 ts = tm.tm_hour * 3600 + tm.tm_min * 60 + tm.tm_sec
903 dt = self._wizard._bookedFlight.departureTime.time()
904 dts = dt.hour * 3600 + dt.minute * 60 + dt.second
905 diff = dts-ts
906
907 markupBegin = ""
908 markupEnd = ""
909 if diff < 0:
910 markupBegin = '<b><span foreground="red">'
911 markupEnd = '</span></b>'
912 elif diff < 3*60 or diff > 30*60:
913 markupBegin = '<b><span foreground="orange">'
914 markupEnd = '</span></b>'
915
916 self._departure.set_markup(markupBegin + str(dt) + markupEnd)
917
918 def _backClicked(self, button):
919 """Called when the Back button is pressed."""
920 self.goBack()
921
922 def _forwardClicked(self, button):
923 """Called when the forward button is clicked."""
924 self._wizard.nextPage()
925
926#-----------------------------------------------------------------------------
927
928class RoutePage(Page):
929 """The page containing the route and the flight level."""
930 def __init__(self, wizard):
931 help = "Set your cruise flight level below, and\n" \
932 "if necessary, edit the flight plan."
933
934 super(RoutePage, self).__init__(wizard, "Route", help)
935
936 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
937 xscale = 0.0, yscale = 0.0)
938
939 mainBox = gtk.VBox()
940 alignment.add(mainBox)
941 self.setMainWidget(alignment)
942
943 levelBox = gtk.HBox()
944
945 label = gtk.Label("_Cruise level")
946 label.set_use_underline(True)
947 levelBox.pack_start(label, True, True, 0)
948
949 self._cruiseLevel = gtk.SpinButton()
950 self._cruiseLevel.set_increments(step = 10, page = 100)
951 self._cruiseLevel.set_range(min = 50, max = 500)
952 self._cruiseLevel.set_value(240)
953 self._cruiseLevel.set_tooltip_text("The cruise flight level.")
954 self._cruiseLevel.set_numeric(True)
955 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
956 label.set_mnemonic_widget(self._cruiseLevel)
957
958 levelBox.pack_start(self._cruiseLevel, False, False, 8)
959
960 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
961 xscale = 0.0, yscale = 0.0)
962 alignment.add(levelBox)
963
964 mainBox.pack_start(alignment, False, False, 0)
965
966
967 routeBox = gtk.VBox()
968
969 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
970 xscale = 0.0, yscale = 0.0)
971 label = gtk.Label("_Route")
972 label.set_use_underline(True)
973 alignment.add(label)
974 routeBox.pack_start(alignment, True, True, 0)
975
976 routeWindow = gtk.ScrolledWindow()
977 routeWindow.set_size_request(400, 80)
978 routeWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
979 else gtk.SHADOW_IN)
980 routeWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
981 else gtk.POLICY_AUTOMATIC,
982 gtk.PolicyType.AUTOMATIC if pygobject
983 else gtk.POLICY_AUTOMATIC)
984
985 self._route = gtk.TextView()
986 self._route.set_tooltip_text("The planned flight route.")
987 self._route.get_buffer().connect("changed", self._routeChanged)
988 routeWindow.add(self._route)
989
990 label.set_mnemonic_widget(self._route)
991 routeBox.pack_start(routeWindow, True, True, 0)
992
993 mainBox.pack_start(routeBox, True, True, 8)
994
995 self._backButton = self.addButton(gtk.STOCK_GO_BACK)
996 self._backButton.set_use_stock(True)
997 self._backButton.connect("clicked", self._backClicked)
998
999 self._button = self.addButton(gtk.STOCK_GO_FORWARD, default = True)
1000 self._button.set_use_stock(True)
1001 self._button.connect("clicked", self._forwardClicked)
1002
1003 @property
1004 def cruiseLevel(self):
1005 """Get the cruise level."""
1006 return self._cruiseLevel.get_value_as_int()
1007
1008 def activate(self):
1009 """Setup the route from the booked flight."""
1010 self._route.set_sensitive(True)
1011 self._cruiseLevel.set_sensitive(True)
1012 self._route.get_buffer().set_text(self._wizard._bookedFlight.route)
1013 self._updateForwardButton()
1014
1015 def finalize(self):
1016 """Finalize the page."""
1017 self._route.set_sensitive(False)
1018 self._cruiseLevel.set_sensitive(False)
1019
1020 def _getRoute(self):
1021 """Get the text of the route."""
1022 buffer = self._route.get_buffer()
1023 return buffer.get_text(buffer.get_start_iter(),
1024 buffer.get_end_iter(), True)
1025
1026 def _updateForwardButton(self):
1027 """Update the sensitivity of the forward button."""
1028 self._button.set_sensitive(self._cruiseLevel.get_value_as_int()>=50 and \
1029 self._getRoute()!="")
1030
1031 def _cruiseLevelChanged(self, spinButton):
1032 """Called when the cruise level has changed."""
1033 self._updateForwardButton()
1034
1035 def _routeChanged(self, textBuffer):
1036 """Called when the route has changed."""
1037 self._updateForwardButton()
1038
1039 def _backClicked(self, button):
1040 """Called when the Back button is pressed."""
1041 self.goBack()
1042
1043 def _forwardClicked(self, button):
1044 """Called when the Forward button is clicked."""
1045 if self._finalized:
1046 self._wizard.nextPage()
1047 else:
1048 self._backButton.set_sensitive(False)
1049 self._button.set_sensitive(False)
1050 self._cruiseLevel.set_sensitive(False)
1051 self._route.set_sensitive(False)
1052
1053 bookedFlight = self._wizard._bookedFlight
1054 self._wizard.gui.beginBusy("Downloading NOTAMs...")
1055 self._wizard.gui.webHandler.getNOTAMs(self._notamsCallback,
1056 bookedFlight.departureICAO,
1057 bookedFlight.arrivalICAO)
1058
1059 def _notamsCallback(self, returned, result):
1060 """Callback for the NOTAMs."""
1061 gobject.idle_add(self._handleNOTAMs, returned, result)
1062
1063 def _handleNOTAMs(self, returned, result):
1064 """Handle the NOTAMs."""
1065 if returned:
1066 self._wizard._departureNOTAMs = result.departureNOTAMs
1067 self._wizard._arrivalNOTAMs = result.arrivalNOTAMs
1068 else:
1069 self._wizard._departureNOTAMs = None
1070 self._wizard._arrivalNOTAMs = None
1071
1072 bookedFlight = self._wizard._bookedFlight
1073 self._wizard.gui.beginBusy("Downloading METARs...")
1074 self._wizard.gui.webHandler.getMETARs(self._metarsCallback,
1075 [bookedFlight.departureICAO,
1076 bookedFlight.arrivalICAO])
1077
1078 def _metarsCallback(self, returned, result):
1079 """Callback for the METARs."""
1080 gobject.idle_add(self._handleMETARs, returned, result)
1081
1082 def _handleMETARs(self, returned, result):
1083 """Handle the METARs."""
1084 self._wizard._departureMETAR = None
1085 self._wizard._arrivalMETAR = None
1086 bookedFlight = self._wizard._bookedFlight
1087 if returned:
1088 if bookedFlight.departureICAO in result.metars:
1089 self._wizard._departureMETAR = result.metars[bookedFlight.departureICAO]
1090 if bookedFlight.arrivalICAO in result.metars:
1091 self._wizard._arrivalMETAR = result.metars[bookedFlight.arrivalICAO]
1092
1093 self._wizard.gui.endBusy()
1094 self._backButton.set_sensitive(True)
1095 self._button.set_sensitive(True)
1096 self._wizard.nextPage()
1097
1098#-----------------------------------------------------------------------------
1099
1100class BriefingPage(Page):
1101 """Page for the briefing."""
1102 def __init__(self, wizard, departure):
1103 """Construct the briefing page."""
1104 self._departure = departure
1105
1106 title = "Briefing (%d/2): %s" % (1 if departure else 2,
1107 "departure" if departure
1108 else "arrival")
1109
1110 help = "Read carefully the NOTAMs and METAR below."
1111
1112 super(BriefingPage, self).__init__(wizard, title, help)
1113
1114 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1115 xscale = 1.0, yscale = 1.0)
1116
1117 mainBox = gtk.VBox()
1118 alignment.add(mainBox)
1119 self.setMainWidget(alignment)
1120
1121 self._notamsFrame = gtk.Frame()
1122 self._notamsFrame.set_label("LHBP NOTAMs")
1123 scrolledWindow = gtk.ScrolledWindow()
1124 scrolledWindow.set_size_request(-1, 128)
1125 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1126 else gtk.POLICY_AUTOMATIC,
1127 gtk.PolicyType.AUTOMATIC if pygobject
1128 else gtk.POLICY_AUTOMATIC)
1129 self._notams = gtk.TextView()
1130 self._notams.set_editable(False)
1131 self._notams.set_accepts_tab(False)
1132 self._notams.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
1133 scrolledWindow.add(self._notams)
1134 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
1135 xscale = 1.0, yscale = 1.0)
1136 alignment.set_padding(padding_top = 4, padding_bottom = 0,
1137 padding_left = 0, padding_right = 0)
1138 alignment.add(scrolledWindow)
1139 self._notamsFrame.add(alignment)
1140 mainBox.pack_start(self._notamsFrame, True, True, 4)
1141
1142 self._metarFrame = gtk.Frame()
1143 self._metarFrame.set_label("LHBP METAR")
1144 scrolledWindow = gtk.ScrolledWindow()
1145 scrolledWindow.set_size_request(-1, 32)
1146 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1147 else gtk.POLICY_AUTOMATIC,
1148 gtk.PolicyType.AUTOMATIC if pygobject
1149 else gtk.POLICY_AUTOMATIC)
1150 self._metar = gtk.TextView()
1151 self._metar.set_editable(False)
1152 self._metar.set_accepts_tab(False)
1153 self._metar.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
1154 scrolledWindow.add(self._metar)
1155 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
1156 xscale = 1.0, yscale = 1.0)
1157 alignment.set_padding(padding_top = 4, padding_bottom = 0,
1158 padding_left = 0, padding_right = 0)
1159 alignment.add(scrolledWindow)
1160 self._metarFrame.add(alignment)
1161 mainBox.pack_start(self._metarFrame, True, True, 4)
1162
1163 button = self.addButton(gtk.STOCK_GO_BACK)
1164 button.set_use_stock(True)
1165 button.connect("clicked", self._backClicked)
1166
1167 self._button = self.addButton(gtk.STOCK_GO_FORWARD, default = True)
1168 self._button.set_use_stock(True)
1169 self._button.connect("clicked", self._forwardClicked)
1170
1171 def activate(self):
1172 """Activate the page."""
1173 if not self._departure:
1174 self._button.set_label("I have read the briefing and am ready to fly!")
1175 self._button.set_use_stock(False)
1176
1177 bookedFlight = self._wizard._bookedFlight
1178
1179 icao = bookedFlight.departureICAO if self._departure \
1180 else bookedFlight.arrivalICAO
1181 notams = self._wizard._departureNOTAMs if self._departure \
1182 else self._wizard._arrivalNOTAMs
1183 metar = self._wizard._departureMETAR if self._departure \
1184 else self._wizard._arrivalMETAR
1185
1186 self._notamsFrame.set_label(icao + " NOTAMs")
1187 buffer = self._notams.get_buffer()
1188 if notams is None:
1189 buffer.set_text("Could not download NOTAMs")
1190 else:
1191 s = ""
1192 for notam in notams:
1193 s += str(notam.begin)
1194 if notam.end is not None:
1195 s += " - " + str(notam.end)
1196 elif notam.permanent:
1197 s += " - PERMANENT"
1198 s += "\n"
1199 if notam.repeatCycle:
1200 s += "Repeat cycle: " + notam.repeatCycle + "\n"
1201 s += notam.notice + "\n"
1202 s += "-------------------- * --------------------\n"
1203 buffer.set_text(s)
1204
1205 self._metarFrame.set_label(icao + " METAR")
1206 buffer = self._metar.get_buffer()
1207 if metar is None:
1208 buffer.set_text("Could not download METAR")
1209 else:
1210 buffer.set_text(metar)
1211
1212 def finalize(self):
1213 """Finalize the page."""
1214 if not self._departure:
1215 self._button.set_use_stock(True)
1216 self._button.set_label(gtk.STOCK_GO_FORWARD)
1217
1218 def _backClicked(self, button):
1219 """Called when the Back button is pressed."""
1220 self.goBack()
1221
1222 def _forwardClicked(self, button):
1223 """Called when the forward button is clicked."""
1224 if not self._departure:
1225 if not self._finalized:
1226 self._wizard.gui.startMonitoring()
1227 self._finalized = True
1228
1229 self._wizard.nextPage()
1230
1231#-----------------------------------------------------------------------------
1232
1233class TakeoffPage(Page):
1234 """Page for entering the takeoff data."""
1235 def __init__(self, wizard):
1236 """Construct the takeoff page."""
1237 help = "Enter the runway and SID used, as well as the speeds."
1238
1239 super(TakeoffPage, self).__init__(wizard, "Takeoff", help)
1240
1241 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1242 xscale = 0.0, yscale = 0.0)
1243
1244 table = gtk.Table(5, 4)
1245 table.set_row_spacings(4)
1246 table.set_col_spacings(16)
1247 table.set_homogeneous(False)
1248 alignment.add(table)
1249 self.setMainWidget(alignment)
1250
1251 label = gtk.Label("Run_way:")
1252 label.set_use_underline(True)
1253 label.set_alignment(0.0, 0.5)
1254 table.attach(label, 0, 1, 0, 1)
1255
1256 self._runway = gtk.Entry()
1257 self._runway.set_width_chars(10)
1258 self._runway.set_tooltip_text("The runway the takeoff is performed from.")
1259 table.attach(self._runway, 1, 3, 0, 1)
1260 label.set_mnemonic_widget(self._runway)
1261
1262 label = gtk.Label("_SID:")
1263 label.set_use_underline(True)
1264 label.set_alignment(0.0, 0.5)
1265 table.attach(label, 0, 1, 1, 2)
1266
1267 self._sid = gtk.Entry()
1268 self._sid.set_width_chars(10)
1269 self._sid.set_tooltip_text("The name of the Standard Instrument Deparature procedure followed.")
1270 table.attach(self._sid, 1, 3, 1, 2)
1271 label.set_mnemonic_widget(self._sid)
1272
1273 label = gtk.Label("V<sub>_1</sub>:")
1274 label.set_use_markup(True)
1275 label.set_use_underline(True)
1276 label.set_alignment(0.0, 0.5)
1277 table.attach(label, 0, 1, 2, 3)
1278
1279 self._v1 = IntegerEntry()
1280 self._v1.set_width_chars(1)
1281 self._v1.set_tooltip_markup("The takeoff decision speed in knots.")
1282 table.attach(self._v1, 2, 3, 2, 3)
1283 label.set_mnemonic_widget(self._v1)
1284
1285 table.attach(gtk.Label("knots"), 3, 4, 2, 3)
1286
1287 label = gtk.Label("V<sub>_r</sub>:")
1288 label.set_use_markup(True)
1289 label.set_use_underline(True)
1290 label.set_alignment(0.0, 0.5)
1291 table.attach(label, 0, 1, 3, 4)
1292
1293 self._vr = IntegerEntry()
1294 self._vr.set_width_chars(1)
1295 self._vr.set_tooltip_markup("The takeoff rotation speed in knots.")
1296 table.attach(self._vr, 2, 3, 3, 4)
1297 label.set_mnemonic_widget(self._vr)
1298
1299 table.attach(gtk.Label("knots"), 3, 4, 3, 4)
1300
1301 label = gtk.Label("V<sub>_2</sub>:")
1302 label.set_use_markup(True)
1303 label.set_use_underline(True)
1304 label.set_alignment(0.0, 0.5)
1305 table.attach(label, 0, 1, 4, 5)
1306
1307 self._v2 = IntegerEntry()
1308 self._v2.set_width_chars(1)
1309 self._v2.set_tooltip_markup("The takeoff safety speed in knots.")
1310 table.attach(self._v2, 2, 3, 4, 5)
1311 label.set_mnemonic_widget(self._v2)
1312
1313 table.attach(gtk.Label("knots"), 3, 4, 4, 5)
1314
1315 button = self.addButton(gtk.STOCK_GO_BACK)
1316 button.set_use_stock(True)
1317 button.connect("clicked", self._backClicked)
1318
1319 self._button = self.addButton(gtk.STOCK_GO_FORWARD, default = True)
1320 self._button.set_use_stock(True)
1321 self._button.connect("clicked", self._forwardClicked)
1322
1323 @property
1324 def v1(self):
1325 """Get the v1 speed."""
1326 return self._v1.get_int()
1327
1328 @property
1329 def vr(self):
1330 """Get the vr speed."""
1331 return self._vr.get_int()
1332
1333 @property
1334 def v2(self):
1335 """Get the v2 speed."""
1336 return self._v2.get_int()
1337
1338 def activate(self):
1339 """Activate the page."""
1340 self._runway.set_text("")
1341 self._runway.set_sensitive(True)
1342 self._sid.set_text("")
1343 self._sid.set_sensitive(True)
1344 self._v1.set_int(None)
1345 self._v1.set_sensitive(True)
1346 self._vr.set_sensitive(True)
1347 self._v2.set_sensitive(True)
1348 self._button.set_sensitive(False)
1349
1350 def freezeValues(self):
1351 """Freeze the values on the page, and enable the forward button."""
1352 self._runway.set_sensitive(False)
1353 self._sid.set_sensitive(False)
1354 self._v1.set_sensitive(False)
1355 self._vr.set_sensitive(False)
1356 self._v2.set_sensitive(False)
1357 self._button.set_sensitive(True)
1358
1359 def _backClicked(self, button):
1360 """Called when the Back button is pressed."""
1361 self.goBack()
1362
1363 def _forwardClicked(self, button):
1364 """Called when the forward button is clicked."""
1365 self._wizard.nextPage()
1366
1367#-----------------------------------------------------------------------------
1368
1369class LandingPage(Page):
1370 """Page for entering landing data."""
1371 def __init__(self, wizard):
1372 """Construct the landing page."""
1373 help = "Enter the STAR and/or transition, runway,\n" \
1374 "approach type and V<sub>ref</sub> used."
1375
1376 super(LandingPage, self).__init__(wizard, "Landing", help)
1377
1378 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1379 xscale = 0.0, yscale = 0.0)
1380
1381 table = gtk.Table(5, 4)
1382 table.set_row_spacings(4)
1383 table.set_col_spacings(16)
1384 table.set_homogeneous(False)
1385 alignment.add(table)
1386 self.setMainWidget(alignment)
1387
1388 self._starButton = gtk.CheckButton()
1389 self._starButton.connect("clicked", self._starButtonClicked)
1390 table.attach(self._starButton, 0, 1, 0, 1)
1391
1392 label = gtk.Label("_STAR:")
1393 label.set_use_underline(True)
1394 label.set_alignment(0.0, 0.5)
1395 table.attach(label, 1, 2, 0, 1)
1396
1397 self._star = gtk.Entry()
1398 self._star.set_width_chars(10)
1399 self._star.set_tooltip_text("The name of Standard Terminal Arrival Route followed.")
1400 self._star.connect("changed", self._updateForwardButton)
1401 self._star.set_sensitive(False)
1402 table.attach(self._star, 2, 3, 0, 1)
1403 label.set_mnemonic_widget(self._starButton)
1404
1405 self._transitionButton = gtk.CheckButton()
1406 self._transitionButton.connect("clicked", self._transitionButtonClicked)
1407 table.attach(self._transitionButton, 0, 1, 1, 2)
1408
1409 label = gtk.Label("_Transition:")
1410 label.set_use_underline(True)
1411 label.set_alignment(0.0, 0.5)
1412 table.attach(label, 1, 2, 1, 2)
1413
1414 self._transition = gtk.Entry()
1415 self._transition.set_width_chars(10)
1416 self._transition.set_tooltip_text("The name of transition executed or VECTORS if vectored by ATC.")
1417 self._transition.connect("changed", self._updateForwardButton)
1418 self._transition.set_sensitive(False)
1419 table.attach(self._transition, 2, 3, 1, 2)
1420 label.set_mnemonic_widget(self._transitionButton)
1421
1422 label = gtk.Label("Run_way:")
1423 label.set_use_underline(True)
1424 label.set_alignment(0.0, 0.5)
1425 table.attach(label, 1, 2, 2, 3)
1426
1427 self._runway = gtk.Entry()
1428 self._runway.set_width_chars(10)
1429 self._runway.set_tooltip_text("The runway the landing is performed on.")
1430 self._runway.connect("changed", self._updateForwardButton)
1431 table.attach(self._runway, 2, 3, 2, 3)
1432 label.set_mnemonic_widget(self._runway)
1433
1434 label = gtk.Label("_Approach type:")
1435 label.set_use_underline(True)
1436 label.set_alignment(0.0, 0.5)
1437 table.attach(label, 1, 2, 3, 4)
1438
1439 self._approachType = gtk.Entry()
1440 self._approachType.set_width_chars(10)
1441 self._approachType.set_tooltip_text("The type of the approach, e.g. ILS or VISUAL.")
1442 self._approachType.connect("changed", self._updateForwardButton)
1443 table.attach(self._approachType, 2, 3, 3, 4)
1444 label.set_mnemonic_widget(self._approachType)
1445
1446 label = gtk.Label("V<sub>_ref</sub>:")
1447 label.set_use_markup(True)
1448 label.set_use_underline(True)
1449 label.set_alignment(0.0, 0.5)
1450 table.attach(label, 1, 2, 5, 6)
1451
1452 self._vref = gtk.SpinButton()
1453 self._vref.set_increments(step = 1, page = 10)
1454 self._vref.set_range(min = 50, max = 300)
1455 self._vref.set_value(140)
1456 self._vref.set_numeric(True)
1457 self._vref.set_tooltip_markup("The approach reference speed in knots.")
1458 table.attach(self._vref, 2, 3, 5, 6)
1459 label.set_mnemonic_widget(self._vref)
1460
1461 table.attach(gtk.Label("knots"), 3, 4, 5, 6)
1462
1463 button = self.addButton(gtk.STOCK_GO_BACK)
1464 button.set_use_stock(True)
1465 button.connect("clicked", self._backClicked)
1466
1467 self._button = self.addButton(gtk.STOCK_GO_FORWARD, default = True)
1468 self._button.set_use_stock(True)
1469 self._button.connect("clicked", self._forwardClicked)
1470
1471 # These are needed for correct size calculations
1472 self._starButton.set_active(True)
1473 self._transitionButton.set_active(True)
1474
1475 def activate(self):
1476 """Called when the page is activated."""
1477 self._starButton.set_sensitive(True)
1478 self._starButton.set_active(False)
1479 self._star.set_text("")
1480
1481 self._transitionButton.set_sensitive(True)
1482 self._transitionButton.set_active(False)
1483 self._transition.set_text("")
1484
1485 self._runway.set_text("")
1486 self._runway.set_sensitive(True)
1487
1488 self._approachType.set_text("")
1489 self._approachType.set_sensitive(True)
1490
1491 self._vref.set_value(140)
1492 self._vref.set_sensitive(True)
1493
1494 self._updateForwardButton()
1495
1496 def finalize(self):
1497 """Finalize the page."""
1498 self._starButton.set_sensitive(False)
1499 self._star.set_sensitive(False)
1500
1501 self._transitionButton.set_sensitive(False)
1502 self._transition.set_sensitive(False)
1503
1504 self._runway.set_sensitive(False)
1505
1506 self._approachType.set_sensitive(False)
1507
1508 self._vref.set_sensitive(False)
1509
1510 def _starButtonClicked(self, button):
1511 """Called when the STAR button is clicked."""
1512 active = button.get_active()
1513 self._star.set_sensitive(active)
1514 if active:
1515 self._star.grab_focus()
1516 self._updateForwardButton()
1517
1518 def _transitionButtonClicked(self, button):
1519 """Called when the Transition button is clicked."""
1520 active = button.get_active()
1521 self._transition.set_sensitive(active)
1522 if active:
1523 self._transition.grab_focus()
1524 self._updateForwardButton()
1525
1526 def _updateForwardButton(self, widget = None):
1527 """Update the sensitivity of the forward button."""
1528 sensitive = (self._starButton.get_active() or \
1529 self._transitionButton.get_active()) and \
1530 (self._star.get_text()!="" or
1531 not self._starButton.get_active()) and \
1532 (self._transition.get_text()!="" or
1533 not self._transitionButton.get_active()) and \
1534 self._runway.get_text()!="" and \
1535 self._approachType.get_text()!=""
1536 self._button.set_sensitive(sensitive)
1537
1538 def _backClicked(self, button):
1539 """Called when the Back button is pressed."""
1540 self.goBack()
1541
1542 def _forwardClicked(self, button):
1543 """Called when the forward button is clicked."""
1544 #self._wizard.nextPage()
1545 self.finalize()
1546
1547#-----------------------------------------------------------------------------
1548
1549class Wizard(gtk.VBox):
1550 """The flight wizard."""
1551 def __init__(self, gui):
1552 """Construct the wizard."""
1553 super(Wizard, self).__init__()
1554
1555 self.gui = gui
1556
1557 self._pages = []
1558 self._currentPage = None
1559
1560 self._pages.append(LoginPage(self))
1561 self._pages.append(FlightSelectionPage(self))
1562 self._pages.append(GateSelectionPage(self))
1563 self._pages.append(ConnectPage(self))
1564 self._payloadPage = PayloadPage(self)
1565 self._pages.append(self._payloadPage)
1566 self._pages.append(TimePage(self))
1567 self._routePage = RoutePage(self)
1568 self._pages.append(self._routePage)
1569 self._pages.append(BriefingPage(self, True))
1570 self._pages.append(BriefingPage(self, False))
1571 self._takeoffPage = TakeoffPage(self)
1572 self._pages.append(self._takeoffPage)
1573 self._pages.append(LandingPage(self))
1574
1575 maxWidth = 0
1576 maxHeight = 0
1577 for page in self._pages:
1578 page.show_all()
1579 pageSizeRequest = page.size_request()
1580 width = pageSizeRequest.width if pygobject else pageSizeRequest[0]
1581 height = pageSizeRequest.height if pygobject else pageSizeRequest[1]
1582 maxWidth = max(maxWidth, width)
1583 maxHeight = max(maxHeight, height)
1584 maxWidth += 16
1585 maxHeight += 32
1586 self.set_size_request(maxWidth, maxHeight)
1587
1588 self._initialize()
1589
1590 @property
1591 def loginResult(self):
1592 """Get the login result."""
1593 return self._loginResult
1594
1595 def setCurrentPage(self, index, finalize = False):
1596 """Set the current page to the one with the given index."""
1597 assert index < len(self._pages)
1598
1599 fromPage = self._currentPage
1600 if fromPage is not None:
1601 page = self._pages[fromPage]
1602 if finalize and not page._finalized:
1603 page.finalize()
1604 page._finalized = True
1605 self.remove(page)
1606
1607 self._currentPage = index
1608 page = self._pages[index]
1609 self.add(page)
1610 if page._fromPage is None:
1611 page._fromPage = fromPage
1612 page.activate()
1613 self.show_all()
1614 if fromPage is not None:
1615 self.grabDefault()
1616
1617 @property
1618 def zfw(self):
1619 """Get the calculated ZFW value."""
1620 return 0 if self._bookedFlight is None \
1621 else self._payloadPage.calculateZFW()
1622
1623 @property
1624 def cruiseAltitude(self):
1625 """Get the cruise altitude."""
1626 return self._routePage.cruiseLevel * 100
1627
1628 @property
1629 def v1(self):
1630 """Get the V1 speed."""
1631 return None if self._bookedFlight is None else self._takeoffPage.v1
1632
1633 @property
1634 def vr(self):
1635 """Get the Vr speed."""
1636 return None if self._bookedFlight is None else self._takeoffPage.vr
1637
1638 @property
1639 def v2(self):
1640 """Get the V2 speed."""
1641 return None if self._bookedFlight is None else self._takeoffPage.v2
1642
1643 def nextPage(self, finalize = True):
1644 """Go to the next page."""
1645 self.jumpPage(1, finalize)
1646
1647 def jumpPage(self, count, finalize = True):
1648 """Go to the page which is 'count' pages after the current one."""
1649 self.setCurrentPage(self._currentPage + count, finalize = finalize)
1650
1651 def grabDefault(self):
1652 """Make the default button of the current page the default."""
1653 self._pages[self._currentPage].grabDefault()
1654
1655 def connected(self, fsType, descriptor):
1656 """Called when the connection could be made to the simulator."""
1657 self.nextPage()
1658
1659 def connectionFailed(self):
1660 """Called when the connection could not be made to the simulator."""
1661 self._initialize()
1662
1663 def disconnected(self):
1664 """Called when we have disconnected from the simulator."""
1665 self._initialize()
1666
1667 def setStage(self, stage):
1668 """Set the flight stage to the given one."""
1669 if stage==const.STAGE_TAKEOFF:
1670 self._takeoffPage.freezeValues()
1671
1672 def _initialize(self):
1673 """Initialize the wizard."""
1674 self._fleet = None
1675 self._fleetCallback = None
1676 self._updatePlaneCallback = None
1677
1678 self._loginResult = None
1679 self._bookedFlight = None
1680 self._departureGate = "-"
1681 self._departureNOTAMs = None
1682 self._departureMETAR = None
1683 self._arrivalNOTAMs = None
1684 self._arrivalMETAR = None
1685
1686 for page in self._pages:
1687 page.reset()
1688
1689 self.setCurrentPage(0)
1690
1691 def _getFleet(self, callback, force = False):
1692 """Get the fleet, if needed.
1693
1694 callback is function that will be called, when the feet is retrieved,
1695 or the retrieval fails. It should have a single argument that will
1696 receive the fleet object on success, None otherwise.
1697 """
1698 if self._fleet is not None and not force:
1699 callback(self._fleet)
1700
1701 self.gui.beginBusy("Retrieving fleet...")
1702 self._fleetCallback = callback
1703 self.gui.webHandler.getFleet(self._fleetResultCallback)
1704
1705 def _fleetResultCallback(self, returned, result):
1706 """Called when the fleet has been queried."""
1707 gobject.idle_add(self._handleFleetResult, returned, result)
1708
1709 def _handleFleetResult(self, returned, result):
1710 """Handle the fleet result."""
1711 self.gui.endBusy()
1712 if returned:
1713 self._fleet = result.fleet
1714 else:
1715 self._fleet = None
1716
1717 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
1718 buttons = BUTTONSTYPE_OK,
1719 message_format =
1720 "Failed to retrieve the information on "
1721 "the fleet.")
1722 dialog.run()
1723 dialog.hide()
1724
1725 self._fleetCallback(self._fleet)
1726
1727 def _updatePlane(self, callback, tailNumber, status, gateNumber = None):
1728 """Update the given plane's gate information."""
1729 self.gui.beginBusy("Updating plane status...")
1730 self._updatePlaneCallback = callback
1731 self.gui.webHandler.updatePlane(self._updatePlaneResultCallback,
1732 tailNumber, status, gateNumber)
1733
1734 def _updatePlaneResultCallback(self, returned, result):
1735 """Callback for the plane updating operation."""
1736 gobject.idle_add(self._handleUpdatePlaneResult, returned, result)
1737
1738 def _handleUpdatePlaneResult(self, returned, result):
1739 """Handle the result of a plane update operation."""
1740 self.gui.endBusy()
1741 if returned:
1742 success = result.success
1743 else:
1744 success = None
1745
1746 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
1747 buttons = BUTTONSTYPE_OK,
1748 message_format =
1749 "Failed to update the statuis of "
1750 "the airplane.")
1751 dialog.run()
1752 dialog.hide()
1753
1754 self._updatePlaneCallback(success)
1755
1756 def _connectSimulator(self):
1757 """Connect to the simulator."""
1758 self.gui.connectSimulator(self._bookedFlight.aircraftType)
1759
1760#-----------------------------------------------------------------------------
1761
Note: See TracBrowser for help on using the repository browser.