source: src/mlx/gui/cef.py@ 703:df10e569b029

cef
Last change on this file since 703:df10e569b029 was 703:df10e569b029, checked in by István Váradi <ivaradi@…>, 9 years ago

The getting of the XML link is also protected by an exception handler (re #279).

File size: 13.7 KB
RevLine 
[648]1from common import *
2
[685]3from mava_simbrief import MavaSimbriefIntegrator
4
[682]5from mlx.util import secondaryInstallation
6
7from cefpython3 import cefpython
8from selenium import webdriver
9from selenium.webdriver.chrome.options import Options
10
[648]11import platform
12import json
[682]13import time
[648]14import os
15import re
[682]16import threading
17import tempfile
18import traceback
[648]19
20#------------------------------------------------------------------------------
21
22## @package mlx.gui.cef
23#
24# Some helper stuff related to the Chrome Embedded Framework
25
26#------------------------------------------------------------------------------
27
28# Indicate if we should quit
29_toQuit = False
30
[682]31# The Selenium thread
32_seleniumHandler = None
33
34#------------------------------------------------------------------------------
35
36def getArgsFilePath():
37 """Get the path of the argument file."""
38 if os.name=="nt":
39 return os.path.join(tempfile.gettempdir(),
40 "mlxcef.args" +
41 (".secondary" if secondaryInstallation else ""))
42 else:
43 import pwd
44 return os.path.join(tempfile.gettempdir(),
45 "mlxcef." + pwd.getpwuid(os.getuid())[0] + ".args" +
46 (".secondary" if secondaryInstallation else ""))
47
48#------------------------------------------------------------------------------
49
50class ArgsFileWaiter(threading.Thread):
51 """A thread to wait for the appearance of the arguments file."""
52 def __init__(self, initializedCallback):
53 """Construct the thread."""
54 threading.Thread.__init__(self)
55 self.daemon = True
56
57 self._initializedCallback = initializedCallback
58
59 def run(self):
60 """Repeatedly check for the existence of the arguments file.
61
62 If it is found, read it, extract the arguments and insert a job into
63 the GUI loop to perform the actual initialization of CEF."""
64 argsFilePath = getArgsFilePath()
65 print "Waiting for the arguments file '%s' to appear" % (argsFilePath,)
66
67 while not os.path.exists(argsFilePath):
68 time.sleep(0.1)
69
70 print "Got arguments, reading them."""
71
72 with open(argsFilePath, "rt") as f:
73 args = f.read().split()
74
75 gobject.idle_add(_initializeCEF, args, self._initializedCallback)
76
[648]77#------------------------------------------------------------------------------
78
[692]79SIMBRIEF_PROGRESS_SEARCHING_BROWSER = MavaSimbriefIntegrator.PROGRESS_SEARCHING_BROWSER
80SIMBRIEF_PROGRESS_LOADING_FORM = MavaSimbriefIntegrator.PROGRESS_LOADING_FORM
81SIMBRIEF_PROGRESS_FILLING_FORM = MavaSimbriefIntegrator.PROGRESS_FILLING_FORM
82SIMBRIEF_PROGRESS_WAITING_LOGIN = MavaSimbriefIntegrator.PROGRESS_WAITING_LOGIN
83SIMBRIEF_PROGRESS_LOGGING_IN = MavaSimbriefIntegrator.PROGRESS_LOGGING_IN
84SIMBRIEF_PROGRESS_WAITING_RESULT = MavaSimbriefIntegrator.PROGRESS_WAITING_RESULT
85
86SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING = MavaSimbriefIntegrator.PROGRESS_MAX + 1
87SIMBRIEF_PROGRESS_DONE = 1000
88
89SIMBRIEF_RESULT_NONE = MavaSimbriefIntegrator.RESULT_NONE
90SIMBRIEF_RESULT_OK = MavaSimbriefIntegrator.RESULT_OK
91SIMBRIEF_RESULT_ERROR_OTHER = MavaSimbriefIntegrator.RESULT_ERROR_OTHER
92SIMBRIEF_RESULT_ERROR_NO_FORM = MavaSimbriefIntegrator.RESULT_ERROR_NO_FORM
93SIMBRIEF_RESULT_ERROR_NO_POPUP = MavaSimbriefIntegrator.RESULT_ERROR_NO_POPUP
94SIMBRIEF_RESULT_ERROR_LOGIN_FAILED = MavaSimbriefIntegrator.RESULT_ERROR_LOGIN_FAILED
95
96#------------------------------------------------------------------------------
97
[682]98class SeleniumHandler(threading.Thread):
99 """Thread to handle Selenium operations."""
100 def __init__(self, programDirectory):
101 """Construct the thread."""
102 threading.Thread.__init__(self)
103 self.daemon = False
104
105 self._programDirectory = programDirectory
106
107 self._commandsCondition = threading.Condition()
108 self._commands = []
109
[685]110 self._driver = None
111
[695]112 self._simBriefBrowser = None
113
[682]114 self._toQuit = False
115
[685]116 @property
117 def programDirectory(self):
118 """Get the program directory."""
119 return self._programDirectory
120
[695]121 @property
122 def simBriefInitURL(self):
123 """Get the initial URL for the SimBrief browser."""
124 return "file://" + os.path.join(self.programDirectory, "simbrief.html")
125
[682]126 def run(self):
127 """Create the Selenium driver and the perform any operations
128 requested."""
129 scriptName = "mlx_cef_caller"
130 if secondaryInstallation:
131 scriptName += "_secondary"
132 scriptName += ".bat" if os.name=="nt" else ".sh"
133
134 scriptPath = os.path.join(self._programDirectory, scriptName)
135 print "Creating the Selenium driver to call script", scriptPath
136
137 options = Options()
138 options.binary_location = scriptPath
[685]139 driver = self._driver = webdriver.Chrome(chrome_options = options)
[682]140 # try:
141 # except:
142 # traceback.print_exc()
143
144 print "Created Selenium driver."
145 while not self._toQuit:
146 with self._commandsCondition:
147 while not self._commands:
148 self._commandsCondition.wait()
149
150 command = self._commands[0]
151 del self._commands[0]
152
153 command()
154
155 driver.quit()
156
[695]157 def initializeSimBrief(self):
158 """Create and initialize the browser used for Simbrief."""
159 windowInfo = cefpython.WindowInfo()
160 windowInfo.SetAsOffscreen(int(0))
161
162 url = self.simBriefInitURL
163 self._simBriefBrowser = \
164 cefpython.CreateBrowserSync(windowInfo, browserSettings = {},
165 navigateUrl = self.simBriefInitURL)
166 self._simBriefBrowser.SetClientHandler(OffscreenRenderHandler())
167 self._simBriefBrowser.SetFocus(True)
168
[685]169 def callSimBrief(self, plan, getCredentials, updateProgress,
170 htmlFilePath):
171 """Call SimBrief with the given plan."""
172 self._enqueue(lambda:
173 self._callSimBrief(plan, self._driver,
174 getCredentials, updateProgress,
175 htmlFilePath))
176
[682]177 def quit(self):
178 """Instruct the thread to quit and then join it."""
179 self._enqueue(self._quit)
180 self.join()
181
182 def _enqueue(self, command):
183 """Enqueue the given command.
184
185 command should be a function to be executed in the thread."""
186 with self._commandsCondition:
187 self._commands.append(command)
188 self._commandsCondition.notify()
189
[685]190 def _callSimBrief(self, plan, driver,
191 getCredentials, updateProgress, htmlFilePath):
192 """Perform the SimBrief call."""
[696]193 self._simBriefBrowser.LoadUrl(self.simBriefInitURL)
194
[685]195 integrator = MavaSimbriefIntegrator(plan = plan, driver = driver)
[703]196 link = None
197 try:
198 link = integrator.get_xml_link(getCredentials, updateProgress,
199 local_xml_debug = False,
200 local_html_debug = False)
201 except Exception, e:
202 print "Failed to initiate the generation of the briefing:", e
203 updateProgress(SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING,
204 SIMBRIEF_RESULT_ERROR_OTHER, None)
[685]205
[692]206 if link is not None:
207 updateProgress(SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING,
208 SIMBRIEF_RESULT_NONE, None)
[685]209
[692]210 try:
211 flight_info = integrator.get_results(link,
212 html_file_path =
213 htmlFilePath)
[685]214
[692]215 updateProgress(SIMBRIEF_PROGRESS_DONE,
216 SIMBRIEF_RESULT_OK, flight_info)
217 except Exception, e:
[697]218 print "Failed to retrieve the briefing:", e
[692]219 updateProgress(SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING,
220 SIMBRIEF_RESULT_ERROR_OTHER, None)
[685]221
[682]222 def _quit(self):
223 """Set the _toQuit member variable to indicate that the thread should
224 quit."""
225 self._toQuit = True
226
227#------------------------------------------------------------------------------
228
229def initialize(programDirectory, initializedCallback):
[648]230 """Initialize the Chrome Embedded Framework."""
[682]231 global _toQuit, _seleniumHandler
[648]232 _toQuit = False
233
234 gobject.threads_init()
235
[682]236 argsFilePath = getArgsFilePath()
237 try:
238 os.unlink(argsFilePath)
239 except:
240 pass
241
242 _seleniumHandler = SeleniumHandler(programDirectory)
243 _seleniumHandler.start()
244
245 ArgsFileWaiter(initializedCallback).start()
246
247#------------------------------------------------------------------------------
248
249def _initializeCEF(args, initializedCallback):
250 """Perform the actual initialization of CEF using the given arguments."""
251 print "Initializing CEF with args:", args
252
[648]253 settings = {
254 "debug": True, # cefpython debug messages in console and in log_file
255 "log_severity": cefpython.LOGSEVERITY_VERBOSE, # LOGSEVERITY_VERBOSE
256 "log_file": "", # Set to "" to disable
257 "release_dcheck_enabled": True, # Enable only when debugging
258 # This directories must be set on Linux
259 "locales_dir_path": os.path.join(cefpython.GetModuleDirectory(), "locales"),
260 "resources_dir_path": cefpython.GetModuleDirectory(),
261 "browser_subprocess_path": "%s/%s" % \
262 (cefpython.GetModuleDirectory(), "subprocess"),
263 }
264
[682]265 switches={}
266 for arg in args:
267 if arg.startswith("--"):
268 if arg != "--enable-logging":
269 assignIndex = arg.find("=")
270 if assignIndex<0:
271 switches[arg[2:]] = ""
272 else:
273 switches[arg[2:assignIndex]] = arg[assignIndex+1:]
274 else:
275 print "Unhandled switch", arg
276
277 cefpython.Initialize(settings, switches)
[648]278
279 gobject.timeout_add(10, _handleTimeout)
280
[682]281 print "Initialized, executing callback..."
282 initializedCallback()
283
[648]284#------------------------------------------------------------------------------
285
286def getContainer():
287 """Get a container object suitable for running a browser instance
288 within."""
289 if os.name=="nt":
290 container = gtk.DrawingArea()
291 container.set_property("can-focus", True)
292 container.connect("size-allocate", _handleSizeAllocate)
293 else:
294 container = gtk.VBox(True, 0)
295
296 container.show()
297
298 return container
299
300#------------------------------------------------------------------------------
301
302def startInContainer(container, url, browserSettings = {}):
303 """Start a browser instance in the given container with the given URL."""
304 if os.name=="nt":
305 windowID = container.get_window().handle
306 else:
307 m = re.search("GtkVBox at 0x(\w+)", str(container))
308 hexID = m.group(1)
309 windowID = int(hexID, 16)
310
311 windowInfo = cefpython.WindowInfo()
312 windowInfo.SetAsChild(windowID)
313
314 return cefpython.CreateBrowserSync(windowInfo,
315 browserSettings = browserSettings,
316 navigateUrl = url)
317
318#------------------------------------------------------------------------------
319
[685]320class OffscreenRenderHandler(object):
321 def GetRootScreenRect(self, browser, rect):
322 #print "GetRootScreenRect"
323 rect += [0, 0, 800, 600]
324 return True
325
326 def GetViewRect(self, browser, rect):
327 #print "GetViewRect"
328 rect += [0, 0, 800, 600]
329 return True
330
331 def GetScreenPoint(self, browser, viewX, viewY, screenCoordinates):
332 #print "GetScreenPoint", viewX, viewY
333 rect += [viewX, viewY]
334 return True
335
336 def GetScreenInfo(self, browser, screenInfo):
337 #print "GetScreenInfo"
338 pass
339
340 def OnPopupShow(self, browser, show):
341 #print "OnPopupShow", show
342 pass
343
344 def OnPopupSize(self, browser, rect):
345 #print "OnPopupSize", rect
346 pass
347
348 def OnPaint(self, browser, paintElementType, dirtyRects, buffer, width, height):
349 #print "OnPaint", paintElementType, dirtyRects, buffer, width, height
350 pass
351
352 def OnCursorChange(self, browser, cursor):
353 #print "OnCursorChange", cursor
354 pass
355
356 def OnScrollOffsetChanged(self, browser):
357 #print "OnScrollOffsetChange"
358 pass
359
360 def OnBeforePopup(self, browser, frame, targetURL, targetFrameName,
361 popupFeatures, windowInfo, client, browserSettings,
362 noJavascriptAccess):
363 wInfo = cefpython.WindowInfo()
364 wInfo.SetAsOffscreen(int(0))
365
366 windowInfo.append(wInfo)
367
368 return False
369
370#------------------------------------------------------------------------------
371
372def initializeSimBrief():
373 """Initialize the (hidden) browser window for SimBrief."""
[695]374 _seleniumHandler.initializeSimBrief()
[685]375
376#------------------------------------------------------------------------------
377
378def callSimBrief(plan, getCredentials, updateProgress, htmlFilePath):
379 """Call SimBrief with the given plan."""
380 _seleniumHandler.callSimBrief(plan, getCredentials,
381 updateProgress, htmlFilePath)
382
383#------------------------------------------------------------------------------
384
[648]385def finalize():
386 """Finalize the Chrome Embedded Framework."""
[682]387 global _toQuit, _seleniumHandler
[648]388 toQuit = True
[702]389 cefpython.Shutdown()
[682]390 _seleniumHandler.quit()
[648]391
392#------------------------------------------------------------------------------
393
394def _handleTimeout():
395 """Handle the timeout by running the CEF message loop."""
396 if _toQuit:
397 return False
398 else:
399 cefpython.MessageLoopWork()
400 return True
401
402#------------------------------------------------------------------------------
403
404def _handleSizeAllocate(widget, sizeAlloc):
405 """Handle the size-allocate event."""
406 cefpython.WindowUtils.OnSize(widget.get_window().handle, 0, 0, 0)
Note: See TracBrowser for help on using the repository browser.