source: src/mlx/gui/flight.py@ 80:d3ca14051108

Last change on this file since 80:d3ca14051108 was 80:d3ca14051108, checked in by István Váradi <ivaradi@…>, 13 years ago

The simulator time is displayed in the status bar

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