Печать таблиц и документов в Qt

Г.Е. Берман (http://genberm.narod.ru)

При разработке многих приложений используются плоские (QTableWidget и QTableView) и иерархические (QTreeWidget и QTreeView) таблицы. При этом очень часто возникает потребность их печати. Более того, зачастую таблица является частью документа, т.е. необходимо в печатный документ, кроме информации таблицы, включить и другие реквизиты, например, название и номер документа, утверждающие и согласующие подписи и т.д.

В Qt стандартный способ подготовки информации для ее вывода на печать предполагает рисование выводимого объекта на QPrinter с использованием QPainter. На мой взгляд, рисование таблиц и, особенно, деревьев достаточно трудоемкое занятие. Если же нужно вывести документ сложной структуры, то трудоемкость еще более увеличивается.

В данной статье я предлагаю способ вывода информации на печать без ее прямого рисования. 

Метод состоит всего из нескольких шагов.

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

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

Остальные шаги реализуются в программе, они стандартные.

Второй шаг – это формирование HTML кода в программе. Фрагмент выполнения этого шага представлен ниже:

      QTextStream out;
      . . .
      QString s;
      out.setString(&s, QIODevice::ReadWrite);
      . . .
      out << "<html>\n";
      out << "<head>\n";
      out << "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />\n";
      . . .
   

В этом фрагменте:

Третий шаг – собственно сам вывод на печать. В примере, который прилагается к данной статье, используется класс QgebPrint. Это мой несколько упрощенный вариант класса печати, но предлагаемый подход в нем отражен полностью.

Заголовок класса QgebPrint:

  #ifndef QGEBPRINT_H
  #define QGEBPRINT_H
  #include <QPrintPreviewDialog>
  #include <QPrinter>
  #include <QWebView>
  class QgebPrint : public QObject
  {
      Q_OBJECT
  public:
      QgebPrint(QObject *parent = 0);
      ~QgebPrint();
      void printPreview(QString sHtml);
      void print(QString sHtml);
  signals:
      void printTable(QPrinter*);
  private:
      QWebView *webView;
      QPrintPreviewDialog *prevDlg;
  };
  #endif // QGEBPRINT_H

Класс включает два публичных метода:
    void print(QString sHtml);
    void printPreview(QString sHtml);
Первый из них обеспечивает прямой вывод информации на печать, а второй сначала выдает окно предварительного просмотра (рис.1), при нажатии на кнопку с иконкой принтера (крайняя справа) активизируется окно настройки принтера (рис.2), и уже затем, при нажатии на кнопку “Печать” выполняется непосредственно печать. Оба метода в качестве входного параметра принимают строку, которая используется как буфер текстового потока (см. шаг 2)

Рисунок 1 – Окно предварительного просмотра

Рисунок 2 – Окно настройки принтера

Реализация класса представлена ниже:

  #include "qgebprint.h"
  /*
    Конструктор
  */
  QgebPrint::QgebPrint(QObject *parent) : QObject(parent)
  {
      webView = new QWebView;
      webView->setVisible(false);     
  }
  /*
    Деструктор
  */
  QgebPrint::~QgebPrint()
  {
      delete webView;
  }
  /*
    Выдать диалог предварительного просмотра и печатать уже из него
  */
  void QgebPrint::printPreview(QString sHtml)
  {
  #ifndef QT_NO_PRINTDIALOG
      webView->setHtml(sHtml.toAscii()); 
      QPrinter printer;
      prevDlg = new QPrintPreviewDialog(&printer);
      connect(prevDlg, SIGNAL(paintRequested(QPrinter*)), 
              webView, SLOT(print(QPrinter*)));
      prevDlg->exec();
      delete prevDlg;
  #endif
  }
  /*
    Прямой вывод на печать
  */
  void QgebPrint::print(QString sHtml)
  {
  #ifndef QT_NO_PRINTDIALOG
      webView->setHtml(sHtml.toAscii());
      QPrinter printer;
      prevDlg = new QPrintPreviewDialog(&printer);
      connect(prevDlg, SIGNAL(paintRequested(QPrinter*)), 
              webView, SLOT(print(QPrinter*)));
      connect(this, SIGNAL(printTable(QPrinter*)), 
              prevDlg, SIGNAL(paintRequested(QPrinter*)));
      prevDlg->hide();
      emit printTable(&printer);
      delete prevDlg;
  #endif
  }
  

Суть подхода видна из текста методов. В его основе – использование метода print класса QWebView. Создаем скрытый экземпляр этого класса и передаем ему строку с кодом HTML документа. Далее, в случае печати с использованием предварительного просмотра:

В случае прямого вывода на печать действуем аналогично, но с одним отличием. Окно предварительного просмотра скрываем, а для того, чтобы сымитировать нажатие кнопки печати в этом окне посылаем сигнал printTable. Предварительно этот сигнал связываем с сигналом paintRequested класса QPrintPreview. В остальном все как в первом случае.

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

Примечание. Для правильной работы примера файл qt_ru.qm требуется положить в тот же каталог, в котором будет находиться исполняемый файл.

 

Сайт создан в системе uCoz