source: src/mlx/gui/flight.py@ 88:3fdb6ad947ad

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

The logger disconnects from the simulator if the flight has ended and the Forward button on the landing page can be activated only then.

File size: 67.0 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.connect("integer-changed", self._cargoWeightChanged)
689 self._cargoWeight.set_tooltip_text("The weight of the cargo for your flight.")
690 table.attach(self._cargoWeight, 1, 2, 3, 4)
691 label.set_mnemonic_widget(self._cargoWeight)
692
693 table.attach(gtk.Label("kg"), 2, 3, 3, 4)
694
695 label = gtk.Label("Mail:")
696 label.set_alignment(0.0, 0.5)
697 table.attach(label, 0, 1, 4, 5)
698
699 self._mailWeight = gtk.Label()
700 self._mailWeight.set_width_chars(6)
701 self._mailWeight.set_alignment(1.0, 0.5)
702 table.attach(self._mailWeight, 1, 2, 4, 5)
703
704 table.attach(gtk.Label("kg"), 2, 3, 4, 5)
705
706 label = gtk.Label("<b>Calculated ZFW:</b>")
707 label.set_alignment(0.0, 0.5)
708 label.set_use_markup(True)
709 table.attach(label, 0, 1, 5, 6)
710
711 self._calculatedZFW = gtk.Label()
712 self._calculatedZFW.set_width_chars(6)
713 self._calculatedZFW.set_alignment(1.0, 0.5)
714 table.attach(self._calculatedZFW, 1, 2, 5, 6)
715
716 table.attach(gtk.Label("kg"), 2, 3, 5, 6)
717
718 self._zfwButton = gtk.Button("_ZFW from FS:")
719 self._zfwButton.set_use_underline(True)
720 self._zfwButton.connect("clicked", self._zfwRequested)
721 table.attach(self._zfwButton, 0, 1, 6, 7)
722
723 self._simulatorZFW = gtk.Label("-")
724 self._simulatorZFW.set_width_chars(6)
725 self._simulatorZFW.set_alignment(1.0, 0.5)
726 table.attach(self._simulatorZFW, 1, 2, 6, 7)
727 self._simulatorZFWValue = None
728
729 table.attach(gtk.Label("kg"), 2, 3, 6, 7)
730
731 self._backButton = self.addButton(gtk.STOCK_GO_BACK)
732 self._backButton.set_use_stock(True)
733 self._backButton.connect("clicked", self._backClicked)
734
735 self._button = self.addButton(gtk.STOCK_GO_FORWARD, default = True)
736 self._button.set_use_stock(True)
737 self._button.connect("clicked", self._forwardClicked)
738
739 def activate(self):
740 """Setup the information."""
741 bookedFlight = self._wizard._bookedFlight
742 self._numCrew.set_text(str(bookedFlight.numCrew))
743 self._numPassengers.set_text(str(bookedFlight.numPassengers))
744 self._bagWeight.set_text(str(bookedFlight.bagWeight))
745 self._cargoWeight.set_int(bookedFlight.cargoWeight)
746 self._cargoWeight.set_sensitive(True)
747 self._mailWeight.set_text(str(bookedFlight.mailWeight))
748 self._zfwButton.set_sensitive(True)
749 self._updateCalculatedZFW()
750
751 def finalize(self):
752 """Finalize the payload page."""
753 self._cargoWeight.set_sensitive(False)
754 self._zfwButton.set_sensitive(False)
755
756 def calculateZFW(self):
757 """Calculate the ZFW value."""
758 zfw = self._wizard.gui._flight.aircraft.dow
759 bookedFlight = self._wizard._bookedFlight
760 zfw += (bookedFlight.numCrew + bookedFlight.numPassengers) * 82
761 zfw += bookedFlight.bagWeight
762 zfw += self._cargoWeight.get_int()
763 zfw += bookedFlight.mailWeight
764 return zfw
765
766 def _updateCalculatedZFW(self):
767 """Update the calculated ZFW"""
768 zfw = self.calculateZFW()
769
770 markupBegin = "<b>"
771 markupEnd = "</b>"
772 if self._simulatorZFWValue is not None and \
773 PayloadChecker.isZFWFaulty(self._simulatorZFWValue, zfw):
774 markupBegin += '<span foreground="red">'
775 markupEnd = "</span>" + markupEnd
776 self._calculatedZFW.set_markup(markupBegin + str(zfw) + markupEnd)
777
778 def _cargoWeightChanged(self, entry, weight):
779 """Called when the cargo weight has changed."""
780 self._updateCalculatedZFW()
781
782 def _zfwRequested(self, button):
783 """Called when the ZFW is requested from the simulator."""
784 self._zfwButton.set_sensitive(False)
785 self._backButton.set_sensitive(False)
786 self._button.set_sensitive(False)
787 gui = self._wizard.gui
788 gui.beginBusy("Querying ZFW...")
789 gui.simulator.requestZFW(self._handleZFW)
790
791 def _handleZFW(self, zfw):
792 """Called when the ZFW value is retrieved."""
793 gobject.idle_add(self._processZFW, zfw)
794
795 def _processZFW(self, zfw):
796 """Process the given ZFW value received from the simulator."""
797 self._wizard.gui.endBusy()
798 self._zfwButton.set_sensitive(True)
799 self._backButton.set_sensitive(True)
800 self._button.set_sensitive(True)
801 self._simulatorZFWValue = zfw
802 self._simulatorZFW.set_text("%.0f" % (zfw,))
803 self._updateCalculatedZFW()
804
805 def _forwardClicked(self, button):
806 """Called when the forward button is clicked."""
807 self._wizard.nextPage()
808
809 def _backClicked(self, button):
810 """Called when the Back button is pressed."""
811 self.goBack()
812
813#-----------------------------------------------------------------------------
814
815class TimePage(Page):
816 """Page displaying the departure and arrival times and allows querying the
817 current time from the flight simulator."""
818 def __init__(self, wizard):
819 help = "The departure and arrival times are displayed below in UTC.\n\n" \
820 "You can also query the current UTC time from the simulator.\n" \
821 "Ensure that you have enough time to properly prepare for the flight."
822
823 super(TimePage, self).__init__(wizard, "Time", help)
824
825 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
826 xscale = 0.0, yscale = 0.0)
827
828 table = gtk.Table(3, 2)
829 table.set_row_spacings(4)
830 table.set_col_spacings(16)
831 table.set_homogeneous(False)
832 alignment.add(table)
833 self.setMainWidget(alignment)
834
835 label = gtk.Label("Departure:")
836 label.set_alignment(0.0, 0.5)
837 table.attach(label, 0, 1, 0, 1)
838
839 self._departure = gtk.Label()
840 self._departure.set_alignment(0.0, 0.5)
841 table.attach(self._departure, 1, 2, 0, 1)
842
843 label = gtk.Label("Arrival:")
844 label.set_alignment(0.0, 0.5)
845 table.attach(label, 0, 1, 1, 2)
846
847 self._arrival = gtk.Label()
848 self._arrival.set_alignment(0.0, 0.5)
849 table.attach(self._arrival, 1, 2, 1, 2)
850
851 self._timeButton = gtk.Button("_Time from FS:")
852 self._timeButton.set_use_underline(True)
853 self._timeButton.connect("clicked", self._timeRequested)
854 table.attach(self._timeButton, 0, 1, 2, 3)
855
856 self._simulatorTime = gtk.Label("-")
857 self._simulatorTime.set_alignment(0.0, 0.5)
858 table.attach(self._simulatorTime, 1, 2, 2, 3)
859
860 self._backButton = self.addButton(gtk.STOCK_GO_BACK)
861 self._backButton.set_use_stock(True)
862 self._backButton.connect("clicked", self._backClicked)
863
864 self._button = self.addButton(gtk.STOCK_GO_FORWARD, default = True)
865 self._button.set_use_stock(True)
866 self._button.connect("clicked", self._forwardClicked)
867
868 def activate(self):
869 """Activate the page."""
870 self._timeButton.set_sensitive(True)
871 bookedFlight = self._wizard._bookedFlight
872 self._departure.set_text(str(bookedFlight.departureTime.time()))
873 self._arrival.set_text(str(bookedFlight.arrivalTime.time()))
874
875 def finalize(self):
876 """Finalize the page."""
877 self._timeButton.set_sensitive(False)
878
879 def _timeRequested(self, button):
880 """Request the time from the simulator."""
881 self._timeButton.set_sensitive(False)
882 self._backButton.set_sensitive(False)
883 self._button.set_sensitive(False)
884 self._wizard.gui.beginBusy("Querying time...")
885 self._wizard.gui.simulator.requestTime(self._handleTime)
886
887 def _handleTime(self, timestamp):
888 """Handle the result of a time retrieval."""
889 gobject.idle_add(self._processTime, timestamp)
890
891 def _processTime(self, timestamp):
892 """Process the given time."""
893 self._wizard.gui.endBusy()
894 self._timeButton.set_sensitive(True)
895 self._backButton.set_sensitive(True)
896 self._button.set_sensitive(True)
897 tm = time.gmtime(timestamp)
898 t = datetime.time(tm.tm_hour, tm.tm_min, tm.tm_sec)
899 self._simulatorTime.set_text(str(t))
900
901 ts = tm.tm_hour * 3600 + tm.tm_min * 60 + tm.tm_sec
902 dt = self._wizard._bookedFlight.departureTime.time()
903 dts = dt.hour * 3600 + dt.minute * 60 + dt.second
904 diff = dts-ts
905
906 markupBegin = ""
907 markupEnd = ""
908 if diff < 0:
909 markupBegin = '<b><span foreground="red">'
910 markupEnd = '</span></b>'
911 elif diff < 3*60 or diff > 30*60:
912 markupBegin = '<b><span foreground="orange">'
913 markupEnd = '</span></b>'
914
915 self._departure.set_markup(markupBegin + str(dt) + markupEnd)
916
917 def _backClicked(self, button):
918 """Called when the Back button is pressed."""
919 self.goBack()
920
921 def _forwardClicked(self, button):
922 """Called when the forward button is clicked."""
923 self._wizard.nextPage()
924
925#-----------------------------------------------------------------------------
926
927class RoutePage(Page):
928 """The page containing the route and the flight level."""
929 def __init__(self, wizard):
930 help = "Set your cruise flight level below, and\n" \
931 "if necessary, edit the flight plan."
932
933 super(RoutePage, self).__init__(wizard, "Route", help)
934
935 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
936 xscale = 0.0, yscale = 0.0)
937
938 mainBox = gtk.VBox()
939 alignment.add(mainBox)
940 self.setMainWidget(alignment)
941
942 levelBox = gtk.HBox()
943
944 label = gtk.Label("_Cruise level")
945 label.set_use_underline(True)
946 levelBox.pack_start(label, True, True, 0)
947
948 self._cruiseLevel = gtk.SpinButton()
949 self._cruiseLevel.set_increments(step = 10, page = 100)
950 self._cruiseLevel.set_range(min = 50, max = 500)
951 self._cruiseLevel.set_value(240)
952 self._cruiseLevel.set_tooltip_text("The cruise flight level.")
953 self._cruiseLevel.set_numeric(True)
954 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
955 label.set_mnemonic_widget(self._cruiseLevel)
956
957 levelBox.pack_start(self._cruiseLevel, False, False, 8)
958
959 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
960 xscale = 0.0, yscale = 0.0)
961 alignment.add(levelBox)
962
963 mainBox.pack_start(alignment, False, False, 0)
964
965
966 routeBox = gtk.VBox()
967
968 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
969 xscale = 0.0, yscale = 0.0)
970 label = gtk.Label("_Route")
971 label.set_use_underline(True)
972 alignment.add(label)
973 routeBox.pack_start(alignment, True, True, 0)
974
975 routeWindow = gtk.ScrolledWindow()
976 routeWindow.set_size_request(400, 80)
977 routeWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
978 else gtk.SHADOW_IN)
979 routeWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
980 else gtk.POLICY_AUTOMATIC,
981 gtk.PolicyType.AUTOMATIC if pygobject
982 else gtk.POLICY_AUTOMATIC)
983
984 self._route = gtk.TextView()
985 self._route.set_tooltip_text("The planned flight route.")
986 self._route.get_buffer().connect("changed", self._routeChanged)
987 routeWindow.add(self._route)
988
989 label.set_mnemonic_widget(self._route)
990 routeBox.pack_start(routeWindow, True, True, 0)
991
992 mainBox.pack_start(routeBox, True, True, 8)
993
994 self._backButton = self.addButton(gtk.STOCK_GO_BACK)
995 self._backButton.set_use_stock(True)
996 self._backButton.connect("clicked", self._backClicked)
997
998 self._button = self.addButton(gtk.STOCK_GO_FORWARD, default = True)
999 self._button.set_use_stock(True)
1000 self._button.connect("clicked", self._forwardClicked)
1001
1002 @property
1003 def cruiseLevel(self):
1004 """Get the cruise level."""
1005 return self._cruiseLevel.get_value_as_int()
1006
1007 def activate(self):
1008 """Setup the route from the booked flight."""
1009 self._route.set_sensitive(True)
1010 self._cruiseLevel.set_sensitive(True)
1011 self._route.get_buffer().set_text(self._wizard._bookedFlight.route)
1012 self._updateForwardButton()
1013
1014 def finalize(self):
1015 """Finalize the page."""
1016 self._route.set_sensitive(False)
1017 self._cruiseLevel.set_sensitive(False)
1018
1019 def _getRoute(self):
1020 """Get the text of the route."""
1021 buffer = self._route.get_buffer()
1022 return buffer.get_text(buffer.get_start_iter(),
1023 buffer.get_end_iter(), True)
1024
1025 def _updateForwardButton(self):
1026 """Update the sensitivity of the forward button."""
1027 self._button.set_sensitive(self._cruiseLevel.get_value_as_int()>=50 and \
1028 self._getRoute()!="")
1029
1030 def _cruiseLevelChanged(self, spinButton):
1031 """Called when the cruise level has changed."""
1032 self._updateForwardButton()
1033
1034 def _routeChanged(self, textBuffer):
1035 """Called when the route has changed."""
1036 self._updateForwardButton()
1037
1038 def _backClicked(self, button):
1039 """Called when the Back button is pressed."""
1040 self.goBack()
1041
1042 def _forwardClicked(self, button):
1043 """Called when the Forward button is clicked."""
1044 if self._finalized:
1045 self._wizard.nextPage()
1046 else:
1047 self._backButton.set_sensitive(False)
1048 self._button.set_sensitive(False)
1049 self._cruiseLevel.set_sensitive(False)
1050 self._route.set_sensitive(False)
1051
1052 bookedFlight = self._wizard._bookedFlight
1053 self._wizard.gui.beginBusy("Downloading NOTAMs...")
1054 self._wizard.gui.webHandler.getNOTAMs(self._notamsCallback,
1055 bookedFlight.departureICAO,
1056 bookedFlight.arrivalICAO)
1057
1058 def _notamsCallback(self, returned, result):
1059 """Callback for the NOTAMs."""
1060 gobject.idle_add(self._handleNOTAMs, returned, result)
1061
1062 def _handleNOTAMs(self, returned, result):
1063 """Handle the NOTAMs."""
1064 if returned:
1065 self._wizard._departureNOTAMs = result.departureNOTAMs
1066 self._wizard._arrivalNOTAMs = result.arrivalNOTAMs
1067 else:
1068 self._wizard._departureNOTAMs = None
1069 self._wizard._arrivalNOTAMs = None
1070
1071 bookedFlight = self._wizard._bookedFlight
1072 self._wizard.gui.beginBusy("Downloading METARs...")
1073 self._wizard.gui.webHandler.getMETARs(self._metarsCallback,
1074 [bookedFlight.departureICAO,
1075 bookedFlight.arrivalICAO])
1076
1077 def _metarsCallback(self, returned, result):
1078 """Callback for the METARs."""
1079 gobject.idle_add(self._handleMETARs, returned, result)
1080
1081 def _handleMETARs(self, returned, result):
1082 """Handle the METARs."""
1083 self._wizard._departureMETAR = None
1084 self._wizard._arrivalMETAR = None
1085 bookedFlight = self._wizard._bookedFlight
1086 if returned:
1087 if bookedFlight.departureICAO in result.metars:
1088 self._wizard._departureMETAR = result.metars[bookedFlight.departureICAO]
1089 if bookedFlight.arrivalICAO in result.metars:
1090 self._wizard._arrivalMETAR = result.metars[bookedFlight.arrivalICAO]
1091
1092 self._wizard.gui.endBusy()
1093 self._backButton.set_sensitive(True)
1094 self._button.set_sensitive(True)
1095 self._wizard.nextPage()
1096
1097#-----------------------------------------------------------------------------
1098
1099class BriefingPage(Page):
1100 """Page for the briefing."""
1101 def __init__(self, wizard, departure):
1102 """Construct the briefing page."""
1103 self._departure = departure
1104
1105 title = "Briefing (%d/2): %s" % (1 if departure else 2,
1106 "departure" if departure
1107 else "arrival")
1108
1109 help = "Read carefully the NOTAMs and METAR below."
1110
1111 super(BriefingPage, self).__init__(wizard, title, help)
1112
1113 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1114 xscale = 1.0, yscale = 1.0)
1115
1116 mainBox = gtk.VBox()
1117 alignment.add(mainBox)
1118 self.setMainWidget(alignment)
1119
1120 self._notamsFrame = gtk.Frame()
1121 self._notamsFrame.set_label("LHBP NOTAMs")
1122 scrolledWindow = gtk.ScrolledWindow()
1123 scrolledWindow.set_size_request(-1, 128)
1124 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1125 else gtk.POLICY_AUTOMATIC,
1126 gtk.PolicyType.AUTOMATIC if pygobject
1127 else gtk.POLICY_AUTOMATIC)
1128 self._notams = gtk.TextView()
1129 self._notams.set_editable(False)
1130 self._notams.set_accepts_tab(False)
1131 self._notams.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
1132 scrolledWindow.add(self._notams)
1133 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
1134 xscale = 1.0, yscale = 1.0)
1135 alignment.set_padding(padding_top = 4, padding_bottom = 0,
1136 padding_left = 0, padding_right = 0)
1137 alignment.add(scrolledWindow)
1138 self._notamsFrame.add(alignment)
1139 mainBox.pack_start(self._notamsFrame, True, True, 4)
1140
1141 self._metarFrame = gtk.Frame()
1142 self._metarFrame.set_label("LHBP METAR")
1143 scrolledWindow = gtk.ScrolledWindow()
1144 scrolledWindow.set_size_request(-1, 32)
1145 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1146 else gtk.POLICY_AUTOMATIC,
1147 gtk.PolicyType.AUTOMATIC if pygobject
1148 else gtk.POLICY_AUTOMATIC)
1149 self._metar = gtk.TextView()
1150 self._metar.set_editable(False)
1151 self._metar.set_accepts_tab(False)
1152 self._metar.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
1153 scrolledWindow.add(self._metar)
1154 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
1155 xscale = 1.0, yscale = 1.0)
1156 alignment.set_padding(padding_top = 4, padding_bottom = 0,
1157 padding_left = 0, padding_right = 0)
1158 alignment.add(scrolledWindow)
1159 self._metarFrame.add(alignment)
1160 mainBox.pack_start(self._metarFrame, True, True, 4)
1161
1162 button = self.addButton(gtk.STOCK_GO_BACK)
1163 button.set_use_stock(True)
1164 button.connect("clicked", self._backClicked)
1165
1166 self._button = self.addButton(gtk.STOCK_GO_FORWARD, default = True)
1167 self._button.set_use_stock(True)
1168 self._button.connect("clicked", self._forwardClicked)
1169
1170 def activate(self):
1171 """Activate the page."""
1172 if not self._departure:
1173 self._button.set_label("I have read the briefing and am ready to fly!")
1174 self._button.set_use_stock(False)
1175
1176 bookedFlight = self._wizard._bookedFlight
1177
1178 icao = bookedFlight.departureICAO if self._departure \
1179 else bookedFlight.arrivalICAO
1180 notams = self._wizard._departureNOTAMs if self._departure \
1181 else self._wizard._arrivalNOTAMs
1182 metar = self._wizard._departureMETAR if self._departure \
1183 else self._wizard._arrivalMETAR
1184
1185 self._notamsFrame.set_label(icao + " NOTAMs")
1186 buffer = self._notams.get_buffer()
1187 if notams is None:
1188 buffer.set_text("Could not download NOTAMs")
1189 else:
1190 s = ""
1191 for notam in notams:
1192 s += str(notam.begin)
1193 if notam.end is not None:
1194 s += " - " + str(notam.end)
1195 elif notam.permanent:
1196 s += " - PERMANENT"
1197 s += "\n"
1198 if notam.repeatCycle:
1199 s += "Repeat cycle: " + notam.repeatCycle + "\n"
1200 s += notam.notice + "\n"
1201 s += "-------------------- * --------------------\n"
1202 buffer.set_text(s)
1203
1204 self._metarFrame.set_label(icao + " METAR")
1205 buffer = self._metar.get_buffer()
1206 if metar is None:
1207 buffer.set_text("Could not download METAR")
1208 else:
1209 buffer.set_text(metar)
1210
1211 def _backClicked(self, button):
1212 """Called when the Back button is pressed."""
1213 self.goBack()
1214
1215 def _forwardClicked(self, button):
1216 """Called when the forward button is clicked."""
1217 if not self._departure:
1218 if not self._finalized:
1219 self._wizard.gui.startMonitoring()
1220 self._button.set_use_stock(True)
1221 self._button.set_label(gtk.STOCK_GO_FORWARD)
1222 self._finalized = True
1223
1224 self._wizard.nextPage()
1225
1226#-----------------------------------------------------------------------------
1227
1228class TakeoffPage(Page):
1229 """Page for entering the takeoff data."""
1230 def __init__(self, wizard):
1231 """Construct the takeoff page."""
1232 help = "Enter the runway and SID used, as well as the speeds."
1233
1234 super(TakeoffPage, self).__init__(wizard, "Takeoff", help)
1235
1236 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1237 xscale = 0.0, yscale = 0.0)
1238
1239 table = gtk.Table(5, 4)
1240 table.set_row_spacings(4)
1241 table.set_col_spacings(16)
1242 table.set_homogeneous(False)
1243 alignment.add(table)
1244 self.setMainWidget(alignment)
1245
1246 label = gtk.Label("Run_way:")
1247 label.set_use_underline(True)
1248 label.set_alignment(0.0, 0.5)
1249 table.attach(label, 0, 1, 0, 1)
1250
1251 self._runway = gtk.Entry()
1252 self._runway.set_width_chars(10)
1253 self._runway.set_tooltip_text("The runway the takeoff is performed from.")
1254 table.attach(self._runway, 1, 3, 0, 1)
1255 label.set_mnemonic_widget(self._runway)
1256
1257 label = gtk.Label("_SID:")
1258 label.set_use_underline(True)
1259 label.set_alignment(0.0, 0.5)
1260 table.attach(label, 0, 1, 1, 2)
1261
1262 self._sid = gtk.Entry()
1263 self._sid.set_width_chars(10)
1264 self._sid.set_tooltip_text("The name of the Standard Instrument Deparature procedure followed.")
1265 table.attach(self._sid, 1, 3, 1, 2)
1266 label.set_mnemonic_widget(self._sid)
1267
1268 label = gtk.Label("V<sub>_1</sub>:")
1269 label.set_use_markup(True)
1270 label.set_use_underline(True)
1271 label.set_alignment(0.0, 0.5)
1272 table.attach(label, 0, 1, 2, 3)
1273
1274 self._v1 = IntegerEntry()
1275 self._v1.set_width_chars(4)
1276 self._v1.set_tooltip_markup("The takeoff decision speed in knots.")
1277 table.attach(self._v1, 2, 3, 2, 3)
1278 label.set_mnemonic_widget(self._v1)
1279
1280 table.attach(gtk.Label("knots"), 3, 4, 2, 3)
1281
1282 label = gtk.Label("V<sub>_R</sub>:")
1283 label.set_use_markup(True)
1284 label.set_use_underline(True)
1285 label.set_alignment(0.0, 0.5)
1286 table.attach(label, 0, 1, 3, 4)
1287
1288 self._vr = IntegerEntry()
1289 self._vr.set_width_chars(4)
1290 self._vr.set_tooltip_markup("The takeoff rotation speed in knots.")
1291 table.attach(self._vr, 2, 3, 3, 4)
1292 label.set_mnemonic_widget(self._vr)
1293
1294 table.attach(gtk.Label("knots"), 3, 4, 3, 4)
1295
1296 label = gtk.Label("V<sub>_2</sub>:")
1297 label.set_use_markup(True)
1298 label.set_use_underline(True)
1299 label.set_alignment(0.0, 0.5)
1300 table.attach(label, 0, 1, 4, 5)
1301
1302 self._v2 = IntegerEntry()
1303 self._v2.set_width_chars(4)
1304 self._v2.set_tooltip_markup("The takeoff safety speed in knots.")
1305 table.attach(self._v2, 2, 3, 4, 5)
1306 label.set_mnemonic_widget(self._v2)
1307
1308 table.attach(gtk.Label("knots"), 3, 4, 4, 5)
1309
1310 button = self.addButton(gtk.STOCK_GO_BACK)
1311 button.set_use_stock(True)
1312 button.connect("clicked", self._backClicked)
1313
1314 self._button = self.addButton(gtk.STOCK_GO_FORWARD, default = True)
1315 self._button.set_use_stock(True)
1316 self._button.connect("clicked", self._forwardClicked)
1317
1318 @property
1319 def v1(self):
1320 """Get the v1 speed."""
1321 return self._v1.get_int()
1322
1323 @property
1324 def vr(self):
1325 """Get the vr speed."""
1326 return self._vr.get_int()
1327
1328 @property
1329 def v2(self):
1330 """Get the v2 speed."""
1331 return self._v2.get_int()
1332
1333 def activate(self):
1334 """Activate the page."""
1335 self._runway.set_text("")
1336 self._runway.set_sensitive(True)
1337 self._sid.set_text("")
1338 self._sid.set_sensitive(True)
1339 self._v1.set_int(None)
1340 self._v1.set_sensitive(True)
1341 self._vr.set_int(None)
1342 self._vr.set_sensitive(True)
1343 self._v2.set_int(None)
1344 self._v2.set_sensitive(True)
1345 self._button.set_sensitive(False)
1346
1347 def freezeValues(self):
1348 """Freeze the values on the page, and enable the forward button."""
1349 self._runway.set_sensitive(False)
1350 self._sid.set_sensitive(False)
1351 self._v1.set_sensitive(False)
1352 self._vr.set_sensitive(False)
1353 self._v2.set_sensitive(False)
1354 self._button.set_sensitive(True)
1355
1356 def _backClicked(self, button):
1357 """Called when the Back button is pressed."""
1358 self.goBack()
1359
1360 def _forwardClicked(self, button):
1361 """Called when the forward button is clicked."""
1362 self._wizard.nextPage()
1363
1364#-----------------------------------------------------------------------------
1365
1366class LandingPage(Page):
1367 """Page for entering landing data."""
1368 def __init__(self, wizard):
1369 """Construct the landing page."""
1370 help = "Enter the STAR and/or transition, runway,\n" \
1371 "approach type and V<sub>Ref</sub> used."
1372
1373 super(LandingPage, self).__init__(wizard, "Landing", help)
1374
1375 self._flightEnded = False
1376
1377 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1378 xscale = 0.0, yscale = 0.0)
1379
1380 table = gtk.Table(5, 5)
1381 table.set_row_spacings(4)
1382 table.set_col_spacings(16)
1383 table.set_homogeneous(False)
1384 alignment.add(table)
1385 self.setMainWidget(alignment)
1386
1387 self._starButton = gtk.CheckButton()
1388 self._starButton.connect("clicked", self._starButtonClicked)
1389 table.attach(self._starButton, 0, 1, 0, 1)
1390
1391 label = gtk.Label("_STAR:")
1392 label.set_use_underline(True)
1393 label.set_alignment(0.0, 0.5)
1394 table.attach(label, 1, 2, 0, 1)
1395
1396 self._star = gtk.Entry()
1397 self._star.set_width_chars(10)
1398 self._star.set_tooltip_text("The name of Standard Terminal Arrival Route followed.")
1399 self._star.connect("changed", self._updateForwardButton)
1400 self._star.set_sensitive(False)
1401 table.attach(self._star, 2, 4, 0, 1)
1402 label.set_mnemonic_widget(self._starButton)
1403
1404 self._transitionButton = gtk.CheckButton()
1405 self._transitionButton.connect("clicked", self._transitionButtonClicked)
1406 table.attach(self._transitionButton, 0, 1, 1, 2)
1407
1408 label = gtk.Label("_Transition:")
1409 label.set_use_underline(True)
1410 label.set_alignment(0.0, 0.5)
1411 table.attach(label, 1, 2, 1, 2)
1412
1413 self._transition = gtk.Entry()
1414 self._transition.set_width_chars(10)
1415 self._transition.set_tooltip_text("The name of transition executed or VECTORS if vectored by ATC.")
1416 self._transition.connect("changed", self._updateForwardButton)
1417 self._transition.set_sensitive(False)
1418 table.attach(self._transition, 2, 4, 1, 2)
1419 label.set_mnemonic_widget(self._transitionButton)
1420
1421 label = gtk.Label("Run_way:")
1422 label.set_use_underline(True)
1423 label.set_alignment(0.0, 0.5)
1424 table.attach(label, 1, 2, 2, 3)
1425
1426 self._runway = gtk.Entry()
1427 self._runway.set_width_chars(10)
1428 self._runway.set_tooltip_text("The runway the landing is performed on.")
1429 self._runway.connect("changed", self._updateForwardButton)
1430 table.attach(self._runway, 2, 4, 2, 3)
1431 label.set_mnemonic_widget(self._runway)
1432
1433 label = gtk.Label("_Approach type:")
1434 label.set_use_underline(True)
1435 label.set_alignment(0.0, 0.5)
1436 table.attach(label, 1, 2, 3, 4)
1437
1438 self._approachType = gtk.Entry()
1439 self._approachType.set_width_chars(10)
1440 self._approachType.set_tooltip_text("The type of the approach, e.g. ILS or VISUAL.")
1441 self._approachType.connect("changed", self._updateForwardButton)
1442 table.attach(self._approachType, 2, 4, 3, 4)
1443 label.set_mnemonic_widget(self._approachType)
1444
1445 label = gtk.Label("V<sub>_Ref</sub>:")
1446 label.set_use_markup(True)
1447 label.set_use_underline(True)
1448 label.set_alignment(0.0, 0.5)
1449 table.attach(label, 1, 2, 5, 6)
1450
1451 self._vref = IntegerEntry()
1452 self._vref.set_width_chars(5)
1453 self._vref.set_tooltip_markup("The approach reference speed in knots.")
1454 self._vref.connect("integer-changed", self._vrefChanged)
1455 table.attach(self._vref, 3, 4, 5, 6)
1456 label.set_mnemonic_widget(self._vref)
1457
1458 table.attach(gtk.Label("knots"), 4, 5, 5, 6)
1459
1460 button = self.addButton(gtk.STOCK_GO_BACK)
1461 button.set_use_stock(True)
1462 button.connect("clicked", self._backClicked)
1463
1464 self._button = self.addButton(gtk.STOCK_GO_FORWARD, default = True)
1465 self._button.set_use_stock(True)
1466 self._button.connect("clicked", self._forwardClicked)
1467
1468 # These are needed for correct size calculations
1469 self._starButton.set_active(True)
1470 self._transitionButton.set_active(True)
1471
1472 @property
1473 def vref(self):
1474 """Return the landing reference speed."""
1475 return self._vref.get_int()
1476
1477 def activate(self):
1478 """Called when the page is activated."""
1479 self._flightEnded = False
1480
1481 self._starButton.set_sensitive(True)
1482 self._starButton.set_active(False)
1483 self._star.set_text("")
1484
1485 self._transitionButton.set_sensitive(True)
1486 self._transitionButton.set_active(False)
1487 self._transition.set_text("")
1488
1489 self._runway.set_text("")
1490 self._runway.set_sensitive(True)
1491
1492 self._approachType.set_text("")
1493 self._approachType.set_sensitive(True)
1494
1495 self._vref.set_int(None)
1496 self._vref.set_sensitive(True)
1497
1498 self._updateForwardButton()
1499
1500 def flightEnded(self):
1501 """Called when the flight has ended."""
1502 self._flightEnded = True
1503 self._updateForwardButton()
1504
1505 def finalize(self):
1506 """Finalize the page."""
1507 self._starButton.set_sensitive(False)
1508 self._star.set_sensitive(False)
1509
1510 self._transitionButton.set_sensitive(False)
1511 self._transition.set_sensitive(False)
1512
1513 self._runway.set_sensitive(False)
1514
1515 self._approachType.set_sensitive(False)
1516
1517 self._vref.set_sensitive(False)
1518
1519 def _starButtonClicked(self, button):
1520 """Called when the STAR button is clicked."""
1521 active = button.get_active()
1522 self._star.set_sensitive(active)
1523 if active:
1524 self._star.grab_focus()
1525 self._updateForwardButton()
1526
1527 def _transitionButtonClicked(self, button):
1528 """Called when the Transition button is clicked."""
1529 active = button.get_active()
1530 self._transition.set_sensitive(active)
1531 if active:
1532 self._transition.grab_focus()
1533 self._updateForwardButton()
1534
1535 def _updateForwardButton(self, widget = None):
1536 """Update the sensitivity of the forward button."""
1537 sensitive = self._flightEnded and \
1538 (self._starButton.get_active() or \
1539 self._transitionButton.get_active()) and \
1540 (self._star.get_text()!="" or
1541 not self._starButton.get_active()) and \
1542 (self._transition.get_text()!="" or
1543 not self._transitionButton.get_active()) and \
1544 self._runway.get_text()!="" and \
1545 self._approachType.get_text()!="" and \
1546 self.vref is not None
1547 self._button.set_sensitive(sensitive)
1548
1549 def _vrefChanged(self, widget, value):
1550 """Called when the Vref has changed."""
1551 self._updateForwardButton()
1552
1553 def _backClicked(self, button):
1554 """Called when the Back button is pressed."""
1555 self.goBack()
1556
1557 def _forwardClicked(self, button):
1558 """Called when the forward button is clicked."""
1559 #self._wizard.nextPage()
1560 self.finalize()
1561
1562#-----------------------------------------------------------------------------
1563
1564class Wizard(gtk.VBox):
1565 """The flight wizard."""
1566 def __init__(self, gui):
1567 """Construct the wizard."""
1568 super(Wizard, self).__init__()
1569
1570 self.gui = gui
1571
1572 self._pages = []
1573 self._currentPage = None
1574
1575 self._pages.append(LoginPage(self))
1576 self._pages.append(FlightSelectionPage(self))
1577 self._pages.append(GateSelectionPage(self))
1578 self._pages.append(ConnectPage(self))
1579 self._payloadPage = PayloadPage(self)
1580 self._pages.append(self._payloadPage)
1581 self._pages.append(TimePage(self))
1582 self._routePage = RoutePage(self)
1583 self._pages.append(self._routePage)
1584 self._pages.append(BriefingPage(self, True))
1585 self._pages.append(BriefingPage(self, False))
1586 self._takeoffPage = TakeoffPage(self)
1587 self._pages.append(self._takeoffPage)
1588 self._landingPage = LandingPage(self)
1589 self._pages.append(self._landingPage)
1590
1591 maxWidth = 0
1592 maxHeight = 0
1593 for page in self._pages:
1594 page.show_all()
1595 pageSizeRequest = page.size_request()
1596 width = pageSizeRequest.width if pygobject else pageSizeRequest[0]
1597 height = pageSizeRequest.height if pygobject else pageSizeRequest[1]
1598 maxWidth = max(maxWidth, width)
1599 maxHeight = max(maxHeight, height)
1600 maxWidth += 16
1601 maxHeight += 32
1602 self.set_size_request(maxWidth, maxHeight)
1603
1604 self._initialize()
1605
1606 @property
1607 def loginResult(self):
1608 """Get the login result."""
1609 return self._loginResult
1610
1611 def setCurrentPage(self, index, finalize = False):
1612 """Set the current page to the one with the given index."""
1613 assert index < len(self._pages)
1614
1615 fromPage = self._currentPage
1616 if fromPage is not None:
1617 page = self._pages[fromPage]
1618 if finalize and not page._finalized:
1619 page.finalize()
1620 page._finalized = True
1621 self.remove(page)
1622
1623 self._currentPage = index
1624 page = self._pages[index]
1625 self.add(page)
1626 if page._fromPage is None:
1627 page._fromPage = fromPage
1628 page.activate()
1629 self.show_all()
1630 if fromPage is not None:
1631 self.grabDefault()
1632
1633 @property
1634 def zfw(self):
1635 """Get the calculated ZFW value."""
1636 return 0 if self._bookedFlight is None \
1637 else self._payloadPage.calculateZFW()
1638
1639 @property
1640 def cruiseAltitude(self):
1641 """Get the cruise altitude."""
1642 return self._routePage.cruiseLevel * 100
1643
1644 @property
1645 def v1(self):
1646 """Get the V1 speed."""
1647 return self._takeoffPage.v1
1648
1649 @property
1650 def vr(self):
1651 """Get the Vr speed."""
1652 return self._takeoffPage.vr
1653
1654 @property
1655 def v2(self):
1656 """Get the V2 speed."""
1657 return self._takeoffPage.v2
1658
1659 @property
1660 def vref(self):
1661 """Get the Vref speed."""
1662 return self._landingPage.vref
1663
1664 def nextPage(self, finalize = True):
1665 """Go to the next page."""
1666 self.jumpPage(1, finalize)
1667
1668 def jumpPage(self, count, finalize = True):
1669 """Go to the page which is 'count' pages after the current one."""
1670 self.setCurrentPage(self._currentPage + count, finalize = finalize)
1671
1672 def grabDefault(self):
1673 """Make the default button of the current page the default."""
1674 self._pages[self._currentPage].grabDefault()
1675
1676 def connected(self, fsType, descriptor):
1677 """Called when the connection could be made to the simulator."""
1678 self.nextPage()
1679
1680 def connectionFailed(self):
1681 """Called when the connection could not be made to the simulator."""
1682 self._initialize()
1683
1684 def disconnected(self):
1685 """Called when we have disconnected from the simulator."""
1686 self._initialize()
1687
1688 def setStage(self, stage):
1689 """Set the flight stage to the given one."""
1690 if stage==const.STAGE_TAKEOFF:
1691 self._takeoffPage.freezeValues()
1692 elif stage==const.STAGE_END:
1693 self._landingPage.flightEnded()
1694
1695 def _initialize(self):
1696 """Initialize the wizard."""
1697 self._fleet = None
1698 self._fleetCallback = None
1699 self._updatePlaneCallback = None
1700
1701 self._loginResult = None
1702 self._bookedFlight = None
1703 self._departureGate = "-"
1704 self._departureNOTAMs = None
1705 self._departureMETAR = None
1706 self._arrivalNOTAMs = None
1707 self._arrivalMETAR = None
1708
1709 for page in self._pages:
1710 page.reset()
1711
1712 self.setCurrentPage(0)
1713
1714 def _getFleet(self, callback, force = False):
1715 """Get the fleet, if needed.
1716
1717 callback is function that will be called, when the feet is retrieved,
1718 or the retrieval fails. It should have a single argument that will
1719 receive the fleet object on success, None otherwise.
1720 """
1721 if self._fleet is not None and not force:
1722 callback(self._fleet)
1723
1724 self.gui.beginBusy("Retrieving fleet...")
1725 self._fleetCallback = callback
1726 self.gui.webHandler.getFleet(self._fleetResultCallback)
1727
1728 def _fleetResultCallback(self, returned, result):
1729 """Called when the fleet has been queried."""
1730 gobject.idle_add(self._handleFleetResult, returned, result)
1731
1732 def _handleFleetResult(self, returned, result):
1733 """Handle the fleet result."""
1734 self.gui.endBusy()
1735 if returned:
1736 self._fleet = result.fleet
1737 else:
1738 self._fleet = None
1739
1740 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
1741 buttons = BUTTONSTYPE_OK,
1742 message_format =
1743 "Failed to retrieve the information on "
1744 "the fleet.")
1745 dialog.run()
1746 dialog.hide()
1747
1748 self._fleetCallback(self._fleet)
1749
1750 def _updatePlane(self, callback, tailNumber, status, gateNumber = None):
1751 """Update the given plane's gate information."""
1752 self.gui.beginBusy("Updating plane status...")
1753 self._updatePlaneCallback = callback
1754 self.gui.webHandler.updatePlane(self._updatePlaneResultCallback,
1755 tailNumber, status, gateNumber)
1756
1757 def _updatePlaneResultCallback(self, returned, result):
1758 """Callback for the plane updating operation."""
1759 gobject.idle_add(self._handleUpdatePlaneResult, returned, result)
1760
1761 def _handleUpdatePlaneResult(self, returned, result):
1762 """Handle the result of a plane update operation."""
1763 self.gui.endBusy()
1764 if returned:
1765 success = result.success
1766 else:
1767 success = None
1768
1769 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
1770 buttons = BUTTONSTYPE_OK,
1771 message_format =
1772 "Failed to update the statuis of "
1773 "the airplane.")
1774 dialog.run()
1775 dialog.hide()
1776
1777 self._updatePlaneCallback(success)
1778
1779 def _connectSimulator(self):
1780 """Connect to the simulator."""
1781 self.gui.connectSimulator(self._bookedFlight.aircraftType)
1782
1783#-----------------------------------------------------------------------------
1784
Note: See TracBrowser for help on using the repository browser.