30.1 制作简单浏览器
30.2 小结
如果需要在程序中加载并显示网页,那QWebEngineView绝对是最佳的选择。该控件基于Chrome浏览器内核引擎,所提供的功能和方法还是比较强大的。
本章我们就通过制作下图所示的简单浏览器来了解QWebEngineView的用法:
在输入框中输入网址并敲回车后,QWebEngineView控件加载并显示相应的网址内容。左上方的三个按钮可以允许我们进行前进、后退和刷新操作,右上方的两个按钮可以放大和缩小网页。
下面我们一步步来实现,首先完成浏览器的外观:
import sys
from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtGui import QIcon
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QLineEdit, QVBoxLayout, QHBoxLayout
class Demo(QWidget):
def __init__(self):
super(Demo, self).__init__()
self.resize(1000, 600) # 1
self.back_btn = QPushButton(self) # 2
self.forward_btn = QPushButton(self)
self.refresh_btn = QPushButton(self)
self.zoom_in_btn = QPushButton(self)
self.zoom_out_btn = QPushButton(self)
self.url_le = QLineEdit(self)
self.browser = QWebEngineView(self)
self.h_layout = QHBoxLayout()
self.v_layout = QVBoxLayout()
self.layout_init()
self.btn_init()
self.le_init()
def layout_init(self): # 3
self.h_layout.setSpacing(0)
self.h_layout.addWidget(self.back_btn)
self.h_layout.addWidget(self.forward_btn)
self.h_layout.addWidget(self.refresh_btn)
self.h_layout.addStretch(2)
self.h_layout.addWidget(self.url_le)
self.h_layout.addStretch(2)
self.h_layout.addWidget(self.zoom_in_btn)
self.h_layout.addWidget(self.zoom_out_btn)
self.v_layout.addLayout(self.h_layout)
self.v_layout.addWidget(self.browser)
self.setLayout(self.v_layout)
def btn_init(self): # 4
self.back_btn.setIcon(QIcon('images/back.png'))
self.forward_btn.setIcon(QIcon('images/forward.png'))
self.refresh_btn.setIcon(QIcon('images/refresh.png'))
self.zoom_in_btn.setIcon(QIcon('images/zoom_in.png'))
self.zoom_out_btn.setIcon(QIcon('images/zoom_out.png'))
def le_init(self): # 5
self.url_le.setFixedWidth(400)
self.url_le.setPlaceholderText('Search or enter website name')
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = Demo()
demo.show()
sys.exit(app.exec_())
1. 通过resize()方法来将窗口大小设为1000x600;
2. 实例化按钮控件、输入框控件以及QWebEngineView控件;
3. 完成布局,使用setSpacing()传入参数0代表让各个控件之间不存在间隔(主要想让按钮靠拢),随后我们在想要的地方使用addStretch(),这样就达到让输入框和按钮分离开来,而按钮之间又是相互靠拢的目的。
4. 给按钮添加图标,图片下载地址如下:
5. 设置输入框宽度为400并设置占位符提示用户在这里输入网址。
此时运行截图如下,QWebEngine还没有显示任何网页:
接下来完成以下功能:在输入框中输入网址并敲击回车后,QWebEngineView加载并显示相应的网址内容:
def keyPressEvent(self, QKeyEvent):
if QKeyEvent.key() == Qt.Key_Return or QKeyEvent.key() == Qt.Key_Enter:
if self.url_le.hasFocus():
self.browser.load(QUrl(self.url_le.text()))
既然涉及到键盘,那肯定要用事件函数来处理了。
标准的键盘上是有两个回车键的,Key_Return为大回车,Key_Enter为小回车(这里我们把两种都考虑进来)如下图所示:
加上if self.url_le.hasFocus():判断是为了只有在输入框被编辑的状态下敲击回车才会让网页实现跳转(读者也可以去使用下市面上的浏览器看下是否是这样)。
条件都满足的话,就调用load()方法并传入一个QUrl类型参数即可(不能单单传入字符串,需要用QUrl()把字符串转为QUrl对象)。
虽然QWebEngineView已经提供了很多有用的方法,但是许多方面还是需要我们开发人员进行完善。比如用户如果只输入"baidu.com"的话,那QWebEngineView其实是识别不出来的,也就无法加载和显示网页了(会跳转到空白页)。
市面上的浏览器在用户只输入"baidu.com"的情况下,会自动在最前面加上"http://"或者“https://”,我们接下来完善下代码:
def keyPressEvent(self, QKeyEvent):
if QKeyEvent.key() == Qt.Key_Return or QKeyEvent.key() == Qt.Key_Enter:
if self.url_le.hasFocus():
if self.url_le.text().startswith('https://') or self.url_le.text().startswith('http://'):
self.browser.load(QUrl(self.url_le.text()))
else:
self.browser.load(QUrl('https://'+self.url_le.text()))
调用startswith()方法判断用户输入的网址字符串是否是以"https://"或者“http://”开头的,如果是的话,则采用用户输入的网址;如果不是,则在前面加上"https://"。这样的话QWebEngineView就可以正常显示了。
接下来完成以下功能:首次运行程序时,QWebEngineView显示百度网页。
def browser_init(self):
self.browser.load(QUrl('https://baidu.com'))
非常简单,只需要在类初始化函数中调用以上函数即可。
到目前为止,程序代码如下:
import sys
from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtGui import QIcon
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QLineEdit, QVBoxLayout, QHBoxLayout
class Demo(QWidget):
def __init__(self):
super(Demo, self).__init__()
self.resize(1000, 600)
self.back_btn = QPushButton(self)
self.forward_btn = QPushButton(self)
self.refresh_btn = QPushButton(self)
self.zoom_in_btn = QPushButton(self)
self.zoom_out_btn = QPushButton(self)
self.url_le = QLineEdit(self)
self.browser = QWebEngineView()
self.h_layout = QHBoxLayout()
self.v_layout = QVBoxLayout()
self.layout_init()
self.btn_init()
self.le_init()
self.browser_init()
def layout_init(self):
self.h_layout.setSpacing(0)
self.h_layout.addWidget(self.back_btn)
self.h_layout.addWidget(self.forward_btn)
self.h_layout.addWidget(self.refresh_btn)
self.h_layout.addStretch(2)
self.h_layout.addWidget(self.url_le)
self.h_layout.addStretch(2)
self.h_layout.addWidget(self.zoom_in_btn)
self.h_layout.addWidget(self.zoom_out_btn)
self.v_layout.addLayout(self.h_layout)
self.v_layout.addWidget(self.browser)
self.setLayout(self.v_layout)
def browser_init(self):
self.browser.load(QUrl('https://baidu.com'))
def btn_init(self):
self.back_btn.setIcon(QIcon('images/back.png'))
self.forward_btn.setIcon(QIcon('images/forward.png'))
self.refresh_btn.setIcon(QIcon('images/refresh.png'))
self.zoom_in_btn.setIcon(QIcon('images/zoom_in.png'))
self.zoom_out_btn.setIcon(QIcon('images/zoom_out.png'))
def le_init(self):
self.url_le.setFixedWidth(400)
self.url_le.setPlaceholderText('Search or enter website name')
def keyPressEvent(self, QKeyEvent):
if QKeyEvent.key() == Qt.Key_Return or QKeyEvent.key() == Qt.Key_Enter:
if self.url_le.hasFocus():
if self.url_le.text().startswith('https://') or self.url_le.text().startswith('http://'):
self.browser.load(QUrl(self.url_le.text()))
else:
self.browser.load(QUrl('https://'+self.url_le.text()))
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = Demo()
demo.show()
sys.exit(app.exec_())
我们运行一下,发现两个问题:
1. 程序首次运行时,输入框文本并没有显示百度网址。
2. 当我们在输入框中输入"python.org"并敲击回车后,网页可以加载,但是输入框并没有显示当前应该要显示的“https://www.python.org/”。
或者在点击页面中的“Downloads”跳转到另一个页面后,输入框的文本也应该要相应地变为“https://www.python.org/downloads/”。
根据以上问题我们知道接下来应该把输入框的文本设置为QWebEngineView所加载的网址。QWebEngineView控件有一个信号urlChanged,该信号会在每次要加载的网址发生变化时发射。那我们其实可以把这个信号和槽函数连接起来,在槽函数中我们把输入框的文本设置为变化后QWebEngineView要加载的网址即可(其他信号例如loadStarted, loadFinished等其实也可以)。
我们只需要在browser_init()函数中加一行信号和槽函数连接的代码即可:
def browser_init(self):
self.browser.load(QUrl('https://baidu.com'))
self.browser.urlChanged.connect(lambda: self.url_le.setText(self.browser.url().toDisplayString()))
调用QWebEngineView的url()方法获取QUrl对象,但这个不是字符串类型,所以还需要调用toDisplayString()方法来获取url文本字符串。现在再来运行下就会发现输入框的文本会跟随QWebEngineView当前加载的网址改变而改变了。
接下来我们完善按钮功能,首先是左上角的后退、前进和刷新。查看文档我们很容易就会发现QWebEngineView有几个槽函数可以实现这三个功能:
back()方法实现后退功能,forward()方法实现前进功能而reload()方法实现刷新功能。我们接下来只需要在btn_init()函数中加几行信号和槽函数连接的代码即可:
def btn_init(self):
self.back_btn.setIcon(QIcon('images/back.png'))
self.forward_btn.setIcon(QIcon('images/forward.png'))
self.refresh_btn.setIcon(QIcon('images/refresh.png'))
self.zoom_in_btn.setIcon(QIcon('images/zoom_in.png'))
self.zoom_out_btn.setIcon(QIcon('images/zoom_out.png'))
self.back_btn.clicked.connect(self.browser.back)
self.forward_btn.clicked.connect(self.browser.forward)
self.refresh_btn.clicked.connect(self.browser.reload)
此时运行下程序,就会发现程序可以像一个真正的浏览器一样后退、前进和刷新了。
最后是放大缩小功能,要实现这种功能我们需要用到zoomFactor()方法和setZoomFactor()方法。前者获取当前缩放值,后者设置缩放值。那也就是说我们每当用户点击放大或缩小按钮时,只需要先获取到当前的缩放值,然后放大的话就增加缩放值,缩小就减小缩放值。首先实现槽函数,代码如下:
def zoom_in_func(self):
self.browser.setZoomFactor(self.browser.zoomFactor()+0.1)
def zoom_out_func(self):
self.browser.setZoomFactor(self.browser.zoomFactor()-0.1)
然后在btn_init()函数中再加两行信号和槽函数连接的代码:
def btn_init(self):
self.back_btn.setIcon(QIcon('images/back.png'))
self.forward_btn.setIcon(QIcon('images/forward.png'))
self.refresh_btn.setIcon(QIcon('images/refresh.png'))
self.zoom_in_btn.setIcon(QIcon('images/zoom_in.png'))
self.zoom_out_btn.setIcon(QIcon('images/zoom_out.png'))
self.back_btn.clicked.connect(self.browser.back)
self.forward_btn.clicked.connect(self.browser.forward)
self.refresh_btn.clicked.connect(self.browser.reload)
self.zoom_in_btn.clicked.connect(self.zoom_in_func)
self.zoom_out_btn.clicked.connect(self.zoom_out_func)
现在运行下程序,点击放大缩小按钮,页面就会相应的放大缩小:
但是放大和缩小的范围是有限定的:
根据文档我们可以知道范围为0.25-5.0,默认值是1.0。
完整代码如下:
import sys
from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtGui import QIcon
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QLineEdit, QVBoxLayout, QHBoxLayout
class Demo(QWidget):
def __init__(self):
super(Demo, self).__init__()
self.resize(1000, 600)
self.back_btn = QPushButton(self)
self.forward_btn = QPushButton(self)
self.refresh_btn = QPushButton(self)
self.zoom_in_btn = QPushButton(self)
self.zoom_out_btn = QPushButton(self)
self.url_le = QLineEdit(self)
self.browser = QWebEngineView()
self.h_layout = QHBoxLayout()
self.v_layout = QVBoxLayout()
self.layout_init()
self.btn_init()
self.le_init()
self.browser_init()
def layout_init(self):
self.h_layout.setSpacing(0)
self.h_layout.addWidget(self.back_btn)
self.h_layout.addWidget(self.forward_btn)
self.h_layout.addWidget(self.refresh_btn)
self.h_layout.addStretch(2)
self.h_layout.addWidget(self.url_le)
self.h_layout.addStretch(2)
self.h_layout.addWidget(self.zoom_in_btn)
self.h_layout.addWidget(self.zoom_out_btn)
self.v_layout.addLayout(self.h_layout)
self.v_layout.addWidget(self.browser)
self.setLayout(self.v_layout)
def browser_init(self):
self.browser.load(QUrl('https://baidu.com'))
self.browser.urlChanged.connect(lambda: self.url_le.setText(self.browser.url().toDisplayString()))
def btn_init(self):
self.back_btn.setIcon(QIcon('images/back.png'))
self.forward_btn.setIcon(QIcon('images/forward.png'))
self.refresh_btn.setIcon(QIcon('images/refresh.png'))
self.zoom_in_btn.setIcon(QIcon('images/zoom_in.png'))
self.zoom_out_btn.setIcon(QIcon('images/zoom_out.png'))
self.back_btn.clicked.connect(self.browser.back)
self.forward_btn.clicked.connect(self.browser.forward)
self.refresh_btn.clicked.connect(self.browser.reload)
self.zoom_in_btn.clicked.connect(self.zoom_in_func)
self.zoom_out_btn.clicked.connect(self.zoom_out_func)
def le_init(self):
self.url_le.setFixedWidth(400)
self.url_le.setPlaceholderText('Search or enter website name')
def keyPressEvent(self, QKeyEvent):
if QKeyEvent.key() == Qt.Key_Return or QKeyEvent.key() == Qt.Key_Enter:
if self.url_le.hasFocus():
if self.url_le.text().startswith('https://') or self.url_le.text().startswith('http://'):
self.browser.load(QUrl(self.url_le.text()))
else:
self.browser.load(QUrl('https://'+self.url_le.text()))
def zoom_in_func(self):
self.browser.setZoomFactor(self.browser.zoomFactor()+0.1)
def zoom_out_func(self):
self.browser.setZoomFactor(self.browser.zoomFactor()-0.1)
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = Demo()
demo.show()
sys.exit(app.exec_())
1. 这个简单的浏览器还可以添加很多功能,或者再进行完善,让它更像一个真正的浏览器。比如程序首次运行时,前进和后退按钮应该都要设置为不可用,而且在无法前进和后退是也要设置为不可用(涉及到history()方法,小伙伴可以自己去实现下)。
2. 还是那句话——多查文档。如果你脑海里有想要实现的功能,你可以去查下文档看看这个控件有没有提供相应的方法。或者你脑海里可能一片空白,那你其实也可以先去读下文档看看某控件提供方法有没有你喜欢的,能给你带来灵感也说不定。
----------------------------------------------------------------------
喜欢的小伙伴可以加入这个Python QQ交流群一起学习:820934083