source: src/mlx/gui/flight.py@ 77:cc8b178b8102

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

Added a Quit option to the status icon's menu and a confirmation dialog.

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