source: src/mlx/gui/gui.py@ 148:453ebaea9090

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

Implemented FS time synchronization

File size: 29.7 KB
RevLine 
[28]1# The main file for the GUI
2
[29]3from statusicon import StatusIcon
[32]4from statusbar import Statusbar
[90]5from info import FlightInfo
[36]6from update import Updater
[29]7from mlx.gui.common import *
[42]8from mlx.gui.flight import Wizard
[77]9from mlx.gui.monitor import MonitorWindow
[117]10from mlx.gui.weighthelp import WeightHelp
[118]11from mlx.gui.gates import FleetGateStatus
[123]12from mlx.gui.prefs import Preferences
[28]13
14import mlx.const as const
15import mlx.fs as fs
16import mlx.flight as flight
17import mlx.logger as logger
18import mlx.acft as acft
[41]19import mlx.web as web
[107]20from mlx.i18n import xstr
[28]21
22import time
[38]23import threading
24import sys
[28]25
[77]26#------------------------------------------------------------------------------
27
[28]28class GUI(fs.ConnectionListener):
29 """The main GUI class."""
[96]30 @staticmethod
31 def _formatFlightLogLine(timeStr, line):
32 """Format the given line for flight logging."""
33 if timeStr is not None:
34 line = timeStr + ": " + line
35 return line + "\n"
36
[36]37 def __init__(self, programDirectory, config):
[28]38 """Construct the GUI."""
39 gobject.threads_init()
40
[36]41 self._programDirectory = programDirectory
[42]42 self.config = config
[28]43 self._connecting = False
[59]44 self._reconnecting = False
[28]45 self._connected = False
[96]46 self._logger = logger.Logger(self)
[28]47 self._flight = None
48 self._simulator = None
[59]49 self._monitoring = False
[130]50
[119]51 self._fleet = None
[130]52
[119]53 self._fleetCallback = None
[38]54
[130]55 self._updatePlaneCallback = None
56 self._updatePlaneTailNumber = None
57 self._updatePlaneStatus = None
58 self._updatePlaneGateNumber = None
59
[38]60 self._stdioLock = threading.Lock()
61 self._stdioText = ""
[28]62
[41]63 self.webHandler = web.Handler()
64 self.webHandler.start()
65
[38]66 self.toRestart = False
67
[29]68 def build(self, iconDirectory):
[28]69 """Build the GUI."""
[29]70
[123]71 self._mainWindow = window = gtk.Window()
[105]72 window.set_title(WINDOW_TITLE_BASE)
[36]73 window.set_icon_from_file(os.path.join(iconDirectory, "logo.ico"))
74 window.connect("delete-event",
75 lambda a, b: self.hideMainWindow())
76 window.connect("window-state-event", self._handleMainWindowState)
[93]77 accelGroup = gtk.AccelGroup()
78 window.add_accel_group(accelGroup)
[28]79
80 mainVBox = gtk.VBox()
[36]81 window.add(mainVBox)
[28]82
[123]83 self._preferences = Preferences(self)
84
[93]85 menuBar = self._buildMenuBar(accelGroup)
86 mainVBox.pack_start(menuBar, False, False, 0)
87
[92]88 self._notebook = gtk.Notebook()
[93]89 mainVBox.pack_start(self._notebook, True, True, 4)
[142]90
[46]91 self._wizard = Wizard(self)
[107]92 label = gtk.Label(xstr("tab_flight"))
[46]93 label.set_use_underline(True)
[108]94 label.set_tooltip_text(xstr("tab_flight_tooltip"))
[92]95 self._notebook.append_page(self._wizard, label)
[42]96
[90]97 self._flightInfo = FlightInfo(self)
[107]98 label = gtk.Label(xstr("tab_flight_info"))
[90]99 label.set_use_underline(True)
[108]100 label.set_tooltip_text(xstr("tab_flight_info_tooltip"))
[92]101 self._notebook.append_page(self._flightInfo, label)
[93]102 self._flightInfo.disable()
[90]103
[117]104 self._weightHelp = WeightHelp(self)
105 label = gtk.Label(xstr("tab_weight_help"))
106 label.set_use_underline(True)
107 label.set_tooltip_text(xstr("tab_weight_help_tooltip"))
108 self._notebook.append_page(self._weightHelp, label)
109
[93]110 (logWidget, self._logView) = self._buildLogWidget()
[107]111 label = gtk.Label(xstr("tab_log"))
[46]112 label.set_use_underline(True)
[108]113 label.set_tooltip_text(xstr("tab_log_tooltip"))
[93]114 self._notebook.append_page(logWidget, label)
115
[118]116 self._fleetGateStatus = FleetGateStatus(self)
117 label = gtk.Label(xstr("tab_gates"))
118 label.set_use_underline(True)
119 label.set_tooltip_text(xstr("tab_gates_tooltip"))
120 self._notebook.append_page(self._fleetGateStatus, label)
121
[93]122 (self._debugLogWidget, self._debugLogView) = self._buildLogWidget()
123 self._debugLogWidget.show_all()
[32]124
125 mainVBox.pack_start(gtk.HSeparator(), False, False, 0)
126
127 self._statusbar = Statusbar()
128 mainVBox.pack_start(self._statusbar, False, False, 0)
129
[92]130 self._notebook.connect("switch-page", self._notebookPageSwitch)
[46]131
[77]132 self._monitorWindow = MonitorWindow(self, iconDirectory)
[93]133 self._monitorWindow.add_accel_group(accelGroup)
[77]134 self._monitorWindowX = None
135 self._monitorWindowY = None
[93]136 self._selfToggling = False
[77]137
[46]138 window.show_all()
139 self._wizard.grabDefault()
[117]140 self._weightHelp.reset()
141 self._weightHelp.disable()
[28]142
[29]143 self._statusIcon = StatusIcon(iconDirectory, self)
[28]144
[49]145 self._busyCursor = gdk.Cursor(gdk.CursorType.WATCH if pygobject
146 else gdk.WATCH)
147
[59]148 @property
[105]149 def mainWindow(self):
150 """Get the main window of the GUI."""
151 return self._mainWindow
152
153 @property
[97]154 def logger(self):
155 """Get the logger used by us."""
156 return self._logger
157
158 @property
[59]159 def simulator(self):
160 """Get the simulator used by us."""
161 return self._simulator
162
[71]163 @property
164 def flight(self):
165 """Get the flight being performed."""
166 return self._flight
[84]167
168 @property
[139]169 def loginResult(self):
170 """Get the result of the login."""
171 return self._wizard.loginResult
172
173 @property
[97]174 def bookedFlight(self):
175 """Get the booked flight selected, if any."""
176 return self._wizard.bookedFlight
177
178 @property
179 def cargoWeight(self):
180 """Get the cargo weight."""
181 return self._wizard.cargoWeight
182
183 @property
[84]184 def zfw(self):
185 """Get Zero-Fuel Weight calculated for the current flight."""
186 return self._wizard.zfw
187
188 @property
[97]189 def filedCruiseAltitude(self):
190 """Get cruise altitude filed for the current flight."""
191 return self._wizard.filedCruiseAltitude
192
193 @property
[84]194 def cruiseAltitude(self):
[97]195 """Get cruise altitude set for the current flight."""
[84]196 return self._wizard.cruiseAltitude
[97]197
198 @property
199 def route(self):
200 """Get the flight route."""
201 return self._wizard.route
202
203 @property
204 def departureMETAR(self):
205 """Get the METAR of the deprature airport."""
206 return self._wizard.departureMETAR
[84]207
208 @property
[97]209 def arrivalMETAR(self):
210 """Get the METAR of the deprature airport."""
211 return self._wizard.arrivalMETAR
212
213 @property
214 def departureRunway(self):
215 """Get the name of the departure runway."""
216 return self._wizard.departureRunway
217
218 @property
219 def sid(self):
220 """Get the SID."""
221 return self._wizard.sid
222
223 @property
[84]224 def v1(self):
225 """Get the V1 speed calculated for the flight."""
226 return self._wizard.v1
227
228 @property
229 def vr(self):
230 """Get the Vr speed calculated for the flight."""
231 return self._wizard.vr
232
233 @property
234 def v2(self):
235 """Get the V2 speed calculated for the flight."""
236 return self._wizard.v2
[71]237
[86]238 @property
[97]239 def arrivalRunway(self):
240 """Get the arrival runway."""
241 return self._wizard.arrivalRunway
242
243 @property
244 def star(self):
245 """Get the STAR."""
246 return self._wizard.star
247
248 @property
249 def transition(self):
250 """Get the transition."""
251 return self._wizard.transition
252
253 @property
254 def approachType(self):
255 """Get the approach type."""
256 return self._wizard.approachType
257
258 @property
[86]259 def vref(self):
260 """Get the Vref speed calculated for the flight."""
261 return self._wizard.vref
262
[97]263 @property
264 def flightType(self):
265 """Get the flight type."""
266 return self._wizard.flightType
267
268 @property
269 def online(self):
270 """Get whether the flight was online or not."""
271 return self._wizard.online
272
273 @property
274 def comments(self):
275 """Get the comments."""
276 return self._flightInfo.comments
277
278 @property
279 def flightDefects(self):
280 """Get the flight defects."""
281 return self._flightInfo.flightDefects
282
[99]283 @property
284 def delayCodes(self):
285 """Get the delay codes."""
286 return self._flightInfo.delayCodes
287
[28]288 def run(self):
289 """Run the GUI."""
[42]290 if self.config.autoUpdate:
[38]291 self._updater = Updater(self,
292 self._programDirectory,
[42]293 self.config.updateURL,
[36]294 self._mainWindow)
295 self._updater.start()
296
[28]297 gtk.main()
[36]298
[91]299 self._disconnect()
[28]300
301 def connected(self, fsType, descriptor):
302 """Called when we have connected to the simulator."""
303 self._connected = True
304 self._logger.untimedMessage("Connected to the simulator %s" % (descriptor,))
[133]305 fs.sendMessage(const.MESSAGETYPE_INFORMATION,
306 "Welcome to MAVA Logger X " + const.VERSION)
[59]307 gobject.idle_add(self._handleConnected, fsType, descriptor)
308
309 def _handleConnected(self, fsType, descriptor):
310 """Called when the connection to the simulator has succeeded."""
311 self._statusbar.updateConnection(self._connecting, self._connected)
312 self.endBusy()
313 if not self._reconnecting:
314 self._wizard.connected(fsType, descriptor)
315 self._reconnecting = False
316
317 def connectionFailed(self):
318 """Called when the connection failed."""
319 self._logger.untimedMessage("Connection to the simulator failed")
320 gobject.idle_add(self._connectionFailed)
321
322 def _connectionFailed(self):
323 """Called when the connection failed."""
324 self.endBusy()
325 self._statusbar.updateConnection(self._connecting, self._connected)
326
[105]327 dialog = gtk.MessageDialog(parent = self._mainWindow,
328 type = MESSAGETYPE_ERROR,
[108]329 message_format = xstr("conn_failed"))
330
[105]331 dialog.set_title(WINDOW_TITLE_BASE)
[108]332 dialog.format_secondary_markup(xstr("conn_failed_sec"))
[59]333
[108]334 dialog.add_button(xstr("button_cancel"), 0)
335 dialog.add_button(xstr("button_tryagain"), 1)
[59]336 dialog.set_default_response(1)
337
338 result = dialog.run()
339 dialog.hide()
340 if result == 1:
[107]341 self.beginBusy(xstr("connect_busy"))
[59]342 self._simulator.reconnect()
343 else:
[91]344 self.reset()
[28]345
346 def disconnected(self):
347 """Called when we have disconnected from the simulator."""
348 self._connected = False
349 self._logger.untimedMessage("Disconnected from the simulator")
[59]350
351 gobject.idle_add(self._disconnected)
352
353 def _disconnected(self):
354 """Called when we have disconnected from the simulator unexpectedly."""
355 self._statusbar.updateConnection(self._connecting, self._connected)
356
357 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
[108]358 message_format = xstr("conn_broken"),
[59]359 parent = self._mainWindow)
[105]360 dialog.set_title(WINDOW_TITLE_BASE)
[108]361 dialog.format_secondary_markup(xstr("conn_broken_sec"))
[59]362
[108]363 dialog.add_button(xstr("button_cancel"), 0)
364 dialog.add_button(xstr("button_reconnect"), 1)
[59]365 dialog.set_default_response(1)
366
367 result = dialog.run()
368 dialog.hide()
369 if result == 1:
[108]370 self.beginBusy(xstr("connect_busy"))
[59]371 self._reconnecting = True
372 self._simulator.reconnect()
373 else:
[91]374 self.reset()
375
[93]376 def enableFlightInfo(self):
377 """Enable the flight info tab."""
378 self._flightInfo.enable()
379
[91]380 def reset(self):
381 """Reset the GUI."""
382 self._disconnect()
[92]383
[91]384 self._flightInfo.reset()
[93]385 self._flightInfo.disable()
[91]386 self.resetFlightStatus()
[28]387
[117]388 self._weightHelp.reset()
389 self._weightHelp.disable()
[92]390 self._wizard.reset()
391 self._notebook.set_current_page(0)
392
393 self._logView.get_buffer().set_text("")
394
[91]395 def _disconnect(self):
396 """Disconnect from the simulator if connected."""
[92]397 self.stopMonitoring()
398
[91]399 if self._connected:
400 self._flight.simulator.disconnect()
401 self._connected = False
402
403 self._connecting = False
404 self._reconnecting = False
405 self._statusbar.updateConnection(False, False)
[128]406 self._weightHelp.disable()
[134]407
408 return True
[91]409
[96]410 def addFlightLogLine(self, timeStr, line):
411 """Write the given message line to the log."""
412 gobject.idle_add(self._writeLog,
413 GUI._formatFlightLogLine(timeStr, line),
414 self._logView)
415
416 def updateFlightLogLine(self, index, timeStr, line):
417 """Update the line with the given index."""
418 gobject.idle_add(self._updateFlightLogLine, index,
419 GUI._formatFlightLogLine(timeStr, line))
420
421 def _updateFlightLogLine(self, index, line):
422 """Replace the contents of the given line in the log."""
423 buffer = self._logView.get_buffer()
424 startIter = buffer.get_iter_at_line(index)
425 endIter = buffer.get_iter_at_line(index + 1)
426 buffer.delete(startIter, endIter)
427 buffer.insert(startIter, line)
428 self._logView.scroll_mark_onscreen(buffer.get_insert())
429
[28]430 def check(self, flight, aircraft, logger, oldState, state):
431 """Update the data."""
[77]432 gobject.idle_add(self._monitorWindow.setData, state)
[80]433 gobject.idle_add(self._statusbar.updateTime, state.timestamp)
[28]434
[31]435 def resetFlightStatus(self):
436 """Reset the status of the flight."""
[32]437 self._statusbar.resetFlightStatus()
[80]438 self._statusbar.updateTime()
[31]439 self._statusIcon.resetFlightStatus()
440
441 def setStage(self, stage):
442 """Set the stage of the flight."""
443 gobject.idle_add(self._setStage, stage)
444
445 def _setStage(self, stage):
446 """Set the stage of the flight."""
[32]447 self._statusbar.setStage(stage)
[31]448 self._statusIcon.setStage(stage)
[84]449 self._wizard.setStage(stage)
[88]450 if stage==const.STAGE_END:
[134]451 # FIXME: perhaps a more elegant method, e.g.
452 # the simulator should provide a function disconnect
453 # with a final message
[135]454 gobject.timeout_add(1000, self._disconnect)
[31]455
456 def setRating(self, rating):
457 """Set the rating of the flight."""
458 gobject.idle_add(self._setRating, rating)
459
460 def _setRating(self, rating):
461 """Set the rating of the flight."""
[32]462 self._statusbar.setRating(rating)
[31]463 self._statusIcon.setRating(rating)
464
465 def setNoGo(self, reason):
466 """Set the rating of the flight to No-Go with the given reason."""
467 gobject.idle_add(self._setNoGo, reason)
468
469 def _setNoGo(self, reason):
470 """Set the rating of the flight."""
[32]471 self._statusbar.setNoGo(reason)
[31]472 self._statusIcon.setNoGo(reason)
473
[29]474 def _handleMainWindowState(self, window, event):
475 """Hande a change in the state of the window"""
476 iconified = gdk.WindowState.ICONIFIED if pygobject \
477 else gdk.WINDOW_STATE_ICONIFIED
[147]478 if self.config.hideMinimizedWindow and \
479 (event.changed_mask&iconified)!=0 and \
480 (event.new_window_state&iconified)!=0:
[35]481 self.hideMainWindow(savePosition = False)
[29]482
[35]483 def hideMainWindow(self, savePosition = True):
[29]484 """Hide the main window and save its position."""
[35]485 if savePosition:
486 (self._mainWindowX, self._mainWindowY) = \
487 self._mainWindow.get_window().get_root_origin()
488 else:
489 self._mainWindowX = self._mainWindowY = None
[29]490 self._mainWindow.hide()
491 self._statusIcon.mainWindowHidden()
492 return True
493
494 def showMainWindow(self):
495 """Show the main window at its former position."""
[35]496 if self._mainWindowX is not None and self._mainWindowY is not None:
497 self._mainWindow.move(self._mainWindowX, self._mainWindowY)
498
499 self._mainWindow.show()
[29]500 self._mainWindow.deiconify()
[35]501
[29]502 self._statusIcon.mainWindowShown()
503
504 def toggleMainWindow(self):
505 """Toggle the main window."""
506 if self._mainWindow.get_visible():
507 self.hideMainWindow()
508 else:
509 self.showMainWindow()
[36]510
[77]511 def hideMonitorWindow(self, savePosition = True):
512 """Hide the monitor window."""
513 if savePosition:
514 (self._monitorWindowX, self._monitorWindowY) = \
515 self._monitorWindow.get_window().get_root_origin()
516 else:
517 self._monitorWindowX = self._monitorWindowY = None
518 self._monitorWindow.hide()
519 self._statusIcon.monitorWindowHidden()
[93]520 if self._showMonitorMenuItem.get_active():
521 self._selfToggling = True
522 self._showMonitorMenuItem.set_active(False)
[77]523 return True
524
525 def showMonitorWindow(self):
526 """Show the monitor window."""
527 if self._monitorWindowX is not None and self._monitorWindowY is not None:
528 self._monitorWindow.move(self._monitorWindowX, self._monitorWindowY)
529 self._monitorWindow.show_all()
530 self._statusIcon.monitorWindowShown()
[93]531 if not self._showMonitorMenuItem.get_active():
532 self._selfToggling = True
533 self._showMonitorMenuItem.set_active(True)
534
535 def _toggleMonitorWindow(self, menuItem):
536 if self._selfToggling:
537 self._selfToggling = False
538 elif self._monitorWindow.get_visible():
539 self.hideMonitorWindow()
540 else:
541 self.showMonitorWindow()
[77]542
[38]543 def restart(self):
544 """Quit and restart the application."""
545 self.toRestart = True
[81]546 self._quit(force = True)
[38]547
548 def flushStdIO(self):
549 """Flush any text to the standard error that could not be logged."""
550 if self._stdioText:
551 sys.__stderr__.write(self._stdioText)
552
[36]553 def writeStdIO(self, text):
554 """Write the given text into standard I/O log."""
[38]555 with self._stdioLock:
556 self._stdioText += text
557
558 gobject.idle_add(self._writeStdIO)
[36]559
[49]560 def beginBusy(self, message):
561 """Begin a period of background processing."""
[93]562 self._wizard.set_sensitive(False)
[117]563 self._weightHelp.set_sensitive(False)
[49]564 self._mainWindow.get_window().set_cursor(self._busyCursor)
565 self._statusbar.updateBusyState(message)
566
567 def endBusy(self):
568 """End a period of background processing."""
569 self._mainWindow.get_window().set_cursor(None)
[117]570 self._weightHelp.set_sensitive(True)
[93]571 self._wizard.set_sensitive(True)
[49]572 self._statusbar.updateBusyState(None)
573
[117]574 def initializeWeightHelp(self):
575 """Initialize the weight help tab."""
576 self._weightHelp.reset()
577 self._weightHelp.enable()
578
[134]579 def getFleetAsync(self, callback = None, force = None):
580 """Get the fleet asynchronously."""
581 gobject.idle_add(self.getFleet, callback, force)
582
[119]583 def getFleet(self, callback = None, force = False):
584 """Get the fleet.
585
586 If force is False, and we already have a fleet retrieved,
587 that one will be used."""
588 if self._fleet is None or force:
589 self._fleetCallback = callback
590 self.beginBusy(xstr("fleet_busy"))
591 self.webHandler.getFleet(self._fleetResultCallback)
592 else:
593 callback(self._fleet)
594
595 def _fleetResultCallback(self, returned, result):
596 """Called when the fleet has been queried."""
597 gobject.idle_add(self._handleFleetResult, returned, result)
598
599 def _handleFleetResult(self, returned, result):
600 """Handle the fleet result."""
601 self.endBusy()
602 if returned:
603 self._fleet = result.fleet
604 else:
605 self._fleet = None
606
[120]607 dialog = gtk.MessageDialog(parent = self.mainWindow,
[119]608 type = MESSAGETYPE_ERROR,
609 message_format = xstr("fleet_failed"))
[130]610 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
611 dialog.set_title(WINDOW_TITLE_BASE)
612 dialog.run()
613 dialog.hide()
614
615 callback = self._fleetCallback
616 self._fleetCallback = None
617 if callback is not None:
618 callback(self._fleet)
619 self._fleetGateStatus.handleFleet(self._fleet)
620
621 def updatePlane(self, tailNumber, status,
622 gateNumber = None, callback = None):
623 """Update the status of the given plane."""
624 self.beginBusy(xstr("fleet_update_busy"))
625
626 self._updatePlaneCallback = callback
627
628 self._updatePlaneTailNumber = tailNumber
629 self._updatePlaneStatus = status
630 self._updatePlaneGateNumber = gateNumber
631
632 self.webHandler.updatePlane(self._updatePlaneResultCallback,
633 tailNumber, status, gateNumber)
634
635 def _updatePlaneResultCallback(self, returned, result):
636 """Called when the status of a plane has been updated."""
637 gobject.idle_add(self._handleUpdatePlaneResult, returned, result)
638
639 def _handleUpdatePlaneResult(self, returned, result):
640 """Handle the plane update result."""
641 self.endBusy()
642 if returned:
643 success = result.success
644 if success:
645 if self._fleet is not None:
646 self._fleet.updatePlane(self._updatePlaneTailNumber,
647 self._updatePlaneStatus,
648 self._updatePlaneGateNumber)
649 self._fleetGateStatus.handleFleet(self._fleet)
650 else:
651 dialog = gtk.MessageDialog(parent = self.mainWindow,
652 type = MESSAGETYPE_ERROR,
653 message_format = xstr("fleet_update_failed"))
[124]654 dialog.add_button(xstr("button_ok"), RESPONSETYPE_ACCEPT)
[119]655 dialog.set_title(WINDOW_TITLE_BASE)
656 dialog.run()
657 dialog.hide()
658
[130]659 success = None
660
661 callback = self._updatePlaneCallback
662 self._updatePlaneCallback = None
663 if callback is not None:
664 callback(success)
[119]665
[38]666 def _writeStdIO(self):
[36]667 """Perform the real writing."""
[38]668 with self._stdioLock:
669 text = self._stdioText
670 self._stdioText = ""
671 if not text: return
672
[36]673 lines = text.splitlines()
674 if text[-1]=="\n":
675 text = ""
676 else:
677 text = lines[-1]
678 lines = lines[:-1]
679
680 for line in lines:
[96]681 #print >> sys.__stdout__, line
[93]682 self._writeLog(line + "\n", self._debugLogView)
[36]683
684 if text:
[96]685 #print >> sys.__stdout__, text,
[93]686 self._writeLog(text, self._debugLogView)
[51]687
[59]688 def connectSimulator(self, aircraftType):
689 """Connect to the simulator for the first time."""
690 self._logger.reset()
691
692 self._flight = flight.Flight(self._logger, self)
[131]693 self._flight.flareTimeFromFS = self.config.flareTimeFromFS
[59]694 self._flight.aircraftType = aircraftType
695 self._flight.aircraft = acft.Aircraft.create(self._flight)
696 self._flight.aircraft._checkers.append(self)
697
698 if self._simulator is None:
699 self._simulator = fs.createSimulator(const.SIM_MSFS9, self)
[133]700 fs.setupMessageSending(self.config, self._simulator)
[148]701 self._setupTimeSync()
702
[59]703 self._flight.simulator = self._simulator
704
[107]705 self.beginBusy(xstr("connect_busy"))
[59]706 self._statusbar.updateConnection(self._connecting, self._connected)
707
708 self._connecting = True
709 self._simulator.connect(self._flight.aircraft)
710
[70]711 def startMonitoring(self):
712 """Start monitoring."""
[88]713 if not self._monitoring:
714 self.simulator.startMonitoring()
715 self._monitoring = True
[70]716
717 def stopMonitoring(self):
718 """Stop monitoring."""
[88]719 if self._monitoring:
720 self.simulator.stopMonitoring()
721 self._monitoring = False
[70]722
[93]723 def _buildMenuBar(self, accelGroup):
724 """Build the main menu bar."""
725 menuBar = gtk.MenuBar()
726
[110]727 fileMenuItem = gtk.MenuItem(xstr("menu_file"))
[93]728 fileMenu = gtk.Menu()
729 fileMenuItem.set_submenu(fileMenu)
730 menuBar.append(fileMenuItem)
731
732 quitMenuItem = gtk.ImageMenuItem(gtk.STOCK_QUIT)
733 quitMenuItem.set_use_stock(True)
[110]734 quitMenuItem.set_label(xstr("menu_file_quit"))
[93]735 quitMenuItem.add_accelerator("activate", accelGroup,
[110]736 ord(xstr("menu_file_quit_key")),
737 CONTROL_MASK, ACCEL_VISIBLE)
[93]738 quitMenuItem.connect("activate", self._quit)
739 fileMenu.append(quitMenuItem)
740
[123]741 toolsMenuItem = gtk.MenuItem(xstr("menu_tools"))
742 toolsMenu = gtk.Menu()
743 toolsMenuItem.set_submenu(toolsMenu)
744 menuBar.append(toolsMenuItem)
745
746 prefsMenuItem = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
747 prefsMenuItem.set_use_stock(True)
748 prefsMenuItem.set_label(xstr("menu_tools_prefs"))
749 prefsMenuItem.add_accelerator("activate", accelGroup,
750 ord(xstr("menu_tools_prefs_key")),
751 CONTROL_MASK, ACCEL_VISIBLE)
752 prefsMenuItem.connect("activate", self._editPreferences)
753 toolsMenu.append(prefsMenuItem)
[93]754
[110]755 viewMenuItem = gtk.MenuItem(xstr("menu_view"))
[93]756 viewMenu = gtk.Menu()
757 viewMenuItem.set_submenu(viewMenu)
758 menuBar.append(viewMenuItem)
[28]759
[93]760 self._showMonitorMenuItem = gtk.CheckMenuItem()
[110]761 self._showMonitorMenuItem.set_label(xstr("menu_view_monitor"))
[93]762 self._showMonitorMenuItem.set_use_underline(True)
763 self._showMonitorMenuItem.set_active(False)
764 self._showMonitorMenuItem.add_accelerator("activate", accelGroup,
[110]765 ord(xstr("menu_view_monitor_key")),
766 CONTROL_MASK, ACCEL_VISIBLE)
[93]767 self._showMonitorMenuItem.connect("toggled", self._toggleMonitorWindow)
768 viewMenu.append(self._showMonitorMenuItem)
769
770 showDebugMenuItem = gtk.CheckMenuItem()
[110]771 showDebugMenuItem.set_label(xstr("menu_view_debug"))
[93]772 showDebugMenuItem.set_use_underline(True)
773 showDebugMenuItem.set_active(False)
774 showDebugMenuItem.add_accelerator("activate", accelGroup,
[110]775 ord(xstr("menu_view_debug_key")),
776 CONTROL_MASK, ACCEL_VISIBLE)
[93]777 showDebugMenuItem.connect("toggled", self._toggleDebugLog)
778 viewMenu.append(showDebugMenuItem)
[28]779
[93]780 return menuBar
[28]781
[93]782 def _toggleDebugLog(self, menuItem):
783 """Toggle the debug log."""
784 if menuItem.get_active():
[107]785 label = gtk.Label(xstr("tab_debug_log"))
[93]786 label.set_use_underline(True)
[110]787 label.set_tooltip_text(xstr("tab_debug_log_tooltip"))
[93]788 self._debugLogPage = self._notebook.append_page(self._debugLogWidget, label)
789 self._notebook.set_current_page(self._debugLogPage)
790 else:
791 self._notebook.remove_page(self._debugLogPage)
792
793 def _buildLogWidget(self):
794 """Build the widget for the log."""
795 alignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
796
797 alignment.set_padding(padding_top = 8, padding_bottom = 8,
798 padding_left = 16, padding_right = 16)
[28]799
800 logScroller = gtk.ScrolledWindow()
[93]801 # FIXME: these should be constants in common
802 logScroller.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
803 else gtk.POLICY_AUTOMATIC,
804 gtk.PolicyType.AUTOMATIC if pygobject
805 else gtk.POLICY_AUTOMATIC)
806 logScroller.set_shadow_type(gtk.ShadowType.IN if pygobject
807 else gtk.SHADOW_IN)
808 logView = gtk.TextView()
809 logView.set_editable(False)
810 logScroller.add(logView)
[28]811
812 logBox = gtk.VBox()
813 logBox.pack_start(logScroller, True, True, 0)
814 logBox.set_size_request(-1, 200)
815
[93]816 alignment.add(logBox)
[28]817
[93]818 return (alignment, logView)
[28]819
[93]820 def _writeLog(self, msg, logView):
[28]821 """Write the given message to the log."""
[93]822 buffer = logView.get_buffer()
[28]823 buffer.insert(buffer.get_end_iter(), msg)
[93]824 logView.scroll_mark_onscreen(buffer.get_insert())
[28]825
[81]826 def _quit(self, what = None, force = False):
[38]827 """Quit from the application."""
[81]828 if force:
829 result=RESPONSETYPE_YES
830 else:
[105]831 dialog = gtk.MessageDialog(parent = self._mainWindow,
832 type = MESSAGETYPE_QUESTION,
[110]833 message_format = xstr("quit_question"))
834
[124]835 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
836 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
837
[105]838 dialog.set_title(WINDOW_TITLE_BASE)
[81]839 result = dialog.run()
840 dialog.hide()
[76]841
842 if result==RESPONSETYPE_YES:
843 self._statusIcon.destroy()
844 return gtk.main_quit()
[38]845
[46]846 def _notebookPageSwitch(self, notebook, page, page_num):
847 """Called when the current page of the notebook has changed."""
848 if page_num==0:
[48]849 gobject.idle_add(self._wizard.grabDefault)
[46]850 else:
851 self._mainWindow.set_default(None)
[123]852
853 def _editPreferences(self, menuItem):
854 """Callback for editing the preferences."""
855 self._preferences.run(self.config)
[148]856 self._setupTimeSync()
857
858 def _setupTimeSync(self):
859 """Enable or disable the simulator time synchronization based on the
860 configuration."""
861 simulator = self._simulator
862 if simulator is not None:
863 if self.config.syncFSTime:
864 simulator.enableTimeSync()
865 else:
866 simulator.disableTimeSync()
Note: See TracBrowser for help on using the repository browser.