source: src/mlx/gui/flight.py@ 67:6a8ed0857638

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

The briefing works

File size: 46.1 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.ALWAYS if pygobject
278 else gtk.POLICY_ALWAYS)
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.ALWAYS if pygobject
376 else gtk.POLICY_ALWAYS)
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
822 self._route = gtk.TextView()
823 self._route.set_tooltip_text("The planned flight route.")
824 self._route.get_buffer().connect("changed", self._routeChanged)
825 routeWindow.add(self._route)
826
827 label.set_mnemonic_widget(self._route)
828 routeBox.pack_start(routeWindow, True, True, 0)
829
830 mainBox.pack_start(routeBox, True, True, 8)
831
832 self._button = self.addButton(gtk.STOCK_GO_FORWARD, default = True)
833 self._button.set_use_stock(True)
834 self._button.connect("clicked", self._forwardClicked)
835
836 def activate(self):
837 """Setup the route from the booked flight."""
838 self._route.get_buffer().set_text(self._wizard._bookedFlight.route)
839 self._updateForwardButton()
840
841 def _getRoute(self):
842 """Get the text of the route."""
843 buffer = self._route.get_buffer()
844 return buffer.get_text(buffer.get_start_iter(),
845 buffer.get_end_iter(), True)
846
847 def _updateForwardButton(self):
848 """Update the sensitivity of the forward button."""
849 self._button.set_sensitive(self._cruiseLevel.get_value_as_int()>=50 and \
850 self._getRoute())
851
852 def _cruiseLevelChanged(self, spinButton):
853 """Called when the cruise level has changed."""
854 self._updateForwardButton()
855
856 def _routeChanged(self, textBuffer):
857 """Called when the route has changed."""
858 self._updateForwardButton()
859
860 def _forwardClicked(self, button):
861 """Called when the Forward button is clicked."""
862 self._wizard._cruiseAltitude = self._cruiseLevel.get_value_as_int() * 100
863 self._wizard._route = self._getRoute()
864
865 bookedFlight = self._wizard._bookedFlight
866 self._wizard.gui.beginBusy("Downloading NOTAMs...")
867 self._wizard.gui.webHandler.getNOTAMs(self._notamsCallback,
868 bookedFlight.departureICAO,
869 bookedFlight.arrivalICAO)
870
871 def _notamsCallback(self, returned, result):
872 """Callback for the NOTAMs."""
873 gobject.idle_add(self._handleNOTAMs, returned, result)
874
875 def _handleNOTAMs(self, returned, result):
876 """Handle the NOTAMs."""
877 if returned:
878 self._wizard._departureNOTAMs = result.departureNOTAMs
879 self._wizard._arrivalNOTAMs = result.arrivalNOTAMs
880 else:
881 self._wizard._departureNOTAMs = None
882 self._wizard._arrivalNOTAMs = None
883
884 bookedFlight = self._wizard._bookedFlight
885 self._wizard.gui.beginBusy("Downloading METARs...")
886 self._wizard.gui.webHandler.getMETARs(self._metarsCallback,
887 [bookedFlight.departureICAO,
888 bookedFlight.arrivalICAO])
889
890 def _metarsCallback(self, returned, result):
891 """Callback for the METARs."""
892 gobject.idle_add(self._handleMETARs, returned, result)
893
894 def _handleMETARs(self, returned, result):
895 """Handle the METARs."""
896 self._wizard._departureMETAR = None
897 self._wizard._arrivalMETAR = None
898 bookedFlight = self._wizard._bookedFlight
899 if returned:
900 if bookedFlight.departureICAO in result.metars:
901 self._wizard._departureMETAR = result.metars[bookedFlight.departureICAO]
902 if bookedFlight.arrivalICAO in result.metars:
903 self._wizard._arrivalMETAR = result.metars[bookedFlight.arrivalICAO]
904
905 self._wizard.gui.endBusy()
906 self._wizard.nextPage()
907
908#-----------------------------------------------------------------------------
909
910class BriefingPage(Page):
911 """Page for the briefing."""
912 def __init__(self, wizard, departure):
913 """Construct the briefing page."""
914 self._departure = departure
915 self._activated = False
916
917 title = "Briefing (%d/2): %s airport" % (1 if departure else 2,
918 "departure" if departure
919 else "arrival")
920
921 help = "Read carefully the NOTAMs and METAR below."
922
923 super(BriefingPage, self).__init__(wizard, title, help)
924
925 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
926 xscale = 1.0, yscale = 1.0)
927
928 mainBox = gtk.VBox()
929 alignment.add(mainBox)
930 self.setMainWidget(alignment)
931
932 self._notamsFrame = gtk.Frame()
933 self._notamsFrame.set_label("LHBP NOTAMs")
934 scrolledWindow = gtk.ScrolledWindow()
935 scrolledWindow.set_size_request(-1, 128)
936 self._notams = gtk.TextView()
937 self._notams.set_editable(False)
938 self._notams.set_accepts_tab(False)
939 self._notams.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
940 scrolledWindow.add(self._notams)
941 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
942 xscale = 1.0, yscale = 1.0)
943 alignment.set_padding(padding_top = 4, padding_bottom = 0,
944 padding_left = 0, padding_right = 0)
945 alignment.add(scrolledWindow)
946 self._notamsFrame.add(alignment)
947 mainBox.pack_start(self._notamsFrame, True, True, 4)
948
949 self._metarFrame = gtk.Frame()
950 self._metarFrame.set_label("LHBP METAR")
951 scrolledWindow = gtk.ScrolledWindow()
952 scrolledWindow.set_size_request(-1, 32)
953 self._metar = gtk.TextView()
954 self._metar.set_editable(False)
955 self._metar.set_accepts_tab(False)
956 self._metar.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
957 scrolledWindow.add(self._metar)
958 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
959 xscale = 1.0, yscale = 1.0)
960 alignment.set_padding(padding_top = 4, padding_bottom = 0,
961 padding_left = 0, padding_right = 0)
962 alignment.add(scrolledWindow)
963 self._metarFrame.add(alignment)
964 mainBox.pack_start(self._metarFrame, True, True, 4)
965
966 self._button = self.addButton(gtk.STOCK_GO_FORWARD, default = True)
967 self._button.set_use_stock(True)
968 self._button.connect("clicked", self._forwardClicked)
969
970 def activate(self):
971 """Activate the page."""
972 if self._activated:
973 if not self._departure:
974 self._button.set_label(gtk.STOCK_GO_FORWARD)
975 self._button.set_use_stock(True)
976 else:
977 if not self._departure:
978 self._button.set_label("I have read the briefing and am ready to fly!")
979 self._button.set_use_stock(False)
980
981 bookedFlight = self._wizard._bookedFlight
982
983 icao = bookedFlight.departureICAO if self._departure \
984 else bookedFlight.arrivalICAO
985 notams = self._wizard._departureNOTAMs if self._departure \
986 else self._wizard._arrivalNOTAMs
987 metar = self._wizard._departureMETAR if self._departure \
988 else self._wizard._arrivalMETAR
989
990 self._notamsFrame.set_label(icao + " NOTAMs")
991 buffer = self._notams.get_buffer()
992 if notams is None:
993 buffer.set_text("Could not download NOTAMs")
994 else:
995 s = ""
996 for notam in notams:
997 s += str(notam.begin)
998 if notam.end is not None:
999 s += " - " + str(notam.end)
1000 elif notam.permanent:
1001 s += " - PERMANENT"
1002 s += "\n"
1003 if notam.repeatCycle:
1004 s += "Repeat cycle: " + notam.repeatCycle + "\n"
1005 s += notam.notice + "\n"
1006 s += "-------------------- * --------------------\n"
1007 buffer.set_text(s)
1008
1009 self._metarFrame.set_label(icao + " METAR")
1010 buffer = self._metar.get_buffer()
1011 if metar is None:
1012 buffer.set_text("Could not download METAR")
1013 else:
1014 buffer.set_text(metar)
1015
1016 self._activated = True
1017
1018 def reset(self):
1019 """Reset the page if the wizard is reset."""
1020 super(BriefingPage, self).reset()
1021 self._activated = False
1022
1023 def _forwardClicked(self, button):
1024 """Called when the forward button is clicked."""
1025 self._wizard.nextPage()
1026
1027#-----------------------------------------------------------------------------
1028
1029class Wizard(gtk.VBox):
1030 """The flight wizard."""
1031 def __init__(self, gui):
1032 """Construct the wizard."""
1033 super(Wizard, self).__init__()
1034
1035 self.gui = gui
1036
1037 self._pages = []
1038 self._currentPage = None
1039
1040 self._pages.append(LoginPage(self))
1041 self._pages.append(FlightSelectionPage(self))
1042 self._pages.append(GateSelectionPage(self))
1043 self._pages.append(ConnectPage(self))
1044 self._pages.append(PayloadPage(self))
1045 self._pages.append(TimePage(self))
1046 self._pages.append(RoutePage(self))
1047 self._pages.append(BriefingPage(self, True))
1048 self._pages.append(BriefingPage(self, False))
1049
1050 maxWidth = 0
1051 maxHeight = 0
1052 for page in self._pages:
1053 page.show_all()
1054 pageSizeRequest = page.size_request()
1055 width = pageSizeRequest.width if pygobject else pageSizeRequest[0]
1056 height = pageSizeRequest.height if pygobject else pageSizeRequest[1]
1057 maxWidth = max(maxWidth, width)
1058 maxHeight = max(maxHeight, height)
1059 maxWidth += 16
1060 maxHeight += 32
1061 self.set_size_request(maxWidth, maxHeight)
1062
1063 self._initialize()
1064
1065 @property
1066 def loginResult(self):
1067 """Get the login result."""
1068 return self._loginResult
1069
1070 def setCurrentPage(self, index):
1071 """Set the current page to the one with the given index."""
1072 assert index < len(self._pages)
1073
1074 if self._currentPage is not None:
1075 self.remove(self._pages[self._currentPage])
1076
1077 self._currentPage = index
1078 self.add(self._pages[index])
1079 self._pages[index].activate()
1080 self.show_all()
1081
1082 def nextPage(self):
1083 """Go to the next page."""
1084 self.jumpPage(1)
1085
1086 def jumpPage(self, count):
1087 """Go to the page which is 'count' pages after the current one."""
1088 self.setCurrentPage(self._currentPage + count)
1089 self.grabDefault()
1090
1091 def grabDefault(self):
1092 """Make the default button of the current page the default."""
1093 self._pages[self._currentPage].grabDefault()
1094
1095 def connected(self, fsType, descriptor):
1096 """Called when the connection could be made to the simulator."""
1097 self.nextPage()
1098
1099 def connectionFailed(self):
1100 """Called when the connection could not be made to the simulator."""
1101 self._initialize()
1102
1103 def disconnected(self):
1104 """Called when we have disconnected from the simulator."""
1105 self._initialize()
1106
1107 def _initialize(self):
1108 """Initialize the wizard."""
1109 self._fleet = None
1110 self._fleetCallback = None
1111 self._updatePlaneCallback = None
1112
1113 self._loginResult = None
1114 self._bookedFlight = None
1115 self._departureGate = "-"
1116 self._zfw = None
1117 self._cruiseAltitude = None
1118 self._route = None
1119 self._departureNOTAMs = None
1120 self._departureMETAR = None
1121 self._arrivalNOTAMs = None
1122 self._arrivalMETAR = None
1123
1124 for page in self._pages:
1125 page.reset()
1126
1127 self.setCurrentPage(0)
1128
1129 def _getFleet(self, callback, force = False):
1130 """Get the fleet, if needed.
1131
1132 callback is function that will be called, when the feet is retrieved,
1133 or the retrieval fails. It should have a single argument that will
1134 receive the fleet object on success, None otherwise.
1135 """
1136 if self._fleet is not None and not force:
1137 callback(self._fleet)
1138
1139 self.gui.beginBusy("Retrieving fleet...")
1140 self._fleetCallback = callback
1141 self.gui.webHandler.getFleet(self._fleetResultCallback)
1142
1143 def _fleetResultCallback(self, returned, result):
1144 """Called when the fleet has been queried."""
1145 gobject.idle_add(self._handleFleetResult, returned, result)
1146
1147 def _handleFleetResult(self, returned, result):
1148 """Handle the fleet result."""
1149 self.gui.endBusy()
1150 if returned:
1151 self._fleet = result.fleet
1152 else:
1153 self._fleet = None
1154
1155 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
1156 buttons = BUTTONSTYPE_OK,
1157 message_format =
1158 "Failed to retrieve the information on "
1159 "the fleet.")
1160 dialog.run()
1161 dialog.hide()
1162
1163 self._fleetCallback(self._fleet)
1164
1165 def _updatePlane(self, callback, tailNumber, status, gateNumber = None):
1166 """Update the given plane's gate information."""
1167 self.gui.beginBusy("Updating plane status...")
1168 self._updatePlaneCallback = callback
1169 self.gui.webHandler.updatePlane(self._updatePlaneResultCallback,
1170 tailNumber, status, gateNumber)
1171
1172 def _updatePlaneResultCallback(self, returned, result):
1173 """Callback for the plane updating operation."""
1174 gobject.idle_add(self._handleUpdatePlaneResult, returned, result)
1175
1176 def _handleUpdatePlaneResult(self, returned, result):
1177 """Handle the result of a plane update operation."""
1178 self.gui.endBusy()
1179 if returned:
1180 success = result.success
1181 else:
1182 success = None
1183
1184 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
1185 buttons = BUTTONSTYPE_OK,
1186 message_format =
1187 "Failed to update the statuis of "
1188 "the airplane.")
1189 dialog.run()
1190 dialog.hide()
1191
1192 self._updatePlaneCallback(success)
1193
1194 def _connectSimulator(self):
1195 """Connect to the simulator."""
1196 self.gui.connectSimulator(self._bookedFlight.aircraftType)
1197
1198#-----------------------------------------------------------------------------
1199
Note: See TracBrowser for help on using the repository browser.