用Qt开发简单的浏览器(一)

来源:互联网 发布:商品关键字的seo处理 编辑:程序博客网 时间:2024/05/16 19:22


1.代码实现

工程目录结构如下:



AddressBar类包含了地址栏和按钮两个控件,将地址栏回车和按钮点击信号与goToSite()槽连接。
当回车和点击事件发生时,goToSite()将获得Url地址并发送go(QUrl)信号。

addressbar.h
#ifndef ADDRESSBAR_H#define ADDRESSBAR_H#include <QWidget>#include <QLineEdit>#include <QPushButton>#include <QHBoxLayout>#include <QUrl>#include <QString>class AddressBar : public QWidget{    Q_OBJECTpublic:    explicit AddressBar(QWidget *parent = 0);        signals:    void go(QUrl);    public slots:    void goToSite();private:    QLineEdit *addressEdit;    QPushButton *goButton;    QHBoxLayout *layout;};#endif // ADDRESSBAR_H

addressbar.cpp
#include "addressbar.h"AddressBar::AddressBar(QWidget *parent) :    QWidget(parent){    addressEdit = new QLineEdit(parent);    goButton = new QPushButton("Go", parent);    layout = new QHBoxLayout;    layout->addWidget(addressEdit);    layout->addWidget(goButton);    this->setLayout(layout);    connect(goButton, SIGNAL(clicked()), this, SLOT(goToSite()));    connect(addressEdit, SIGNAL(returnPressed()), this, SLOT(goToSite()));}void AddressBar::goToSite(){    QString address = addressEdit->text();    emit go(QUrl(address));}

接下来是QtWebkit包中的主要类QWebView,我们借助这个类来渲染Url指向的网页。
为了当用户在地址栏回车或者点击Go按钮时能够自动加载网页,我们需要给QWebView
添加loadNewPage(QUrl)槽(因为QWebView没有类似load(QUrl)的槽),并将其与go(QUrl)
信号连接。所以我们实现一个QWebView的子类HtmlView。

htmlview.h
#ifndef HTMLVIEW_H#define HTMLVIEW_H#include <QWebView>class HtmlView : public QWebView{    Q_OBJECTpublic:    explicit HtmlView(QWidget *parent = 0);    signals:    public slots:    void loadNewPage(const QUrl &url);    };#endif // HTMLVIEW_H

htmlview.cpp
#include "htmlview.h"HtmlView::HtmlView(QWidget *parent) :    QWebView(parent){}void HtmlView::loadNewPage(const QUrl &url){    this->load(url);}

接下来实现程序的主窗口QMainWindow,将AddressBar和HtmlView放置其中。

mainwindow.h
#ifndef MAINWINDOW_H#define MAINWINDOW_H#include <QWidget>#include <QLineEdit>#include <QPushButton>#include <QGridLayout>#include <QtWebKit>#include <QMainWindow>#include "addressbar.h"#include "htmlview.h"class MainWindow : public QMainWindow{    Q_OBJECT    public:    explicit MainWindow(QWidget *parent = 0);};#endif // MAINWINDOW_H

mainwindow.cpp
#include "mainwindow.h"#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :    QMainWindow(parent){    // 1.Create widget    QWidget *centralWidget = new QWidget(this);    AddressBar *bar = new AddressBar;    HtmlView *view = new HtmlView;    // 2.Add widget to layout    QGridLayout *layout = new QGridLayout;    layout->addWidget(bar, 0, 0, 1, 10);    layout->addWidget(view, 1, 0, 1, 10);    centralWidget->setLayout(layout);    // 3.Connect widget    QObject::connect(bar, SIGNAL(go(QUrl)), view, SLOT(loadNewPage(QUrl)));    this->setCentralWidget(centralWidget);    this->setWindowTitle("My Browser v1.0");    this->resize(640, 480);}

最后是程序的入口main.cpp
#include <QApplication>#include "mainwindow.h"int main(int argc, char *argv[]){    QApplication app(argc, argv);    MainWindow *window = new MainWindow;    window->show();        return app.exec();}


2.事件流分析



我们分别为AddressBar和HtmlView自定义了两个槽goToSite(QUrl)和loadNewPage(QUrl),以及新的信号go(QUrl)。
就是为了将Url地址传递给QWebView的load函数。

这里需要注意的是SIGNAL-SLOT机制是Qt的内部机制,它是同步执行的。源头上returnPressed()和clicked()槽的触发,
是从操作系统的事件队列中得到的,并进行异步的处理。以QPushButton的clicked()槽的触发为例,QApplication.exec()
执行后将会监听操作系统事件队列,当鼠标事件发生时,事件将会发送到QPushButton的event()函数进行分发:
QPushButton::event -> QAbstractButton::event ->QWidget::event [Dispatch event]:    case QEvent::KeyPress: {        QKeyEvent *k = (QKeyEvent *)event;        bool res = false;        if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) {  //### Add MetaModifier?            if (k->key() == Qt::Key_Backtab                || (k->key() == Qt::Key_Tab && (k->modifiers() & Qt::ShiftModifier)))                res = focusNextPrevChild(false);            else if (k->key() == Qt::Key_Tab)                res = focusNextPrevChild(true);            if (res)                break;        }        keyPressEvent(k);-> QPushButton::keyPressEventvoid QPushButton::keyPressEvent(QKeyEvent *e) { Q_D(QPushButton); switch (e->key()) { case Qt::Key_Enter: case Qt::Key_Return:   if (autoDefault() || d->defaultButton) {   click();   break; } // fall through default:   QAbstractButton::keyPressEvent(e); } }-> QAbstractButton::click:void QAbstractButton::click() { if (!isEnabled())   return; Q_D(QAbstractButton); QPointer<QAbstractButton> guard(this); d->down = true; d->emitPressed(); if (guard) {   d->down = false;   nextCheckState();   if (guard)    d->emitReleased();   if (guard)    d->emitClicked(); } }


3.SIGNAL-SLOT类的编译

关于Q_OBJECT宏以及SIGNAL,SLOT,emit等关键字奇怪的语法,其实他们是通过一个叫做MOC元对象编译器
的组件来进行预编译的,因此我们可以使用SIGNAL,SLOT,emit来清晰地连接各个信号槽,而非函数指针。
SIGNAL-SLOT使用很方便,但也是会损失一点执行效率,使用时要谨慎。


4.总结及学习资料

通过这个例子可以对Qt的SIGNAL-SLOT机制有个简单的了解,它可以减少对象间的依赖。假如不使用它,我们就
需要在AddressBar中直接调用HtmlView的load()函数,两个类耦合在了一起。在这个例子中MainWindow负责AddressBar
和HtmlView的构建和连接,使它们互相不知道对方的存在!

让我更感兴趣的其实是QtWebKit,通过它我们可以在Qt开发桌面应用时使用Web技术,而不用局限于Qt,MFC等等。
Web开发人员也可以投身桌面应用开发之中。

QtWebKit官方文档:http://doc.qt.nokia.com/4.7-snapshot/qtwebkit.html
学习笔记:http://caterpillar.onlyfun.net/Gossip/Qt4Gossip/Qt4Gossip.html
QtWebKit系列教程:http://software.intel.com/zh-cn/blogs/2010/06/08/qt-webkit-qt-webkit/
信号槽深入学习:http://www.ibm.com/developerworks/cn/linux/guitoolkit/qt/signal-slot/
QtWeb - 一个开源的Qt浏览器项目


2015年8月16日

需要源码的同学还真不少啊,现在源码已经共享到GitHub上了,请自行获取吧!地址是:https://github.com/cdai/qtbrowser