Writing ODF Files with Qt

Материал из Wiki.crossplatform.ru

(Различия между версиями)
Перейти к: навигация, поиск
 
(4 промежуточные версии не показаны)
Строка 1: Строка 1:
-
{{menu_Qt_Издания}}
+
{{Панель навигации по Qt Quarterly|Выпуск 27}}
by Thomas Zander
by Thomas Zander
Строка 5: Строка 5:
Предстоящий релиз Qt 4.5 отмечается появлением класса QTextDocumentWriter, который позволяет создавать файлы OpenDocument Format (ODF) из любого текстового документа Qt. Это открывает путь для автоматического создания и распространения документа в стандартном совместимом формате, который позволяет пользователю открыть его в различных текстовых редакторах.
Предстоящий релиз Qt 4.5 отмечается появлением класса QTextDocumentWriter, который позволяет создавать файлы OpenDocument Format (ODF) из любого текстового документа Qt. Это открывает путь для автоматического создания и распространения документа в стандартном совместимом формате, который позволяет пользователю открыть его в различных текстовых редакторах.
-
*[[#gettingstarted | Getting Started]]
+
__TOC__
-
*[[#writingthebill | Writing the Bill]]
+
-
*[[#drawingconclusions | Drawing Conclusions]]
+
</div>
</div>
-
A prime use case for automated document creation is report generation. An example of this is a store that performs periodic inventory checks and, thus, needs to know if what the database thinks is on the shelves actually reflects real life. Consider some software that takes the database records and creates a readable document for one day's worth of work. That document can then be exported to ODF and sent to the person who does the work.
+
Основным поводом к использованию автоматической генерации документов является создание отчетов. Возьмем, к примеру, магазин, в котором периодически происходит переучет товара и возникает необходимость узнать, действительно ли то, что лежит на полках, и то, что содержится в базе данных, есть одно и то же. Предположим, существует программа, которая обрабатывает базу данных и создает документ, содержащий план работы на день. Этот документ экспортируется в ODF и отсылается человеку, производящему проверку.
[[Image:qq27-phonebill-pretty.png|center]]
[[Image:qq27-phonebill-pretty.png|center]]
-
In this article we will write a simple report generator in the form of a phone bill creator. We will create one class, the <tt>PhoneBillWriter</tt>, to represent the phone bill for one client, and you can modify it or use it as a starting point for your own reporting needs.
+
В данной статье будет написан простой генератор отчетов, создающий телефонный счет. Мы создадим один класс, <tt>PhoneBillWriter</tt>, соответствующий телефонному счету для одного клиента, а вы можете модифицировать его, чтобы использовать как отправную точку для своих генераторов отчета.
-
<div id="gettingstarted"></div>
 
-
===Getting Started===
 
-
The <tt>PhoneBillWriter</tt> class has the following definition:
+
===Вначале===
 +
Класс <tt>PhoneBillWriter</tt> будет иметь следующее определение:
<source lang="cpp-qt">
<source lang="cpp-qt">
     class PhoneBillWriter
     class PhoneBillWriter
Строка 27: Строка 24:
       struct PhoneCall {
       struct PhoneCall {
         QDateTime date;
         QDateTime date;
-
         int duration; // in seconds
+
         int duration; // в секундах
-
         int cost; // in euro-cents
+
         int cost; // в евро-центах
       };
       };
       void addPhoneCall(const PhoneCall &amp;call);
       void addPhoneCall(const PhoneCall &amp;call);
Строка 41: Строка 38:
</source>  
</source>  
-
This allows us to create one <tt>PhoneBillWriter</tt> for each bill and fill it with data, using calls to the <tt>addPhoneCall()</tt> method. After we have added all our data we call the <tt>write()</tt> method to finish the current bill. This is a classic case of using the builder pattern.
+
Это даст нам способ создания одного экземпляра <tt>PhoneBillWriter</tt> для каждого счета, и наполнения его данными при помощи вызовов метода <tt>addPhoneCall()</tt>. Закончив вносить информацию, мы вызываем метод <tt>write()</tt> для того, чтобы закрыть текущий счет. Это классический пример использования паттерна "Строитель" ("Builder").
-
Internally, the class uses a [[Qt:Документация 4.3.2//qtextdocument | QTextDocument]] as can be seen in the list of private members. The [[Qt:Документация 4.3.2//qtextdocument | QTextDocument]] class is used in a lot of places in Qt and its widgets. [[Qt:Документация 4.3.2//qtextedit | QTextEdit]] uses one and you can access it using the [[Qt:Документация 4.3.2//qtextedit#document | QTextEdit::document()]] method. The [[Qt:Документация 4.3.2//qtextdocument | QTextDocument]] is a text document with the ability to contain structured text as well as markup (bold and italic) and much more. See Qt's ''Text Edit'' demo for an overview.  
+
Внутри себя класс пользуется [[Qt:Документация 4.3.2//qtextdocument | QTextDocument]], что видно в списке его закрытых членов. Класс [[Qt:Документация 4.3.2//qtextdocument | QTextDocument]] применяется во многих местах в Qt и ее виджетах. [[Qt:Документация 4.3.2//qtextedit | QTextEdit]] является одним из таких виджетов, и для доступа к документу в нем вызывается метод [[Qt:Документация 4.3.2//qtextedit#document | QTextEdit::document()]]. [[Qt:Документация 4.3.2//qtextdocument | QTextDocument]] - это текстовый документ, который может содержать структурированный текст, элементы разметки (полужирный шрифт, курсив) и многое другое. Для ознакомления см. демонстрационную программу Qt ''Text Edit''.
-
The class [[Qt:Документация 4.3.2//qtextcursor | QTextCursor]] is provided to allow the content of a text document to be manipulated. This is very much in the spirit of having a blinking cursor on your word processor and having the ability to move the cursor around and insert text. The [[Qt:Документация 4.3.2//qtextcursor | QTextCursor]] class gives us the ability to do all this programatically.
+
Класс [[Qt:Документация 4.3.2//qtextcursor | QTextCursor]] создан для того, чтобы предоставить способ управления содержимым текстового документа. Это что-то вроде мигающего курсора, который вы видите на экране в текстовых процессорах, и вводите текст, ориентируясь на него. Класс [[Qt:Документация 4.3.2//qtextcursor | QTextCursor]] служит достижению такой цели внутри программы.
-
<div id="writingthebill"></div>
 
-
===Writing the Bill===
 
-
The approach used in this report writer is to create a [[Qt:Документация 4.3.2//qtextdocument | QTextDocument]] in the constructor and add the header and nice formatting information that will be the same for each phone bill. Here's a simple implementation:  
+
===Создание счета===
 +
Мы создадим экземпляр [[Qt:Документация 4.3.2//qtextdocument | QTextDocument]] в конструкторе нашего генератора отчетов, затем добавим заголовок и симпатичное форматирование, которые станут общими для каждого телефонного счета. Вот простая реализация:
<source lang="cpp-qt">
<source lang="cpp-qt">
     PhoneBillWriter::PhoneBillWriter(const QString &amp;client)
     PhoneBillWriter::PhoneBillWriter(const QString &amp;client)
Строка 79: Строка 75:
     }
     }
-
</source>  
+
</source>
-
We start by adding a personal note and follow this with a table and the table header, which we fill them with the header labels. We don't need to specify in advance how many rows there are in the table.  
+
Для начала мы ввели в заголовок информацию о персоне, после чего создали таблицу с заголовком, который заполнили метками с названиями столбцов. Заранее задавать количество строк нашей таблицы мы не станем.
-
The interesting bits are the <tt>m_cursor</tt> usage, which includes call to methods like <tt>insertTable()</tt> and <tt>insertText()</tt> to actually modify the document.  
+
Обратите внимание на применение <tt>m_cursor</tt> - мы вызываем методы наподобие <tt>insertTable()</tt> и <tt>insertText()</tt>, чтобы непосредственно изменить наш документ.
-
The [[Qt:Документация 4.3.2//qtextcursor | QTextCursor]] also understands the concepts of selecting and removing text. A very powerful method on the cursor is <tt>movePosition()</tt>, to which you can pass one of the many values from its <tt>MoveOperation</tt> enum. This makes the class really behave like a cursor since all the usual navigation operations are there. In this case we use a navigation operation that's new in Qt 4.5: the <tt>NextCell</tt> operation moves the cursor to the start of the next table cell. As a result, the text passed to the following <tt>insertText()</tt> call will end up at the start of the table cell.
+
Класс [[Qt:Документация 4.3.2//qtextcursor | QTextCursor]] также умеет выделять и удалять текст. Очень мощным инструментом можно назвать его метод <tt>movePosition()</tt>, которому вы передаете один из множества элементов перечисления <tt>MoveOperation</tt>. Это заставляет класс вести себя как настоящий текстовый курсор, поскольку все операции ориентации в документе в нем присутствуют. В нашем примере мы используем операцию, новую для Qt 4.5: <tt>NextCell</tt> перемещает курсор в начало следующей ячейки таблицы. В результате текст, который мы затем передаем при вызове метода <tt>insertText()</tt>, появится в данной ячейке таблицы.
-
After setting up the header of the document we are ready to add actual user information to it. The caller can add individual phone calls using the <tt>addPhoneCall()</tt> method, passing in the individual fields that we show in the table.
+
После создания заголовка документа мы готовы передать документу реальную информацию счета. Отдельные телефонные звонки добавим в таблицу методом <tt>addPhoneCall()</tt>, передав аргументом конкретные поля, которые мы и хотим увидеть в таблице.
<source lang="cpp-qt">
<source lang="cpp-qt">
     void PhoneBillWriter::addPhoneCall(
     void PhoneBillWriter::addPhoneCall(
Строка 106: Строка 102:
</source>  
</source>  
-
To show the phone call later, we add a row to our table and then continue to add the text to the document for each of the table cells. We call the appropriate [[Qt:Документация 4.3.2//qstring | QString]] methods to convert the integer data into text so we can fine-tune the way our data is shown. After all, the goal in our example is to make a pretty looking version of the raw data, and properly formatted text is the way to get there.  
+
Для того, чтобы телефонный звонок позже появился в таблице, мы добавляем в нее строку и вводим текст в каждую из добавившихся ячеек. Для преобразования численной информации в строковую мы используем соответствующие методы класса [[Qt:Документация 4.3.2//qstring | QString]], поэтому мы можем тонко настроить формат, в котором данные будут видны пользователю. В конце концов, наша задача - сделать симпатичное представление "сырых" данных - и правильно отформатированный текст является как раз тем, что нам нужно.
-
Having just text, even with tables, makes for boring reading. Adding graphs or other pictures is always a good way to make a document much more readable. This document talks about creating an ODF document, and the OpenDocument Format allows us to embed images into the final file, unlike HTML, for example.  
+
Если документ содержит только текст, пусть даже текст и таблицы, его очень печально воспринимать человеку. Добавив графики или прочие картинки, мы делаем документ гораздо более интересным и читабельным для пользователя. Данная статья повествует о создании документа ODF, а формат OpenDocument позволяет встраивать изображения в конечный файл, в отличие от, скажем, HTML.
-
In order to get the document into the final ODF file all we need to do is insert the image into the [[Qt:Документация 4.3.2//qtextdocument | QTextDocument]]. It will not be a big surprise to learn that there is a [[Qt:Документация 4.3.2//qtextcursor#insertImage | QTextCursor::insertImage()]] method to do exactly this.  
+
На пути к тому, чтобы превратить наш документ в финальный файл ODF, нам осталось только вставить картинку в [[Qt:Документация 4.3.2//qtextdocument | QTextDocument]]. Думаем, вы не очень удивитесь, узнав о существовании метода [[Qt:Документация 4.3.2//qtextcursor#insertImage | QTextCursor::insertImage()]], производящего в точности данное действие.
-
For our example document, we wanted to add a graph to show the amount of calls the client made in the last few months. For this the following code was used:  
+
Добавим в наш образец документа график, показывающий сравнение количества звонков, которые клиент совершал за предшествующие этому несколько месяцев. Для этого используем код:  
<source lang="cpp-qt">
<source lang="cpp-qt">
     QList<int> callsPerMonth;
     QList<int> callsPerMonth;
Строка 119: Строка 115:
     phoneBill.addGraph(callsPerMonth, "Your past usage:");
     phoneBill.addGraph(callsPerMonth, "Your past usage:");
</source>  
</source>  
-
To actually create the graph, our solution is to create a [[Qt:Документация 4.3.2//qimage | QImage]], use a [[Qt:Документация 4.3.2//qpainter | QPainter]] to draw the values onto it, and then insert the image into the document at the right location.
+
Непосредственно создание графика будет заключаться в инстанцировании объекта изображения [[Qt:Документация 4.3.2//qimage | QImage]], использовании объекта класса QPainter для рисования значений на изображении, и вставке изобажения в нужное место нашего документа.
<source lang="cpp-qt">
<source lang="cpp-qt">
     void PhoneBillWriter::addGraph(QList<int> values, const QString &amp;subtext)
     void PhoneBillWriter::addGraph(QList<int> values, const QString &amp;subtext)
Строка 131: Строка 127:
       QPainter painter(&amp;image);
       QPainter painter(&amp;image);
       painter.fillRect(0, 0, image.width(), image.height(),
       painter.fillRect(0, 0, image.width(), image.height(),
-
                       Qt::white); // background
+
                       Qt::white); // фон
       for (int index = 0; index < values.count(); ++index) {
       for (int index = 0; index < values.count(); ++index) {
-
         // Adjust scale to our 100 pixel tall image:
+
         // Изменить шкалу для нашей 100-пиксельной в высоту картинки:
         int height = values[index] * 100 / max;
         int height = values[index] * 100 / max;
         painter.fillRect(index * columnSize,
         painter.fillRect(index * columnSize,
Строка 149: Строка 145:
</source>  
</source>  
-
First, we calculate the required width of our graph based on the amount of values passed in. We choose to always have a height of 100 pixels and scale the values to fit in that range since most graphs are designed to show the relative sizes of the values to each other.  
+
Сначала вычислим требуемую ширину графика, базирующуюся на количестве значений, передаваемых в него. Пусть график всегда имеет высоту, равную 100 пикселам, и все значения отмасштабируются так, чтобы уместиться в этот интервал - ведь большинство графиков существуют для отображения размеров одной величины относительно другой.  
-
We use a monochrome format for the image, which means only two colors. You can use any image format you want for ODF, though. Two colors just seems enough for this use case.
+
Применим монохромный формат картинки, что означает всего два цвета для ее создания. Хотя вы можете использовать любой другой формат для ODF, для нашего случая вполне достаточно двух цветов.
-
After creation of the actual image we create a new [[Qt:Документация 4.3.2//qtextcursor | QTextCursor]] and move it to the end of the document, where we insert the graph. An important note is that this cursor has a position that may be different from the <tt>m_cursor</tt> object. The reason for creating a new cursor here is to make sure the user can still call <tt>addPhoneCall()</tt> after the <tt>addGraph()</tt> call without affecting the positioning of the textual content.
+
После того, как изображение готово, создадим новый объект [[Qt:Документация 4.3.2//qtextcursor | QTextCursor]], и переместим его в конец документа в то место, куда и будет вставлен график. Обратите внимание на то, что положение этого курсора будет независимо от положения <tt>m_cursor</tt>. Мы сделали новый курсор для того, чтобы пользователь мог вызывать метод <tt>addPhoneCall()</tt> уже после того, как добавлен график, без изменения взаимного положения частей, составляющих наш документ.
-
The last part of our <tt>PhoneBillWriter</tt> class is to actually create the OpenDocument Format file from all the information we put into the writer object. The hard work is all done by the [[Qt:Документация 4.3.2//qtextdocumentwriter | QTextDocumentWriter]] class, which is capable of writing to various formats, like plain text, HTML and ODF. The default is ODF, making the implementation trivial for us:
+
Последним штрихом к классу <tt>PhoneBillWriter</tt> станет непосредственно создание файла формата OpenDocument, содержащего все данные, которые мы записали в объект класса. Весь этот нелегкий процесс выполнит класс  [[Qt:Документация 4.3.2//qtextdocumentwriter | QTextDocumentWriter]], который отвечает за запись в некоторые форматы, такие как простой текст, HTML и ODF. По умолчанию используется ODF, что облегчает нашу запись:
<source lang="cpp-qt">
<source lang="cpp-qt">
     void PhoneBillWriter::write(const QString &amp;fileName)
     void PhoneBillWriter::write(const QString &amp;fileName)
Строка 165: Строка 161:
[[Image:qq27-phonebill.png|center]]
[[Image:qq27-phonebill.png|center]]
-
The above image shows the final document in OpenOffice.org. As it stands, it's intentionally simple for demonstration purposes, but there's a slightly more decorative example included alongside it in the archive available from the ''Qt Quarterly'' Web site.
+
Изображение сверху демонстрирует наш документ, открытый в OpenOffice.org. Как вы видите, он достаточно прост, каким и должен быть образец, но вы можете изучить гораздо более декорированный вариант, доступный по ссылке чуть ниже в виде архива с сайта ''Qt Quarterly''.
-
<div id="drawingconclusions"></div>
 
-
===Drawing Conclusions===
 
-
The example we have presented is fairly simple and is focused on showing the basic ODF writing capabilities of [[Qt:Документация 4.3.2//qtextdocumentwriter | QTextDocumentWriter]] rather than making the output look as pretty as possible.
 
-
Qt has plenty of features that we can use to improve the appearance of the phone bill and usefulness of the writer. For example, we could use the Qt SQL module to extract real information from an existing database and use [[Qt:Документация 4.3.2//qpainter | QPainter]] to draw more visually appealing graphs and charts.
+
===В заключение===
 +
Образец, рассмотренный в статье, очень прост и имеет целью показать базовые способности класса [[Qt:Документация 4.3.2//qtextdocumentwriter | QTextDocumentWriter]] по записи ODF-файла, а не сделать вывод как можно красивее.
-
We could also create a nice frontend application to help the user create reports, or use Qt's ODF capabilities to do something completely unrelated to phone bill, reports or accounting. What you create with this new feature is up to you!
+
Qt включает массу способов улучшить внешний вид нашего телефонного счета и полезность записывающей программы. Например, мы можем использовать SQL-модуль и получить реальную информацию из реальной базы данных, и использовать [[Qt:Документация 4.3.2//qpainter | QPainter]], чтобы отрисовывать более привлекательные графики и диаграммы.
-
The source code for the example described in this article can be obtained [http://doc.trolltech.com/qq/qq27-odfwriter.zip from the ''Qt Quarterly'' Web site].
+
Вы также можете создать приятный графический интерефейс вашему приложению, чтобы облегчить труд пользователя по созданию отчетов; или использовать поддержку ODF в Qt для совершенно иных, нежели телефонные счета, отчеты или аккаунты, целей. Все, что вы будете делать с этой предоставленной возможностью, зависит только от вас!
-
[[Категория:Qt Издания]]
+
Исходные коды образца, представленного в данной статье, доступны здесь: [http://doc.trolltech.com/qq/qq27-odfwriter.zip from the ''Qt Quarterly'' Web site].

Текущая версия на 16:12, 3 апреля 2010

Image:qt-logo_new.png Image:qq-title-article.png
Qt Quarterly | Выпуск 27 | Документация


by Thomas Zander

Предстоящий релиз Qt 4.5 отмечается появлением класса QTextDocumentWriter, который позволяет создавать файлы OpenDocument Format (ODF) из любого текстового документа Qt. Это открывает путь для автоматического создания и распространения документа в стандартном совместимом формате, который позволяет пользователю открыть его в различных текстовых редакторах.

Содержание

Основным поводом к использованию автоматической генерации документов является создание отчетов. Возьмем, к примеру, магазин, в котором периодически происходит переучет товара и возникает необходимость узнать, действительно ли то, что лежит на полках, и то, что содержится в базе данных, есть одно и то же. Предположим, существует программа, которая обрабатывает базу данных и создает документ, содержащий план работы на день. Этот документ экспортируется в ODF и отсылается человеку, производящему проверку.

center

В данной статье будет написан простой генератор отчетов, создающий телефонный счет. Мы создадим один класс, PhoneBillWriter, соответствующий телефонному счету для одного клиента, а вы можете модифицировать его, чтобы использовать как отправную точку для своих генераторов отчета.


[править] Вначале

Класс PhoneBillWriter будет иметь следующее определение:

    class PhoneBillWriter
    {
    public:
      PhoneBillWriter(const QString &amp;client);
      ~PhoneBillWriter();
      struct PhoneCall {
        QDateTime date;
        int duration; // в секундах
        int cost; // в евро-центах
      };
      void addPhoneCall(const PhoneCall &amp;call);
      void addGraph(QList<int> values,
                    const QString &amp;subtext);
      void write(const QString &amp;fileName);
 
    private:
      QTextDocument * const m_document;
      QTextCursor m_cursor;
    };

Это даст нам способ создания одного экземпляра PhoneBillWriter для каждого счета, и наполнения его данными при помощи вызовов метода addPhoneCall(). Закончив вносить информацию, мы вызываем метод write() для того, чтобы закрыть текущий счет. Это классический пример использования паттерна "Строитель" ("Builder").

Внутри себя класс пользуется QTextDocument, что видно в списке его закрытых членов. Класс QTextDocument применяется во многих местах в Qt и ее виджетах. QTextEdit является одним из таких виджетов, и для доступа к документу в нем вызывается метод QTextEdit::document(). QTextDocument - это текстовый документ, который может содержать структурированный текст, элементы разметки (полужирный шрифт, курсив) и многое другое. Для ознакомления см. демонстрационную программу Qt Text Edit.

Класс QTextCursor создан для того, чтобы предоставить способ управления содержимым текстового документа. Это что-то вроде мигающего курсора, который вы видите на экране в текстовых процессорах, и вводите текст, ориентируясь на него. Класс QTextCursor служит достижению такой цели внутри программы.


[править] Создание счета

Мы создадим экземпляр QTextDocument в конструкторе нашего генератора отчетов, затем добавим заголовок и симпатичное форматирование, которые станут общими для каждого телефонного счета. Вот простая реализация:

    PhoneBillWriter::PhoneBillWriter(const QString &amp;client)
        : m_document(new QTextDocument()),
        m_cursor(m_document)
    {
      m_cursor.insertText(QObject::tr(
               "Phone bill for %1\n").arg(client));
 
      QTextTableFormat tableFormat;
      tableFormat.setCellPadding(5);
      tableFormat.setHeaderRowCount(1);
      tableFormat.setBorderStyle(
                  QTextFrameFormat::BorderStyle_Solid);
      tableFormat.setWidth(QTextLength(
                  QTextLength::PercentageLength, 100));
      m_cursor.insertTable(1, 3, tableFormat);
      m_cursor.insertText(QObject::tr("Date"));
      m_cursor.movePosition(QTextCursor::NextCell);
      m_cursor.insertText(QObject::tr("Duration (sec)"));
      m_cursor.movePosition(QTextCursor::NextCell);
      m_cursor.insertText(QObject::tr("Cost"));
    }
 
    PhoneBillWriter::~PhoneBillWriter()
    {
      delete m_document;
    }

Для начала мы ввели в заголовок информацию о персоне, после чего создали таблицу с заголовком, который заполнили метками с названиями столбцов. Заранее задавать количество строк нашей таблицы мы не станем.

Обратите внимание на применение m_cursor - мы вызываем методы наподобие insertTable() и insertText(), чтобы непосредственно изменить наш документ.

Класс QTextCursor также умеет выделять и удалять текст. Очень мощным инструментом можно назвать его метод movePosition(), которому вы передаете один из множества элементов перечисления MoveOperation. Это заставляет класс вести себя как настоящий текстовый курсор, поскольку все операции ориентации в документе в нем присутствуют. В нашем примере мы используем операцию, новую для Qt 4.5: NextCell перемещает курсор в начало следующей ячейки таблицы. В результате текст, который мы затем передаем при вызове метода insertText(), появится в данной ячейке таблицы.

После создания заголовка документа мы готовы передать документу реальную информацию счета. Отдельные телефонные звонки добавим в таблицу методом addPhoneCall(), передав аргументом конкретные поля, которые мы и хотим увидеть в таблице.

    void PhoneBillWriter::addPhoneCall(
                  const PhoneBillWriter::PhoneCall &amp;call)
    {
      QTextTable *table = m_cursor.currentTable();
      table->appendRows(1);
      m_cursor.movePosition(QTextCursor::PreviousRow);
      m_cursor.movePosition(QTextCursor::NextCell);
      m_cursor.insertText(call.date.toString());
      m_cursor.movePosition(QTextCursor::NextCell);
      m_cursor.insertText(QString::number(call.duration));
      m_cursor.movePosition(QTextCursor::NextCell);
 
      QChar euro(0x20ac);
      m_cursor.insertText(QString("%1 %2").arg(euro)
              .arg(call.cost / (double) 100, 0, 'f', 2));
    }

Для того, чтобы телефонный звонок позже появился в таблице, мы добавляем в нее строку и вводим текст в каждую из добавившихся ячеек. Для преобразования численной информации в строковую мы используем соответствующие методы класса QString, поэтому мы можем тонко настроить формат, в котором данные будут видны пользователю. В конце концов, наша задача - сделать симпатичное представление "сырых" данных - и правильно отформатированный текст является как раз тем, что нам нужно.

Если документ содержит только текст, пусть даже текст и таблицы, его очень печально воспринимать человеку. Добавив графики или прочие картинки, мы делаем документ гораздо более интересным и читабельным для пользователя. Данная статья повествует о создании документа ODF, а формат OpenDocument позволяет встраивать изображения в конечный файл, в отличие от, скажем, HTML.

На пути к тому, чтобы превратить наш документ в финальный файл ODF, нам осталось только вставить картинку в QTextDocument. Думаем, вы не очень удивитесь, узнав о существовании метода QTextCursor::insertImage(), производящего в точности данное действие.

Добавим в наш образец документа график, показывающий сравнение количества звонков, которые клиент совершал за предшествующие этому несколько месяцев. Для этого используем код:

    QList<int> callsPerMonth;
    callsPerMonth << 6 << 84 << 76 << 0 << 93 << 128 << 76
                  << 31 << 19 << 4 << 12 << 78;
    phoneBill.addGraph(callsPerMonth, "Your past usage:");

Непосредственно создание графика будет заключаться в инстанцировании объекта изображения QImage, использовании объекта класса QPainter для рисования значений на изображении, и вставке изобажения в нужное место нашего документа.

    void PhoneBillWriter::addGraph(QList<int> values, const QString &amp;subtext)
    {
      const int columnSize = 10;
      int width = values.count() * columnSize;
      int max = 0;
      foreach (int x, values)
        max = qMax(max, x);
      QImage image(width, 100, QImage::Format_Mono);
      QPainter painter(&amp;image);
      painter.fillRect(0, 0, image.width(), image.height(),
                       Qt::white); // фон
      for (int index = 0; index < values.count(); ++index) {
        // Изменить шкалу для нашей 100-пиксельной в высоту картинки:
        int height = values[index] * 100 / max;
        painter.fillRect(index * columnSize,
            image.height() - height, columnSize, height,
            Qt::black);
      }
      painter.end();
 
      QTextCursor cursor(m_document);
      cursor.movePosition(QTextCursor::End);
      cursor.insertText(subtext);
      cursor.insertBlock();
      cursor.insertImage(image);
    }

Сначала вычислим требуемую ширину графика, базирующуюся на количестве значений, передаваемых в него. Пусть график всегда имеет высоту, равную 100 пикселам, и все значения отмасштабируются так, чтобы уместиться в этот интервал - ведь большинство графиков существуют для отображения размеров одной величины относительно другой.

Применим монохромный формат картинки, что означает всего два цвета для ее создания. Хотя вы можете использовать любой другой формат для ODF, для нашего случая вполне достаточно двух цветов.

После того, как изображение готово, создадим новый объект QTextCursor, и переместим его в конец документа в то место, куда и будет вставлен график. Обратите внимание на то, что положение этого курсора будет независимо от положения m_cursor. Мы сделали новый курсор для того, чтобы пользователь мог вызывать метод addPhoneCall() уже после того, как добавлен график, без изменения взаимного положения частей, составляющих наш документ.

Последним штрихом к классу PhoneBillWriter станет непосредственно создание файла формата OpenDocument, содержащего все данные, которые мы записали в объект класса. Весь этот нелегкий процесс выполнит класс QTextDocumentWriter, который отвечает за запись в некоторые форматы, такие как простой текст, HTML и ODF. По умолчанию используется ODF, что облегчает нашу запись:

    void PhoneBillWriter::write(const QString &amp;fileName)
    {
      QTextDocumentWriter writer(fileName);
      writer.write(m_document);
    }

center

Изображение сверху демонстрирует наш документ, открытый в OpenOffice.org. Как вы видите, он достаточно прост, каким и должен быть образец, но вы можете изучить гораздо более декорированный вариант, доступный по ссылке чуть ниже в виде архива с сайта Qt Quarterly.


[править] В заключение

Образец, рассмотренный в статье, очень прост и имеет целью показать базовые способности класса QTextDocumentWriter по записи ODF-файла, а не сделать вывод как можно красивее.

Qt включает массу способов улучшить внешний вид нашего телефонного счета и полезность записывающей программы. Например, мы можем использовать SQL-модуль и получить реальную информацию из реальной базы данных, и использовать QPainter, чтобы отрисовывать более привлекательные графики и диаграммы.

Вы также можете создать приятный графический интерефейс вашему приложению, чтобы облегчить труд пользователя по созданию отчетов; или использовать поддержку ODF в Qt для совершенно иных, нежели телефонные счета, отчеты или аккаунты, целей. Все, что вы будете делать с этой предоставленной возможностью, зависит только от вас!

Исходные коды образца, представленного в данной статье, доступны здесь: from the Qt Quarterly Web site.