changeset 114:a5c8741b8662

Initial prototype support for printing list of users + print preview dialog. Has some issues currently.
author Matti Hamalainen <ccr@tnsp.org>
date Wed, 05 Jul 2017 12:48:55 +0300
parents 907f2ddf6801
children a596a7df736a
files Makefile Makefile.cross-mingw-win32 build-win32.sh src/main.cpp src/main.h src/mainwindow.ui
diffstat 6 files changed, 362 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Wed Jul 05 09:52:10 2017 +0300
+++ b/Makefile	Wed Jul 05 12:48:55 2017 +0300
@@ -4,7 +4,7 @@
 ###
 
 # Miscellaneous
-QT5_MODULES = Core Gui Widgets Sql
+QT5_MODULES = Core Gui Widgets Sql PrintSupport
 QT5_PREFIX = 
 BINTOOL_PREFIX =
 
--- a/Makefile.cross-mingw-win32	Wed Jul 05 09:52:10 2017 +0300
+++ b/Makefile.cross-mingw-win32	Wed Jul 05 12:48:55 2017 +0300
@@ -3,7 +3,7 @@
 ###
 
 # Miscellaneous
-QT5_MODULES = Core Gui Widgets Sql
+QT5_MODULES = Core Gui Widgets Sql PrintSupport
 QT5_PREFIX ?= /misc/packages/qt5-src
 QT5_BASE ?= $(QT5_PREFIX)/qtbase
 BINTOOL_PREFIX ?= i686-w64-mingw32-
--- a/build-win32.sh	Wed Jul 05 09:52:10 2017 +0300
+++ b/build-win32.sh	Wed Jul 05 12:48:55 2017 +0300
@@ -33,8 +33,9 @@
 
     do_cpinstall "$QT5_BASE/plugins/" "$TARGET" "platforms" "qwindows.dll"
     do_cpinstall "$QT5_BASE/plugins/" "$TARGET" "sqldrivers" "qsqlite.dll"
+    do_cpinstall "$QT5_BASE/plugins/" "$TARGET" "printsupport" "windowsprintersupport.dll"
 
-    for i in Core Gui Sql Widgets; do
+    for i in Core Gui Sql Widgets PrintSupport; do
         cp -f "$QT5_BASE/lib/Qt5$i.dll" "$TARGET"
     done
 
--- a/src/main.cpp	Wed Jul 05 09:52:10 2017 +0300
+++ b/src/main.cpp	Wed Jul 05 12:48:55 2017 +0300
@@ -9,6 +9,9 @@
 #include <QApplication>
 #include <QMessageBox>
 #include <QSettings>
+#include <QPrintDialog>
+#include <QPrintPreviewDialog>
+#include <QProgressDialog>
 #include "main.h"
 #include "ui_mainwindow.h"
 #include "ui_editperson.h"
@@ -337,6 +340,8 @@
     new QShortcut(QKeySequence(Qt::Key_PageDown), this, SLOT(selectRowNext()));
 
     new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Return), this, SLOT(focusDebtEdit()));
+
+    new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_P), this, SLOT(on_button_Print_clicked()));
 }
 
 
@@ -506,6 +511,228 @@
 }
 
 
