python QT5运行Edge

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python QT5运行Edge相关的知识,希望对你有一定的参考价值。

参考技术A 您是想问pythonQT5怎么运行Edge吗?pythonQT5运行Edge的方法:
1、查看Edge浏览器版本。(在浏览器的设置中找到关于MicroEdge。
2、下载浏览器驱动。
3、将下载好的压缩包解压,然后将其中的.exe文件分别复制到浏览器和python的安装路径下。
4、利用pythonQT5的脚本语言,指定浏览器的驱动即可。

Qt系列:调用Edge浏览器示例

背景

需要解决以下几个问题

  1. 政府项目新浏览器兼容老系统ActiveX控件,Qt WebEngineView没有直接的实现方案,需要利用Qt的ActiveX兼容模块与浏览器往返多次交互
  2. Qt ActiveX未实现COM事件通知
  3. 官方Win32示例存在滥用lambda函数的嫌疑,lambda函数多层嵌套,程序逻辑层次混乱,整个逻辑被揉成一垛。

官方示例代码

官方介绍文档在这里:https://learn.microsoft.com/microsoft-edge/webview2/get-started/win32。官方代码仓库在这里:GitHub - MicrosoftEdge/WebView2Samples: Microsoft Edge WebView2 samples

摘录一段lambda多层嵌套的代码,你们体会一下:

	CreateCoreWebView2EnvironmentWithOptions(nullptr, nullptr, nullptr,
		Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
			[hWnd](HRESULT result, ICoreWebView2Environment* env) -> HRESULT 

				// Create a CoreWebView2Controller and get the associated CoreWebView2 whose parent is the main window hWnd
				env->CreateCoreWebView2Controller(hWnd, Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
					[hWnd](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT 
						if (controller != nullptr) 
							webviewController = controller;
							webviewController->get_CoreWebView2(&webview);
						

						// Add a few settings for the webview
						// The demo step is redundant since the values are the default settings
						wil::com_ptr<ICoreWebView2Settings> settings;
						webview->get_Settings(&settings);
						settings->put_IsScriptEnabled(TRUE);
						settings->put_AreDefaultScriptDialogsEnabled(TRUE);
						settings->put_IsWebMessageEnabled(TRUE);

						// Resize WebView to fit the bounds of the parent window
						RECT bounds;
						GetClientRect(hWnd, &bounds);
						webviewController->put_Bounds(bounds);

						// Schedule an async task to navigate to Bing
						webview->Navigate(L"https://www.bing.com/");

						// <NavigationEvents>
						// Step 4 - Navigation events
						// register an ICoreWebView2NavigationStartingEventHandler to cancel any non-https navigation
						EventRegistrationToken token;
						webview->add_NavigationStarting(Callback<ICoreWebView2NavigationStartingEventHandler>(
							[](ICoreWebView2* webview, ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT 
								wil::unique_cotaskmem_string uri;
								args->get_Uri(&uri);
								std::wstring source(uri.get());
								if (source.substr(0, 5) != L"https") 
									args->put_Cancel(true);
								
								return S_OK;
							).Get(), &token);
						// </NavigationEvents>

						// <Scripting>
						// Step 5 - Scripting
						// Schedule an async task to add initialization script that freezes the Object object
						webview->AddScriptToExecuteOnDocumentCreated(L"Object.freeze(Object);", nullptr);
						// Schedule an async task to get the document URL
						webview->ExecuteScript(L"window.document.URL;", Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
							[](HRESULT errorCode, LPCWSTR resultObjectAsJson) -> HRESULT 
								LPCWSTR URL = resultObjectAsJson;
								//doSomethingWithURL(URL);
								return S_OK;
							).Get());
						// </Scripting>

						// <CommunicationHostWeb>
						// Step 6 - Communication between host and web content
						// Set an event handler for the host to return received message back to the web content
						webview->add_WebMessageReceived(Callback<ICoreWebView2WebMessageReceivedEventHandler>(
							[](ICoreWebView2* webview, ICoreWebView2WebMessageReceivedEventArgs* args) -> HRESULT 
								wil::unique_cotaskmem_string message;
								args->TryGetWebMessageAsString(&message);
								// processMessage(&message);
								webview->PostWebMessageAsString(message.get());
								return S_OK;
							).Get(), &token);

						// Schedule an async task to add initialization script that
						// 1) Add an listener to print message from the host
						// 2) Post document URL to the host
						webview->AddScriptToExecuteOnDocumentCreated(
							L"window.chrome.webview.addEventListener(\\'message\\', event => alert(event.data));" \\
							L"window.chrome.webview.postMessage(window.document.URL);",
							nullptr);
						// </CommunicationHostWeb>

						return S_OK;
					).Get());
				return S_OK;
			).Get());

