Кастомизация класса wxHtmlEasyPrinting для печати отчетов в wxWidgets

Александр (sandy) Илюшенко любезно предоставил статью о кастомизации класса для печати отчетов wxHtmlEasyPrinting:

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

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

Example1.cpp

#include <wx/html/htmprint.h>
 /*... */
 wxHtmlEasyPrinting* prn = new wxHtmlEasyPrinting(wxT("Довідник користувачів"));
 prn->GetPrintData()->SetOrientation(wxLANDSCAPE);
 prn->GetPageSetupData()->SetMarginTopLeft(wxPoint(10,10));
 prn->GetPageSetupData()->SetMarginBottomRight(wxPoint(5,5));
 prn->SetFooter(sFooter);
 prn->PreviewText(sText);
 /*... */

Для пояснения: sFooter – это строка, содержащая html-код нижнего колонтитула; sText – строка, содержащая html-код содержания документа, выводимого на печать.

Выглядит все это довольно неплохо:

wxHtmlEasyPrinting

Но мне, как всегда нашлось что-то, что не понравилось. Первое, что мне захотелось улучшить, – это кнопки навигации. Второе, – мне (вернее пользователю) нужно было все написать “на родном языке”. Чтобы долго не рассказывать, посмотрите на рисунок ниже, и ощутите разницу.

wxHtmlPrint

Теперь, как это мне удалось. Для начала, посмотрите на заголовочный файл.

wxHtmlPrint.h

#pragma once
#include <wx/html/htmprint.h>
#include <wx/choice.h>
#include <wx/numdlg.h>
#include <wx/bmpbuttn.h>
#include <wx/sizer.h>

class wxPreviewCtrlBar : public wxPreviewControlBar {
    wxBitmapButton* m_bPrint;
    wxBitmapButton* m_bRewind;
    wxBitmapButton* m_bPreview;
    wxBitmapButton* m_bNext;
    wxBitmapButton* m_bLast;
public:
    wxPreviewCtrlBar(wxPrintPreviewBase* preview,
                     long buttons,
                     wxWindow* parent,
                     const wxPoint& pos = wxDefaultPosition,
                     const wxSize& size = wxDefaultSize,
                     long style = wxTAB_TRAVERSAL,
                     const wxString& name = wxT("panel"));
    void CreateButtons();
    void OnGoto();
};

class wxPreviewCtrl : public wxPreviewFrame {
    wxPreviewCtrlBar* m_ctrlBar;
public:
    wxPreviewCtrl(wxPrintPreviewBase* preview,
                  wxWindow* parent,
                  const wxString& title = wxT("Print Preview"),
                  const wxPoint& pos = wxDefaultPosition,
                  const wxSize& size = wxDefaultSize,
                  long style = wxDEFAULT_FRAME_STYLE,
                  const wxString& name = wxT("frame"));
    inline wxPreviewControlBar* GetControlBar() const {return (wxPreviewControlBar*)m_ctrlBar;}
    void Initialize();
    void CreateControlBar();
};

class wxHtmlPrint : public wxHtmlEasyPrinting {
    wxString m_sTitle;
    wxWindow* m_wParent;
protected:
    bool DoPreview(wxHtmlPrintout* printout1, wxHtmlPrintout* printout2);
public:
    wxHtmlPrint::wxHtmlPrint(const wxString& name, wxWindow* parentWindow = NULL);
};

Как видите, переопределить пришлось не один класс. а целых три. Теперь, по-порядку. Класс wxPreviewCtrlBar, наследуемый от wxPreviewControlBar нужен больше всего, так как именно в этом классе переопределены кнопки и их названия. Подробнее, вместо wxButton для навигации используются wxBitmapButton, а для кнопки “Close” написано “Закрити”. (Интерфейс программы был написан на украинском языке. Кому необходимо, – перепишите по-русски сами) Для реализации этой задачи и нужно было переписать функцию CreateButtons(). Функция OnGoto() переписана для того, чтобы в функцию wxGetNumberFromUser передать строки на украинском языке, а не на английском.

Класс wxPreviewCtrl, наследуемый от wxPreviewFrame, имеет член экземпляра wxPreviewCtrlBar* m_ctrlBar. Его предок использовал предка этого члена. Потому пришлось переписать функции, в которых использовался wxPreviewControlBar, почти полностью, изменив лишь использование wxPreviewControlBar* на wxPreviewCtrlBar*.

