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@…>, 7 years ago

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

File size: 13.4 KB
Line 
1from common import *
2
3from mava_simbrief import MavaSimbriefIntegrator
4
5from mlx.util import secondaryInstallation
6
7from cefpython3 import cefpython
8from selenium import webdriver
9from selenium.webdriver.chrome.options import Options
10
11import platform
12import json
13import time
14import os
15import re
16import threading
17import tempfile
18import traceback
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
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
77#------------------------------------------------------------------------------
78
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
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
110 self._driver = None
111
112 self._simBriefBrowser = None
113
114 self._toQuit = False
115
116 @property
117 def programDirectory(self):
118 """Get the program directory."""
119 return self._programDirectory
120
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
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
139 driver = self._driver = webdriver.Chrome(chrome_options = options)
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
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
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
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
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
198 if link is not None:
199 updateProgress(SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING,
200 SIMBRIEF_RESULT_NONE, None)
201
202 try:
203 flight_info = integrator.get_results(link,
204 html_file_path =
205 htmlFilePath)
206
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)
213
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):
222 """Initialize the Chrome Embedded Framework."""
223 global _toQuit, _seleniumHandler
224 _toQuit = False
225
226 gobject.threads_init()
227
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
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
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)
270
271 gobject.timeout_add(10, _handleTimeout)
272
273 print "Initialized, executing callback..."
274 initializedCallback()
275
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
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."""
366 _seleniumHandler.initializeSimBrief()
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
377def finalize():
378 """Finalize the Chrome Embedded Framework."""
379 global _toQuit, _seleniumHandler
380 toQuit = True
381 _seleniumHandler.quit()
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.