source: src/mlx/gui/flight.py@ 69:e08dab9f98ed

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

Some cosmetic changes

File size: 46.9 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 alignment.add(label)
67 table.attach(alignment, 0, 1, 0, 1)
68
69 self._mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
70 xscale = 1.0, yscale = 1.0)
71 table.attach(self._mainAlignment, 0, 1, 1, 3)
72
73 buttonAlignment = gtk.Alignment(xalign = 1.0, xscale=0.0, yscale = 0.0)
74 buttonAlignment.set_padding(padding_top = 4, padding_bottom = 10,
75 padding_left = 16, padding_right = 16)
76
77 self._buttonBox = gtk.HButtonBox()
78 self._defaultButton = None
79 buttonAlignment.add(self._buttonBox)
80
81 self._vbox.pack_start(buttonAlignment, False, False, 0)
82
83 self._wizard = wizard
84
85 def setMainWidget(self, widget):
86 """Set the given widget as the main one."""
87 self._mainAlignment.add(widget)
88
89 def addButton(self, label, default = False):
90 """Add a button with the given label.
91
92 Return the button object created."""
93 button = gtk.Button(label)
94 self._buttonBox.add(button)
95 button.set_use_underline(True)
96 if default:
97 button.set_can_default(True)
98 self._defaultButton = button
99 return button
100
101 def activate(self):
102 """Called when this page becomes active.
103
104 This default implementation does nothing."""
105 pass
106
107 def grabDefault(self):
108 """If the page has a default button, make it the default one."""
109 if self._defaultButton is not None:
110 self._defaultButton.grab_default()
111
112 def reset(self):
113 """Reset the page if the wizard is reset."""
114 pass
115
116#-----------------------------------------------------------------------------
117
118class LoginPage(Page):
119 """The login page."""
120 def __init__(self, wizard):
121 """Construct the login page."""
122 help = "Enter your MAVA pilot's ID and password to\n" \
123 "log in to the MAVA website and download\n" \
124 "your booked flights."
125 super(LoginPage, self).__init__(wizard, "Login", help)
126
127 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
128 xscale = 0.0, yscale = 0.0)
129
130 table = gtk.Table(2, 3)
131 table.set_row_spacings(4)
132 table.set_col_spacings(32)
133 alignment.add(table)
134 self.setMainWidget(alignment)
135
136 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
137 label = gtk.Label("Pilot _ID:")
138 label.set_use_underline(True)
139 labelAlignment.add(label)
140 table.attach(labelAlignment, 0, 1, 0, 1)
141
142 self._pilotID = gtk.Entry()
143 self._pilotID.connect("changed", self._setLoginButton)
144 self._pilotID.set_tooltip_text("Enter your MAVA pilot's ID. This "
145 "usually starts with a "
146 "'P' followed by 3 digits.")
147 table.attach(self._pilotID, 1, 2, 0, 1)
148 label.set_mnemonic_widget(self._pilotID)
149
150 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
151 label = gtk.Label("_Password:")
152 label.set_use_underline(True)
153 labelAlignment.add(label)
154 table.attach(labelAlignment, 0, 1, 1, 2)
155
156 self._password = gtk.Entry()
157 self._password.set_visibility(False)
158 self._password.connect("changed", self._setLoginButton)
159 self._password.set_tooltip_text("Enter the password for your pilot's ID")
160 table.attach(self._password, 1, 2, 1, 2)
161 label.set_mnemonic_widget(self._password)
162
163 self._rememberButton = gtk.CheckButton("_Remember password")
164 self._rememberButton.set_use_underline(True)
165 self._rememberButton.set_tooltip_text("If checked, your password will "
166 "be stored, so that you should "
167 "not have to enter it every time. "
168 "Note, however, that the password "
169 "is stored as text, and anybody "
170 "who can access your files will "
171 "be able to read it.")
172 table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
173
174 self._loginButton = self.addButton("_Login", default = True)
175 self._loginButton.set_sensitive(False)
176 self._loginButton.connect("clicked", self._loginClicked)
177 self._loginButton.set_tooltip_text("Click to log in.")
178
179 config = self._wizard.gui.config
180 self._pilotID.set_text(config.pilotID)
181 self._password.set_text(config.password)
182 self._rememberButton.set_active(config.rememberPassword)
183
184 def _setLoginButton(self, entry):
185 """Set the login button's sensitivity.
186
187 The button is sensitive only if both the pilot ID and the password
188 fields contain values."""
189 self._loginButton.set_sensitive(self._pilotID.get_text()!="" and
190 self._password.get_text()!="")
191
192 def _loginClicked(self, button):
193 """Called when the login button was clicked."""
194 self._wizard.gui.beginBusy("Logging in...")
195 self._wizard.gui.webHandler.login(self._loginResultCallback,
196 self._pilotID.get_text(),
197 self._password.get_text())
198
199 def _loginResultCallback(self, returned, result):
200 """The login result callback, called in the web handler's thread."""
201 gobject.idle_add(self._handleLoginResult, returned, result)
202
203 def _handleLoginResult(self, returned, result):
204 """Handle the login result."""
205 self._wizard.gui.endBusy()
206 if returned:
207 if result.loggedIn:
208 config = self._wizard.gui.config
209
210 config.pilotID = self._pilotID.get_text()
211
212 rememberPassword = self._rememberButton.get_active()
213 config.password = self._password.get_text() if rememberPassword \
214 else ""
215
216 config.rememberPassword = rememberPassword
217
218 config.save()
219 self._wizard._loginResult = result
220 self._wizard.nextPage()
221 else:
222 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
223 buttons = BUTTONSTYPE_OK,
224 message_format =
225 "Invalid pilot's ID or password.")
226 dialog.format_secondary_markup("Check the ID and try to reenter"
227 " the password.")
228 dialog.run()
229 dialog.hide()
230 else:
231 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
232 buttons = BUTTONSTYPE_OK,
233 message_format =
234 "Failed to connect to the MAVA website.")
235 dialog.format_secondary_markup("Try again in a few minutes.")
236 dialog.run()
237 dialog.hide()
238
239#-----------------------------------------------------------------------------
240
241class FlightSelectionPage(Page):
242 """The page to select the flight."""
243 def __init__(self, wizard):
244 """Construct the flight selection page."""
245 super(FlightSelectionPage, self).__init__(wizard, "Flight selection",
246 "Select the flight you want "
247 "to perform.")
248
249
250 self._listStore = gtk.ListStore(str, str, str, str)
251 self._flightList = gtk.TreeView(self._listStore)
252 column = gtk.TreeViewColumn("Flight no.", gtk.CellRendererText(),
253 text = 1)
254 column.set_expand(True)
255 self._flightList.append_column(column)
256 column = gtk.TreeViewColumn("Departure time [UTC]", gtk.CellRendererText(),
257 text = 0)
258 column.set_expand(True)
259 self._flightList.append_column(column)
260 column = gtk.TreeViewColumn("From", gtk.CellRendererText(),
261 text = 2)
262 column.set_expand(True)
263 self._flightList.append_column(column)
264 column = gtk.TreeViewColumn("To", gtk.CellRendererText(),
265 text = 3)
266 column.set_expand(True)
267 self._flightList.append_column(column)
268
269 flightSelection = self._flightList.get_selection()
270 flightSelection.connect("changed", self._selectionChanged)
271
272 scrolledWindow = gtk.ScrolledWindow()
273 scrolledWindow.add(self._flightList)
274 scrolledWindow.set_size_request(400, -1)
275 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
276 else gtk.POLICY_AUTOMATIC,
277 gtk.PolicyType.AUTOMATIC if pygobject
278 else gtk.POLICY_AUTOMATIC)
279 scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
280 else gtk.SHADOW_IN)
281
282 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 0.0, yscale = 1.0)
283 alignment.add(scrolledWindow)
284
285 self.setMainWidget(alignment)
286
287 self._button = self.addButton(gtk.STOCK_GO_FORWARD, default = True)
288 self._button.set_use_stock(True)
289 self._button.set_sensitive(False)
290 self._button.connect("clicked", self._forwardClicked)
291
292 self._activated = False
293
294 def activate(self):
295 """Fill the flight list."""
296 if not self._activated:
297 for flight in self._wizard.loginResult.flights:
298 self._listStore.append([str(flight.departureTime),
299 flight.callsign,
300 flight.departureICAO,
301 flight.arrivalICAO])
302 self._activated = True
303
304 def _selectionChanged(self, selection):
305 """Called when the selection is changed."""
306 self._button.set_sensitive(selection.count_selected_rows()==1)
307
308 def _forwardClicked(self, button):
309 """Called when the forward button was clicked."""
310 selection = self._flightList.get_selection()
311 (listStore, iter) = selection.get_selected()
312 path = listStore.get_path(iter)
313 [index] = path.get_indices() if pygobject else path
314
315 flight = self._wizard.loginResult.flights[index]
316 self._wizard._bookedFlight = flight
317
318 self._updateDepartureGate()
319
320 def _updateDepartureGate(self):
321 """Update the departure gate for the booked flight."""
322 flight = self._wizard._bookedFlight
323 if flight.departureICAO=="LHBP":
324 self._wizard._getFleet(self._fleetRetrieved)
325 else:
326 self._wizard.jumpPage(2)
327
328 def _fleetRetrieved(self, fleet):
329 """Called when the fleet has been retrieved."""
330 if fleet is None:
331 self._wizard.jumpPage(2)
332 else:
333 plane = fleet[self._wizard._bookedFlight.tailNumber]
334 if plane is None:
335 self._wizard.jumpPage(2)
336
337 if plane.gateNumber is not None and \
338 not fleet.isGateConflicting(plane):
339 self._wizard._departureGate = plane.gateNumber
340 self._wizard.jumpPage(2)
341 else:
342 self._wizard.nextPage()
343
344#-----------------------------------------------------------------------------
345
346class GateSelectionPage(Page):
347 """Page to select a free gate at LHBP.
348
349 This page should be displayed only if we have fleet information!."""
350 def __init__(self, wizard):
351 """Construct the gate selection page."""
352 help = "The airplane's gate position is invalid.\n\n" \
353 "Select the gate from which you\n" \
354 "would like to begin the flight."
355 super(GateSelectionPage, self).__init__(wizard,
356 "LHBP gate selection",
357 help)
358
359 self._listStore = gtk.ListStore(str)
360 self._gateList = gtk.TreeView(self._listStore)
361 column = gtk.TreeViewColumn(None, gtk.CellRendererText(),
362 text = 0)
363 column.set_expand(True)
364 self._gateList.append_column(column)
365 self._gateList.set_headers_visible(False)
366
367 gateSelection = self._gateList.get_selection()
368 gateSelection.connect("changed", self._selectionChanged)
369
370 scrolledWindow = gtk.ScrolledWindow()
371 scrolledWindow.add(self._gateList)
372 scrolledWindow.set_size_request(50, -1)
373 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
374 else gtk.POLICY_AUTOMATIC,
375 gtk.PolicyType.AUTOMATIC if pygobject
376 else gtk.POLICY_AUTOMATIC)
377 scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
378 else gtk.SHADOW_IN)
379
380 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 0.0, yscale = 1.0)
381 alignment.add(scrolledWindow)
382
383 self.setMainWidget(alignment)
384
385 self._button = self.addButton(gtk.STOCK_GO_FORWARD, default = True)
386 self._button.set_use_stock(True)
387 self._button.set_sensitive(False)
388 self._button.connect("clicked", self._forwardClicked)
389
390 def activate(self):
391 """Fill the gate list."""
392 self._listStore.clear()
393 occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
394 for gateNumber in const.lhbpGateNumbers:
395 if gateNumber not in occupiedGateNumbers:
396 self._listStore.append([gateNumber])
397
398 def _selectionChanged(self, selection):
399 """Called when the selection is changed."""
400 self._button.set_sensitive(selection.count_selected_rows()==1)
401
402 def _forwardClicked(self, button):
403 """Called when the forward button is clicked."""
404 selection = self._gateList.get_selection()
405 (listStore, iter) = selection.get_selected()
406 (gateNumber,) = listStore.get(iter, 0)
407
408 self._wizard._departureGate = gateNumber
409
410 #self._wizard._updatePlane(self._planeUpdated,
411 # self._wizard._bookedFlight.tailNumber,
412 # const.PLANE_HOME,
413 # gateNumber)
414 self._wizard.nextPage()
415
416 def _planeUpdated(self, success):
417 """Callback for the plane updating call."""
418 if success is None or success:
419 self._wizard.nextPage()
420 else:
421 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
422 buttons = BUTTONSTYPE_OK,
423 message_format = "Gate conflict detected again")
424 dialog.format_secondary_markup("Try to select a different gate.")
425 dialog.run()
426 dialog.hide()
427
428 self._wizard._getFleet(self._fleetRetrieved)
429
430 def _fleetRetrieved(self, fleet):
431 """Called when the fleet has been retrieved."""
432 if fleet is None:
433 self._wizard.nextPage()
434 else:
435 self.activate()
436
437#-----------------------------------------------------------------------------
438
439class ConnectPage(Page):
440 """Page which displays the departure airport and gate (if at LHBP)."""
441 def __init__(self, wizard):
442 """Construct the connect page."""
443 help = "The flight begins at the airport given below.\n" \
444 "Park your aircraft there, at the gate below, if given.\n\n" \
445 "Then press the Connect button to connect to the simulator."
446 super(ConnectPage, self).__init__(wizard,
447 "Connect to the simulator",
448 help)
449
450 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
451 xscale = 0.0, yscale = 0.0)
452
453 table = gtk.Table(2, 2)
454 table.set_row_spacings(4)
455 table.set_col_spacings(16)
456 table.set_homogeneous(True)
457 alignment.add(table)
458 self.setMainWidget(alignment)
459
460 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
461 label = gtk.Label("ICAO code:")
462 labelAlignment.add(label)
463 table.attach(labelAlignment, 0, 1, 0, 1)
464
465 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
466 self._departureICAO = gtk.Label()
467 self._departureICAO.set_width_chars(5)
468 self._departureICAO.set_alignment(0.0, 0.5)
469 labelAlignment.add(self._departureICAO)
470 table.attach(labelAlignment, 1, 2, 0, 1)
471
472 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
473 label = gtk.Label("Gate:")
474 labelAlignment.add(label)
475 table.attach(labelAlignment, 0, 1, 1, 2)
476
477 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
478 self._departureGate = gtk.Label()
479 self._departureGate.set_width_chars(5)
480 self._departureGate.set_alignment(0.0, 0.5)
481 labelAlignment.add(self._departureGate)
482 table.attach(labelAlignment, 1, 2, 1, 2)
483
484 self._button = self.addButton("_Connect", default = True)
485 self._button.set_use_underline(True)
486 self._button.connect("clicked", self._connectClicked)
487
488 def activate(self):
489 """Setup the departure information."""
490 icao = self._wizard._bookedFlight.departureICAO
491 self._departureICAO.set_markup("<b>" + icao + "</b>")
492 gate = self._wizard._departureGate
493 if gate!="-":
494 gate = "<b>" + gate + "</b>"
495 self._departureGate.set_markup(gate)
496
497 def _connectClicked(self, button):
498 """Called when the Connect button is pressed."""
499 self._wizard._connectSimulator()
500
501#-----------------------------------------------------------------------------
502
503class PayloadPage(Page):
504 """Page to allow setting up the payload."""
505 def __init__(self, wizard):
506 """Construct the page."""
507 help = "The briefing contains the weights below.\n" \
508 "Setup the cargo weight here and the payload weight in the simulator.\n\n" \
509 "You can also check here what the simulator reports as ZFW."
510
511 super(PayloadPage, self).__init__(wizard, "Payload", help)
512
513 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
514 xscale = 0.0, yscale = 0.0)
515
516 table = gtk.Table(7, 3)
517 table.set_row_spacings(4)
518 table.set_col_spacings(16)
519 table.set_homogeneous(False)
520 alignment.add(table)
521 self.setMainWidget(alignment)
522
523 label = gtk.Label("Crew:")
524 label.set_alignment(0.0, 0.5)
525 table.attach(label, 0, 1, 0, 1)
526
527 self._numCrew = gtk.Label()
528 self._numCrew.set_width_chars(6)
529 self._numCrew.set_alignment(1.0, 0.5)
530 table.attach(self._numCrew, 1, 2, 0, 1)
531
532 label = gtk.Label("Passengers:")
533 label.set_alignment(0.0, 0.5)
534 table.attach(label, 0, 1, 1, 2)
535
536 self._numPassengers = gtk.Label()
537 self._numPassengers.set_width_chars(6)
538 self._numPassengers.set_alignment(1.0, 0.5)
539 table.attach(self._numPassengers, 1, 2, 1, 2)
540
541 label = gtk.Label("Baggage:")
542 label.set_alignment(0.0, 0.5)
543 table.attach(label, 0, 1, 2, 3)
544
545 self._bagWeight = gtk.Label()
546 self._bagWeight.set_width_chars(6)
547 self._bagWeight.set_alignment(1.0, 0.5)
548 table.attach(self._bagWeight, 1, 2, 2, 3)
549
550 table.attach(gtk.Label("kg"), 2, 3, 2, 3)
551
552 label = gtk.Label("_Cargo:")
553 label.set_use_underline(True)
554 label.set_alignment(0.0, 0.5)
555 table.attach(label, 0, 1, 3, 4)
556
557 self._cargoWeight = gtk.Entry()
558 self._cargoWeight.set_width_chars(6)
559 self._cargoWeight.set_alignment(1.0)
560 self._cargoWeight.connect("changed", self._cargoWeightChanged)
561 self._cargoWeight.set_tooltip_text("The weight of the cargo for your flight.")
562 table.attach(self._cargoWeight, 1, 2, 3, 4)
563 self._cargoWeightValue = 0
564 label.set_mnemonic_widget(self._cargoWeight)
565
566 table.attach(gtk.Label("kg"), 2, 3, 3, 4)
567
568 label = gtk.Label("Mail:")
569 label.set_alignment(0.0, 0.5)
570 table.attach(label, 0, 1, 4, 5)
571
572 self._mailWeight = gtk.Label()
573 self._mailWeight.set_width_chars(6)
574 self._mailWeight.set_alignment(1.0, 0.5)
575 table.attach(self._mailWeight, 1, 2, 4, 5)
576
577 table.attach(gtk.Label("kg"), 2, 3, 4, 5)
578
579 label = gtk.Label("<b>Calculated ZFW:</b>")
580 label.set_alignment(0.0, 0.5)
581 label.set_use_markup(True)
582 table.attach(label, 0, 1, 5, 6)
583
584 self._calculatedZFW = gtk.Label()
585 self._calculatedZFW.set_width_chars(6)
586 self._calculatedZFW.set_alignment(1.0, 0.5)
587 table.attach(self._calculatedZFW, 1, 2, 5, 6)
588
589 table.attach(gtk.Label("kg"), 2, 3, 5, 6)
590
591 button = gtk.Button("_ZFW from FS:")
592 button.set_use_underline(True)
593 button.connect("clicked", self._zfwRequested)
594 table.attach(button, 0, 1, 6, 7)
595
596 self._simulatorZFW = gtk.Label("-")
597 self._simulatorZFW.set_width_chars(6)
598 self._simulatorZFW.set_alignment(1.0, 0.5)
599 table.attach(self._simulatorZFW, 1, 2, 6, 7)
600 self._simulatorZFWValue = None
601
602 table.attach(gtk.Label("kg"), 2, 3, 6, 7)
603
604 self._button = self.addButton(gtk.STOCK_GO_FORWARD, default = True)
605 self._button.set_use_stock(True)
606 self._button.connect("clicked", self._forwardClicked)
607
608 def activate(self):
609 """Setup the information."""
610 bookedFlight = self._wizard._bookedFlight
611 self._numCrew.set_text(str(bookedFlight.numCrew))
612 self._numPassengers.set_text(str(bookedFlight.numPassengers))
613 self._bagWeight.set_text(str(bookedFlight.bagWeight))
614 self._cargoWeightValue = bookedFlight.cargoWeight
615 self._cargoWeight.set_text(str(bookedFlight.cargoWeight))
616 self._mailWeight.set_text(str(bookedFlight.mailWeight))
617 self._updateCalculatedZFW()
618
619 def _calculateZFW(self):
620 """Calculate the ZFW value."""
621 zfw = self._wizard.gui._flight.aircraft.dow
622 bookedFlight = self._wizard._bookedFlight
623 zfw += (bookedFlight.numCrew + bookedFlight.numPassengers) * 82
624 zfw += bookedFlight.bagWeight
625 zfw += self._cargoWeightValue
626 zfw += bookedFlight.mailWeight
627 return zfw
628
629 def _updateCalculatedZFW(self):
630 """Update the calculated ZFW"""
631 zfw = self._calculateZFW()
632
633 markupBegin = "<b>"
634 markupEnd = "</b>"
635 if self._simulatorZFWValue is not None and \
636 PayloadChecker.isZFWFaulty(self._simulatorZFWValue, zfw):
637 markupBegin += '<span foreground="red">'
638 markupEnd = "</span>" + markupEnd
639 self._calculatedZFW.set_markup(markupBegin + str(zfw) + markupEnd)
640
641 def _cargoWeightChanged(self, entry):
642 """Called when the cargo weight has changed."""
643 text = self._cargoWeight.get_text()
644 if text=="":
645 self._cargoWeightValue = 0
646 else:
647 try:
648 self._cargoWeightValue = int(text)
649 except:
650 self._cargoWeight.set_text(str(self._cargoWeightValue))
651 self._updateCalculatedZFW()
652
653 def _zfwRequested(self, button):
654 """Called when the ZFW is requested from the simulator."""
655 self._wizard.gui.beginBusy("Querying ZFW...")
656 self._wizard.gui.simulator.requestZFW(self._handleZFW)
657
658 def _handleZFW(self, zfw):
659 """Called when the ZFW value is retrieved."""
660 gobject.idle_add(self._processZFW, zfw)
661
662 def _processZFW(self, zfw):
663 """Process the given ZFW value received from the simulator."""
664 self._wizard.gui.endBusy()
665 self._simulatorZFWValue = zfw
666 self._simulatorZFW.set_text("%.0f" % (zfw,))
667 self._updateCalculatedZFW()
668
669 def _forwardClicked(self, button):
670 """Called when the forward button is clicked."""
671 self._wizard._zfw = self._calculateZFW()
672 self._wizard.nextPage()
673
674#-----------------------------------------------------------------------------
675
676class TimePage(Page):
677 """Page displaying the departure and arrival times and allows querying the
678 current time from the flight simulator."""
679 def __init__(self, wizard):
680 help = "The departure and arrival times are displayed below in UTC.\n\n" \
681 "You can also query the current UTC time from the simulator.\n" \
682 "Ensure that you have enough time to properly prepare for the flight."
683
684 super(TimePage, self).__init__(wizard, "Time", help)
685
686 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
687 xscale = 0.0, yscale = 0.0)
688
689 table = gtk.Table(3, 2)
690 table.set_row_spacings(4)
691 table.set_col_spacings(16)
692 table.set_homogeneous(False)
693 alignment.add(table)
694 self.setMainWidget(alignment)
695
696 label = gtk.Label("Departure:")
697 label.set_alignment(0.0, 0.5)
698 table.attach(label, 0, 1, 0, 1)
699
700 self._departure = gtk.Label()
701 self._departure.set_alignment(0.0, 0.5)
702 table.attach(self._departure, 1, 2, 0, 1)
703
704 label = gtk.Label("Arrival:")
705 label.set_alignment(0.0, 0.5)
706 table.attach(label, 0, 1, 1, 2)
707
708 self._arrival = gtk.Label()
709 self._arrival.set_alignment(0.0, 0.5)
710 table.attach(self._arrival, 1, 2, 1, 2)
711
712 button = gtk.Button("_Time from FS:")
713 button.set_use_underline(True)
714 button.connect("clicked", self._timeRequested)
715 table.attach(button, 0, 1, 2, 3)
716
717 self._simulatorTime = gtk.Label("-")
718 self._simulatorTime.set_alignment(0.0, 0.5)
719 table.attach(self._simulatorTime, 1, 2, 2, 3)
720
721 self._button = self.addButton(gtk.STOCK_GO_FORWARD, default = True)
722 self._button.set_use_stock(True)
723 self._button.connect("clicked", self._forwardClicked)
724
725 def activate(self):
726 """Activate the page."""
727 bookedFlight = self._wizard._bookedFlight
728 self._departure.set_text(str(bookedFlight.departureTime.time()))
729 self._arrival.set_text(str(bookedFlight.arrivalTime.time()))
730
731 def _timeRequested(self, button):
732 """Request the time from the simulator."""
733 self._wizard.gui.beginBusy("Querying time...")
734 self._wizard.gui.simulator.requestTime(self._handleTime)
735
736 def _handleTime(self, timestamp):
737 """Handle the result of a time retrieval."""
738 gobject.idle_add(self._processTime, timestamp)
739
740 def _processTime(self, timestamp):
741 """Process the given time."""
742 self._wizard.gui.endBusy()
743 tm = time.gmtime(timestamp)
744 t = datetime.time(tm.tm_hour, tm.tm_min, tm.tm_sec)
745 self._simulatorTime.set_text(str(t))
746
747 ts = tm.tm_hour * 3600 + tm.tm_min * 60 + tm.tm_sec
748 dt = self._wizard._bookedFlight.departureTime.time()
749 dts = dt.hour * 3600 + dt.minute * 60 + dt.second
750 diff = dts-ts
751
752 markupBegin = ""
753 markupEnd = ""
754 if diff < 0:
755 markupBegin = '<b><span foreground="red">'
756 markupEnd = '</span></b>'
757 elif diff < 3*60 or diff > 30*60:
758 markupBegin = '<b><span foreground="orange">'
759 markupEnd = '</span></b>'
760
761 self._departure.set_markup(markupBegin + str(dt) + markupEnd)
762
763 def _forwardClicked(self, button):
764 """Called when the forward button is clicked."""
765 self._wizard.nextPage()
766
767#-----------------------------------------------------------------------------
768
769class RoutePage(Page):
770 """The page containing the route and the flight level."""
771 def __init__(self, wizard):
772 help = "Set your cruise flight level below, and\n" \
773 "if necessary, edit the flight plan."
774
775 super(RoutePage, self).__init__(wizard, "Route", help)
776
777 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
778 xscale = 0.0, yscale = 0.0)
779
780 mainBox = gtk.VBox()
781 alignment.add(mainBox)
782 self.setMainWidget(alignment)
783
784 levelBox = gtk.HBox()
785
786 label = gtk.Label("_Cruise level")
787 label.set_use_underline(True)
788 levelBox.pack_start(label, True, True, 0)
789
790 self._cruiseLevel = gtk.SpinButton()
791 self._cruiseLevel.set_increments(step = 10, page = 100)
792 self._cruiseLevel.set_range(min = 50, max = 500)
793 self._cruiseLevel.set_value(240)
794 self._cruiseLevel.set_tooltip_text("The cruise flight level.")
795 self._cruiseLevel.set_numeric(True)
796 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
797 label.set_mnemonic_widget(self._cruiseLevel)
798
799 levelBox.pack_start(self._cruiseLevel, False, False, 8)
800
801 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
802 xscale = 0.0, yscale = 0.0)
803 alignment.add(levelBox)
804
805 mainBox.pack_start(alignment, False, False, 0)
806
807
808 routeBox = gtk.VBox()
809
810 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
811 xscale = 0.0, yscale = 0.0)
812 label = gtk.Label("_Route")
813 label.set_use_underline(True)
814 alignment.add(label)
815 routeBox.pack_start(alignment, True, True, 0)
816
817 routeWindow = gtk.ScrolledWindow()
818 routeWindow.set_size_request(400, 80)
819 routeWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
820 else gtk.SHADOW_IN)
821 routeWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
822 else gtk.POLICY_AUTOMATIC,
823 gtk.PolicyType.AUTOMATIC if pygobject
824 else gtk.POLICY_AUTOMATIC)
825
826 self._route = gtk.TextView()
827 self._route.set_tooltip_text("The planned flight route.")
828 self._route.get_buffer().connect("changed", self._routeChanged)
829 routeWindow.add(self._route)
830
831 label.set_mnemonic_widget(self._route)
832 routeBox.pack_start(routeWindow, True, True, 0)
833
834 mainBox.pack_start(routeBox, True, True, 8)
835
836 self._button = self.addButton(gtk.STOCK_GO_FORWARD, default = True)
837 self._button.set_use_stock(True)
838 self._button.connect("clicked", self._forwardClicked)
839
840 def activate(self):
841 """Setup the route from the booked flight."""
842 self._route.get_buffer().set_text(self._wizard._bookedFlight.route)
843 self._updateForwardButton()
844
845 def _getRoute(self):
846 """Get the text of the route."""
847 buffer = self._route.get_buffer()
848 return buffer.get_text(buffer.get_start_iter(),
849 buffer.get_end_iter(), True)
850
851 def _updateForwardButton(self):
852 """Update the sensitivity of the forward button."""
853 self._button.set_sensitive(self._cruiseLevel.get_value_as_int()>=50 and \
854 self._getRoute()!="")
855
856 def _cruiseLevelChanged(self, spinButton):
857 """Called when the cruise level has changed."""
858 self._updateForwardButton()
859
860 def _routeChanged(self, textBuffer):
861 """Called when the route has changed."""
862 self._updateForwardButton()
863
864 def _forwardClicked(self, button):
865 """Called when the Forward button is clicked."""
866 self._wizard._cruiseAltitude = self._cruiseLevel.get_value_as_int() * 100
867 self._wizard._route = self._getRoute()
868
869 bookedFlight = self._wizard._bookedFlight
870 self._wizard.gui.beginBusy("Downloading NOTAMs...")
871 self._wizard.gui.webHandler.getNOTAMs(self._notamsCallback,
872 bookedFlight.departureICAO,
873 bookedFlight.arrivalICAO)
874
875 def _notamsCallback(self, returned, result):
876 """Callback for the NOTAMs."""
877 gobject.idle_add(self._handleNOTAMs, returned, result)
878
879 def _handleNOTAMs(self, returned, result):
880 """Handle the NOTAMs."""
881 if returned:
882 self._wizard._departureNOTAMs = result.departureNOTAMs
883 self._wizard._arrivalNOTAMs = result.arrivalNOTAMs
884 else:
885 self._wizard._departureNOTAMs = None
886 self._wizard._arrivalNOTAMs = None
887
888 bookedFlight = self._wizard._bookedFlight
889 self._wizard.gui.beginBusy("Downloading METARs...")
890 self._wizard.gui.webHandler.getMETARs(self._metarsCallback,
891 [bookedFlight.departureICAO,
892 bookedFlight.arrivalICAO])
893
894 def _metarsCallback(self, returned, result):
895 """Callback for the METARs."""
896 gobject.idle_add(self._handleMETARs, returned, result)
897
898 def _handleMETARs(self, returned, result):
899 """Handle the METARs."""
900 self._wizard._departureMETAR = None
901 self._wizard._arrivalMETAR = None
902 bookedFlight = self._wizard._bookedFlight
903 if returned:
904 if bookedFlight.departureICAO in result.metars:
905 self._wizard._departureMETAR = result.metars[bookedFlight.departureICAO]
906 if bookedFlight.arrivalICAO in result.metars:
907 self._wizard._arrivalMETAR = result.metars[bookedFlight.arrivalICAO]
908
909 self._wizard.gui.endBusy()
910 self._wizard.nextPage()
911
912#-----------------------------------------------------------------------------
913
914class BriefingPage(Page):
915 """Page for the briefing."""
916 def __init__(self, wizard, departure):
917 """Construct the briefing page."""
918 self._departure = departure
919 self._activated = False
920
921 title = "Briefing (%d/2): %s" % (1 if departure else 2,
922 "departure" if departure
923 else "arrival")
924
925 help = "Read carefully the NOTAMs and METAR below."
926
927 super(BriefingPage, self).__init__(wizard, title, help)
928
929 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
930 xscale = 1.0, yscale = 1.0)
931
932 mainBox = gtk.VBox()
933 alignment.add(mainBox)
934 self.setMainWidget(alignment)
935
936 self._notamsFrame = gtk.Frame()
937 self._notamsFrame.set_label("LHBP NOTAMs")
938 scrolledWindow = gtk.ScrolledWindow()
939 scrolledWindow.set_size_request(-1, 128)
940 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
941 else gtk.POLICY_AUTOMATIC,
942 gtk.PolicyType.AUTOMATIC if pygobject
943 else gtk.POLICY_AUTOMATIC)
944 self._notams = gtk.TextView()
945 self._notams.set_editable(False)
946 self._notams.set_accepts_tab(False)
947 self._notams.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
948 scrolledWindow.add(self._notams)
949 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
950 xscale = 1.0, yscale = 1.0)
951 alignment.set_padding(padding_top = 4, padding_bottom = 0,
952 padding_left = 0, padding_right = 0)
953 alignment.add(scrolledWindow)
954 self._notamsFrame.add(alignment)
955 mainBox.pack_start(self._notamsFrame, True, True, 4)
956
957 self._metarFrame = gtk.Frame()
958 self._metarFrame.set_label("LHBP METAR")
959 scrolledWindow = gtk.ScrolledWindow()
960 scrolledWindow.set_size_request(-1, 32)
961 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
962 else gtk.POLICY_AUTOMATIC,
963 gtk.PolicyType.AUTOMATIC if pygobject
964 else gtk.POLICY_AUTOMATIC)
965 self._metar = gtk.TextView()
966 self._metar.set_editable(False)
967 self._metar.set_accepts_tab(False)
968 self._metar.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
969 scrolledWindow.add(self._metar)
970 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
971 xscale = 1.0, yscale = 1.0)
972 alignment.set_padding(padding_top = 4, padding_bottom = 0,
973 padding_left = 0, padding_right = 0)
974 alignment.add(scrolledWindow)
975 self._metarFrame.add(alignment)
976 mainBox.pack_start(self._metarFrame, True, True, 4)
977
978 self._button = self.addButton(gtk.STOCK_GO_FORWARD, default = True)
979 self._button.set_use_stock(True)
980 self._button.connect("clicked", self._forwardClicked)
981
982 def activate(self):
983 """Activate the page."""
984 if self._activated:
985 if not self._departure:
986 self._button.set_label(gtk.STOCK_GO_FORWARD)
987 self._button.set_use_stock(True)
988 else:
989 if not self._departure:
990 self._button.set_label("I have read the briefing and am ready to fly!")
991 self._button.set_use_stock(False)
992
993 bookedFlight = self._wizard._bookedFlight
994
995 icao = bookedFlight.departureICAO if self._departure \
996 else bookedFlight.arrivalICAO
997 notams = self._wizard._departureNOTAMs if self._departure \
998 else self._wizard._arrivalNOTAMs
999 metar = self._wizard._departureMETAR if self._departure \
1000 else self._wizard._arrivalMETAR
1001
1002 self._notamsFrame.set_label(icao + " NOTAMs")
1003 buffer = self._notams.get_buffer()
1004 if notams is None:
1005 buffer.set_text("Could not download NOTAMs")
1006 else:
1007 s = ""
1008 for notam in notams:
1009 s += str(notam.begin)
1010 if notam.end is not None:
1011 s += " - " + str(notam.end)
1012 elif notam.permanent:
1013 s += " - PERMANENT"
1014 s += "\n"
1015 if notam.repeatCycle:
1016 s += "Repeat cycle: " + notam.repeatCycle + "\n"
1017 s += notam.notice + "\n"
1018 s += "-------------------- * --------------------\n"
1019 buffer.set_text(s)
1020
1021 self._metarFrame.set_label(icao + " METAR")
1022 buffer = self._metar.get_buffer()
1023 if metar is None:
1024 buffer.set_text("Could not download METAR")
1025 else:
1026 buffer.set_text(metar)
1027
1028 self._activated = True
1029
1030 def reset(self):
1031 """Reset the page if the wizard is reset."""
1032 super(BriefingPage, self).reset()
1033 self._activated = False
1034
1035 def _forwardClicked(self, button):
1036 """Called when the forward button is clicked."""
1037 self._wizard.nextPage()
1038
1039#-----------------------------------------------------------------------------
1040
1041class Wizard(gtk.VBox):
1042 """The flight wizard."""
1043 def __init__(self, gui):
1044 """Construct the wizard."""
1045 super(Wizard, self).__init__()
1046
1047 self.gui = gui
1048
1049 self._pages = []
1050 self._currentPage = None
1051
1052 self._pages.append(LoginPage(self))
1053 self._pages.append(FlightSelectionPage(self))
1054 self._pages.append(GateSelectionPage(self))
1055 self._pages.append(ConnectPage(self))
1056 self._pages.append(PayloadPage(self))
1057 self._pages.append(TimePage(self))
1058 self._pages.append(RoutePage(self))
1059 self._pages.append(BriefingPage(self, True))
1060 self._pages.append(BriefingPage(self, False))
1061
1062 maxWidth = 0
1063 maxHeight = 0
1064 for page in self._pages:
1065 page.show_all()
1066 pageSizeRequest = page.size_request()
1067 width = pageSizeRequest.width if pygobject else pageSizeRequest[0]
1068 height = pageSizeRequest.height if pygobject else pageSizeRequest[1]
1069 maxWidth = max(maxWidth, width)
1070 maxHeight = max(maxHeight, height)
1071 maxWidth += 16
1072 maxHeight += 32
1073 self.set_size_request(maxWidth, maxHeight)
1074
1075 self._initialize()
1076
1077 @property
1078 def loginResult(self):
1079 """Get the login result."""
1080 return self._loginResult
1081
1082 def setCurrentPage(self, index):
1083 """Set the current page to the one with the given index."""
1084 assert index < len(self._pages)
1085
1086 if self._currentPage is not None:
1087 self.remove(self._pages[self._currentPage])
1088
1089 self._currentPage = index
1090 self.add(self._pages[index])
1091 self._pages[index].activate()
1092 self.show_all()
1093
1094 def nextPage(self):
1095 """Go to the next page."""
1096 self.jumpPage(1)
1097
1098 def jumpPage(self, count):
1099 """Go to the page which is 'count' pages after the current one."""
1100 self.setCurrentPage(self._currentPage + count)
1101 self.grabDefault()
1102
1103 def grabDefault(self):
1104 """Make the default button of the current page the default."""
1105 self._pages[self._currentPage].grabDefault()
1106
1107 def connected(self, fsType, descriptor):
1108 """Called when the connection could be made to the simulator."""
1109 self.nextPage()
1110
1111 def connectionFailed(self):
1112 """Called when the connection could not be made to the simulator."""
1113 self._initialize()
1114
1115 def disconnected(self):
1116 """Called when we have disconnected from the simulator."""
1117 self._initialize()
1118
1119 def _initialize(self):
1120 """Initialize the wizard."""
1121 self._fleet = None
1122 self._fleetCallback = None
1123 self._updatePlaneCallback = None
1124
1125 self._loginResult = None
1126 self._bookedFlight = None
1127 self._departureGate = "-"
1128 self._zfw = None
1129 self._cruiseAltitude = None
1130 self._route = None
1131 self._departureNOTAMs = None
1132 self._departureMETAR = None
1133 self._arrivalNOTAMs = None
1134 self._arrivalMETAR = None
1135
1136 for page in self._pages:
1137 page.reset()
1138
1139 self.setCurrentPage(0)
1140
1141 def _getFleet(self, callback, force = False):
1142 """Get the fleet, if needed.
1143
1144 callback is function that will be called, when the feet is retrieved,
1145 or the retrieval fails. It should have a single argument that will
1146 receive the fleet object on success, None otherwise.
1147 """
1148 if self._fleet is not None and not force:
1149 callback(self._fleet)
1150
1151 self.gui.beginBusy("Retrieving fleet...")
1152 self._fleetCallback = callback
1153 self.gui.webHandler.getFleet(self._fleetResultCallback)
1154
1155 def _fleetResultCallback(self, returned, result):
1156 """Called when the fleet has been queried."""
1157 gobject.idle_add(self._handleFleetResult, returned, result)
1158
1159 def _handleFleetResult(self, returned, result):
1160 """Handle the fleet result."""
1161 self.gui.endBusy()
1162 if returned:
1163 self._fleet = result.fleet
1164 else:
1165 self._fleet = None
1166
1167 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
1168 buttons = BUTTONSTYPE_OK,
1169 message_format =
1170 "Failed to retrieve the information on "
1171 "the fleet.")
1172 dialog.run()
1173 dialog.hide()
1174
1175 self._fleetCallback(self._fleet)
1176
1177 def _updatePlane(self, callback, tailNumber, status, gateNumber = None):
1178 """Update the given plane's gate information."""
1179 self.gui.beginBusy("Updating plane status...")
1180 self._updatePlaneCallback = callback
1181 self.gui.webHandler.updatePlane(self._updatePlaneResultCallback,
1182 tailNumber, status, gateNumber)
1183
1184 def _updatePlaneResultCallback(self, returned, result):
1185 """Callback for the plane updating operation."""
1186 gobject.idle_add(self._handleUpdatePlaneResult, returned, result)
1187
1188 def _handleUpdatePlaneResult(self, returned, result):
1189 """Handle the result of a plane update operation."""
1190 self.gui.endBusy()
1191 if returned:
1192 success = result.success
1193 else:
1194 success = None
1195
1196 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
1197 buttons = BUTTONSTYPE_OK,
1198 message_format =
1199 "Failed to update the statuis of "
1200 "the airplane.")
1201 dialog.run()
1202 dialog.hide()
1203
1204 self._updatePlaneCallback(success)
1205
1206 def _connectSimulator(self):
1207 """Connect to the simulator."""
1208 self.gui.connectSimulator(self._bookedFlight.aircraftType)
1209
1210#-----------------------------------------------------------------------------
1211
Note: See TracBrowser for help on using the repository browser.