杰瑞科技汇

如何在Python中使用QWebEngine?

Of course! Let's dive deep into QWebEngineView from the Qt for Python project. This is the primary component for embedding web content (like a modern browser) into your desktop applications.

如何在Python中使用QWebEngine?-图1
(图片来源网络,侵删)

What is QWebEngineView?

QWebEngineView is a widget that provides a web browser engine within a PyQt or PySide application. It uses the Chromium engine under the hood, the same engine that powers Google Chrome, Microsoft Edge, and many other modern browsers.

This means your application can display:

  • Modern websites (HTML5, CSS3, JavaScript).
  • Complex web applications (like Google Docs or Figma).
  • Custom HTML, CSS, and JavaScript that you generate yourself.

It's part of the PyQt6WebEngine or PySide6WebEngine module, which you need to install separately.


Installation

First, you need to install the base Qt library and the WebEngine component.

如何在Python中使用QWebEngine?-图2
(图片来源网络,侵删)

For PyQt6:

pip install PyQt6 PyQt6-WebEngine

For PySide6:

pip install PySide6 PySide6-WebEngine

Basic Example: Displaying a URL

This is the simplest use case: creating a window that loads and displays a website.

import sys
from PyQt6.QtWidgets import QApplication, QMainWindow, QWebEngineView
from PyQt6.QtCore import QUrl
class BrowserWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Simple Browser")
        self.setGeometry(100, 100, 1024, 768)
        # Create the QWebEngineView widget
        self.browser = QWebEngineView()
        # Load a URL
        self.browser.setUrl(QUrl("https://www.python.org"))
        # Set the browser as the central widget of the window
        self.setCentralWidget(self.browser)
if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = BrowserWindow()
    window.show()
    sys.exit(app.exec())

To run this: Save it as simple_browser.py and execute it from your terminal. You'll see a window with the Python website rendered perfectly.


Loading Local HTML Files

Often, you'll want to load content from your application, not just from the internet. You can do this by providing a local file path.

Let's create a simple HTML file named index.html:

