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

Added some basic support for SimBrief (re #279)

File size: 11.5 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
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
91 self._driver = None
92
93 self._toQuit = False
94
95 @property
96 def programDirectory(self):
97 """Get the program directory."""
98 return self._programDirectory
99
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
113 driver = self._driver = webdriver.Chrome(chrome_options = options)
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
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
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
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
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):
175 """Initialize the Chrome Embedded Framework."""
176 global _toQuit, _seleniumHandler
177 _toQuit = False
178
179 gobject.threads_init()
180
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
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
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)
223
224 gobject.timeout_add(10, _handleTimeout)
225
226 print "Initialized, executing callback..."
227 initializedCallback()
228
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
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
340def finalize():
341 """Finalize the Chrome Embedded Framework."""
342 global _toQuit, _seleniumHandler
343 toQuit = True
344 _seleniumHandler.quit()
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.