Я не понимаю, почему у меня нет корректного завершения потока. Заходит в поток только при первом вызове, а соответственно рисует график только при первом выборе параметров, далее необходимо закрывать это диалоговое окошко и открывать снова- что крайне неудобно. Просьба, пожалуйста, поясните, в чем ошибки?
Ниже приведен код:
class GraphicDisplay : public QDialog, public Ui::GraphicDisplayClass
{
Q_OBJECT
private:
QMap<int, QVector<QPointF>> curveMap; ///< список всех изображаемых кривых
// куча переменных
private:
void drawGrid(QPainter* painter);
void drawCurves(QPainter* painter);
void drawLegend(QPainter* painter, QRect& rect, int& k, int id);
QPointF initXY(double& sx, double& sy);
QPointF initXY(const QPoint* scr_coor);
protected:
void paintEvent(QPaintEvent* events);
void keyPressEvent(QKeyEvent* events);
void wheelEvent(QWheelEvent* events);
void mousePressEvent(QMouseEvent* events);
void mouseMoveEvent(QMouseEvent* events);
void mouseReleaseEvent(QMouseEvent* events);
void contextMenuEvent(QContextMenuEvent* events);
void resizeEvent(QResizeEvent* events) { QDialog::resizeEvent(events); update(); }
void closeEvent(QCloseEvent* events) { QDialog::closeEvent(events); }
void showEvent(QShowEvent* events) { QDialog::showEvent(events); }
public:
GraphicDisplay(QWidget *parent = 0);
GraphicDisplay(QWidget *parent, ParamPlotSettings& st);
~GraphicDisplay();
// куча функций
void initThread(QDialDistParam* pD, QDialTimeParam* pT, PlotSettings* sts);
void fillCurve();
};
/// Инициализация нужных для потока параметров
void GraphicDisplay::initThread(QDialDistParam* pD, QDialTimeParam* pT, PlotSettings* sts)
{
p_thread -> mainWindow = mainWindow;
p_thread -> pDist = pD;
p_thread -> pTime = pT;
p_thread -> settings = sts;
p_thread -> CurveMap(&curveMap);
}
/// Заполнение вектора данными
void GraphicDisplay::fillCurve()
{
curveMap.clear();
p_thread -> start();
if(p_thread -> getStop())
p_thread -> stop();
update();
}
/// Класс параллельного потока для заполнения данными кривых графика
class DTThread: public QThread
{
Q_OBJECT
private:
QMap<int, QVector<QPointF>>* curveMap; ///< список изображаемых кривых
volatile bool stopped; ///< переменная, контролирующая начало и конец выполнения потока
QMutex mtx; ///< мьютекс для блокировки данных при работе с дополнительным потоком
public:
QPen myPen; ///< карандаш для рисования линий определенной жирности и цвета
TLV* mainWindow; ///< указатель на главное окно
QVector<QPointF> data; ///< вектор загружаемой кривой
QDialDistParam* pDist; ///< указатель на класс диалога параметров по расстоянию
QDialTimeParam* pTime; ///< указатель на класс диалога параметров по времени
PlotSettings* settings; ///< указатель на настройку для определения масштаба
private:
void fillXVec(QVector<QPointF>& data);
protected:
void run();
public:
DTThread(): stopped(false), pDist(0), pTime(0), mainWindow(0) {}
~DTThread() { mtx.lock(); stopped = true; mtx.unlock(); }
void stop() { mtx.lock(); stopped = true; mtx.unlock(); quit(); };
QMap<int, QVector<QPointF>>* CurveMap() { return curveMap; }
void CurveMap(QMap<int, QVector<QPointF>>* crv) { mtx.lock(); curveMap = crv; mtx.unlock(); }
bool getStop() { return stopped; }
};
/// Запуск дополнительного потока
void DTThread::run()
{
if(!stopped)
{
int id = 0;
/// Заполнение данными по оси x
fillXVec(data);
for(fact_param_iter fit=fact_prm.begin(); fit!=fact_prm.end(); ++fit, ++id)
{
/// Заполнение данными по оси y
ParamDescr* param_record = fit -> param_record;
int index = 0;
for(logI jter=mainWindow -> log.begin(); jter!=mainWindow -> log.end() && index!=data.size(); ++jter, ++index)
{
PARAMVALUE val = jter -> GetParamValue(&mainWindow -> cur_rec, param_record -> Name(),
param_record -> Address());
data[index].setY(val.value);
}
curveMap -> insert(id, data);
}
mtx.lock(); stopped = true; mtx.unlock();
}
exec();
}
/// Заполнение данными по оси x
void DTThread::fillXVec(QVector<QPointF>& data)
{
double x = 0.0;
ParamPlotSettings* p_set = (ParamPlotSettings*)settings;
switch(p_set -> win_type)
{
case DISTPARAM:
for(QVector<PARAMVALUE>::iterator iter=x_data.dist_x.begin(); iter!=x_data.dist_x.end(); ++iter)
{
x += (iter -> value / 1000.0);
data.push_back(QPointF(x, 0.0));
int k = 0;
switch(iter -> status)
{
case PS_FAIL:
k = pDist -> spinFault -> text().toInt();
myPen.setWidth(k);
break;
case PS_NODATA:
k = pDist -> spinNoData -> text().toInt();
myPen.setWidth(k);
break;
case PS_TEST:
k = pDist -> spinTest -> text().toInt();
myPen.setWidth(k);
break;
case PS_OK:
k = pDist -> spinNormal -> text().toInt();
myPen.setWidth(k);
break;
}
}
break;
case TIMEPARAM:
for(QVector<PARAMVALUE>::iterator iter=x_data.time_x.begin(); iter!=x_data.time_x.end(); ++iter)
{
x += (iter -> value / 60.0);
data.push_back(QPointF(x, 0.0));
int k = 0;
switch(iter -> status)
{
case PS_FAIL:
k = pTime -> spinFault -> text().toInt();
myPen.setWidth(k);
break;
case PS_NODATA:
k = pTime -> spinNoData -> text().toInt();
myPen.setWidth(k);
break;
case PS_TEST:
k = pTime -> spinTest -> text().toInt();
myPen.setWidth(k);
break;
case PS_OK:
k = pTime -> spinNormal -> text().toInt();
myPen.setWidth(k);
break;
}
}
break;
}
}
Удалено
Всё правильно - так и должно быть (в смысле выполнятся 1 раз).
В коде нужно if заменить на while, stopped с bool на QWaitCondition
и добавить ещё QWaitCondition для убиения или лучше на QThreadPool или QtConcurrent::run переделать - тогда и стопать не надо и специально завершать.
П.С. Ты уверен, что тут нужен отдельный поток?
П.П.С. Убей дубль сообщения, чтобы не смущать народ.
Переходи на 4.4?
А набросок примерно такой:
QWaitCondition w_terminated;
QMutex m_terminated;
QReadWriteLock rw_run;
void DTThread::Run() {
while(!w_terminated.wait(m_terminated, 0)) {
QWriteLocker w_run(rw_run);
//Здесь выполняем то, что у тебя было внутри if(!stopped)
}
}
/// Инициализация нужных для потока параметров
void GraphicDisplay::initThread(QDialDistParam* pD, QDialTimeParam* pT, PlotSettings* sts) {
//Здесь старая инициализация
w_run.lockForRead();
p_thread -> start();
}
/// Заполнение вектора данными
void GraphicDisplay::fillCurve() {
curveMap.clear();
w_run.unlock();
w_run.lockForRead();
update();
}
Я же написал, что нужно изменить - fillCurve() разбить на 2:
/// Заполнение вектора данными
void GraphicDisplay::fillCurve() {
p_thread ->clearCurveMap();
w_run.unlock();
}
void GraphicDisplay::test4Update()
if (w_run.tryLockForRead()) {
curveMap = p_thread->getCurveMap();
update();
}
}
Ну и последний вопросик:
А набросок примерно такой:
QWaitCondition w_terminated;QMutex m_terminated;QReadWriteLock rw_run;void DTThread::Run()
{
while(!w_terminated.wait(m_terminated, 0))
{
QWriteLocker w_run(rw_run);
//Здесь выполняем то, что у тебя было внутри if(!stopped)
}
}
Видимо, где-то напортачил, но вот где? Ломается с сообщением:
/// Класс параллельного потока для заполнения данными кривых графика
class DTThread: public QThread
{
Q_OBJECT
private:
QMap<int, QVector<QPointF>> curveMap; ///< список изображаемых кривых
public:
QPen myPen; ///< карандаш для рисования линий определенной жирности и цвета
TLV* mainWindow; ///< указатель на главное окно
QVector<QPointF> data; ///< вектор загружаемой кривой
QDialDistParam* pDist; ///< указатель на класс диалога параметров по расстоянию
QDialTimeParam* pTime; ///< указатель на класс диалога параметров по времени
PlotSettings* settings; ///< указатель на настройку для определения масштаба
QMutex m_terminated; ///< мьютекс для блокировки данных при работе с дополнительным потоком
QWaitCondition w_terminated; ///< критическая секция для разблокировки мьютексов
QReadWriteLock rw_run; ///< мьютекс для записи и чтения
private:
void fillXVec(QVector<QPointF>& data);
protected:
void run();
public:
DTThread(): pDist(0), pTime(0), mainWindow(0) {}
~DTThread() {}
QMap<int, QVector<QPointF>> CurveMap() { return curveMap; }
void CurveMap(QMap<int, QVector<QPointF>> crv) { curveMap = crv; }
void clearCurveMap() { curveMap.clear(); }
void wake_all() { w_terminated.wakeAll(); }
};
/// Запуск дополнительного потока
void DTThread::run()
{
while(!w_terminated.wait(&m_terminated, 0))
{
rw_run.lockForRead();
int id = 0;
/// Заполнение данными по оси x
fillXVec(data);
for(fact_param_iter fit=fact_prm.begin(); fit!=fact_prm.end(); ++fit, ++id)
{
/// Заполнение данными по оси y
ParamDescr* param_record = fit -> param_record;
int index = 0;
for(logI jter=mainWindow -> log.begin(); jter!=mainWindow -> log.end() && index!=data.size(); ++jter, ++index)
{
PARAMVALUE val = jter -> GetParamValue(&mainWindow -> cur_rec, param_record -> Name(),
param_record -> Address());
data[index].setY(val.value);
}
curveMap.insert(id, data);
}
}
exec();
}
/// Заполнение данными по оси x
void DTThread::fillXVec(QVector<QPointF>& data)
{
double x = 0.0;
ParamPlotSettings* p_set = (ParamPlotSettings*)settings;
switch(p_set -> win_type)
{
case DISTPARAM:
for(QVector<PARAMVALUE>::iterator iter=x_data.dist_x.begin(); iter!=x_data.dist_x.end(); ++iter)
{
x += (iter -> value / 1000.0);
data.push_back(QPointF(x, 0.0));
int k = 0;
switch(iter -> status)
{
case PS_FAIL:
k = pDist -> spinFault -> text().toInt();
myPen.setWidth(k);
break;
case PS_NODATA:
k = pDist -> spinNoData -> text().toInt();
myPen.setWidth(k);
break;
case PS_TEST:
k = pDist -> spinTest -> text().toInt();
myPen.setWidth(k);
break;
case PS_OK:
k = pDist -> spinNormal -> text().toInt();
myPen.setWidth(k);
break;
}
}
break;
case TIMEPARAM:
for(QVector<PARAMVALUE>::iterator iter=x_data.time_x.begin(); iter!=x_data.time_x.end(); ++iter)
{
x += (iter -> value / 60.0);
data.push_back(QPointF(x, 0.0));
int k = 0;
switch(iter -> status)
{
case PS_FAIL:
k = pTime -> spinFault -> text().toInt();
myPen.setWidth(k);
break;
case PS_NODATA:
k = pTime -> spinNoData -> text().toInt();
myPen.setWidth(k);
break;
case PS_TEST:
k = pTime -> spinTest -> text().toInt();
myPen.setWidth(k);
break;
case PS_OK:
k = pTime -> spinNormal -> text().toInt();
myPen.setWidth(k);
break;
}
}
break;
}
}
/// Заполнение вектора данными
void GraphicDisplay::fillCurve()
{
p_thread -> clearCurveMap();
p_thread -> rw_run.unlock();
}
/// Инициализация нужных для потока параметров
void GraphicDisplay::initThread(QDialDistParam* pD, QDialTimeParam* pT, PlotSettings* sts)
{
p_thread -> mainWindow = mainWindow;
p_thread -> pDist = pD;
p_thread -> pTime = pT;
p_thread -> settings = sts;
p_thread -> CurveMap(curveMap);
p_thread -> rw_run.lockForRead();
p_thread -> start();
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(forUpdate()));
timer -> start(UPDATETIME);
}
/// Обновление данных списка
void GraphicDisplay::forUpdate()
{
if(p_thread -> rw_run.tryLockForRead())
{
curveMap = p_thread -> CurveMap();
update();
}
}
/// Заставить остановиться остальным процессам
void GraphicDisplay::wake_all()
{
p_thread -> wake_all();
}
/// Нажатие на кнопку OK
void QDialDistParam::btnOKClick()
{
ParamPlotSettings settings;
settings.setMaxX(q_calc.maxDist, DISTPARAM);
settings.setMinX(x_data.dist_x.front().value, DISTPARAM);
double c_max = 10.0, c_min = 0.0;
findMinMaxY(c_min, c_max);
settings.setMinY(c_min);
settings.setMaxY(c_max);
settings.adjust();
graphic -> setPlotSettings(settings);
graphic -> pDist = this;
graphic -> retranslateHeader(graphic);
graphic -> initThread(this, 0, &settings);
graphic -> fillCurve();
graphic -> wake_all();
graphic -> exec();
}
Немного изменил. Все-равно ломается, во-первых, правда, в другом месте.
Подскажите, пожалуйста, где ошибка.
Правка произошла в функции run:
/// Запуск дополнительного потока
void DTThread::run()
{
m_terminated.lock();
while(!w_terminated.wait(&m_terminated, 0))
{
int id = 0;
/// Заполнение данными по оси x
fillXVec(data);
for(fact_param_iter fit=fact_prm.begin(); fit!=fact_prm.end(); ++fit, ++id)
{
/// Заполнение данными по оси y
ParamDescr* param_record = fit -> param_record;
int index = 0;
for(logI jter=mainWindow -> log.begin(); jter!=mainWindow -> log.end() && index!=data.size(); ++jter, ++index)
{
PARAMVALUE val = jter -> GetParamValue(&mainWindow -> cur_rec, param_record -> Name(),
param_record -> Address());
data[index].setY(val.value);
}
curveMap.insert(id, data);
}
}
w_terminated.wakeAll();
m_terminated.unlock();
exec();
}
Поправил немного. Но все-равно, работает кривовато. Причем глюки очень разнообразные: то график отображается параметра, который выбран не в данный момент, а во время предыдущей выборке, то совсем не отображается, то застревает на этапе выборки. Может быть сможете подсказать, где копнуть, чтобы поправить это безобразие?
GraphicDisplay::GraphicDisplay(QWidget *parent): QDialog(parent)
{
p_thread = new DTThread;
}
GraphicDisplay::GraphicDisplay(QWidget *parent, ParamPlotSettings& st): QDialog(parent)
{
p_thread = new DTThread;
}
GraphicDisplay::~GraphicDisplay()
{
delete rubber;
delete contextMenu;
if(timer)
{
timer -> stop();
delete timer;
}
if(p_thread)
{
p_thread -> quit();
if(p_thread -> isRunning()) p_thread -> terminate();
delete p_thread;
}
}
/// Отрисовка графика
void GraphicDisplay::paintEvent(QPaintEvent* events)
{
painter.begin(this);
painter.setWindow(paramsDisplay -> rect());
painter.setFont(QFont("Tahoma", 8, Qt::SolidLine));
painter.setPen(Qt::black);
if(gridAction -> isChecked()) drawGrid(&painter);
drawCurves(&painter);
if(timer != 0) if(curveMap.size() == fact_prm.size())
timer -> stop();
painter.end();
}
/// Заполнение вектора данными
void GraphicDisplay::fillCurve()
{
p_thread -> clearCurveMap();
p_thread -> rw_run.unlock();
}
/// Инициализация нужных для потока параметров
void GraphicDisplay::initThread(QDialDistParam* pD, QDialTimeParam* pT, PlotSettings* sts)
{
p_thread -> mainWindow = mainWindow;
p_thread -> pDist = pD;
p_thread -> pTime = pT;
p_thread -> settings = sts;
p_thread -> CurveMap(curveMap);
p_thread -> rw_run.lockForRead();
p_thread -> start();
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(forUpdate()));
timer -> start(UPDATETIME);
}
/// Обновление данных списка
void GraphicDisplay::forUpdate()
{
if(p_thread -> rw_run.tryLockForRead())
{
curveMap = p_thread -> CurveMap();
update();
}
}
/// Закрытие окна
void GraphicDisplay::closeEvent(QCloseEvent* events)
{
QDialog::closeEvent(events);
if(p_thread)
{
if(p_thread -> isRunning()) p_thread -> terminate();
p_thread -> quit();
}
if(timer)
timer -> stop();
}
/// Запуск дополнительного потока
void DTThread::run()
{
m_terminated.lock();
while(!w_terminated.wait(&m_terminated, 0))
{
int id = 0;
/// Заполнение данными по оси x
fillXVec(data);
for(fact_param_iter fit=fact_prm.begin(); fit!=fact_prm.end(); ++fit, ++id)
{
m_terminated.unlock();
/// Заполнение данными по оси y
ParamDescr* param_record = fit -> param_record;
int index = 0;
for(logI jter=mainWindow -> log.begin(); jter!=mainWindow -> log.end() && index!=data.size(); ++jter, ++index)
{
PARAMVALUE val = jter -> GetParamValue(&mainWindow -> cur_rec, param_record -> Name(),
param_record -> Address());
data[index].setY(val.value);
}
curveMap.insert(id, data);
m_terminated.lock();
}
}
m_terminated.unlock();
exec();
}
Извини, правда некогда разбираться.
Но, если ты таки хочешь работать с потоками, стоит самому разобраться - я же твою прогу писать не буду.
Не знаю уж чего я там еще такого наваял, но пока вроде бы работает без багов!
Форум Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)