解决方案

下面以实现自动登录外网网关为目标,企业微信自动上线,免开机输入账号密码。这样领导看到你上线,可以开机以后就可以慢慢吃早餐了。(开个玩笑)

本实现方案把官方示例代码做成了静态库,没有添加其它东西。

新建CMake项目

这里用到了Qt5静态库,目的是单文件可执行,不需要部署。需要静态库的读者可以自行删除。代码如下:

cmake_minimum_required(VERSION 3.21)
project(auto-online CXX)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOMOC ON)

add_definitions(/D_UNICODE /DUNICODE)
add_compile_definitions(WIN32 _WINDOWS)

add_compile_options(/utf-8 $<IF:$<CONFIG:Debug>,/MTd,/MT>)
link_directories($ENVQt5_DIR/lib)
link_directories($ENVQt5_DIR/plugins/platforms)
link_directories($ENVQt5_DIR/plugins/imageformats)
link_libraries(UxTheme Winmm Version ws2_32 imm32 dwmapi)

link_libraries($<IF:$<CONFIG:Debug>,Qt5FontDatabaseSupportd,Qt5FontDatabaseSupport>)
link_libraries($<IF:$<CONFIG:Debug>,Qt5UiToolsd,Qt5UiTools>)
link_libraries($<IF:$<CONFIG:Debug>,Qt5AccessibilitySupportd,Qt5AccessibilitySupport>)
link_libraries($<IF:$<CONFIG:Debug>,Qt5EventDispatcherSupportd,Qt5EventDispatcherSupport>)
link_libraries($<IF:$<CONFIG:Debug>,Qt5ThemeSupportd,Qt5ThemeSupport>)
link_libraries($<IF:$<CONFIG:Debug>,Qt5UiToolsd,Qt5UiTools>)

link_libraries($<IF:$<CONFIG:Debug>,qtpcre2d,qtpcre2>)
link_libraries($<IF:$<CONFIG:Debug>,qtlibpngd,qtlibpng>)
link_libraries($<IF:$<CONFIG:Debug>,qtharfbuzzd,qtharfbuzz>)
link_libraries($<IF:$<CONFIG:Debug>,qtfreetyped,qtfreetype>)
link_libraries($<IF:$<CONFIG:Debug>,qwindowsd,qwindows>)
link_libraries($<IF:$<CONFIG:Debug>,qicnsd,qicns>)
link_libraries($<IF:$<CONFIG:Debug>,qtgad,qtga>)
link_libraries($<IF:$<CONFIG:Debug>,qtiffd,qtiff>)
link_libraries($<IF:$<CONFIG:Debug>,qwbmpd,qwbmp>)
link_libraries($<IF:$<CONFIG:Debug>,qtiffd,qtiff>)
link_libraries($<IF:$<CONFIG:Debug>,qwebpd,qwebp>)
link_libraries($<IF:$<CONFIG:Debug>,qgifd,qgif>)
link_libraries($<IF:$<CONFIG:Debug>,qjpegd,qjpeg>)
link_libraries($<IF:$<CONFIG:Debug>,qicod,qico>)

message("-- Qt5_DIR: " $ENVQt5_DIR)
find_package(Qt5 COMPONENTS Core Gui Widgets Network REQUIRED)

include_directories($CMAKE_SOURCE_DIR/3rdparty/webview2loader/include)
include_directories($CMAKE_SOURCE_DIR/3rdparty/wil/include)
link_directories($CMAKE_SOURCE_DIR/3rdparty/webview2loader/lib)
link_libraries(Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Network WebView2LoaderStatic)

