source: src/mlx/gui/gui.py@ 83:92b601c6cbf2

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

Updated the version, and modified the code to not ask for confirmation when restarting after an update

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