source: src/mlx/gui/cef.py@ 692:eea9d6135944

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

Progress reporting is more detailed and uses translatable strings (re #279)

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