source: src/mlx/gui/cef.py@ 685:6391018c120b

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

Added some basic support for SimBrief (re #279)

File size: 11.5 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
[682]79class SeleniumHandler(threading.Thread):
80 """Thread to handle Selenium operations."""
81 def __init__(self, programDirectory):
82 """Construct the thread."""
83 threading.Thread.__init__(self)
84 self.daemon = False
85
86 self._programDirectory = programDirectory
87
88 self._commandsCondition = threading.Condition()
89 self._commands = []
90
[685]91 self._driver = None
92
[682]93 self._toQuit = False
94
[685]95 @property
96 def programDirectory(self):
97 """Get the program directory."""
98 return self._programDirectory
99
[682]100 def run(self):
101 """Create the Selenium driver and the perform any operations
102 requested."""
103 scriptName = "mlx_cef_caller"
104 if secondaryInstallation:
105 scriptName += "_secondary"
106 scriptName += ".bat" if os.name=="nt" else ".sh"
107
108 scriptPath = os.path.join(self._programDirectory, scriptName)
109 print "Creating the Selenium driver to call script", scriptPath
110
111 options = Options()
112 options.binary_location = scriptPath
[685]113 driver = self._driver = webdriver.Chrome(chrome_options = options)
[682]114 # try:
115 # except:
116 # traceback.print_exc()
117
118 print "Created Selenium driver."
119 while not self._toQuit:
120 with self._commandsCondition:
121 while not self._commands:
122 self._commandsCondition.wait()
123
124 command = self._commands[0]
125 del self._commands[0]
126
127 command()
128
129 driver.quit()
130
[685]131 def callSimBrief(self, plan, getCredentials, updateProgress,
132 htmlFilePath):
133 """Call SimBrief with the given plan."""
134 self._enqueue(lambda:
135 self._callSimBrief(plan, self._driver,
136 getCredentials, updateProgress,
137 htmlFilePath))
138
[682]139 def quit(self):
140 """Instruct the thread to quit and then join it."""
141 self._enqueue(self._quit)
142 self.join()
143
144 def _enqueue(self, command):
145 """Enqueue the given command.
146
147 command should be a function to be executed in the thread."""
148 with self._commandsCondition:
149 self._commands.append(command)
150 self._commandsCondition.notify()
151
[685]152 def _callSimBrief(self, plan, driver,
153 getCredentials, updateProgress, htmlFilePath):
154 """Perform the SimBrief call."""
155 integrator = MavaSimbriefIntegrator(plan = plan, driver = driver)
156 link = integrator.get_xml_link(getCredentials, updateProgress,
157 local_xml_debug = False,
158 local_html_debug = False)
159
160 updateProgress("Retrieving briefing...", False)
161
162 flight_info = integrator.get_results(link,
163 html_file_path = htmlFilePath)
164
165 updateProgress("Done", True)
166
[682]167 def _quit(self):
168 """Set the _toQuit member variable to indicate that the thread should
169 quit."""
170 self._toQuit = True
171
172#------------------------------------------------------------------------------
173
174def initialize(programDirectory, initializedCallback):
[648]175 """Initialize the Chrome Embedded Framework."""
[682]176 global _toQuit, _seleniumHandler
[648]177 _toQuit = False
178
179 gobject.threads_init()
180
[682]181 argsFilePath = getArgsFilePath()
182 try:
183 os.unlink(argsFilePath)
184 except:
185 pass
186
187 _seleniumHandler = SeleniumHandler(programDirectory)
188 _seleniumHandler.start()
189
190 ArgsFileWaiter(initializedCallback).start()
191
192#------------------------------------------------------------------------------
193
194def _initializeCEF(args, initializedCallback):
195 """Perform the actual initialization of CEF using the given arguments."""
196 print "Initializing CEF with args:", args
197
[648]198 settings = {
199 "debug": True, # cefpython debug messages in console and in log_file
200 "log_severity": cefpython.LOGSEVERITY_VERBOSE, # LOGSEVERITY_VERBOSE
201 "log_file": "", # Set to "" to disable
202 "release_dcheck_enabled": True, # Enable only when debugging
203 # This directories must be set on Linux
204 "locales_dir_path": os.path.join(cefpython.GetModuleDirectory(), "locales"),
205 "resources_dir_path": cefpython.GetModuleDirectory(),
206 "browser_subprocess_path": "%s/%s" % \
207 (cefpython.GetModuleDirectory(), "subprocess"),
208 }
209
[682]210 switches={}
211 for arg in args:
212 if arg.startswith("--"):
213 if arg != "--enable-logging":
214 assignIndex = arg.find("=")
215 if assignIndex<0:
216 switches[arg[2:]] = ""
217 else:
218 switches[arg[2:assignIndex]] = arg[assignIndex+1:]
219 else:
220 print "Unhandled switch", arg
221
222 cefpython.Initialize(settings, switches)
[648]223
224 gobject.timeout_add(10, _handleTimeout)
225
[682]226 print "Initialized, executing callback..."
227 initializedCallback()
228
[648]229#------------------------------------------------------------------------------
230
231def getContainer():
232 """Get a container object suitable for running a browser instance
233 within."""
234 if os.name=="nt":
235 container = gtk.DrawingArea()
236 container.set_property("can-focus", True)
237 container.connect("size-allocate", _handleSizeAllocate)
238 else:
239 container = gtk.VBox(True, 0)
240
241 container.show()
242
243 return container
244
245#------------------------------------------------------------------------------
246
247def startInContainer(container, url, browserSettings = {}):
248 """Start a browser instance in the given container with the given URL."""
249 if os.name=="nt":
250 windowID = container.get_window().handle
251 else:
252 m = re.search("GtkVBox at 0x(\w+)", str(container))
253 hexID = m.group(1)
254 windowID = int(hexID, 16)
255
256 windowInfo = cefpython.WindowInfo()
257 windowInfo.SetAsChild(windowID)
258
259 return cefpython.CreateBrowserSync(windowInfo,
260 browserSettings = browserSettings,
261 navigateUrl = url)
262
263#------------------------------------------------------------------------------
264
[685]265class OffscreenRenderHandler(object):
266 def GetRootScreenRect(self, browser, rect):
267 #print "GetRootScreenRect"
268 rect += [0, 0, 800, 600]
269 return True
270
271 def GetViewRect(self, browser, rect):
272 #print "GetViewRect"
273 rect += [0, 0, 800, 600]
274 return True
275
276 def GetScreenPoint(self, browser, viewX, viewY, screenCoordinates):
277 #print "GetScreenPoint", viewX, viewY
278 rect += [viewX, viewY]
279 return True
280
281 def GetScreenInfo(self, browser, screenInfo):
282 #print "GetScreenInfo"
283 pass
284
285 def OnPopupShow(self, browser, show):
286 #print "OnPopupShow", show
287 pass
288
289 def OnPopupSize(self, browser, rect):
290 #print "OnPopupSize", rect
291 pass
292
293 def OnPaint(self, browser, paintElementType, dirtyRects, buffer, width, height):
294 #print "OnPaint", paintElementType, dirtyRects, buffer, width, height
295 pass
296
297 def OnCursorChange(self, browser, cursor):
298 #print "OnCursorChange", cursor
299 pass
300
301 def OnScrollOffsetChanged(self, browser):
302 #print "OnScrollOffsetChange"
303 pass
304
305 def OnBeforePopup(self, browser, frame, targetURL, targetFrameName,
306 popupFeatures, windowInfo, client, browserSettings,
307 noJavascriptAccess):
308 wInfo = cefpython.WindowInfo()
309 wInfo.SetAsOffscreen(int(0))
310
311 windowInfo.append(wInfo)
312
313 return False
314
315#------------------------------------------------------------------------------
316
317def initializeSimBrief():
318 """Initialize the (hidden) browser window for SimBrief."""
319 windowInfo = cefpython.WindowInfo()
320 windowInfo.SetAsOffscreen(int(0))
321
322 url = "file://" + os.path.join(_seleniumHandler.programDirectory,
323 "simbrief.html")
324 simBriefBrowser = cefpython.CreateBrowserSync(windowInfo,
325 browserSettings = {},
326 navigateUrl = url)
327 simBriefBrowser.SetClientHandler(OffscreenRenderHandler())
328
329 simBriefBrowser.SetFocus(True)
330
331#------------------------------------------------------------------------------
332
333def callSimBrief(plan, getCredentials, updateProgress, htmlFilePath):
334 """Call SimBrief with the given plan."""
335 _seleniumHandler.callSimBrief(plan, getCredentials,
336 updateProgress, htmlFilePath)
337
338#------------------------------------------------------------------------------
339
[648]340def finalize():
341 """Finalize the Chrome Embedded Framework."""
[682]342 global _toQuit, _seleniumHandler
[648]343 toQuit = True
[682]344 _seleniumHandler.quit()
[648]345 cefpython.Shutdown()
346
347#------------------------------------------------------------------------------
348
349def _handleTimeout():
350 """Handle the timeout by running the CEF message loop."""
351 if _toQuit:
352 return False
353 else:
354 cefpython.MessageLoopWork()
355 return True
356
357#------------------------------------------------------------------------------
358
359def _handleSizeAllocate(widget, sizeAlloc):
360 """Handle the size-allocate event."""
361 cefpython.WindowUtils.OnSize(widget.get_window().handle, 0, 0, 0)
Note: See TracBrowser for help on using the repository browser.