python:pyqt5 + cef 模仿 mdict 界面

5 篇文章 0 订阅

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 查询英汉词典

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值