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
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._toQuit = False
113
114 @property
115 def programDirectory(self):
116 """Get the program directory."""
117 return self._programDirectory
118
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
132 driver = self._driver = webdriver.Chrome(chrome_options = options)
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
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
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
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
179 if link is not None:
180 updateProgress(SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING,
181 SIMBRIEF_RESULT_NONE, None)
182
183 try:
184 flight_info = integrator.get_results(link,
185 html_file_path =
186 htmlFilePath)
187
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)
194
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):
203 """Initialize the Chrome Embedded Framework."""
204 global _toQuit, _seleniumHandler
205 _toQuit = False
206
207 gobject.threads_init()
208
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
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
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)
251
252 gobject.timeout_add(10, _handleTimeout)
253
254 print "Initialized, executing callback..."
255 initializedCallback()
256
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
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
368def finalize():
369 """Finalize the Chrome Embedded Framework."""
370 global _toQuit, _seleniumHandler
371 toQuit = True
372 _seleniumHandler.quit()
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.