Skip to content

Commit da0a09f

Browse files
committed
Add DevToolsHandler. Fix keyboard issues in wxpython.py DevTools (cztomczak#381).
1 parent 28afa80 commit da0a09f

6 files changed

Lines changed: 146 additions & 8 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,8 @@ supporting this project actively on a daily basis.
612612
* [FlushStore](api/CookieManager.md#flushstore)
613613
* [CookieVisitor (interface)](api/CookieVisitor.md#cookievisitor-interface)
614614
* [Visit](api/CookieVisitor.md#visit)
615+
* [DevToolsHandler (interface)](api/DevToolsHandler.md#devtoolshandler-interface)
616+
* [ShowDevTools](api/DevToolsHandler.md#showdevtools)
615617
* [DisplayHandler (interface)](api/DisplayHandler.md#displayhandler-interface)
616618
* [OnAddressChange](api/DisplayHandler.md#onaddresschange)
617619
* [OnAutoResize](api/DisplayHandler.md#onautoresize)

api/API-index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,8 @@
214214
* [FlushStore](CookieManager.md#flushstore)
215215
* [CookieVisitor (interface)](CookieVisitor.md#cookievisitor-interface)
216216
* [Visit](CookieVisitor.md#visit)
217+
* [DevToolsHandler (interface)](DevToolsHandler.md#devtoolshandler-interface)
218+
* [ShowDevTools](DevToolsHandler.md#showdevtools)
217219
* [DisplayHandler (interface)](DisplayHandler.md#displayhandler-interface)
218220
* [OnAddressChange](DisplayHandler.md#onaddresschange)
219221
* [OnAutoResize](DisplayHandler.md#onautoresize)

api/DevToolsHandler.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
[API categories](API-categories.md) | [API index](API-index.md)
2+
3+
4+
# DevToolsHandler (interface)
5+
6+
Implement this interface to handle events related to Developer Tools window.
7+
8+
9+
Table of contents:
10+
* [Callbacks](#callbacks)
11+
* [ShowDevTools](#showdevtools)
12+
13+
14+
## Callbacks
15+
16+
17+
### ShowDevTools
18+
19+
| Parameter | Type |
20+
| --- | --- |
21+
| browser | [Browser](Browser.md) |
22+
| __Return__ | void |
23+
24+
Implement this callback to overwrite the [Browser](Browser.md).`ShowDevTools`
25+
method. This will also overwrite the behavior of mouse context menu option
26+
"Developer Tools". This callback is useful to implement custom behavior
27+
and also to fix keyboard issues in DevTools popup in the `wxpython.py`
28+
example reported in [Issue #381](../../../issues/381).
29+
30+
Example usage case is in the `wxpython.py` and `devtools.py`
31+
examples. See parts of the code from the `wxpython.py` example:
32+
33+
```py
34+
# DevTools port and url. This is needed to workaround keyboard issues
35+
# in DevTools popup on Windows (Issue #381).
36+
DEVTOOLS_PORT = 0 # By default a random port is generated.
37+
if WINDOWS:
38+
DEVTOOLS_PORT = 54008
39+
DEVTOOLS_URL = "http://127.0.0.1:{0}/".format(DEVTOOLS_PORT)
40+
41+
...
42+
if DEVTOOLS_PORT:
43+
settings["remote_debugging_port"] = DEVTOOLS_PORT
44+
...
45+
46+
def ShowDevTools(self, browser, **_):
47+
# Check if app was frozen with e.g. pyinstaller.
48+
if getattr(sys, "frozen", None):
49+
dir = os.path.dirname(os.path.realpath(__file__))
50+
executable = os.path.join(dir, "devtools.exe");
51+
if os.path.exists(executable):
52+
# If making executable with pyinstaller then create
53+
# executable for the devtools.py script as well.
54+
subprocess.Popen([executable, DEVTOOLS_URL])
55+
else:
56+
# Another way to show DevTools is to open it in Google Chrome
57+
# system browser.
58+
webbrowser.open(DEVTOOLS_URL)
59+
else:
60+
# Use the devtools.py script to open DevTools popup.
61+
dir = os.path.dirname(os.path.realpath(__file__))
62+
script = os.path.join(dir, "devtools.py")
63+
subprocess.Popen([sys.executable, script, DEVTOOLS_URL])
64+
```

examples/devtools.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""
2+
This script is a workaround for CEF Python Issue #381 which is causing
3+
keyboard issues in DevTools window in the wxpython.py example.
4+
A solution is to open devtools window in a seprate process by executing
5+
this script. An example usage is in the wxpython.py example. See
6+
also the "api/DevToolsHandler.md" document.
7+
"""
8+
9+
from cefpython3 import cefpython as cef
10+
import sys
11+
12+
DEVTOOLS_URL = sys.argv[1]
13+
14+
15+
def main():
16+
print("[devtools.py] url={0}".format(DEVTOOLS_URL))
17+
sys.excepthook = cef.ExceptHook # To shutdown all CEF processes on error
18+
cef.Initialize()
19+
cef.CreateBrowserSync(url=DEVTOOLS_URL,
20+
window_title="DevTools")
21+
cef.MessageLoop()
22+
cef.Shutdown()
23+
24+
25+
if __name__ == '__main__':
26+
main()

examples/wxpython.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
# - wxPython 2.8 on Linux
88
# - CEF Python v66.0+
99

10-
import wx
1110
from cefpython3 import cefpython as cef
11+
import os
1212
import platform
13+
import subprocess
1314
import sys
14-
import os
15+
import webbrowser
16+
import wx
1517

1618
# Platforms
1719
WINDOWS = (platform.system() == "Windows")
@@ -29,6 +31,13 @@
2931
"pip install -U pyobjc")
3032
sys.exit(1)
3133

34+
# DevTools port and url. This is needed to workaround keyboard issues
35+
# in DevTools popup on Windows (Issue #381).
36+
DEVTOOLS_PORT = 0 # By default a random port is generated.
37+
if WINDOWS:
38+
DEVTOOLS_PORT = 54008
39+
DEVTOOLS_URL = "http://127.0.0.1:{0}/".format(DEVTOOLS_PORT)
40+
3241
# Configuration
3342
WIDTH = 900
3443
HEIGHT = 640
@@ -41,6 +50,8 @@ def main():
4150
check_versions()
4251
sys.excepthook = cef.ExceptHook # To shutdown all CEF processes on error
4352
settings = {}
53+
if DEVTOOLS_PORT:
54+
settings["remote_debugging_port"] = DEVTOOLS_PORT
4455
if MAC:
4556
# Issue #442 requires enabling message pump on Mac
4657
# and calling message loop work in a timer both at
@@ -176,6 +187,10 @@ def embed_browser(self):
176187
[0, 0, width, height])
177188
self.browser = cef.CreateBrowserSync(window_info,
178189
url="https://www.google.com/")
190+
if WINDOWS:
191+
# Override the Brower.ShowDevTools method to fix keyboard
192+
# problems on Windows (Issue #381).
193+
self.browser.SetClientHandler(DevToolsHandler())
179194
self.browser.SetClientHandler(FocusHandler())
180195

181196
def OnSetFocus(self, _):
@@ -231,6 +246,29 @@ def clear_browser_references(self):
231246
self.browser = None
232247

233248

249+
class DevToolsHandler(object):
250+
"""This handler is set only on Windows platform."""
251+
252+
def ShowDevTools(self, browser, **_):
253+
# Check if app was frozen with e.g. pyinstaller.
254+
if getattr(sys, "frozen", None):
255+
dir = os.path.dirname(os.path.realpath(__file__))
256+
executable = os.path.join(dir, "devtools.exe");
257+
if os.path.exists(executable):
258+
# If making executable with pyinstaller then create
259+
# executable for the devtools.py script as well.
260+
subprocess.Popen([executable, DEVTOOLS_URL])
261+
else:
262+
# Another way to show DevTools is to open it in Google Chrome
263+
# system browser.
264+
webbrowser.open(DEVTOOLS_URL)
265+
else:
266+
# Use the devtools.py script to open DevTools popup.
267+
dir = os.path.dirname(os.path.realpath(__file__))
268+
script = os.path.join(dir, "devtools.py")
269+
subprocess.Popen([sys.executable, script, DEVTOOLS_URL])
270+
271+
234272
class FocusHandler(object):
235273
def OnGotFocus(self, browser, **_):
236274
# Temporary fix for focus issues on Linux (Issue #284).

src/browser.pyx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,8 @@ cdef class PyBrowser:
278278
# FocusHandler
279279
self.allowedClientCallbacks += ["OnTakeFocus", "OnSetFocus",
280280
"OnGotFocus"]
281+
# DevToolsHandler
282+
self.allowedClientCallbacks += ["ShowDevTools"]
281283

282284
if name not in self.allowedClientCallbacks:
283285
raise Exception("Browser.SetClientCallback() failed: unknown "
@@ -574,15 +576,19 @@ cdef class PyBrowser:
574576
self.GetCefBrowserHost().get().SetZoomLevel(zoomLevel)
575577

576578
cpdef py_void ShowDevTools(self):
579+
cdef object callback = self.GetClientCallback("ShowDevTools")
577580
cdef CefWindowInfo window_info
578-
IF UNAME_SYSNAME == "Windows":
579-
window_info.SetAsPopup(<CefWindowHandle>self.GetWindowHandle(),
580-
PyToCefStringValue("DevTools"))
581581
cdef CefBrowserSettings settings
582582
cdef CefPoint inspect_element_at
583-
self.GetCefBrowserHost().get().ShowDevTools(
584-
window_info, <CefRefPtr[CefClient]?>NULL, settings,
585-
inspect_element_at)
583+
if callback:
584+
callback(self)
585+
else:
586+
IF UNAME_SYSNAME == "Windows":
587+
window_info.SetAsPopup(<CefWindowHandle>self.GetWindowHandle(),
588+
PyToCefStringValue("DevTools"))
589+
self.GetCefBrowserHost().get().ShowDevTools(
590+
window_info, <CefRefPtr[CefClient]?>NULL, settings,
591+
inspect_element_at)
586592

587593
cpdef py_void StopLoad(self):
588594
self.GetCefBrowser().get().StopLoad()

0 commit comments

Comments
 (0)