source: src/mlx/gui/gui.py@ 85:42b688827d63

Last change on this file since 85:42b688827d63 was 84:40b2d74e74f4, checked in by István Váradi <ivaradi@…>, 13 years ago

Created an integer entry widget and made use of it for entering the cargo weight and the takeoff speeds.

File size: 16.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 @property
112 def zfw(self):
113 """Get Zero-Fuel Weight calculated for the current flight."""
114 return self._wizard.zfw
115
116 @property
117 def cruiseAltitude(self):
118 """Get cruise altitude calculated for the current flight."""
119 return self._wizard.cruiseAltitude
120
121 @property
122 def v1(self):
123 """Get the V1 speed calculated for the flight."""
124 return self._wizard.v1
125
126 @property
127 def vr(self):
128 """Get the Vr speed calculated for the flight."""
129 return self._wizard.vr
130
131 @property
132 def v2(self):
133 """Get the V2 speed calculated for the flight."""
134 return self._wizard.v2
135
136 def run(self):
137 """Run the GUI."""
138 if self.config.autoUpdate:
139 self._updater = Updater(self,
140 self._programDirectory,
141 self.config.updateURL,
142 self._mainWindow)
143 self._updater.start()
144
145 gtk.main()
146
147 if self._flight is not None:
148 simulator = self._flight.simulator
149 if self._monitoring:
150 simulator.stopMonitoring()
151 self._monitoring = False
152 simulator.disconnect()
153
154 def connected(self, fsType, descriptor):
155 """Called when we have connected to the simulator."""
156 self._connected = True
157 self._logger.untimedMessage("Connected to the simulator %s" % (descriptor,))
158 gobject.idle_add(self._handleConnected, fsType, descriptor)
159
160 def _handleConnected(self, fsType, descriptor):
161 """Called when the connection to the simulator has succeeded."""
162 self._statusbar.updateConnection(self._connecting, self._connected)
163 self.endBusy()
164 if not self._reconnecting:
165 self._wizard.connected(fsType, descriptor)
166 self._reconnecting = False
167
168 def connectionFailed(self):
169 """Called when the connection failed."""
170 self._logger.untimedMessage("Connection to the simulator failed")
171 gobject.idle_add(self._connectionFailed)
172
173 def _connectionFailed(self):
174 """Called when the connection failed."""
175 self.endBusy()
176 self._statusbar.updateConnection(self._connecting, self._connected)
177
178 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
179 message_format =
180 "Cannot connect to the simulator.",
181 parent = self._mainWindow)
182 dialog.format_secondary_markup("Rectify the situation, and press <b>Try again</b> "
183 "to try the connection again, "
184 "or <b>Cancel</b> to cancel the flight.")
185
186 dialog.add_button("_Cancel", 0)
187 dialog.add_button("_Try again", 1)
188 dialog.set_default_response(1)
189
190 result = dialog.run()
191 dialog.hide()
192 if result == 1:
193 self.beginBusy("Connecting to the simulator.")
194 self._simulator.reconnect()
195 else:
196 self._connecting = False
197 self._reconnecting = False
198 self._statusbar.updateConnection(self._connecting, self._connected)
199 self._wizard.connectionFailed()
200
201 def disconnected(self):
202 """Called when we have disconnected from the simulator."""
203 self._connected = False
204 self._logger.untimedMessage("Disconnected from the simulator")
205
206 gobject.idle_add(self._disconnected)
207
208 def _disconnected(self):
209 """Called when we have disconnected from the simulator unexpectedly."""
210 self._statusbar.updateConnection(self._connecting, self._connected)
211
212 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
213 message_format =
214 "The connection to the simulator failed unexpectedly.",
215 parent = self._mainWindow)
216 dialog.format_secondary_markup("If the simulator has crashed, restart it "
217 "and restore your flight as much as possible "
218 "to the state it was in before the crash.\n"
219 "Then press <b>Reconnect</b> to reconnect.\n\n"
220 "If you want to cancel the flight, press <b>Cancel</b>.")
221
222 dialog.add_button("_Cancel", 0)
223 dialog.add_button("_Reconnect", 1)
224 dialog.set_default_response(1)
225
226 result = dialog.run()
227 dialog.hide()
228 if result == 1:
229 self.beginBusy("Connecting to the simulator.")
230 self._reconnecting = True
231 self._simulator.reconnect()
232 else:
233 self._connecting = False
234 self._reconnecting = False
235 self._statusbar.updateConnection(self._connecting, self._connected)
236 self._wizard.disconnected()
237
238 def write(self, msg):
239 """Write the given message to the log."""
240 gobject.idle_add(self._writeLog, msg)
241
242 def check(self, flight, aircraft, logger, oldState, state):
243 """Update the data."""
244 gobject.idle_add(self._monitorWindow.setData, state)
245 gobject.idle_add(self._statusbar.updateTime, state.timestamp)
246
247 def resetFlightStatus(self):
248 """Reset the status of the flight."""
249 self._statusbar.resetFlightStatus()
250 self._statusbar.updateTime()
251 self._statusIcon.resetFlightStatus()
252
253 def setStage(self, stage):
254 """Set the stage of the flight."""
255 gobject.idle_add(self._setStage, stage)
256
257 def _setStage(self, stage):
258 """Set the stage of the flight."""
259 self._statusbar.setStage(stage)
260 self._statusIcon.setStage(stage)
261 self._wizard.setStage(stage)
262
263 def setRating(self, rating):
264 """Set the rating of the flight."""
265 gobject.idle_add(self._setRating, rating)
266
267 def _setRating(self, rating):
268 """Set the rating of the flight."""
269 self._statusbar.setRating(rating)
270 self._statusIcon.setRating(rating)
271
272 def setNoGo(self, reason):
273 """Set the rating of the flight to No-Go with the given reason."""
274 gobject.idle_add(self._setNoGo, reason)
275
276 def _setNoGo(self, reason):
277 """Set the rating of the flight."""
278 self._statusbar.setNoGo(reason)
279 self._statusIcon.setNoGo(reason)
280
281 def _handleMainWindowState(self, window, event):
282 """Hande a change in the state of the window"""
283 iconified = gdk.WindowState.ICONIFIED if pygobject \
284 else gdk.WINDOW_STATE_ICONIFIED
285 if (event.changed_mask&iconified)!=0 and (event.new_window_state&iconified)!=0:
286 self.hideMainWindow(savePosition = False)
287
288 def hideMainWindow(self, savePosition = True):
289 """Hide the main window and save its position."""
290 if savePosition:
291 (self._mainWindowX, self._mainWindowY) = \
292 self._mainWindow.get_window().get_root_origin()
293 else:
294 self._mainWindowX = self._mainWindowY = None
295 self._mainWindow.hide()
296 self._statusIcon.mainWindowHidden()
297 return True
298
299 def showMainWindow(self):
300 """Show the main window at its former position."""
301 if self._mainWindowX is not None and self._mainWindowY is not None:
302 self._mainWindow.move(self._mainWindowX, self._mainWindowY)
303
304 self._mainWindow.show()
305 self._mainWindow.deiconify()
306
307 self._statusIcon.mainWindowShown()
308
309 def toggleMainWindow(self):
310 """Toggle the main window."""
311 if self._mainWindow.get_visible():
312 self.hideMainWindow()
313 else:
314 self.showMainWindow()
315
316 def hideMonitorWindow(self, savePosition = True):
317 """Hide the monitor window."""
318 if savePosition:
319 (self._monitorWindowX, self._monitorWindowY) = \
320 self._monitorWindow.get_window().get_root_origin()
321 else:
322 self._monitorWindowX = self._monitorWindowY = None
323 self._monitorWindow.hide()
324 self._statusIcon.monitorWindowHidden()
325 return True
326
327 def showMonitorWindow(self):
328 """Show the monitor window."""
329 if self._monitorWindowX is not None and self._monitorWindowY is not None:
330 self._monitorWindow.move(self._monitorWindowX, self._monitorWindowY)
331 self._monitorWindow.show_all()
332 self._statusIcon.monitorWindowShown()
333
334 def restart(self):
335 """Quit and restart the application."""
336 self.toRestart = True
337 self._quit(force = True)
338
339 def flushStdIO(self):
340 """Flush any text to the standard error that could not be logged."""
341 if self._stdioText:
342 sys.__stderr__.write(self._stdioText)
343
344 def writeStdIO(self, text):
345 """Write the given text into standard I/O log."""
346 with self._stdioLock:
347 self._stdioText += text
348
349 gobject.idle_add(self._writeStdIO)
350
351 def beginBusy(self, message):
352 """Begin a period of background processing."""
353 self._mainWindow.get_window().set_cursor(self._busyCursor)
354 self._statusbar.updateBusyState(message)
355
356 def endBusy(self):
357 """End a period of background processing."""
358 self._mainWindow.get_window().set_cursor(None)
359 self._statusbar.updateBusyState(None)
360
361 def _writeStdIO(self):
362 """Perform the real writing."""
363 with self._stdioLock:
364 text = self._stdioText
365 self._stdioText = ""
366 if not text: return
367
368 lines = text.splitlines()
369 if text[-1]=="\n":
370 text = ""
371 else:
372 text = lines[-1]
373 lines = lines[:-1]
374
375 for line in lines:
376 if self._stdioAfterNewLine:
377 line = "[STDIO] " + line
378 self._writeLog(line + "\n")
379 self._stdioAfterNewLine = True
380
381 if text:
382 if self._stdioAfterNewLine:
383 text = "[STDIO] " + text
384 self._writeLog(text)
385 self._stdioAfterNewLine = False
386
387 def connectSimulator(self, aircraftType):
388 """Connect to the simulator for the first time."""
389 self._logger.reset()
390
391 self._flight = flight.Flight(self._logger, self)
392 self._flight.aircraftType = aircraftType
393 self._flight.aircraft = acft.Aircraft.create(self._flight)
394 self._flight.aircraft._checkers.append(self)
395
396 if self._simulator is None:
397 self._simulator = fs.createSimulator(const.SIM_MSFS9, self)
398
399 self._flight.simulator = self._simulator
400
401 self.beginBusy("Connecting to the simulator...")
402 self._statusbar.updateConnection(self._connecting, self._connected)
403
404 self._connecting = True
405 self._simulator.connect(self._flight.aircraft)
406
407 def startMonitoring(self):
408 """Start monitoring."""
409 self._simulator.startMonitoring()
410 self._monitoring = True
411
412 def stopMonitoring(self):
413 """Stop monitoring."""
414 self._simulator.stoptMonitoring()
415 self._monitoring = False
416
417 def _buildLogFrame(self):
418 """Build the frame for the log."""
419 logFrame = gtk.Frame(label = "Log")
420
421 frameAlignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
422
423 frameAlignment.set_padding(padding_top = 4, padding_bottom = 10,
424 padding_left = 16, padding_right = 16)
425
426 logFrame.add(frameAlignment)
427
428 logScroller = gtk.ScrolledWindow()
429 self._logView = gtk.TextView()
430 self._logView.set_editable(False)
431 logScroller.add(self._logView)
432
433 logBox = gtk.VBox()
434 logBox.pack_start(logScroller, True, True, 0)
435 logBox.set_size_request(-1, 200)
436
437 frameAlignment.add(logBox)
438
439 return logFrame
440
441 def _writeLog(self, msg):
442 """Write the given message to the log."""
443 buffer = self._logView.get_buffer()
444 buffer.insert(buffer.get_end_iter(), msg)
445 self._logView.scroll_mark_onscreen(buffer.get_insert())
446
447 def _quit(self, what = None, force = False):
448 """Quit from the application."""
449 if force:
450 result=RESPONSETYPE_YES
451 else:
452 dialog = gtk.MessageDialog(type = MESSAGETYPE_QUESTION,
453 buttons = BUTTONSTYPE_YES_NO,
454 message_format =
455 "Are you sure to quit the logger?")
456 result = dialog.run()
457 dialog.hide()
458
459 if result==RESPONSETYPE_YES:
460 self._statusIcon.destroy()
461 return gtk.main_quit()
462
463 def _notebookPageSwitch(self, notebook, page, page_num):
464 """Called when the current page of the notebook has changed."""
465 if page_num==0:
466 gobject.idle_add(self._wizard.grabDefault)
467 else:
468 self._mainWindow.set_default(None)
Note: See TracBrowser for help on using the repository browser.