00001 #include <qlayout.h> 00002 #include <qpen.h> 00003 #include <qwt_plot.h> 00004 #include <qwt_plot_canvas.h> 00005 #include <qwt_scale_widget.h> 00006 #include <qwt_scale_draw.h> 00007 #include "plotmatrix.h" 00008 #include "customtracker.h" 00009 00010 static int widget_uid = 0; 00011 00012 PlotMatrix::PlotMatrix(QString name, PlotDataMap& datamap, QWidget *parent ): 00013 QFrame( parent ), 00014 _mapped_data(datamap), 00015 _name(name) 00016 { 00017 _num_rows = 0; 00018 _num_cols = 0; 00019 _layout = new QGridLayout( this ); 00020 _horizontal_link = true; 00021 updateLayout(); 00022 } 00023 00024 00025 PlotWidget* PlotMatrix::addPlotWidget(unsigned row, unsigned col) 00026 { 00027 PlotWidget *plot = new PlotWidget( _mapped_data, this ); 00028 00029 plot->setWindowTitle(QString("PlotWidget ") + QString::number(widget_uid++)); 00030 00031 connect( plot, &PlotWidget::rectChanged, 00032 this, &PlotMatrix::on_singlePlotScaleChanged); 00033 00034 plot->setAttribute(Qt::WA_DeleteOnClose); 00035 00036 _layout->addWidget( plot, row, col ); 00037 _layout->setRowStretch(row,1); 00038 _layout->setColumnStretch(col,1); 00039 00040 emit plotAdded(plot); 00041 00042 return plot; 00043 } 00044 00045 void PlotMatrix::addRow() 00046 { 00047 if( _num_rows==0 && _num_cols==0 ) 00048 { 00049 addPlotWidget( 0, 0 ); 00050 _num_rows = 1; 00051 _num_cols = 1; 00052 } 00053 else{ 00054 for ( unsigned col = 0; col < colsCount(); col++ ) { 00055 addPlotWidget( _num_rows, col ); 00056 } 00057 _num_rows++; 00058 } 00059 00060 updateLayout(); 00061 } 00062 00063 void PlotMatrix::addColumn() 00064 { 00065 if( _num_rows==0 && _num_cols==0 ) 00066 { 00067 addPlotWidget( 0, 0 ); 00068 _num_rows = 1; 00069 _num_cols = 1; 00070 } 00071 else { 00072 for ( unsigned row = 0; row < rowsCount(); row++ ) 00073 { 00074 addPlotWidget( row, _num_cols ); 00075 } 00076 _num_cols++; 00077 } 00078 updateLayout(); 00079 } 00080 00081 void PlotMatrix::swapPlots( unsigned rowA, unsigned colA, unsigned rowB, unsigned colB) 00082 { 00083 QWidget *widgetA = _layout->itemAtPosition(rowA, colA)->widget(); 00084 QWidget *widgetB = _layout->itemAtPosition(rowB, colB)->widget(); 00085 00086 _layout->removeItem( _layout->itemAtPosition(rowA, colA) ); 00087 _layout->removeItem( _layout->itemAtPosition(rowB, colB) ); 00088 00089 _layout->addWidget(widgetA, rowB, colB); 00090 _layout->addWidget(widgetB, rowA, colA); 00091 updateLayout(); 00092 } 00093 00094 void PlotMatrix::removeColumn(unsigned column_to_delete) 00095 { 00096 if(_num_rows==1 && _num_cols ==1 ) { 00097 return; 00098 } 00099 00100 for(unsigned col = column_to_delete; col< _num_cols-1; col++) 00101 { 00102 for(unsigned row=0; row< _num_rows; row++) 00103 { 00104 this->swapPlots( row, col, row, col+1); 00105 } 00106 } 00107 for(unsigned row=0; row< _num_rows; row++) 00108 { 00109 plotAt( row, _num_cols -1)->close(); 00110 } 00111 _layout->setColumnStretch(_num_cols -1,0); 00112 00113 _num_cols--; 00114 if( _num_cols == 0){ 00115 _num_rows = 0; 00116 } 00117 00118 updateLayout(); 00119 00120 } 00121 00122 void PlotMatrix::removeRow(unsigned row_to_delete) 00123 { 00124 if(_num_rows==1 && _num_cols ==1 ) { 00125 return; 00126 } 00127 for(unsigned row = row_to_delete; row< _num_rows-1; row++) 00128 { 00129 for(unsigned col = 0; col< _num_cols; col++) 00130 { 00131 this->swapPlots( row, col, row+1, col); 00132 } 00133 } 00134 for(unsigned col=0; col< _num_cols; col++) 00135 { 00136 plotAt( _num_rows-1, col)->close(); 00137 } 00138 _layout->setRowStretch(_num_rows -1,0); 00139 00140 _num_rows--; 00141 if( _num_rows == 0){ 00142 _num_cols = 0; 00143 } 00144 00145 updateLayout(); 00146 } 00147 00148 00149 PlotMatrix::~PlotMatrix(){} 00150 00151 unsigned PlotMatrix::rowsCount() const 00152 { 00153 return _num_rows; 00154 } 00155 00156 unsigned PlotMatrix::colsCount() const 00157 { 00158 return _num_cols; 00159 } 00160 00161 unsigned PlotMatrix::plotCount() const 00162 { 00163 return _num_rows*_num_cols; 00164 } 00165 00166 bool PlotMatrix::isColumnEmpty( unsigned col ) const 00167 { 00168 for (int r=0; r < _layout->rowCount(); r++) 00169 { 00170 auto plot = plotAt(r, col); 00171 if( plot && ! plot->isEmpty() ) { 00172 return false; 00173 } 00174 } 00175 return true; 00176 } 00177 00178 bool PlotMatrix::isRowEmpty(unsigned row ) const 00179 { 00180 for (int c=0; c< _layout->columnCount(); c++) 00181 { 00182 auto plot = plotAt(row, c); 00183 if( plot && ! plot->isEmpty() ) { 00184 return false; 00185 } 00186 } 00187 return true; 00188 } 00189 00190 00191 PlotWidget* PlotMatrix::plotAt( unsigned row, unsigned column ) 00192 { 00193 QLayoutItem* item = _layout->itemAtPosition(row,column); 00194 if(item) { 00195 PlotWidget* plot = static_cast<PlotWidget*>( item->widget() ); 00196 return plot; 00197 } 00198 return NULL; 00199 } 00200 00201 const PlotWidget* PlotMatrix::plotAt( unsigned row, unsigned column ) const 00202 { 00203 QLayoutItem* item = _layout->itemAtPosition(row,column); 00204 if(item) { 00205 PlotWidget* plot = static_cast<PlotWidget*>( item->widget() ); 00206 return plot; 00207 } 00208 return NULL; 00209 } 00210 00211 PlotWidget* PlotMatrix::plotAt( unsigned index ) 00212 { 00213 return plotAt(index% rowsCount(), index/rowsCount()); 00214 } 00215 00216 const PlotWidget* PlotMatrix::plotAt( unsigned index ) const 00217 { 00218 return plotAt(index% rowsCount(), index/rowsCount()); 00219 } 00220 00221 00222 void PlotMatrix::setAxisScale(QwtPlot::Axis axisId, unsigned row, unsigned col, 00223 double min, double max, double step ) 00224 { 00225 PlotWidget *plt = plotAt( row, col ); 00226 if ( plt ) 00227 { 00228 plt->setAxisScale( axisId, min, max, step ); 00229 plt->updateAxes(); 00230 } 00231 } 00232 00233 QDomElement PlotMatrix::xmlSaveState( QDomDocument &doc ) const 00234 { 00235 QDomElement element = doc.createElement("plotmatrix"); 00236 00237 element.setAttribute("rows", _num_rows ); 00238 element.setAttribute("columns", _num_cols ); 00239 00240 for(unsigned col = 0; col< _num_cols; col++) 00241 { 00242 for(unsigned row = 0; row< _num_rows; row++) 00243 { 00244 const PlotWidget* plot = plotAt(row,col); 00245 QDomElement child = plot->xmlSaveState(doc); 00246 00247 child.setAttribute("row", row); 00248 child.setAttribute("col", col); 00249 00250 element.appendChild( child ); 00251 } 00252 } 00253 return element; 00254 } 00255 00256 bool PlotMatrix::xmlLoadState( QDomElement &plotmatrix ) 00257 { 00258 if( !plotmatrix.hasAttribute("rows") || !plotmatrix.hasAttribute("columns") ) 00259 { 00260 qWarning() << "No [rows] or [columns] attribute in <plotmatrix> XML file!"; 00261 return false; 00262 } 00263 unsigned rows = plotmatrix.attribute("rows").toUInt(); 00264 unsigned cols = plotmatrix.attribute("columns" ).toUInt(); 00265 00266 while( rows > _num_rows){ addRow(); } 00267 while( rows < _num_rows){ removeRow( _num_rows-1 ); } 00268 00269 while( cols > _num_cols){ addColumn(); } 00270 while( cols < _num_cols){ removeColumn( _num_cols-1 ); } 00271 00272 QMessageBox::StandardButton load_answer = QMessageBox::Ok; 00273 00274 QDomElement plot_element; 00275 for ( plot_element = plotmatrix.firstChildElement( "plot" ) ; 00276 !plot_element.isNull(); 00277 plot_element = plot_element.nextSiblingElement( "plot" ) ) 00278 { 00279 if( !plot_element.hasAttribute("row") || !plot_element.hasAttribute("col") ) 00280 { 00281 qWarning() << "No [row] or [col] attribute in <plot> XML file!"; 00282 return false; 00283 } 00284 unsigned row = plot_element.attribute("row").toUInt(); 00285 unsigned col = plot_element.attribute("col").toUInt(); 00286 00287 bool success = plotAt(row,col)->xmlLoadState( plot_element, &load_answer ) ; 00288 if( !success ) 00289 { 00290 return false; 00291 } 00292 } 00293 return true; 00294 } 00295 00296 00297 00298 void PlotMatrix::updateLayout() 00299 { 00300 for ( unsigned row = 0; row < rowsCount(); row++ ) 00301 { 00302 alignAxes( row, QwtPlot::xBottom ); 00303 alignScaleBorder( row, QwtPlot::yLeft ); 00304 } 00305 00306 for ( unsigned col = 0; col < colsCount(); col++ ) 00307 { 00308 alignAxes( col, QwtPlot::yLeft ); 00309 alignScaleBorder( col, QwtPlot::xBottom ); 00310 } 00311 } 00312 00313 void PlotMatrix::replot() 00314 { 00315 for ( unsigned i = 0; i< plotCount(); i++ ) 00316 { 00317 PlotWidget *plot = plotAt(i); 00318 plot->replot(); 00319 } 00320 } 00321 00322 00323 void PlotMatrix::setHorizontalLink(bool linked) 00324 { 00325 _horizontal_link = linked; 00326 } 00327 00328 00329 void PlotMatrix::setName(const QString &new_name) 00330 { 00331 _name = new_name; 00332 } 00333 00334 const QString &PlotMatrix::name() const 00335 { 00336 return _name; 00337 } 00338 00339 QGridLayout *PlotMatrix::gridLayout() 00340 { 00341 return _layout; 00342 } 00343 00344 void PlotMatrix::maximumZoomOutHorizontal() 00345 { 00346 for ( unsigned i = 0; i< plotCount(); i++ ) 00347 { 00348 PlotWidget *plot = plotAt(i); 00349 if( plot->isEmpty() == false) 00350 { 00351 plot->on_zoomOutHorizontal_triggered( false ); 00352 } 00353 } 00354 replot(); 00355 } 00356 00357 void PlotMatrix::maximumZoomOutVertical() 00358 { 00359 for ( unsigned i = 0; i < plotCount(); i++ ) 00360 { 00361 PlotWidget *plot = plotAt(i); 00362 if( plot->isEmpty() == false) 00363 { 00364 plot->on_zoomOutVertical_triggered(false); 00365 } 00366 } 00367 replot(); 00368 } 00369 00370 void PlotMatrix::maximumZoomOut() 00371 { 00372 for ( unsigned i = 0; i < plotCount(); i++ ) 00373 { 00374 PlotWidget *plot = plotAt(i); 00375 if( plot->isEmpty() == false) 00376 { 00377 plot->zoomOut(false); 00378 } 00379 } 00380 replot(); 00381 } 00382 00383 00384 void PlotMatrix::on_singlePlotScaleChanged(PlotWidget *modified_plot, QRectF new_range) 00385 { 00386 if( _horizontal_link ) 00387 { 00388 for ( unsigned i = 0; i< plotCount(); i++ ) 00389 { 00390 PlotWidget *plot = plotAt(i); 00391 if( plot->isEmpty() == false && 00392 modified_plot != plot && 00393 plot->isXYPlot() == false) 00394 { 00395 QRectF bound_act = plot->currentBoundingRect(); 00396 bound_act.setLeft( new_range.left() ); 00397 bound_act.setRight( new_range.right() ); 00398 plot->setScale( bound_act, false ); 00399 plot->replot(); 00400 } 00401 } 00402 } 00403 emit undoableChange(); 00404 } 00405 00406 void PlotMatrix::alignAxes( unsigned rowOrColumn, QwtPlot::Axis axisId ) 00407 { 00408 bool iterating_rows = ( axisId == QwtPlot::yLeft || axisId == QwtPlot::yRight ); 00409 const int COUNT = iterating_rows ? rowsCount() : colsCount(); 00410 00411 double maxExtent = 0; 00412 00413 for ( unsigned i = 0; i < COUNT; i++ ) 00414 { 00415 QwtPlot *p = iterating_rows? plotAt( i, rowOrColumn ) : plotAt( rowOrColumn, i ); 00416 if ( p ) 00417 { 00418 QwtScaleWidget *scaleWidget = p->axisWidget( axisId ); 00419 00420 QwtScaleDraw *sd = scaleWidget->scaleDraw(); 00421 sd->setMinimumExtent( 0.0 ); 00422 00423 const double extent = sd->extent( scaleWidget->font() ); 00424 if ( extent > maxExtent ) 00425 maxExtent = extent; 00426 } 00427 } 00428 00429 for ( unsigned i = 0; i < COUNT; i++ ) 00430 { 00431 QwtPlot *p = iterating_rows? plotAt( i, rowOrColumn ) : plotAt( rowOrColumn, i ); 00432 if ( p ) 00433 { 00434 QwtScaleWidget *scaleWidget = p->axisWidget( axisId ); 00435 scaleWidget->scaleDraw()->setMinimumExtent( maxExtent ); 00436 } 00437 } 00438 } 00439 00440 void PlotMatrix::alignScaleBorder(unsigned rowOrColumn, QwtPlot::Axis axisId ) 00441 { 00442 if ( axisId == QwtPlot::yLeft || axisId == QwtPlot::yRight ) 00443 { 00444 for ( unsigned col = 0; col < colsCount(); col++ ) 00445 { 00446 QwtPlot *p = plotAt( rowOrColumn, col ); 00447 if ( p ) 00448 p->axisWidget( axisId )->setMinBorderDist( 10, 10 ); 00449 } 00450 } 00451 else if ( axisId == QwtPlot::xTop || axisId == QwtPlot::xBottom ) 00452 { 00453 for ( unsigned row = 0; row < rowsCount(); row++ ) 00454 { 00455 QwtPlot *p = plotAt( row, rowOrColumn ); 00456 if ( p ) 00457 p->axisWidget( axisId )->setMinBorderDist( 15, 15 ); 00458 } 00459 } 00460 }