Skip to content

Commit de6bcd5

Browse files
committed
Fix window resize issues (cztomczak#464). Expose Browser.SetBounds method,
update examples and documentation. Fix event handling in qt.py example. Parent methods need to be called when overriding events. Fix screenshot.py example. Sometimes OnLoadingStateChange was called before OnPaint resulting in error.
1 parent 3427a73 commit de6bcd5

14 files changed

Lines changed: 75 additions & 57 deletions

File tree

api/Browser.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -814,7 +814,12 @@ Description from upstream CEF:
814814
| height | int |
815815
| __Return__ | void |
816816

817-
Linux-only. Set window bounds.
817+
Set browser internal window bounds. This method should be called during
818+
size events of parent window in which CEF browser is embedded.
819+
820+
On Windows the x and y parameters are ignored.
821+
822+
On Mac this function does nothing.
818823

819824

820825
### SendKeyEvent

api/WindowUtils.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,11 @@ Windows-only. This method processes WM_SETFOCUS message which is sent to a windo
4646
| lparam | long |
4747
| __Return__ | void |
4848

49-
Windows-only. This method processes WM_SIZE message which is sent to a window after its size has changed.
49+
Windows-only. This method should be called during WM_SIZE message which
50+
is sent to a window after its size has changed. Most examples should call
51+
the `Browser`.[SetBounds](Browser.md#setbounds) method during size events.
52+
The `pywin32.py` example is an exception and it needs to use the
53+
`WindowUtils.OnSize` method.
5054

5155

5256
### OnEraseBackground (Win)

docs/Migration-guide.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ Table of contents:
5151
* [v66+ RequestHandler.GetCookieManager not getting called in some cases](#v66-requesthandlergetcookiemanager-not-getting-called-in-some-cases)
5252
* [v66+ Changes to Mac apps that integrate into existing message loop (Qt, wxPython)](#v66-changes-to-mac-apps-that-integrate-into-existing-message-loop-qt-wxpython)
5353
* [v66.1+ Navigation urls passed to CreateBrowserSync or LoadUrl methods need to be encoded by app code](#v661-navigation-urls-passed-to-createbrowsersync-or-loadurl-methods-need-to-be-encoded-by-app-code)
54-
* [v67+ Do not call the 'WindowUtils.OnSize' function](#v67-do-not-call-the-windowutilsonsize-function)
54+
* [v66.1+ Do not call 'WindowUtils.OnSize', use `Browser.SetBounds` instead.](#v661-do-not-call-windowutilsonsize-use-browsersetbounds-instead)
5555

5656

5757
## v49+ Distribution packages
@@ -499,10 +499,10 @@ Python 2 (`urllib.request.pathname2url` in Python 3) depending on your case.
499499
The `cef.GetNavigateUrl` function was removed from the cefpython3 module.
500500

501501

502-
## v67+ Do not call the 'WindowUtils.OnSize' function
502+
## v66.1+ Do not call 'WindowUtils.OnSize', use `Browser.SetBounds` instead.
503503

504504
This function can sometimes cause app hanging during window resize.
505-
Call instead the new `WindowUtils`.[UpdateBrowserSize](../api/WindowUtils.md#updatebrowsersize)
506-
function. Except when you use the `pywin32.py` example, in such case
507-
`WindowUtils.OnSize` must be called.
508-
See [Issue #464](../../../issues/464) for more details.
505+
Call instead the new `Browser`.[SetBounds](../api/Browser.md#setbounds)
506+
function. Except for when you use the `pywin32.py` example, in such case
507+
`WindowUtils.OnSize` must be called. See
508+
[Issue #464](../../../issues/464) for more details.

examples/gtk2.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,7 @@ def on_vbox_size_allocate(self, _, data):
160160
y = data.y + self.menubar_height
161161
width = data.width
162162
height = data.height - self.menubar_height
163-
if WINDOWS:
164-
WindowUtils.OnSize(self.get_window_handle(), 0, 0, 0)
165-
elif LINUX:
163+
if WINDOWS or LINUX:
166164
self.browser.SetBounds(x, y, width, height)
167165

168166
def on_menubar_size_allocate(self, _, data):

examples/gtk3.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,7 @@ def on_configure(self, *_):
125125

126126
def on_size_allocate(self, _, data):
127127
if self.browser:
128-
if WINDOWS:
129-
WindowUtils.OnSize(self.win32_handle, 0, 0, 0)
130-
elif LINUX:
128+
if WINDOWS or LINUX:
131129
self.browser.SetBounds(data.x, data.y,
132130
data.width, data.height)
133131

examples/hello_world.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ def main():
1010
check_versions()
1111
sys.excepthook = cef.ExceptHook # To shutdown all CEF processes on error
1212
cef.Initialize()
13-
cef.CreateBrowserSync(url="https://www.google.com/",
14-
window_title="Hello World!")
13+
browser = cef.CreateBrowserSync(url="https://www.google.com/",
14+
window_title="Hello World!")
1515
cef.MessageLoop()
1616
cef.Shutdown()
1717

examples/qt.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ def setupLayout(self):
191191
def closeEvent(self, event):
192192
# Close browser (force=True) and free CEF reference
193193
if self.cef_widget.browser:
194+
self.cef_widget.browser.SetUserData("closing-browser", True)
194195
self.cef_widget.browser.CloseBrowser(True)
195196
self.clear_browser_references()
196197

@@ -218,6 +219,7 @@ def focusInEvent(self, event):
218219
if WINDOWS:
219220
WindowUtils.OnSetFocus(self.getHandle(), 0, 0, 0)
220221
self.browser.SetFocus(True)
222+
super(CefWidget, self).focusInEvent(event)
221223

222224
def focusOutEvent(self, event):
223225
# This event seems to never get called on Linux, as CEF is
@@ -226,6 +228,7 @@ def focusOutEvent(self, event):
226228
print("[qt.py] CefWidget.focusOutEvent")
227229
if self.browser:
228230
self.browser.SetFocus(False)
231+
super(CefWidget, self).focusOutEvent(event)
229232

230233
def embedBrowser(self):
231234
if (PYSIDE2 or PYQT5) and LINUX:
@@ -266,27 +269,24 @@ def getHandle(self):
266269
return ctypes.pythonapi.PyCapsule_GetPointer(
267270
self.winId(), None)
268271

269-
def moveEvent(self, _):
272+
def moveEvent(self, event):
270273
self.x = 0
271274
self.y = 0
272275
if self.browser:
273-
if WINDOWS:
274-
WindowUtils.OnSize(self.getHandle(), 0, 0, 0)
275-
elif LINUX:
276+
if WINDOWS or LINUX:
276277
self.browser.SetBounds(self.x, self.y,
277278
self.width(), self.height())
278279
self.browser.NotifyMoveOrResizeStarted()
280+
super(CefWidget, self).moveEvent(event)
279281

280282
def resizeEvent(self, event):
281283
size = event.size()
282284
if self.browser:
283-
if WINDOWS:
284-
WindowUtils.OnSize(self.getHandle(), 0, 0, 0)
285-
elif LINUX:
285+
if WINDOWS or LINUX:
286286
self.browser.SetBounds(self.x, self.y,
287287
size.width(), size.height())
288288
self.browser.NotifyMoveOrResizeStarted()
289-
289+
super(CefWidget, self).resizeEvent(event)
290290

291291
class CefApplication(QApplication):
292292
def __init__(self, args):
@@ -351,7 +351,10 @@ def OnSetFocus(self, **_):
351351
def OnGotFocus(self, browser, **_):
352352
if cef.GetAppSetting("debug"):
353353
print("[qt.py] FocusHandler.OnGotFocus")
354-
self.cef_widget.setFocus()
354+
# Check if browser is not closing otherwise error occurs in PySide:
355+
# > RuntimeError: Internal C++ object (CefWidget) already deleted.
356+
if not browser.GetUserData("closing-browser") and self.cef_widget:
357+
self.cef_widget.setFocus()
355358
# Temporary fix no. 1 for focus issues on Linux (Issue #284)
356359
if LINUX:
357360
browser.SetFocus(True)

examples/screenshot.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,11 +153,24 @@ def save_screenshot(browser):
153153
# "OnPaint.buffer_string" data is set in RenderHandler.OnPaint.
154154
buffer_string = browser.GetUserData("OnPaint.buffer_string")
155155
if not buffer_string:
156-
raise Exception("buffer_string is empty, OnPaint never called?")
156+
# Sometimes LoadHandler.OnLoadingStateChange gets called
157+
# before RenderHandler.OnPaint.
158+
if not browser.GetUserData("save_screenshot.delay_printed"):
159+
sys.stdout.write("[screenshot.py] Delay")
160+
sys.stdout.flush()
161+
browser.SetUserData("save_screenshot.delay_printed", True)
162+
else:
163+
sys.stdout.write(".")
164+
sys.stdout.flush()
165+
cef.PostDelayedTask(cef.TID_UI, 13, save_screenshot, browser)
166+
return
157167
image = Image.frombytes("RGBA", VIEWPORT_SIZE, buffer_string,
158168
"raw", "RGBA", 0, 1)
159169
image.save(SCREENSHOT_PATH, "PNG")
170+
sys.stdout.write(os.linesep)
160171
print("[screenshot.py] Saved image: {path}".format(path=SCREENSHOT_PATH))
172+
# See comments in exit_app() why PostTask must be used
173+
cef.PostTask(cef.TID_UI, exit_app, browser)
161174

162175

163176
def open_with_default_application(path):
@@ -187,10 +200,9 @@ def OnLoadingStateChange(self, browser, is_loading, **_):
187200
if not is_loading:
188201
# Loading is complete
189202
sys.stdout.write(os.linesep)
203+
sys.stdout.flush()
190204
print("[screenshot.py] Web page loading is complete")
191205
save_screenshot(browser)
192-
# See comments in exit_app() why PostTask must be used
193-
cef.PostTask(cef.TID_UI, exit_app, browser)
194206

195207
def OnLoadError(self, browser, frame, error_code, failed_url, **_):
196208
"""Called when the resource load for a navigation fails
@@ -225,7 +237,10 @@ def OnPaint(self, browser, element_type, paint_buffer, **_):
225237
sys.stdout.write(".")
226238
sys.stdout.flush()
227239
else:
240+
if browser.GetUserData("save_screenshot.delay_printed"):
241+
sys.stdout.write(os.linesep)
228242
sys.stdout.write("[screenshot.py] OnPaint")
243+
sys.stdout.flush()
229244
self.OnPaint_called = True
230245
if element_type == cef.PET_VIEW:
231246
# Buffer string is a huge string, so for performance

examples/snippets/window_size.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,15 @@ def main():
1515
cef.Initialize()
1616
window_info = cef.WindowInfo()
1717
parent_handle = 0
18-
# This call has effect only on Mac and Linux.
18+
# SetAsChild() call has effect only on Mac and Linux.
1919
# All rect coordinates are applied including X and Y parameters.
2020
window_info.SetAsChild(parent_handle, [0, 0, 900, 640])
2121
browser = cef.CreateBrowserSync(url="https://www.google.com/",
2222
window_info=window_info,
2323
window_title="Window size")
2424
if platform.system() == "Windows":
25-
window_handle = browser.GetOuterWindowHandle()
26-
insert_after_handle = 0
27-
# X and Y parameters are ignored by setting the SWP_NOMOVE flag
28-
SWP_NOMOVE = 0x0002
29-
# noinspection PyUnresolvedReferences
30-
ctypes.windll.user32.SetWindowPos(window_handle, insert_after_handle,
31-
0, 0, 900, 640, SWP_NOMOVE)
25+
# X and Y parameters are ignored.
26+
browser.SetBounds(0, 0, 900, 640)
3227
cef.MessageLoop()
3328
del browser
3429
cef.Shutdown()

examples/tkinter_.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -204,11 +204,7 @@ def on_root_configure(self):
204204

205205
def on_mainframe_configure(self, width, height):
206206
if self.browser:
207-
if WINDOWS:
208-
ctypes.windll.user32.SetWindowPos(
209-
self.browser.GetWindowHandle(), 0,
210-
0, 0, width, height, 0x0002)
211-
elif LINUX:
207+
if WINDOWS or LINUX:
212208
self.browser.SetBounds(0, 0, width, height)
213209
self.browser.NotifyMoveOrResizeStarted()
214210

0 commit comments

Comments
 (0)