From a7577f8c3ae3a025384d8d19fdf3575ef141a70b Mon Sep 17 00:00:00 2001 From: Morten Engvoldsen Date: Mon, 9 Nov 2009 13:02:51 +0100 Subject: =?UTF-8?q?=FF=FEd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/src/examples/googlesuggest.qdoc | 148 ++++++++++++++++++++++- doc/src/images/googlesuggest-example.png | Bin 18809 -> 9006 bytes examples/network/googlesuggest/googlesuggest.cpp | 58 +++++---- examples/network/googlesuggest/googlesuggest.h | 6 +- examples/network/googlesuggest/searchbox.cpp | 7 +- examples/network/googlesuggest/searchbox.h | 2 + 6 files changed, 191 insertions(+), 30 deletions(-) diff --git a/doc/src/examples/googlesuggest.qdoc b/doc/src/examples/googlesuggest.qdoc index bec242d..f3693e9 100644 --- a/doc/src/examples/googlesuggest.qdoc +++ b/doc/src/examples/googlesuggest.qdoc @@ -43,10 +43,152 @@ \example network/googlesuggest \title Google Suggest Example - The Google Suggest example shows how to use the network access manager - to get the list of suggested search terms from Google. + The Google Suggest example demonstrates how to use the QNetworkAccessManager + class to obtain a list of suggestions from the Google search engine as the + user types into a QLineEdit. \image googlesuggest-example.png - \note The Google Suggest suggestion service is a registered trademark of Google Inc. + The application makes use of the \c get function in +QNetworkAccessManager to post a request and obtain the result of the search +query sent to the Google search engine. The results returned are listed as +clickable links appearing below the search box as a drop-down menu. + +The widget is built up by a QLineEdit as the search box, and a QTreeView +used as a popup menu below the search box. + +\section1 GSuggestCompletion Class Declaration + +This class implements an event filter and a number of functions to display +the search results and to determent when and how to perform the search. + +\snippet examples/network/googlesuggest/googlesuggest.h 1 + +The class connects to a QLineEdit and uses a QTreeWidget to display the +results. A QTimer controls the start of the network requests that are +executed using a QNetworkAccessManager. + +\section1 GSuggestCompletion Class Implementation + +We start by defining a constant containing the URL to be used in the Google +queries. This is the basis for the query. The letters typed into the search +box will be added to the query to perform the search itself. + +\snippet examples/network/googlesuggest/googlesuggest.cpp 1 + +In the constructor, we set the parent of this GSuggestCompletion instance +to be the QLineEdit passed in. For simplicity, the QLineEdit is also stored +in the explicit \c editor member variable. + +We then create a QTreeWidget as a toplevel widget and configure the various +properties to give it the look of a popup widget. + +The popup will be populated by the results returned from Google. We set +the number of columns to be two, since we want to display both the +suggested search term and the number of hits it will trigger in the search +engine. + +Furthermore, we install the GSuggestCompletion instance as an event filter +on the QTreeWidget, and connect the \c itemClicked() signal with the \c +doneCompletion() slot. + +A single-shot QTimer is used to start the request when the user has stopped +typing for 500 ms. + +Finally, we connect the networkManagers \c finished() signal with the \c +handleNetworkData() slot to handle the incoming data. + +\snippet examples/network/googlesuggest/googlesuggest.cpp 2 + +Since the QTreeWidget popup has been instantiated as a toplevel widget, the +destructor has to delete it explicitly from memory to avoid a memory leak. + +\snippet examples/network/googlesuggest/googlesuggest.cpp 3 + +The event filter handles mouse press and key press events that are +delivered to the popup. For mouse press events we just hide the popup and +return focus to the editor widget, and then return true to prevent further +event processing. + +Key event handling is implemented so that Enter and Return execute the +selected link, while the Escape key hides the popup. Sine we want to be +able to navigate the list of suggestions using the different navigation +keys on the keyboard we let Qt continue regular event processing for those +by returning false from the eventFilter reimplementation. + +For all other keys, the event will be passed on to the editor widget and the +popup is hidden. This way the user's typing will not be interrupted by the +popping up of the completion list. + +\snippet examples/network/googlesuggest/googlesuggest.cpp 4 + +The \c showCompletion() function populates the QTreeWidget with the results +returned from the query. It takes two QStringLists, one with the suggested +search terms and the other with the corresponding number of hits. + +\snippet examples/network/googlesuggest/googlesuggest.cpp 5 + +A QTreeWidgetItem is created for each index in the list and inserted into +the QTreeWidget. Finally, we adjust position and size of the popup to make +sure that it pops up in the correct position below the editor, and show it. + +The \c doneCompletion() function, which is called by the event filter when +either Enter or Return keys are pressed, stops the timer to prevent further +requests and passes the text of the selected item to the editor. We then +make the \c editor QLineEdit emit the returnPressed() signal, to which the +application can connect to open the respective web page. + +\snippet examples/network/googlesuggest/googlesuggest.cpp 6 + +The \c autoSuggest() slot is called when the timer times out, and uses the +text in the editor to build the complete search query. The query is then +passed to the QNetworkAccessManager's \c get() function to start the +request. + +\snippet examples/network/googlesuggest/googlesuggest.cpp 7 + +The function \c preventSuggest() stops the timer to prevent further +requests from being started. + +\snippet examples/network/googlesuggest/googlesuggest.cpp 8 + +When the network request is finished, the QNetworkAccessManager delivers the +data received from the server through the networkReply object. + +\snippet examples/network/googlesuggest/googlesuggest.cpp 9 + +To extract the data from the reply we use the \c readAll() function, which +is inherited from QIODevice and returns a QByteArray. Since this data is +encoded in XML we can use a QXmlStreamReader to traverse the data and +extract the search result as QStrings, which we can stream into two +QStringLists used to populate the popup. + +Finally, we schedule the QNetworkReply object for deletion using the \c +deleteLater function. + +\section1 SearchBox Class Declaration + +The SearchBox class inherits QLineEdit and adds the protected slot \c +doSearch(). + +A \c GSuggestCompletion member provides the SearchBox with the request +functionality and the suggestions returned from the Google search engine. + +\snippet examples/network/googlesuggest/searchbox.h 1 + +\section1 SearchBox Class Implementation + +The search box constructor instantiates the GSuggestCompletion object and +connects the returnPressed() signal to the doSearch() slot. + +\snippet examples/network/googlesuggest/searchbox.cpp 1 + +The function \c doSearch() stops the completer from sending any further +queries to the search engine. + +Further, the function extracts the selected search phrase and opens it in +the default web browser using QDesktopServices. + +\snippet examples/network/googlesuggest/searchbox.cpp 2 + */ diff --git a/doc/src/images/googlesuggest-example.png b/doc/src/images/googlesuggest-example.png index 4ef072a..477d444 100644 Binary files a/doc/src/images/googlesuggest-example.png and b/doc/src/images/googlesuggest-example.png differ diff --git a/examples/network/googlesuggest/googlesuggest.cpp b/examples/network/googlesuggest/googlesuggest.cpp index e1588a6..a1075ec 100644 --- a/examples/network/googlesuggest/googlesuggest.cpp +++ b/examples/network/googlesuggest/googlesuggest.cpp @@ -39,17 +39,22 @@ ** ****************************************************************************/ -#include -#include -#include +//! [1] #include "googlesuggest.h" #define GSUGGEST_URL "http://google.com/complete/search?output=toolbar&q=%1" +//! [1] +//! [2] GSuggestCompletion::GSuggestCompletion(QLineEdit *parent): QObject(parent), editor(parent) { popup = new QTreeWidget; + popup->setWindowFlags(Qt::Popup); + popup->setFocusPolicy(Qt::NoFocus); + popup->setFocusProxy(parent); + popup->setMouseTracking(true); + popup->setColumnCount(2); popup->setUniformRowHeights(true); popup->setRootIsDecorated(false); @@ -57,18 +62,13 @@ GSuggestCompletion::GSuggestCompletion(QLineEdit *parent): QObject(parent), edit popup->setSelectionBehavior(QTreeWidget::SelectRows); popup->setFrameStyle(QFrame::Box | QFrame::Plain); popup->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - popup->header()->hide(); + popup->installEventFilter(this); - popup->setMouseTracking(true); connect(popup, SIGNAL(itemClicked(QTreeWidgetItem*, int)), SLOT(doneCompletion())); - popup->setWindowFlags(Qt::Popup); - popup->setFocusPolicy(Qt::NoFocus); - popup->setFocusProxy(parent); - timer = new QTimer(this); timer->setSingleShot(true); timer->setInterval(500); @@ -79,12 +79,16 @@ GSuggestCompletion::GSuggestCompletion(QLineEdit *parent): QObject(parent), edit this, SLOT(handleNetworkData(QNetworkReply*))); } +//! [2] +//! [3] GSuggestCompletion::~GSuggestCompletion() { delete popup; } +//! [3] +//! [4] bool GSuggestCompletion::eventFilter(QObject *obj, QEvent *ev) { if (obj != popup) @@ -131,9 +135,12 @@ bool GSuggestCompletion::eventFilter(QObject *obj, QEvent *ev) return false; } +//! [4] +//! [5] void GSuggestCompletion::showCompletion(const QStringList &choices, const QStringList &hits) { + if (choices.isEmpty() || choices.count() != hits.count()) return; @@ -163,7 +170,9 @@ void GSuggestCompletion::showCompletion(const QStringList &choices, const QStrin popup->setFocus(); popup->show(); } +//! [5] +//! [6] void GSuggestCompletion::doneCompletion() { timer->stop(); @@ -172,26 +181,28 @@ void GSuggestCompletion::doneCompletion() QTreeWidgetItem *item = popup->currentItem(); if (item) { editor->setText(item->text(0)); - QKeyEvent *e; - e = new QKeyEvent(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier); - QApplication::postEvent(editor, e); - e = new QKeyEvent(QEvent::KeyRelease, Qt::Key_Enter, Qt::NoModifier); - QApplication::postEvent(editor, e); + QMetaObject::invokeMethod(editor, "returnPressed"); } } +//! [6] -void GSuggestCompletion::preventSuggest() -{ - timer->stop(); -} - +//! [7] void GSuggestCompletion::autoSuggest() { QString str = editor->text(); QString url = QString(GSUGGEST_URL).arg(str); networkManager.get(QNetworkRequest(QString(url))); } +//! [7] + +//! [8] +void GSuggestCompletion::preventSuggest() +{ + timer->stop(); +} +//! [8] +//! [9] void GSuggestCompletion::handleNetworkData(QNetworkReply *networkReply) { QUrl url = networkReply->url(); @@ -199,20 +210,20 @@ void GSuggestCompletion::handleNetworkData(QNetworkReply *networkReply) QStringList choices; QStringList hits; - QString response(networkReply->readAll()); + QByteArray response(networkReply->readAll()); QXmlStreamReader xml(response); while (!xml.atEnd()) { xml.readNext(); - if (xml.isStartElement()) { + if (xml.tokenType() == QXmlStreamReader::StartElement) if (xml.name() == "suggestion") { QStringRef str = xml.attributes().value("data"); choices << str.toString(); } - else if (xml.name() == "num_queries") { + if (xml.tokenType() == QXmlStreamReader::StartElement) + if (xml.name() == "num_queries") { QStringRef str = xml.attributes().value("int"); hits << str.toString(); } - } } showCompletion(choices, hits); @@ -220,3 +231,4 @@ void GSuggestCompletion::handleNetworkData(QNetworkReply *networkReply) networkReply->deleteLater(); } +//! [9] \ No newline at end of file diff --git a/examples/network/googlesuggest/googlesuggest.h b/examples/network/googlesuggest/googlesuggest.h index 2a3c878..c33df36 100644 --- a/examples/network/googlesuggest/googlesuggest.h +++ b/examples/network/googlesuggest/googlesuggest.h @@ -42,8 +42,9 @@ #ifndef GOOGLESUGGEST_H #define GOOGLESUGGEST_H +#include +#include #include -#include QT_BEGIN_NAMESPACE class QLineEdit; @@ -52,6 +53,7 @@ class QTimer; class QTreeWidget; QT_END_NAMESPACE +//! [1] class GSuggestCompletion : public QObject { Q_OBJECT @@ -75,6 +77,6 @@ private: QTimer *timer; QNetworkAccessManager networkManager; }; - +//! [1] #endif // GOOGLESUGGEST_H diff --git a/examples/network/googlesuggest/searchbox.cpp b/examples/network/googlesuggest/searchbox.cpp index 21599e0..ae08a75 100644 --- a/examples/network/googlesuggest/searchbox.cpp +++ b/examples/network/googlesuggest/searchbox.cpp @@ -47,12 +47,12 @@ #define GSEARCH_URL "http://www.google.com/search?q=%1" - +//! [1] SearchBox::SearchBox(QWidget *parent): QLineEdit(parent) { completer = new GSuggestCompletion(this); - connect(this, SIGNAL(returnPressed()), SLOT(doSearch())); + connect(this, SIGNAL(returnPressed()),this, SLOT(doSearch())); setWindowTitle("Search with Google"); @@ -60,10 +60,13 @@ SearchBox::SearchBox(QWidget *parent): QLineEdit(parent) resize(400, height()); setFocus(); } +//! [1] +//! [2] void SearchBox::doSearch() { completer->preventSuggest(); QString url = QString(GSEARCH_URL).arg(text()); QDesktopServices::openUrl(QUrl(url)); } +//! [2] \ No newline at end of file diff --git a/examples/network/googlesuggest/searchbox.h b/examples/network/googlesuggest/searchbox.h index 4b03dba..ec18bb0 100644 --- a/examples/network/googlesuggest/searchbox.h +++ b/examples/network/googlesuggest/searchbox.h @@ -42,6 +42,7 @@ #ifndef SEARCHBOX_H #define SEARCHBOX_H +//! [1] #include class GSuggestCompletion; @@ -58,6 +59,7 @@ protected slots: private: GSuggestCompletion *completer; +//! [1] }; -- cgit v0.12