source: src/mlx/gui/gui.py@ 134:9ce031d5d4a9

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

Most of the remaining messages are implemented

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