source: src/mlx/gui/gui.py@ 77:cc8b178b8102

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

The monitoring window is now separate window

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