source: src/mlx/gui/gui.py@ 261:aad834a04851

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

Added airport names

File size: 49.1 KB
Line 
1# The main file for the GUI
2# -*- coding: utf-8 -*-
3
4from statusicon import StatusIcon
5from statusbar import Statusbar
6from info import FlightInfo
7from update import Updater
8from mlx.gui.common import *
9from mlx.gui.flight import Wizard
10from mlx.gui.monitor import MonitorWindow
11from mlx.gui.weighthelp import WeightHelp
12from mlx.gui.gates import FleetGateStatus
13from mlx.gui.prefs import Preferences
14from mlx.gui.checklist import ChecklistEditor
15from mlx.gui.pirep import PIREPViewer
16
17import mlx.const as const
18import mlx.fs as fs
19import mlx.flight as flight
20import mlx.logger as logger
21import mlx.acft as acft
22import mlx.web as web
23import mlx.singleton as singleton
24import mlx.airports as airports
25from mlx.i18n import xstr, getLanguage
26from mlx.pirep import PIREP
27
28import time
29import threading
30import sys
31import datetime
32import webbrowser
33
34#------------------------------------------------------------------------------
35
36class GUI(fs.ConnectionListener):
37 """The main GUI class."""
38 _authors = [ ("Váradi", "István", "prog_test"),
39 ("Galyassy", "Tamás", "negotiation"),
40 ("Petrovszki", "Gábor", "test"),
41 ("Zsebényi-Loksa", "Gergely", "test"),
42 ("Kurják", "Ákos", "test"),
43 ("Nagy", "Dániel", "test"),
44 ("Radó", "Iván", "test") ]
45
46 def __init__(self, programDirectory, config):
47 """Construct the GUI."""
48 gobject.threads_init()
49
50 self._programDirectory = programDirectory
51 self.config = config
52 self._connecting = False
53 self._reconnecting = False
54 self._connected = False
55 self._logger = logger.Logger(self)
56 self._flight = None
57 self._simulator = None
58 self._monitoring = False
59
60 self._fleet = None
61
62 self._fleetCallback = None
63
64 self._updatePlaneCallback = None
65 self._updatePlaneTailNumber = None
66 self._updatePlaneStatus = None
67 self._updatePlaneGateNumber = None
68
69 self._stdioLock = threading.Lock()
70 self._stdioText = ""
71 self._stdioStartingLine = True
72
73 self._sendPIREPCallback = None
74
75 self.webHandler = web.Handler()
76 self.webHandler.start()
77
78 self.toRestart = False
79
80 def build(self, iconDirectory):
81 """Build the GUI."""
82
83 self._mainWindow = window = gtk.Window()
84 window.set_title(WINDOW_TITLE_BASE)
85 window.set_icon_from_file(os.path.join(iconDirectory, "logo.ico"))
86 window.set_resizable(False)
87 window.connect("delete-event", self.deleteMainWindow)
88 window.connect("window-state-event", self._handleMainWindowState)
89 accelGroup = gtk.AccelGroup()
90 window.add_accel_group(accelGroup)
91
92 mainVBox = gtk.VBox()
93 window.add(mainVBox)
94
95 self._preferences = Preferences(self)
96 self._checklistEditor = ChecklistEditor(self)
97
98 menuBar = self._buildMenuBar(accelGroup)
99 mainVBox.pack_start(menuBar, False, False, 0)
100
101 self._notebook = gtk.Notebook()
102 mainVBox.pack_start(self._notebook, True, True, 4)
103
104 self._wizard = Wizard(self)
105 label = gtk.Label(xstr("tab_flight"))
106 label.set_use_underline(True)
107 label.set_tooltip_text(xstr("tab_flight_tooltip"))
108 self._notebook.append_page(self._wizard, label)
109
110 self._flightInfo = FlightInfo(self)
111 label = gtk.Label(xstr("tab_flight_info"))
112 label.set_use_underline(True)
113 label.set_tooltip_text(xstr("tab_flight_info_tooltip"))
114 self._notebook.append_page(self._flightInfo, label)
115 self._flightInfo.disable()
116
117 self._weightHelp = WeightHelp(self)
118 label = gtk.Label(xstr("tab_weight_help"))
119 label.set_use_underline(True)
120 label.set_tooltip_text(xstr("tab_weight_help_tooltip"))
121 self._notebook.append_page(self._weightHelp, label)
122
123 (logWidget, self._logView) = self._buildLogWidget()
124 addFaultTag(self._logView.get_buffer())
125 label = gtk.Label(xstr("tab_log"))
126 label.set_use_underline(True)
127 label.set_tooltip_text(xstr("tab_log_tooltip"))
128 self._notebook.append_page(logWidget, label)
129
130 self._fleetGateStatus = FleetGateStatus(self)
131 label = gtk.Label(xstr("tab_gates"))
132 label.set_use_underline(True)
133 label.set_tooltip_text(xstr("tab_gates_tooltip"))
134 self._notebook.append_page(self._fleetGateStatus, label)
135
136 (self._debugLogWidget, self._debugLogView) = self._buildLogWidget()
137 self._debugLogWidget.show_all()
138
139 mainVBox.pack_start(gtk.HSeparator(), False, False, 0)
140
141 self._statusbar = Statusbar()
142 mainVBox.pack_start(self._statusbar, False, False, 0)
143
144 self._notebook.connect("switch-page", self._notebookPageSwitch)
145
146 self._monitorWindow = MonitorWindow(self, iconDirectory)
147 self._monitorWindow.add_accel_group(accelGroup)
148 self._monitorWindowX = None
149 self._monitorWindowY = None
150 self._selfToggling = False
151
152 self._pirepViewer = PIREPViewer(self)
153
154 window.show_all()
155 self._wizard.grabDefault()
156 self._weightHelp.reset()
157 self._weightHelp.disable()
158
159 self._statusIcon = StatusIcon(iconDirectory, self)
160
161 self._busyCursor = gdk.Cursor(gdk.CursorType.WATCH if pygobject
162 else gdk.WATCH)
163
164 self._loadPIREPDialog = None
165 self._lastLoadedPIREP = None
166
167 self._hotkeySetID = None
168 self._pilotHotkeyIndex = None
169 self._checklistHotkeyIndex = None
170
171 self._aboutDialog = None
172
173 @property
174 def mainWindow(self):
175 """Get the main window of the GUI."""
176 return self._mainWindow
177
178 @property
179 def logger(self):
180 """Get the logger used by us."""
181 return self._logger
182
183 @property
184 def simulator(self):
185 """Get the simulator used by us."""
186 return self._simulator
187
188 @property
189 def flight(self):
190 """Get the flight being performed."""
191 return self._flight
192
193 @property
194 def entranceExam(self):
195 """Get whether an entrance exam is about to be taken."""
196 return self._wizard.entranceExam
197
198 @property
199 def loggedIn(self):
200 """Indicate if the user has logged in properly."""
201 return self._wizard.loggedIn
202
203 @property
204 def loginResult(self):
205 """Get the result of the login."""
206 return self._wizard.loginResult
207
208 @property
209 def bookedFlight(self):
210 """Get the booked flight selected, if any."""
211 return self._wizard.bookedFlight
212
213 @property
214 def cargoWeight(self):
215 """Get the cargo weight."""
216 return self._wizard.cargoWeight
217
218 @property
219 def zfw(self):
220 """Get Zero-Fuel Weight calculated for the current flight."""
221 return self._wizard.zfw
222
223 @property
224 def filedCruiseAltitude(self):
225 """Get cruise altitude filed for the current flight."""
226 return self._wizard.filedCruiseAltitude
227
228 @property
229 def cruiseAltitude(self):
230 """Get cruise altitude set for the current flight."""
231 return self._wizard.cruiseAltitude
232
233 @property
234 def route(self):
235 """Get the flight route."""
236 return self._wizard.route
237
238 @property
239 def departureMETAR(self):
240 """Get the METAR of the deprature airport."""
241 return self._wizard.departureMETAR
242
243 @property
244 def arrivalMETAR(self):
245 """Get the METAR of the deprature airport."""
246 return self._wizard.arrivalMETAR
247
248 @property
249 def departureRunway(self):
250 """Get the name of the departure runway."""
251 return self._wizard.departureRunway
252
253 @property
254 def sid(self):
255 """Get the SID."""
256 return self._wizard.sid
257
258 @property
259 def v1(self):
260 """Get the V1 speed calculated for the flight."""
261 return self._wizard.v1
262
263 @property
264 def vr(self):
265 """Get the Vr speed calculated for the flight."""
266 return self._wizard.vr
267
268 @property
269 def v2(self):
270 """Get the V2 speed calculated for the flight."""
271 return self._wizard.v2
272
273 @property
274 def arrivalRunway(self):
275 """Get the arrival runway."""
276 return self._wizard.arrivalRunway
277
278 @property
279 def star(self):
280 """Get the STAR."""
281 return self._wizard.star
282
283 @property
284 def transition(self):
285 """Get the transition."""
286 return self._wizard.transition
287
288 @property
289 def approachType(self):
290 """Get the approach type."""
291 return self._wizard.approachType
292
293 @property
294 def vref(self):
295 """Get the Vref speed calculated for the flight."""
296 return self._wizard.vref
297
298 @property
299 def flightType(self):
300 """Get the flight type."""
301 return self._wizard.flightType
302
303 @property
304 def online(self):
305 """Get whether the flight was online or not."""
306 return self._wizard.online
307
308 @property
309 def comments(self):
310 """Get the comments."""
311 return self._flightInfo.comments
312
313 @property
314 def flightDefects(self):
315 """Get the flight defects."""
316 return self._flightInfo.flightDefects
317
318 @property
319 def delayCodes(self):
320 """Get the delay codes."""
321 return self._flightInfo.delayCodes
322
323 def run(self):
324 """Run the GUI."""
325 if self.config.autoUpdate:
326 self._updater = Updater(self,
327 self._programDirectory,
328 self.config.updateURL,
329 self._mainWindow)
330 self._updater.start()
331
332 singleton.raiseCallback = self.raiseCallback
333 gtk.main()
334 singleton.raiseCallback = None
335
336 self._disconnect()
337
338 def connected(self, fsType, descriptor):
339 """Called when we have connected to the simulator."""
340 self._connected = True
341 self._logger.untimedMessage("MLX %s connected to the simulator %s" % \
342 (const.VERSION, descriptor))
343 fs.sendMessage(const.MESSAGETYPE_INFORMATION,
344 "Welcome to MAVA Logger X " + const.VERSION)
345 gobject.idle_add(self._handleConnected, fsType, descriptor)
346
347 def _handleConnected(self, fsType, descriptor):
348 """Called when the connection to the simulator has succeeded."""
349 self._statusbar.updateConnection(self._connecting, self._connected)
350 self.endBusy()
351 if not self._reconnecting:
352 self._wizard.connected(fsType, descriptor)
353 self._reconnecting = False
354 self._listenHotkeys()
355
356 def connectionFailed(self):
357 """Called when the connection failed."""
358 self._logger.untimedMessage("Connection to the simulator failed")
359 gobject.idle_add(self._connectionFailed)
360
361 def _connectionFailed(self):
362 """Called when the connection failed."""
363 self.endBusy()
364 self._statusbar.updateConnection(self._connecting, self._connected)
365
366 dialog = gtk.MessageDialog(parent = self._mainWindow,
367 type = MESSAGETYPE_ERROR,
368 message_format = xstr("conn_failed"))
369
370 dialog.set_title(WINDOW_TITLE_BASE)
371 dialog.format_secondary_markup(xstr("conn_failed_sec"))
372
373 dialog.add_button(xstr("button_cancel"), 0)
374 dialog.add_button(xstr("button_tryagain"), 1)
375 dialog.set_default_response(1)
376
377 result = dialog.run()
378 dialog.hide()
379 if result == 1:
380 self.beginBusy(xstr("connect_busy"))
381 self._simulator.reconnect()
382 else:
383 self.reset()
384
385 def disconnected(self):
386 """Called when we have disconnected from the simulator."""
387 self._connected = False
388 self._logger.untimedMessage("Disconnected from the simulator")
389
390 gobject.idle_add(self._disconnected)
391
392 def _disconnected(self):
393 """Called when we have disconnected from the simulator unexpectedly."""
394 self._statusbar.updateConnection(self._connecting, self._connected)
395
396 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
397 message_format = xstr("conn_broken"),
398 parent = self._mainWindow)
399 dialog.set_title(WINDOW_TITLE_BASE)
400 dialog.format_secondary_markup(xstr("conn_broken_sec"))
401
402 dialog.add_button(xstr("button_cancel"), 0)
403 dialog.add_button(xstr("button_reconnect"), 1)
404 dialog.set_default_response(1)
405
406 result = dialog.run()
407 dialog.hide()
408 if result == 1:
409 self.beginBusy(xstr("connect_busy"))
410 self._reconnecting = True
411 self._simulator.reconnect()
412 else:
413 self.reset()
414
415 def enableFlightInfo(self):
416 """Enable the flight info tab."""
417 self._flightInfo.enable()
418
419 def cancelFlight(self):
420 """Cancel the current file, if the user confirms it."""
421 dialog = gtk.MessageDialog(parent = self._mainWindow,
422 type = MESSAGETYPE_QUESTION,
423 message_format = xstr("cancelFlight_question"))
424
425 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
426 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
427
428 dialog.set_title(WINDOW_TITLE_BASE)
429 result = dialog.run()
430 dialog.hide()
431
432 if result==RESPONSETYPE_YES:
433 self.reset()
434
435 def reset(self):
436 """Reset the GUI."""
437 self._disconnect()
438
439 self._flightInfo.reset()
440 self._flightInfo.disable()
441 self.resetFlightStatus()
442
443 self._weightHelp.reset()
444 self._weightHelp.disable()
445 self._notebook.set_current_page(0)
446
447 self._logView.get_buffer().set_text("")
448
449 if self.loggedIn:
450 self._wizard.reloadFlights(self._handleReloadResult)
451 else:
452 self._wizard.reset(None)
453
454 def _handleReloadResult(self, returned, result):
455 """Handle the result of the reloading of the flights."""
456 self._wizard.reset(result if returned and result.loggedIn else None)
457
458 def _disconnect(self, closingMessage = None, duration = 3):
459 """Disconnect from the simulator if connected."""
460 self.stopMonitoring()
461 self._clearHotkeys()
462
463 if self._connected:
464 if closingMessage is None:
465 self._flight.simulator.disconnect()
466 else:
467 fs.sendMessage(const.MESSAGETYPE_ENVIRONMENT,
468 closingMessage, duration,
469 disconnect = True)
470 self._connected = False
471
472 self._connecting = False
473 self._reconnecting = False
474 self._statusbar.updateConnection(False, False)
475 self._weightHelp.disable()
476
477 return True
478
479 def addFlightLogLine(self, timeStr, line, isFault = False):
480 """Write the given message line to the log."""
481 gobject.idle_add(self._writeLog,
482 formatFlightLogLine(timeStr, line),
483 self._logView, isFault)
484
485 def updateFlightLogLine(self, index, timeStr, line):
486 """Update the line with the given index."""
487 gobject.idle_add(self._updateFlightLogLine, index,
488 formatFlightLogLine(timeStr, line))
489
490 def _updateFlightLogLine(self, index, line):
491 """Replace the contents of the given line in the log."""
492 buffer = self._logView.get_buffer()
493 startIter = buffer.get_iter_at_line(index)
494 endIter = buffer.get_iter_at_line(index + 1)
495 buffer.delete(startIter, endIter)
496 buffer.insert(startIter, line)
497 self._logView.scroll_mark_onscreen(buffer.get_insert())
498
499 def check(self, flight, aircraft, logger, oldState, state):
500 """Update the data."""
501 gobject.idle_add(self._monitorWindow.setData, state)
502 gobject.idle_add(self._statusbar.updateTime, state.timestamp)
503
504 def resetFlightStatus(self):
505 """Reset the status of the flight."""
506 self._statusbar.resetFlightStatus()
507 self._statusbar.updateTime()
508 self._statusIcon.resetFlightStatus()
509
510 def setStage(self, stage):
511 """Set the stage of the flight."""
512 gobject.idle_add(self._setStage, stage)
513
514 def _setStage(self, stage):
515 """Set the stage of the flight."""
516 self._statusbar.setStage(stage)
517 self._statusIcon.setStage(stage)
518 self._wizard.setStage(stage)
519 if stage==const.STAGE_END:
520 welcomeMessage = \
521 airports.getWelcomeMessage(self.bookedFlight.arrivalICAO)
522 self._disconnect(closingMessage =
523 "Flight plan closed. " + welcomeMessage,
524 duration = 5)
525
526 def setRating(self, rating):
527 """Set the rating of the flight."""
528 gobject.idle_add(self._setRating, rating)
529
530 def _setRating(self, rating):
531 """Set the rating of the flight."""
532 self._statusbar.setRating(rating)
533 self._statusIcon.setRating(rating)
534
535 def setNoGo(self, reason):
536 """Set the rating of the flight to No-Go with the given reason."""
537 gobject.idle_add(self._setNoGo, reason)
538
539 def _setNoGo(self, reason):
540 """Set the rating of the flight."""
541 self._statusbar.setNoGo(reason)
542 self._statusIcon.setNoGo(reason)
543
544 def _handleMainWindowState(self, window, event):
545 """Hande a change in the state of the window"""
546 iconified = gdk.WindowState.ICONIFIED if pygobject \
547 else gdk.WINDOW_STATE_ICONIFIED
548
549 if (event.changed_mask&WINDOW_STATE_WITHDRAWN)!=0:
550 if (event.new_window_state&WINDOW_STATE_WITHDRAWN)!=0:
551 self._statusIcon.mainWindowHidden()
552 else:
553 self._statusIcon.mainWindowShown()
554
555 if self.config.hideMinimizedWindow and not pygobject and \
556 (event.changed_mask&WINDOW_STATE_ICONIFIED)!=0 and \
557 (event.new_window_state&WINDOW_STATE_ICONIFIED)!=0:
558 self.hideMainWindow(savePosition = False)
559 elif (event.changed_mask&WINDOW_STATE_ICONIFIED)!=0 and \
560 (event.new_window_state&WINDOW_STATE_ICONIFIED)==0:
561 self._mainWindow.present()
562
563 def raiseCallback(self):
564 """Callback for the singleton handling code."""
565 gobject.idle_add(self.raiseMainWindow)
566
567 def raiseMainWindow(self):
568 """Show the main window if invisible, and raise it."""
569 if not self._mainWindow.get_visible():
570 self.showMainWindow()
571 self._mainWindow.present()
572
573 def deleteMainWindow(self, window, event):
574 """Handle the delete event for the main window."""
575 if self.config.quitOnClose:
576 self._quit()
577 else:
578 self.hideMainWindow()
579 return True
580
581 def hideMainWindow(self, savePosition = True):
582 """Hide the main window and save its position."""
583 if savePosition:
584 (self._mainWindowX, self._mainWindowY) = \
585 self._mainWindow.get_window().get_root_origin()
586 else:
587 self._mainWindowX = self._mainWindowY = None
588 self._mainWindow.hide()
589 return True
590
591 def showMainWindow(self):
592 """Show the main window at its former position."""
593 if self._mainWindowX is not None and self._mainWindowY is not None:
594 self._mainWindow.move(self._mainWindowX, self._mainWindowY)
595
596 if pygobject:
597 self._mainWindow.show()
598 else:
599 self._mainWindow.present()
600 self._mainWindow.deiconify()
601
602 def toggleMainWindow(self):
603 """Toggle the main window."""
604 if self._mainWindow.get_visible():
605 self.hideMainWindow()
606 else:
607 self.showMainWindow()
608
609 def hideMonitorWindow(self, savePosition = True):
610 """Hide the monitor window."""
611 if savePosition:
612 (self._monitorWindowX, self._monitorWindowY) = \
613 self._monitorWindow.get_window().get_root_origin()
614 else:
615 self._monitorWindowX = self._monitorWindowY = None
616 self._monitorWindow.hide()
617 self._statusIcon.monitorWindowHidden()
618 if self._showMonitorMenuItem.get_active():
619 self._selfToggling = True
620 self._showMonitorMenuItem.set_active(False)
621 return True
622
623 def showMonitorWindow(self):
624 """Show the monitor window."""
625 if self._monitorWindowX is not None and self._monitorWindowY is not None:
626 self._monitorWindow.move(self._monitorWindowX, self._monitorWindowY)
627 self._monitorWindow.show_all()
628 self._statusIcon.monitorWindowShown()
629 if not self._showMonitorMenuItem.get_active():
630 self._selfToggling = True
631 self._showMonitorMenuItem.set_active(True)
632
633 def _toggleMonitorWindow(self, menuItem):
634 if self._selfToggling:
635 self._selfToggling = False
636 elif self._monitorWindow.get_visible():
637 self.hideMonitorWindow()
638 else:
639 self.showMonitorWindow()
640
641 def restart(self):
642 """Quit and restart the application."""
643 self.toRestart = True
644 self._quit(force = True)
645
646 def flushStdIO(self):
647 """Flush any text to the standard error that could not be logged."""
648 if self._stdioText:
649 sys.__stderr__.write(self._stdioText)
650
651 def writeStdIO(self, text):
652 """Write the given text into standard I/O log."""
653 with self._stdioLock:
654 self._stdioText += text
655
656 gobject.idle_add(self._writeStdIO)
657
658 def beginBusy(self, message):
659 """Begin a period of background processing."""
660 self._wizard.set_sensitive(False)
661 self._weightHelp.set_sensitive(False)
662 self._mainWindow.get_window().set_cursor(self._busyCursor)
663 self._statusbar.updateBusyState(message)
664
665 def endBusy(self):
666 """End a period of background processing."""
667 self._mainWindow.get_window().set_cursor(None)
668 self._weightHelp.set_sensitive(True)
669 self._wizard.set_sensitive(True)
670 self._statusbar.updateBusyState(None)
671
672 def initializeWeightHelp(self):
673 """Initialize the weight help tab."""
674 self._weightHelp.reset()
675 self._weightHelp.enable()
676
677 def getFleetAsync(self, callback = None, force = None):
678 """Get the fleet asynchronously."""
679 gobject.idle_add(self.getFleet, callback, force)
680
681 def getFleet(self, callback = None, force = False):
682 """Get the fleet.
683
684 If force is False, and we already have a fleet retrieved,
685 that one will be used."""
686 if self._fleet is None or force:
687 self._fleetCallback = callback
688 self.beginBusy(xstr("fleet_busy"))
689 self.webHandler.getFleet(self._fleetResultCallback)
690 else:
691 callback(self._fleet)
692
693 def _fleetResultCallback(self, returned, result):
694 """Called when the fleet has been queried."""
695 gobject.idle_add(self._handleFleetResult, returned, result)
696
697 def _handleFleetResult(self, returned, result):
698 """Handle the fleet result."""
699 self.endBusy()
700 if returned:
701 self._fleet = result.fleet
702 else:
703 self._fleet = None
704
705 dialog = gtk.MessageDialog(parent = self.mainWindow,
706 type = MESSAGETYPE_ERROR,
707 message_format = xstr("fleet_failed"))
708 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
709 dialog.set_title(WINDOW_TITLE_BASE)
710 dialog.run()
711 dialog.hide()
712
713 callback = self._fleetCallback
714 self._fleetCallback = None
715 if callback is not None:
716 callback(self._fleet)
717 self._fleetGateStatus.handleFleet(self._fleet)
718
719 def updatePlane(self, tailNumber, status,
720 gateNumber = None, callback = None):
721 """Update the status of the given plane."""
722 self.beginBusy(xstr("fleet_update_busy"))
723
724 self._updatePlaneCallback = callback
725
726 self._updatePlaneTailNumber = tailNumber
727 self._updatePlaneStatus = status
728 self._updatePlaneGateNumber = gateNumber
729
730 self.webHandler.updatePlane(self._updatePlaneResultCallback,
731 tailNumber, status, gateNumber)
732
733 def _updatePlaneResultCallback(self, returned, result):
734 """Called when the status of a plane has been updated."""
735 gobject.idle_add(self._handleUpdatePlaneResult, returned, result)
736
737 def _handleUpdatePlaneResult(self, returned, result):
738 """Handle the plane update result."""
739 self.endBusy()
740 if returned:
741 success = result.success
742 if success:
743 if self._fleet is not None:
744 self._fleet.updatePlane(self._updatePlaneTailNumber,
745 self._updatePlaneStatus,
746 self._updatePlaneGateNumber)
747 self._fleetGateStatus.handleFleet(self._fleet)
748 else:
749 dialog = gtk.MessageDialog(parent = self.mainWindow,
750 type = MESSAGETYPE_ERROR,
751 message_format = xstr("fleet_update_failed"))
752 dialog.add_button(xstr("button_ok"), RESPONSETYPE_ACCEPT)
753 dialog.set_title(WINDOW_TITLE_BASE)
754 dialog.run()
755 dialog.hide()
756
757 success = None
758
759 callback = self._updatePlaneCallback
760 self._updatePlaneCallback = None
761 if callback is not None:
762 callback(success)
763
764 def _writeStdIO(self):
765 """Perform the real writing."""
766 with self._stdioLock:
767 text = self._stdioText
768 self._stdioText = ""
769 if not text: return
770
771 lines = text.splitlines()
772 if text[-1]=="\n":
773 text = ""
774 else:
775 text = lines[-1]
776 lines = lines[:-1]
777
778 now = datetime.datetime.now()
779 timeStr = "%02d:%02d:%02d: " % (now.hour, now.minute, now.second)
780
781 for line in lines:
782 #print >> sys.__stdout__, line
783 if self._stdioStartingLine:
784 self._writeLog(timeStr, self._debugLogView)
785 self._writeLog(line + "\n", self._debugLogView)
786 self._stdioStartingLine = True
787
788 if text:
789 #print >> sys.__stdout__, text,
790 if self._stdioStartingLine:
791 self._writeLog(timeStr, self._debugLogView)
792 self._writeLog(text, self._debugLogView)
793 self._stdioStartingLine = False
794
795 def connectSimulator(self, aircraftType):
796 """Connect to the simulator for the first time."""
797 self._logger.reset()
798
799 self._flight = flight.Flight(self._logger, self)
800 self._flight.flareTimeFromFS = self.config.flareTimeFromFS
801 self._flight.aircraftType = aircraftType
802 self._flight.aircraft = acft.Aircraft.create(self._flight)
803 self._flight.aircraft._checkers.append(self)
804
805 if self._simulator is None:
806 self._simulator = fs.createSimulator(const.SIM_MSFS9, self)
807 fs.setupMessageSending(self.config, self._simulator)
808 self._setupTimeSync()
809
810 self._flight.simulator = self._simulator
811
812 self.beginBusy(xstr("connect_busy"))
813 self._statusbar.updateConnection(self._connecting, self._connected)
814
815 self._connecting = True
816 self._simulator.connect(self._flight.aircraft)
817
818 def startMonitoring(self):
819 """Start monitoring."""
820 if not self._monitoring:
821 self.simulator.startMonitoring()
822 self._monitoring = True
823
824 def stopMonitoring(self):
825 """Stop monitoring."""
826 if self._monitoring:
827 self.simulator.stopMonitoring()
828 self._monitoring = False
829
830 def _buildMenuBar(self, accelGroup):
831 """Build the main menu bar."""
832 menuBar = gtk.MenuBar()
833
834 fileMenuItem = gtk.MenuItem(xstr("menu_file"))
835 fileMenu = gtk.Menu()
836 fileMenuItem.set_submenu(fileMenu)
837 menuBar.append(fileMenuItem)
838
839 loadPIREPMenuItem = gtk.ImageMenuItem(gtk.STOCK_OPEN)
840 loadPIREPMenuItem.set_use_stock(True)
841 loadPIREPMenuItem.set_label(xstr("menu_file_loadPIREP"))
842 loadPIREPMenuItem.add_accelerator("activate", accelGroup,
843 ord(xstr("menu_file_loadPIREP_key")),
844 CONTROL_MASK, ACCEL_VISIBLE)
845 loadPIREPMenuItem.connect("activate", self._loadPIREP)
846 fileMenu.append(loadPIREPMenuItem)
847
848 fileMenu.append(gtk.SeparatorMenuItem())
849
850 quitMenuItem = gtk.ImageMenuItem(gtk.STOCK_QUIT)
851 quitMenuItem.set_use_stock(True)
852 quitMenuItem.set_label(xstr("menu_file_quit"))
853 quitMenuItem.add_accelerator("activate", accelGroup,
854 ord(xstr("menu_file_quit_key")),
855 CONTROL_MASK, ACCEL_VISIBLE)
856 quitMenuItem.connect("activate", self._quit)
857 fileMenu.append(quitMenuItem)
858
859 toolsMenuItem = gtk.MenuItem(xstr("menu_tools"))
860 toolsMenu = gtk.Menu()
861 toolsMenuItem.set_submenu(toolsMenu)
862 menuBar.append(toolsMenuItem)
863
864 checklistMenuItem = gtk.ImageMenuItem(gtk.STOCK_APPLY)
865 checklistMenuItem.set_use_stock(True)
866 checklistMenuItem.set_label(xstr("menu_tools_chklst"))
867 checklistMenuItem.add_accelerator("activate", accelGroup,
868 ord(xstr("menu_tools_chklst_key")),
869 CONTROL_MASK, ACCEL_VISIBLE)
870 checklistMenuItem.connect("activate", self._editChecklist)
871 toolsMenu.append(checklistMenuItem)
872
873 prefsMenuItem = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
874 prefsMenuItem.set_use_stock(True)
875 prefsMenuItem.set_label(xstr("menu_tools_prefs"))
876 prefsMenuItem.add_accelerator("activate", accelGroup,
877 ord(xstr("menu_tools_prefs_key")),
878 CONTROL_MASK, ACCEL_VISIBLE)
879 prefsMenuItem.connect("activate", self._editPreferences)
880 toolsMenu.append(prefsMenuItem)
881
882 viewMenuItem = gtk.MenuItem(xstr("menu_view"))
883 viewMenu = gtk.Menu()
884 viewMenuItem.set_submenu(viewMenu)
885 menuBar.append(viewMenuItem)
886
887 self._showMonitorMenuItem = gtk.CheckMenuItem()
888 self._showMonitorMenuItem.set_label(xstr("menu_view_monitor"))
889 self._showMonitorMenuItem.set_use_underline(True)
890 self._showMonitorMenuItem.set_active(False)
891 self._showMonitorMenuItem.add_accelerator("activate", accelGroup,
892 ord(xstr("menu_view_monitor_key")),
893 CONTROL_MASK, ACCEL_VISIBLE)
894 self._showMonitorMenuItem.connect("toggled", self._toggleMonitorWindow)
895 viewMenu.append(self._showMonitorMenuItem)
896
897 showDebugMenuItem = gtk.CheckMenuItem()
898 showDebugMenuItem.set_label(xstr("menu_view_debug"))
899 showDebugMenuItem.set_use_underline(True)
900 showDebugMenuItem.set_active(False)
901 showDebugMenuItem.add_accelerator("activate", accelGroup,
902 ord(xstr("menu_view_debug_key")),
903 CONTROL_MASK, ACCEL_VISIBLE)
904 showDebugMenuItem.connect("toggled", self._toggleDebugLog)
905 viewMenu.append(showDebugMenuItem)
906
907 helpMenuItem = gtk.MenuItem(xstr("menu_help"))
908 helpMenu = gtk.Menu()
909 helpMenuItem.set_submenu(helpMenu)
910 menuBar.append(helpMenuItem)
911
912 manualMenuItem = gtk.ImageMenuItem(gtk.STOCK_HELP)
913 manualMenuItem.set_use_stock(True)
914 manualMenuItem.set_label(xstr("menu_help_manual"))
915 manualMenuItem.add_accelerator("activate", accelGroup,
916 ord(xstr("menu_help_manual_key")),
917 CONTROL_MASK, ACCEL_VISIBLE)
918 manualMenuItem.connect("activate", self._showManual)
919 helpMenu.append(manualMenuItem)
920
921 helpMenu.append(gtk.SeparatorMenuItem())
922
923 aboutMenuItem = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
924 aboutMenuItem.set_use_stock(True)
925 aboutMenuItem.set_label(xstr("menu_help_about"))
926 aboutMenuItem.add_accelerator("activate", accelGroup,
927 ord(xstr("menu_help_about_key")),
928 CONTROL_MASK, ACCEL_VISIBLE)
929 aboutMenuItem.connect("activate", self._showAbout)
930 helpMenu.append(aboutMenuItem)
931
932 return menuBar
933
934 def _toggleDebugLog(self, menuItem):
935 """Toggle the debug log."""
936 if menuItem.get_active():
937 label = gtk.Label(xstr("tab_debug_log"))
938 label.set_use_underline(True)
939 label.set_tooltip_text(xstr("tab_debug_log_tooltip"))
940 self._debugLogPage = self._notebook.append_page(self._debugLogWidget, label)
941 self._notebook.set_current_page(self._debugLogPage)
942 else:
943 self._notebook.remove_page(self._debugLogPage)
944
945 def _buildLogWidget(self):
946 """Build the widget for the log."""
947 alignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
948
949 alignment.set_padding(padding_top = 8, padding_bottom = 8,
950 padding_left = 16, padding_right = 16)
951
952 logScroller = gtk.ScrolledWindow()
953 # FIXME: these should be constants in common
954 logScroller.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
955 else gtk.POLICY_AUTOMATIC,
956 gtk.PolicyType.AUTOMATIC if pygobject
957 else gtk.POLICY_AUTOMATIC)
958 logScroller.set_shadow_type(gtk.ShadowType.IN if pygobject
959 else gtk.SHADOW_IN)
960 logView = gtk.TextView()
961 logView.set_editable(False)
962 logView.set_cursor_visible(False)
963 logScroller.add(logView)
964
965 logBox = gtk.VBox()
966 logBox.pack_start(logScroller, True, True, 0)
967 logBox.set_size_request(-1, 200)
968
969 alignment.add(logBox)
970
971 return (alignment, logView)
972
973 def _writeLog(self, msg, logView, isFault = False):
974 """Write the given message to the log."""
975 buffer = logView.get_buffer()
976 appendTextBuffer(buffer, msg, isFault = isFault)
977 logView.scroll_mark_onscreen(buffer.get_insert())
978
979 def _quit(self, what = None, force = False):
980 """Quit from the application."""
981 if force:
982 result=RESPONSETYPE_YES
983 else:
984 dialog = gtk.MessageDialog(parent = self._mainWindow,
985 type = MESSAGETYPE_QUESTION,
986 message_format = xstr("quit_question"))
987
988 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
989 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
990
991 dialog.set_title(WINDOW_TITLE_BASE)
992 result = dialog.run()
993 dialog.hide()
994
995 if result==RESPONSETYPE_YES:
996 self._statusIcon.destroy()
997 return gtk.main_quit()
998
999 def _notebookPageSwitch(self, notebook, page, page_num):
1000 """Called when the current page of the notebook has changed."""
1001 if page_num==0:
1002 gobject.idle_add(self._wizard.grabDefault)
1003 else:
1004 self._mainWindow.set_default(None)
1005
1006 def _editChecklist(self, menuItem):
1007 """Callback for editing the checklists."""
1008 self._checklistEditor.run()
1009
1010 def _editPreferences(self, menuItem):
1011 """Callback for editing the preferences."""
1012 self._clearHotkeys()
1013 self._preferences.run(self.config)
1014 self._setupTimeSync()
1015 self._listenHotkeys()
1016
1017 def _setupTimeSync(self):
1018 """Enable or disable the simulator time synchronization based on the
1019 configuration."""
1020 simulator = self._simulator
1021 if simulator is not None:
1022 if self.config.syncFSTime:
1023 simulator.enableTimeSync()
1024 else:
1025 simulator.disableTimeSync()
1026
1027 def _loadPIREP(self, menuItem):
1028 """Load a PIREP for sending."""
1029 dialog = self._getLoadPirepDialog()
1030
1031 if self._lastLoadedPIREP:
1032 dialog.set_current_folder(os.path.dirname(self._lastLoadedPIREP))
1033 else:
1034 pirepDirectory = self.config.pirepDirectory
1035 if pirepDirectory is not None:
1036 dialog.set_current_folder(pirepDirectory)
1037
1038 result = dialog.run()
1039 dialog.hide()
1040
1041 if result==RESPONSETYPE_OK:
1042 self._lastLoadedPIREP = text2unicode(dialog.get_filename())
1043
1044 pirep = PIREP.load(self._lastLoadedPIREP)
1045 if pirep is None:
1046 dialog = gtk.MessageDialog(parent = self._mainWindow,
1047 type = MESSAGETYPE_ERROR,
1048 message_format = xstr("loadPIREP_failed"))
1049 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1050 dialog.set_title(WINDOW_TITLE_BASE)
1051 dialog.format_secondary_markup(xstr("loadPIREP_failed_sec"))
1052 dialog.run()
1053 dialog.hide()
1054 else:
1055 dialog = self._getSendLoadedDialog(pirep)
1056 dialog.show_all()
1057 while True:
1058 result = dialog.run()
1059
1060 if result==RESPONSETYPE_OK:
1061 self.sendPIREP(pirep)
1062 elif result==1:
1063 self._pirepViewer.setPIREP(pirep)
1064 self._pirepViewer.show_all()
1065 self._pirepViewer.run()
1066 self._pirepViewer.hide()
1067 else:
1068 break
1069
1070 dialog.hide()
1071
1072 def _getLoadPirepDialog(self):
1073 """Get the PIREP loading file chooser dialog.
1074
1075 If it is not created yet, it will be created."""
1076 if self._loadPIREPDialog is None:
1077 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
1078 xstr("loadPIREP_browser_title"),
1079 action = FILE_CHOOSER_ACTION_OPEN,
1080 buttons = (gtk.STOCK_CANCEL,
1081 RESPONSETYPE_CANCEL,
1082 gtk.STOCK_OK, RESPONSETYPE_OK),
1083 parent = self._mainWindow)
1084 dialog.set_modal(True)
1085
1086
1087 filter = gtk.FileFilter()
1088 filter.set_name(xstr("file_filter_pireps"))
1089 filter.add_pattern("*.pirep")
1090 dialog.add_filter(filter)
1091
1092 filter = gtk.FileFilter()
1093 filter.set_name(xstr("file_filter_all"))
1094 filter.add_pattern("*.*")
1095 dialog.add_filter(filter)
1096
1097 self._loadPIREPDialog = dialog
1098
1099 return self._loadPIREPDialog
1100
1101 def _getSendLoadedDialog(self, pirep):
1102 """Get a dialog displaying the main information of the flight from the
1103 PIREP and providing Cancel and Send buttons."""
1104 dialog = gtk.Dialog(title = WINDOW_TITLE_BASE + " - " +
1105 xstr("loadPIREP_send_title"),
1106 parent = self._mainWindow,
1107 flags = DIALOG_MODAL)
1108
1109 contentArea = dialog.get_content_area()
1110
1111 label = gtk.Label(xstr("loadPIREP_send_help"))
1112 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1113 xscale = 0.0, yscale = 0.0)
1114 alignment.set_padding(padding_top = 16, padding_bottom = 0,
1115 padding_left = 48, padding_right = 48)
1116 alignment.add(label)
1117 contentArea.pack_start(alignment, False, False, 8)
1118
1119 table = gtk.Table(5, 2)
1120 tableAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1121 xscale = 0.0, yscale = 0.0)
1122 tableAlignment.set_padding(padding_top = 0, padding_bottom = 32,
1123 padding_left = 48, padding_right = 48)
1124 table.set_row_spacings(4)
1125 table.set_col_spacings(16)
1126 tableAlignment.add(table)
1127 contentArea.pack_start(tableAlignment, True, True, 8)
1128
1129 bookedFlight = pirep.bookedFlight
1130
1131 label = gtk.Label("<b>" + xstr("loadPIREP_send_flightno") + "</b>")
1132 label.set_use_markup(True)
1133 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1134 xscale = 0.0, yscale = 0.0)
1135 labelAlignment.add(label)
1136 table.attach(labelAlignment, 0, 1, 0, 1)
1137
1138 label = gtk.Label(bookedFlight.callsign)
1139 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1140 xscale = 0.0, yscale = 0.0)
1141 labelAlignment.add(label)
1142 table.attach(labelAlignment, 1, 2, 0, 1)
1143
1144 label = gtk.Label("<b>" + xstr("loadPIREP_send_date") + "</b>")
1145 label.set_use_markup(True)
1146 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1147 xscale = 0.0, yscale = 0.0)
1148 labelAlignment.add(label)
1149 table.attach(labelAlignment, 0, 1, 1, 2)
1150
1151 label = gtk.Label(str(bookedFlight.departureTime.date()))
1152 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1153 xscale = 0.0, yscale = 0.0)
1154 labelAlignment.add(label)
1155 table.attach(labelAlignment, 1, 2, 1, 2)
1156
1157 label = gtk.Label("<b>" + xstr("loadPIREP_send_from") + "</b>")
1158 label.set_use_markup(True)
1159 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1160 xscale = 0.0, yscale = 0.0)
1161 labelAlignment.add(label)
1162 table.attach(labelAlignment, 0, 1, 2, 3)
1163
1164 label = gtk.Label(bookedFlight.departureICAO)
1165 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1166 xscale = 0.0, yscale = 0.0)
1167 labelAlignment.add(label)
1168 table.attach(labelAlignment, 1, 2, 2, 3)
1169
1170 label = gtk.Label("<b>" + xstr("loadPIREP_send_to") + "</b>")
1171 label.set_use_markup(True)
1172 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1173 xscale = 0.0, yscale = 0.0)
1174 labelAlignment.add(label)
1175 table.attach(labelAlignment, 0, 1, 3, 4)
1176
1177 label = gtk.Label(bookedFlight.arrivalICAO)
1178 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1179 xscale = 0.0, yscale = 0.0)
1180 labelAlignment.add(label)
1181 table.attach(labelAlignment, 1, 2, 3, 4)
1182
1183 label = gtk.Label("<b>" + xstr("loadPIREP_send_rating") + "</b>")
1184 label.set_use_markup(True)
1185 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1186 xscale = 0.0, yscale = 0.0)
1187 labelAlignment.add(label)
1188 table.attach(labelAlignment, 0, 1, 4, 5)
1189
1190 rating = pirep.rating
1191 label = gtk.Label()
1192 if rating<0:
1193 label.set_markup('<b><span foreground="red">NO GO</span></b>')
1194 else:
1195 label.set_text("%.1f %%" % (rating,))
1196
1197 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1198 xscale = 0.0, yscale = 0.0)
1199 labelAlignment.add(label)
1200 table.attach(labelAlignment, 1, 2, 4, 5)
1201
1202 dialog.add_button(xstr("button_cancel"), RESPONSETYPE_REJECT)
1203 dialog.add_button(xstr("viewPIREP"), 1)
1204 dialog.add_button(xstr("sendPIREP"), RESPONSETYPE_OK)
1205
1206 return dialog
1207
1208 def sendPIREP(self, pirep, callback = None):
1209 """Send the given PIREP."""
1210 self.beginBusy(xstr("sendPIREP_busy"))
1211 self._sendPIREPCallback = callback
1212 self.webHandler.sendPIREP(self._pirepSentCallback, pirep)
1213
1214 def _pirepSentCallback(self, returned, result):
1215 """Callback for the PIREP sending result."""
1216 gobject.idle_add(self._handlePIREPSent, returned, result)
1217
1218 def _handlePIREPSent(self, returned, result):
1219 """Callback for the PIREP sending result."""
1220 self.endBusy()
1221 secondaryMarkup = None
1222 type = MESSAGETYPE_ERROR
1223 if returned:
1224 if result.success:
1225 type = MESSAGETYPE_INFO
1226 messageFormat = xstr("sendPIREP_success")
1227 secondaryMarkup = xstr("sendPIREP_success_sec")
1228 elif result.alreadyFlown:
1229 messageFormat = xstr("sendPIREP_already")
1230 secondaryMarkup = xstr("sendPIREP_already_sec")
1231 elif result.notAvailable:
1232 messageFormat = xstr("sendPIREP_notavail")
1233 else:
1234 messageFormat = xstr("sendPIREP_unknown")
1235 secondaryMarkup = xstr("sendPIREP_unknown_sec")
1236 else:
1237 print "PIREP sending failed", result
1238 messageFormat = xstr("sendPIREP_failed")
1239 secondaryMarkup = xstr("sendPIREP_failed_sec")
1240
1241 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
1242 type = type, message_format = messageFormat)
1243 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1244 dialog.set_title(WINDOW_TITLE_BASE)
1245 if secondaryMarkup is not None:
1246 dialog.format_secondary_markup(secondaryMarkup)
1247
1248 dialog.run()
1249 dialog.hide()
1250
1251 callback = self._sendPIREPCallback
1252 self._sendPIREPCallback = None
1253 if callback is not None:
1254 callback(returned, result)
1255
1256 def _listenHotkeys(self):
1257 """Setup the hotkeys based on the configuration."""
1258 if self._hotkeySetID is None and self._simulator is not None:
1259 self._pilotHotkeyIndex = None
1260 self._checklistHotkeyIndex = None
1261
1262 hotkeys = []
1263
1264 config = self.config
1265 if config.enableSounds and config.pilotControlsSounds:
1266 self._pilotHotkeyIndex = len(hotkeys)
1267 hotkeys.append(config.pilotHotkey)
1268
1269 if config.enableChecklists:
1270 self._checklistHotkeyIndex = len(hotkeys)
1271 hotkeys.append(config.checklistHotkey)
1272
1273 if hotkeys:
1274 self._hotkeySetID = \
1275 self._simulator.listenHotkeys(hotkeys, self._handleHotkeys)
1276
1277 def _clearHotkeys(self):
1278 """Clear the hotkeys."""
1279 if self._hotkeySetID is not None:
1280 self._hotkeySetID=None
1281 self._simulator.clearHotkeys()
1282
1283 def _handleHotkeys(self, id, hotkeys):
1284 """Handle the hotkeys."""
1285 if id==self._hotkeySetID:
1286 for index in hotkeys:
1287 if index==self._pilotHotkeyIndex:
1288 print "gui.GUI._handleHotkeys: pilot hotkey pressed"
1289 self._flight.pilotHotkeyPressed()
1290 elif index==self._checklistHotkeyIndex:
1291 print "gui.GUI._handleHotkeys: checklist hotkey pressed"
1292 self._flight.checklistHotkeyPressed()
1293 else:
1294 print "gui.GUI._handleHotkeys: unhandled hotkey index:", index
1295
1296 def _showManual(self, menuitem):
1297 """Show the user's manual."""
1298 webbrowser.open(url ="file://" +
1299 os.path.join(self._programDirectory, "doc", "manual",
1300 getLanguage(), "index.html"),
1301 new = 1)
1302
1303 def _showAbout(self, menuitem):
1304 """Show the about dialog."""
1305 dialog = self._getAboutDialog()
1306 dialog.show_all()
1307 dialog.run()
1308 dialog.hide()
1309
1310 def _getAboutDialog(self):
1311 """Get the about dialog.
1312
1313 If it does not exist yet, it will be created."""
1314 if self._aboutDialog is None:
1315 self._aboutDialog = dialog = gtk.AboutDialog()
1316 dialog.set_transient_for(self._mainWindow)
1317 dialog.set_modal(True)
1318
1319 logoPath = os.path.join(self._programDirectory, "logo.png")
1320 logo = pixbuf_new_from_file(logoPath)
1321 dialog.set_logo(logo)
1322
1323 dialog.set_program_name(PROGRAM_NAME)
1324 dialog.set_version(const.VERSION)
1325 dialog.set_copyright("(c) 2012 by István Váradi")
1326 dialog.set_website("http://mlx.varadiistvan.hu")
1327 dialog.set_website_label(xstr("about_website"))
1328
1329 isHungarian = getLanguage()=="hu"
1330 authors = []
1331 for (familyName, firstName, role) in GUI._authors:
1332 authors.append("%s %s (%s)" % \
1333 (familyName if isHungarian else firstName,
1334 firstName if isHungarian else familyName,
1335 xstr("about_role_" + role)))
1336 dialog.set_authors(authors)
1337
1338 dialog.set_license(xstr("about_license"))
1339
1340 if not pygobject:
1341 gtk.about_dialog_set_url_hook(self._showAboutURL, None)
1342
1343 return self._aboutDialog
1344
1345 def _showAboutURL(self, dialog, link, user_data):
1346 """Show the about URL."""
1347 webbrowser.open(url = link, new = 1)
Note: See TracBrowser for help on using the repository browser.