source: src/mlx/gui/cef.py@ 695:c33fc35b99a1

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

Moved the initialization of the SimBrief browser into the Selenium handler (re #279).

File size: 13.4 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."""
193 integrator = MavaSimbriefIntegrator(plan = plan, driver = driver)
194 link = integrator.get_xml_link(getCredentials, updateProgress,
195 local_xml_debug = False,
196 local_html_debug = False)
197
[692]198 if link is not None:
199 updateProgress(SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING,
200 SIMBRIEF_RESULT_NONE, None)
[685]201
[692]202 try:
203 flight_info = integrator.get_results(link,
204 html_file_path =
205 htmlFilePath)
[685]206
[692]207 updateProgress(SIMBRIEF_PROGRESS_DONE,
208 SIMBRIEF_RESULT_OK, flight_info)
209 except Exception, e:
210 print "Failed retrieving the briefing:", e
211 updateProgress(SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING,
212 SIMBRIEF_RESULT_ERROR_OTHER, None)
[685]213
[682]214 def _quit(self):
215 """Set the _toQuit member variable to indicate that the thread should
216 quit."""
217 self._toQuit = True
218
219#------------------------------------------------------------------------------
220
221def initialize(programDirectory, initializedCallback):
[648]222 """Initialize the Chrome Embedded Framework."""
[682]223 global _toQuit, _seleniumHandler
[648]224 _toQuit = False
225
226 gobject.threads_init()
227
[682]228 argsFilePath = getArgsFilePath()
229 try:
230 os.unlink(argsFilePath)
231 except:
232 pass
233
234 _seleniumHandler = SeleniumHandler(programDirectory)
235 _seleniumHandler.start()
236
237 ArgsFileWaiter(initializedCallback).start()
238
239#------------------------------------------------------------------------------
240
241def _initializeCEF(args, initializedCallback):
242 """Perform the actual initialization of CEF using the given arguments."""
243 print "Initializing CEF with args:", args
244
[648]245 settings = {
246 "debug": True, # cefpython debug messages in console and in log_file
247 "log_severity": cefpython.LOGSEVERITY_VERBOSE, # LOGSEVERITY_VERBOSE
248 "log_file": "", # Set to "" to disable
249 "release_dcheck_enabled": True, # Enable only when debugging
250 # This directories must be set on Linux
251 "locales_dir_path": os.path.join(cefpython.GetModuleDirectory(), "locales"),
252 "resources_dir_path": cefpython.GetModuleDirectory(),
253 "browser_subprocess_path": "%s/%s" % \
254 (cefpython.GetModuleDirectory(), "subprocess"),
255 }
256
[682]257 switches={}
258 for arg in args:
259 if arg.startswith("--"):
260 if arg != "--enable-logging":
261 assignIndex = arg.find("=")
262 if assignIndex<0:
263 switches[arg[2:]] = ""
264 else:
265 switches[arg[2:assignIndex]] = arg[assignIndex+1:]
266 else:
267 print "Unhandled switch", arg
268
269 cefpython.Initialize(settings, switches)
[648]270
271 gobject.timeout_add(10, _handleTimeout)
272
[682]273 print "Initialized, executing callback..."
274 initializedCallback()
275
[648]276#------------------------------------------------------------------------------
277
278def getContainer():
279 """Get a container object suitable for running a browser instance
280 within."""
281 if os.name=="nt":
282 container = gtk.DrawingArea()
283 container.set_property("can-focus", True)
284 container.connect("size-allocate", _handleSizeAllocate)
285 else:
286 container = gtk.VBox(True, 0)
287
288 container.show()
289
290 return container
291
292#------------------------------------------------------------------------------
293
294def startInContainer(container, url, browserSettings = {}):
295 """Start a browser instance in the given container with the given URL."""
296 if os.name=="nt":
297 windowID = container.get_window().handle
298 else:
299 m = re.search("GtkVBox at 0x(\w+)", str(container))
300 hexID = m.group(1)
301 windowID = int(hexID, 16)
302
303 windowInfo = cefpython.WindowInfo()
304 windowInfo.SetAsChild(windowID)
305
306 return cefpython.CreateBrowserSync(windowInfo,
307 browserSettings = browserSettings,
308 navigateUrl = url)
309
310#------------------------------------------------------------------------------
311
[685]312class OffscreenRenderHandler(object):
313 def GetRootScreenRect(self, browser, rect):
314 #print "GetRootScreenRect"
315 rect += [0, 0, 800, 600]
316 return True
317
318 def GetViewRect(self, browser, rect):
319 #print "GetViewRect"
320 rect += [0, 0, 800, 600]
321 return True
322
323 def GetScreenPoint(self, browser, viewX, viewY, screenCoordinates):
324 #print "GetScreenPoint", viewX, viewY
325 rect += [viewX, viewY]
326 return True
327
328 def GetScreenInfo(self, browser, screenInfo):
329 #print "GetScreenInfo"
330 pass
331
332 def OnPopupShow(self, browser, show):
333 #print "OnPopupShow", show
334 pass
335
336 def OnPopupSize(self, browser, rect):
337 #print "OnPopupSize", rect
338 pass
339
340 def OnPaint(self, browser, paintElementType, dirtyRects, buffer, width, height):
341 #print "OnPaint", paintElementType, dirtyRects, buffer, width, height
342 pass
343
344 def OnCursorChange(self, browser, cursor):
345 #print "OnCursorChange", cursor
346 pass
347
348 def OnScrollOffsetChanged(self, browser):
349 #print "OnScrollOffsetChange"
350 pass
351
352 def OnBeforePopup(self, browser, frame, targetURL, targetFrameName,
353 popupFeatures, windowInfo, client, browserSettings,
354 noJavascriptAccess):
355 wInfo = cefpython.WindowInfo()
356 wInfo.SetAsOffscreen(int(0))
357
358 windowInfo.append(wInfo)
359
360 return False
361
362#------------------------------------------------------------------------------
363
364def initializeSimBrief():
365 """Initialize the (hidden) browser window for SimBrief."""
[695]366 _seleniumHandler.initializeSimBrief()
[685]367
368#------------------------------------------------------------------------------
369
370def callSimBrief(plan, getCredentials, updateProgress, htmlFilePath):
371 """Call SimBrief with the given plan."""
372 _seleniumHandler.callSimBrief(plan, getCredentials,
373 updateProgress, htmlFilePath)
374
375#------------------------------------------------------------------------------
376
[648]377def finalize():
378 """Finalize the Chrome Embedded Framework."""
[682]379 global _toQuit, _seleniumHandler
[648]380 toQuit = True
[682]381 _seleniumHandler.quit()
[648]382 cefpython.Shutdown()
383
384#------------------------------------------------------------------------------
385
386def _handleTimeout():
387 """Handle the timeout by running the CEF message loop."""
388 if _toQuit:
389 return False
390 else:
391 cefpython.MessageLoopWork()
392 return True
393
394#------------------------------------------------------------------------------
395
396def _handleSizeAllocate(widget, sizeAlloc):
397 """Handle the size-allocate event."""
398 cefpython.WindowUtils.OnSize(widget.get_window().handle, 0, 0, 0)
Note: See TracBrowser for help on using the repository browser.