1 Star 0 Fork 0

linglingier / webview-tab

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
app.py 19.37 KB
一键复制 编辑 原始数据 按行查看 历史
linglingier 提交于 2023-10-21 22:58 . 优化
# ! /usr/bin/python
# -*- coding: utf-8 -*-
# author:凌
# datetime:2023/10/19 09:48
# software:PyCharm
# pip install pyqt6-webengine
import sys
import os
from PyQt6.QtNetwork import QNetworkCookie
from PyQt6.QtWebEngineCore import QWebEngineSettings, QWebEngineProfile, QWebEngineUrlRequestInterceptor, QWebEnginePage
from PyQt6.QtWidgets import QApplication, QSystemTrayIcon, QMenu, QToolBar, QTabWidget
from PyQt6.QtGui import QGuiApplication, QIcon, QAction
from PyQt6.QtCore import Qt, QUrl, QPropertyAnimation, QEasingCurve
from PyQt6.QtWebEngineWidgets import QWebEngineView
import urllib.parse
from lib.Conf import Conf
from lib.RelateWin import RMainWindow
from sysconf import Sysconf
import json
from test_web import TabWidget
class WebEngineUrlRequestInterceptor(QWebEngineUrlRequestInterceptor):
def __init__(self, parent=None):
super().__init__(parent)
def interceptRequest(self, info):
url = info.requestUrl()
# print(url.url())
# 定义一个标签页控件类,继承自QTabWidget
class TabWidget(QTabWidget):
# 定义一个创建新标签页的方法,接收一个可选的网址参数,默认为Bing首页
def new_tab(self, url):
# 创建一个网页视图控件
webview = MyWebEngineView()
# 创建一个自定义的网页对象,并设置给网页视图控件
webpage = CustomWebEnginePage(webview)
webview.setPage(webpage)
print("load new tab")
if url is False:
url = QUrl("https://www.baidu.com/")
# 加载网址
webview.load(url)
webpage.linkHovered.connect(self.linkUrl)
# 获取当前标签页的索引
index = self.addTab(webview, "加载中...")
# 设置当前标签页为新打开的标签页
self.setCurrentIndex(index)
# 设置网页加载完成时的信号槽函数,用于更新标签页的标题和图标
webview.loadFinished.connect(lambda: self.update_tab(index))
# 返回网页视图控件,供其他地方使用
return webview
def linkUrl(self, url):
# print("change url =>%s" % url)
self.parent().current_url = url
# 定义一个更新标签页的方法,接收一个索引参数
def update_tab(self, index):
# 根据索引获取对应的网页视图控件和标题
webview = self.widget(index)
title = webview.page().title()
# 设置标签页的标题为网页标题,如果标题为空,则设置为网址
if title:
self.setTabText(index, title)
else:
self.setTabText(index, webview.url().toString())
# 设置标签页的图标为网页图标
self.setTabIcon(index, webview.icon())
# 定义一个关闭标签页的方法,接收一个索引参数
def close_tab(self, index):
# 根据索引获取对应的网页视图控件,并删除它
webview = self.widget(index)
webview.deleteLater()
# 移除对应的标签页
self.removeTab(index)
# 如果标签页的数量为0,则关闭窗口
if self.count() == 0:
self.window().close()
# 重写创建窗口的方法,用于处理_blank目标的链接
def createWindow(self, type_):
print("create window", end=" ")
print(self.parent().parent().parent().current_url)
# 如果是在新标签页中打开链接,则调用祖父控件的创建新标签页的方法,并返回新创建的网页视图控件
if type_ == QWebEnginePage.WebWindowType.WebBrowserTab:
tab_widget = self.parent().parent().parent()
url = QUrl(self.parent().parent().parent().current_url)
return tab_widget.new_tab(url)
# 否则,返回None,不创建窗口
return None
# 定义一个自定义的网页类,继承自QWebEnginePage
class CustomWebEnginePage(QWebEnginePage):
# 重写接受导航请求的方法,用于自定义链接的处理方式
def acceptNavigationRequest(self, url, _type, isMainFrame):
# 如果是用户点击了链接,并且链接的主机名和当前网页不同,则打开新的标签页
if _type == QWebEnginePage.NavigationType.NavigationTypeLinkClicked and url.host() != self.url().host():
# 获取父控件,即网页视图控件
webview = self.parent()
# 获取祖父控件,即标签页控件
tab_widget = webview.parent()
# 调用标签页控件的创建新标签页的方法,并传入链接地址
tab_widget.new_tab(url)
# 返回False,拒绝默认的导航行为
return False
# 否则,返回True,接受默认的导航行为
return True
class MyWebEngineView(QWebEngineView):
def __init__(self, parent=None):
super().__init__(parent)
self.urlChanged.connect(self.onUrlChange)
def onUrlChange(self, url):
result = urllib.parse.urlparse(url.toString())
query_dict = urllib.parse.parse_qs(result.query)
page = self.parent()
if 'ps' in query_dict:
ps = query_dict['ps'][0].split("X")
page.resize(int(ps[0]), int(ps[1]))
page.center()
if 'anim' in query_dict or 'ps' in query_dict:
print("跳转动画")
page.animOut()
page.animIn()
if 'closewin' in query_dict:
print("关闭")
page.closeWin()
self.stop()
if 'minwin' in query_dict:
print("最小化")
page.minWindow()
self.stop()
if 'maxwin' in query_dict:
print("最大化")
page.maxWindow()
self.stop()
# def contextMenuEvent(self, event):
# return None
class App(RMainWindow):
def __init__(self):
super().__init__()
self.loadIndex = 0
self.conf_action = None
self.sysconf = Sysconf()
self.quit_action = None
self.hide_action = None
self.show_action = None
self.tray_menu = None
self.tray_icon = None
self.t = None
self.page = None
self.view = None
self.cookie_file = None
self.localStorage_path = None
# 系统托盘
self.setTray()
# 创建一个属性动画对象,指定目标对象为self,属性为windowOpacity
self.anim = QPropertyAnimation(self, b"windowOpacity")
self.current_url = ""
self.setupBrowser()
self.initUI()
def setupBrowser(self):
# 设置窗口标题和图标
self.setWindowTitle("浏览器")
self.setWindowIcon(QIcon("logo.png"))
# 创建一个标签页控件
self.tabs = TabWidget()
# 设置标签页可以关闭和拖动
self.tabs.setTabsClosable(True)
self.tabs.setMovable(True)
# 设置标签页关闭时的信号槽函数
self.tabs.tabCloseRequested.connect(self.tabs.close_tab)
# 设置窗口的中心部件为标签页控件
self.setCentralWidget(self.tabs)
# 创建一个工具栏
self.toolbar = QToolBar()
# 设置工具栏不可移动
self.toolbar.setMovable(False)
# 添加工具栏到窗口
self.addToolBar(self.toolbar)
# 创建一个后退按钮,并添加到工具栏
self.back_button = QAction(QIcon("./images/back.png"), "后退", self)
self.back_button.triggered.connect(self.back)
self.toolbar.addAction(self.back_button)
# 创建一个前进按钮,并添加到工具栏
self.forward_button = QAction(QIcon("./images/go.png"), "前进", self)
self.forward_button.triggered.connect(self.forward)
self.toolbar.addAction(self.forward_button)
# 创建一个刷新按钮,并添加到工具栏
self.reload_button = QAction(QIcon("./images/reload.png"), "刷新", self)
self.reload_button.triggered.connect(self.reload)
self.toolbar.addAction(self.reload_button)
# 创建一个新标签页按钮,并添加到工具栏
self.new_tab_button = QAction(QIcon("./images/add.png"), "新标签页", self)
self.new_tab_button.triggered.connect(self.new_tab)
self.toolbar.addAction(self.new_tab_button)
# 打开一个新的标签页
self.new_tab()
def initUI(self):
self.setWindowTitle("PyQt6 WebView")
# Qt.WindowType.FramelessWindowHint 无标题栏
# Qt.WindowType.CustomizeWindowHint
# self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
# url = "http://qlcms.jiuaitu.com/test.html"
conf = Conf("./conf.ini")
cf = conf.getInstance()
url = cf.get("settings", "url")
# url = "http://admins.com/test.html"
# url = "https://www.baidu.com/"
self.view = MyWebEngineView()
self.page = CustomWebEnginePage()
# 禁用右键菜单
# self.view.contextMenuEventPolicy = Qt.ContextMenuPolicy.NoContextMenu
# 安装事件过滤器,防止 child 接收鼠标键盘事件信息
# self.view.installEventFilter(self)
settings = self.page.settings()
settings.setAttribute(QWebEngineSettings.WebAttribute.LocalStorageEnabled, True)
profile = self.page.profile()
profile.setCachePath("cache")
profile.setPersistentStoragePath("storage")
profile.setPersistentCookiesPolicy(
QWebEngineProfile.PersistentCookiesPolicy.ForcePersistentCookies) # 设置会话和持久化cookie均保存到磁盘或从磁盘还原
profile.setHttpCacheType(QWebEngineProfile.HttpCacheType.DiskHttpCache) # 设置使用磁盘缓存
# 拦截请求信息
self.t = WebEngineUrlRequestInterceptor()
profile.setUrlRequestInterceptor(self.t)
# 自动保存cookie
self.cookie_file = "./cache/cookie.txt"
self.loadCookie()
profile.cookieStore().cookieAdded.connect(self.saveCookie)
self.view.loadFinished.connect(self.endLoadPage)
self.page.setUrl(QUrl(url))
self.view.setPage(self.page)
self.view.resize(800, 600)
self.setCentralWidget(self.view)
self.setGeometry(100, 100, 800, 600)
# self.view.setWindowState(Qt.WindowState.WindowMaximized)
# self.setWindowState(Qt.WindowState.WindowMaximized)
self.center()
def endLoadPage(self, ok):
print("end page")
# self.view.page().runJavaScript("console.log(localStorage);")
if ok:
# 设置一个名为name的localStorage项,值为"John"
# self.page.runJavaScript("localStorage.setItem('name', 'John');")
# print("self.loadIndex => %d" % self.loadIndex)
if self.loadIndex < 1:
self.loadLocalStorage()
local_js = '''
var json_obj = {};
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i); // 获取本地存储的Key
var value = localStorage.getItem(key); // 获取本地存储的Value
json_obj[key] = JSON.parse(value)
}
json_obj;
'''
# 获取名为name的localStorage项的值,并打印到控制台
# self.page.runJavaScript("localStorage.getItem('layuiAdmin')", self.saveLocalStorage)
self.page.runJavaScript(local_js, self.saveLocalStorage)
self.loadIndex += 1
@staticmethod
def saveLocalStorage(result):
# print("saveLocalStorage => %s " % result)
with open("./cache/localstorage.txt", "w+", encoding="utf-8") as f:
f.write(json.dumps(result))
def loadLocalStorage(self):
with open("./cache/localstorage.txt", "r", encoding="utf-8") as f:
result = f.read()
if result.strip() == "":
return
result_js = "json_obj = JSON.parse('" + result + "');"
result_js += '''
for(var p in json_obj){
localStorage.setItem(p,JSON.stringify(json_obj[p]))
}
'''
self.page.runJavaScript(result_js, self.saveLocalStorage)
def loadCookie(self):
if os.path.exists(self.cookie_file):
with open(self.cookie_file, "r", encoding="utf-8") as f:
# 读取所有行,返回字节串列表
lines = f.readlines()
# 遍历每一行
for line in lines:
# 将字节串转换为 cookie 列表
cookies = QNetworkCookie.parseCookies(line.encode("utf-8"))
# 遍历每一个 cookie
for cookie in cookies:
# 设置 cookie 到网页
self.view.page().profile().cookieStore().setCookie(cookie)
def saveCookie(self, cookie):
# 如果 cookie 的域名是 baidu.com
# if "baidu.com" in cookie.domain():
# 将 cookie 转换为字节串
cookie_str = cookie.toRawForm().data().decode("utf-8")
# 打开文件,追加模式
with open(self.cookie_file, "w+") as f:
# 写入文件,换行符分隔
f.write(cookie_str + "\n")
def animIn(self):
print("win in")
# 设置动画的持续时间为2000毫秒
self.anim.setDuration(500)
# 设置动画的起始值为1.0,即完全不透明
self.anim.setStartValue(0.0)
# 设置动画的结束值为0.0,即完全透明
self.anim.setEndValue(1.0)
# 设置动画的曲线类型为InOutQuad,即先加速后减速
self.anim.setEasingCurve(QEasingCurve.Type.InOutQuad)
self.anim.start()
def animOut(self):
print("win out")
# 设置动画的持续时间为2000毫秒
self.anim.setDuration(500)
# 设置动画的起始值为1.0,即完全不透明
self.anim.setStartValue(1.0)
# 设置动画的结束值为0.0,即完全透明
self.anim.setEndValue(0.0)
# 设置动画的曲线类型为InOutQuad,即先加速后减速
self.anim.setEasingCurve(QEasingCurve.Type.InOutQuad)
self.anim.start()
def center(self):
qr = self.frameGeometry()
cp = QGuiApplication.primaryScreen().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
def closeWin(self):
# 设置动画的持续时间为2000毫秒
self.anim.setDuration(500)
# 设置动画的起始值为1.0,即完全不透明
self.anim.setStartValue(1.0)
# 设置动画的结束值为0.0,即完全透明
self.anim.setEndValue(0.0)
# 设置动画的曲线类型为InOutQuad,即先加速后减速
self.anim.setEasingCurve(QEasingCurve.Type.InOutQuad)
self.anim.start()
self.anim.finished.connect(QApplication.quit)
def setTray(self):
# demo:创建一个系统托盘图标对象--
self.tray_icon = QSystemTrayIcon(self)
# 设置图标为一个png文件
self.tray_icon.setIcon(QIcon("logo_16.png"))
# 设置图标的提示信息
self.tray_icon.setToolTip("这是一个窗口系统托盘")
# 创建一个菜单对象,用于显示在系统托盘的右键菜单中
self.tray_menu = QMenu()
# 创建一些菜单项,用于执行不同的操作
self.conf_action = QAction("系统配置", triggered=self.sysconf.show)
self.show_action = QAction("显示窗口", triggered=self.showWindow)
self.hide_action = QAction("隐藏窗口", triggered=self.hideWindow)
self.quit_action = QAction("退出程序", triggered=QApplication.instance().quit)
# 将菜单项添加到菜单中
self.tray_menu.addAction(self.conf_action)
self.tray_menu.addAction(self.show_action)
self.tray_menu.addAction(self.hide_action)
self.tray_menu.addSeparator()
self.tray_menu.addAction(self.quit_action)
# 将菜单设置为系统托盘图标的右键菜单
self.tray_icon.setContextMenu(self.tray_menu)
print("tray show")
# 显示系统托盘图标
self.tray_icon.show()
def showWindow(self):
# 显示窗口,并将其激活为当前窗口
self.show()
self.activateWindow()
def hideWindow(self):
# 隐藏窗口
self.hide()
def minWindow(self):
# 隐藏窗口
self.setWindowState(Qt.WindowState.WindowMinimized)
def maxWindow(self):
# 隐藏窗口
self.setWindowState(Qt.WindowState.WindowMaximized)
# 定义一个打开新标签页的方法,接收一个可选的网址参数,默认为Bing首页
def new_tab(self, url=QUrl("https://www.baidu.com/")):
# 调用标签页控件的创建新标签页的方法,并传入网址参数
self.tabs.new_tab(url)
# 定义一个后退的方法
def back(self):
# 获取当前的网页视图控件,并执行后退操作
webview = self.tabs.currentWidget()
webview.back()
# 定义一个前进的方法
def forward(self):
# 获取当前的网页视图控件,并执行前进操作
webview = self.tabs.currentWidget()
webview.forward()
# 定义一个刷新的方法
def reload(self):
# 获取当前的网页视图控件,并执行刷新操作
webview = self.tabs.currentWidget()
webview.reload()
if __name__ == "__main__":
app = QApplication(sys.argv)
cls_app = App()
cls_app.show()
sys.exit(app.exec())
# Qt.Window:这是一个掩码,用于指定窗口的基本类型。它可以与以下标志位进行按位或运算,以创建不同类型的窗口。
# Qt.CustomizeWindowHint:这个标志位表示窗口已经被定制化了,例如通过样式表进行更改。
# Qt.WindowTitleHint:这个标志位表示窗口标题栏的可见性。
# Qt.WindowSystemMenuHint:这个标志位表示系统菜单的可见性。
# Qt.WindowMinimizeButtonHint:这个标志位表示最小化按钮的可见性。
# Qt.WindowMaximizeButtonHint:这个标志位表示最大化按钮的可见性。
# Qt.WindowCloseButtonHint:这个标志位表示关闭按钮的可见性。
# Qt.WindowContextHelpButtonHint:这个标志位表示上下文帮助按钮的可见性。
# Qt.WindowShadeButtonHint:这个标志位表示shade按钮的可见性(用于将窗口最小化为图标)。
# Qt.WindowStaysOnTopHint:这个标志位表示窗口是否始终保持在其他窗口的顶部。
# Qt.WindowTransparentForInputHint:这个标志位表示窗口是否透明,允许鼠标和键盘输入穿过窗口。
# Qt.WindowMinimized:这个标志位表示窗口已经被最小化了。
# Qt.WindowMaximized:这个标志位表示窗口已经被最大化了。
# Qt.WindowFullScreen:这个标志位表示窗口已经全屏显示。
# Qt.WindowNoState:这个标志位表示窗口没有特定的状态。
# Qt.WindowFullScreenButtonHint:这个标志位表示全屏按钮的可见性。
# https://www.bilibili.com/read/cv17017472/
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/lingling2tu/webview-tab.git
git@gitee.com:lingling2tu/webview-tab.git
lingling2tu
webview-tab
webview-tab
master

搜索帮助

344bd9b3 5694891 D2dac590 5694891