Skip to content

Commit b9875f5

Browse files
committed
Update pywin32.py example (PR #374).
Add link to README-examples. Add usage comments. Fix code style issues - get rid of PEP8 and PyCharm notices/warnings. Load chromium.ico icon.
1 parent 43126af commit b9875f5

File tree

3 files changed

+141
-72
lines changed

3 files changed

+141
-72
lines changed

examples/README-examples.md

+2-3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ workarounds.
4343
library (GTK 2)
4444
- [gtk3.py](gtk3.py): example for [PyGObject / PyGI](https://wiki.gnome.org/Projects/PyGObject)
4545
library (GTK 3). Currently broken on Mac ([#310](../../../issues/310)).
46+
- [pywin32.py](pywin32.py): example for [pywin32](https://github.com/mhammond/pywin32)
47+
library
4648
- [qt.py](qt.py): example for [PyQt4](https://wiki.python.org/moin/PyQt4),
4749
[PyQt5](https://pypi.python.org/pypi/PyQt5)
4850
and [PySide](https://wiki.qt.io/PySide) libraries
@@ -83,9 +85,6 @@ maintained.
8385
example in the cefpython31 branch.
8486
- Example of using Python network library (urllib3/openssl) instead of Chromium's
8587
network library - see [gist by Massimiliano Dal Cero](https://gist.github.com/yattamax/0252a3c5dc54a2f81650d5c0eafabf99)
86-
- Old PyWin32 example:
87-
see [pywin32.py](https://github.com/cztomczak/cefpython/blob/cefpython31/cefpython/cef3/windows/binaries_32bit/pywin32.py)
88-
in the cefpython31 branch
8988

9089
There are ongoing efforts to add these examples to the official examples/
9190
directory, see issues in the tracker.

examples/pywin32.py

+139-69
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
1-
# Example of embedding CEF browser using the PyWin32 extension.
2-
# Tested with pywin32 version 219.
1+
# Example of embedding CEF browser using PyWin32 library.
2+
# Tested with pywin32 version 221 and CEF Python v57.0+.
3+
#
4+
# Usage:
5+
# python pywin32.py
6+
# python pywin32.py --multi-threaded
7+
#
8+
# By passing --multi-threaded arg CEF will run using multi threaded
9+
# message loop which has best performance on Windows. However there
10+
# is one issue with it on exit, see "Known issues" below. See also
11+
# docs/Tutorial.md and the "Message loop" section.
12+
#
13+
# Known issues:
14+
# - Crash on exit with multi threaded message loop (Issue #380)
315

416
from cefpython3 import cefpython as cef
517

@@ -13,111 +25,169 @@
1325
import win32con
1426
import win32gui
1527

28+
# Globals
1629
WindowUtils = cef.WindowUtils()
30+
g_multi_threaded = False
1731

18-
# Platforms (Windows only)
19-
assert(platform.system() == "Windows")
2032

21-
def main(multi_threaded_message_loop):
22-
33+
def main():
34+
command_line_args()
2335
check_versions()
2436
sys.excepthook = cef.ExceptHook # To shutdown all CEF processes on error
2537

26-
settings = {"multi_threaded_message_loop": 1 if multi_threaded_message_loop else 0,
27-
"remote_debugging_port": 2020}
28-
cef.Initialize(settings)
38+
settings = {
39+
"multi_threaded_message_loop": g_multi_threaded,
40+
}
41+
cef.Initialize(settings=settings)
2942

30-
wndproc = {
31-
win32con.WM_CLOSE: CloseWindow,
32-
win32con.WM_DESTROY: QuitApplication,
43+
window_proc = {
44+
win32con.WM_CLOSE: close_window,
45+
win32con.WM_DESTROY: exit_app,
3346
win32con.WM_SIZE: WindowUtils.OnSize,
3447
win32con.WM_SETFOCUS: WindowUtils.OnSetFocus,
3548
win32con.WM_ERASEBKGND: WindowUtils.OnEraseBackground
3649
}
37-
windowHandle = CreateWindow(title="pywin32 example", className="cefpython3_example", width=1024, height=768, windowProc=wndproc)
50+
window_handle = create_window(title="PyWin32 example",
51+
class_name="pywin32.example",
52+
width=800,
53+
height=600,
54+
window_proc=window_proc,
55+
icon="resources/chromium.ico")
3856

39-
windowInfo = cef.WindowInfo()
40-
windowInfo.SetAsChild(windowHandle)
57+
window_info = cef.WindowInfo()
58+
window_info.SetAsChild(window_handle)
4159

42-
if(multi_threaded_message_loop):
43-
# when using multi-threaded message loop, CEF's UI thread is no more application's main thread
44-
cef.PostTask(cef.TID_UI, _createBrowserInUiThread, windowInfo, {}, "https://www.google.com/")
60+
if g_multi_threaded:
61+
# When using multi-threaded message loop, CEF's UI thread
62+
# is no more application's main thread. In such case browser
63+
# must be created using cef.PostTask function and CEF message
64+
# loop must not be run explicitilly.
65+
cef.PostTask(cef.TID_UI,
66+
create_browser,
67+
window_info,
68+
{},
69+
"https://www.google.com/")
4570
win32gui.PumpMessages()
4671

4772
else:
48-
browser = _createBrowserInUiThread(windowInfo, {}, "https://www.google.com/")
73+
create_browser(window_info=window_info,
74+
settings={},
75+
url="https://www.google.com/")
4976
cef.MessageLoop()
5077

5178
cef.Shutdown()
5279

5380

54-
def check_versions():
55-
print("[pywin32.py] CEF Python {ver}".format(ver=cef.__version__))
56-
print("[pywin32.py] Python {ver} {arch}".format(ver=platform.python_version(), arch=platform.architecture()[0]))
57-
print("[pywin32.py] pywin32 {ver}".format(ver=GetPywin32Version()))
58-
assert cef.__version__ >= "55.3", "CEF Python v55.3+ required to run this"
81+
def command_line_args():
82+
global g_multi_threaded
83+
if "--multi-threaded" in sys.argv:
84+
sys.argv.remove("--multi-threaded")
85+
print("[pywin32.py] Message loop mode: CEF multi-threaded"
86+
" (best performance)")
87+
g_multi_threaded = True
88+
else:
89+
print("[pywin32.py] Message loop mode: CEF single-threaded")
90+
if len(sys.argv) > 1:
91+
print("ERROR: Invalid args passed."
92+
" For usage see top comments in pywin32.py.")
93+
sys.exit(1)
5994

6095

61-
def _createBrowserInUiThread(windowInfo, settings, url):
62-
63-
assert(cef.IsThread(cef.TID_UI))
64-
browser = cef.CreateBrowserSync(windowInfo, settings, url)
96+
def check_versions():
97+
if platform.system() != "Windows":
98+
print("ERROR: This example is for Windows platform only")
99+
sys.exit(1)
65100

101+
print("[pywin32.py] CEF Python {ver}".format(ver=cef.__version__))
102+
print("[pywin32.py] Python {ver} {arch}".format(
103+
ver=platform.python_version(), arch=platform.architecture()[0]))
66104

67-
def CloseWindow(windowHandle, message, wparam, lparam):
68-
browser = cef.GetBrowserByWindowHandle(windowHandle)
69-
browser.CloseBrowser()
70-
return win32gui.DefWindowProc(windowHandle, message, wparam, lparam)
105+
# PyWin32 version
106+
python_lib = distutils.sysconfig.get_python_lib(plat_specific=1)
107+
with open(os.path.join(python_lib, "pywin32.version.txt")) as fp:
108+
pywin32_version = fp.read().strip()
109+
print("[pywin32.py] pywin32 {ver}".format(ver=pywin32_version))
71110

111+
assert cef.__version__ >= "57.0", "CEF Python v57.0 required to run this"
72112

73-
def QuitApplication(windowHandle, message, wparam, lparam):
74-
win32gui.PostQuitMessage(0)
75-
return 0
76113

114+
def create_browser(window_info, settings, url):
115+
assert(cef.IsThread(cef.TID_UI))
116+
cef.CreateBrowserSync(window_info=window_info,
117+
settings=settings,
118+
url=url)
77119

78-
def CreateWindow(title, className, width, height, windowProc):
79-
120+
121+
def create_window(title, class_name, width, height, window_proc, icon):
122+
# Register window class
80123
wndclass = win32gui.WNDCLASS()
81124
wndclass.hInstance = win32api.GetModuleHandle(None)
82-
wndclass.lpszClassName = className
125+
wndclass.lpszClassName = class_name
83126
wndclass.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW
84-
# win32con.CS_GLOBALCLASS
85127
wndclass.hbrBackground = win32con.COLOR_WINDOW
86128
wndclass.hCursor = win32gui.LoadCursor(0, win32con.IDC_ARROW)
87-
wndclass.lpfnWndProc = windowProc
88-
atomClass = win32gui.RegisterClass(wndclass)
89-
assert(atomClass != 0)
90-
91-
# Center window on the screen.
129+
wndclass.lpfnWndProc = window_proc
130+
atom_class = win32gui.RegisterClass(wndclass)
131+
assert(atom_class != 0)
132+
133+
# Center window on screen.
92134
screenx = win32api.GetSystemMetrics(win32con.SM_CXSCREEN)
93135
screeny = win32api.GetSystemMetrics(win32con.SM_CYSCREEN)
94136
xpos = int(math.floor((screenx - width) / 2))
95137
ypos = int(math.floor((screeny - height) / 2))
96-
if xpos < 0: xpos = 0
97-
if ypos < 0: ypos = 0
98-
99-
windowHandle = win32gui.CreateWindow(className, title,
100-
win32con.WS_OVERLAPPEDWINDOW | win32con.WS_CLIPCHILDREN | win32con.WS_VISIBLE,
101-
xpos, ypos, width, height, # xpos, ypos, width, height
102-
0, 0, wndclass.hInstance, None)
103-
104-
assert(windowHandle != 0)
105-
return windowHandle
106-
107-
108-
def GetPywin32Version():
109-
pth = distutils.sysconfig.get_python_lib(plat_specific=1)
110-
ver = open(os.path.join(pth, "pywin32.version.txt")).read().strip()
111-
return ver
138+
if xpos < 0:
139+
xpos = 0
140+
if ypos < 0:
141+
ypos = 0
142+
143+
# Create window
144+
window_style = (win32con.WS_OVERLAPPEDWINDOW | win32con.WS_CLIPCHILDREN
145+
| win32con.WS_VISIBLE)
146+
window_handle = win32gui.CreateWindow(class_name, title, window_style,
147+
xpos, ypos, width, height,
148+
0, 0, wndclass.hInstance, None)
149+
assert(window_handle != 0)
150+
151+
# Window icon
152+
icon = os.path.abspath(icon)
153+
if not os.path.isfile(icon):
154+
icon = None
155+
if icon:
156+
# Load small and big icon.
157+
# WNDCLASSEX (along with hIconSm) is not supported by pywin32,
158+
# we need to use WM_SETICON message after window creation.
159+
# Ref:
160+
# 1. http://stackoverflow.com/questions/2234988
161+
# 2. http://blog.barthe.ph/2009/07/17/wmseticon/
162+
bigx = win32api.GetSystemMetrics(win32con.SM_CXICON)
163+
bigy = win32api.GetSystemMetrics(win32con.SM_CYICON)
164+
big_icon = win32gui.LoadImage(0, icon, win32con.IMAGE_ICON,
165+
bigx, bigy,
166+
win32con.LR_LOADFROMFILE)
167+
smallx = win32api.GetSystemMetrics(win32con.SM_CXSMICON)
168+
smally = win32api.GetSystemMetrics(win32con.SM_CYSMICON)
169+
small_icon = win32gui.LoadImage(0, icon, win32con.IMAGE_ICON,
170+
smallx, smally,
171+
win32con.LR_LOADFROMFILE)
172+
win32api.SendMessage(window_handle, win32con.WM_SETICON,
173+
win32con.ICON_BIG, big_icon)
174+
win32api.SendMessage(window_handle, win32con.WM_SETICON,
175+
win32con.ICON_SMALL, small_icon)
176+
177+
return window_handle
178+
179+
180+
def close_window(window_handle, message, wparam, lparam):
181+
browser = cef.GetBrowserByWindowHandle(window_handle)
182+
browser.CloseBrowser(True)
183+
# OFF: win32gui.DestroyWindow(window_handle)
184+
return win32gui.DefWindowProc(window_handle, message, wparam, lparam)
185+
186+
187+
def exit_app(*_):
188+
win32gui.PostQuitMessage(0)
189+
return 0
112190

113191

114192
if __name__ == '__main__':
115-
116-
if "--multi_threaded_message_loop" in sys.argv:
117-
print("[pywin32.py] Message loop mode: CEF multi-threaded (best performance)")
118-
multi_threaded_message_loop = True
119-
else:
120-
print("[pywin32.py] Message loop mode: CEF single-threaded")
121-
multi_threaded_message_loop = False
122-
123-
main(multi_threaded_message_loop)
193+
main()

examples/resources/chromium.ico

194 KB
Binary file not shown.

0 commit comments

Comments
 (0)