+void SyntilistaMainWindow::on_button_Print_clicked()
+{
+    // Create a printer object and force some basic settings
+    QPrinter printer(QPrinter::HighResolution);
+    printer.setPageSize(QPageSize(QPageSize::A4));
+    printer.setColorMode(QPrinter::GrayScale);
+    printer.setResolution(300);
+
+    // We need to get the page count here, and also need it again in
+    // printDocument(), but there is no sane way to pass that there,
+    // so some code duplication is unfortunately necessary
+    SLPageInfo pinfo;
+    pinfo.npages = 0;
+    pinfo.nlinesPerPage = 0;
+
+    QPixmap tmpPixmap(1000, 1300);
+    QPainter tmpPainter;
+    tmpPainter.begin(&tmpPixmap);
+    bool ret = printDocumentPage(pinfo, true, -1, &tmpPainter, &printer);
+    tmpPainter.end();
+
+    if (!ret)
+    {
+        // Some kind of error occured
+        return;
+    }
+
+
+    // Set available pages
+    printer.setFromTo(1, pinfo.npages);
+
+    // Create print preview dialog and show it
+    QPrintPreviewDialog preview(&printer, this);
+    preview.setSizeGripEnabled(true);
+
+    connect(
+        &preview,
+        SIGNAL(paintRequested(QPrinter *)),
+        this,
+        SLOT(printDocument(QPrinter *)));
+
+    preview.exec();
+}
+
+
+void SyntilistaMainWindow::printDocument(QPrinter *printer)
+{
+    // Create progress dialog
+    QProgressDialog progress(
+        tr("Tulostetaan ..."),
+        tr("Peruuta"),
+        0,
+        1,
+        this);
+
+    // Again, get the page info here .. we need the number of lines per page
+    SLPageInfo pinfo;
+    pinfo.npages = 0;
+    pinfo.nlinesPerPage = 0;
+
+    QPixmap tmpPixmap(1000, 1300);
+    QPainter tmpPainter;
+    tmpPainter.begin(&tmpPixmap);
+    bool ret = printDocumentPage(pinfo, true, -1, &tmpPainter, printer);
+    tmpPainter.end();
+
+    if (!ret)
+        return;
+
+    // Setup rest of the progress dialog here
+    progress.setWindowModality(Qt::ApplicationModal);
+    progress.setMinimum(printer->fromPage() - 1);
+    progress.setMaximum(printer->toPage());
+
+
+    // Begin painting to the printer (or preview)
+    QPainter painter;
+    painter.begin(printer);
+
+    bool firstPage = true;
+    for (int page = printer->fromPage(); page <= printer->toPage(); page++)
+    {
+        if (!firstPage)
+            printer->newPage();
+
+        qApp->processEvents();
+        if (progress.wasCanceled())
+            break;
+
+        printDocumentPage(pinfo, false, page, &painter, printer);
+        progress.setValue(page);
+        firstPage = false;
+    }
+
+    painter.end();
+}
+
+
+bool SyntilistaMainWindow::printDocumentPage(SLPageInfo &pinfo, const bool getPageInfo, const int npage, QPainter *pt, QPrinter *printer)
+{
+    // Form the SQL query for list of users
+    QString querystr = QStringLiteral(
+        "SELECT id,first_name,last_name,extra_info,added,updated, "
+        "(SELECT TOTAL(value) FROM transactions WHERE transactions.person=people.id) AS balance "
+        "FROM people ORDER BY last_name ASC,first_name ASC");
+
+    // If we are fetching page info, we need to process all entries
+    if (!getPageInfo)
+    {
+        // Otherwise we can limit to given page number
+        querystr += QStringLiteral(" LIMIT %1 OFFSET %2").
+            arg(pinfo.nlinesPerPage).
+            arg((npage - 1) * pinfo.nlinesPerPage);
+    }
+
+    QSqlQuery query;
+    query.prepare(querystr);
+    query.setForwardOnly(true);
+    query.exec();
+
+    if (!slCheckAndReportSQLError("printDocumentPage()", query.lastError()))
+    {
+        slErrorMsg(
+            tr("SQL-tietokantavirhe"),
+            tr("Tietokantaa selattaessa tapahtui virhe."));
+
+        return false;
+    }
+
+    pt->save();
+    if (!getPageInfo)
+    {
+        pt->scale(
+            printer->pageRect().width() / 1000.0f,
+            printer->pageRect().height() / 1300.0f);
+    }
+
+    QFont font1("Arial", 5);
+    SLDrawContext ctx(pt);
+    ctx.setFont(font1);
+
+    int nline = 0;
+    while (query.next())
+    {
+        PersonInfo info;
+        slGetPersonInfoRec(query, info);
+
+        // Check for end of page
+        // KLUDGE for now
+        if (getPageInfo &&
+            ctx.m_pos.y() + (ctx.boundRect().height() * 4) >= 1300.0f / 2.0f)
+        {
+            if (nline > pinfo.nlinesPerPage)
+                pinfo.nlinesPerPage = nline;
+
+            pinfo.npages++;
+            nline = 0;
+        }
+
+        if (nline == 0)
+        {
+            // If we are at the start of the page, we shall draw a header
+            pt->setBrush(QBrush(Qt::black));
+            pt->setPen(QPen(Qt::black, 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
+
+            ctx.setPos(0, 0);
+            ctx.drawText(   5,  180, "Etunimi");
+            ctx.drawText( 200,  230, "Sukunimi");
+            ctx.drawText( 450,  190, "Lisätty");
+            ctx.drawText( 650,  190, "Päivitetty");
+            ctx.drawText( 870,  120, "Tase");
+            ctx.lf();
+
+            pt->drawLine(0, ctx.m_pos.y(), 1000, ctx.m_pos.y());
+
+            ctx.move(0, 5);
+        }
+
+        // Draw a gray bar under every second line
+        if (nline % 2 == 0)
+        {
+            pt->fillRect(
+                0,
+                ctx.m_pos.y() - 1,
+                1000,
+                ctx.boundRect().height() + 4,
+                QColor(0, 0, 0, 40));
+        }
+
+        ctx.drawText(   5,  180, info.firstName);
+        ctx.drawText( 200,  230, info.lastName);
+        ctx.drawText( 450,  190, slDateTimeToStr(info.added));
+        ctx.drawText( 650,  190, slDateTimeToStr(info.updated));
+        ctx.drawText( 870,  120, slMoneyValueToStr(info.balance), Qt::AlignRight);
+
+        ctx.lf(10);
+        nline++;
+    }
+
+    query.finish();
+
+    if (getPageInfo)
+    {
+        if (nline > pinfo.nlinesPerPage)
+            pinfo.nlinesPerPage = nline;
+
+        pinfo.npages++;
+    }
+    else
+    {
+        ctx.setPos(0, 1240);
+        ctx.drawText(0, 1000,
+            QStringLiteral("Sivu %1 / %2").
+            arg(npage).arg(printer->toPage()),
+            Qt::AlignHCenter);
+    }
+
+    pt->restore();
+    return true;
+}
+
+
 void SyntilistaMainWindow::on_button_DeletePerson_clicked()
 {
     if (currPerson.id <= 0)
--- a/src/main.h	Wed Jul 05 09:52:10 2017 +0300
+++ b/src/main.h	Wed Jul 05 12:48:55 2017 +0300
@@ -14,6 +14,8 @@
 #include <QDialog>
 #include <QtSql>
 #include <QSqlQueryModel>
+#include <QPainter>
+#include <QPrinter>
 
 
 //
@@ -106,6 +108,13 @@
     class AboutWindow;
 }
 
+typedef struct
+{
+    int nlinesPerPage;
+    int npages;
+} SLPageInfo;
+
+
 class SyntilistaMainWindow : public QMainWindow
 {
     Q_OBJECT
@@ -123,6 +132,8 @@
     int  addTransactionGUI(qint64 id, bool debt, double value);
     void updatePersonList();
 
+    bool printDocumentPage(SLPageInfo &pinfo, const bool getPageInfo, const int page, QPainter *pt, QPrinter *printer);
+
     PersonSQLModel *model_People;
 
 public slots:
@@ -138,6 +149,7 @@
 
     void on_button_Quit_clicked();
     void on_button_About_clicked();
+    void on_button_Print_clicked();
 
     void on_button_AddDebt_clicked();
     void on_button_PayDebt_clicked();
@@ -156,6 +168,8 @@
 
     void updateSortOrder(int index, Qt::SortOrder order);
 
+    void printDocument(QPrinter *printer);
+
 
 private:
     Ui::SyntilistaMainWindow *ui;
@@ -222,4 +236,114 @@
 };
 
 
+//
+// Custom painter drawing helper class
+//
+class SLDrawContext : public QObject
+{
+    Q_OBJECT
+
+public:
+    QPointF m_pos;
+    QString m_str;
+    qreal m_lf_add;
+
+    explicit SLDrawContext(QPainter *pt)
+    {
+        painter = pt;
+        metrics = NULL;
+        m_str = "ABC";
+        setPos(0, 0);
+        m_lf_add = 0;
+    }
+
+    ~SLDrawContext()
+    {
+        if (metrics)
+            delete metrics;
+    }
+
+    void setFont(const QFont &ft)
+    {
+        if (metrics)
+            delete metrics;
+
+        font = QFont(ft, painter->device());
+        painter->setFont(font);
+        metrics = new QFontMetricsF(font);
+    }
+
+    void setLFAdd(const qreal lf_add)
+    {
+        m_lf_add = lf_add;
+    }
+
+    void drawText(const QPointF &pos, const QString &str)
+    {
+        m_str = str;
+        painter->drawText(m_pos + pos, str);
+    }
+
+    void drawText(const QString &str)
+    {
+        drawText(QPointF(0, 0), str);
+    }
+
+    void drawText(const qreal xc, const qreal width, const QString &str, const int flags = 0)
+    {
+        m_str = str;
+        painter->drawText(
+            m_pos.x() + xc, m_pos.y(),
+            width, boundRect().height(),
+            flags,
+            str);
+    }
+
+    const QRectF boundRect(const QString str)
+    {
+        return metrics->boundingRect(str);
+    }
+
+    const QRectF boundRect()
+    {
+        return metrics->boundingRect(m_str);
+    }
+
+    void lf(qreal yadd)
+    {
+        m_pos.setY(m_pos.y() + boundRect().height() + yadd);
+    }
+
+    void lf()
+    {
+        lf(m_lf_add);
+    }
+
+    void setPos(const QPointF &pos)
+    {
+        m_pos = pos;
+    }
+
+    void setPos(const qreal x, const qreal y)
+    {
+        setPos(QPointF(x, y));
+    }
+
+    void move(const QPointF &pos)
+    {
+        m_pos += pos;
+    }
+
+    void move(const qreal x, const qreal y)
+    {
+        move(QPointF(x, y));
+    }
+
+private:
+    QPainter *painter;
+    QFont font;
+    QFontMetricsF *metrics;
+};
+
+
 #endif // SYNTILISTA_H
--- a/src/mainwindow.ui	Wed Jul 05 09:52:10 2017 +0300
+++ b/src/mainwindow.ui	Wed Jul 05 12:48:55 2017 +0300
@@ -262,6 +262,13 @@
          </widget>
         </item>
         <item>
+         <widget class="QPushButton" name="button_Print">
+          <property name="text">
+           <string>Tulosta</string>
+          </property>
+         </widget>
+        </item>
+        <item>
          <spacer name="horizontalSpacer_2">
           <property name="orientation">
            <enum>Qt::Horizontal</enum>