Trong Qt có module QtWebEngine
cung cấp các lớp C++ hỗ trợ dựng trang web từ mã HTML, XML, SVG, CSS, Javascript lên một chương trình Qt, dùng cho các hệ điều hành không có sẵn web engine. Đối với các hệ điều hành có sẵn web engine thì chúng ta có thể dùng module QtWebKit,
tuy nhiên module này đã bị loại bỏ khỏi Qt 5.5, nên hầu như chúng ta sẽ dùng module QtWebEngine
để làm việc với web.
Tổng quan về Qt WebEngine
Kiến trúc:
Theo sơ đồ trên thì module QtWebEngine bao gồm những thành phần sau:
- Qt WebEngine Widgets: cung cấp các thư viện tạo giao diện người dùng với Widget.
- Qt WebEngine: cung cấp các lớp tạo giao diện người dùng với QtQuick.
- Qt WebEngine Core: làm việc với Chromium.
- Qt WebEngine Process: dựng trang, xử lý Javascript.
Module Qt WebEngine Core được xây dựng dựa trên Chromium. Các thành phần của Chromium chịu trách nhiệm trong việc xử lý kết nối mạng, xây dựng trang web, xử lý Javascript, HTML… Ngoài WebEngine thì Qt còn có module WebKit cũng có chức năng thao tác với trang web nhưng rất hạn chế, WebEngine nặng hơn, cung cấp nhiều chức năng thao tác trực tiếp với web hơn WebKit. Dù QtWebEngine
được xây dựng trên Chromium nhưng không có nghĩa là module này có các dịch vụ hỗ trợ thêm như của trình duyệt Chrome do Google phát triển.
Cài đặt
Mặc định module QtWebEngine
không có sẵn trong Qt Creator sử dụng trình biên dịch MinGW, bạn phải chuyển qua sử dụng Qt Creator dùng trình biên dịch MSVC hoặc sử dụng Add-In trong Visual Studio.
Hoặc có một cách khác là biên dịch lại toàn bộ mã nguồn Qt và Chromium sử dụng trình biên dịch MinGW, cách này rất phức tạp nên mình sẽ không đề cập ở đây.
Ngoài ra Visual Studio Add-In cũng không chấp nhận Visual Studio phiên bản Express (hoặc Community) nên cách này cũng khó khả thi (trừ khi bạn dùng crack). Nên tóm lại trong phần này mình sẽ sử dụng Qt Creator với trình biên dịch msvc từ Visual Studio 2015.
Lưu ý: tại thời điểm viết bài này trình biên dịch msvc đi kèm với Qt Creator khi download trên trang chủ không sử dụng được, mình phải cài Visual Studio 2015 Community rồi chỉ định Qt Creator sử dụng trình biên dịch của VS mới chạy được (khi cài Visual Studio 2015 bạn phải tích chọn cài 2 thành phần là Programming Languages→Visual C++ và Windows and Web Development→Universal Windows App Developments Tools vì đây là 2 thành phần cần thiết để biên dịch trong Qt nhưng không được cài mặc định).
Bây giờ chúng ta sẽ đi tìm hiểu một số chức năng có trong Qt WebEngine.
Mở trang web
Để mở một trang web và hiển thị thì chúng ta dùng lớp QWebEngineView.
Chúng ta tạo một project mới.
... QT += webenginewidgets ...
Trong file .pro chúng ta phải khai báo module QtWebEngine
bằng dòng code trên.
#include <QMainWindow> #include <QWebEngineView> class Browser : public QMainWindow { Q_OBJECT public: explicit Browser(QWidget *parent = 0); private: QWebEngineView *view; };
Chúng ta định nghĩa lớp Browser
kế thừa từ lớp QMainWindow,
bên trong lớp này có một đối tượng QWebEngineView.
#include "browser.h" Browser::Browser(QWidget *parent) : QMainWindow(parent) { view = new QWebEngineView(this); view->load(QUrl("https://phocode.com")); setCentralWidget(view); }
Để tải một trang web về thì chúng ta chỉ cần gọi phương thức load()
và truyền vào một đối tượng QUrl.
Phương thức setCentralWidget()
sẽ thiết lập đối tượng QWebEngineView
làm giao diện chính của cửa sổ.
#include "browser.h" #include <QApplication> #include <qtwebenginewidgetsglobal.h> int main(int argc, char *argv[]) { QApplication a(argc, argv); Browser browser; browser.show(); return a.exec(); }
Trong file main.cpp
chúng ta chỉ tạo đối tượng Browser
rồi gọi phương thức show()
như bình thường.
Một số tính năng của QWebEngineView
Lớp QWebEngineView
có sẵn một số tính năng cơ bản của một trình duyệt web. Chúng ta sẽ tìm hiểu qua ví dụ dưới đây:
#include <QMainWindow> #include <QWebEngineView> #include <QLineEdit> #include <QUrl> #include <QToolBar> class Browser : public QMainWindow { Q_OBJECT public: explicit Browser(QWidget *parent = 0); signals: public slots: void adjustLocation(); void changeLocation(); void adjustTitle(); void setProgress(int p); private: QWebEngineView *view; QLineEdit *locationEdit; int progress; };
Chúng ta định nghĩa lại lớp Browser.
Lớp QWebEngineView
có một số Signal đặc biệt được phát đi trong quá trình duyệt trang web nên chúng ta có thể định nghĩa một số phương thức Slot và thuộc tính để bắt và xử lý các signal đó.
#include "browser.h" Browser::Browser(QWidget *parent) : QMainWindow(parent) { view = new QWebEngineView(this); view->load(QUrl("http://google.com")); connect(view, SIGNAL(loadFinished(bool)), this, SLOT(adjustLocation())); connect(view, SIGNAL(titleChanged(QString)), this, SLOT(adjustTitle())); connect(view, SIGNAL(loadProgress(int)), this, SLOT(setProgress(int))); locationEdit = new QLineEdit(this); locationEdit->setSizePolicy(QSizePolicy::Expanding, locationEdit->sizePolicy().verticalPolicy()); connect(locationEdit, SIGNAL(returnPressed()), SLOT(changeLocation())); QToolBar *toolBar = addToolBar(tr("Navigation")); toolBar->addAction(view->pageAction(QWebEnginePage::Back)); toolBar->addAction(view->pageAction(QWebEnginePage::Forward)); toolBar->addAction(view->pageAction(QWebEnginePage::Reload)); toolBar->addAction(view->pageAction(QWebEnginePage::Stop)); toolBar->addWidget(locationEdit); setCentralWidget(view); } void Browser::adjustLocation() { locationEdit->setText(view->url().toString()); } void Browser::changeLocation() { QUrl url = QUrl::fromUserInput(locationEdit->text()); view->load(url); view->setFocus(); } void Browser::adjustTitle() { if(progress <= 0 || progress >= 100) setWindowTitle(view->title()); else setWindowTitle(QString("%1 (%2%)").arg(view->title()).arg(progress)); } void Browser::setProgress(int p) { progress = p; adjustTitle(); }
Trong file browser.cpp
chúng ta xây dựng lớp Browser
thành một trình duyệt web đơn giản.
connect(view, SIGNAL(loadFinished(bool)), this, SLOT(adjustLocation())); connect(view, SIGNAL(titleChanged(QString)), this, SLOT(adjustTitle())); connect(view, SIGNAL(loadProgress(int)), this, SLOT(setProgress(int)));
Tín hiệu loadFinished()
được phát đi khi QWebEngineView
đã tải xong trang web. Chúng ta xử lý bằng phương thức adjustLocation().
Tín hiệu titleChanged()
được phát đi khi tiêu đề của trang web thay đổi, tức là gần như mỗi lần loadFinished()
được phát đi thì titleChanged()
cũng được phát đi cùng luôn.
Tín hiệu loadProgress()
được phát đi khi mỗi phần tử của trang web được tải xong, ví dụ một trang web mang theo rất nhiều file ảnh, file javascript, file css… mỗi lần những file này được tải xong thì tín hiệu này được phát đi, tín hiệu này mang theo một con số từ 0 đến 100 có nghĩa là trang web đang được tải bao nhiêu %.
locationEdit = new QLineEdit(this); locationEdit->setSizePolicy(QSizePolicy::Expanding, locationEdit->sizePolicy().verticalPolicy()); connect(locationEdit, SIGNAL(returnPressed()), SLOT(changeLocation()));
Ở đây chúng ta định nghĩa một đối tượng QLineEdit
dùng làm thanh địa chỉ của trình duyệt web. Phương thức setSizePolicy()
sẽ quy định kích thước của QLineEdit.
Tín hiệu returnPressed()
được phát đi khi chúng ta gõ phím Enter trên QLineEdit,
chúng ta bắt tín hiệu này bằng slot changeLocation().
QToolBar *toolBar = addToolBar(tr("Navigation")); toolBar->addAction(view->pageAction(QWebEnginePage::Back)); toolBar->addAction(view->pageAction(QWebEnginePage::Forward)); toolBar->addAction(view->pageAction(QWebEnginePage::Reload)); toolBar->addAction(view->pageAction(QWebEnginePage::Stop)); toolBar->addWidget(locationEdit);
Ở đây chúng ta tạo thêm một thanh toolbar, ngoài việc dùng để chứa thanh địa chỉ ở trên, chúng ta còn dùng thêm các đối tượng Action
có sẵn của lớp QWebEnginePage,
lớp này có thuộc tính QWebEnginePage::WebAction
cung cấp một số đối tượng Action
có các chức năng cơ bản của một trình duyệt web và thậm chí là có cả icon sẵn. Ví dụ ở trên Back
là nút quay lại trang vừa xem, Forward
là nút đi tới trang vừa mở, Reload
là nút refresh trình duyệt, Stop
là nút dừng tải trang.
void Browser::adjustLocation() { locationEdit->setText(view->url().toString()); }
Phương thức adjustLocation()
chỉ đơn giản là thiết lập lại đoạn text trong thanh địa chỉ.
void Browser::changeLocation() { QUrl url = QUrl::fromUserInput(locationEdit->text()); view->load(url); view->setFocus(); }
Phương thức changeLocation()
sẽ được gọi khi chúng ta gõ phím Enter trên thanh địa chỉ, lúc này chúng ta tiến hành tải lại trang web.
void Browser::adjustTitle() { if(progress <= 0 || progress >= 100) setWindowTitle(view->title()); else setWindowTitle(QString("%1 (%2%)").arg(view->title()).arg(progress)); }
Phương thức adjustTitle()
sẽ được gọi khi trang web được tải xong, hoặc một phần của trang web được tải xong, vì ở đây chúng ta chèn thêm số % trang web đã được tải là bao nhiêu.
void Browser::setProgress(int p) { progress = p; adjustTitle(); }
Trong lớp Browser
chúng ta định nghĩa thuộc tính progress
dùng để lưu số % trang web đã được tải. Phương thức setProgress()
sẽ được gọi khi QWebEngineView
phát tín hiệu loadProgress(),
lúc này chúng ta thiết lập thông số mới cho thuộc tính progress,
đồng thời gọi lại phương thức adjustTitle().
#include "browser.h" #include <QApplication> #include <qtwebenginewidgetsglobal.h> int main(int argc, char *argv[]) { QApplication a(argc, argv); Browser browser; browser.show(); return a.exec(); }
Trong file main.cpp
chúng ta chỉ tạo đối tượng Browser
và gọi phương thức show().