Chromium Embedded Framework

CEF and CEFPython are new BSD license libraries for render to texture chromium. The most simple implementation renders webpages into panda3d textures. Using javascript and callbacks CEF can be used to create powerful UI tools.

Installation: I’ve only tested this with panda3d cvs for 1.9 on Arch Linux 64. From CEFPython click on the Download CEF3 link for your appropriate OS. I installed via disutils.


Basic script. Gist with additional demos (3 as of this post)

from cefpython3 import cefpython

import os
import sys


from panda3d.core import loadPrcFileData
loadPrcFileData("", "Panda3D example")
loadPrcFileData("", "fullscreen 0")
loadPrcFileData("", "win-size 1024 768")

import direct.directbase.DirectStart
from panda3d.core import CardMaker, Texture


settings = {
        "log_severity": cefpython.LOGSEVERITY_INFO, # LOGSEVERITY_VERBOSE
        #"log_file": GetApplicationPath("debug.log"), # Set to "" to disable.
        "release_dcheck_enabled": True, # Enable only when debugging.
        # This directories must be set on Linux
        "locales_dir_path": cefpython.GetModuleDirectory()+"/locales",
        "resources_dir_path": cefpython.GetModuleDirectory(),
        "browser_subprocess_path": "%s/%s" % (
            cefpython.GetModuleDirectory(), "subprocess")
    }
	
	
class ClientHandler:
    """A client handler is required for the browser to do built in callbacks back into the application."""
    browser = None
    texture = None

    def __init__(self, browser, texture):
        self.browser = browser
        self.texture = texture

    def OnPaint(self, browser, paintElementType, dirtyRects, buffer, width, height):
        img = self.texture.modifyRamImage()
        if paintElementType == cefpython.PET_POPUP:
            print("width=%s, height=%s" % (width, height))
        elif paintElementType == cefpython.PET_VIEW:
            img.setData(buffer.GetString(mode="rgba", origin="bottom-left"))
        else:
            raise Exception("Unknown paintElementType: %s" % paintElementType)

    def GetViewRect(self, browser, rect):
        width  = self.texture.getXSize()
        height = self.texture.getYSize()
        rect.append(0)
        rect.append(0)
        rect.append(width)
        rect.append(height)
        return True

    def GetScreenPoint(self, browser, viewX, viewY, screenCoordinates):
        #return False
        print("GetScreenPoint()")
        return False

    def OnLoadEnd(self, browser, frame, httpStatusCode):
        return
        self._saveImage()
    
    def OnLoadError(self, browser, frame, errorCode, errorText, failedURL):
        print("load error", browser, frame, errorCode, errorText, failedURL)


'''def setBrowserSize(window=None):
    """Use something like this for a full screen UI. Texture and browser size should equal window size. Also remember to set panda textures to ignore power of 2"""
      width = int(round(base.win.getXSize() * 0.75))
      height = int(round(base.win.getYSize() * 0.75))
      texture.setXSize(width)
      texture.setYSize(height)
      browser.WasResized()
      #browser.SetSize(cefpython.PET_VIEW, width, height)'''

def messageLoop(task):
    cefpython.MessageLoopWork()
    return task.cont


cefpython.g_debug = True
cefpython.Initialize(settings)

texture = Texture()
texture.setXSize(1024)
texture.setYSize(1024)
texture.setCompression(Texture.CMOff)
texture.setComponentType(Texture.TUnsignedByte)
texture.setFormat(Texture.FRgba4)

cardMaker = CardMaker("browser2d")
cardMaker.setFrame(-0.75, 0.75, -0.75, 0.75)
node = cardMaker.generate()
#For UI attach to render2d
nodePath = render.attachNewNode(node)
nodePath.setTexture(texture)

windowHandle = base.win.getWindowHandle().getIntHandle()
windowInfo = cefpython.WindowInfo()

#You can pass 0 to parentWindowHandle, but then some things like context menus and plugins may not display correctly. 
windowInfo.SetAsOffscreen(windowHandle)
#windowInfo.SetAsOffscreen(0)
# By default window rendering is 30 fps, let's change
# it to 60 for better user experience when scrolling.
browserSettings = {}

# Using non about:blank in constructor results in error before render handler callback is set.
# Either set it before/during construction, or set it after then call LoadURL after it is set.
browser = cefpython.CreateBrowserSync(
                windowInfo, browserSettings,
                navigateUrl="http://www.panda3d.org")
browser.SendFocusEvent(True)
browser.SetClientHandler(ClientHandler(browser, texture))

def on_load_end(*args, **kwargs): return  # Example of one kind of call back

browser.SetClientCallback("OnLoadEnd", on_load_end)
browser.WasResized()

'''    IMPORTANT: there is a bug in CEF 3 that causes js bindings to be removed when LoadUrl() is called (see http://www.magpcss.org/ceforum/viewtopic.php?f=6&t=11009). A temporary fix to this bug is to do the navigation through javascript by calling: GetMainFrame().ExecuteJavascript('window.location="http://google.com/"'). '''
#browser.GetMainFrame().ExecuteJavascript('window.location="http://www.panda3d.org"')
#browser.GetMainFrame().LoadUrl("http://wwww.panda3d.org")
#base.accept("window-event", setBrowserSize)
taskMgr.add(messageLoop, "CefMessageLoop")

run()
cefpython.Shutdown()

I got this:

Traceback (most recent call last):
  File "cef_test.py", line 117, in <module>
    browser.SetClientCallback("OnLoadEnd", OnLoadEnd)
NameError: name 'OnLoadEnd' is not defined

Commenting out that line got the program running, but an error message pops up when the panda window gets closed ('The Instruction at 0x####### referenced memory at 0x#######. The memory could not be ‘read’). This is on windows[size=25]xp[/size]

Can’t investigate more at this time, but it looks promising, keep it up :wink:

Fixed.

Amazing work !

Added to the gist with an example of passing variables from python to javascript and calling javascript functions from python.

Added another example to the gist which parents the browser to render2d and adds transparency.

hardware-accelerated off-screen rendering is in works yay magpcss.org/ceforum/viewtopic.php?f=8&t=11635

Added another sample for passing clicks to the browser vs “through” to the 3d scene.