Qt: Open links with target in default browser, without leaking memory

Searching through the Internet, I've come across so many ways, mostly nonfunctional, nonspecific, or partially functional, to do various things with QWebView and opening URLs.

After much swearing and cursing, I've managed to get an example to do what I want, which is open normal links normally, and open anything that requests a new window in the external browser; however, there's a hitch. It leaks memory, because I make a bunch of extra WebViews that aren't cleaned up until the process exits. How can I do this without leaking memory?

Please forgive my rather sophomoric understanding of Qt in advance. I've only been using it for a handful of hours at this point.

SSCCE:

test.hpp

#include <QMainWindow>
#include <QWebView>

class Window : public QMainWindow {
  Q_OBJECT

public:
  Window();

private:
  QWebView* m_web;

private slots:
};

class WebPage : public QWebPage {
  Q_OBJECT

public:
    bool acceptNavigationRequest(QWebFrame *frame, const QNetworkRequest &request, NavigationType type);
};

class WebView : public QWebView {
  Q_OBJECT

public:
  QWebView* createWindow(QWebPage::WebWindowType type);
};

test.cpp

#include <QApplication>
#include <QGridLayout>
#include <QNetworkRequest>
#include <QDesktopServices>

#include "test.hpp"

Window::Window() :
    QMainWindow() {
  m_web = new WebView;
  m_web->setHtml("<div align="center"><a href="http://www.google.com/">Same Window</a> <a href="http://www.google.com/" target="_blank">New Window</a></div>");

  setCentralWidget(m_web);
}

bool WebPage::acceptNavigationRequest(QWebFrame*, QNetworkRequest const& request, NavigationType) {
  QDesktopServices::openUrl(request.url());
  return false;
}

QWebView* WebView::createWindow(QWebPage::WebWindowType) {
  auto res = new WebView;
  auto page = new WebPage;
  res->setPage(page);
  return res;
}

int main(int argc, char *argv[]) {
  QApplication a(argc, argv);

  Window w;
  w.show();

  return a.exec();
}

test.pro

QT += core gui network webkitwidgets widgets

TEMPLATE = app
TARGET = test
INCLUDEPATH += .

CONFIG += c++11

# Input
SOURCES += test.cpp
HEADERS += test.hpp

To Compile and Run

qmake test.pro
make
./test

It seems the view becomes useless after the page is rendered in the extern browser. You might just schedule the ExternalWebView for deletetion with deleteLater():

#include <iostream>

#include <QApplication>
#include <QGridLayout>
#include <QNetworkRequest>
#include <QDesktopServices>

#include <QEvent>
#include <QMainWindow>
#include <QWebView>

class ExternWebPage : public QWebPage {
    //Q_OBJECT

    public:
    ExternWebPage(QObject* parent = 0)
    :   QWebPage(parent)
    {
        std::cout << "ExternWebPage" << std::endl;
    }

    ~ExternWebPage() {
        std::cout << "Destroy ExternWebPage" << std::endl;
    }

    virtual bool event(QEvent *e) {
        static unsigned counter;
        std::cout << ++counter << " ExternWebPage: " << e->type() << std::endl;
        return QWebPage::event(e);
    }

    bool acceptNavigationRequest(QWebFrame *, const QNetworkRequest &request, NavigationType) {
        QDesktopServices::openUrl(request.url());
        return false;
    }
};


class ExternWebView : public QWebView {
    //Q_OBJECT

    public:
    ExternWebView(QWidget* parent = 0)
    :   QWebView(parent)
    {
        std::cout << "ExternWebView" << std::endl;
    }
    ~ExternWebView() { std::cout << "Destroy ExternWebView" << std::endl; }
    virtual bool event(QEvent *e) {
        static unsigned counter;
        std::cout << ++counter << " ExternWebView: " << e->type() << std::endl;
        return QWebView::event(e);
    }
};


class InternalWebView : public QWebView {
    //Q_OBJECT

    public:
    InternalWebView(QWidget* parent = 0)
    :   QWebView(parent)
    {}
    QWebView* createWindow(QWebPage::WebWindowType) {
        auto res = new ExternWebView();
        res->setPage(new ExternWebPage(res));
        res->deleteLater();
        return res;
    }
};


class Window : public QMainWindow {
    //Q_OBJECT

    public:
    Window()
    :   QMainWindow()
    {
        std::cout << "Window" << std::endl;
        auto web = new InternalWebView(this);
        web->setHtml("<div align="center"><a href="http://www.google.com/">Same Window</a> <a href="http://www.google.com/" target="_blank">New Window</a></div>");
        setCentralWidget(web);
    }
    ~Window() { std::cout << "Destroy Window" << std::endl; }

};


int main(int argc, char *argv[]) {
  QApplication a(argc, argv);
  Window w;
  w.show();
  return a.exec();
}

Test without deletion:

Window
ExternWebView
1 ExternWebView: 68
ExternWebPage
2 ExternWebView: 74
3 ExternWebView: 75
1 ExternWebPage: 43
2 ExternWebPage: 43
3 ExternWebPage: 43
4 ExternWebPage: 43
5 ExternWebPage: 43
Destroy Window

Test with delete later:

ExternWebView
1 ExternWebView: 68
ExternWebPage
2 ExternWebView: 74
3 ExternWebView: 75
4 ExternWebView: 52
Destroy ExternWebView
Destroy ExternWebPage
Destroy Window

QDesktopServices::openUrl(QUrl("http://stackoverflow.com/"));

由于这是内置函数,因此不应该有任何内存泄漏。


There are no leaks in the program, because the qt memory management system is going to take care of the objects created on the heap.

First, setPage is going to make your view object parent of the page object. That means that the page object will be deleted when the view object gets destroyed.

Second, since the view has no parent, you are always going to get a window. It will be released when you close the window, or end the program. That is why I said it is going to be taken cared by the qt's memory management system.

Now, when you run your program using memory profiler program (like valgrind), you may get leaks, which may or may not be real leaks. You need to identify them, and filter them out.

链接地址: http://www.djcxy.com/p/75934.html

上一篇: 与供应商目录的git工作流程

下一篇: Qt:在默认浏览器中打开目标链接,不会泄漏内存