source: src/mlx/gui/gui.py@ 92:1ca250ec2aa0

Last change on this file since 92:1ca250ec2aa0 was 92:1ca250ec2aa0, checked in by István Váradi <ivaradi@…>, 13 years ago

Further work on the reset

File size: 16.6 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 self._notebook = gtk.Notebook()
63 mainVBox.add(self._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 self._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 self._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 self._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 self._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
243 self._flightInfo.reset()
244 self.resetFlightStatus()
245
246 self._wizard.reset()
247 self._notebook.set_current_page(0)
248
249 self._logView.get_buffer().set_text("")
250
251 def _disconnect(self):
252 """Disconnect from the simulator if connected."""
253 self.stopMonitoring()
254
255 if self._connected:
256 self._flight.simulator.disconnect()
257 self._connected = False
258
259 self._connecting = False
260 self._reconnecting = False
261 self._statusbar.updateConnection(False, False)
262
263 def write(self, msg):
264 """Write the given message to the log."""
265 gobject.idle_add(self._writeLog, msg)
266
267 def check(self, flight, aircraft, logger, oldState, state):
268 """Update the data."""
269 gobject.idle_add(self._monitorWindow.setData, state)
270 gobject.idle_add(self._statusbar.updateTime, state.timestamp)
271
272 def resetFlightStatus(self):
273 """Reset the status of the flight."""
274 self._statusbar.resetFlightStatus()
275 self._statusbar.updateTime()
276 self._statusIcon.resetFlightStatus()
277
278 def setStage(self, stage):
279 """Set the stage of the flight."""
280 gobject.idle_add(self._setStage, stage)
281
282 def _setStage(self, stage):
283 """Set the stage of the flight."""
284 self._statusbar.setStage(stage)
285 self._statusIcon.setStage(stage)
286 self._wizard.setStage(stage)
287 if stage==const.STAGE_END:
288 self._disconnect()
289
290 def setRating(self, rating):
291 """Set the rating of the flight."""
292 gobject.idle_add(self._setRating, rating)
293
294 def _setRating(self, rating):
295 """Set the rating of the flight."""
296 self._statusbar.setRating(rating)
297 self._statusIcon.setRating(rating)
298
299 def setNoGo(self, reason):
300 """Set the rating of the flight to No-Go with the given reason."""
301 gobject.idle_add(self._setNoGo, reason)
302
303 def _setNoGo(self, reason):
304 """Set the rating of the flight."""
305 self._statusbar.setNoGo(reason)
306 self._statusIcon.setNoGo(reason)
307
308 def _handleMainWindowState(self, window, event):
309 """Hande a change in the state of the window"""
310 iconified = gdk.WindowState.ICONIFIED if pygobject \
311 else gdk.WINDOW_STATE_ICONIFIED
312 if (event.changed_mask&iconified)!=0 and (event.new_window_state&iconified)!=0:
313 self.hideMainWindow(savePosition = False)
314
315 def hideMainWindow(self, savePosition = True):
316 """Hide the main window and save its position."""
317 if savePosition:
318 (self._mainWindowX, self._mainWindowY) = \
319 self._mainWindow.get_window().get_root_origin()
320 else:
321 self._mainWindowX = self._mainWindowY = None
322 self._mainWindow.hide()
323 self._statusIcon.mainWindowHidden()
324 return True
325
326 def showMainWindow(self):
327 """Show the main window at its former position."""
328 if self._mainWindowX is not None and self._mainWindowY is not None:
329 self._mainWindow.move(self._mainWindowX, self._mainWindowY)
330
331 self._mainWindow.show()
332 self._mainWindow.deiconify()
333
334 self._statusIcon.mainWindowShown()
335
336 def toggleMainWindow(self):
337 """Toggle the main window."""
338 if self._mainWindow.get_visible():
339 self.hideMainWindow()
340 else:
341 self.showMainWindow()
342
343 def hideMonitorWindow(self, savePosition = True):
344 """Hide the monitor window."""
345 if savePosition:
346 (self._monitorWindowX, self._monitorWindowY) = \
347 self._monitorWindow.get_window().get_root_origin()
348 else:
349 self._monitorWindowX = self._monitorWindowY = None
350 self._monitorWindow.hide()
351 self._statusIcon.monitorWindowHidden()
352 return True
353
354 def showMonitorWindow(self):
355 """Show the monitor window."""
356 if self._monitorWindowX is not None and self._monitorWindowY is not None:
357 self._monitorWindow.move(self._monitorWindowX, self._monitorWindowY)
358 self._monitorWindow.show_all()
359 self._statusIcon.monitorWindowShown()
360
361 def restart(self):
362 """Quit and restart the application."""
363 self.toRestart = True
364 self._quit(force = True)
365
366 def flushStdIO(self):
367 """Flush any text to the standard error that could not be logged."""
368 if self._stdioText:
369 sys.__stderr__.write(self._stdioText)
370
371 def writeStdIO(self, text):
372 """Write the given text into standard I/O log."""
373 with self._stdioLock:
374 self._stdioText += text
375
376 gobject.idle_add(self._writeStdIO)
377
378 def beginBusy(self, message):
379 """Begin a period of background processing."""
380 self._mainWindow.get_window().set_cursor(self._busyCursor)
381 self._statusbar.updateBusyState(message)
382
383 def endBusy(self):
384 """End a period of background processing."""
385 self._mainWindow.get_window().set_cursor(None)
386 self._statusbar.updateBusyState(None)
387
388 def _writeStdIO(self):
389 """Perform the real writing."""
390 with self._stdioLock:
391 text = self._stdioText
392 self._stdioText = ""
393 if not text: return
394
395 lines = text.splitlines()
396 if text[-1]=="\n":
397 text = ""
398 else:
399 text = lines[-1]
400 lines = lines[:-1]
401
402 for line in lines:
403 if self._stdioAfterNewLine:
404 line = "[STDIO] " + line
405 self._writeLog(line + "\n")
406 self._stdioAfterNewLine = True
407
408 if text:
409 if self._stdioAfterNewLine:
410 text = "[STDIO] " + text
411 self._writeLog(text)
412 self._stdioAfterNewLine = False
413
414 def connectSimulator(self, aircraftType):
415 """Connect to the simulator for the first time."""
416 self._logger.reset()
417
418 self._flight = flight.Flight(self._logger, self)
419 self._flight.aircraftType = aircraftType
420 self._flight.aircraft = acft.Aircraft.create(self._flight)
421 self._flight.aircraft._checkers.append(self)
422
423 if self._simulator is None:
424 self._simulator = fs.createSimulator(const.SIM_MSFS9, self)
425
426 self._flight.simulator = self._simulator
427
428 self.beginBusy("Connecting to the simulator...")
429 self._statusbar.updateConnection(self._connecting, self._connected)
430
431 self._connecting = True
432 self._simulator.connect(self._flight.aircraft)
433
434 def startMonitoring(self):
435 """Start monitoring."""
436 if not self._monitoring:
437 self.simulator.startMonitoring()
438 self._monitoring = True
439
440 def stopMonitoring(self):
441 """Stop monitoring."""
442 if self._monitoring:
443 self.simulator.stopMonitoring()
444 self._monitoring = False
445
446 def _buildLogFrame(self):
447 """Build the frame for the log."""
448 logFrame = gtk.Frame(label = "Log")
449
450 frameAlignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
451
452 frameAlignment.set_padding(padding_top = 4, padding_bottom = 10,
453 padding_left = 16, padding_right = 16)
454
455 logFrame.add(frameAlignment)
456
457 logScroller = gtk.ScrolledWindow()
458 self._logView = gtk.TextView()
459 self._logView.set_editable(False)
460 logScroller.add(self._logView)
461
462 logBox = gtk.VBox()
463 logBox.pack_start(logScroller, True, True, 0)
464 logBox.set_size_request(-1, 200)
465
466 frameAlignment.add(logBox)
467
468 return logFrame
469
470 def _writeLog(self, msg):
471 """Write the given message to the log."""
472 buffer = self._logView.get_buffer()
473 buffer.insert(buffer.get_end_iter(), msg)
474 self._logView.scroll_mark_onscreen(buffer.get_insert())
475
476 def _quit(self, what = None, force = False):
477 """Quit from the application."""
478 if force:
479 result=RESPONSETYPE_YES
480 else:
481 dialog = gtk.MessageDialog(type = MESSAGETYPE_QUESTION,
482 buttons = BUTTONSTYPE_YES_NO,
483 message_format =
484 "Are you sure to quit the logger?")
485 result = dialog.run()
486 dialog.hide()
487
488 if result==RESPONSETYPE_YES:
489 self._statusIcon.destroy()
490 return gtk.main_quit()
491
492 def _notebookPageSwitch(self, notebook, page, page_num):
493 """Called when the current page of the notebook has changed."""
494 if page_num==0:
495 gobject.idle_add(self._wizard.grabDefault)
496 else:
497 self._mainWindow.set_default(None)
Note: See TracBrowser for help on using the repository browser.