file(GLOB SRCS *.ui *.cpp *.h)
add_executable($PROJECT_NAME WIN32 $SRCS)
set_directory_properties(PROPERTIES VS_STARTUP_PROJECT $PROJECT_NAME)
# 环境变量的路径不能带双引号
# message("VCINSTALLDIR: " $ENVVCINSTALLDIR)
# find_file(VSPATH NAMES "vcruntime140d.dll" PATHS $ENVVCINSTALLDIR  REQUIRED NO_DEFAULT_PATH)
# file(TO_NATIVE_PATH $VSPATH VSPATH) 
#message("VC CRT PATH: " $VSPATH)
# set(VSPATH $<IF:$<CONFIG:Debug>,$ENVVCINSTALLDIR/Redist/MSVC/14.29.30133/onecore/debug_nonredist/x64/Microsoft.VC142.DebugCRT,$ENVVCINSTALLDIR/Redist/MSVC/14.29.30133/x64/Microsoft.VC142.CRT> CACHE STRING "VCRT" FORCE)
# string(CONCAT VSPATH $VSPATH ";$ENVQt5_DIR\\\\bin")

set_target_properties($PROJECT_NAME PROPERTIES
    VS_DEBUGGER_WORKING_DIRECTORY $CMAKE_BINARY_DIR
    VS_DEBUGGER_ENVIRONMENT "Path=$ENVQt5_DIR\\\\bin"
    WIN32_EXECUTABLE TRUE)

用Qt Designer设计一个简单的窗口类型

类型名字很简单,就是MainWindow。这个窗口仅启用了布局,没有添加任何控件。UI代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="central_widget">
   <layout class="QVBoxLayout" name="vl1">
    <property name="spacing">
     <number>0</number>
    </property>
    <property name="leftMargin">
     <number>0</number>
    </property>
    <property name="topMargin">
     <number>0</number>
    </property>
    <property name="rightMargin">
     <number>0</number>
    </property>
    <property name="bottomMargin">
     <number>0</number>
    </property>
    <item>
     <widget class="QWidget" name="browser_widget" native="true">
      <layout class="QVBoxLayout" name="vl2"/>
     </widget>
    </item>
   </layout>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

声明信号槽

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QThread>

#include <windows.h>
#include <stdlib.h>
#include <string>
#include <tchar.h>
#include "wrl.h"
#include "wil/com.h"
#include "WebView2.h"

#define PROFILE_DATA "cw_webview2"

namespace Ui 
class MainWindow;


class MainWindow : public QMainWindow

    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    void load_webview2(HWND hWnd);
    void task_run();

    HRESULT cb_create_environment(HWND, HRESULT, ICoreWebView2Environment *);
    HRESULT cb_create_controller(HWND, HRESULT, ICoreWebView2Controller *);
    HRESULT on_navigate_started(ICoreWebView2 *, ICoreWebView2NavigationStartingEventArgs *);
    HRESULT cb_execute_script(HRESULT, LPCWSTR);
    HRESULT on_webmessage_received(ICoreWebView2 *, ICoreWebView2WebMessageReceivedEventArgs *);
    HRESULT on_navigate_completed(ICoreWebView2*, ICoreWebView2NavigationCompletedEventArgs*);
    // HRESULT cb_deliver(HWND, HRESULT, ...);

signals:
    void prepare();
    void load_auth_page();
    void put_name();
    void put_password();
    void click_login();
    void wait(int iminute);

protected:
    void resizeEvent(QResizeEvent* evt) override;
    void closeEvent(QCloseEvent*) override;

protected slots:
    void on_prepare();
    void on_load_auth_page();
    void on_put_name();
    void on_put_password();
    void on_click_login();
    void on_wait(int iminute);

