source: src/mlx/gui/gui.py@ 1015:a6e03fae41e0

python3
Last change on this file since 1015:a6e03fae41e0 was 1010:e2b76a41d9ca, checked in by István Váradi <ivaradi@…>, 5 years ago

Updated copyright end years

File size: 71.7 KB
Line 
1# -*- coding: utf-8 -*-
2
3from .statusicon import StatusIcon
4from .statusbar import Statusbar
5from .info import FlightInfo
6from .update import Updater
7from mlx.gui.common import *
8from mlx.gui.flight import Wizard
9from mlx.gui.monitor import MonitorWindow
10from mlx.gui.weighthelp import WeightHelp
11from mlx.gui.gates import FleetGateStatus
12from mlx.gui.prefs import Preferences
13from mlx.gui.checklist import ChecklistEditor
14from mlx.gui.callouts import ApproachCalloutsEditor
15from mlx.gui.flightlist import AcceptedFlightsWindow
16from mlx.gui.pirep import PIREPViewer, PIREPEditor
17from mlx.gui.bugreport import BugReportDialog
18from mlx.gui.acars import ACARS
19from mlx.gui.timetable import TimetableWindow
20from . import cef
21
22import mlx.const as const
23import mlx.fs as fs
24import mlx.flight as flight
25import mlx.logger as logger
26import mlx.acft as acft
27import mlx.web as web
28import mlx.singleton as singleton
29import mlx.airports as airports
30from mlx.i18n import xstr, getLanguage
31from mlx.pirep import PIREP
32
33import time
34import threading
35import sys
36import datetime
37import webbrowser
38
39#------------------------------------------------------------------------------
40
41## @package mlx.gui.gui
42#
43# The main GUI class.
44#
45# The \ref GUI class is the main class of the GUI. It is a connection listener,
46# and aggregates all the windows, the menu, etc. It maintains the connection to
47# the simulator as well as the flight object.
48
49#------------------------------------------------------------------------------
50
51class GUI(fs.ConnectionListener):
52 """The main GUI class."""
53 _authors = [ ("Váradi", "István", "prog_test"),
54 ("Galyassy", "Tamás", "negotiation"),
55 ("Kurják", "Ákos", "test"),
56 ("Nagy", "Dániel", "test"),
57 ("Radó", "Iván", "test"),
58 ("Petrovszki", "Gábor", "test"),
59 ("Serfőző", "Tamás", "test"),
60 ("Szebenyi", "Bálint", "test"),
61 ("Zsebényi-Loksa", "Gergely", "test") ]
62
63 def __init__(self, programDirectory, config):
64 """Construct the GUI."""
65 GObject.threads_init()
66
67 self._programDirectory = programDirectory
68 self.config = config
69 self._connecting = False
70 self._reconnecting = False
71 self._connected = False
72 self._logger = logger.Logger(self)
73 self._flight = None
74 self._simulator = None
75 self._fsType = None
76 self._monitoring = False
77
78 self._fleet = None
79
80 self._fleetCallback = None
81
82 self._updatePlaneCallback = None
83 self._updatePlaneTailNumber = None
84 self._updatePlaneStatus = None
85 self._updatePlaneGateNumber = None
86
87 self._stdioLock = threading.Lock()
88 self._stdioText = ""
89 self._stdioStartingLine = True
90
91 self._sendPIREPCallback = None
92 self._sendBugReportCallback = None
93
94 self._credentialsCondition = threading.Condition()
95 self._credentialsAvailable = False
96 self._credentialsUserName = None
97 self._credentialsPassword = None
98
99 self._bookFlightsUserCallback = None
100 self._bookFlightsBusyCallback = None
101
102 self.webHandler = web.Handler(config, self._getCredentialsCallback)
103 self.webHandler.start()
104
105 self.toRestart = False
106
107 def build(self, iconDirectory):
108 """Build the GUI."""
109
110 self._mainWindow = window = Gtk.Window()
111 if os.name!="nt":
112 window.set_visual(window.get_screen().lookup_visual(0x21))
113 window.set_title(WINDOW_TITLE_BASE)
114 window.set_icon_from_file(os.path.join(iconDirectory, "logo.ico"))
115 window.set_resizable(False)
116 window.connect("delete-event", self.deleteMainWindow)
117 window.connect("window-state-event", self._handleMainWindowState)
118 if os.name=="nt":
119 window.connect("leave-notify-event", self._handleLeaveNotify)
120 accelGroup = Gtk.AccelGroup()
121 window.add_accel_group(accelGroup)
122 window.realize()
123
124 mainVBox = Gtk.VBox()
125 window.add(mainVBox)
126
127 self._preferences = Preferences(self)
128 self._timetableWindow = TimetableWindow(self)
129 self._timetableWindow.connect("delete-event", self._hideTimetableWindow)
130 self._flightsWindow = AcceptedFlightsWindow(self)
131 self._flightsWindow.connect("delete-event", self._hideFlightsWindow)
132 self._checklistEditor = ChecklistEditor(self)
133 self._approachCalloutsEditor = ApproachCalloutsEditor(self)
134 self._bugReportDialog = BugReportDialog(self)
135
136 menuBar = self._buildMenuBar(accelGroup)
137 mainVBox.pack_start(menuBar, False, False, 0)
138
139 self._notebook = Gtk.Notebook()
140 mainVBox.pack_start(self._notebook, True, True, 4)
141
142 self._wizard = Wizard(self)
143 label = Gtk.Label(xstr("tab_flight"))
144 label.set_use_underline(True)
145 label.set_tooltip_text(xstr("tab_flight_tooltip"))
146 self._notebook.append_page(self._wizard, label)
147
148 self._flightInfo = FlightInfo(self)
149 label = Gtk.Label(xstr("tab_flight_info"))
150 label.set_use_underline(True)
151 label.set_tooltip_text(xstr("tab_flight_info_tooltip"))
152 self._notebook.append_page(self._flightInfo, label)
153 self._flightInfo.disable()
154
155 self._weightHelp = WeightHelp(self)
156 label = Gtk.Label(xstr("tab_weight_help"))
157 label.set_use_underline(True)
158 label.set_tooltip_text(xstr("tab_weight_help_tooltip"))
159 self._notebook.append_page(self._weightHelp, label)
160
161 (logWidget, self._logView) = self._buildLogWidget()
162 addFaultTag(self._logView.get_buffer())
163 label = Gtk.Label(xstr("tab_log"))
164 label.set_use_underline(True)
165 label.set_tooltip_text(xstr("tab_log_tooltip"))
166 self._notebook.append_page(logWidget, label)
167
168 self._fleetGateStatus = FleetGateStatus(self)
169 label = Gtk.Label(xstr("tab_gates"))
170 label.set_use_underline(True)
171 label.set_tooltip_text(xstr("tab_gates_tooltip"))
172 self._notebook.append_page(self._fleetGateStatus, label)
173
174 self._acars = ACARS(self)
175 label = Gtk.Label("ACARS")
176 label.set_use_underline(True)
177 self._notebook.append_page(self._acars, label)
178
179 (self._debugLogWidget, self._debugLogView) = self._buildLogWidget()
180 self._debugLogWidget.show_all()
181
182 mainVBox.pack_start(Gtk.HSeparator(), False, False, 0)
183
184 self._statusbar = Statusbar(iconDirectory)
185 mainVBox.pack_start(self._statusbar, False, False, 0)
186
187 self._notebook.connect("switch-page", self._notebookPageSwitch)
188
189 self._monitorWindow = MonitorWindow(self, iconDirectory)
190 self._monitorWindow.add_accel_group(accelGroup)
191 self._monitorWindowX = None
192 self._monitorWindowY = None
193 self._selfToggling = False
194
195 self._pirepViewer = PIREPViewer(self)
196 self._messagedPIREPViewer = PIREPViewer(self, showMessages = True)
197
198 self._pirepEditor = PIREPEditor(self)
199
200 window.show_all()
201
202 self._wizard.grabDefault()
203 self._weightHelp.reset()
204 self._weightHelp.disable()
205
206 self._statusIcon = StatusIcon(iconDirectory, self)
207
208 self._busyCursor = Gdk.Cursor(Gdk.CursorType.WATCH)
209
210 self._loadPIREPDialog = None
211 self._lastLoadedPIREP = None
212
213 self._hotkeySetID = None
214 self._pilotHotkeyIndex = None
215 self._checklistHotkeyIndex = None
216
217 self._aboutDialog = None
218
219 @property
220 def mainWindow(self):
221 """Get the main window of the GUI."""
222 return self._mainWindow
223
224 @property
225 def logger(self):
226 """Get the logger used by us."""
227 return self._logger
228
229 @property
230 def simulator(self):
231 """Get the simulator used by us."""
232 return self._simulator
233
234 @property
235 def flight(self):
236 """Get the flight being performed."""
237 return self._flight
238
239 @property
240 def fsType(self):
241 """Get the flight simulator type."""
242 return self._fsType
243
244 @property
245 def entranceExam(self):
246 """Get whether an entrance exam is about to be taken."""
247 return self._wizard.entranceExam
248
249 @property
250 def loggedIn(self):
251 """Indicate if the user has logged in properly."""
252 return self._wizard.loggedIn
253
254 @property
255 def loginResult(self):
256 """Get the result of the login."""
257 return self._wizard.loginResult
258
259 @property
260 def bookedFlight(self):
261 """Get the booked flight selected, if any."""
262 return self._wizard.bookedFlight
263
264 @property
265 def numCrew(self):
266 """Get the number of crew members."""
267 return self._wizard.numCrew
268
269 @property
270 def numPassengers(self):
271 """Get the number of passengers."""
272 return self._wizard.numPassengers
273
274 @property
275 def bagWeight(self):
276 """Get the bag weight."""
277 return self._wizard.bagWeight
278
279 @property
280 def cargoWeight(self):
281 """Get the cargo weight."""
282 return self._wizard.cargoWeight
283
284 @property
285 def mailWeight(self):
286 """Get the mail weight."""
287 return self._wizard.mailWeight
288
289 @property
290 def zfw(self):
291 """Get Zero-Fuel Weight calculated for the current flight."""
292 return self._wizard.zfw
293
294 @property
295 def filedCruiseAltitude(self):
296 """Get cruise altitude filed for the current flight."""
297 return self._wizard.filedCruiseAltitude
298
299 @property
300 def cruiseAltitude(self):
301 """Get cruise altitude set for the current flight."""
302 return self._wizard.cruiseAltitude
303
304 @property
305 def loggableCruiseAltitude(self):
306 """Get the cruise altitude that can be logged."""
307 return self._wizard.loggableCruiseAltitude
308
309 @property
310 def route(self):
311 """Get the flight route."""
312 return self._wizard.route
313
314 @property
315 def departureMETAR(self):
316 """Get the METAR of the deprature airport."""
317 return self._wizard.departureMETAR
318
319 @property
320 def arrivalMETAR(self):
321 """Get the METAR of the deprature airport."""
322 return self._wizard.arrivalMETAR
323
324 @property
325 def departureRunway(self):
326 """Get the name of the departure runway."""
327 return self._wizard.departureRunway
328
329 @property
330 def sid(self):
331 """Get the SID."""
332 return self._wizard.sid
333
334 @property
335 def v1(self):
336 """Get the V1 speed calculated for the flight."""
337 return self._wizard.v1
338
339 @property
340 def vr(self):
341 """Get the Vr speed calculated for the flight."""
342 return self._wizard.vr
343
344 @property
345 def v2(self):
346 """Get the V2 speed calculated for the flight."""
347 return self._wizard.v2
348
349 @property
350 def derate(self):
351 """Get the derate value calculated for the flight."""
352 return self._wizard.derate
353
354 @property
355 def takeoffAntiIceOn(self):
356 """Get whether the anti-ice system was on during take-off."""
357 return self._wizard.takeoffAntiIceOn
358
359 @takeoffAntiIceOn.setter
360 def takeoffAntiIceOn(self, value):
361 """Set the anti-ice on indicator."""
362 GObject.idle_add(self._setTakeoffAntiIceOn, value)
363
364 @property
365 def rtoIndicated(self):
366 """Get whether the pilot has indicated than an RTO has occured."""
367 return self._wizard.rtoIndicated
368
369 @property
370 def arrivalRunway(self):
371 """Get the arrival runway."""
372 return self._wizard.arrivalRunway
373
374 @property
375 def star(self):
376 """Get the STAR."""
377 return self._wizard.star
378
379 @property
380 def transition(self):
381 """Get the transition."""
382 return self._wizard.transition
383
384 @property
385 def approachType(self):
386 """Get the approach type."""
387 return self._wizard.approachType
388
389 @property
390 def vref(self):
391 """Get the Vref speed calculated for the flight."""
392 return self._wizard.vref
393
394 @property
395 def landingAntiIceOn(self):
396 """Get whether the anti-ice system was on during landing."""
397 return self._wizard.landingAntiIceOn
398
399 @landingAntiIceOn.setter
400 def landingAntiIceOn(self, value):
401 """Set the anti-ice on indicator."""
402 GObject.idle_add(self._setLandingAntiIceOn, value)
403
404 @property
405 def flightType(self):
406 """Get the flight type."""
407 return self._wizard.flightType
408
409 @property
410 def online(self):
411 """Get whether the flight was online or not."""
412 return self._wizard.online
413
414 @property
415 def comments(self):
416 """Get the comments."""
417 return self._flightInfo.comments
418
419 @property
420 def hasComments(self):
421 """Indicate whether there is a comment."""
422 return self._flightInfo.hasComments
423
424 @property
425 def flightDefects(self):
426 """Get the flight defects."""
427 return self._flightInfo.faultsAndExplanations
428
429 @property
430 def delayCodes(self):
431 """Get the delay codes."""
432 return self._flightInfo.delayCodes
433
434 @property
435 def hasDelayCode(self):
436 """Determine if there is at least one delay code selected."""
437 return self._flightInfo.hasDelayCode
438
439 @property
440 def faultsFullyExplained(self):
441 """Determine if all the faults have been fully explained by the
442 user."""
443 return self._flightInfo.faultsFullyExplained
444
445 @property
446 def backgroundColour(self):
447 """Get the background colour of the main window."""
448 return self._mainWindow.get_style_context().\
449 get_background_color(Gtk.StateFlags.NORMAL)
450
451 def run(self):
452 """Run the GUI."""
453 if self.config.autoUpdate:
454 self._updater = Updater(self,
455 self._programDirectory,
456 self.config.updateURL,
457 self._mainWindow)
458 self._updater.start()
459 else:
460 self.updateDone()
461
462 singleton.raiseCallback = self.raiseCallback
463 Gtk.main()
464 singleton.raiseCallback = None
465
466 cef.finalize()
467
468 self._disconnect()
469
470 def updateDone(self):
471 """Called when the update is done (and there is no need to restart)."""
472 GObject.idle_add(self._updateDone)
473
474 def connected(self, fsType, descriptor):
475 """Called when we have connected to the simulator."""
476 self._connected = True
477 self._logger.untimedMessage("MLX %s connected to the simulator %s" % \
478 (const.VERSION, descriptor))
479 fs.sendMessage(const.MESSAGETYPE_INFORMATION,
480 "Welcome to MAVA Logger X " + const.VERSION)
481 GObject.idle_add(self._handleConnected, fsType, descriptor)
482
483 def _handleConnected(self, fsType, descriptor):
484 """Called when the connection to the simulator has succeeded."""
485 self._statusbar.updateConnection(self._connecting, self._connected)
486 self.endBusy()
487 if not self._reconnecting:
488 self._wizard.connected(fsType, descriptor)
489 self._reconnecting = False
490 self._fsType = fsType
491 self._listenHotkeys()
492
493 def connectionFailed(self):
494 """Called when the connection failed."""
495 self._logger.untimedMessage("Connection to the simulator failed")
496 GObject.idle_add(self._connectionFailed)
497
498 def _connectionFailed(self):
499 """Called when the connection failed."""
500 self.endBusy()
501 self._statusbar.updateConnection(self._connecting, self._connected)
502
503 dialog = Gtk.MessageDialog(parent = self._mainWindow,
504 type = Gtk.MessageType.ERROR,
505 message_format = xstr("conn_failed"))
506
507 dialog.set_title(WINDOW_TITLE_BASE)
508 dialog.format_secondary_markup(xstr("conn_failed_sec"))
509
510 dialog.add_button(xstr("button_cancel"), 0)
511 dialog.add_button(xstr("button_tryagain"), 1)
512 dialog.set_default_response(1)
513
514 result = dialog.run()
515 dialog.hide()
516 if result == 1:
517 self.beginBusy(xstr("connect_busy"))
518 self._simulator.reconnect()
519 else:
520 self.reset()
521
522 def disconnected(self):
523 """Called when we have disconnected from the simulator."""
524 self._connected = False
525 self._logger.untimedMessage("Disconnected from the simulator")
526 if self._flight is not None:
527 self._flight.disconnected()
528
529 GObject.idle_add(self._disconnected)
530
531 def _disconnected(self):
532 """Called when we have disconnected from the simulator unexpectedly."""
533 self._statusbar.updateConnection(self._connecting, self._connected)
534
535 dialog = Gtk.MessageDialog(type = Gtk.MessageType.ERROR,
536 message_format = xstr("conn_broken"),
537 parent = self._mainWindow)
538 dialog.set_title(WINDOW_TITLE_BASE)
539 dialog.format_secondary_markup(xstr("conn_broken_sec"))
540
541 dialog.add_button(xstr("button_cancel"), 0)
542 dialog.add_button(xstr("button_reconnect"), 1)
543 dialog.set_default_response(1)
544
545 result = dialog.run()
546 dialog.hide()
547 if result == 1:
548 self.beginBusy(xstr("connect_busy"))
549 self._reconnecting = True
550 self._simulator.reconnect()
551 else:
552 self.reset()
553
554 def enableFlightInfo(self, aircraftType):
555 """Enable the flight info tab."""
556 self._flightInfo.enable(aircraftType)
557
558 def bookFlights(self, callback, flightIDs, date, tailNumber,
559 busyCallback = None):
560 """Initiate the booking of flights with the given timetable IDs and
561 other data"""
562 self._bookFlightsUserCallback = callback
563 self._bookFlightsBusyCallback = busyCallback
564
565 self.beginBusy(xstr("bookflights_busy"))
566 if busyCallback is not None:
567 busyCallback(True)
568
569 self.webHandler.bookFlights(self._bookFlightsCallback,
570 flightIDs, date, tailNumber)
571
572 def _bookFlightsCallback(self, returned, result):
573 """Called when the booking of flights has finished."""
574 GObject.idle_add(self._handleBookFlightsResult, returned, result)
575
576 def _handleBookFlightsResult(self, returned, result):
577 """Called when the booking of flights is done.
578
579 If it was successful, the booked flights are added to the list of the
580 flight selector."""
581 if self._bookFlightsBusyCallback is not None:
582 self._bookFlightsBusyCallback(False)
583 self.endBusy()
584
585 if returned:
586 for bookedFlight in result.bookedFlights:
587 self._wizard.addFlight(bookedFlight)
588
589 self._bookFlightsUserCallback(returned, result)
590
591 def cancelFlight(self):
592 """Cancel the current file, if the user confirms it."""
593 dialog = Gtk.MessageDialog(parent = self._mainWindow,
594 type = Gtk.MessageType.QUESTION,
595 message_format = xstr("cancelFlight_question"))
596
597 dialog.add_button(xstr("button_no"), Gtk.ResponseType.NO)
598 dialog.add_button(xstr("button_yes"), Gtk.ResponseType.YES)
599
600 dialog.set_title(WINDOW_TITLE_BASE)
601 result = dialog.run()
602 dialog.hide()
603
604 if result==Gtk.ResponseType.YES:
605 self.reset()
606
607 def reset(self):
608 """Reset the GUI."""
609 self._disconnect()
610
611 self._simulator = None
612
613 self._flightInfo.reset()
614 self._flightInfo.disable()
615 self.resetFlightStatus()
616
617 self._weightHelp.reset()
618 self._weightHelp.disable()
619 self._notebook.set_current_page(0)
620
621 self._logView.get_buffer().set_text("")
622
623 if self.loggedIn:
624 self._wizard.cancelFlight(self._handleReloadResult)
625 else:
626 self._wizard.reset(None)
627
628 def _handleReloadResult(self, returned, result):
629 """Handle the result of the reloading of the flights."""
630 self._wizard.reset(result if returned and result.loggedIn else None)
631
632 def _disconnect(self, closingMessage = None, duration = 3):
633 """Disconnect from the simulator if connected."""
634 self.stopMonitoring()
635 self._clearHotkeys()
636
637 if self._connected:
638 if closingMessage is None:
639 self._flight.simulator.disconnect()
640 else:
641 fs.sendMessage(const.MESSAGETYPE_ENVIRONMENT,
642 closingMessage, duration,
643 disconnect = True)
644 self._connected = False
645
646 self._connecting = False
647 self._reconnecting = False
648 self._statusbar.updateConnection(False, False)
649 self._weightHelp.disable()
650
651 return True
652
653 def insertFlightLogLine(self, index, timestampString, text, isFault):
654 """Insert the flight log line with the given data."""
655 GObject.idle_add(self._insertFlightLogLine, index,
656 formatFlightLogLine(timestampString, text),
657 isFault)
658
659 def _insertFlightLogLine(self, index, line, isFault):
660 """Perform the real insertion.
661
662 To be called from the event loop."""
663 buffer = self._logView.get_buffer()
664 lineIter = buffer.get_iter_at_line(index)
665 insertTextBuffer(buffer, lineIter, line, isFault = isFault)
666 self._logView.scroll_mark_onscreen(buffer.get_insert())
667
668 def removeFlightLogLine(self, index):
669 """Remove the flight log line with the given index."""
670 GObject.idle_add(self._removeFlightLogLine, index)
671
672 def addFault(self, id, timestampString, text):
673 """Add a fault to the list of faults."""
674 faultText = formatFlightLogLine(timestampString, text).strip()
675 GObject.idle_add(self._flightInfo.addFault, id, faultText)
676
677 def updateFault(self, id, timestampString, text):
678 """Update a fault in the list of faults."""
679 faultText = formatFlightLogLine(timestampString, text).strip()
680 GObject.idle_add(self._flightInfo.updateFault, id, faultText)
681
682 def clearFault(self, id):
683 """Clear a fault in the list of faults."""
684 GObject.idle_add(self._flightInfo.clearFault, id)
685
686 def _removeFlightLogLine(self, index):
687 """Perform the real removal."""
688 buffer = self._logView.get_buffer()
689 startIter = buffer.get_iter_at_line(index)
690 endIter = buffer.get_iter_at_line(index+1)
691 buffer.delete(startIter, endIter)
692 self._logView.scroll_mark_onscreen(buffer.get_insert())
693
694 def check(self, flight, aircraft, logger, oldState, state):
695 """Update the data."""
696 GObject.idle_add(self._monitorWindow.setData, state)
697 GObject.idle_add(self._statusbar.updateTime, state.timestamp)
698
699 def resetFlightStatus(self):
700 """Reset the status of the flight."""
701 self._statusbar.resetFlightStatus()
702 self._statusbar.updateTime()
703 self._statusIcon.resetFlightStatus()
704
705 def setStage(self, stage):
706 """Set the stage of the flight."""
707 GObject.idle_add(self._setStage, stage)
708
709 def _setStage(self, stage):
710 """Set the stage of the flight."""
711 self._statusbar.setStage(stage)
712 self._statusIcon.setStage(stage)
713 self._wizard.setStage(stage)
714 if stage==const.STAGE_END:
715 welcomeMessage = \
716 airports.getWelcomeMessage(self.bookedFlight.arrivalICAO)
717 self._disconnect(closingMessage =
718 "Flight plan closed. " + welcomeMessage,
719 duration = 5)
720
721 def setRating(self, rating):
722 """Set the rating of the flight."""
723 GObject.idle_add(self._setRating, rating)
724
725 def _setRating(self, rating):
726 """Set the rating of the flight."""
727 self._statusbar.setRating(rating)
728 self._statusIcon.setRating(rating)
729
730 def setNoGo(self, reason):
731 """Set the rating of the flight to No-Go with the given reason."""
732 GObject.idle_add(self._setNoGo, reason)
733
734 def _setNoGo(self, reason):
735 """Set the rating of the flight."""
736 self._statusbar.setNoGo(reason)
737 self._statusIcon.setNoGo(reason)
738
739 def _handleMainWindowState(self, window, event):
740 """Hande a change in the state of the window"""
741 iconified = Gdk.WindowState.ICONIFIED
742
743 if (event.changed_mask&Gdk.WindowState.WITHDRAWN)!=0:
744 if (event.new_window_state&Gdk.WindowState.WITHDRAWN)!=0:
745 self._statusIcon.mainWindowHidden()
746 else:
747 self._statusIcon.mainWindowShown()
748
749 if (event.changed_mask&Gdk.WindowState.ICONIFIED)!=0 and \
750 (event.new_window_state&Gdk.WindowState.ICONIFIED)==0:
751 self._mainWindow.present()
752
753 def _handleLeaveNotify(self, widget, event):
754 """Handle the leave-notify event.
755
756 Here we reset the focus to the main window as CEF might have acquired
757 it earlier."""
758 self._mainWindow.get_window().focus(0)
759
760 def raiseCallback(self):
761 """Callback for the singleton handling code."""
762 GObject.idle_add(self.raiseMainWindow)
763
764 def raiseMainWindow(self):
765 """Show the main window if invisible, and raise it."""
766 if not self._mainWindow.get_visible():
767 self.showMainWindow()
768 self._mainWindow.present()
769
770 def deleteMainWindow(self, window, event):
771 """Handle the delete event for the main window."""
772 if self.config.quitOnClose:
773 self._quit()
774 else:
775 self.hideMainWindow()
776 return True
777
778 def hideMainWindow(self, savePosition = True):
779 """Hide the main window and save its position."""
780 if savePosition:
781 (self._mainWindowX, self._mainWindowY) = \
782 self._mainWindow.get_window().get_root_origin()
783 else:
784 self._mainWindowX = self._mainWindowY = None
785 self._mainWindow.hide()
786 return True
787
788 def showMainWindow(self):
789 """Show the main window at its former position."""
790 if self._mainWindowX is not None and self._mainWindowY is not None:
791 self._mainWindow.move(self._mainWindowX, self._mainWindowY)
792
793 self._mainWindow.show()
794 self._mainWindow.deiconify()
795
796 def toggleMainWindow(self):
797 """Toggle the main window."""
798 if self._mainWindow.get_visible():
799 self.hideMainWindow()
800 else:
801 self.showMainWindow()
802
803 def hideMonitorWindow(self, savePosition = True):
804 """Hide the monitor window."""
805 if savePosition:
806 (self._monitorWindowX, self._monitorWindowY) = \
807 self._monitorWindow.get_window().get_root_origin()
808 else:
809 self._monitorWindowX = self._monitorWindowY = None
810 self._monitorWindow.hide()
811 self._statusIcon.monitorWindowHidden()
812 if self._showMonitorMenuItem.get_active():
813 self._selfToggling = True
814 self._showMonitorMenuItem.set_active(False)
815 return True
816
817 def showMonitorWindow(self):
818 """Show the monitor window."""
819 if self._monitorWindowX is not None and self._monitorWindowY is not None:
820 self._monitorWindow.move(self._monitorWindowX, self._monitorWindowY)
821 self._monitorWindow.show_all()
822 self._statusIcon.monitorWindowShown()
823 if not self._showMonitorMenuItem.get_active():
824 self._selfToggling = True
825 self._showMonitorMenuItem.set_active(True)
826
827 def _toggleMonitorWindow(self, menuItem):
828 if self._selfToggling:
829 self._selfToggling = False
830 elif self._monitorWindow.get_visible():
831 self.hideMonitorWindow()
832 else:
833 self.showMonitorWindow()
834
835 def restart(self):
836 """Quit and restart the application."""
837 self.toRestart = True
838 self._quit(force = True)
839
840 def flushStdIO(self):
841 """Flush any text to the standard error that could not be logged."""
842 if self._stdioText:
843 sys.__stderr__.write(self._stdioText)
844
845 def writeStdIO(self, text):
846 """Write the given text into standard I/O log."""
847 with self._stdioLock:
848 self._stdioText += text
849
850 GObject.idle_add(self._writeStdIO)
851
852 def beginBusy(self, message):
853 """Begin a period of background processing."""
854 self._wizard.set_sensitive(False)
855 self._weightHelp.set_sensitive(False)
856 self._mainWindow.get_window().set_cursor(self._busyCursor)
857 self._statusbar.updateBusyState(message)
858
859 def updateBusyState(self, message):
860 """Update the busy state."""
861 self._statusbar.updateBusyState(message)
862
863 def endBusy(self):
864 """End a period of background processing."""
865 self._mainWindow.get_window().set_cursor(None)
866 self._weightHelp.set_sensitive(True)
867 self._wizard.set_sensitive(True)
868 self._statusbar.updateBusyState(None)
869
870 def initializeWeightHelp(self):
871 """Initialize the weight help tab."""
872 self._weightHelp.reset()
873 self._weightHelp.enable()
874
875 def getFleetAsync(self, callback = None, force = None):
876 """Get the fleet asynchronously."""
877 GObject.idle_add(self.getFleet, callback, force)
878
879 def getFleet(self, callback = None, force = False, busyCallback = None):
880 """Get the fleet.
881
882 If force is False, and we already have a fleet retrieved,
883 that one will be used."""
884 if self._fleet is None or force:
885 self._fleetCallback = callback
886 self._fleetBusyCallback = busyCallback
887 if busyCallback is not None:
888 busyCallback(True)
889 self.beginBusy(xstr("fleet_busy"))
890 self.webHandler.getFleet(self._fleetResultCallback)
891 else:
892 callback(self._fleet)
893
894 def commentsChanged(self):
895 """Indicate that the comments have changed."""
896 self._wizard.commentsChanged()
897
898 def delayCodesChanged(self):
899 """Called when the delay codes have changed."""
900 self._wizard.delayCodesChanged()
901
902 def faultExplanationsChanged(self):
903 """Called when the status of the explanations of the faults have
904 changed."""
905 self._wizard.faultExplanationsChanged()
906
907 def updateRTO(self, inLoop = False):
908 """Indicate that the RTO state should be updated."""
909 if inLoop:
910 self._wizard.updateRTO()
911 else:
912 GObject.idle_add(self.updateRTO, True)
913
914 def rtoToggled(self, indicated):
915 """Called when the user has toggled the RTO checkbox."""
916 self._flight.rtoToggled(indicated)
917
918 def _fleetResultCallback(self, returned, result):
919 """Called when the fleet has been queried."""
920 GObject.idle_add(self._handleFleetResult, returned, result)
921
922 def _handleFleetResult(self, returned, result):
923 """Handle the fleet result."""
924 self.endBusy()
925 if self._fleetBusyCallback is not None:
926 self._fleetBusyCallback(False)
927 if returned:
928 self._fleet = result.fleet
929 else:
930 self._fleet = None
931
932 dialog = Gtk.MessageDialog(parent = self.mainWindow,
933 type = Gtk.MessageType.ERROR,
934 message_format = xstr("fleet_failed"))
935 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
936 dialog.set_title(WINDOW_TITLE_BASE)
937 dialog.run()
938 dialog.hide()
939
940 callback = self._fleetCallback
941 self._fleetCallback = None
942 self._fleetBusyCallback = None
943 if callback is not None:
944 callback(self._fleet)
945 self._fleetGateStatus.handleFleet(self._fleet)
946
947 def updatePlane(self, tailNumber, status,
948 gateNumber = None, callback = None):
949 """Update the status of the given plane."""
950 self.beginBusy(xstr("fleet_update_busy"))
951
952 self._updatePlaneCallback = callback
953
954 self._updatePlaneTailNumber = tailNumber
955 self._updatePlaneStatus = status
956 self._updatePlaneGateNumber = gateNumber
957
958 self.webHandler.updatePlane(self._updatePlaneResultCallback,
959 tailNumber, status, gateNumber)
960
961 def _updatePlaneResultCallback(self, returned, result):
962 """Called when the status of a plane has been updated."""
963 GObject.idle_add(self._handleUpdatePlaneResult, returned, result)
964
965 def _handleUpdatePlaneResult(self, returned, result):
966 """Handle the plane update result."""
967 self.endBusy()
968 if returned:
969 success = result.success
970 if success:
971 if self._fleet is not None:
972 self._fleet.updatePlane(self._updatePlaneTailNumber,
973 self._updatePlaneStatus,
974 self._updatePlaneGateNumber)
975 self._fleetGateStatus.handleFleet(self._fleet)
976 else:
977 dialog = Gtk.MessageDialog(parent = self.mainWindow,
978 type = Gtk.MessageType.ERROR,
979 message_format = xstr("fleet_update_failed"))
980 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.ACCEPT)
981 dialog.set_title(WINDOW_TITLE_BASE)
982 dialog.run()
983 dialog.hide()
984
985 success = None
986
987 callback = self._updatePlaneCallback
988 self._updatePlaneCallback = None
989 if callback is not None:
990 callback(success)
991
992 def _writeStdIO(self):
993 """Perform the real writing."""
994 with self._stdioLock:
995 text = self._stdioText
996 self._stdioText = ""
997 if not text: return
998
999 lines = text.splitlines()
1000 if text[-1]=="\n":
1001 text = ""
1002 else:
1003 text = lines[-1]
1004 lines = lines[:-1]
1005
1006 now = datetime.datetime.now()
1007 timeStr = "%02d:%02d:%02d: " % (now.hour, now.minute, now.second)
1008
1009 for line in lines:
1010 #print >> sys.__stdout__, line
1011 if self._stdioStartingLine:
1012 self._writeLog(timeStr, self._debugLogView)
1013 self._writeLog(line + "\n", self._debugLogView)
1014 self._stdioStartingLine = True
1015
1016 if text:
1017 #print >> sys.__stdout__, text,
1018 if self._stdioStartingLine:
1019 self._writeLog(timeStr, self._debugLogView)
1020 self._writeLog(text, self._debugLogView)
1021 self._stdioStartingLine = False
1022
1023 def connectSimulator(self, bookedFlight, simulatorType):
1024 """Connect to the simulator for the first time."""
1025 self._logger.reset()
1026
1027 self._flight = flight.Flight(self._logger, self)
1028 self._flight.flareTimeFromFS = self.config.flareTimeFromFS
1029 self._flight.aircraftType = bookedFlight.aircraftType
1030 self._flight.aircraft = acft.Aircraft.create(self._flight, bookedFlight)
1031 self._flight.aircraft._checkers.append(self)
1032
1033 if self._simulator is None:
1034 self._simulator = fs.createSimulator(simulatorType, self)
1035 fs.setupMessageSending(self.config, self._simulator)
1036 self._setupTimeSync()
1037
1038 self._flight.simulator = self._simulator
1039
1040 self.beginBusy(xstr("connect_busy"))
1041 self._statusbar.updateConnection(self._connecting, self._connected)
1042
1043 self._connecting = True
1044 self._simulator.connect(self._flight.aircraft)
1045
1046 def startMonitoring(self):
1047 """Start monitoring."""
1048 if not self._monitoring:
1049 self.simulator.startMonitoring()
1050 self._monitoring = True
1051
1052 def stopMonitoring(self):
1053 """Stop monitoring."""
1054 if self._monitoring:
1055 self.simulator.stopMonitoring()
1056 self._monitoring = False
1057
1058 def cruiseLevelChanged(self):
1059 """Called when the cruise level is changed in the flight wizard."""
1060 if self._flight is not None:
1061 return self._flight.cruiseLevelChanged()
1062 else:
1063 return False
1064
1065 def _buildMenuBar(self, accelGroup):
1066 """Build the main menu bar."""
1067 menuBar = Gtk.MenuBar()
1068
1069 fileMenuItem = Gtk.MenuItem(xstr("menu_file"))
1070 fileMenu = Gtk.Menu()
1071 fileMenuItem.set_submenu(fileMenu)
1072 menuBar.append(fileMenuItem)
1073
1074 loadPIREPMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_OPEN)
1075 loadPIREPMenuItem.set_use_stock(True)
1076 loadPIREPMenuItem.set_label(xstr("menu_file_loadPIREP"))
1077 loadPIREPMenuItem.add_accelerator("activate", accelGroup,
1078 ord(xstr("menu_file_loadPIREP_key")),
1079 Gdk.ModifierType.CONTROL_MASK,
1080 Gtk.AccelFlags.VISIBLE)
1081 loadPIREPMenuItem.connect("activate", self._loadPIREP)
1082 fileMenu.append(loadPIREPMenuItem)
1083
1084 fileMenu.append(Gtk.SeparatorMenuItem())
1085
1086 quitMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_QUIT)
1087 quitMenuItem.set_use_stock(True)
1088 quitMenuItem.set_label(xstr("menu_file_quit"))
1089 quitMenuItem.add_accelerator("activate", accelGroup,
1090 ord(xstr("menu_file_quit_key")),
1091 Gdk.ModifierType.CONTROL_MASK,
1092 Gtk.AccelFlags.VISIBLE)
1093 quitMenuItem.connect("activate", self._quit)
1094 fileMenu.append(quitMenuItem)
1095
1096 toolsMenuItem = Gtk.MenuItem(xstr("menu_tools"))
1097 toolsMenu = Gtk.Menu()
1098 toolsMenuItem.set_submenu(toolsMenu)
1099 menuBar.append(toolsMenuItem)
1100
1101 self._timetableMenuItem = timetableMenuItem = \
1102 Gtk.ImageMenuItem(Gtk.STOCK_INDENT)
1103 timetableMenuItem.set_use_stock(True)
1104 timetableMenuItem.set_label(xstr("menu_tools_timetable"))
1105 timetableMenuItem.add_accelerator("activate", accelGroup,
1106 ord(xstr("menu_tools_timetable_key")),
1107 Gdk.ModifierType.CONTROL_MASK,
1108 Gtk.AccelFlags.VISIBLE)
1109 timetableMenuItem.connect("activate", self.showTimetable)
1110 self._timetableMenuItem.set_sensitive(False)
1111 toolsMenu.append(timetableMenuItem)
1112
1113 self._flightsMenuItem = flightsMenuItem = \
1114 Gtk.ImageMenuItem(Gtk.STOCK_SPELL_CHECK)
1115 flightsMenuItem.set_use_stock(True)
1116 flightsMenuItem.set_label(xstr("menu_tools_flights"))
1117 flightsMenuItem.add_accelerator("activate", accelGroup,
1118 ord(xstr("menu_tools_flights_key")),
1119 Gdk.ModifierType.CONTROL_MASK,
1120 Gtk.AccelFlags.VISIBLE)
1121 flightsMenuItem.connect("activate", self.showFlights)
1122 self._flightsMenuItem.set_sensitive(False)
1123 toolsMenu.append(flightsMenuItem)
1124
1125 checklistMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_APPLY)
1126 checklistMenuItem.set_use_stock(True)
1127 checklistMenuItem.set_label(xstr("menu_tools_chklst"))
1128 checklistMenuItem.add_accelerator("activate", accelGroup,
1129 ord(xstr("menu_tools_chklst_key")),
1130 Gdk.ModifierType.CONTROL_MASK,
1131 Gtk.AccelFlags.VISIBLE)
1132 checklistMenuItem.connect("activate", self._editChecklist)
1133 toolsMenu.append(checklistMenuItem)
1134
1135 approachCalloutsMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_EDIT)
1136 approachCalloutsMenuItem.set_use_stock(True)
1137 approachCalloutsMenuItem.set_label(xstr("menu_tools_callouts"))
1138 approachCalloutsMenuItem.add_accelerator("activate", accelGroup,
1139 ord(xstr("menu_tools_callouts_key")),
1140 Gdk.ModifierType.CONTROL_MASK,
1141 Gtk.AccelFlags.VISIBLE)
1142 approachCalloutsMenuItem.connect("activate", self._editApproachCallouts)
1143 toolsMenu.append(approachCalloutsMenuItem)
1144
1145 prefsMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_PREFERENCES)
1146 prefsMenuItem.set_use_stock(True)
1147 prefsMenuItem.set_label(xstr("menu_tools_prefs"))
1148 prefsMenuItem.add_accelerator("activate", accelGroup,
1149 ord(xstr("menu_tools_prefs_key")),
1150 Gdk.ModifierType.CONTROL_MASK,
1151 Gtk.AccelFlags.VISIBLE)
1152 prefsMenuItem.connect("activate", self._editPreferences)
1153 toolsMenu.append(prefsMenuItem)
1154
1155 toolsMenu.append(Gtk.SeparatorMenuItem())
1156
1157 bugReportMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_PASTE)
1158 bugReportMenuItem.set_use_stock(True)
1159 bugReportMenuItem.set_label(xstr("menu_tools_bugreport"))
1160 bugReportMenuItem.add_accelerator("activate", accelGroup,
1161 ord(xstr("menu_tools_bugreport_key")),
1162 Gdk.ModifierType.CONTROL_MASK,
1163 Gtk.AccelFlags.VISIBLE)
1164 bugReportMenuItem.connect("activate", self._reportBug)
1165 toolsMenu.append(bugReportMenuItem)
1166
1167 viewMenuItem = Gtk.MenuItem(xstr("menu_view"))
1168 viewMenu = Gtk.Menu()
1169 viewMenuItem.set_submenu(viewMenu)
1170 menuBar.append(viewMenuItem)
1171
1172 self._showMonitorMenuItem = Gtk.CheckMenuItem()
1173 self._showMonitorMenuItem.set_label(xstr("menu_view_monitor"))
1174 self._showMonitorMenuItem.set_use_underline(True)
1175 self._showMonitorMenuItem.set_active(False)
1176 self._showMonitorMenuItem.add_accelerator("activate", accelGroup,
1177 ord(xstr("menu_view_monitor_key")),
1178 Gdk.ModifierType.CONTROL_MASK,
1179 Gtk.AccelFlags.VISIBLE)
1180 self._showMonitorMenuItem.connect("toggled", self._toggleMonitorWindow)
1181 viewMenu.append(self._showMonitorMenuItem)
1182
1183 showDebugMenuItem = Gtk.CheckMenuItem()
1184 showDebugMenuItem.set_label(xstr("menu_view_debug"))
1185 showDebugMenuItem.set_use_underline(True)
1186 showDebugMenuItem.set_active(False)
1187 showDebugMenuItem.add_accelerator("activate", accelGroup,
1188 ord(xstr("menu_view_debug_key")),
1189 Gdk.ModifierType.CONTROL_MASK,
1190 Gtk.AccelFlags.VISIBLE)
1191 showDebugMenuItem.connect("toggled", self._toggleDebugLog)
1192 viewMenu.append(showDebugMenuItem)
1193
1194 helpMenuItem = Gtk.MenuItem(xstr("menu_help"))
1195 helpMenu = Gtk.Menu()
1196 helpMenuItem.set_submenu(helpMenu)
1197 menuBar.append(helpMenuItem)
1198
1199 manualMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_HELP)
1200 manualMenuItem.set_use_stock(True)
1201 manualMenuItem.set_label(xstr("menu_help_manual"))
1202 manualMenuItem.add_accelerator("activate", accelGroup,
1203 ord(xstr("menu_help_manual_key")),
1204 Gdk.ModifierType.CONTROL_MASK,
1205 Gtk.AccelFlags.VISIBLE)
1206 manualMenuItem.connect("activate", self._showManual)
1207 helpMenu.append(manualMenuItem)
1208
1209 helpMenu.append(Gtk.SeparatorMenuItem())
1210
1211 aboutMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_ABOUT)
1212 aboutMenuItem.set_use_stock(True)
1213 aboutMenuItem.set_label(xstr("menu_help_about"))
1214 aboutMenuItem.add_accelerator("activate", accelGroup,
1215 ord(xstr("menu_help_about_key")),
1216 Gdk.ModifierType.CONTROL_MASK,
1217 Gtk.AccelFlags.VISIBLE)
1218 aboutMenuItem.connect("activate", self._showAbout)
1219 helpMenu.append(aboutMenuItem)
1220
1221 return menuBar
1222
1223 def _toggleDebugLog(self, menuItem):
1224 """Toggle the debug log."""
1225 if menuItem.get_active():
1226 label = Gtk.Label(xstr("tab_debug_log"))
1227 label.set_use_underline(True)
1228 label.set_tooltip_text(xstr("tab_debug_log_tooltip"))
1229 self._debugLogPage = self._notebook.append_page(self._debugLogWidget, label)
1230 self._notebook.set_current_page(self._debugLogPage)
1231 else:
1232 self._notebook.remove_page(self._debugLogPage)
1233
1234 def _buildLogWidget(self):
1235 """Build the widget for the log."""
1236 alignment = Gtk.Alignment(xscale = 1.0, yscale = 1.0)
1237
1238 alignment.set_padding(padding_top = 8, padding_bottom = 8,
1239 padding_left = 16, padding_right = 16)
1240
1241 logScroller = Gtk.ScrolledWindow()
1242 # FIXME: these should be constants in common
1243 logScroller.set_policy(Gtk.PolicyType.AUTOMATIC,
1244 Gtk.PolicyType.AUTOMATIC)
1245 logScroller.set_shadow_type(Gtk.ShadowType.IN)
1246 logView = Gtk.TextView()
1247 logView.set_editable(False)
1248 logView.set_cursor_visible(False)
1249 logScroller.add(logView)
1250
1251 logBox = Gtk.VBox()
1252 logBox.pack_start(logScroller, True, True, 0)
1253 logBox.set_size_request(-1, 200)
1254
1255 alignment.add(logBox)
1256
1257 return (alignment, logView)
1258
1259 def _writeLog(self, msg, logView, isFault = False):
1260 """Write the given message to the log."""
1261 buffer = logView.get_buffer()
1262 appendTextBuffer(buffer, msg, isFault = isFault)
1263 logView.scroll_mark_onscreen(buffer.get_insert())
1264
1265 def _quit(self, what = None, force = False):
1266 """Quit from the application."""
1267 if force:
1268 result=Gtk.ResponseType.YES
1269 else:
1270 dialog = Gtk.MessageDialog(parent = self._mainWindow,
1271 type = Gtk.MessageType.QUESTION,
1272 message_format = xstr("quit_question"))
1273
1274 dialog.add_button(xstr("button_no"), Gtk.ResponseType.NO)
1275 dialog.add_button(xstr("button_yes"), Gtk.ResponseType.YES)
1276
1277 dialog.set_title(WINDOW_TITLE_BASE)
1278 result = dialog.run()
1279 dialog.hide()
1280
1281 if result==Gtk.ResponseType.YES:
1282 self._statusIcon.destroy()
1283 return Gtk.main_quit()
1284
1285 def _notebookPageSwitch(self, notebook, page, page_num):
1286 """Called when the current page of the notebook has changed."""
1287 if page_num==0:
1288 GObject.idle_add(self._wizard.grabDefault)
1289 else:
1290 self._mainWindow.set_default(None)
1291
1292 def loginSuccessful(self):
1293 """Called when the login is successful."""
1294 self._flightsMenuItem.set_sensitive(True)
1295 self._timetableMenuItem.set_sensitive(True)
1296
1297 def isWizardActive(self):
1298 """Determine if the flight wizard is active."""
1299 return self._notebook.get_current_page()==0
1300
1301 def showTimetable(self, menuItem = None):
1302 """Callback for showing the timetable."""
1303 if self._timetableWindow.hasFlightPairs:
1304 self._timetableWindow.show_all()
1305 else:
1306 date = datetime.date.today()
1307 self._timetableWindow.setTypes(self.loginResult.types)
1308 self._timetableWindow.setDate(date)
1309 self.updateTimeTable(date)
1310 self.beginBusy(xstr("timetable_query_busy"))
1311
1312 def updateTimeTable(self, date):
1313 """Update the time table for the given date."""
1314 self.beginBusy(xstr("timetable_query_busy"))
1315 self._timetableWindow.set_sensitive(False)
1316 window = self._timetableWindow.get_window()
1317 if window is not None:
1318 window.set_cursor(self._busyCursor)
1319 self.webHandler.getTimetable(self._timetableCallback, date,
1320 self.loginResult.types)
1321
1322 def _timetableCallback(self, returned, result):
1323 """Called when the timetable has been received."""
1324 GObject.idle_add(self._handleTimetable, returned, result)
1325
1326 def _handleTimetable(self, returned, result):
1327 """Handle the result of the query for the timetable."""
1328 self.endBusy()
1329 window = self._timetableWindow.get_window()
1330 if window is not None:
1331 window.set_cursor(None)
1332 self._timetableWindow.set_sensitive(True)
1333 if returned:
1334 self._timetableWindow.setFlightPairs(result.flightPairs)
1335 self._timetableWindow.show_all()
1336 else:
1337 dialog = Gtk.MessageDialog(parent = self.mainWindow,
1338 type = Gtk.MessageType.ERROR,
1339 message_format = xstr("timetable_failed"))
1340 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1341 dialog.set_title(WINDOW_TITLE_BASE)
1342 dialog.run()
1343 dialog.hide()
1344 self._timetableWindow.clear()
1345
1346 def showFlights(self, menuItem):
1347 """Callback for showing the flight list."""
1348 if self._flightsWindow.hasFlights:
1349 self._flightsWindow.show_all()
1350 else:
1351 self.beginBusy(xstr("acceptedflt_query_busy"))
1352 self.webHandler.getAcceptedFlights(self._acceptedFlightsCallback)
1353
1354 def _acceptedFlightsCallback(self, returned, result):
1355 """Called when the accepted flights have been received."""
1356 GObject.idle_add(self._handleAcceptedFlights, returned, result)
1357
1358 def _handleAcceptedFlights(self, returned, result):
1359 """Handle the result of the query for accepted flights."""
1360 self.endBusy()
1361 if returned:
1362 self._flightsWindow.clear()
1363 for flight in result.flights:
1364 self._flightsWindow.addFlight(flight)
1365 self._flightsWindow.show_all()
1366 else:
1367 dialog = Gtk.MessageDialog(parent = self.mainWindow,
1368 type = Gtk.MessageType.ERROR,
1369 message_format = xstr("acceptedflt_failed"))
1370 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1371 dialog.set_title(WINDOW_TITLE_BASE)
1372 dialog.run()
1373 dialog.hide()
1374
1375 def _hideTimetableWindow(self, window, event):
1376 """Hide the window of the timetable."""
1377 self._timetableWindow.hide()
1378 return True
1379
1380 def _hideFlightsWindow(self, window, event):
1381 """Hide the window of the accepted flights."""
1382 self._flightsWindow.hide()
1383 return True
1384
1385 def _editChecklist(self, menuItem):
1386 """Callback for editing the checklists."""
1387 self._checklistEditor.run()
1388
1389 def _editApproachCallouts(self, menuItem):
1390 """Callback for editing the approach callouts."""
1391 self._approachCalloutsEditor.run()
1392
1393 def _editPreferences(self, menuItem):
1394 """Callback for editing the preferences."""
1395 self._clearHotkeys()
1396 self._preferences.run(self.config)
1397 self._setupTimeSync()
1398 self._listenHotkeys()
1399
1400 def _reportBug(self, menuItem):
1401 """Callback for reporting a bug."""
1402 self._bugReportDialog.run()
1403
1404 def _setupTimeSync(self):
1405 """Enable or disable the simulator time synchronization based on the
1406 configuration."""
1407 simulator = self._simulator
1408 if simulator is not None:
1409 if self.config.syncFSTime:
1410 simulator.enableTimeSync()
1411 else:
1412 simulator.disableTimeSync()
1413
1414 def viewPIREP(self, pirep):
1415 """Display the PIREP viewer window with the given PIREP."""
1416 self._pirepViewer.setPIREP(pirep)
1417 self._pirepViewer.show_all()
1418 self._pirepViewer.run()
1419 self._pirepViewer.hide()
1420
1421 def viewMessagedPIREP(self, pirep):
1422 """Display the PIREP viewer window with the given PIREP containing
1423 messages as well."""
1424 self._messagedPIREPViewer.setPIREP(pirep)
1425 self._messagedPIREPViewer.show_all()
1426 self._messagedPIREPViewer.run()
1427 self._messagedPIREPViewer.hide()
1428
1429 def editPIREP(self, pirep):
1430 """Display the PIREP editor window and allow editing the PIREP."""
1431 self._pirepEditor.setPIREP(pirep)
1432 self._pirepEditor.show_all()
1433 if self._pirepEditor.run()==Gtk.ResponseType.OK:
1434 self.beginBusy(xstr("pirepEdit_save_busy"))
1435 self.webHandler.sendPIREP(self._pirepUpdatedCallback, pirep,
1436 update = True)
1437 else:
1438 self._pirepEditor.hide()
1439
1440 def _pirepUpdatedCallback(self, returned, result):
1441 """Callback for the PIREP updating result."""
1442 GObject.idle_add(self._handlePIREPUpdated, returned, result)
1443
1444 def _handlePIREPUpdated(self, returned, result):
1445 """Callback for the PIREP updating result."""
1446 self.endBusy()
1447 secondaryMarkup = None
1448 type = Gtk.MessageType.ERROR
1449 if returned:
1450 if result.success:
1451 type = None
1452 elif result.alreadyFlown:
1453 messageFormat = xstr("sendPIREP_already")
1454 secondaryMarkup = xstr("sendPIREP_already_sec")
1455 elif result.notAvailable:
1456 messageFormat = xstr("sendPIREP_notavail")
1457 else:
1458 messageFormat = xstr("sendPIREP_unknown")
1459 secondaryMarkup = xstr("sendPIREP_unknown_sec")
1460 else:
1461 print("PIREP sending failed", result)
1462 messageFormat = xstr("sendPIREP_failed")
1463 secondaryMarkup = xstr("sendPIREP_failed_sec")
1464
1465 if type is not None:
1466 dialog = Gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
1467 type = type, message_format = messageFormat)
1468 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1469 dialog.set_title(WINDOW_TITLE_BASE)
1470 if secondaryMarkup is not None:
1471 dialog.format_secondary_markup(secondaryMarkup)
1472
1473 dialog.run()
1474 dialog.hide()
1475
1476 self._pirepEditor.hide()
1477
1478 def _loadPIREP(self, menuItem):
1479 """Load a PIREP for sending."""
1480 dialog = self._getLoadPirepDialog()
1481
1482 if self._lastLoadedPIREP:
1483 dialog.set_current_folder(os.path.dirname(self._lastLoadedPIREP))
1484 else:
1485 pirepDirectory = self.config.pirepDirectory
1486 if pirepDirectory is not None:
1487 dialog.set_current_folder(pirepDirectory)
1488
1489 result = dialog.run()
1490 dialog.hide()
1491
1492 if result==Gtk.ResponseType.OK:
1493 self._lastLoadedPIREP = dialog.get_filename()
1494
1495 pirep = PIREP.load(self._lastLoadedPIREP)
1496 if pirep is None:
1497 dialog = Gtk.MessageDialog(parent = self._mainWindow,
1498 type = Gtk.MessageType.ERROR,
1499 message_format = xstr("loadPIREP_failed"))
1500 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1501 dialog.set_title(WINDOW_TITLE_BASE)
1502 dialog.format_secondary_markup(xstr("loadPIREP_failed_sec"))
1503 dialog.run()
1504 dialog.hide()
1505 else:
1506 dialog = self._getSendLoadedDialog(pirep)
1507 dialog.show_all()
1508 while True:
1509 result = dialog.run()
1510
1511 if result==Gtk.ResponseType.OK:
1512 self.sendPIREP(pirep)
1513 elif result==1:
1514 self.viewPIREP(pirep)
1515 else:
1516 break
1517
1518 dialog.hide()
1519
1520 def _getLoadPirepDialog(self):
1521 """Get the PIREP loading file chooser dialog.
1522
1523 If it is not created yet, it will be created."""
1524 if self._loadPIREPDialog is None:
1525 dialog = Gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
1526 xstr("loadPIREP_browser_title"),
1527 action = Gtk.FileChooserAction.OPEN,
1528 buttons = (Gtk.STOCK_CANCEL,
1529 Gtk.ResponseType.CANCEL,
1530 Gtk.STOCK_OK, Gtk.ResponseType.OK),
1531 parent = self._mainWindow)
1532 dialog.set_modal(True)
1533
1534
1535 filter = Gtk.FileFilter()
1536 filter.set_name(xstr("file_filter_pireps"))
1537 filter.add_pattern("*.pirep")
1538 dialog.add_filter(filter)
1539
1540 filter = Gtk.FileFilter()
1541 filter.set_name(xstr("file_filter_all"))
1542 filter.add_pattern("*.*")
1543 dialog.add_filter(filter)
1544
1545 self._loadPIREPDialog = dialog
1546
1547 return self._loadPIREPDialog
1548
1549 def _getSendLoadedDialog(self, pirep):
1550 """Get a dialog displaying the main information of the flight from the
1551 PIREP and providing Cancel and Send buttons."""
1552 dialog = Gtk.Dialog(title = WINDOW_TITLE_BASE + " - " +
1553 xstr("loadPIREP_send_title"),
1554 parent = self._mainWindow,
1555 flags = Gtk.DialogFlags.MODAL)
1556
1557 contentArea = dialog.get_content_area()
1558
1559 label = Gtk.Label(xstr("loadPIREP_send_help"))
1560 alignment = Gtk.Alignment(xalign = 0.5, yalign = 0.5,
1561 xscale = 0.0, yscale = 0.0)
1562 alignment.set_padding(padding_top = 16, padding_bottom = 0,
1563 padding_left = 48, padding_right = 48)
1564 alignment.add(label)
1565 contentArea.pack_start(alignment, False, False, 8)
1566
1567 table = Gtk.Table(5, 2)
1568 tableAlignment = Gtk.Alignment(xalign = 0.5, yalign = 0.5,
1569 xscale = 0.0, yscale = 0.0)
1570 tableAlignment.set_padding(padding_top = 0, padding_bottom = 32,
1571 padding_left = 48, padding_right = 48)
1572 table.set_row_spacings(4)
1573 table.set_col_spacings(16)
1574 tableAlignment.add(table)
1575 contentArea.pack_start(tableAlignment, True, True, 8)
1576
1577 bookedFlight = pirep.bookedFlight
1578
1579 label = Gtk.Label("<b>" + xstr("loadPIREP_send_flightno") + "</b>")
1580 label.set_use_markup(True)
1581 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1582 xscale = 0.0, yscale = 0.0)
1583 labelAlignment.add(label)
1584 table.attach(labelAlignment, 0, 1, 0, 1)
1585
1586 label = Gtk.Label(bookedFlight.callsign)
1587 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1588 xscale = 0.0, yscale = 0.0)
1589 labelAlignment.add(label)
1590 table.attach(labelAlignment, 1, 2, 0, 1)
1591
1592 label = Gtk.Label("<b>" + xstr("loadPIREP_send_date") + "</b>")
1593 label.set_use_markup(True)
1594 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1595 xscale = 0.0, yscale = 0.0)
1596 labelAlignment.add(label)
1597 table.attach(labelAlignment, 0, 1, 1, 2)
1598
1599 label = Gtk.Label(str(bookedFlight.departureTime.date()))
1600 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1601 xscale = 0.0, yscale = 0.0)
1602 labelAlignment.add(label)
1603 table.attach(labelAlignment, 1, 2, 1, 2)
1604
1605 label = Gtk.Label("<b>" + xstr("loadPIREP_send_from") + "</b>")
1606 label.set_use_markup(True)
1607 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1608 xscale = 0.0, yscale = 0.0)
1609 labelAlignment.add(label)
1610 table.attach(labelAlignment, 0, 1, 2, 3)
1611
1612 label = Gtk.Label(bookedFlight.departureICAO)
1613 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1614 xscale = 0.0, yscale = 0.0)
1615 labelAlignment.add(label)
1616 table.attach(labelAlignment, 1, 2, 2, 3)
1617
1618 label = Gtk.Label("<b>" + xstr("loadPIREP_send_to") + "</b>")
1619 label.set_use_markup(True)
1620 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1621 xscale = 0.0, yscale = 0.0)
1622 labelAlignment.add(label)
1623 table.attach(labelAlignment, 0, 1, 3, 4)
1624
1625 label = Gtk.Label(bookedFlight.arrivalICAO)
1626 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1627 xscale = 0.0, yscale = 0.0)
1628 labelAlignment.add(label)
1629 table.attach(labelAlignment, 1, 2, 3, 4)
1630
1631 label = Gtk.Label("<b>" + xstr("loadPIREP_send_rating") + "</b>")
1632 label.set_use_markup(True)
1633 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1634 xscale = 0.0, yscale = 0.0)
1635 labelAlignment.add(label)
1636 table.attach(labelAlignment, 0, 1, 4, 5)
1637
1638 rating = pirep.rating
1639 label = Gtk.Label()
1640 if rating<0:
1641 label.set_markup('<b><span foreground="red">NO GO</span></b>')
1642 else:
1643 label.set_text("%.1f %%" % (rating,))
1644
1645 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1646 xscale = 0.0, yscale = 0.0)
1647 labelAlignment.add(label)
1648 table.attach(labelAlignment, 1, 2, 4, 5)
1649
1650 dialog.add_button(xstr("button_cancel"), Gtk.ResponseType.REJECT)
1651 dialog.add_button(xstr("viewPIREP"), 1)
1652 dialog.add_button(xstr("sendPIREP"), Gtk.ResponseType.OK)
1653
1654 return dialog
1655
1656 def sendPIREP(self, pirep, callback = None):
1657 """Send the given PIREP."""
1658 self.beginBusy(xstr("sendPIREP_busy"))
1659 self._sendPIREPCallback = callback
1660 self.webHandler.sendPIREP(self._pirepSentCallback, pirep)
1661
1662 def _pirepSentCallback(self, returned, result):
1663 """Callback for the PIREP sending result."""
1664 GObject.idle_add(self._handlePIREPSent, returned, result)
1665
1666 def _handlePIREPSent(self, returned, result):
1667 """Callback for the PIREP sending result."""
1668 self.endBusy()
1669 secondaryMarkup = None
1670 type = Gtk.MessageType.ERROR
1671 if returned:
1672 if result.success:
1673 type = Gtk.MessageType.INFO
1674 messageFormat = xstr("sendPIREP_success")
1675 secondaryMarkup = xstr("sendPIREP_success_sec")
1676 elif result.alreadyFlown:
1677 messageFormat = xstr("sendPIREP_already")
1678 secondaryMarkup = xstr("sendPIREP_already_sec")
1679 elif result.notAvailable:
1680 messageFormat = xstr("sendPIREP_notavail")
1681 else:
1682 messageFormat = xstr("sendPIREP_unknown")
1683 secondaryMarkup = xstr("sendPIREP_unknown_sec")
1684 else:
1685 print("PIREP sending failed", result)
1686 messageFormat = xstr("sendPIREP_failed")
1687 secondaryMarkup = xstr("sendPIREP_failed_sec")
1688
1689 dialog = Gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
1690 type = type, message_format = messageFormat)
1691 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1692 dialog.set_title(WINDOW_TITLE_BASE)
1693 if secondaryMarkup is not None:
1694 dialog.format_secondary_markup(secondaryMarkup)
1695
1696 dialog.run()
1697 dialog.hide()
1698
1699 callback = self._sendPIREPCallback
1700 self._sendPIREPCallback = None
1701 if callback is not None:
1702 callback(returned, result)
1703
1704 def sendBugReport(self, summary, description, email, callback = None):
1705 """Send the bug report with the given data."""
1706 description += "\n\n" + ("=" * 40)
1707 description += "\n\nThe contents of the log:\n\n"
1708
1709 for (timestampString, text) in self._logger.lines:
1710 description += str(formatFlightLogLine(timestampString, text))
1711
1712 description += "\n\n" + ("=" * 40)
1713 description += "\n\nThe contents of the debug log:\n\n"
1714
1715 buffer = self._debugLogView.get_buffer()
1716 description += buffer.get_text(buffer.get_start_iter(),
1717 buffer.get_end_iter(), True)
1718
1719 self.beginBusy(xstr("sendBugReport_busy"))
1720 self._sendBugReportCallback = callback
1721 self.webHandler.sendBugReport(self._bugReportSentCallback,
1722 summary, description, email)
1723
1724 def _cefInitialized(self):
1725 """Called when CEF has been initialized."""
1726 self._acars.start()
1727 cef.initializeSimBrief()
1728
1729 def _bugReportSentCallback(self, returned, result):
1730 """Callback function for the bug report sending result."""
1731 GObject.idle_add(self._handleBugReportSent, returned, result)
1732
1733 def _handleBugReportSent(self, returned, result):
1734 """Callback for the bug report sending result."""
1735 self.endBusy()
1736 secondaryMarkup = None
1737 type = Gtk.MessageType.ERROR
1738 if returned:
1739 if result.success:
1740 type = Gtk.MessageType.INFO
1741 messageFormat = xstr("sendBugReport_success") % (result.ticketID,)
1742 secondaryMarkup = xstr("sendBugReport_success_sec")
1743 else:
1744 messageFormat = xstr("sendBugReport_error")
1745 secondaryMarkup = xstr("sendBugReport_siteerror_sec")
1746 else:
1747 messageFormat = xstr("sendBugReport_error")
1748 secondaryMarkup = xstr("sendBugReport_error_sec")
1749
1750 dialog = Gtk.MessageDialog(parent = self._wizard.gui._bugReportDialog,
1751 type = type, message_format = messageFormat)
1752 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1753 dialog.set_title(WINDOW_TITLE_BASE)
1754 if secondaryMarkup is not None:
1755 dialog.format_secondary_markup(secondaryMarkup)
1756
1757 dialog.run()
1758 dialog.hide()
1759
1760 callback = self._sendBugReportCallback
1761 self._sendBugReportCallback = None
1762 if callback is not None:
1763 callback(returned, result)
1764
1765 def _listenHotkeys(self):
1766 """Setup the hotkeys based on the configuration."""
1767 if self._hotkeySetID is None and self._simulator is not None:
1768 self._pilotHotkeyIndex = None
1769 self._checklistHotkeyIndex = None
1770
1771 hotkeys = []
1772
1773 config = self.config
1774 if config.enableSounds and config.pilotControlsSounds:
1775 self._pilotHotkeyIndex = len(hotkeys)
1776 hotkeys.append(config.pilotHotkey)
1777
1778 if config.enableChecklists:
1779 self._checklistHotkeyIndex = len(hotkeys)
1780 hotkeys.append(config.checklistHotkey)
1781
1782 if hotkeys:
1783 self._hotkeySetID = \
1784 self._simulator.listenHotkeys(hotkeys, self._handleHotkeys)
1785
1786 def _clearHotkeys(self):
1787 """Clear the hotkeys."""
1788 if self._hotkeySetID is not None:
1789 self._hotkeySetID=None
1790 self._simulator.clearHotkeys()
1791
1792 def _handleHotkeys(self, id, hotkeys):
1793 """Handle the hotkeys."""
1794 if id==self._hotkeySetID:
1795 for index in hotkeys:
1796 if index==self._pilotHotkeyIndex:
1797 print("gui.GUI._handleHotkeys: pilot hotkey pressed")
1798 self._flight.pilotHotkeyPressed()
1799 elif index==self._checklistHotkeyIndex:
1800 print("gui.GUI._handleHotkeys: checklist hotkey pressed")
1801 self._flight.checklistHotkeyPressed()
1802 else:
1803 print("gui.GUI._handleHotkeys: unhandled hotkey index:", index)
1804
1805 def _showManual(self, menuitem):
1806 """Show the user's manual."""
1807 webbrowser.open(url ="file://" +
1808 os.path.join(self._programDirectory, "doc", "manual",
1809 getLanguage(), "index.html"),
1810 new = 1)
1811
1812 def _showAbout(self, menuitem):
1813 """Show the about dialog."""
1814 dialog = self._getAboutDialog()
1815 dialog.show_all()
1816 dialog.run()
1817 dialog.hide()
1818
1819 def _getAboutDialog(self):
1820 """Get the about dialog.
1821
1822 If it does not exist yet, it will be created."""
1823 if self._aboutDialog is None:
1824 dialog = Gtk.AboutDialog()
1825 dialog.set_transient_for(self._mainWindow)
1826 dialog.set_modal(True)
1827
1828 logoPath = os.path.join(self._programDirectory, "logo.png")
1829 logo = GdkPixbuf.Pixbuf.new_from_file(logoPath)
1830 dialog.set_logo(logo)
1831
1832 dialog.set_program_name(PROGRAM_NAME)
1833 dialog.set_version(const.VERSION)
1834 dialog.set_copyright("(c) 2012 - 2019 by István Váradi")
1835 dialog.set_website("http://mlx.varadiistvan.hu")
1836 dialog.set_website_label(xstr("about_website"))
1837
1838 isHungarian = getLanguage()=="hu"
1839 authors = []
1840 for (familyName, firstName, role) in GUI._authors:
1841 author = "%s %s" % \
1842 (familyName if isHungarian else firstName,
1843 firstName if isHungarian else familyName)
1844 role = xstr("about_role_" + role)
1845 authors.append(author + " (" + role + ")")
1846 dialog.set_authors(authors)
1847
1848 dialog.set_license(xstr("about_license"))
1849
1850 self._aboutDialog = dialog
1851
1852 return self._aboutDialog
1853
1854 def _showAboutURL(self, dialog, link, user_data):
1855 """Show the about URL."""
1856 webbrowser.open(url = link, new = 1)
1857
1858 def _setTakeoffAntiIceOn(self, value):
1859 """Set the anti-ice on indicator."""
1860 self._wizard.takeoffAntiIceOn = value
1861
1862 def _setLandingAntiIceOn(self, value):
1863 """Set the anti-ice on indicator."""
1864 self._wizard.landingAntiIceOn = value
1865
1866 def _getCredentialsCallback(self):
1867 """Called when the web handler asks for the credentials."""
1868 # FIXME: this is almost the same as
1869 # SimBriefSetupPage._getCredentialsCallback
1870 with self._credentialsCondition:
1871 self._credentialsAvailable = False
1872
1873 GObject.idle_add(self._getCredentials)
1874
1875 while not self._credentialsAvailable:
1876 self._credentialsCondition.wait()
1877
1878 return (self._credentialsUserName, self._credentialsPassword)
1879
1880 def _getCredentials(self):
1881 """Get the credentials."""
1882 # FIXME: this is almost the same as
1883 # SimBriefSetupPage._getCredentials
1884 with self._credentialsCondition:
1885 config = self.config
1886
1887 dialog = CredentialsDialog(self, config.pilotID, config.password,
1888 xstr("login_title"),
1889 xstr("button_cancel"),
1890 xstr("button_ok"),
1891 xstr("label_pilotID"),
1892 xstr("login_pilotID_tooltip"),
1893 xstr("label_password"),
1894 xstr("login_password_tooltip"),
1895 xstr("login_info"),
1896 config.rememberPassword,
1897 xstr("remember_password"),
1898 xstr("login_remember_tooltip"))
1899 response = dialog.run()
1900
1901 if response==Gtk.ResponseType.OK:
1902 self._credentialsUserName = dialog.userName
1903 self._credentialsPassword = dialog.password
1904 rememberPassword = dialog.rememberPassword
1905
1906 config.pilotID = self._credentialsUserName
1907
1908 config.password = \
1909 self._credentialsPassword if rememberPassword else ""
1910 config.rememberPassword = rememberPassword
1911
1912 config.save()
1913 else:
1914 self._credentialsUserName = None
1915 self._credentialsPassword = None
1916
1917 self._credentialsAvailable = True
1918 self._credentialsCondition.notify()
1919
1920 def _updateDone(self):
1921 """Called when the update is done.
1922
1923 It checks if we already know the PID, and if not, asks the user whether
1924 to register."""
1925 cef.initialize(self._cefInitialized)
1926
1927 if not self.config.pilotID and not self.config.password:
1928 dialog = Gtk.MessageDialog(parent = self._mainWindow,
1929 type = Gtk.MessageType.QUESTION,
1930 message_format = xstr("register_ask"))
1931
1932 dialog.set_title(WINDOW_TITLE_BASE)
1933 dialog.format_secondary_markup(xstr("register_ask_sec"))
1934
1935 dialog.add_button(xstr("button_cancel"), 0)
1936 dialog.add_button(xstr("button_register"), 1)
1937 dialog.set_default_response(1)
1938
1939 result = dialog.run()
1940 dialog.hide()
1941 if result == 1:
1942 self._wizard.jumpPage("register")
Note: See TracBrowser for help on using the repository browser.