Russian Qt Forum

Qt => Печать => Тема начата: kirill от Ноябрь 12, 2008, 12:27



Название: Фоновая печать из QWebView
Отправлено: kirill от Ноябрь 12, 2008, 12:27
Пытаюсь распечатать из QWebView достаточно большой html-документ (на 100 страниц) функцией print(QPrinter).

В итоге программа "подмерзает" пока функция не отработает, а работает она довольно долго. Попытался убрать ее в поток.

Код:
class PrintThread : public QThread
{
   Q_OBJECT
public:
   PrintThread(QWebView*, QPrinter*);
protected:
   void run();
   QWebView *wv;
   QPrinter *printer;
};

void PrintThread::run()
{
     wv->print(printer);
}


Пытаюсь печатать так:
Код:
QWebView *webview = new QWebView();
QPrinter *printer = new QPrinter();
//...
PrintThread ptPrint(webview, printer);
ptPrint->start();

Вроде бы печать уходит в фон и в общем-то иногда печатает нормально. Но если попытаться в это время подергать главное окно, то программа валится.
В Output валятся сообщения
Цитировать
QObject::startTimer: timers cannot be started from another thread
QObject::killTimer: timers cannot be stopped from another thread
This may be due to a corruption of the heap, and indicates a bug in vrfstat.exe or any of the DLLs it has loaded.


Вопрос собстна как правильно организовать фоновую печть?


Название: Re: Фоновая печать из QWebView
Отправлено: pastor от Ноябрь 12, 2008, 12:42
Собственно странного ничего не ту в том что программа валиться. Вы неправильно работаете с потоком (обращаетесь к гую из другого потока). Все работа с гуем должна быть ТОЛЬКО в гуевом потоке (main\GUI thread).Почитайте вот это по потокам :Thread Support in Qt (http://doc.trolltech.com/4.4/threads.html)


Название: Re: Фоновая печать из QWebView
Отправлено: kirill от Ноябрь 12, 2008, 12:56
Если я буду вызывать webview->print() из главного потока, то получу "заморозку". Может ткнете пальцем как правильно вызвать долгоработающие функции в фоне?


Название: Re: Фоновая печать из QWebView
Отправлено: pastor от Ноябрь 12, 2008, 12:58
Думаю вот эта статья вам поможет: http://labs.trolltech.com/blogs/2007/09/27/multi-threaded-text-layout-and-printing/

В статье приведена печать в потоке через QTextDocument, а в вашем случае печать будет через QWebFrame (загляните в исходники QWebView::print).

Удачи :)


Название: Re: Фоновая печать из QWebView
Отправлено: kirill от Ноябрь 12, 2008, 13:13
Спасибо за линк!


Название: Re: Фоновая печать из QWebView
Отправлено: kirill от Ноябрь 13, 2008, 08:50
Таки не вышло у меня сделать нормальныю печать.
Подскажите что не так.

1) Класс потока, который будет печатать:
Код:
class PrintThread : public QThread
{
   Q_OBJECT
public:
   PrintThread(QObject * parent = 0);
   void start(const QString &);
   QPrinter printer_;
   QWebPage *wpage_;
   QPrinter *printer(){return &printer_;};

protected:
   void run();
};

Это почти как в примере выше.

2) Вызываю из GUI main
Код:
   PrintThread *ptPrint = new PrintThread(this);

   QPrintDialog dlg(ptPrint->printer(), this);

   if (dlg.exec() == QDialog::Accepted)
   {
      connect(ptPrint, SIGNAL(finished()), ptPrint, SLOT(deleteLater()));
      ptPrint->start(html);
    }
   else
   {
      delete ptPrint;
   }

Тут тоже вроде все понятно.

3) Внутренности PrintThread, которые вызывают сомнения
Код:
void PrintThread::start(const QString & html)
{
   html_ = html;
   wpage_ = new QWebPage(this);
   wpage_->moveToThread(this);
   wpage_->mainFrame()->setHtml(html_);
   
   QThread::start();
}

В класс потока передается строка html, которая интерпретируется QWebPage.

4) Печать в потоке
Код:
void PrintThread::run()
{
   wpage_->mainFrame()->print(&printer_);
}

В итоге это не работает. То есть иногда работает, когда немного надо печатать, но для большого документа выдает чистый лист и все. И иногда валится с ошибкой.
Что за коряга такая непонятно.

Подскажите что не так то?

ЗЫ
Исследуя QWebPage обнаружил, что его setHtml непонятно работает. Такое ощущение, что она завершает работу не успев загрузить весь html. Так вот у QwebPage есть сигнал loadFinished(bool), который сигнализирует об окончании загрузки документа. Но прикрутить его к слоту потока не вышло. Я хотел в start потока поставить коннект connect(wpage_, SIGNAL(loadFinished(bool)), this, SLOT(loaded(bool))) прописав с класс потока слот loaded и на его срабатывание запускать QThread::start(). Однако слот не срабатывает.