private:
    Ui::MainWindow *ui;
    // Pointer to WebViewController
    wil::com_ptr<ICoreWebView2Controller> m_controller;
    bool m_brunning = false;
    QThread* m_task;

    // Pointer to WebView window
    wil::com_ptr<ICoreWebView2_15> m_webview;
    Microsoft::WRL::ComPtr<ICoreWebView2ExecuteScriptCompletedHandler> js_cb;
    Microsoft::WRL::ComPtr<ICoreWebView2WebMessageReceivedEventHandler> msg_cb;
;

#endif // MAINWINDOW_H

在合适的时机填入账号密码

本来想做成弹框让用户输入账号密码,忽然发现这种做法对信息部门的同事不太友好,各位读者如有请自行修改。示例代码中的网关地址、用户名和密码是乱写的。

#include "MainWindow.h"
#include "ui_MainWindow.h"

#include <QResizeEvent>
#include <QRect>
#include <QDebug>
#include <QStandardPaths>
#include <QDir>
#include <QMessageBox>
#include <QTimer>
#include <QApplication>

using namespace Microsoft::WRL;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)

    ui->setupUi(this);
    load_webview2((HWND)ui->browser_widget->winId());
    setWindowTitle(u8"自动连接外网工具");
    connect(this, SIGNAL(prepare()), this, SLOT(on_prepare()), Qt::QueuedConnection);
    connect(this, SIGNAL(load_auth_page()), this, SLOT(on_load_auth_page()), Qt::QueuedConnection);
    connect(this, SIGNAL(put_name()), this, SLOT(on_put_name()), Qt::QueuedConnection);
    connect(this, SIGNAL(put_password()), this, SLOT(on_put_password()), Qt::QueuedConnection);
    connect(this, SIGNAL(click_login()), this, SLOT(on_click_login()), Qt::QueuedConnection);
    connect(this, SIGNAL(wait(int)), this, SLOT(on_wait(int)), Qt::QueuedConnection);


MainWindow::~MainWindow()

    delete ui;


HRESULT MainWindow::on_navigate_started(ICoreWebView2 *webview,
                                        ICoreWebView2NavigationStartingEventArgs *args)

    wil::unique_cotaskmem_string uri;
    args->get_Uri(&uri);
    QString zurl = QString::fromStdWString(uri.get());
    // std::wstring source(uri.get());
    // if (source.substr(0, 5) != L"https")
    // 
    //     args->put_Cancel(true);
    // 
    qInfo().noquote() << "navigate: " << zurl;
    return S_OK;



HRESULT MainWindow::cb_execute_script(HRESULT errorCode, LPCWSTR resultObjectAsJson)

    LPCWSTR URL = resultObjectAsJson;
    //doSomethingWithURL(URL);
    qInfo() << "executed javascript.";
    return S_OK;


HRESULT MainWindow::on_webmessage_received(ICoreWebView2 *webview,
                                           ICoreWebView2WebMessageReceivedEventArgs *args)

    wil::unique_cotaskmem_string message;
    args->TryGetWebMessageAsString(&message);
    // processMessage(&message);
    //webview->PostWebMessageAsString(message.get());
    QString zmsg = QString::fromStdWString(message.get());
    qInfo().noquote() << "message: " << zmsg;
    return S_OK;


HRESULT MainWindow::on_navigate_completed(ICoreWebView2 *webview,
                                          ICoreWebView2NavigationCompletedEventArgs *args)

    BOOL bsuccess;
    COREWEBVIEW2_WEB_ERROR_STATUS ierror;
    args->get_IsSuccess(&bsuccess);
    args->get_WebErrorStatus(&ierror);
    LPWSTR lpuri;
    webview->get_Source(&lpuri);
    QString zuri = QString::fromStdWString(lpuri);
    qInfo().noquote() << "complate : " << bsuccess << ", " << ierror << ", " << zuri;
    return S_OK;


void MainWindow::resizeEvent(QResizeEvent *evt)

    if (!m_controller)
        return;
    RECT rc;
    QRect qrc =  ui->browser_widget->rect();
    rc.left = qrc.left();
    rc.top = qrc.top();
    rc.right = qrc.right();
    rc.bottom = qrc.bottom();
    m_controller->put_Bounds(rc);


