source: src/mlx/gui/gui.py@ 91:c3fc045d7aeb

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

Reworked the GUI reset

File size: 16.5 KB
Line 
1# The main file for the GUI
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
10
11import mlx.const as const
12import mlx.fs as fs
13import mlx.flight as flight
14import mlx.logger as logger
15import mlx.acft as acft
16import mlx.web as web
17
18import time
19import threading
20import sys
21
22#------------------------------------------------------------------------------
23
24class GUI(fs.ConnectionListener):
25 """The main GUI class."""
26 def __init__(self, programDirectory, config):
27 """Construct the GUI."""
28 gobject.threads_init()
29
30 self._programDirectory = programDirectory
31 self.config = config
32 self._connecting = False
33 self._reconnecting = False
34 self._connected = False
35 self._logger = logger.Logger(output = self)
36 self._flight = None
37 self._simulator = None
38 self._monitoring = False
39
40 self._stdioLock = threading.Lock()
41 self._stdioText = ""
42 self._stdioAfterNewLine = True
43
44 self.webHandler = web.Handler()
45 self.webHandler.start()
46
47 self.toRestart = False
48
49 def build(self, iconDirectory):
50 """Build the GUI."""
51
52 window = gtk.Window()
53 window.set_title("MAVA Logger X " + const.VERSION)
54 window.set_icon_from_file(os.path.join(iconDirectory, "logo.ico"))
55 window.connect("delete-event",
56 lambda a, b: self.hideMainWindow())
57 window.connect("window-state-event", self._handleMainWindowState)
58
59 mainVBox = gtk.VBox()
60 window.add(mainVBox)
61
62 notebook = gtk.Notebook()
63 mainVBox.add(notebook)
64
65 self._wizard = Wizard(self)
66 label = gtk.Label("Fligh_t")
67 label.set_use_underline(True)
68 label.set_tooltip_text("Flight wizard")
69 notebook.append_page(self._wizard, label)
70
71 self._flightInfo = FlightInfo(self)
72 label = gtk.Label("Flight _info")
73 label.set_use_underline(True)
74 label.set_tooltip_text("Flight information")
75 notebook.append_page(self._flightInfo, label)
76
77 logVBox = gtk.VBox()
78 label = gtk.Label("_Log")
79 label.set_use_underline(True)
80 label.set_tooltip_text("Flight log")
81 notebook.append_page(logVBox, label)
82
83 logFrame = self._buildLogFrame()
84 logFrame.set_border_width(8)
85 logVBox.pack_start(logFrame, True, True, 0)
86
87 mainVBox.pack_start(gtk.HSeparator(), False, False, 0)
88
89 self._statusbar = Statusbar()
90 mainVBox.pack_start(self._statusbar, False, False, 0)
91
92 notebook.connect("switch-page", self._notebookPageSwitch)
93
94 self._monitorWindow = MonitorWindow(self, iconDirectory)
95 self._monitorWindowX = None
96 self._monitorWindowY = None
97
98 window.show_all()
99 self._wizard.grabDefault()
100
101 self._mainWindow = window
102
103 self._statusIcon = StatusIcon(iconDirectory, self)
104
105 self._busyCursor = gdk.Cursor(gdk.CursorType.WATCH if pygobject
106 else gdk.WATCH)
107
108 @property
109 def simulator(self):
110 """Get the simulator used by us."""
111 return self._simulator
112
113 @property
114 def flight(self):
115 """Get the flight being performed."""
116 return self._flight
117
118 @property
119 def zfw(self):
120 """Get Zero-Fuel Weight calculated for the current flight."""
121 return self._wizard.zfw
122
123 @property
124 def cruiseAltitude(self):
125 """Get cruise altitude calculated for the current flight."""
126 return self._wizard.cruiseAltitude
127
128 @property
129 def v1(self):
130 """Get the V1 speed calculated for the flight."""
131 return self._wizard.v1
132
133 @property
134 def vr(self):
135 """Get the Vr speed calculated for the flight."""
136 return self._wizard.vr
137
138 @property
139 def v2(self):
140 """Get the V2 speed calculated for the flight."""
141 return self._wizard.v2
142
143 @property
144 def vref(self):
145 """Get the Vref speed calculated for the flight."""
146 return self._wizard.vref
147
148 def run(self):
149 """Run the GUI."""
150 if self.config.autoUpdate:
151 self._updater = Updater(self,
152 self._programDirectory,
153 self.config.updateURL,
154 self._mainWindow)
155 self._updater.start()
156
157 gtk.main()
158
159 self._disconnect()
160
161 def connected(self, fsType, descriptor):
162 """Called when we have connected to the simulator."""
163 self._connected = True
164 self._logger.untimedMessage("Connected to the simulator %s" % (descriptor,))
165 gobject.idle_add(self._handleConnected, fsType, descriptor)
166
167 def _handleConnected(self, fsType, descriptor):
168 """Called when the connection to the simulator has succeeded."""
169 self._statusbar.updateConnection(self._connecting, self._connected)
170 self.endBusy()
171 if not self._reconnecting:
172 self._wizard.connected(fsType, descriptor)
173 self._reconnecting = False
174
175 def connectionFailed(self):
176 """Called when the connection failed."""
177 self._logger.untimedMessage("Connection to the simulator failed")
178 gobject.idle_add(self._connectionFailed)
179
180 def _connectionFailed(self):
181 """Called when the connection failed."""
182 self.endBusy()
183 self._statusbar.updateConnection(self._connecting, self._connected)
184
185 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
186 message_format =
187 "Cannot connect to the simulator.",
188 parent = self._mainWindow)
189 dialog.format_secondary_markup("Rectify the situation, and press <b>Try again</b> "
190 "to try the connection again, "
191 "or <b>Cancel</b> to cancel the flight.")
192
193 dialog.add_button("_Cancel", 0)
194 dialog.add_button("_Try again", 1)
195 dialog.set_default_response(1)
196
197 result = dialog.run()
198 dialog.hide()
199 if result == 1:
200 self.beginBusy("Connecting to the simulator.")
201 self._simulator.reconnect()
202 else:
203 self.reset()
204
205 def disconnected(self):
206 """Called when we have disconnected from the simulator."""
207 self._connected = False
208 self._logger.untimedMessage("Disconnected from the simulator")
209
210 gobject.idle_add(self._disconnected)
211
212 def _disconnected(self):
213 """Called when we have disconnected from the simulator unexpectedly."""
214 self._statusbar.updateConnection(self._connecting, self._connected)
215
216 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
217 message_format =
218 "The connection to the simulator failed unexpectedly.",
219 parent = self._mainWindow)
220 dialog.format_secondary_markup("If the simulator has crashed, restart it "
221 "and restore your flight as much as possible "
222 "to the state it was in before the crash.\n"
223 "Then press <b>Reconnect</b> to reconnect.\n\n"
224 "If you want to cancel the flight, press <b>Cancel</b>.")
225
226 dialog.add_button("_Cancel", 0)
227 dialog.add_button("_Reconnect", 1)
228 dialog.set_default_response(1)
229
230 result = dialog.run()
231 dialog.hide()
232 if result == 1:
233 self.beginBusy("Connecting to the simulator.")
234 self._reconnecting = True
235 self._simulator.reconnect()
236 else:
237 self.reset()
238
239 def reset(self):
240 """Reset the GUI."""
241 self._disconnect()
242 self._wizard.reset()
243 self._flightInfo.reset()
244 self.resetFlightStatus()
245
246 def _disconnect(self):
247 """Disconnect from the simulator if connected."""
248 if self._connected:
249 self.stopMonitoring()
250 self._flight.simulator.disconnect()
251 self._connected = False
252
253 self._connecting = False
254 self._reconnecting = False
255 self._statusbar.updateConnection(False, False)
256
257 def write(self, msg):
258 """Write the given message to the log."""
259 gobject.idle_add(self._writeLog, msg)
260
261 def check(self, flight, aircraft, logger, oldState, state):
262 """Update the data."""
263 gobject.idle_add(self._monitorWindow.setData, state)
264 gobject.idle_add(self._statusbar.updateTime, state.timestamp)
265
266 def resetFlightStatus(self):
267 """Reset the status of the flight."""
268 self._statusbar.resetFlightStatus()
269 self._statusbar.updateTime()
270 self._statusIcon.resetFlightStatus()
271
272 def setStage(self, stage):
273 """Set the stage of the flight."""
274 gobject.idle_add(self._setStage, stage)
275
276 def _setStage(self, stage):
277 """Set the stage of the flight."""
278 self._statusbar.setStage(stage)
279 self._statusIcon.setStage(stage)
280 self._wizard.setStage(stage)
281 if stage==const.STAGE_END:
282 self._disconnect()
283
284 def setRating(self, rating):
285 """Set the rating of the flight."""
286 gobject.idle_add(self._setRating, rating)
287
288 def _setRating(self, rating):
289 """Set the rating of the flight."""
290 self._statusbar.setRating(rating)
291 self._statusIcon.setRating(rating)
292
293 def setNoGo(self, reason):
294 """Set the rating of the flight to No-Go with the given reason."""
295 gobject.idle_add(self._setNoGo, reason)
296
297 def _setNoGo(self, reason):
298 """Set the rating of the flight."""
299 self._statusbar.setNoGo(reason)
300 self._statusIcon.setNoGo(reason)
301
302 def _handleMainWindowState(self, window, event):
303 """Hande a change in the state of the window"""
304 iconified = gdk.WindowState.ICONIFIED if pygobject \
305 else gdk.WINDOW_STATE_ICONIFIED
306 if (event.changed_mask&iconified)!=0 and (event.new_window_state&iconified)!=0:
307 self.hideMainWindow(savePosition = False)
308
309 def hideMainWindow(self, savePosition = True):
310 """Hide the main window and save its position."""
311 if savePosition:
312 (self._mainWindowX, self._mainWindowY) = \
313 self._mainWindow.get_window().get_root_origin()
314 else:
315 self._mainWindowX = self._mainWindowY = None
316 self._mainWindow.hide()
317 self._statusIcon.mainWindowHidden()
318 return True
319
320 def showMainWindow(self):
321 """Show the main window at its former position."""
322 if self._mainWindowX is not None and self._mainWindowY is not None:
323 self._mainWindow.move(self._mainWindowX, self._mainWindowY)
324
325 self._mainWindow.show()
326 self._mainWindow.deiconify()
327
328 self._statusIcon.mainWindowShown()
329
330 def toggleMainWindow(self):
331 """Toggle the main window."""
332 if self._mainWindow.get_visible():
333 self.hideMainWindow()
334 else:
335 self.showMainWindow()
336
337 def hideMonitorWindow(self, savePosition = True):
338 """Hide the monitor window."""
339 if savePosition:
340 (self._monitorWindowX, self._monitorWindowY) = \
341 self._monitorWindow.get_window().get_root_origin()
342 else:
343 self._monitorWindowX = self._monitorWindowY = None
344 self._monitorWindow.hide()
345 self._statusIcon.monitorWindowHidden()
346 return True
347
348 def showMonitorWindow(self):
349 """Show the monitor window."""
350 if self._monitorWindowX is not None and self._monitorWindowY is not None:
351 self._monitorWindow.move(self._monitorWindowX, self._monitorWindowY)
352 self._monitorWindow.show_all()
353 self._statusIcon.monitorWindowShown()
354
355 def restart(self):
356 """Quit and restart the application."""
357 self.toRestart = True
358 self._quit(force = True)
359
360 def flushStdIO(self):
361 """Flush any text to the standard error that could not be logged."""
362 if self._stdioText:
363 sys.__stderr__.write(self._stdioText)
364
365 def writeStdIO(self, text):
366 """Write the given text into standard I/O log."""
367 with self._stdioLock:
368 self._stdioText += text
369
370 gobject.idle_add(self._writeStdIO)
371
372 def beginBusy(self, message):
373 """Begin a period of background processing."""
374 self._mainWindow.get_window().set_cursor(self._busyCursor)
375 self._statusbar.updateBusyState(message)
376
377 def endBusy(self):
378 """End a period of background processing."""
379 self._mainWindow.get_window().set_cursor(None)
380 self._statusbar.updateBusyState(None)
381
382 def _writeStdIO(self):
383 """Perform the real writing."""
384 with self._stdioLock:
385 text = self._stdioText
386 self._stdioText = ""
387 if not text: return
388
389 lines = text.splitlines()
390 if text[-1]=="\n":
391 text = ""
392 else:
393 text = lines[-1]
394 lines = lines[:-1]
395
396 for line in lines:
397 if self._stdioAfterNewLine:
398 line = "[STDIO] " + line
399 self._writeLog(line + "\n")
400 self._stdioAfterNewLine = True
401
402 if text:
403 if self._stdioAfterNewLine:
404 text = "[STDIO] " + text
405 self._writeLog(text)
406 self._stdioAfterNewLine = False
407
408 def connectSimulator(self, aircraftType):
409 """Connect to the simulator for the first time."""
410 self._logger.reset()
411
412 self._flight = flight.Flight(self._logger, self)
413 self._flight.aircraftType = aircraftType
414 self._flight.aircraft = acft.Aircraft.create(self._flight)
415 self._flight.aircraft._checkers.append(self)
416
417 if self._simulator is None:
418 self._simulator = fs.createSimulator(const.SIM_MSFS9, self)
419
420 self._flight.simulator = self._simulator
421
422 self.beginBusy("Connecting to the simulator...")
423 self._statusbar.updateConnection(self._connecting, self._connected)
424
425 self._connecting = True
426 self._simulator.connect(self._flight.aircraft)
427
428 def startMonitoring(self):
429 """Start monitoring."""
430 if not self._monitoring:
431 self.simulator.startMonitoring()
432 self._monitoring = True
433
434 def stopMonitoring(self):
435 """Stop monitoring."""
436 if self._monitoring:
437 self.simulator.stopMonitoring()
438 self._monitoring = False
439
440 def _buildLogFrame(self):
441 """Build the frame for the log."""
442 logFrame = gtk.Frame(label = "Log")
443
444 frameAlignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
445
446 frameAlignment.set_padding(padding_top = 4, padding_bottom = 10,
447 padding_left = 16, padding_right = 16)
448
449 logFrame.add(frameAlignment)
450
451 logScroller = gtk.ScrolledWindow()
452 self._logView = gtk.TextView()
453 self._logView.set_editable(False)
454 logScroller.add(self._logView)
455
456 logBox = gtk.VBox()
457 logBox.pack_start(logScroller, True, True, 0)
458 logBox.set_size_request(-1, 200)
459
460 frameAlignment.add(logBox)
461
462 return logFrame
463
464 def _writeLog(self, msg):
465 """Write the given message to the log."""
466 buffer = self._logView.get_buffer()
467 buffer.insert(buffer.get_end_iter(), msg)
468 self._logView.scroll_mark_onscreen(buffer.get_insert())
469
470 def _quit(self, what = None, force = False):
471 """Quit from the application."""
472 if force:
473 result=RESPONSETYPE_YES
474 else:
475 dialog = gtk.MessageDialog(type = MESSAGETYPE_QUESTION,
476 buttons = BUTTONSTYPE_YES_NO,
477 message_format =
478 "Are you sure to quit the logger?")
479 result = dialog.run()
480 dialog.hide()
481
482 if result==RESPONSETYPE_YES:
483 self._statusIcon.destroy()
484 return gtk.main_quit()
485
486 def _notebookPageSwitch(self, notebook, page, page_num):
487 """Called when the current page of the notebook has changed."""
488 if page_num==0:
489 gobject.idle_add(self._wizard.grabDefault)
490 else:
491 self._mainWindow.set_default(None)
Note: See TracBrowser for help on using the repository browser.