Skip to content

Commit 0a1154e

Browse files
committed
Added an option to use Pygame to save browser view as an image.
Making cefpython work with python packaging required removing GetRealPath() & ExceptHook() functions from cefpython module, they are now defined in examples, GetRealPath() renamed to GetApplicationPath(), encodeUrl param was removed, the local url is now encoded automatically when calling CreateBrowserSync() or Frame.LoadUrl(). Fixed naming inconsistencies in API. Updated cefpython.CreateBrowserSync(), third param `navigateURL` changed name to `navigateUrl`. Updated cefpython.GetJavascriptStackTrace(), scriptSourceURL frame key renamed to scriptSourceUrl. Updated examples to conform to API changes. Added LICENSE file. Added scripts to create package installer using Inno Setup.
1 parent c92b13b commit 0a1154e

26 files changed

Lines changed: 941 additions & 237 deletions

cefpython/!linux_todo.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
- przeniesc IsWindowHandle z utils do window_utils_win,
2+
zeby wspierac rozne biblioteki GUI, kazda aplikacja
3+
dostarczy wlasna klase WindowUtils poprzez nadpisanie
4+
cefpython.WindowUtils własną klasą.
5+
- dostosowac string_utils pod linuxa, uzyc odpoowiednikow
6+
MultibyteToWideChar i WideCharToMultibyte.

cefpython/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,6 @@ Release*/
2323
*.pdb
2424

2525
ctags
26+
27+
/cef1/windows/installer/Output/
28+
/cef3/windows/installer/Output/
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
Copyright (c) 2012 Czarek Tomczak. Portions Copyright (c)
2+
2008-2012 Marshall A.Greenblatt, 2006-2009 Google Inc.
3+
All rights reserved.
4+
5+
Redistribution and use in source and binary forms, with
6+
or without modification, are permitted provided that the
7+
following conditions are met:
8+
9+
* Redistributions of source code must retain the above
10+
copyright notice, this list of conditions and the
11+
following disclaimer.
12+
13+
* Redistributions in binary form must reproduce the above
14+
copyright notice, this list of conditions and the
15+
following disclaimer in the documentation and/or other
16+
materials provided with the distribution.
17+
18+
* Neither the name of Google Inc. nor the name Chromium
19+
Embedded Framework nor the name of CEF Python nor the
20+
names of its contributors may be used to endorse or
21+
promote products derived from this software without
22+
specific prior written permission.
23+
24+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
25+
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
26+
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
27+
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
29+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31+
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
35+
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

cefpython/cef1/windows/binaries/README.txt

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,6 @@
11
Chromium Embedded Framework (CEF) Binary Distribution
22
-------------------------------------------------------------------------------
33

4-
Date: $DATE$
5-
6-
CEF Version: $CEF_VER$
7-
CEF URL: $CEF_URL$@$CEF_REV$
8-
9-
Chromium Verison: $CHROMIUM_VER$
10-
Chromium URL: $CHROMIUM_URL$@$CHROMIUM_REV$
11-
12-
134
This distribution contains all components necessary to build and distribute an
145
application using CEF. Please see the LICENSING section of this document for
156
licensing terms and conditions.
@@ -109,4 +100,4 @@ this binary distribution for licensing terms and conditions. Other software
109100
included in this distribution is provided under other licenses. Please visit the
110101
below link for complete Chromium and third-party licensing information.
111102

112-
http://code.google.com/chromium/terms.html
103+
http://code.google.com/chromium/terms.html

cefpython/cef1/windows/binaries/cefadvanced.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@ <h3>Browser view as an image</h3>
124124
python.SaveImage('!cefadvanced.png', 'PNG')</a><br>
125125
(you need to have installed the PIL extension).
126126

127+
<br><br>
128+
TODO: capture an image of all scrollable content on a webpage,
129+
detect the height of content in javascript and call
130+
Browser.SetSize() using that height.
131+
127132
<h3>Test unicode string</h3>
128133

129134
<a href="javascript:alert(python.GetUnicodeString())">

cefpython/cef1/windows/binaries/cefadvanced.py

Lines changed: 89 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,20 @@
1010

1111
import platform
1212
if platform.architecture()[0] != "32bit":
13-
raise Exception("Unsupported architecture: %s" % (
14-
platform.architecture()[0]))
13+
raise Exception("Only 32bit architecture is supported")
1514