В классе wxHtmlPrint, наследнике wxHtmlEasyPrinting, надо было переопределить только функцию DoPreview. Но, так как члены предка wxWindow* m_ParentWindow и wxString m_Name – закрытые, то пришлось ввести их альтернативы: wxWindow* m_wParent и wxString m_sTitle.

wxHtmlPrint.cpp

#include "wxHtmlPrint.h"

/// XPM
static char * btn_rew_xpm[] = {
"16 16 2 1",
" 	c None",
".	c #000000",
"                ",
" ..       .     ",
" ..      ..     ",
" ..     ...     ",
" ..    ....     ",
" ..   .....     ",
" ..  ......     ",
" .. .......     ",
" .. .......     ",
" ..  ......     ",
" ..   .....     ",
" ..    ....     ",
" ..     ...     ",
" ..      ..     ",
" ..       .     ",
"                "};

static char * btn_prev_xpm[] = {
"16 16 2 1",
" 	c None",
".	c #000000",
"                ",
"       .      . ",
"      ..     .. ",
"     ...    ... ",
"    ....   .... ",
"   .....  ..... ",
"  ...... ...... ",
" .............. ",
" .............. ",
"  ...... ...... ",
"   .....  ..... ",
"    ....   .... ",
"     ...    ... ",
"      ..     .. ",
"       .      . ",
"                "};

static char * btn_next_xpm[] = {
"16 16 2 1",
" 	c None",
".	c #000000",
"                ",
" .      .       ",
" ..     ..      ",
" ...    ...     ",
" ....   ....    ",
" .....  .....   ",
" ...... ......  ",
" .............. ",
" .............. ",
" ...... ......  ",
" .....  .....   ",
" ....   ....    ",
" ...    ...     ",
" ..     ..      ",
" .      .       ",
"                "};

static char * btn_last_xpm[] = {
"16 16 2 1",
" 	c None",
".	c #000000",
"                ",
"     .       .. ",
"     ..      .. ",
"     ...     .. ",
"     ....    .. ",
"     .....   .. ",
"     ......  .. ",
"     ....... .. ",
"     ....... .. ",
"     ......  .. ",
"     .....   .. ",
"     ....    .. ",
"     ...     .. ",
"     ..      .. ",
"     .       .. ",
"                "};

static char *print_xpm[] = {
/* columns rows colors chars-per-pixel */
"16 15 39 1",
"< c #E3E4E6",
"+ c #C3C3C4",
"i c #FFFFFF",
": c #74879B",
"# c #5A89A6",
"a c #F1F4F7",
"r c #5A809C",
"@ c #BDCCD9",
"e c #7A92A4",
"% c #3F6F93",
"t c #9FA2A6",
"3 c #939495",
"w c #5F666D",
"9 c #65839E",
"5 c #4A7291",
"$ c #4B7F9E",
"  c None",
"O c #DFE0E2",
"o c #F3F3F3",
"; c #84A5BB",
"& c #467291",
". c #7897AD",
"* c #407598",
"4 c #CFCFD0",
"7 c #6F90A6",
"y c #6A89A2",
"0 c #AAADB2",
"1 c #D2D3D4",
"u c #4F7592",
", c #BCBDBE",
"p c #57778E",
"q c #979BA0",
"2 c #ABABAC",
"- c #E7E7E7",
"= c #D6DEE6",
"> c #9FA0A0",
"8 c #829EB5",
"X c #8FB0C3",
"6 c #5D7C93",
/* pixels */
"   .XXXXXXXX    ",
"   .oooooooX    ",
"   .OOOOOOOX    ",
"   .+++++++X    ",
"@##$%&&&&&%*##@ ",
"$=-;:>,<123$-=$ ",
".44.5678.96$44. ",
"7,,,,,,,,,,,,,7 ",
"900qwwwwwwwe009 ",
"rtt9ryyyyyyuttr ",
"6qq6iiiiiii%qq6 ",
"633paiiiiii%336 ",
"XXX*iiiiiii%XXX ",
"   6iiiiiii%    ",
"   $XXXXXXX#    "
};

/// wxPreviewCtrlBar
wxPreviewCtrlBar::wxPreviewCtrlBar(wxPrintPreviewBase* preview,
                                   long buttons,
                                   wxWindow* parent,
                                   const wxPoint& pos,
                                   const wxSize& size,
                                   long style,
                                   const wxString& name) :
    wxPreviewControlBar(preview, buttons, parent, pos, size, style, name)
{
    m_bPrint = (wxBitmapButton*)NULL;
    m_bRewind = (wxBitmapButton*)NULL;
    m_bPreview = (wxBitmapButton*)NULL;
    m_bNext = (wxBitmapButton*)NULL;
    m_bLast = (wxBitmapButton*)NULL;
}