Название: Re: Фоновая печать из QWebView
Отправлено: BRE от Ноябрь 13, 2008, 10:08
А попробуй  в классе объявить QWebFrame *wframe_;
и сделать так:
Код:
void PrintThread::start(const QString & html)
{
   html_ = html;
   wpage_ = new QWebPage(this);
   wframe_ = wpage_->mainFrame();
   wframe_->setHtml(html_);
   wframe_->moveToThread( this );

   QThread::start();
}

void PrintThread::run()
{
   wframe_->print(&printer_);
}



Название: Re: Фоновая печать из QWebView
Отправлено: kirill от Ноябрь 13, 2008, 10:49
Попробовал, та же фигня.
Маленькие документы печатает, а на большие выдает 1 чистый лист.


Название: Re: Фоновая печать из QWebView
Отправлено: pastor от Ноябрь 13, 2008, 12:50
А если попробывать так:

Код:
class PrintThread : public QThread
{
    QPrinter _printer;
    QWebFrame *_frame;

public:
    PrintThread(QObject *parent)
        : QThread(parent), _printer(QPrinter::HighResolution), _frame(0)
    {
    }
    ~PrintThread()
    {
        wait();
    }

    QPrinter *printer()
    {
        return &_printer;
    }

    void start(const QString &html)
    {
        _frame = new QWebFrame();     //парент не указываем!
        _frame->moveToThread(this);
        _frame->setHtml(html);
        QThread::start();
    }

protected:
    void run()
    {
        _frame->print(printer());
        delete _frame;
        _frame = 0;
    }
};


void <some_class>::print()
{
    if (!QFontDatabase::supportsThreadedFontRendering()) {
        QPrinter printer(QPrinter::HighResolution);

        QPrintDialog dlg(&printer, this);
        if (dlg.exec() == QDialog::Accepted) {
            qApp->setOverrideCursor(Qt::WaitCursor);
            _webView->print(&printer);
            qApp->restoreOverrideCursor();
        }
        return;
    }

    PrintThread *thread = new PrintThread(this);

    QPrintDialog dlg(thread->printer(), this);
    if (dlg.exec() == QDialog::Accepted) {
        connect(thread, SIGNAL(finished()), SLOT(printingFinished()));
        connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));

        qApp->setOverrideCursor(Qt::BusyCursor);
        thread->start(_webView->page()->mainFrame()->toHtml());
    } else {
        delete thread;
    }

}

void <some_class>::printingFinished()
{
    qApp->restoreOverrideCursor();
}

где _webView - это чден класса <some_class>.


Название: Re: Фоновая печать из QWebView
Отправлено: kirill от Ноябрь 13, 2008, 15:30
Попробую конечно, спасибо.

Но вот сразу вопрос - QWebFrame имеет закрытый конструктор.
Явно к нему обратиться нельзя. Оперировать похоже надо через QWebPage, он наследует от QObject и вроде не является GUI.
Выше BRE приводил пример обращения к QWebFrame.

А в остальном код сильно похож на qt/tools/assistant/compat/mainwindow.cpp
Я его ковырял, но подстроить под себя не удалось.

ЗЫ
Жаль троли не написали функции backgroundPrint()


Название: Re: Фоновая печать из QWebView
Отправлено: pastor от Ноябрь 13, 2008, 15:46
Попробую конечно, спасибо.

Но вот сразу вопрос - QWebFrame имеет закрытый конструктор.
Явно к нему обратиться нельзя. Оперировать похоже надо через QWebPage, он наследует от QObject и вроде не является GUI.
Выше BRE приводил пример обращения к QWebFrame.

Да, быть может. Код я не пытался компилировать, писал прямо на форуме

А в остальном код сильно похож на qt/tools/assistant/compat/mainwindow.cpp
Я его ковырял, но подстроить под себя не удалось.

собственно говоря от туда и есть :)

ЗЫ
Жаль троли не написали функции backgroundPrint()

По этому поводу вы можите написать suggestion тролям: http://trolltech.com/bugreport-form
будет весьма полезная фича :)


В пердыдущих листингах вы создавали QWebPage, указав ему парент

Цитировать
wpage_ = new QWebPage(this);

потом вы перемещаете его в другой поток. А так делать нельзя. Я незря поставил коментарий в своем листинге. Возможно проблема связанна именно с этим.

Цитировать
void QObject::moveToThread ( QThread * targetThread )
Changes the thread affinity for this object and its children. The object cannot be moved if it has a parent. Event processing will continue in the targetThread.