1615
import sys
17-
if sys.hexversion >= 0x02070000 and sys.hexversion < 0x03000000:
18-
import cefpython_py27 as cefpython
19-
elif sys.hexversion >= 0x03000000 and sys.hexversion < 0x04000000:
20-
import cefpython_py32 as cefpython
21-
else:
22-
raise Exception("Unsupported python version: %s" % sys.version)
16+
try:
17+
# Import local PYD file (portable zip).
18+
if sys.hexversion >= 0x02070000 and sys.hexversion < 0x03000000:
19+
import cefpython_py27 as cefpython
20+
elif sys.hexversion >= 0x03000000 and sys.hexversion < 0x04000000:
21+
import cefpython_py32 as cefpython
22+
else:
23+
raise Exception("Unsupported python version: %s" % sys.version)
24+
except ImportError:
25+
# Import from package (installer exe).
26+
from cefpython1 import cefpython
2327

2428
import cefwindow
2529

@@ -33,25 +37,61 @@
3337
import imp
3438
import inspect
3539
import pprint
40+
import time
41+
import imp
3642

3743
DEBUG = True
3844

3945
# TODO: example of creating popup windows from python,
4046
# call WindowInfo.SetAsPopup().
4147
# TODO: example of creating modal windows from python.
4248

43-
def CefAdvanced():
49+
def GetApplicationPath(file=None):
50+
import re, os
51+
# If file is None return current directory without trailing slash.
52+
if file is None:
53+
file = ""
54+
# Only when relative path.
55+
if not file.startswith("/") and not file.startswith("\\") and (
56+
not re.search(r"^[\w-]+:", file)):
57+
if hasattr(sys, "frozen"):
58+
path = os.path.dirname(sys.executable)
59+
elif "__file__" in globals():
60+
path = os.path.dirname(os.path.realpath(__file__))
61+
else:
62+
path = os.getcwd()
63+
path = path + os.sep + file
64+
path = re.sub(r"[/\\]+", re.escape(os.sep), path)
65+
path = re.sub(r"[/\\]+$", "", path)
66+
return path
67+
return str(file)
68+
69+
def ExceptHook(type, value, traceObject):
70+
import traceback, os, time
4471
# This hook does the following: in case of exception display it,
4572
# write to error.log, shutdown CEF and exit application.
46-
sys.excepthook = cefpython.ExceptHook
73+
error = "\n".join(traceback.format_exception(type, value, traceObject))
74+
with open(GetApplicationPath("error.log"), "a") as file:
75+
file.write("\n[%s] %s\n" % (time.strftime("%Y-%m-%d %H:%M:%S"), error))
76+
print("\n"+error+"\n")
77+
cefpython.QuitMessageLoop()
78+
cefpython.Shutdown()
79+
# So that "finally" does not execute.
80+
os._exit(1)
4781

82+
def InitDebugging():
4883
# Whether to print & log debug messages, log file is "debug.log".
4984
if DEBUG:
5085
cefpython.g_debug = True
86+
cefpython.g_debugFile = GetApplicationPath("debug.log")
5187
cefwindow.g_debug = True
5288

89+
def CefAdvanced():
90+
sys.excepthook = ExceptHook
91+
InitDebugging()
92+
5393
appSettings = dict()
54-
appSettings["log_file"] = cefpython.GetRealPath("debug.log")
94+
appSettings["log_file"] = GetApplicationPath("debug.log")
5595

5696
# LOGSEVERITY_INFO - less debug oput.
5797
# LOGSEVERITY_DISABLE - will not create "debug.log" file.
@@ -89,7 +129,8 @@ def CefAdvanced():
89129
windowInfo = cefpython.WindowInfo()
90130
windowInfo.SetAsChild(windowHandle)
91131
browser = cefpython.CreateBrowserSync(
92-
windowInfo, browserSettings, "cefadvanced.html")
132+
windowInfo, browserSettings=browserSettings,
133+
navigateUrl=GetApplicationPath("cefadvanced.html"))
93134
browser.SetUserData("outerWindowHandle", windowInfo.parentWindowHandle)
94135
browser.SetClientHandler(ClientHandler())
95136
browser.SetJavascriptBindings(javascriptBindings)
@@ -137,7 +178,7 @@ def Rebind(self):
137178
# Reload all application modules, next rebind javascript bindings.
138179
# Called from: OnKeyEvent > F5.
139180