void wxPreviewCtrlBar::CreateButtons()
{
    SetSize(0, 0, 400, 40);

    wxBoxSizer *item0 = new wxBoxSizer( wxHORIZONTAL );

    m_closeButton = new wxButton(this, wxID_PREVIEW_CLOSE, wxT("Закрити"),
                                 wxDefaultPosition, wxDefaultSize, 0);
    item0->Add(m_closeButton, 0, wxALIGN_CENTRE | wxALL, 5);

    if (m_buttonFlags & wxPREVIEW_PRINT) {
        m_bPrint = new wxBitmapButton(this, wxID_PREVIEW_PRINT, wxBitmap(print_xpm),
                                      wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW);
        item0->Add(m_bPrint, 0, wxALIGN_CENTRE|wxALL, 5);
    }

    // Exact-fit buttons are too tiny on wxUniversal
    int navButtonStyle;
    wxSize navButtonSize;
#ifdef __WXUNIVERSAL__
    navButtonStyle = 0;
    navButtonSize = wxSize(40, m_closeButton->GetSize().y);
#else
    navButtonStyle = wxBU_EXACTFIT;
    navButtonSize = wxDefaultSize;
#endif

    if (m_buttonFlags & wxPREVIEW_FIRST) {
        m_bRewind = new wxBitmapButton(this, wxID_PREVIEW_FIRST, wxBitmap(btn_rew_xpm),
                                       wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW);
        item0->Add(m_bRewind, 0, wxALIGN_CENTRE | wxALL, 5);
    }
    if (m_buttonFlags & wxPREVIEW_PREVIOUS) {
        m_bPreview = new wxBitmapButton(this, wxID_PREVIEW_PREVIOUS, wxBitmap(btn_prev_xpm),
                                        wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW);
        item0->Add(m_bPreview, 0, wxALIGN_CENTRE |wxRIGHT | wxTOP | wxBOTTOM, 5);
    }
    if (m_buttonFlags & wxPREVIEW_NEXT) {
        m_bNext = new wxBitmapButton(this, wxID_PREVIEW_NEXT, wxBitmap(btn_next_xpm),
                                     wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW);
        item0->Add(m_bNext, 0, wxALIGN_CENTRE | wxRIGHT | wxTOP | wxBOTTOM, 5);
    }
    if (m_buttonFlags & wxPREVIEW_LAST) {
        m_bLast = new wxBitmapButton(this, wxID_PREVIEW_LAST, wxBitmap(btn_last_xpm),
                                     wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW);
        item0->Add(m_bLast, 0, wxALIGN_CENTRE | wxRIGHT | wxTOP | wxBOTTOM, 5);
    }

    if (m_buttonFlags & wxPREVIEW_GOTO) {
        m_gotoPageButton = new wxButton(this, wxID_PREVIEW_GOTO, wxT("Сторінка..."),
                                        wxDefaultPosition, wxDefaultSize, 0);
        item0->Add(m_gotoPageButton, 0, wxALIGN_CENTRE | wxALL, 5);
    }

    if (m_buttonFlags & wxPREVIEW_ZOOM) {
        wxString choices[] = {
            wxT("10%"),
            wxT("15%"),
            wxT("20%"),
            wxT("25%"),
            wxT("30%"),
            wxT("35%"),
            wxT("40%"),
            wxT("45%"),
            wxT("50%"),
            wxT("55%"),
            wxT("60%"),
            wxT("65%"),
            wxT("70%"),
            wxT("75%"),
            wxT("80%"),
            wxT("85%"),
            wxT("90%"),
            wxT("95%"),
            wxT("100%"),
            wxT("110%"),
            wxT("120%"),
            wxT("150%"),
            wxT("200%")};
        int n = WXSIZEOF(choices);
        m_zoomControl = new wxChoice(this, wxID_PREVIEW_ZOOM, wxDefaultPosition, 
			wxSize(70,wxDefaultCoord), n, choices, 0);
        item0->Add(m_zoomControl, 0, wxALIGN_CENTRE | wxALL, 5);
        SetZoomControl(m_printPreview->GetZoom());
    }
    SetSizer(item0);
    item0->Fit(this);
}