void MainWindow::closeEvent(QCloseEvent *)

    m_brunning = false;
    m_task->terminate();
    QThread::sleep(1);
    m_task->deleteLater();
    m_task = nullptr;


void MainWindow::on_prepare()

    if (!m_webview)
        return;
    m_webview->NavigateToString(L"Preparing...");


void MainWindow::on_load_auth_page()

    if (!m_webview)
        return;
    m_webview->Navigate(L"http://1.1.1.3/ac_portal/default/pc.html?tabs=pwd");
    qInfo() << "begin load auth page";


void MainWindow::on_put_name()

    if (!m_webview)
        return;
    m_webview->ExecuteScript(L"$('#password_name').val('test1');"
                             "window.chrome.webview.postMessage('put name ok.');",
                             js_cb.Get());


void MainWindow::on_put_password()

    if (!m_webview)
        return;
    m_webview->ExecuteScript(L"$('#password_pwd').val('123456');"
                             "window.chrome.webview.postMessage('put password ok.');",
                             js_cb.Get());


void MainWindow::on_click_login()

    if (!m_webview)
        return;
    m_webview->ExecuteScript(L"$('#password_submitBtn').click();"
                             "window.chrome.webview.postMessage('click login ok.');",
                             js_cb.Get());


void MainWindow::on_wait(int iminute)

    QString zhtml = QString("Already wairted %1 minutes.").arg(iminute);
    if (!m_webview)
        return;
    m_webview->NavigateToString(zhtml.toStdWString().c_str());


HRESULT MainWindow::cb_create_controller(HWND hWnd, HRESULT result,
                                         ICoreWebView2Controller *controller)

    if (!controller)
        return E_POINTER;

    m_controller = controller;
    wil::com_ptr<ICoreWebView2> webview;
    HRESULT ir =  m_controller->get_CoreWebView2(&webview);
    if (FAILED(ir))
        return ir;
    ir = webview->QueryInterface(IID_ICoreWebView2_15, (void **)&m_webview);
    if (FAILED(ir))
        return ir;

    // Add a few settings for the webview
    // The demo step is redundant since the values are the default settings
    wil::com_ptr<ICoreWebView2Settings> settings;
    m_webview->get_Settings(&settings);
    settings->put_IsScriptEnabled(TRUE);
    settings->put_AreDefaultScriptDialogsEnabled(TRUE);
    settings->put_IsWebMessageEnabled(TRUE);

    // Resize WebView to fit the bounds of the parent window
    RECT bounds;
    GetClientRect(hWnd, &bounds);
    m_controller->put_Bounds(bounds);

    m_webview->NavigateToString(L"Preparing...");

    // <NavigationEvents>
    // Step 4 - Navigation events
    // register an ICoreWebView2NavigationStartingEventHandler to cancel any non-https navigation
    EventRegistrationToken token;
    auto nav_func = std::bind(&MainWindow::on_navigate_started, this, std::placeholders::_1,
                              std::placeholders::_2);
    auto nav_cb = Callback<ICoreWebView2NavigationStartingEventHandler>(nav_func);
    m_webview->add_NavigationStarting(nav_cb.Get(), &token);

    auto com_func = std::bind(&MainWindow::on_navigate_completed, this, std::placeholders::_1,
                              std::placeholders::_2);
    auto com_cb = Callback<ICoreWebView2NavigationCompletedEventHandler>(com_func);
    m_webview->add_NavigationCompleted(com_cb.Get(), &token);
    // </NavigationEvents>

    // <Scripting>
    // Step 5 - Scripting
    // Schedule an async task to add initialization script that freezes the Object object
    // 注入脚本
    //webview->AddScriptToExecuteOnDocumentCreated(L"Object.freeze(Object);", nullptr);
    // Schedule an async task to get the document URL
    auto js_func = std::bind(&MainWindow::cb_execute_script, this, std::placeholders::_1,
                             std::placeholders::_2);
    js_cb = Callback<ICoreWebView2ExecuteScriptCompletedHandler>(js_func);
    //webview->ExecuteScript(L"window.document.URL;", js_cb.Get());
    // </Scripting>

    // <CommunicationHostWeb>
    // Step 6 - Communication between host and web content
    // Set an event handler for the host to return received message back to the web content
    auto msg_func = std::bind(&MainWindow::on_webmessage_received, this, std::placeholders::_1,
                              std::placeholders::_2);
    msg_cb = Callback<ICoreWebView2WebMessageReceivedEventHandler>(msg_func);
    m_webview->add_WebMessageReceived(msg_cb.Get(), &token);
    // Schedule an async task to add initialization script that
    // 1) Add an listener to print message from the host
    // 2) Post document URL to the host
    // 注入脚本
    //webview->AddScriptToExecuteOnDocumentCreated(
    //    L"window.chrome.webview.addEventListener(\\'message\\', event => alert(event.data));" \\
    //    L"window.chrome.webview.postMessage(window.document.URL);",
    //    nullptr);
    // </CommunicationHostWeb>
    m_task = QThread::create(&MainWindow::task_run, this);
    m_task->setParent(this);
    m_task->start();
    return S_OK;


