source: src/mlx/gui/gui.py@ 78:31c23c6721d1

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

Added some further data to the connect page

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