void wxPreviewCtrlBar::OnGoto()
{
    wxPrintPreviewBase *preview = GetPrintPreview();
    long nPage=0;
    if (preview) {
        if (preview->GetMinPage() > 0) {
            nPage=wxGetNumberFromUser(
                wxString::Format("Введіть номер сторінки між %d та %d:",
					preview->GetMinPage(),preview->GetMaxPage()),
                wxString::Format("%d",preview->GetCurrentPage()),
                wxT("Перейти на сторінку"),
                preview->GetCurrentPage());
            if (preview->GetPrintout()->HasPage(nPage)) {
                preview->SetCurrentPage(nPage);
            }
        }
    }
}

/// wxPreviewCtrl
wxPreviewCtrl::wxPreviewCtrl(wxPrintPreviewBase* preview,
                             wxWindow* parent,
                             const wxString& title,
                             const wxPoint& pos,
                             const wxSize& size,
                             long style,
                             const wxString& name) :
    wxPreviewFrame(preview, parent, title, pos, size, style, name)
{
    m_ctrlBar = NULL;
}

void wxPreviewCtrl::Initialize()
{
#if wxUSE_STATUSBAR
    CreateStatusBar();
#endif
    CreateCanvas();
    CreateControlBar();

    m_printPreview->SetCanvas(m_previewCanvas);
    m_printPreview->SetFrame(this);

    wxBoxSizer *item0 = new wxBoxSizer( wxVERTICAL );

    item0->Add(m_ctrlBar, 0, wxGROW | wxALIGN_CENTER_VERTICAL, 5);
    item0->Add(m_previewCanvas, 1, wxGROW | wxALIGN_CENTER_VERTICAL, 5);

    SetAutoLayout( true );
    SetSizer( item0 );

    m_windowDisabler = new wxWindowDisabler(this);

    Layout();

    m_printPreview->AdjustScrollbars(m_previewCanvas);
    m_previewCanvas->SetFocus();
    m_ctrlBar->SetFocus();
}

void wxPreviewCtrl::CreateControlBar()
{
    long buttons = wxPREVIEW_DEFAULT;
    if (m_printPreview->GetPrintoutForPrinting())
        buttons |= wxPREVIEW_PRINT;

    m_ctrlBar = new wxPreviewCtrlBar(m_printPreview, buttons, this, wxPoint(0,0), wxSize(400,40));
    m_ctrlBar->CreateButtons();
}

/// wxHtmlPrint
wxHtmlPrint::wxHtmlPrint(const wxString& name, wxWindow* parentWindow) :
    wxHtmlEasyPrinting(name, parentWindow),
    m_sTitle(name),
    m_wParent(parentWindow)
{
}

bool wxHtmlPrint::DoPreview(wxHtmlPrintout* printout1, wxHtmlPrintout* printout2)
{
    wxPrintDialogData printDialogData(*GetPrintData());
    wxPrintPreview *preview = new wxPrintPreview(printout1, printout2, &printDialogData);
    if (!preview->Ok())
    {
        delete preview;
        return false;
    }

    wxPreviewCtrl* frame = new wxPreviewCtrl(preview, m_wParent,
                                             m_sTitle + wxT(" (Попередній перегляд)"),
                                             wxPoint(100, 100), wxSize(650, 500));

    frame->Centre(wxBOTH);
    frame->Initialize();
    frame->Show(true);
    return true;
}

Теперь, для красивого предварительного просмотра, Вы можете использовать wxHtmlPrint. Использование аналогично его предку – wxHtmlEasyPrinting.

Example2.cpp

#include <wxHtmlPrint.h>
/* ... */
 wxHtmlPrint* prn = new wxHtmlPrint(wxT("Довідник користувачів"));
 prn->GetPrintData()->SetOrientation(wxLANDSCAPE);
 prn->GetPageSetupData()->SetMarginTopLeft(wxPoint(10,10));
 prn->GetPageSetupData()->SetMarginBottomRight(wxPoint(5,5));
 prn->SetFooter(sFooter);
/// Предварительный просмотр
 prn->PreviewText(sText);
 /// Печать без предварительного просмотра
 prn->PrintText(sText);
 /* ... */

2 comments

  1. T-Rex   •     Author

    ИМХО, конечно, но локализацию можно было бы сделать стандартными средствами wxWidgets или с помощью wxTranslationHelper

  2. Sandy   •  

    Так тебе и карты в руки. Мне, если честно, некогда было, надо было побыстрее, а приложение было не многоязычным

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Please leave these two fields as-is: