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



    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_OBJECT
    public:
        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_OBJECT
    public:
        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::keyPressEvent
    void 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




  • 相关阅读:
    [转][LeetCode]Longest Common Prefix ——求字符串的最长公共前缀
    [转]最长回文子串——4种解法
    [转]通过金矿模型介绍动态规划
    一句话说清楚什么是闭包函数
    [转]as3事件流机制彻底理解
    Eclipse 快捷键
    文件打包与解压缩
    第5节 环境变量与文件查找
    vim的多标签
    java思维导图
  • 原文地址:https://www.cnblogs.com/xiaomaohai/p/6157837.html
Copyright © 2020-2023  润新知