140-
currentDir = cefpython.GetRealPath()
181+
currentDir = GetApplicationPath()
141182

142183
for mod in sys.modules.values():
143184
if mod and mod.__name__ != "__main__":
@@ -152,11 +193,9 @@ def Rebind(self):
152193
print("WARNING: reloading module failed: %s. "
153194
"Exception: %s" % (mod.__name__, exc))
154195

155-
if DEBUG:
156-
# These modules have been reloaded, we need to set
157-
# debug variables again.
158-
cefpython.g_debug = True
159-
cefwindow.g_debug = True
196+
# cefpython & cefwindow modules have been reloaded,
197+
# we need to re-initialize debugging.
198+
InitDebugging()
160199

161200
self.Bind()
162201
self.javascriptBindings.Rebind()
@@ -227,7 +266,7 @@ def OnKeyEvent(self, browser, eventType, keyCode, modifiers, isSystemKey,
227266
return False
228267

229268
def OnConsoleMessage(self, browser, message, source, line):
230-
appdir = cefpython.GetRealPath().replace("\\", "/")
269+
appdir = GetApplicationPath().replace("\\", "/")
231270
if appdir[1] == ":":
232271
appdir = appdir[0].upper() + appdir[1:]
233272
source = source.replace("file:///", "")
@@ -253,7 +292,7 @@ def OnUncaughtException(self, browser, frame, exception, stackTrace):
253292
# get rid of the "file://d:/.../cefpython/".
254293
url = re.sub(r"^file:/+", "", url)
255294
url = re.sub(r"[/\\]+", re.escape(os.sep), url)
256-
url = re.sub(r"%s" % re.escape(cefpython.GetRealPath()),
295+
url = re.sub(r"%s" % re.escape(GetApplicationPath()),
257296
"", url, flags=re.IGNORECASE)
258297
url = re.sub(r"^%s" % re.escape(os.sep), "", url)
259298
raise Exception("%s.\n"
@@ -267,31 +306,51 @@ def OnTitleChange(self, browser, title):
267306
# return False
268307
return True
269308

309+
def ModuleExists(module):
310+
try:
311+
imp.find_module(module)
312+
return True
313+
except ImportError:
314+
return False
315+
270316
class Python:
271317
browser = None
272318

273319
def SaveImage(self, outfile, format):
274-
outfile = cefpython.GetRealPath(outfile)
275-
try:
276-
from PIL import Image
277-
except:
278-
print("PIL library not available, can't save image")
279-
return
320+
outfile = GetApplicationPath(outfile)
280321
(width, height) = self.browser.GetSize(cefpython.PET_VIEW)
281322
buffer = self.browser.GetImage(cefpython.PET_VIEW, width, height)
323+
if ModuleExists("PIL"):
324+
self.__SaveImageWithPil(buffer, width, height, outfile, format)
325+
elif ModuleExists("pygame"):
326+
self.__SaveImageWithPygame(buffer, width, height, outfile)
327+
else:
328+
print("Could not save image, no image library found (PIL, pygame)")
329+
if os.path.exists(outfile):
330+
os.system(outfile)
331+
332+
def __SaveImageWithPil(self, buffer, width, height, outfile, format):
333+
from PIL import Image
282334
image = Image.fromstring(
283335
"RGBA", (width,height),
284336
buffer.GetString(mode="rgba", origin="top-left"),
285337
"raw", "RGBA", 0, 1)
286338
image.save(outfile, format)
287-
os.system(outfile)
339+
340+
def __SaveImageWithPygame(self, buffer, width, height, outfile):
341+
import pygame
342+
# Format "PNG" is read from the filename.
343+
surface = pygame.image.frombuffer(
344+
buffer.GetString(mode="rgba", origin="top-left"),
345+
(width, height), "RGBA")
346+
pygame.image.save(surface, outfile)
288347

289348
def ExecuteJavascript(self, jsCode):
290349
self.browser.GetMainFrame().ExecuteJavascript(jsCode)
291350

292351
def LoadUrl(self):
293352
self.browser.GetMainFrame().LoadUrl(
294-
cefpython.GetRealPath("cefsimple.html"))
353+
GetApplicationPath("cefsimple.html"))
295354

296355
def Version(self):
297356
return sys.version
@@ -389,7 +448,8 @@ def CreateSecondBrowser(self):
389448
windowInfo2 = cefpython.WindowInfo()
390449
windowInfo2.SetAsChild(windowHandle2)
391450
browser2 = cefpython.CreateBrowserSync(
392-
windowInfo2, browserSettings={}, navigateURL="cefsimple.html")
451+
windowInfo2, browserSettings={},
452+
navigateUrl=GetApplicationPath("cefsimple.html"))
393453
browser2.SetUserData("outerWindowHandle", windowHandle2)
394454

395455
def GetUnicodeString(self):

cefpython/cef1/windows/binaries/cefsimple.py

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,60 @@
33

44
import platform
55
if platform.architecture()[0] != "32bit":
6-
raise Exception("Architecture not supported: %s" % platform.architecture()[0])
6+
raise Exception("Only 32bit architecture is supported")
77

88
import sys
9-
if sys.hexversion >= 0x02070000 and sys.hexversion < 0x03000000:
10-
import cefpython_py27 as cefpython
11-
elif sys.hexversion >= 0x03000000 and sys.hexversion < 0x04000000:
12-
import cefpython_py32 as cefpython
13-
else:
14-
raise Exception("Unsupported python version: %s" % sys.version)
9+
try:
10+
# Import local PYD file (portable zip).
11+
if sys.hexversion >= 0x02070000 and sys.hexversion < 0x03000000:
12+
import cefpython_py27 as cefpython
13+
elif sys.hexversion >= 0x03000000 and sys.hexversion < 0x04000000:
14+
import cefpython_py32 as cefpython
15+
else:
16+
raise Exception("Unsupported python version: %s" % sys.version)
17+
except ImportError:
18+
# Import from package (installer exe).
19+
from cefpython1 import cefpython
1520

1621
import cefwindow
1722
import win32con
1823
import win32gui
1924

25+
def GetApplicationPath(file=None):
26+
import re, os
27+
# If file is None return current directory without trailing slash.
28+
if file is None:
29+
file = ""
30+
# Only when relative path.
31+
if not file.startswith("/") and not file.startswith("\\") and (
32+
not re.search(r"^[\w-]+:", file)):
33+
if hasattr(sys, "frozen"):
34+
path = os.path.dirname(sys.executable)
35+
elif "__file__" in globals():
36+
path = os.path.dirname(os.path.realpath(__file__))
37+
else:
38+
path = os.getcwd()
39+
path = path + os.sep + file
40+
path = re.sub(r"[/\\]+", re.escape(os.sep), path)
41+
path = re.sub(r"[/\\]+$", "", path)
42+
return path
43+
return str(file)
44+
45+
def ExceptHook(type, value, traceObject):
46+
import traceback, os, time
47+
# This hook does the following: in case of exception display it,
48+
# write to error.log, shutdown CEF and exit application.
49+
error = "\n".join(traceback.format_exception(type, value, traceObject))
50+
with open(GetApplicationPath("error.log"), "a") as file:
51+
file.write("\n[%s] %s\n" % (time.strftime("%Y-%m-%d %H:%M:%S"), error))
52+
print("\n"+error+"\n")
53+
cefpython.QuitMessageLoop()
54+
cefpython.Shutdown()
55+
# So that "finally" does not execute.
56+
os._exit(1)
57+
2058
def CefSimple():
21-
sys.excepthook = cefpython.ExceptHook
59+
sys.excepthook = ExceptHook
2260
cefpython.Initialize()
2361
wndproc = {
2462
win32con.WM_CLOSE: CloseWindow,
@@ -32,7 +70,8 @@ def CefSimple():
3270
windowInfo = cefpython.WindowInfo()
3371
windowInfo.SetAsChild(windowHandle)
3472
browser = cefpython.CreateBrowserSync(
35-
windowInfo, browserSettings={}, navigateURL="cefsimple.html")
73+
windowInfo, browserSettings={},
74+
navigateUrl=GetApplicationPath("cefsimple.html"))
3675
cefpython.MessageLoop()
3776
cefpython.Shutdown()
3877

0 commit comments

Comments
 (0)