PyQt5 安装参见:python:PyQt5 简单示例
cefpython 入门 参考: Python GUI: cefpython3的简单分析和应用
pip install cefpython3
cefpython3-66.1-py2.py3-none-win_amd64.whl (69.0 MB)
Successfully installed cefpython3-66.1
cd \Python37\Lib\site-packages\cefpython3\examples
copy qt.py qt_cef.py
用的图片在 \Python37\Lib\site-packages\cefpython3\examples\resources\
编写 qt_cef.py 如下
# -*- coding: utf-8 -*-
# Example of embedding CEF browser using PyQt5 libraries.
# This example has two widgets: a navigation bar and a browser.
#
# Tested configurations:
# - PyQt 5.8.2 (qt 5.8.0) on Windows/Linux/Mac
# - CEF Python v55.4+
#
import ctypes
import os
import platform
import sys
from cefpython3 import cefpython as cef
import win32com.client # TTS
sapi = win32com.client.Dispatch("SAPI.SpVoice")
# GLOBALS
PYQT4 = False
PYSIDE = False
PYSIDE2 = False
PYQT5 = True
Baseurl = "http://localhost:8888/"
# 不检查 PyUnresolvedReferences
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
if len(sys.argv) ==1:
print("usage: python qt_cef.py pyqt5")
sys.exit(1)
# Fix for PyCharm hints warnings when using static methods
WindowUtils = cef.WindowUtils()
# Platforms
WINDOWS = (platform.system() == "Windows")
LINUX = (platform.system() == "Linux")
MAC = (platform.system() == "Darwin")
# Configuration
WIDTH = 900
HEIGHT = 600
# OS differences
CefWidgetParent = QWidget
def main():
check_versions()
# To shutdown all CEF processes on error
sys.excepthook = cef.ExceptHook
settings = {
"debug": True,
"log_severity": cef.LOGSEVERITY_INFO,
"log_file": "debug.log" }
#cef.DpiAware.EnableHighDpiSupport()
cef.Initialize({})
app = CefApplication(sys.argv)
main_window = MainWindow()
main_window.show()
main_window.activateWindow()
main_window.raise_()
app.exec_()
if not cef.GetAppSetting("external_message_pump"):
app.stopTimer()
del main_window # Just to be safe, similarly to "del app"
del app # Must destroy app object before calling Shutdown
cef.Shutdown()
def check_versions():
print("[qt_cef.py] CEF Python {ver}".format(ver=cef.__version__))
print("[qt_cef.py] Python {ver} {arch}".format(
ver=platform.python_version(), arch=platform.architecture()[0]))
if PYQT4 or PYQT5:
print("[qt_cef.py] PyQt {v1} (qt {v2})".format(
v1=PYQT_VERSION_STR, v2=qVersion()))
# CEF Python version requirement
assert cef.__version__ >= "55.4", "CEF Python v55.4+ required to run this"
class MainWindow(QMainWindow):
def __init__(self):
# 不检查 PyArgumentList
super(MainWindow, self).__init__(None)
# Avoids crash when shutting down CEF (issue #360)
if PYSIDE:
self.setAttribute(Qt.WA_DeleteOnClose, True)
self.cef_widget = None
self.navigation_bar = None
if PYQT4:
self.setWindowTitle("PyQt4 example")
elif PYQT5:
self.setWindowTitle("PyQt5 + cef")
elif PYSIDE:
self.setWindowTitle("PySide example")
elif PYSIDE2:
self.setWindowTitle("PySide2 example")
self.setFocusPolicy(Qt.StrongFocus)
self.setupLayout()
def setupLayout(self):
self.resize(WIDTH, HEIGHT)
self.cef_widget = CefWidget(self)
self.navigation_bar = NavigationBar(self.cef_widget)
layout = QGridLayout()
# 不检查 PyArgumentList
layout.addWidget(self.navigation_bar, 0, 0)
# 不检查 PyArgumentList
layout.addWidget(self.cef_widget, 1, 0)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
layout.setRowStretch(0, 0)
layout.setRowStretch(1, 1)
# 不检查 PyArgumentList
frame = QFrame()
frame.setLayout(layout)
self.setCentralWidget(frame)
if (PYSIDE2 or PYQT5) and WINDOWS:
# On Windows with PyQt5 main window must be shown first
# before CEF browser is embedded, otherwise window is
# not resized and application hangs during resize.
self.show()
# Browser can be embedded only after layout was set up
self.cef_widget.embedBrowser()
if (PYSIDE2 or PYQT5) and LINUX:
# On Linux with PyQt5 the QX11EmbedContainer widget is
# no more available. An equivalent in Qt5 is to create
# a hidden window, embed CEF browser in it and then
# create a container for that hidden window and replace
# cef widget in the layout with the container.
# 不检查 PyUnresolvedReferences, PyArgumentList
self.container = QWidget.createWindowContainer(
self.cef_widget.hidden_window, parent=self)
# 不检查 PyArgumentList
layout.addWidget(self.container, 1, 0)
def closeEvent(self, event):
# Close browser (force=True) and free CEF reference
if self.cef_widget.browser:
self.cef_widget.browser.CloseBrowser(True)
self.clear_browser_references()
def clear_browser_references(self):
# Clear browser references that you keep anywhere in your
# code. All references must be cleared for CEF to shutdown cleanly.
self.cef_widget.browser = None
class CefWidget(CefWidgetParent):
def __init__(self, parent=None):
# 不检查 PyArgumentList
super(CefWidget, self).__init__(parent)
self.parent = parent
self.browser = None
self.hidden_window = None # Required for PyQt5 on Linux
self.show()
def focusInEvent(self, event):
# This event seems to never get called on Linux, as CEF is
# stealing all focus due to Issue #284.
if cef.GetAppSetting("debug"):
print("[qt_cef.py] CefWidget.focusInEvent")
if self.browser:
if WINDOWS:
WindowUtils.OnSetFocus(self.getHandle(), 0, 0, 0)
self.browser.SetFocus(True)
def focusOutEvent(self, event):
# This event seems to never get called on Linux, as CEF is
# stealing all focus due to Issue #284.
if cef.GetAppSetting("debug"):
print("[qt_cef.py] CefWidget.focusOutEvent")
if self.browser:
self.browser.SetFocus(False)
def embedBrowser(self):
if (PYSIDE2 or PYQT5) and LINUX:
# 不检查 PyUnresolvedReferences
self.hidden_window = QWindow()
window_info = cef.WindowInfo()
rect = [0, 0, self.width(), self.height()]
window_info.SetAsChild(self.getHandle(), rect)
self.browser = cef.CreateBrowserSync(window_info, url=Baseurl)
self.browser.SetClientHandler(LoadHandler(self.parent.navigation_bar))
self.browser.SetClientHandler(FocusHandler(self))
# Javascript 绑定 py function
jsb = cef.JavascriptBindings()
jsb.SetFunction("py_speak", self.py_speak)
self.browser.SetJavascriptBindings(jsb)
def py_speak(self, txt):
""" Javascript 调用py TTS发音 """
if txt.strip() !='':
sapi.Speak(txt)
def getHandle(self):
if self.hidden_window:
# PyQt5 on Linux
return int(self.hidden_window.winId())
try:
# PyQt4 and PyQt5
return int(self.winId())
except:
# PySide:
# | QWidget.winId() returns <PyCObject object at 0x02FD8788>
# | Converting it to int using ctypes.
if sys.version_info[0] == 2:
# Python 2
ctypes.pythonapi.PyCObject_AsVoidPtr.restype = (
ctypes.c_void_p)
ctypes.pythonapi.PyCObject_AsVoidPtr.argtypes = (
[ctypes.py_object])
return ctypes.pythonapi.PyCObject_AsVoidPtr(self.winId())
else:
# Python 3
ctypes.pythonapi.PyCapsule_GetPointer.restype = (
ctypes.c_void_p)
ctypes.pythonapi.PyCapsule_GetPointer.argtypes = (
[ctypes.py_object])
return ctypes.pythonapi.PyCapsule_GetPointer(self.winId(), None)
def moveEvent(self, _):
self.x = 0
self.y = 0
if self.browser:
if WINDOWS:
WindowUtils.OnSize(self.getHandle(), 0, 0, 0)
elif LINUX:
self.browser.SetBounds(self.x, self.y,
self.width(), self.height())
self.browser.NotifyMoveOrResizeStarted()
def resizeEvent(self, event):
size = event.size()
if self.browser:
if WINDOWS:
WindowUtils.OnSize(self.getHandle(), 0, 0, 0)
elif LINUX:
self.browser.SetBounds(self.x, self.y,
size.width(), size.height())
self.browser.NotifyMoveOrResizeStarted()
#实时刷新界面
self.browser.viewport().update()
class CefApplication(QApplication):
def __init__(self, args):
super(CefApplication, self).__init__(args)
if not cef.GetAppSetting("external_message_pump"):
self.timer = self.createTimer()
self.setupIcon()
def createTimer(self):
timer = QTimer()
# 不检查 PyUnresolvedReferences
timer.timeout.connect(self.onTimer)
timer.start(10)
return timer
def onTimer(self):
cef.MessageLoopWork()
def stopTimer(self):
# Stop the timer after Qt's message loop has ended
self.timer.stop()
def setupIcon(self):
icon_file = os.path.join(os.path.abspath(os.path.dirname(__file__)),
"resources", "{0}.png".format(sys.argv[1]))
if os.path.exists(icon_file):
self.setWindowIcon(QIcon(icon_file))
class LoadHandler(object):
def __init__(self, navigation_bar):
self.initial_app_loading = True
self.navigation_bar = navigation_bar
def OnLoadingStateChange(self, **_):
self.navigation_bar.updateState()
def OnLoadStart(self, browser, **_):
#self.navigation_bar.url.setText(browser.GetUrl())
if self.initial_app_loading:
self.navigation_bar.cef_widget.setFocus()
# Temporary fix no. 2 for focus issue on Linux (Issue #284)
if LINUX:
print("[qt_cef.py] LoadHandler.OnLoadStart:"
" keyboard focus fix no. 2 (Issue #284)")
browser.SetFocus(True)
self.initial_app_loading = False
class FocusHandler(object):
def __init__(self, cef_widget):
self.cef_widget = cef_widget
def OnTakeFocus(self, **_):
if cef.GetAppSetting("debug"):
print("[qt_cef.py] FocusHandler.OnTakeFocus")
def OnSetFocus(self, **_):
if cef.GetAppSetting("debug"):
print("[qt_cef.py] FocusHandler.OnSetFocus")
def OnGotFocus(self, browser, **_):
if cef.GetAppSetting("debug"):
print("[qt_cef.py] FocusHandler.OnGotFocus")
self.cef_widget.setFocus()
# Temporary fix no. 1 for focus issues on Linux (Issue #284)
if LINUX:
browser.SetFocus(True)
class NavigationBar(QFrame):
def createButton(self, name):
resources = os.path.join(os.path.abspath(os.path.dirname(__file__)),
"resources")
pixmap = QPixmap(os.path.join(resources, "{0}.png".format(name)))
icon = QIcon(pixmap)
button = QPushButton()
button.setIcon(icon)
button.setIconSize(pixmap.rect().size())
return button
def __init__(self, cef_widget):
# 不检查 PyArgumentList
super(NavigationBar, self).__init__()
self.cef_widget = cef_widget
# Init layout
layout = QGridLayout()
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
# Back button
self.back = self.createButton("back")
# 不检查 PyUnresolvedReferences
self.back.clicked.connect(self.onBack)
# 不检查 PyArgumentList
layout.addWidget(self.back, 0, 0)
# Forward button
self.forward = self.createButton("forward")
# 不检查 PyUnresolvedReferences
self.forward.clicked.connect(self.onForward)
# 不检查 PyArgumentList
layout.addWidget(self.forward, 0, 1)
# Reload button
self.reload = self.createButton("reload")
# 不检查 PyUnresolvedReferences
self.reload.clicked.connect(self.onReload)
# 不检查 PyArgumentList
layout.addWidget(self.reload, 0, 2)
# Url input
self.edit = QLineEdit("")
self.edit.returnPressed.connect(self.onGoUrl)
layout.addWidget(self.edit, 0, 3)
self.button4 = QPushButton("go")
self.button4.clicked.connect(self.onGo)
layout.addWidget(self.button4, 0, 4)
self.button5 = QPushButton("前缀匹配")
self.button5.clicked.connect(self.onPrefix)
layout.addWidget(self.button5, 0, 5)
self.button6 = QPushButton("TTS发音")
self.button6.clicked.connect(self.onTTS)
layout.addWidget(self.button6, 0, 6)
# Layout
self.setLayout(layout)
self.updateState()
def onGo(self):
""" 英译汉 """
url = self.edit.text()
if url.strip() =='':
return
elif url.startswith("http://"):
pass
else:
url = Baseurl + "trans?txt=" + url.strip()
if self.cef_widget.browser:
self.cef_widget.browser.LoadUrl(url)
def onPrefix(self):
""" 前缀匹配 """
url = self.edit.text()
if url.strip() =='':
return
elif url.startswith("https://"):
if self.cef_widget.browser:
self.cef_widget.browser.LoadUrl(url)
else:
url = Baseurl + "prefix?txt=" + url.strip();
if self.cef_widget.browser:
self.cef_widget.browser.LoadUrl(url)
def onTTS(self):
""" 屏幕取词,TTS发音 """
txt = self.edit.text()
if txt.startswith("http://"):
return
if txt.strip() !='':
sapi.Speak(txt)
else:
js = "var select=window.getSelection(); var txt=select.toString(); py_speak(txt);"
self.cef_widget.browser.ExecuteJavascript(js);
def onBack(self):
if self.cef_widget.browser:
self.cef_widget.browser.GoBack()
def onForward(self):
if self.cef_widget.browser:
self.cef_widget.browser.GoForward()
def onReload(self):
if self.cef_widget.browser:
self.cef_widget.browser.Reload()
def onGoUrl(self):
url = self.edit.text()
if self.cef_widget.browser:
self.cef_widget.browser.LoadUrl(url)
def updateState(self):
browser = self.cef_widget.browser
if not browser:
self.back.setEnabled(False)
self.forward.setEnabled(False)
self.reload.setEnabled(False)
self.edit.setEnabled(False)
return
self.back.setEnabled(browser.CanGoBack())
self.forward.setEnabled(browser.CanGoForward())
self.reload.setEnabled(True)
self.edit.setEnabled(True)
#self.edit.setText(browser.GetUrl())
if __name__ == '__main__':
main()
运行 python qt_cef.py pyqt5
编写 index3.html 如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>查询英汉词典</title>
<script src="jquery-3.2.1.min.js"></script>
<style>
/* portrait 判断为竖屏 */
@media only screen and (orientation: portrait){
#lab1 {display:none;}
}
/* landscape 判断为横屏 */
@media only screen and (orientation: landscape){
#lab1 {display: ;}
}
</style>
</head>
<body>
<form name="form" id="form" action="trans" method="POST" target="iframe">
<label id="lab1">请输入:</label>
<input type="text" name="txt" id='txt' size="30" placeholder="请输入 a word">
<input type="submit" name="eng_han" value="英译汉">
<input type="button" name="btn1" id="btn1" value="前缀查询">
<input type="button" name="btn2" id="btn2" value="TTS读音" onclick="tts2()">
</form>
<p></p>
<div style="float:left; width:100%;">
<div id="result" style="float:left; width:80%; height:400; border:2px;">
<iframe name="iframe" id="iframe" width="100%" height="400"> </iframe>
</div>
<div id="alist" style="float:right; width:20%; height:400; border:2px;">
</div>
</div>
<script type="text/javascript">
$(function(){
$("#btn1").click(function(){
$.getJSON("/prefix?txt="+$("#txt").val(), function(data){
var items = [];
$.each(data, function(i, item){
if (i<=20){
items[i] = '<a href="/trans?txt=' +item+ '" target="iframe">' +item+ "</a><br>";
}
});
var a = items.join('\n');
if (a) $('#alist').html(a);
})
})
});
// 屏幕双击取词
function tts2() {
// 获取iframe里的选择内容
var select = window.frames['iframe'].getSelection();
var txt = select.toString();
if (txt.length >1) {
var input = document.getElementById('txt');
input.value = txt.trim();
py_speak(txt);
} else {
txt = document.getElementById('txt').value;
py_speak(txt);
}
}
</script>
</body>
</html>
web 服务程序参见: python:mdict + bottle = web 查询英汉词典