void MainWindow::load_webview2(HWND hWnd)

    MainWindow *obj = this;
    auto func = &MainWindow::cb_create_environment;
    auto fn = [hWnd, obj, func](HRESULT result, ICoreWebView2Environment * env)->HRESULT
    
        return (obj->*func)(hWnd, result, env);
    ;
    auto cb = Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(fn);
    QDir d(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
    auto ztemp = d.absoluteFilePath(PROFILE_DATA).toStdWString();
    HRESULT ir = CreateCoreWebView2EnvironmentWithOptions(nullptr, ztemp.c_str(), nullptr, cb.Get());
    if (FAILED(ir))
    
        qCritical() << "create webview2 failed. code: " << ir;
        QTimer::singleShot(3000, []()
        
            QApplication::quit();
            _Exit(1);
        );
        QMessageBox::warning(this,
                             windowTitle(),
                             u8"严重错误。程序即将退出。\\n"
                             "请检查Microsoft Edge WebView2 Runtime是否存在。\\n");
    


void MainWindow::task_run()

    if (!m_controller || !m_webview)
        return;
    m_brunning = true;
    while (m_brunning)
    
        emit on_prepare();
        QThread::sleep(3);
        if (!m_brunning)
            break;
        emit load_auth_page();
        QThread::sleep(10);
        if (!m_brunning)
            break;
        emit put_name();
        QThread::sleep(3);
        if (!m_brunning)
            break;
        emit put_password();
        QThread::sleep(3);
        if (!m_brunning)
            break;
        emit click_login();
        for (int i = 0; i < 10; i++)
        
            QThread::sleep(60);
            if (!m_brunning)
                break;
            emit wait(i + 1);
        
    


HRESULT MainWindow::cb_create_environment(HWND hWnd, HRESULT result, ICoreWebView2Environment *env)

    MainWindow *obj = this;
    auto func = &MainWindow::cb_create_controller;
    auto fn = [hWnd, obj, func](HRESULT result, ICoreWebView2Controller * controller)->HRESULT
    
        return (obj->*func)(hWnd, result, controller);
    ;
    // Create a CoreWebView2Controller and get the associated CoreWebView2 whose parent is the main window hWnd
    auto cb = Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(fn);
    HRESULT hr = env->CreateCoreWebView2Controller(hWnd, cb.Get());
    return hr;


免责声明

此代码仅供技术研究娱乐之用,禁止用于商业用途,否则一切后果自负。

作者:岬淢箫声

岬淢箫声的博客_CSDN博客-C/C++,MFC/VC,桌面H5领域博主https://caowei.blog.csdn.net/

转载请注明来源

以上是关于python QT5运行Edge的主要内容,如果未能解决你的问题,请参考以下文章

由于找不到qt5gui.dll,无法继续执行代码有影响吗

如何在我的 Python Qt5 UI 后端执行代码时显示加载动画 gif?

未找到 Matplotlib Qt5Agg 后端

Qt5 在 Python 脚本中调用函数

Python+Qt5实现flappybird

Python3 Qt5 主窗口