Запутался, не знаю как решить следующую проблему. Необходимо под графиком, который уже нарисован нарисовать рельеф. Отрисовка рельефа должна происходить всего один раз. Т.е. при рисовании в методе paintEvent() естественно эта штуковина нещадно грузит систему. Делаю вызов этой функции в другом месте, но тогда вообще ничего не рисуется (возможно, потому что использую QPainter для получения цвета "кисти" и "карандаша"). Помогите, пожалуйста.
Код самого рисования правилен, вопрос в том, как заставить рисовать не вызывая данную функцию в paintEvent()?
/// Отрисовка рельефа
void ReliefGraphics::drawTrackRelief()
{
for(QVector<QPointF>::iterator rlfPnt=relief_vec.begin(), i_prev=relief_vec.begin(); rlfPnt!=relief_vec.end();
++rlfPnt)
{
QPointF cur(*rlfPnt), prev(*i_prev);
drawReliefNature(&painter, cur, prev);
if(rlfPnt != relief_vec.begin()) ++i_prev;
}
}
/// Настройка цвета рельефа (естественный цвет)
void ReliefGraphics::drawReliefNature(QPainter* painter, QPointF cur, QPointF prev)
{
double distance = cur.x(), delta_height = cur.y();
QPointF scrPnt(initXY(distance, delta_height));
if(delta_height <= 0 || scrPnt.y() >= settings -> maxY)
return;
QColor color(demGetColor(demCalcColor(delta_height)));
double delta_height0;
int div = delta_height > 1000 ? 50 : 5;
int i = (int)(delta_height / div);
if(!((int)delta_height % div)) --i;
delta_height0 = div * i;
double prev_dist = prev.x(), prev_height = prev.y();
QPointF screenPoint(initXY(distance, delta_height0)), scrPrev(initXY(prev_dist, prev_height));
QBrush old_brush(painter -> brush());
QPen old_pen(painter -> pen());
painter -> setBrush(QBrush(color));
painter -> setPen(QPen(color));
painter -> drawLine(scrPrev, screenPoint);
painter -> setPen(old_pen);
painter -> setBrush(old_brush);
QPointF cur0(distance, delta_height0);
drawReliefNature(painter, cur0, cur);
}
Кто-нибудь сможет помочь? Вопрос состоит в том, где правильно использовать функцию drawTrackRilief() или же как сделать так, чтобы не использовать QPainter и отрисовывать другими средствами. Буду благодарен за любую помощь.
Тут почитал многое и понял, что эту функцию я могу использовать только в paintEvent()! Но еще кое-что заметил и понял, что по сути ее следует вызывать, только при изменении размеров окна. Вопрос такой можно ли сделать так, чтобы один и тот же QPainter painter в одних функциях рисовал сразу на экран, а в других рисовал бы в битмап, который бы перерисовывался при изменении размеров. Кто хочет помочь, прошу помогите пожалуйста.
Вот код отрисовки:
/// Настройка цвета рельефа (естественный цвет)
void ReliefGraphics::drawReliefNature(QPainter* painter, QPointF cur, QPointF prev)
{
double distance = cur.x(), delta_height = cur.y();
QPointF scrPnt(initXY(distance, delta_height));
if(delta_height <= 0 || scrPnt.y() >= settings -> maxY)
return;
QColor color(demGetColor(demCalcColor(delta_height)));
double delta_height0;
int div = delta_height > 1000 ? 50 : 5;
int i = (int)(delta_height / div);
if(!((int)delta_height % div)) --i;
delta_height0 = div * i;
double prev_dist = prev.x(), prev_height = prev.y();
QPointF screenPoint(initXY(distance, delta_height0)), scrPrev(initXY(prev_dist, prev_height));
QBrush old_brush(painter -> brush());
QPen old_pen(painter -> pen());
painter -> setBrush(QBrush(color));
painter -> setPen(QPen(color));
painter -> drawLine(scrPrev, screenPoint);
painter -> setPen(old_pen);
painter -> setBrush(old_brush);
QPointF cur0(distance, delta_height0);
drawReliefNature(painter, cur0, cur);
}
/// Отрисовка черной линии рельефа для лучшей видимости его изгибов
void ReliefGraphics::drawBlackLineRelief(QPainter* painter, const QVector<QPointF>& vecBLine)
{
QBrush old_brush(painter -> brush());
QPen old_pen(painter -> pen());
painter -> setPen(Qt::black);
painter -> setBrush(Qt::black);
for(int i=vecBLine.size()-1, j=vecBLine.size()-1; i>=0; --i)
{
painter -> drawLine(vecBLine[i], vecBLine[j]);
if(i != vecBLine.size() - 1) --j;
}
painter -> setPen(old_pen);
painter -> setBrush(old_brush);
}
/// Отрисовка рельефа
void ReliefGraphics::drawTrackRelief()
{
QVector<QPointF> vecBLine;
for(QVector<QPointF>::iterator rlfPnt=relief_vec.begin(), i_prev=relief_vec.begin(); rlfPnt!=relief_vec.end();
++rlfPnt)
{
QPointF cur(*rlfPnt), prev(*i_prev);
double X = cur.x(), Y = cur.y(), prevX = prev.x(), prevY = prev.y();
QPointF screen(initXY(X, Y));
drawReliefNature(&painter, cur, prev);
if(rlfPnt != relief_vec.begin()) ++i_prev;
vecBLine.append(screen);
}
drawBlackLineRelief(&painter, vecBLine);
}
/// Отрисовка графика
void ReliefGraphics::paintEvent(QPaintEvent* events)
{
painter.begin(this);
QRect rect(displayRelief -> rect());
painter.setWindow(displayRelief -> rect());
painter.setFont(QFont("Tahoma", 6, Qt::SolidLine));
painter.setPen(Qt::black);
if(param_vec.size() <= count_logrecords && coords_vec.size() <= count_logrecords && fill_axis <= 3)
{
int sz = coords_vec.size(), sz1 = param_vec.size(), sz2 = relief_vec.size();
setPlotValues();
initParamVec();
if(param_vec.size() == count_logrecords && coords_vec.size() == count_logrecords)
++fill_axis;
}
initCurve();
drawTrackRelief();
drawGrid(&painter);
drawCurves(&painter);
painter.end();
}
Дошло как можно это безобразие организовать - через дополнительный параллельный поток. Ведь в потоке я имею право использовать QPainter? Если что, поможете? Естественно, я буду приводить код...
Один из первых вопрос по потокам такой - можно ли использовать в потоке указатель на QPainter, если он объявлен в главном потоке. Если нельзя, то как сделать можно? Буду благодарен за помощь!
Перенес в доп. поток. Вот так это все выглядит:
#ifndef THREAD_H_055
#define THREAD_H_055
#include <QThread>
#ifdef Q_WS_WIN
#pragma once
#endif // Q_WS_WIN
#include <QPainter>
#include <QDockWidget>
#include <QPointF>
#include <QVector>
class PlotSettings;
/// Класс параллельного потока для отрисовки рельефа вертикальной проекции
class VThread: public QThread
{
Q_OBJECT
private:
QPainter painter; ///< рисовальщик
QDockWidget* relief_widget; ///< указатель на виджет, где будет отрисовываться рельеф
int count_read; ///< количество прочитанных элементов
PlotSettings* settings; ///< указатель на настройки для различных масштабов
QVector<QPointF> relief_vec; ///< вектор геграфических точек рельефа
QRect rect; ///< прямоугольная область, в которой будет производиться рисование
private:
QPointF initXY(double& sx, double& sy);
void drawReliefNature(QPainter* painter, QPointF cur, QPointF prev);
void drawBlackLineRelief(QPainter* painter, const QVector<QPointF>& vecBLine);
void drawCurves(QPainter* painter);
void drawTrackRelief(QPainter* painter);
protected:
virtual void run();
public:
VThread(): relief_widget(0), count_read(0), settings(0) {}
VThread(QDockWidget* rlf_widget): painter(rlf_widget), relief_widget(rlf_widget), count_read(0), settings(0) {}
~VThread() {}
void widget(QDockWidget* rlf_widget) { relief_widget = rlf_widget; }
QDockWidget* widget() { return relief_widget; }
void count(const int r) { count_read = r; }
const int count() { return count_read; }
void init(PlotSettings* s, QRect& rct) { settings = s; rect = rct; }
void init(PlotSettings* s) { settings = s; }
void init(QRect& rct) { rect = rct; }
void initVec(QVector<QPointF>& rv) { relief_vec = rv; }
void stop(const int size) { if(count_read == size) quit(); }
};
#endif // THREAD_H_055
#include "Thread.h"
#include <QtGui>
/// Инициализация координат - преобразование из координат графика (sx,sy) в экранные (x,y)
QPointF VThread::initXY(double& sx, double& sy)
{
const int shift_x = 30;
if(!rect.isValid()) return QPointF();
QRect rect_shift(rect); rect_shift.setLeft(rect_shift.left() + shift_x);
double dx, dy;
dx = sx - settings -> minX;
dy = sy - settings -> minY;
double x = rect_shift.left() + (dx * (rect_shift.width() - 1) / settings -> spanX());;
double y = rect.bottom() - (dy * (rect.height() - 1) / settings -> spanY());
return QPointF(x, y);
}
/// Настройка цвета рельефа (естественный цвет)
void VThread::drawReliefNature(QPainter* painter, QPointF cur, QPointF prev)
{
double distance = cur.x(), delta_height = cur.y();
QPointF scrPnt(initXY(distance, delta_height));
if(delta_height <= 0 || scrPnt.y() >= settings -> maxY)
return;
QColor color(demGetColor(demCalcColor(delta_height)));
double delta_height0;
int div = delta_height > 1000 ? 50 : 5;
int i = (int)(delta_height / div);
if(!((int)delta_height % div)) --i;
delta_height0 = div * i;
double prev_dist = prev.x(), prev_height = prev.y();
QPointF screenPoint(initXY(distance, delta_height0)), scrPrev(initXY(prev_dist, prev_height));
QBrush old_brush(painter -> brush());
QPen old_pen(painter -> pen());
painter -> setBrush(QBrush(color));
painter -> setPen(QPen(color));
painter -> drawLine(scrPrev, screenPoint);
for(int ix=scrPrev.x()+1; ix<screenPoint.x(); ++ix)
painter -> drawLine(QPointF(ix, scrPrev.y()), screenPoint);
painter -> setPen(old_pen);
painter -> setBrush(old_brush);
QPointF cur0(distance, delta_height0);
drawReliefNature(painter, cur0, cur);
}
/// Отрисовка черной линии рельефа для лучшей видимости его изгибов
void VThread::drawBlackLineRelief(QPainter* painter, const QVector<QPointF>& vecBLine)
{
QBrush old_brush(painter -> brush());
QPen old_pen(painter -> pen());
painter -> setPen(Qt::black);
painter -> setBrush(Qt::black);
for(int i=vecBLine.size()-1, j=vecBLine.size()-1; i>=0; --i)
{
painter -> drawLine(vecBLine[i], vecBLine[j]);
if(i != vecBLine.size() - 1) --j;
}
painter -> setPen(old_pen);
painter -> setBrush(old_brush);
}
/// Отрисовка рельефа
void VThread::drawTrackRelief(QPainter* painter)
{
QVector<QPointF> vecBLine;
int size = relief_vec.size();
for(QVector<QPointF>::iterator rlfPnt=relief_vec.begin(), i_prev=relief_vec.begin(); rlfPnt!=relief_vec.end();
++rlfPnt, ++count_read)
{
usedRlfSpace.acquire();
QPointF cur(*rlfPnt), prev(*i_prev);
double X = cur.x(), Y = cur.y(), prevX = prev.x(), prevY = prev.y();
QPointF screen(initXY(X, Y));
drawReliefNature(painter, cur, prev);
if(rlfPnt != relief_vec.begin()) ++i_prev;
usedRlfSpace.release();
vecBLine.append(screen);
}
drawBlackLineRelief(painter, vecBLine);
}
/// Запуск дополницельного потока
void VThread::run()
{
drawTrackRelief(&painter);
}
ReliefGraphics::ReliefGraphics(QWidget *parent): settings(0), main_window(0), fill_axis(0), count_logrecords(0),
maxValY(-1.0e23), minValY(1.0e23), is_resizing(false), fin_init(false)
{
int r_width = displayRelief -> rect().width() - 1.5 * toolBar -> rect().width();
int r_height = displayRelief -> rect().height() - 2.5 * VerticalGraphicClass::scroll -> rect().height();
QRect rect(displayRelief -> rect().x() + toolBar -> rect().width(), displayRelief -> rect().y() +
VerticalGraphicClass::scroll -> rect().height(), r_width, r_height);
settings = new PlotSettings();
vrt_thread = new VThread(this);
vrt_thread -> init(settings, rect);
}
/// Отрисовка графика
void ReliefGraphics::paintEvent(QPaintEvent* events)
{
painter.begin(this);
/// Что-то еще отрисовывается (траектория, например)
vrt_thread -> stop(count_logrecords);
painter.end();
}
/// Отображение вертикальной проекции
void TLV::verticProjection(QAction* _sender)
{
verticPrjAction = _sender;
if(!verticalProjection) return;
verticalProjection -> setVisible(verticPrjAction -> isChecked());
if(verticPrjAction -> isChecked()) verticalProjection -> start();
}
а почему painter можно использовать в другом потоке?
Рельеф не рисуется, хоть лопни, блин. Кто может попробовать разобраться в представленном коде и помочь?
потому что по крайней мере контролы надо юзать только в потоке в котором они были созданы, а паинтер по идее взаимодействует с контролом на котором рисуется, соответственно наверно лучше не юзать в другом потоке.
Ну хоть кто-нибудь подскажите, можно ли использовать QPainter в доп. потоке или нет? Если нельзя, то как заставить отрисоваться рельеф в доп. потоке?
Вот код:
/// Отрисовка графика
void ReliefGraphics::paintEvent(QPaintEvent* events)
{
painter.begin(this);
QRect rect(displayRelief -> rect());
painter.setWindow(displayRelief -> rect());
painter.setFont(QFont("Tahoma", 6, Qt::SolidLine));
painter.setPen(Qt::black);
if(param_vec.size() <= count_logrecords && coords_vec.size() <= count_logrecords && fill_axis <= 3)
{
vrt_thread -> init(settings, winRect());
setPlotValues();
initParamVec();
if(param_vec.size() == count_logrecords && coords_vec.size() == count_logrecords)
++fill_axis;
}
initCurve();
vrt_thread -> initVec(relief_vec);
vrt_thread -> stop(count_logrecords);
if(fill_axis == 3) fin_init = true;
if(is_resizing)
{
vrt_thread -> init(winRect());
start();
is_resizing = false;
}
painter.drawPixmap(0, 0, vrt_thread -> pixmap);
drawGrid(&painter);
drawCurves(&painter);
painter.end();
}
/// Класс параллельного потока для отрисовки рельефа вертикальной проекции
class VThread: public QThread
{
Q_OBJECT
private:
QPainter painter; ///< рисовальщик
QDockWidget* relief_widget; ///< указатель на виджет, где будет отрисовываться рельеф
int count_read; ///< количество прочитанных элементов
PlotSettings* settings; ///< указатель на настройки для различных масштабов
QVector<QPointF> relief_vec; ///< вектор геграфических точек рельефа
QRect rect; ///< прямоугольная область, в которой будет производиться рисование
public:
QPixmap pixmap; ///< пиксельная карта рельефа
private:
QPointF initXY(double& sx, double& sy);
void drawReliefNature(QPainter* painter, QPointF cur, QPointF prev);
void drawBlackLineRelief(QPainter* painter, const QVector<QPointF>& vecBLine);
void drawCurves(QPainter* painter);
void drawTrackRelief(QPainter* painter);
protected:
virtual void run();
public:
VThread(): relief_widget(0), count_read(0), settings(0) {}
VThread(QDockWidget* rlf_widget): relief_widget(rlf_widget), count_read(0), settings(0) {}
~VThread() {}
void widget(QDockWidget* rlf_widget) { relief_widget = rlf_widget; }
QDockWidget* widget() { return relief_widget; }
void count(const int r) { count_read = r; }
const int count() { return count_read; }
void init(PlotSettings* s, QRect& rct)
{ settings = s; rect = rct; pixmap = QPixmap(rect.size()); pixmap.fill(relief_widget, 0, 0); }
void init(PlotSettings* s) { settings = s; }
void init(QRect& rct) { rect = rct; pixmap = QPixmap(rect.size()); pixmap.fill(relief_widget, 0, 0); }
void initVec(QVector<QPointF>& rv) { relief_vec = rv; }
void stop(const int size) { if(count_read == size) quit(); }
};
/// Инициализация координат - преобразование из координат графика (sx,sy) в экранные (x,y)
QPointF VThread::initXY(double& sx, double& sy)
{
const int shift_x = 30;
if(!rect.isValid()) return QPointF();
QRect rect_shift(rect); rect_shift.setLeft(rect_shift.left() + shift_x);
double dx, dy;
dx = sx - settings -> minX;
dy = sy - settings -> minY;
double x = rect_shift.left() + (dx * (rect_shift.width() - 1) / settings -> spanX());;
double y = rect.bottom() - (dy * (rect.height() - 1) / settings -> spanY());
return QPointF(x, y);
}
/// Настройка цвета рельефа (естественный цвет)
void VThread::drawReliefNature(QPainter* painter, QPointF cur, QPointF prev)
{
double distance = cur.x(), delta_height = cur.y();
QPointF scrPnt(initXY(distance, delta_height));
if(_isnan(settings -> maxX) || _isnan(settings -> minX) || _isnan(settings -> maxY) || _isnan(settings -> minY)
|| settings -> maxX == -999.0 || settings -> minX == -999.0 || settings -> maxX == -999.0)
return;
if(delta_height <= 0 || scrPnt.y() >= settings -> maxY)
return;
QColor color(demGetColor(demCalcColor(delta_height)));
double delta_height0;
int div = delta_height > 1000 ? 50 : 5;
int i = (int)(delta_height / div);
if(!((int)delta_height % div)) --i;
delta_height0 = div * i;
double prev_dist = prev.x(), prev_height = prev.y();
QPointF screenPoint(initXY(distance, delta_height0)), scrPrev(initXY(prev_dist, prev_height));
QBrush old_brush(painter -> brush());
QPen old_pen(painter -> pen());
painter -> setBrush(QBrush(color));
painter -> setPen(QPen(color));
painter -> drawLine(scrPrev, screenPoint);
for(int ix=scrPrev.x()+1; ix<screenPoint.x(); ++ix)
painter -> drawLine(QPointF(ix, scrPrev.y()), screenPoint);
painter -> setPen(old_pen);
painter -> setBrush(old_brush);
QPointF cur0(distance, delta_height0);
drawReliefNature(painter, cur0, cur);
}
/// Отрисовка черной линии рельефа для лучшей видимости его изгибов
void VThread::drawBlackLineRelief(QPainter* painter, const QVector<QPointF>& vecBLine)
{
QBrush old_brush(painter -> brush());
QPen old_pen(painter -> pen());
painter -> setPen(Qt::black);
painter -> setBrush(Qt::black);
for(int i=vecBLine.size()-1, j=vecBLine.size()-1; i>=0; --i)
{
painter -> drawLine(vecBLine[i], vecBLine[j]);
if(i != vecBLine.size() - 1) --j;
}
painter -> setPen(old_pen);
painter -> setBrush(old_brush);
}
/// Отрисовка рельефа
void VThread::drawTrackRelief(QPainter* painter)
{
QVector<QPointF> vecBLine;
if(!rect.isValid()) return;
int size = relief_vec.size();
for(QVector<QPointF>::iterator rlfPnt=relief_vec.begin(), i_prev=relief_vec.begin(); rlfPnt!=relief_vec.end();
++rlfPnt, ++count_read)
{
usedRlfSpace.acquire();
QPointF cur(*rlfPnt), prev(*i_prev);
double X = cur.x(), Y = cur.y(), prevX = prev.x(), prevY = prev.y();
QPointF screen(initXY(X, Y));
drawReliefNature(painter, cur, prev);
if(rlfPnt != relief_vec.begin()) ++i_prev;
usedRlfSpace.release();
vecBLine.append(screen);
}
drawBlackLineRelief(painter, vecBLine);
}
/// Запуск дополницельного потока
void VThread::run()
{
painter.begin(&pixmap);
painter.initFrom(relief_widget);
drawTrackRelief(&painter);
painter.end();
}
Вот блин. Спросил Бланшета на счет отрисовки, вот его ответ:
нужен либо маршалинг (я не знаю как его сделать). А вообще к примеру 3Д графику рисуют в главном потоке - и ничего, летает. А там посложнее будет
Ну, я бы сделал примерно так:
1. При изменении размеров окна создал QImage с нужными размерами остальными параметрами.
2. На ей нарисовал всё, что нужно (можно в отдельном потоке).
3. По окончанию её отрисовки пнул перерисовку окна.
4. В paintEvent() выводил енту картинку, ежели она есть.
Форум Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)