source: src/mlx/gui/gui.py@ 59:a3e0b8455dc8

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

Implemented better connection and connection failure handling.

File size: 33.6 KB
Line 
1# The main file for the GUI
2
3from statusicon import StatusIcon
4from statusbar import Statusbar
5from update import Updater
6from mlx.gui.common import *
7from mlx.gui.flight import Wizard
8
9import mlx.const as const
10import mlx.fs as fs
11import mlx.flight as flight
12import mlx.logger as logger
13import mlx.acft as acft
14import mlx.web as web
15
16import time
17import threading
18import sys
19
20acftTypes = [ ("Boeing 737-600", const.AIRCRAFT_B736),
21 ("Boeing 737-700", const.AIRCRAFT_B737),
22 ("Boeing 737-800", const.AIRCRAFT_B738),
23 ("Bombardier Dash 8-Q400", const.AIRCRAFT_DH8D),
24 ("Boeing 737-300", const.AIRCRAFT_B733),
25 ("Boeing 737-400", const.AIRCRAFT_B734),
26 ("Boeing 737-500", const.AIRCRAFT_B735),
27 ("Boeing 767-200", const.AIRCRAFT_B762),
28 ("Boeing 767-300", const.AIRCRAFT_B763),
29 ("Bombardier CRJ200", const.AIRCRAFT_CRJ2),
30 ("Fokker 70", const.AIRCRAFT_F70),
31 ("Lisunov Li-2", const.AIRCRAFT_DC3),
32 ("Tupolev Tu-134", const.AIRCRAFT_T134),
33 ("Tupolev Tu-154", const.AIRCRAFT_T154),
34 ("Yakovlev Yak-40", const.AIRCRAFT_YK40) ]
35
36class GUI(fs.ConnectionListener):
37 """The main GUI class."""
38 def __init__(self, programDirectory, config):
39 """Construct the GUI."""
40 gobject.threads_init()
41
42 self._programDirectory = programDirectory
43 self.config = config
44 self._connecting = False
45 self._reconnecting = False
46 self._connected = False
47 self._logger = logger.Logger(output = self)
48 self._flight = None
49 self._simulator = None
50 self._monitoring = False
51
52 self._stdioLock = threading.Lock()
53 self._stdioText = ""
54 self._stdioAfterNewLine = True
55
56 self.webHandler = web.Handler()
57 self.webHandler.start()
58
59 self.toRestart = False
60
61 def build(self, iconDirectory):
62 """Build the GUI."""
63
64 window = gtk.Window()
65 window.set_title("MAVA Logger X " + const.VERSION)
66 window.set_icon_from_file(os.path.join(iconDirectory, "logo.ico"))
67 window.connect("delete-event",
68 lambda a, b: self.hideMainWindow())
69 window.connect("window-state-event", self._handleMainWindowState)
70
71 mainVBox = gtk.VBox()
72 window.add(mainVBox)
73
74 notebook = gtk.Notebook()
75 mainVBox.add(notebook)
76
77 self._wizard = Wizard(self)
78 label = gtk.Label("_Flight")
79 label.set_use_underline(True)
80 label.set_tooltip_text("Flight wizard")
81 notebook.append_page(self._wizard, label)
82
83
84 dataVBox = gtk.VBox()
85 label = gtk.Label("_Data")
86 label.set_use_underline(True)
87 label.set_tooltip_text("FSUIPC data access")
88
89 if "USE_SCROLLEDDATA" in os.environ:
90 dataScrolledWindow = gtk.ScrolledWindow()
91 dataScrolledWindow.add_with_viewport(dataVBox)
92 notebook.append_page(dataScrolledWindow, label)
93 else:
94 notebook.append_page(dataVBox, label)
95
96 setupFrame = self._buildSetupFrame()
97 setupFrame.set_border_width(8)
98 dataVBox.pack_start(setupFrame, False, False, 0)
99
100 dataFrame = self._buildDataFrame()
101 dataFrame.set_border_width(8)
102 dataVBox.pack_start(dataFrame, False, False, 0)
103
104 logVBox = gtk.VBox()
105 label = gtk.Label("_Log")
106 label.set_use_underline(True)
107 label.set_tooltip_text("Flight log")
108 notebook.append_page(logVBox, label)
109
110 logFrame = self._buildLogFrame()
111 logFrame.set_border_width(8)
112 logVBox.pack_start(logFrame, True, True, 0)
113
114 mainVBox.pack_start(gtk.HSeparator(), False, False, 0)
115
116 self._statusbar = Statusbar()
117 mainVBox.pack_start(self._statusbar, False, False, 0)
118
119 notebook.connect("switch-page", self._notebookPageSwitch)
120
121 window.show_all()
122 self._wizard.grabDefault()
123
124 self._mainWindow = window
125
126 self._statusIcon = StatusIcon(iconDirectory, self)
127
128 self._busyCursor = gdk.Cursor(gdk.CursorType.WATCH if pygobject
129 else gdk.WATCH)
130
131 @property
132 def simulator(self):
133 """Get the simulator used by us."""
134 return self._simulator
135
136 def run(self):
137 """Run the GUI."""
138 if self.config.autoUpdate:
139 self._updater = Updater(self,
140 self._programDirectory,
141 self.config.updateURL,
142 self._mainWindow)
143 self._updater.start()
144
145 gtk.main()
146
147 if self._flight is not None:
148 simulator = self._flight.simulator
149 if self._monitoring:
150 simulator.stopMonitoring()
151 self._monitoring = False
152 simulator.disconnect()
153
154 def connected(self, fsType, descriptor):
155 """Called when we have connected to the simulator."""
156 self._connected = True
157 self._logger.untimedMessage("Connected to the simulator %s" % (descriptor,))
158 gobject.idle_add(self._handleConnected, fsType, descriptor)
159
160 def _handleConnected(self, fsType, descriptor):
161 """Called when the connection to the simulator has succeeded."""
162 self._statusbar.updateConnection(self._connecting, self._connected)
163 self.endBusy()
164 if not self._reconnecting:
165 self._wizard.connected(fsType, descriptor)
166 self._reconnecting = False
167
168 def connectionFailed(self):
169 """Called when the connection failed."""
170 self._logger.untimedMessage("Connection to the simulator failed")
171 gobject.idle_add(self._connectionFailed)
172
173 def _connectionFailed(self):
174 """Called when the connection failed."""
175 self.endBusy()
176 self._statusbar.updateConnection(self._connecting, self._connected)
177
178 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
179 message_format =
180 "Cannot connect to the simulator.",
181 parent = self._mainWindow)
182 dialog.format_secondary_markup("Rectify the situation, and press <b>Try again</b> "
183 "to try the connection again, "
184 "or <b>Cancel</b> to cancel the flight.")
185
186 dialog.add_button("_Cancel", 0)
187 dialog.add_button("_Try again", 1)
188 dialog.set_default_response(1)
189
190 result = dialog.run()
191 dialog.hide()
192 if result == 1:
193 self.beginBusy("Connecting to the simulator.")
194 self._simulator.reconnect()
195 else:
196 self._connecting = False
197 self._reconnecting = False
198 self._statusbar.updateConnection(self._connecting, self._connected)
199 self._wizard.connectionFailed()
200
201 def disconnected(self):
202 """Called when we have disconnected from the simulator."""
203 self._connected = False
204 self._logger.untimedMessage("Disconnected from the simulator")
205
206 gobject.idle_add(self._disconnected)
207
208 def _disconnected(self):
209 """Called when we have disconnected from the simulator unexpectedly."""
210 self._statusbar.updateConnection(self._connecting, self._connected)
211
212 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
213 message_format =
214 "The connection to the simulator failed unexpectedly.",
215 parent = self._mainWindow)
216 dialog.format_secondary_markup("If the simulator has crashed, restart it "
217 "and restore your flight as much as possible "
218 "to the state it was in before the crash.\n"
219 "Then press <b>Reconnect</b> to reconnect.\n\n"
220 "If you want to cancel the flight, press <b>Cancel</b>.")
221
222 dialog.add_button("_Cancel", 0)
223 dialog.add_button("_Reconnect", 1)
224 dialog.set_default_response(1)
225
226 result = dialog.run()
227 dialog.hide()
228 if result == 1:
229 self.beginBusy("Connecting to the simulator.")
230 self._reconnecting = True
231 self._simulator.reconnect()
232 else:
233 self._connecting = False
234 self._reconnecting = False
235 self._statusbar.updateConnection(self._connecting, self._connected)
236 self._wizard.disconnected()
237
238 def write(self, msg):
239 """Write the given message to the log."""
240 gobject.idle_add(self._writeLog, msg)
241
242 def check(self, flight, aircraft, logger, oldState, state):
243 """Update the data."""
244 gobject.idle_add(self._setData, state)
245
246 def resetFlightStatus(self):
247 """Reset the status of the flight."""
248 self._statusbar.resetFlightStatus()
249 self._statusIcon.resetFlightStatus()
250
251 def setStage(self, stage):
252 """Set the stage of the flight."""
253 gobject.idle_add(self._setStage, stage)
254
255 def _setStage(self, stage):
256 """Set the stage of the flight."""
257 self._statusbar.setStage(stage)
258 self._statusIcon.setStage(stage)
259
260 def setRating(self, rating):
261 """Set the rating of the flight."""
262 gobject.idle_add(self._setRating, rating)
263
264 def _setRating(self, rating):
265 """Set the rating of the flight."""
266 self._statusbar.setRating(rating)
267 self._statusIcon.setRating(rating)
268
269 def setNoGo(self, reason):
270 """Set the rating of the flight to No-Go with the given reason."""
271 gobject.idle_add(self._setNoGo, reason)
272
273 def _setNoGo(self, reason):
274 """Set the rating of the flight."""
275 self._statusbar.setNoGo(reason)
276 self._statusIcon.setNoGo(reason)
277
278 def _handleMainWindowState(self, window, event):
279 """Hande a change in the state of the window"""
280 iconified = gdk.WindowState.ICONIFIED if pygobject \
281 else gdk.WINDOW_STATE_ICONIFIED
282 if (event.changed_mask&iconified)!=0 and (event.new_window_state&iconified)!=0:
283 self.hideMainWindow(savePosition = False)
284
285 def hideMainWindow(self, savePosition = True):
286 """Hide the main window and save its position."""
287 if savePosition:
288 (self._mainWindowX, self._mainWindowY) = \
289 self._mainWindow.get_window().get_root_origin()
290 else:
291 self._mainWindowX = self._mainWindowY = None
292 self._mainWindow.hide()
293 self._statusIcon.mainWindowHidden()
294 return True
295
296 def showMainWindow(self):
297 """Show the main window at its former position."""
298 if self._mainWindowX is not None and self._mainWindowY is not None:
299 self._mainWindow.move(self._mainWindowX, self._mainWindowY)
300
301 self._mainWindow.show()
302 self._mainWindow.deiconify()
303
304 self._statusIcon.mainWindowShown()
305
306 def toggleMainWindow(self):
307 """Toggle the main window."""
308 if self._mainWindow.get_visible():
309 self.hideMainWindow()
310 else:
311 self.showMainWindow()
312
313 def restart(self):
314 """Quit and restart the application."""
315 self.toRestart = True
316 self._quit()
317
318 def flushStdIO(self):
319 """Flush any text to the standard error that could not be logged."""
320 if self._stdioText:
321 sys.__stderr__.write(self._stdioText)
322
323 def writeStdIO(self, text):
324 """Write the given text into standard I/O log."""
325 with self._stdioLock:
326 self._stdioText += text
327
328 gobject.idle_add(self._writeStdIO)
329
330 def beginBusy(self, message):
331 """Begin a period of background processing."""
332 self._mainWindow.get_window().set_cursor(self._busyCursor)
333 self._statusbar.updateBusyState(message)
334
335 def endBusy(self):
336 """End a period of background processing."""
337 self._mainWindow.get_window().set_cursor(None)
338 self._statusbar.updateBusyState(None)
339
340 def _writeStdIO(self):
341 """Perform the real writing."""
342 with self._stdioLock:
343 text = self._stdioText
344 self._stdioText = ""
345 if not text: return
346
347 lines = text.splitlines()
348 if text[-1]=="\n":
349 text = ""
350 else:
351 text = lines[-1]
352 lines = lines[:-1]
353
354 for line in lines:
355 if self._stdioAfterNewLine:
356 line = "[STDIO] " + line
357 self._writeLog(line + "\n")
358 self._stdioAfterNewLine = True
359
360 if text:
361 if self._stdioAfterNewLine:
362 text = "[STDIO] " + text
363 self._writeLog(text)
364 self._stdioAfterNewLine = False
365
366 def connectSimulator(self, aircraftType):
367 """Connect to the simulator for the first time."""
368 self._logger.reset()
369
370 self._flight = flight.Flight(self._logger, self)
371 self._flight.aircraftType = aircraftType
372 self._flight.aircraft = acft.Aircraft.create(self._flight)
373 self._flight.aircraft._checkers.append(self)
374
375 if self._simulator is None:
376 self._simulator = fs.createSimulator(const.SIM_MSFS9, self)
377
378 self._flight.simulator = self._simulator
379
380 self.beginBusy("Connecting to the simulator...")
381 self._statusbar.updateConnection(self._connecting, self._connected)
382
383 self._connecting = True
384 self._simulator.connect(self._flight.aircraft)
385
386 def _connectToggled(self, button):
387 """Callback for the connection button."""
388 if self._connectButton.get_active():
389 acftListModel = self._acftList.get_model()
390 self.connectSimulator(acftListModel[self._acftList.get_active()][1])
391
392 self._flight.cruiseAltitude = self._flSpinButton.get_value_as_int() * 100
393
394 self._flight.zfw = self._zfwSpinButton.get_value_as_int()
395
396 self._simulator.startMonitoring()
397 self._monitoring = True
398 else:
399 self.resetFlightStatus()
400 self._connecting = False
401 self._connected = False
402
403 self._simulator.stopMonitoring()
404 self._monitoring = False
405
406 self._simulator.disconnect()
407 self._flight = None
408
409 self._statusbar.updateConnection(self._connecting, self._connected)
410
411 def _buildSetupFrame(self):
412 """Build the setup frame."""
413 setupFrame = gtk.Frame(label = "Setup")
414
415 frameAlignment = gtk.Alignment(xalign = 0.5)
416
417 frameAlignment.set_padding(padding_top = 4, padding_bottom = 10,
418 padding_left = 16, padding_right = 16)
419
420 setupFrame.add(frameAlignment)
421
422 setupBox = gtk.HBox()
423 frameAlignment.add(setupBox)
424
425 # self._fs9Button = gtk.RadioButton(label = "FS9")
426 # self._fs9Button.set_tooltip_text("Use MS Flight Simulator 2004")
427 # setupBox.pack_start(self._fs9Button, False, False, 0)
428
429 # self._fsxButton = gtk.RadioButton(group = self._fs9Button, label = "FSX")
430 # self._fsxButton.set_tooltip_text("Use MS Flight Simulator X")
431 # setupBox.pack_start(self._fsxButton, False, False, 0)
432
433 # setupBox.pack_start(gtk.VSeparator(), False, False, 8)
434
435 alignment = gtk.Alignment(yalign = 0.5)
436 alignment.set_padding(padding_top = 0, padding_bottom = 0,
437 padding_left = 0, padding_right = 16)
438 alignment.add(gtk.Label("Aicraft:"))
439 setupBox.pack_start(alignment, False, False, 0)
440
441 acftListModel = gtk.ListStore(str, int)
442 for (name, type) in acftTypes:
443 acftListModel.append([name, type])
444
445 self._acftList = gtk.ComboBox(model = acftListModel)
446 renderer_text = gtk.CellRendererText()
447 self._acftList.pack_start(renderer_text, True)
448 self._acftList.add_attribute(renderer_text, "text", 0)
449 self._acftList.set_active(0)
450 self._acftList.set_tooltip_text("Select the type of the aircraft used for the flight.")
451
452 setupBox.pack_start(self._acftList, True, True, 0)
453
454 setupBox.pack_start(gtk.VSeparator(), False, False, 8)
455
456 alignment = gtk.Alignment(yalign = 0.5)
457 alignment.set_padding(padding_top = 0, padding_bottom = 0,
458 padding_left = 0, padding_right = 16)
459 alignment.add(gtk.Label("Cruise FL:"))
460 setupBox.pack_start(alignment, False, False, 0)
461
462 self._flSpinButton = gtk.SpinButton()
463 self._flSpinButton.set_increments(step = 10, page = 100)
464 self._flSpinButton.set_range(min = 0, max = 500)
465 self._flSpinButton.set_value(240)
466 self._flSpinButton.set_tooltip_text("The cruise flight level.")
467 self._flSpinButton.set_numeric(True)
468
469 setupBox.pack_start(self._flSpinButton, False, False, 0)
470
471 setupBox.pack_start(gtk.VSeparator(), False, False, 8)
472
473 alignment = gtk.Alignment(yalign = 0.5)
474 alignment.set_padding(padding_top = 0, padding_bottom = 0,
475 padding_left = 0, padding_right = 16)
476 alignment.add(gtk.Label("ZFW:"))
477 setupBox.pack_start(alignment, False, False, 0)
478
479 self._zfwSpinButton = gtk.SpinButton()
480 self._zfwSpinButton.set_increments(step = 100, page = 1000)
481 self._zfwSpinButton.set_range(min = 0, max = 500000)
482 self._zfwSpinButton.set_value(50000)
483 self._zfwSpinButton.set_tooltip_text("The Zero Fuel Weight for the flight in kgs")
484 self._zfwSpinButton.set_numeric(True)
485
486 setupBox.pack_start(self._zfwSpinButton, False, False, 0)
487
488 setupBox.pack_start(gtk.VSeparator(), False, False, 8)
489
490 self._connectButton = gtk.ToggleButton(label = "_Connect",
491 use_underline = True)
492 self._connectButton.set_tooltip_text("Push to connect to Flight Simulator and start a new flight.\n"
493 "Push again to disconnect from FS.")
494 self._connectButton.set_can_default(True)
495
496 self._connectButton.connect("toggled", self._connectToggled)
497
498 setupBox.pack_start(self._connectButton, False, False, 0)
499
500 setupBox.pack_start(gtk.VSeparator(), False, False, 8)
501
502 self._quitButton = gtk.Button(label = "_Quit", use_underline = True)
503 self._quitButton.set_tooltip_text("Quit the program.")
504
505 self._quitButton.connect("clicked", self._quit)
506
507 setupBox.pack_start(self._quitButton, False, False, 0)
508
509 return setupFrame
510
511 def _createLabeledEntry(self, label, width = 8, xalign = 1.0):
512 """Create a labeled entry.
513
514 Return a tuple consisting of:
515 - the box
516 - the entry."""
517
518 alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5, xscale = 1.0)
519 alignment.set_padding(padding_top = 0, padding_bottom = 0,
520 padding_left = 0, padding_right = 16)
521 alignment.add(gtk.Label(label))
522
523 entry = gtk.Entry()
524 entry.set_editable(False)
525 entry.set_width_chars(width)
526 entry.set_max_length(width)
527 entry.set_alignment(xalign)
528
529 return (alignment, entry)
530
531 def _buildDataFrame(self):
532 """Build the frame for the data."""
533 dataFrame = gtk.Frame(label = "Data")
534
535 frameAlignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
536
537 frameAlignment.set_padding(padding_top = 4, padding_bottom = 10,
538 padding_left = 16, padding_right = 16)
539
540 table = gtk.Table(rows = 7, columns = 12)
541 table.set_homogeneous(False)
542 table.set_row_spacings(4)
543 table.set_col_spacings(8)
544
545 (label, self._timestamp) = self._createLabeledEntry("Time:")
546 table.attach(label, 0, 1, 0, 1)
547 table.attach(self._timestamp, 1, 2, 0, 1)
548
549 self._paused = gtk.Label("PAUSED")
550 table.attach(self._paused, 2, 4, 0, 1)
551
552 self._trickMode = gtk.Label("TRICKMODE")
553 table.attach(self._trickMode, 4, 6, 0, 1, xoptions = 0)
554
555 self._overspeed = gtk.Label("OVERSPEED")
556 table.attach(self._overspeed, 6, 8, 0, 1)
557
558 self._stalled = gtk.Label("STALLED")
559 table.attach(self._stalled, 8, 10, 0, 1)
560
561 self._onTheGround = gtk.Label("ONTHEGROUND")
562 table.attach(self._onTheGround, 10, 12, 0, 1)
563
564 (label, self._zfw) = self._createLabeledEntry("ZFW:", 6)
565 table.attach(label, 0, 1, 1, 2)
566 table.attach(self._zfw, 1, 2, 1, 2)
567
568 (label, self._grossWeight) = self._createLabeledEntry("Weight:", 6)
569 table.attach(label, 2, 3, 1, 2)
570 table.attach(self._grossWeight, 3, 4, 1, 2)
571
572 (label, self._heading) = self._createLabeledEntry("Heading:", 3)
573 table.attach(label, 4, 5, 1, 2)
574 table.attach(self._heading, 5, 6, 1, 2)
575
576 (label, self._pitch) = self._createLabeledEntry("Pitch:", 3)
577 table.attach(label, 6, 7, 1, 2)
578 table.attach(self._pitch, 7, 8, 1, 2)
579
580 (label, self._bank) = self._createLabeledEntry("Bank:", 3)
581 table.attach(label, 8, 9, 1, 2)
582 table.attach(self._bank, 9, 10, 1, 2)
583
584 (label, self._vs) = self._createLabeledEntry("VS:", 5)
585 table.attach(label, 10, 11, 1, 2)
586 table.attach(self._vs, 11, 12, 1, 2)
587
588 (label, self._ias) = self._createLabeledEntry("IAS:", 4)
589 table.attach(label, 0, 1, 2, 3)
590 table.attach(self._ias, 1, 2, 2, 3)
591
592 (label, self._mach) = self._createLabeledEntry("Mach:", 4)
593 table.attach(label, 2, 3, 2, 3)
594 table.attach(self._mach, 3, 4, 2, 3)
595
596 (label, self._groundSpeed) = self._createLabeledEntry("GS:", 4)
597 table.attach(label, 4, 5, 2, 3)
598 table.attach(self._groundSpeed, 5, 6, 2, 3)
599
600 (label, self._radioAltitude) = self._createLabeledEntry("Radio alt.:", 6)
601 table.attach(label, 6, 7, 2, 3)
602 table.attach(self._radioAltitude, 7, 8, 2, 3)
603
604 (label, self._altitude) = self._createLabeledEntry("Altitude:", 6)
605 table.attach(label, 8, 9, 2, 3)
606 table.attach(self._altitude, 9, 10, 2, 3)
607
608 (label, self._gLoad) = self._createLabeledEntry("G-Load:", 4)
609 table.attach(label, 10, 11, 2, 3)
610 table.attach(self._gLoad, 11, 12, 2, 3)
611
612 (label, self._flapsSet) = self._createLabeledEntry("Flaps set:", 2)
613 table.attach(label, 0, 1, 3, 4)
614 table.attach(self._flapsSet, 1, 2, 3, 4)
615
616 (label, self._flaps) = self._createLabeledEntry("Flaps:", 2)
617 table.attach(label, 2, 3, 3, 4)
618 table.attach(self._flaps, 3, 4, 3, 4)
619
620 (label, self._altimeter) = self._createLabeledEntry("Altimeter:", 4)
621 table.attach(label, 4, 5, 3, 4)
622 table.attach(self._altimeter, 5, 6, 3, 4)
623
624 (label, self._squawk) = self._createLabeledEntry("Squawk:", 4)
625 table.attach(label, 6, 7, 3, 4)
626 table.attach(self._squawk, 7, 8, 3, 4)
627
628 (label, self._nav1) = self._createLabeledEntry("NAV1:", 5)
629 table.attach(label, 8, 9, 3, 4)
630 table.attach(self._nav1, 9, 10, 3, 4)
631
632 (label, self._nav2) = self._createLabeledEntry("NAV2:", 5)
633 table.attach(label, 10, 11, 3, 4)
634 table.attach(self._nav2, 11, 12, 3, 4)
635
636 (label, self._fuel) = self._createLabeledEntry("Fuel:", 40, xalign = 0.0)
637 table.attach(label, 0, 1, 4, 5)
638 table.attach(self._fuel, 1, 4, 4, 5)
639
640 (label, self._n1) = self._createLabeledEntry("N1/RPM:", 20, xalign = 0.0)
641 table.attach(label, 4, 5, 4, 5)
642 table.attach(self._n1, 5, 8, 4, 5)
643
644 (label, self._reverser) = self._createLabeledEntry("Reverser:", 20, xalign = 0.0)
645 table.attach(label, 8, 9, 4, 5)
646 table.attach(self._reverser, 9, 12, 4, 5)
647
648 self._navLightsOn = gtk.Label("NAV")
649 table.attach(self._navLightsOn, 0, 1, 5, 6)
650
651 self._antiCollisionLightsOn = gtk.Label("ANTICOLLISION")
652 table.attach(self._antiCollisionLightsOn, 1, 3, 5, 6)
653
654 self._strobeLightsOn = gtk.Label("STROBE")
655 table.attach(self._strobeLightsOn, 3, 4, 5, 6)
656
657 self._landingLightsOn = gtk.Label("LANDING")
658 table.attach(self._landingLightsOn, 4, 5, 5, 6)
659
660 self._pitotHeatOn = gtk.Label("PITOT HEAT")
661 table.attach(self._pitotHeatOn, 5, 7, 5, 6)
662
663 self._parking = gtk.Label("PARKING")
664 table.attach(self._parking, 7, 8, 5, 6)
665
666 self._gearsDown = gtk.Label("GEARS DOWN")
667 table.attach(self._gearsDown, 8, 10, 5, 6)
668
669 self._spoilersArmed = gtk.Label("SPOILERS ARMED")
670 table.attach(self._spoilersArmed, 10, 12, 5, 6)
671
672 (label, self._spoilersExtension) = self._createLabeledEntry("Spoilers:", 3)
673 table.attach(label, 0, 1, 6, 7)
674 table.attach(self._spoilersExtension, 1, 2, 6, 7)
675
676 (label, self._windSpeed) = self._createLabeledEntry("Wind speed:", 3)
677 table.attach(label, 2, 3, 6, 7)
678 table.attach(self._windSpeed, 3, 4, 6, 7)
679
680 (label, self._windDirection) = self._createLabeledEntry("Wind from:", 3)
681 table.attach(label, 4, 5, 6, 7)
682 table.attach(self._windDirection, 5, 6, 6, 7)
683
684 frameAlignment.add(table)
685
686 dataFrame.add(frameAlignment)
687
688 self._setData()
689
690 return dataFrame
691
692 def _setData(self, aircraftState = None):
693 """Set the data.
694
695 If aircraftState is None, everything will be set to its default."""
696 if aircraftState is None:
697 self._timestamp.set_text("--:--:--")
698 self._paused.set_sensitive(False)
699 self._trickMode.set_sensitive(False)
700 self._overspeed.set_sensitive(False)
701 self._stalled.set_sensitive(False)
702 self._onTheGround.set_sensitive(False)
703 self._zfw.set_text("-")
704 self._grossWeight.set_text("-")
705 self._heading.set_text("-")
706 self._pitch.set_text("-")
707 self._bank.set_text("-")
708 self._vs.set_text("-")
709 self._ias.set_text("-")
710 self._mach.set_text("-")
711 self._groundSpeed.set_text("-")
712 self._radioAltitude.set_text("-")
713 self._altitude.set_text("-")
714 self._gLoad.set_text("-")
715 self._flapsSet.set_text("-")
716 self._flaps.set_text("-")
717 self._altimeter.set_text("-")
718 self._squawk.set_text("-")
719 self._nav1.set_text("-")
720 self._nav2.set_text("-")
721 self._fuel.set_text("-")
722 self._n1.set_text("-")
723 self._reverser.set_text("-")
724 self._navLightsOn.set_sensitive(False)
725 self._antiCollisionLightsOn.set_sensitive(False)
726 self._strobeLightsOn.set_sensitive(False)
727 self._landingLightsOn.set_sensitive(False)
728 self._pitotHeatOn.set_sensitive(False)
729 self._parking.set_sensitive(False)
730 self._gearsDown.set_sensitive(False)
731 self._spoilersArmed.set_sensitive(False)
732 self._spoilersExtension.set_text("-")
733 self._windSpeed.set_text("-")
734 self._windDirection.set_text("-")
735 else:
736 self._timestamp.set_text(time.strftime("%H:%M:%S",
737 time.gmtime(aircraftState.timestamp)))
738 self._paused.set_sensitive(aircraftState.paused)
739 self._trickMode.set_sensitive(aircraftState.trickMode)
740 self._overspeed.set_sensitive(aircraftState.overspeed)
741 self._stalled.set_sensitive(aircraftState.stalled)
742 self._onTheGround.set_sensitive(aircraftState.onTheGround)
743 self._zfw.set_text("%.0f" % (aircraftState.zfw,))
744 self._grossWeight.set_text("%.0f" % (aircraftState.grossWeight,))
745 self._heading.set_text("%03.0f" % (aircraftState.heading,))
746 self._pitch.set_text("%.0f" % (aircraftState.pitch,))
747 self._bank.set_text("%.0f" % (aircraftState.bank,))
748 self._vs.set_text("%.0f" % (aircraftState.vs,))
749 self._ias.set_text("%.0f" % (aircraftState.ias,))
750 self._mach.set_text("%.2f" % (aircraftState.mach,))
751 self._groundSpeed.set_text("%.0f" % (aircraftState.groundSpeed,))
752 self._radioAltitude.set_text("%.0f" % (aircraftState.radioAltitude,))
753 self._altitude.set_text("%.0f" % (aircraftState.altitude,))
754 self._gLoad.set_text("%.2f" % (aircraftState.gLoad,))
755 self._flapsSet.set_text("%.0f" % (aircraftState.flapsSet,))
756 self._flaps.set_text("%.0f" % (aircraftState.flaps,))
757 self._altimeter.set_text("%.0f" % (aircraftState.altimeter,))
758 self._squawk.set_text(aircraftState.squawk)
759 self._nav1.set_text(aircraftState.nav1)
760 self._nav2.set_text(aircraftState.nav2)
761
762 fuelStr = ""
763 for fuel in aircraftState.fuel:
764 if fuelStr: fuelStr += ", "
765 fuelStr += "%.0f" % (fuel,)
766 self._fuel.set_text(fuelStr)
767
768 if hasattr(aircraftState, "n1"):
769 n1Str = ""
770 for n1 in aircraftState.n1:
771 if n1Str: n1Str += ", "
772 n1Str += "%.0f" % (n1,)
773 elif hasattr(aircraftState, "rpm"):
774 n1Str = ""
775 for rpm in aircraftState.rpm:
776 if n1Str: n1Str += ", "
777 n1Str += "%.0f" % (rpm,)
778 else:
779 n1Str = "-"
780 self._n1.set_text(n1Str)
781
782 reverserStr = ""
783 for reverser in aircraftState.reverser:
784 if reverserStr: reverserStr += ", "
785 reverserStr += "ON" if reverser else "OFF"
786 self._reverser.set_text(reverserStr)
787
788 self._navLightsOn.set_sensitive(aircraftState.navLightsOn)
789 self._antiCollisionLightsOn.set_sensitive(aircraftState.antiCollisionLightsOn)
790 self._strobeLightsOn.set_sensitive(aircraftState.strobeLightsOn)
791 self._landingLightsOn.set_sensitive(aircraftState.landingLightsOn)
792 self._pitotHeatOn.set_sensitive(aircraftState.pitotHeatOn)
793 self._parking.set_sensitive(aircraftState.parking)
794 self._gearsDown.set_sensitive(aircraftState.gearsDown)
795 self._spoilersArmed.set_sensitive(aircraftState.spoilersArmed)
796 self._spoilersExtension.set_text("%.0f" % (aircraftState.spoilersExtension,))
797 self._windSpeed.set_text("%.0f" % (aircraftState.windSpeed,))
798 self._windDirection.set_text("%03.0f" % (aircraftState.windDirection,))
799
800 def _buildLogFrame(self):
801 """Build the frame for the log."""
802 logFrame = gtk.Frame(label = "Log")
803
804 frameAlignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
805
806 frameAlignment.set_padding(padding_top = 4, padding_bottom = 10,
807 padding_left = 16, padding_right = 16)
808
809 logFrame.add(frameAlignment)
810
811 logScroller = gtk.ScrolledWindow()
812 self._logView = gtk.TextView()
813 self._logView.set_editable(False)
814 logScroller.add(self._logView)
815
816 logBox = gtk.VBox()
817 logBox.pack_start(logScroller, True, True, 0)
818 logBox.set_size_request(-1, 200)
819
820 frameAlignment.add(logBox)
821
822 return logFrame
823
824 def _writeLog(self, msg):
825 """Write the given message to the log."""
826 buffer = self._logView.get_buffer()
827 buffer.insert(buffer.get_end_iter(), msg)
828 self._logView.scroll_mark_onscreen(buffer.get_insert())
829
830 def _quit(self, what = None):
831 """Quit from the application."""
832 self._statusIcon.destroy()
833 return gtk.main_quit()
834
835 def _notebookPageSwitch(self, notebook, page, page_num):
836 """Called when the current page of the notebook has changed."""
837 if page_num==0:
838 gobject.idle_add(self._wizard.grabDefault)
839 elif page_num==1:
840 gobject.idle_add(self._connectButton.grab_default)
841 else:
842 self._mainWindow.set_default(None)
843
844class TrackerStatusIcon(gtk.StatusIcon):
845 def __init__(self):
846 gtk.StatusIcon.__init__(self)
847 menu = '''
848 <ui>
849 <menubar name="Menubar">
850 <menu action="Menu">
851 <menuitem action="Search"/>
852 <menuitem action="Preferences"/>
853 <separator/>
854 <menuitem action="About"/>
855 </menu>
856 </menubar>
857 </ui>
858 '''
859 actions = [
860 ('Menu', None, 'Menu'),
861 ('Search', None, '_Search...', None, 'Search files with MetaTracker', self.on_activate),
862 ('Preferences', gtk.STOCK_PREFERENCES, '_Preferences...', None, 'Change MetaTracker preferences', self.on_preferences),
863 ('About', gtk.STOCK_ABOUT, '_About...', None, 'About MetaTracker', self.on_about)]
864 ag = gtk.ActionGroup('Actions')
865 ag.add_actions(actions)
866 self.manager = gtk.UIManager()
867 self.manager.insert_action_group(ag, 0)
868 self.manager.add_ui_from_string(menu)
869 self.menu = self.manager.get_widget('/Menubar/Menu/About').props.parent
870 search = self.manager.get_widget('/Menubar/Menu/Search')
871 search.get_children()[0].set_markup('<b>_Search...</b>')
872 search.get_children()[0].set_use_underline(True)
873 search.get_children()[0].set_use_markup(True)
874 #search.get_children()[1].set_from_stock(gtk.STOCK_FIND, gtk.ICON_SIZE_MENU)
875 self.set_from_stock(gtk.STOCK_FIND)
876 self.set_tooltip('Tracker Desktop Search')
877 self.set_visible(True)
878 self.connect('activate', self.on_activate)
879 self.connect('popup-menu', self.on_popup_menu)
880
881 def on_activate(self, data):
882 os.spawnlpe(os.P_NOWAIT, 'tracker-search-tool', os.environ)
883
884 def on_popup_menu(self, status, button, time):
885 self.menu.popup(None, None, None, button, time)
886
887 def on_preferences(self, data):
888 print 'preferences'
889
890 def on_about(self, data):
891 dialog = gtk.AboutDialog()
892 dialog.set_name('Tracker')
893 dialog.set_version('0.5.0')
894 dialog.set_comments('A desktop indexing and search tool')
895 dialog.set_website('www.freedesktop.org/Tracker')
896 dialog.run()
897 dialog.destroy()
Note: See TracBrowser for help on using the repository browser.