source: src/mlx/gui/gui.py@ 133:dcbe33497899

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

The general message sending works and the most important messages are sent

File size: 28.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)
[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()
[91]402
[96]403 def addFlightLogLine(self, timeStr, line):
404 """Write the given message line to the log."""
405 gobject.idle_add(self._writeLog,
406 GUI._formatFlightLogLine(timeStr, line),
407 self._logView)
408
409 def updateFlightLogLine(self, index, timeStr, line):
410 """Update the line with the given index."""
411 gobject.idle_add(self._updateFlightLogLine, index,
412 GUI._formatFlightLogLine(timeStr, line))
413
414 def _updateFlightLogLine(self, index, line):
415 """Replace the contents of the given line in the log."""
416 buffer = self._logView.get_buffer()
417 startIter = buffer.get_iter_at_line(index)
418 endIter = buffer.get_iter_at_line(index + 1)
419 buffer.delete(startIter, endIter)
420 buffer.insert(startIter, line)
421 self._logView.scroll_mark_onscreen(buffer.get_insert())
422
[28]423 def check(self, flight, aircraft, logger, oldState, state):
424 """Update the data."""
[77]425 gobject.idle_add(self._monitorWindow.setData, state)
[80]426 gobject.idle_add(self._statusbar.updateTime, state.timestamp)
[28]427
[31]428 def resetFlightStatus(self):
429 """Reset the status of the flight."""
[32]430 self._statusbar.resetFlightStatus()
[80]431 self._statusbar.updateTime()
[31]432 self._statusIcon.resetFlightStatus()
433
434 def setStage(self, stage):
435 """Set the stage of the flight."""
436 gobject.idle_add(self._setStage, stage)
437
438 def _setStage(self, stage):
439 """Set the stage of the flight."""
[32]440 self._statusbar.setStage(stage)
[31]441 self._statusIcon.setStage(stage)
[84]442 self._wizard.setStage(stage)
[88]443 if stage==const.STAGE_END:
[91]444 self._disconnect()
[31]445
446 def setRating(self, rating):
447 """Set the rating of the flight."""
448 gobject.idle_add(self._setRating, rating)
449
450 def _setRating(self, rating):
451 """Set the rating of the flight."""
[32]452 self._statusbar.setRating(rating)
[31]453 self._statusIcon.setRating(rating)
454
455 def setNoGo(self, reason):
456 """Set the rating of the flight to No-Go with the given reason."""
457 gobject.idle_add(self._setNoGo, reason)
458
459 def _setNoGo(self, reason):
460 """Set the rating of the flight."""
[32]461 self._statusbar.setNoGo(reason)
[31]462 self._statusIcon.setNoGo(reason)
463
[29]464 def _handleMainWindowState(self, window, event):
465 """Hande a change in the state of the window"""
466 iconified = gdk.WindowState.ICONIFIED if pygobject \
467 else gdk.WINDOW_STATE_ICONIFIED
468 if (event.changed_mask&iconified)!=0 and (event.new_window_state&iconified)!=0:
[35]469 self.hideMainWindow(savePosition = False)
[29]470
[35]471 def hideMainWindow(self, savePosition = True):
[29]472 """Hide the main window and save its position."""
[35]473 if savePosition:
474 (self._mainWindowX, self._mainWindowY) = \
475 self._mainWindow.get_window().get_root_origin()
476 else:
477 self._mainWindowX = self._mainWindowY = None
[29]478 self._mainWindow.hide()
479 self._statusIcon.mainWindowHidden()
480 return True
481
482 def showMainWindow(self):
483 """Show the main window at its former position."""
[35]484 if self._mainWindowX is not None and self._mainWindowY is not None:
485 self._mainWindow.move(self._mainWindowX, self._mainWindowY)
486
487 self._mainWindow.show()
[29]488 self._mainWindow.deiconify()
[35]489
[29]490 self._statusIcon.mainWindowShown()
491
492 def toggleMainWindow(self):
493 """Toggle the main window."""
494 if self._mainWindow.get_visible():
495 self.hideMainWindow()
496 else:
497 self.showMainWindow()
[36]498
[77]499 def hideMonitorWindow(self, savePosition = True):
500 """Hide the monitor window."""
501 if savePosition:
502 (self._monitorWindowX, self._monitorWindowY) = \
503 self._monitorWindow.get_window().get_root_origin()
504 else:
505 self._monitorWindowX = self._monitorWindowY = None
506 self._monitorWindow.hide()
507 self._statusIcon.monitorWindowHidden()
[93]508 if self._showMonitorMenuItem.get_active():
509 self._selfToggling = True
510 self._showMonitorMenuItem.set_active(False)
[77]511 return True
512
513 def showMonitorWindow(self):
514 """Show the monitor window."""
515 if self._monitorWindowX is not None and self._monitorWindowY is not None:
516 self._monitorWindow.move(self._monitorWindowX, self._monitorWindowY)
517 self._monitorWindow.show_all()
518 self._statusIcon.monitorWindowShown()
[93]519 if not self._showMonitorMenuItem.get_active():
520 self._selfToggling = True
521 self._showMonitorMenuItem.set_active(True)
522
523 def _toggleMonitorWindow(self, menuItem):
524 if self._selfToggling:
525 self._selfToggling = False
526 elif self._monitorWindow.get_visible():
527 self.hideMonitorWindow()
528 else:
529 self.showMonitorWindow()
[77]530
[38]531 def restart(self):
532 """Quit and restart the application."""
533 self.toRestart = True
[81]534 self._quit(force = True)
[38]535
536 def flushStdIO(self):
537 """Flush any text to the standard error that could not be logged."""
538 if self._stdioText:
539 sys.__stderr__.write(self._stdioText)
540
[36]541 def writeStdIO(self, text):
542 """Write the given text into standard I/O log."""
[38]543 with self._stdioLock:
544 self._stdioText += text
545
546 gobject.idle_add(self._writeStdIO)
[36]547
[49]548 def beginBusy(self, message):
549 """Begin a period of background processing."""
[93]550 self._wizard.set_sensitive(False)
[117]551 self._weightHelp.set_sensitive(False)
[49]552 self._mainWindow.get_window().set_cursor(self._busyCursor)
553 self._statusbar.updateBusyState(message)
554
555 def endBusy(self):
556 """End a period of background processing."""
557 self._mainWindow.get_window().set_cursor(None)
[117]558 self._weightHelp.set_sensitive(True)
[93]559 self._wizard.set_sensitive(True)
[49]560 self._statusbar.updateBusyState(None)
561
[117]562 def initializeWeightHelp(self):
563 """Initialize the weight help tab."""
564 self._weightHelp.reset()
565 self._weightHelp.enable()
566
[119]567 def getFleet(self, callback = None, force = False):
568 """Get the fleet.
569
570 If force is False, and we already have a fleet retrieved,
571 that one will be used."""
572 if self._fleet is None or force:
573 self._fleetCallback = callback
574 self.beginBusy(xstr("fleet_busy"))
575 self.webHandler.getFleet(self._fleetResultCallback)
576 else:
577 callback(self._fleet)
578
579 def _fleetResultCallback(self, returned, result):
580 """Called when the fleet has been queried."""
581 gobject.idle_add(self._handleFleetResult, returned, result)
582
583 def _handleFleetResult(self, returned, result):
584 """Handle the fleet result."""
585 self.endBusy()
586 if returned:
587 self._fleet = result.fleet
588 else:
589 self._fleet = None
590
[120]591 dialog = gtk.MessageDialog(parent = self.mainWindow,
[119]592 type = MESSAGETYPE_ERROR,
593 message_format = xstr("fleet_failed"))
[130]594 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
595 dialog.set_title(WINDOW_TITLE_BASE)
596 dialog.run()
597 dialog.hide()
598
599 callback = self._fleetCallback
600 self._fleetCallback = None
601 if callback is not None:
602 callback(self._fleet)
603 self._fleetGateStatus.handleFleet(self._fleet)
604
605 def updatePlane(self, tailNumber, status,
606 gateNumber = None, callback = None):
607 """Update the status of the given plane."""
608 self.beginBusy(xstr("fleet_update_busy"))
609
610 self._updatePlaneCallback = callback
611
612 self._updatePlaneTailNumber = tailNumber
613 self._updatePlaneStatus = status
614 self._updatePlaneGateNumber = gateNumber
615
616 self.webHandler.updatePlane(self._updatePlaneResultCallback,
617 tailNumber, status, gateNumber)
618
619 def _updatePlaneResultCallback(self, returned, result):
620 """Called when the status of a plane has been updated."""
621 gobject.idle_add(self._handleUpdatePlaneResult, returned, result)
622
623 def _handleUpdatePlaneResult(self, returned, result):
624 """Handle the plane update result."""
625 self.endBusy()
626 if returned:
627 success = result.success
628 if success:
629 if self._fleet is not None:
630 self._fleet.updatePlane(self._updatePlaneTailNumber,
631 self._updatePlaneStatus,
632 self._updatePlaneGateNumber)
633 self._fleetGateStatus.handleFleet(self._fleet)
634 else:
635 dialog = gtk.MessageDialog(parent = self.mainWindow,
636 type = MESSAGETYPE_ERROR,
637 message_format = xstr("fleet_update_failed"))
[124]638 dialog.add_button(xstr("button_ok"), RESPONSETYPE_ACCEPT)
[119]639 dialog.set_title(WINDOW_TITLE_BASE)
640 dialog.run()
641 dialog.hide()
642
[130]643 success = None
644
645 callback = self._updatePlaneCallback
646 self._updatePlaneCallback = None
647 if callback is not None:
648 callback(success)
[119]649
[38]650 def _writeStdIO(self):
[36]651 """Perform the real writing."""
[38]652 with self._stdioLock:
653 text = self._stdioText
654 self._stdioText = ""
655 if not text: return
656
[36]657 lines = text.splitlines()
658 if text[-1]=="\n":
659 text = ""
660 else:
661 text = lines[-1]
662 lines = lines[:-1]
663
664 for line in lines:
[96]665 #print >> sys.__stdout__, line
[93]666 self._writeLog(line + "\n", self._debugLogView)
[36]667
668 if text:
[96]669 #print >> sys.__stdout__, text,
[93]670 self._writeLog(text, self._debugLogView)
[51]671
[59]672 def connectSimulator(self, aircraftType):
673 """Connect to the simulator for the first time."""
674 self._logger.reset()
675
676 self._flight = flight.Flight(self._logger, self)
[131]677 self._flight.flareTimeFromFS = self.config.flareTimeFromFS
[59]678 self._flight.aircraftType = aircraftType
679 self._flight.aircraft = acft.Aircraft.create(self._flight)
680 self._flight.aircraft._checkers.append(self)
681
682 if self._simulator is None:
683 self._simulator = fs.createSimulator(const.SIM_MSFS9, self)
[133]684 fs.setupMessageSending(self.config, self._simulator)
[59]685
686 self._flight.simulator = self._simulator
687
[107]688 self.beginBusy(xstr("connect_busy"))
[59]689 self._statusbar.updateConnection(self._connecting, self._connected)
690
691 self._connecting = True
692 self._simulator.connect(self._flight.aircraft)
693
[70]694 def startMonitoring(self):
695 """Start monitoring."""
[88]696 if not self._monitoring:
697 self.simulator.startMonitoring()
698 self._monitoring = True
[70]699
700 def stopMonitoring(self):
701 """Stop monitoring."""
[88]702 if self._monitoring:
703 self.simulator.stopMonitoring()
704 self._monitoring = False
[70]705
[93]706 def _buildMenuBar(self, accelGroup):
707 """Build the main menu bar."""
708 menuBar = gtk.MenuBar()
709
[110]710 fileMenuItem = gtk.MenuItem(xstr("menu_file"))
[93]711 fileMenu = gtk.Menu()
712 fileMenuItem.set_submenu(fileMenu)
713 menuBar.append(fileMenuItem)
714
715 quitMenuItem = gtk.ImageMenuItem(gtk.STOCK_QUIT)
716 quitMenuItem.set_use_stock(True)
[110]717 quitMenuItem.set_label(xstr("menu_file_quit"))
[93]718 quitMenuItem.add_accelerator("activate", accelGroup,
[110]719 ord(xstr("menu_file_quit_key")),
720 CONTROL_MASK, ACCEL_VISIBLE)
[93]721 quitMenuItem.connect("activate", self._quit)
722 fileMenu.append(quitMenuItem)
723
[123]724 toolsMenuItem = gtk.MenuItem(xstr("menu_tools"))
725 toolsMenu = gtk.Menu()
726 toolsMenuItem.set_submenu(toolsMenu)
727 menuBar.append(toolsMenuItem)
728
729 prefsMenuItem = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
730 prefsMenuItem.set_use_stock(True)
731 prefsMenuItem.set_label(xstr("menu_tools_prefs"))
732 prefsMenuItem.add_accelerator("activate", accelGroup,
733 ord(xstr("menu_tools_prefs_key")),
734 CONTROL_MASK, ACCEL_VISIBLE)
735 prefsMenuItem.connect("activate", self._editPreferences)
736 toolsMenu.append(prefsMenuItem)
[93]737
[110]738 viewMenuItem = gtk.MenuItem(xstr("menu_view"))
[93]739 viewMenu = gtk.Menu()
740 viewMenuItem.set_submenu(viewMenu)
741 menuBar.append(viewMenuItem)
[28]742
[93]743 self._showMonitorMenuItem = gtk.CheckMenuItem()
[110]744 self._showMonitorMenuItem.set_label(xstr("menu_view_monitor"))
[93]745 self._showMonitorMenuItem.set_use_underline(True)
746 self._showMonitorMenuItem.set_active(False)
747 self._showMonitorMenuItem.add_accelerator("activate", accelGroup,
[110]748 ord(xstr("menu_view_monitor_key")),
749 CONTROL_MASK, ACCEL_VISIBLE)
[93]750 self._showMonitorMenuItem.connect("toggled", self._toggleMonitorWindow)
751 viewMenu.append(self._showMonitorMenuItem)
752
753 showDebugMenuItem = gtk.CheckMenuItem()
[110]754 showDebugMenuItem.set_label(xstr("menu_view_debug"))
[93]755 showDebugMenuItem.set_use_underline(True)
756 showDebugMenuItem.set_active(False)
757 showDebugMenuItem.add_accelerator("activate", accelGroup,
[110]758 ord(xstr("menu_view_debug_key")),
759 CONTROL_MASK, ACCEL_VISIBLE)
[93]760 showDebugMenuItem.connect("toggled", self._toggleDebugLog)
761 viewMenu.append(showDebugMenuItem)
[28]762
[93]763 return menuBar
[28]764
[93]765 def _toggleDebugLog(self, menuItem):
766 """Toggle the debug log."""
767 if menuItem.get_active():
[107]768 label = gtk.Label(xstr("tab_debug_log"))
[93]769 label.set_use_underline(True)
[110]770 label.set_tooltip_text(xstr("tab_debug_log_tooltip"))
[93]771 self._debugLogPage = self._notebook.append_page(self._debugLogWidget, label)
772 self._notebook.set_current_page(self._debugLogPage)
773 else:
774 self._notebook.remove_page(self._debugLogPage)
775
776 def _buildLogWidget(self):
777 """Build the widget for the log."""
778 alignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
779
780 alignment.set_padding(padding_top = 8, padding_bottom = 8,
781 padding_left = 16, padding_right = 16)
[28]782
783 logScroller = gtk.ScrolledWindow()
[93]784 # FIXME: these should be constants in common
785 logScroller.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
786 else gtk.POLICY_AUTOMATIC,
787 gtk.PolicyType.AUTOMATIC if pygobject
788 else gtk.POLICY_AUTOMATIC)
789 logScroller.set_shadow_type(gtk.ShadowType.IN if pygobject
790 else gtk.SHADOW_IN)
791 logView = gtk.TextView()
792 logView.set_editable(False)
793 logScroller.add(logView)
[28]794
795 logBox = gtk.VBox()
796 logBox.pack_start(logScroller, True, True, 0)
797 logBox.set_size_request(-1, 200)
798
[93]799 alignment.add(logBox)
[28]800
[93]801 return (alignment, logView)
[28]802
[93]803 def _writeLog(self, msg, logView):
[28]804 """Write the given message to the log."""
[93]805 buffer = logView.get_buffer()
[28]806 buffer.insert(buffer.get_end_iter(), msg)
[93]807 logView.scroll_mark_onscreen(buffer.get_insert())
[28]808
[81]809 def _quit(self, what = None, force = False):
[38]810 """Quit from the application."""
[81]811 if force:
812 result=RESPONSETYPE_YES
813 else:
[105]814 dialog = gtk.MessageDialog(parent = self._mainWindow,
815 type = MESSAGETYPE_QUESTION,
[110]816 message_format = xstr("quit_question"))
817
[124]818 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
819 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
820
[105]821 dialog.set_title(WINDOW_TITLE_BASE)
[81]822 result = dialog.run()
823 dialog.hide()
[76]824
825 if result==RESPONSETYPE_YES:
826 self._statusIcon.destroy()
827 return gtk.main_quit()
[38]828
[46]829 def _notebookPageSwitch(self, notebook, page, page_num):
830 """Called when the current page of the notebook has changed."""
831 if page_num==0:
[48]832 gobject.idle_add(self._wizard.grabDefault)
[46]833 else:
834 self._mainWindow.set_default(None)
[123]835
836 def _editPreferences(self, menuItem):
837 """Callback for editing the preferences."""
838 self._preferences.run(self.config)
Note: See TracBrowser for help on using the repository browser.