Qt: Висновок звіту стандартними засобами (або живемо без генераторів звіту)

Нудне вступ з Qt 4.8
Нещодавно колега по роботі запитав про досвід використання побудови звітів під Qt (починаємо потихеньку впроваджувати SCADA, написану на Qt) — в силу поставленого завдання дуже потрібна річ. Генераторами ніхто не користувався (на даній платформі), але звіти ми я якимось чином робиві без використання FastReport і волочіння зайвих додатків.

Покопавшись в проектах, знайшов додаток із звітами, віджетами для попереднього перегляду (QLabel, QTableView....). Вид звіту «preview»:

image

Вікно програми нижче. Під Qt 5.x сам додаток вимагає переробки, а от звіти працюють:



Конструювалися звіти лише і тільки до компіляції програми — було завдання зробити швидко (з xml — досвіду роботи майже не було).

Reporter Клас — генератор звіту
Почитавши про форматування Qt народився клас Reporter (містить у собі QTextDocument, QTextCursor і методи роботи з ними). Вся робота всередині Reporter полягає у формуванні QTextDocument, і далі роздруківка на принтері, або відображення в QWidget.

Для шапки документа:

  • setDateDoc() — час
  • setCompanyDoc() — організація
  • setCaptionDoc() — назва
контенту:

  • setDataHeader(QStringList strLst) — шапка таблиці
  • addData(QStringList strLst) — дані
Робота здійснюється c QTextBlockFormat, або QTextTableFormat, дійові особи з секції private

private:
int m_iCntTbls;
int m_iColCnt;
QTextDocument *const m_document;
QTextCursor m_cursor;

Потрібен заголовок — не проблема

void Reporter::setCompanyDoc(QString str){
// ставимо позицію курсору поза чого-небудь раніше редактированного
m_cursor.movePosition(QTextCursor::End);
if(m_iCntTbls>0){
m_cursor.insertBlock();
m_cursor.insertBlock();
m_cursor.movePosition(QTextCursor::End);
}
// правимо формат
QTextBlockFormat blockFrm;

blockFrm.setTopMargin(5);
blockFrm.setBottomMargin(5);
blockFrm.setAlignment(Qt::AlignLeft);
blockFrm.setBackground(QBrush(QColor("lightGray")));
// вставляємо форматування і текст до нього
m_cursor.insertBlock(blockFrm);
m_cursor.insertText(str);

}

Аналогічно працюємо з датою і заголовком таблиці.

Для створення таблиці, необхідно спочатку викликати метод setDataHeader(QStrignList strLst), число стовпців буде дорівнює числу рядків у списку:

void Reporter::setDataHeader(QStringList strLst){
// ставимо позицію курсору поза чого-небудь раніше редактированного
m_cursor.movePosition(QTextCursor::End);
if(m_iCntTbls>0){
m_cursor.insertBlock();
m_cursor.insertBlock();
m_cursor.movePosition(QTextCursor::End);
}
// задамо як будемо малювати рамку
QBrush borderBrush(Qt::SolidPattern);
// опрацюємо формат таблиці
QTextTableFormat tableFormat;
tableFormat.setCellPadding(5);
tableFormat.setCellSpacing(0);
tableFormat.setHeaderRowCount(1);
tableFormat.setBorderBrush(borderBrush);
tableFormat.setBorderStyle(QTextFrameFormat::BorderStyle_Ridge);
tableFormat.setBorder(1);
tableFormat.setWidth(QTextLength(QTextLength::PercentageLength,100)); 
// вставимо її і заповнимо шапку
m_cursor.insertTable(1,strLst.count(),tableFormat);
foreach(QString str, strLst){
m_cursor.insertText(str);
m_cursor.movePosition(QTextCursor::NextCell);
}
m_iCntTbls++;
m_iColCnt=strLst.count();
}

Не забудемо наприкінці сказати, що вже є як мінімум 1 таблиця і зафіксуємо число колонок. А далі заповнюємо даними:

void Reporter::addData(QStringList strLst){
// якщо даних менше, то доповнимо порожніми
if(strLst.count()<m_iColCnt){
int iAdd=m_iColCnt-strLst.count();
while(iAdd>0){
iAdd--;
strLst<<"";
}
}else
// даних більше - то лаємося
if(strLst.count()>m_iColCnt){
QMessageBox::critical(0, tr("AddData"),
tr("Too many elements to paste wait %1 got %2").arg(m_iColCnt).arg(strLst.count()),
QMessageBox::Ok,QMessageBox::Ok
);
return;
}
// заповнюємо
QTextTable *tbl=m_cursor.currentTable();
tbl->appendRows(1);
m_cursor.movePosition(QTextCursor::PreviousRow);
foreach(QString str, strLst){
m_cursor.movePosition(QTextCursor::NextCell);
m_cursor.insertText(str);
}
}

Приклад в дії:
Беремо наш клас, створюємо об'єкт (m_reporter) і штовхаємо в нього дані.

m_reporter->setCompanyDoc(QString::fromLocal8Bit("НПФ Промавтоматика"));
m_reporter->setCaptionDoc(QString::fromLocal8Bit(" Звіт №0"));
strLst<<QString::fromLocal8Bit("Рецепт ")<<setStr("Продукт ")<<setStr("Вага потрібний")<<setStr("Вага набраний")
<<setStr("Завдання відпр.")<<setStr("Завдання підтвер.")<<setStr("Партія");
.....

Отримуємо щось подібне:

image

(вивів на QDialog з допомогою )

QPainter painter(this);
QRect rec(0,0,this->width(),this->height());
m_reporter->getTextDoc()->drawContents(&painter,rec);

Для того щоб роздрукувати, потрібно викликати метод Reporter::printDoc(QPrinter).

Резюме
Для швидкого вирішення конкретної задачі підходять стандартні засоби Qt, з яких можна спорудити відповідні інструменти, однак для надання якісного продукту даний метод не застосовний.

Головним мінусом є необхідність компіляції проекту, що містить звіт при зміні формату документа, при додаванні нових звітів і т. д. дякую Всім, хто дочитав.

p.s. github.com/AlexisVaBel/QtReport.git (все, що потрібно для погратися).
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.