<!-- index.html -->
<!DOCTYPE html>
<html>
<head>Local Page</title>
    <style>
        body { font-family: sans-serif; text-align: center; padding-top: 50px; }
        h1 { color: #333; }
        button { padding: 10px 20px; font-size: 16px; cursor: pointer; }
    </style>
</head>
<body>
    <h1>Hello from a Local HTML File!</h1>
    <p>This page is loaded directly from your application.</p>
    <button id="myButton">Click Me</button>
    <script>
        document.getElementById('myButton').addEventListener('click', function() {
            alert('Button was clicked!');
        });
    </script>
</body>
</html>

Now, modify the Python script to load this local file:

# ... (imports and BrowserWindow class from above)
class BrowserWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Local HTML Viewer")
        self.setGeometry(100, 100, 800, 600)
        self.browser = QWebEngineView()
        # Load the local file
        # Use QUrl.fromLocalFile to correctly format the path
        local_file_url = QUrl.fromLocalFile("/path/to/your/index.html") # <-- IMPORTANT: Change this path
        self.browser.setUrl(local_file_url)
        self.setCentralWidget(self.browser)
# ... (main execution block)

Key Point: Use QUrl.fromLocalFile() to ensure the path is correctly formatted for the web engine.


Communication: Python ↔ JavaScript

This is where QWebEngineView becomes incredibly powerful. You can call Python functions from JavaScript and vice-versa.

A. Calling Python from JavaScript

You can inject a Python object into the web page's JavaScript context. All of its public methods become callable from JavaScript.

import sys
from PyQt6.QtWidgets import QApplication, QMainWindow, QWebEngineView, QVBoxLayout, QWidget, QLabel
from PyQt6.QtCore import QUrl
class Communicator:
    @pyqtSlot(str) # Use the @pyqtSlot decorator for clarity and type safety
    def print_from_py(self, message):
        print(f"Message from JS: {message}")
    @pyqtSlot(int, int)
    def add_numbers(self, a, b):
        result = a + b
        print(f"JS asked to add {a} + {b} = {result}")
        return result
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("JS-Python Communication")
        self.setGeometry(100, 100, 800, 600)
        # Create the communicator object
        self.communicator = Communicator()
        self.browser = QWebEngineView()
        # Create a simple HTML page with JavaScript
        html_content = """
        <!DOCTYPE html>
        <html>
        <head><title>Comm Test</title></head>
        <body>
            <h1>Python-JS Communication</h1>
            <button id="btn1">Send Message to Python</button>
            <button id="btn2">Add Numbers (5 + 10)</button>
            <p id="output"></p>
            <script>
                document.getElementById('btn1').addEventListener('click', () => {
                    // Call the Python method
                    pythonObject.print_from_py("Hello from JavaScript!");
                    document.getElementById('output').innerText = "Message sent to Python console.";
                });
                document.getElementById('btn2').addEventListener('click', () => {
                    // Call the Python method and get a return value
                    let result = pythonObject.add_numbers(5, 10);
                    document.getElementById('output').innerText = `Result from Python: ${result}`;
                });
            </script>
        </body>
        </html>
        """
        self.browser.setHtml(html_content)
        # Inject the Python object into the JavaScript page
        # The name 'pythonObject' will be available in the page's JS context
        self.browser.page().runJavaScript(
            "window.pythonObject = { print_from_py: pyPrint, add_numbers: pyAdd };",
            self.inject_communicator
        )
        self.setCentralWidget(self.browser)
    def inject_communicator(self, result):
        # This callback is executed after the JS runs
        if result is None:
            print("Successfully injected Python object into JS.")
            # Now bind the actual Python methods to the JS names
            self.browser.page().runJavaScript(
                "pyPrint = (msg) => pythonObject.print_from_py(msg); pyAdd = (a, b) => pythonObject.add_numbers(a, b);"
            )
        else:
            print(f"Error injecting object: {result}")
if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

How it works:

  1. We create a Communicator class with Python methods.
  2. We load an HTML string that contains JavaScript.
  3. We use page().runJavaScript() to execute JavaScript code within the page.
  4. We inject a JavaScript object window.pythonObject and assign dummy functions (pyPrint, pyAdd) to it.
  5. We then use runJavaScript again to replace these dummy functions with references to our actual Python methods, effectively "binding" them. The @pyqtSlot decorator helps Qt understand the expected argument types.

B. Calling JavaScript from Python

This is much simpler. You just use the runJavaScript method.

# Inside your MainWindow class or wherever you have the browser instance
def call_js_function(self):
    js_code = "document.title = 'Title Changed by Python!';"
    self.browser.page().runJavaScript(js_code)
# You could connect this to a button click or a timer event

Important Considerations & Best Practices

  1. Threading (QWebEnginePage runs in a separate thread):

    • The web engine page runs in its own thread, separate from your main GUI thread.
    • You cannot directly access GUI elements (like QLabel, QPushButton) from a Python method called by JavaScript. This will lead to crashes or unpredictable behavior.
    • Solution: Use QMetaObject.invokeMethod to safely call methods in the main GUI thread from your Python callback.
    # In your Communicator class
    @pyqtSlot(str)
    def update_gui_label(self, message):
        # This is called from the web engine's thread!
        # We must invoke the update on the main GUI thread.
        label_to_update = self.parent().findChild(QLabel, "statusLabel")
        if label_to_update:
            QMetaObject.invokeMethod(label_to_update, "setText", Qt.ConnectionType.QueuedConnection, Q_ARG(str, message))
  2. Performance:

    • QWebEngineView is resource-intensive as it embeds a full browser engine. Be mindful of memory usage, especially if you load multiple pages or complex content.
    • For simple HTML/CSS without JavaScript, consider the older QTextBrowser or QWebEngineView with JavaScript disabled, as it will be much lighter.
  3. Permissions:

    • By default, it might ask for permissions like geolocation. You can handle these by subclassing QWebEngineProfile and QWebEnginePage and overriding the permission request methods.
  4. Qt Version:

    • The API can differ slightly between PyQt5/PySide5 and PyQt6/PySide6. The examples above use the modern PyQt6 syntax. If you're using PyQt5, replace pyqtSlot with @Slot and adjust some import paths.

Summary

Feature How to Use It Key Component/Method
Display Web Page Create QWebEngineView and call setUrl() with a QUrl. QWebEngineView, setUrl(), QUrl
Display Local HTML Use setHtml() with a string or setUrl() with QUrl.fromLocalFile(). setHtml(), QUrl.fromLocalFile()
Python → JavaScript Use page().runJavaScript(). page().runJavaScript()
JavaScript → Python Inject an object using page().runJavaScript() and bind its methods. page().runJavaScript(), @pyqtSlot
Cross-Thread GUI Update Use QMetaObject.invokeMethod to safely update GUI from a callback. QMetaObject.invokeMethod

QWebEngineView is a fantastic tool for building modern, feature-rich desktop applications that leverage the power of web technologies. It's perfect for creating embedded dashboards, documentation viewers, or full-fledged applications with a web-based UI.

分享:
扫描分享到社交APP
上一篇
下一篇