00001 #include <QMenu>
00002 #include <QSignalMapper>
00003 #include <QAction>
00004 #include <QInputDialog>
00005 #include <QMouseEvent>
00006 #include <QFileDialog>
00007 #include <QApplication>
00008 #include "qwt_plot_renderer.h"
00009 #include "mainwindow.h"
00010 #include "tabbedplotwidget.h"
00011 #include "tab_widget.h"
00012 #include "ui_tabbedplotwidget.h"
00013
00014 std::map<QString,TabbedPlotWidget*> TabbedPlotWidget::_instances;
00015
00016 TabbedPlotWidget::TabbedPlotWidget(QString name,
00017 QMainWindow *main_window_,
00018 PlotMatrix *first_tab,
00019 PlotDataMapRef &mapped_data,
00020 QMainWindow *parent ) :
00021 QWidget(parent),
00022 _mapped_data(mapped_data),
00023 ui(new Ui::TabbedPlotWidget),
00024 _name(name),
00025 _labels_status (LabelStatus::RIGHT)
00026 {
00027 MainWindow* main_window = static_cast<MainWindow*>(main_window_);
00028
00029 if( main_window == parent){
00030 _parent_type = "main_window";
00031 }
00032 else
00033 {
00034 _parent_type = "floating_window";
00035 }
00036
00037 if( TabbedPlotWidget::_instances.count(_name) > 0)
00038 {
00039 throw std::runtime_error("This is not supposed to happen");
00040 }
00041
00042 _instances[_name] = this;
00043
00044 ui->setupUi(this);
00045
00046 _horizontal_link = true;
00047
00048 tabWidget()->tabBar()->installEventFilter( this );
00049
00050 _action_renameTab = new QAction(tr("Rename tab"), this);
00051 connect( _action_renameTab, &QAction::triggered, this, &TabbedPlotWidget::on_renameCurrentTab);
00052
00053 QIcon iconSave;
00054 iconSave.addFile(QStringLiteral(":/icons/resources/light/save.png"), QSize(26, 26));
00055 _action_savePlots = new QAction(tr("&Save plots to file"), this);
00056 _action_savePlots->setIcon(iconSave);
00057 connect(_action_savePlots, &QAction::triggered, this, &TabbedPlotWidget::on_savePlotsToFile);
00058
00059 _tab_menu = new QMenu(this);
00060 _tab_menu->addAction( _action_renameTab );
00061 _tab_menu->addSeparator();
00062 _tab_menu->addAction( _action_savePlots );
00063 _tab_menu->addSeparator();
00064
00065 connect( this, &TabbedPlotWidget::destroyed, main_window, &MainWindow::on_tabbedAreaDestroyed );
00066 connect( this, &TabbedPlotWidget::sendTabToNewWindow, main_window, &MainWindow::onCreateFloatingWindow);
00067 connect( this, &TabbedPlotWidget::matrixAdded, main_window, &MainWindow::onPlotMatrixAdded);
00068 connect( this, &TabbedPlotWidget::undoableChangeHappened, main_window, &MainWindow::onUndoableChange);
00069
00070 connect( ui->tabWidget, &TabWidget::movingPlotWidgetToTab, this, &TabbedPlotWidget::onMoveWidgetIntoNewTab);
00071
00072 this->addTab(first_tab);
00073 }
00074
00075
00076
00077
00078
00079
00080 PlotMatrix *TabbedPlotWidget::currentTab()
00081 {
00082 return static_cast<PlotMatrix*>( tabWidget()->currentWidget() );
00083 }
00084
00085 QTabWidget *TabbedPlotWidget::tabWidget()
00086 {
00087 return ui->tabWidget;
00088 }
00089
00090 const QTabWidget* TabbedPlotWidget::tabWidget() const
00091 {
00092 return ui->tabWidget;
00093 }
00094
00095
00096 void TabbedPlotWidget::addTab( PlotMatrix* tab)
00097 {
00098 if( !tab )
00099 {
00100 tab = new PlotMatrix("plot", _mapped_data, this);
00101 tabWidget()->addTab( tab, QString("plot") );
00102
00103 QApplication::processEvents();
00104 emit matrixAdded( tab );
00105 tab->addColumn();
00106 }
00107 else{
00108 tabWidget()->addTab( tab, tab->name() );
00109 }
00110
00111 tabWidget()->setCurrentWidget( tab );
00112 tab->setHorizontalLink( _horizontal_link );
00113 }
00114
00115 QDomElement TabbedPlotWidget::xmlSaveState(QDomDocument &doc) const
00116 {
00117 QDomElement tabbed_area = doc.createElement( "tabbed_widget" );
00118
00119 tabbed_area.setAttribute("name", _name);
00120 tabbed_area.setAttribute("parent", _parent_type);
00121
00122 for(int i=0; i< tabWidget()->count(); i++)
00123 {
00124 PlotMatrix* widget = static_cast<PlotMatrix*>( tabWidget()->widget(i) );
00125 QDomElement element = widget->xmlSaveState(doc);
00126
00127 element.setAttribute("tab_name", tabWidget()->tabText(i) );
00128 tabbed_area.appendChild( element );
00129 }
00130
00131 QDomElement current_plotmatrix = doc.createElement( "currentPlotMatrix" );
00132 current_plotmatrix.setAttribute( "index", tabWidget()->currentIndex() );
00133 tabbed_area.appendChild( current_plotmatrix );
00134
00135 return tabbed_area;
00136 }
00137
00138 bool TabbedPlotWidget::xmlLoadState(QDomElement &tabbed_area)
00139 {
00140 int num_tabs = tabWidget()->count();
00141 int index = 0;
00142
00143 QDomElement plotmatrix_el;
00144
00145 for ( plotmatrix_el = tabbed_area.firstChildElement( "plotmatrix" ) ;
00146 !plotmatrix_el.isNull();
00147 plotmatrix_el = plotmatrix_el.nextSiblingElement( "plotmatrix" ) )
00148 {
00149
00150 if( index == num_tabs)
00151 {
00152 this->addTab( NULL );
00153 num_tabs++;
00154 }
00155 PlotMatrix* plot_matrix = static_cast<PlotMatrix*>( tabWidget()->widget(index) );
00156 bool success = plot_matrix->xmlLoadState( plotmatrix_el );
00157
00158
00159 if( plotmatrix_el.hasAttribute("tab_name"))
00160 {
00161 QString tab_name = plotmatrix_el.attribute("tab_name" );
00162 tabWidget()->setTabText( index, tab_name );
00163 plot_matrix->setName( tab_name );
00164 }
00165
00166 if( !success )
00167 {
00168 return false;
00169 }
00170
00171 index++;
00172 }
00173
00174
00175 while( num_tabs > index ){
00176 tabWidget()->removeTab( num_tabs-1 );
00177 num_tabs--;
00178 }
00179
00180 QDomElement current_plotmatrix = tabbed_area.firstChildElement( "currentPlotMatrix" );
00181 int current_index = current_plotmatrix.attribute( "index" ).toInt();
00182
00183 if(current_index>=0 && current_index < tabWidget()->count())
00184 {
00185 tabWidget()->setCurrentIndex( current_index );
00186 }
00187 return true;
00188 }
00189
00190 void TabbedPlotWidget::setStreamingMode(bool streaming_mode)
00191 {
00192 ui->buttonLinkHorizontalScale->setEnabled( !streaming_mode );
00193 ui->pushVerticalResize->setEnabled( !streaming_mode );
00194 ui->pushHorizontalResize->setEnabled( !streaming_mode );
00195 }
00196
00197
00198 TabbedPlotWidget::~TabbedPlotWidget(){
00199
00200 delete ui;
00201 }
00202
00203 void TabbedPlotWidget::on_renameCurrentTab()
00204 {
00205 int idx = tabWidget()->tabBar()->currentIndex ();
00206
00207 bool ok = true;
00208 QString newName = QInputDialog::getText (
00209 this, tr ("Change Name of the selected tab"),
00210 tr ("Insert New Tab Name"),
00211 QLineEdit::Normal,
00212 tabWidget()->tabText (idx),
00213 &ok);
00214
00215 if (ok) {
00216 tabWidget()->setTabText (idx, newName);
00217 currentTab()->setName( newName );
00218 }
00219 }
00220
00221 void TabbedPlotWidget::on_savePlotsToFile()
00222 {
00223 int idx = tabWidget()->tabBar()->currentIndex();
00224 PlotMatrix* matrix = static_cast<PlotMatrix*>( tabWidget()->widget(idx) );
00225
00226 QFileDialog saveDialog;
00227 saveDialog.setAcceptMode(QFileDialog::AcceptSave);
00228 saveDialog.setDefaultSuffix("png");
00229 saveDialog.selectFile(currentTab()->name());
00230
00231 saveDialog.setNameFilter("Compatible formats (*.jpg *.jpeg *.png)");
00232
00233 saveDialog.exec();
00234
00235 if(saveDialog.result() == QDialog::Accepted && !saveDialog.selectedFiles().empty())
00236 {
00237 QString fileName = saveDialog.selectedFiles().first();
00238
00239 saveTabImage(fileName, matrix);
00240 }
00241 }
00242
00243 void TabbedPlotWidget::saveTabImage(QString fileName, PlotMatrix* matrix)
00244 {
00245 QPixmap pixmap (1200,900);
00246 QPainter * painter = new QPainter(&pixmap);
00247
00248 if ( !fileName.isEmpty() )
00249 {
00250 QwtPlotRenderer rend;
00251
00252 int delta_X = pixmap.width() / matrix->colsCount();
00253 int delta_Y = pixmap.height() / matrix->rowsCount();
00254
00255 for (unsigned c=0; c< matrix->colsCount(); c++)
00256 {
00257 for (unsigned r=0; r< matrix->rowsCount(); r++)
00258 {
00259 PlotWidget* widget = matrix->plotAt(r,c);
00260 QRect rect(delta_X*c, delta_Y*r, delta_X, delta_Y);
00261 rend.render(widget,painter, rect);
00262 }
00263 }
00264 pixmap.save(fileName);
00265 }
00266 }
00267
00268 void TabbedPlotWidget::on_pushAddRow_pressed()
00269 {
00270 currentTab()->addRow();
00271 onLabelStatusChanged();
00272 emit undoableChangeHappened();
00273 }
00274
00275 void TabbedPlotWidget::on_pushAddColumn_pressed()
00276 {
00277 currentTab()->addColumn();
00278 onLabelStatusChanged();
00279 emit undoableChangeHappened();
00280 }
00281
00282 void TabbedPlotWidget::on_pushVerticalResize_pressed()
00283 {
00284 currentTab()->maximumZoomOutVertical();
00285 emit undoableChangeHappened();
00286 }
00287
00288 void TabbedPlotWidget::on_pushHorizontalResize_pressed()
00289 {
00290 currentTab()->maximumZoomOutHorizontal();
00291 emit undoableChangeHappened();
00292 }
00293
00294 void TabbedPlotWidget::on_pushButtonZoomMax_pressed()
00295 {
00296 currentTab()->maximumZoomOut();
00297 emit undoableChangeHappened();
00298 }
00299
00300 void TabbedPlotWidget::onMoveWidgetIntoNewTab(QString plot_name)
00301 {
00302 int src_row, src_col;
00303 PlotMatrix* src_matrix = nullptr;
00304 PlotWidget* source = nullptr;
00305
00306 auto func = [&](QTabWidget * tabs)
00307 {
00308 for (int t=0; t < tabs->count(); t++)
00309 {
00310 PlotMatrix* matrix = static_cast<PlotMatrix*>(tabs->widget(t));
00311
00312 for(unsigned row=0; row< matrix->rowsCount(); row++)
00313 {
00314 for(unsigned col=0; col< matrix->colsCount(); col++)
00315 {
00316 PlotWidget* plot = matrix->plotAt(row, col);
00317 if( plot->windowTitle() == plot_name)
00318 {
00319 src_matrix = matrix;
00320 src_row = row;
00321 src_col = col;
00322 source = plot;
00323 return;
00324 }
00325 }
00326 }
00327 }
00328 };
00329
00330 for(const auto& it: TabbedPlotWidget::instances())
00331 {
00332 func( it.second->tabWidget() );
00333 }
00334
00335 addTab();
00336 PlotMatrix* dst_matrix = currentTab();
00337 PlotWidget* destination = dst_matrix->plotAt(0,0);
00338
00339 src_matrix->gridLayout()->removeWidget( source );
00340 dst_matrix->gridLayout()->removeWidget( destination );
00341
00342 src_matrix->gridLayout()->addWidget( destination, src_row, src_col );
00343 dst_matrix->gridLayout()->addWidget( source, 0, 0 );
00344 source->changeBackgroundColor( Qt::white );
00345 destination->changeBackgroundColor( Qt::white );
00346
00347 src_matrix->removeEmpty();
00348 src_matrix->updateLayout();
00349 dst_matrix->updateLayout();
00350 emit undoableChangeHappened();
00351
00352 }
00353
00354 void TabbedPlotWidget::on_addTabButton_pressed()
00355 {
00356 addTab( nullptr );
00357 emit undoableChangeHappened();
00358 }
00359
00360 void TabbedPlotWidget::on_pushRemoveEmpty_pressed()
00361 {
00362 currentTab()->removeEmpty();
00363 emit undoableChangeHappened();
00364 }
00365
00366 void TabbedPlotWidget::on_tabWidget_currentChanged(int index)
00367 {
00368 if( tabWidget()->count() == 0)
00369 {
00370 if( _parent_type.compare("main_window") == 0)
00371 {
00372 addTab( NULL);
00373 }
00374 else{
00375 this->parent()->deleteLater();
00376 }
00377 }
00378
00379 PlotMatrix* tab = static_cast<PlotMatrix*>( tabWidget()->widget(index) );
00380 if( tab )
00381 {
00382 tab->replot();
00383 }
00384 }
00385
00386 void TabbedPlotWidget::on_tabWidget_tabCloseRequested(int index)
00387 {
00388 PlotMatrix* tab = static_cast<PlotMatrix*>( tabWidget()->widget(index) );
00389
00390 bool close_confirmed = true;
00391 if( tab->plotCount() == 1 )
00392 {
00393 if( tab->plotAt(0)->isEmpty()){
00394 close_confirmed = false;
00395 }
00396 }
00397
00398 QMessageBox::StandardButton do_remove = QMessageBox::Yes;
00399
00400 if( close_confirmed )
00401 {
00402 tabWidget()->setCurrentIndex( index );
00403 QApplication::processEvents();
00404
00405 do_remove = QMessageBox::question( this, tr("Warning"),
00406 tr("Do you really want to destroy this tab?\n"),
00407 QMessageBox::Yes | QMessageBox::No,
00408 QMessageBox::No );
00409 }
00410 if( do_remove == QMessageBox::Yes )
00411 {
00412
00413
00414 if( tabWidget()->count() == 1){
00415 on_addTabButton_pressed();
00416 }
00417
00418 PlotMatrix* matrix = static_cast<PlotMatrix*>( tabWidget()->widget(index) );
00419
00420 for(unsigned p=0; p< matrix->plotCount(); p++)
00421 {
00422 PlotWidget* plot = matrix->plotAt(p);
00423 plot->detachAllCurves();
00424 plot->deleteLater();
00425 }
00426
00427 tabWidget()->removeTab( index );
00428 emit undoableChangeHappened();
00429 }
00430 }
00431
00432 void TabbedPlotWidget::on_buttonLinkHorizontalScale_toggled(bool checked)
00433 {
00434 _horizontal_link = checked;
00435
00436 for (int i = 0; i < tabWidget()->count(); i++)
00437 {
00438 PlotMatrix* tab = static_cast<PlotMatrix*>( tabWidget()->widget(i) );
00439 tab->setHorizontalLink( _horizontal_link );
00440 }
00441 }
00442
00443 void TabbedPlotWidget::on_requestTabMovement(const QString & destination_name)
00444 {
00445 TabbedPlotWidget* destination_widget = TabbedPlotWidget::_instances[destination_name];
00446
00447 PlotMatrix* tab_to_move = currentTab();
00448 int index = tabWidget()->tabBar()->currentIndex ();
00449
00450 const QString& tab_name = this->tabWidget()->tabText(index);
00451
00452 destination_widget->tabWidget()->addTab( tab_to_move, tab_name );
00453
00454 qDebug() << "move "<< tab_name<< " into " << destination_name;
00455 emit undoableChangeHappened();
00456 }
00457
00458 void TabbedPlotWidget::on_moveTabIntoNewWindow()
00459 {
00460 emit sendTabToNewWindow( currentTab() );
00461 }
00462
00463 void TabbedPlotWidget::on_pushButtonShowLabel_pressed()
00464 {
00465 switch(_labels_status)
00466 {
00467 case LabelStatus::LEFT: _labels_status = LabelStatus::HIDDEN; break;
00468 case LabelStatus::RIGHT: _labels_status = LabelStatus::LEFT; break;
00469 case LabelStatus::HIDDEN: _labels_status = LabelStatus::RIGHT; break;
00470 }
00471 onLabelStatusChanged( );
00472 }
00473
00474
00475 bool TabbedPlotWidget::eventFilter(QObject *obj, QEvent *event)
00476 {
00477 QTabBar* tab_bar = tabWidget()->tabBar();
00478
00479 if (obj == tab_bar )
00480 {
00481 if( event->type() == QEvent::MouseButtonPress)
00482 {
00483 QMouseEvent *mouse_event = static_cast<QMouseEvent*>(event);
00484
00485 int index = tab_bar->tabAt( mouse_event->pos() );
00486 tab_bar->setCurrentIndex( index );
00487
00488
00489 if( mouse_event->button() == Qt::RightButton )
00490 {
00491 QMenu* submenu = new QMenu("Move tab to...");
00492 _tab_menu->addMenu( submenu );
00493
00494 std::map<QString,TabbedPlotWidget*>::iterator it;
00495 QSignalMapper* signalMapper = new QSignalMapper(submenu);
00496
00497
00498 QAction* action_new_window = submenu->addAction( "New Window" );
00499
00500 QIcon icon;
00501 icon.addFile(QStringLiteral(":/icons/resources/light/stacks.png"), QSize(16, 16));
00502
00503 action_new_window->setIcon( icon);
00504 submenu->addSeparator();
00505
00506 connect( action_new_window, &QAction::triggered, this, &TabbedPlotWidget::on_moveTabIntoNewWindow );
00507
00508
00509 for(auto& it : TabbedPlotWidget::_instances)
00510 {
00511 QString name = it.first;
00512 TabbedPlotWidget* tabbed_menu = it.second;
00513 if( tabbed_menu != this )
00514 {
00515 QAction* action = submenu->addAction( name );
00516 connect(action, SIGNAL(triggered()), signalMapper, SLOT(map()));
00517 signalMapper->setMapping( action, name );
00518 }
00519 }
00520
00521 connect(signalMapper, SIGNAL(mapped(QString)), this, SLOT(on_requestTabMovement(QString)) );
00522
00523
00524 _tab_menu->exec( mouse_event->globalPos() );
00525
00526 submenu->deleteLater();
00527 }
00528 }
00529 }
00530
00531
00532 return QObject::eventFilter(obj, event);
00533 }
00534
00535 void TabbedPlotWidget::onLabelStatusChanged()
00536 {
00537 for(int i=0; i< tabWidget()->count(); i++)
00538 {
00539 PlotMatrix* matrix = static_cast<PlotMatrix*>( tabWidget()->widget(i) );
00540
00541 for(unsigned p=0; p< matrix->plotCount(); p++)
00542 {
00543 PlotWidget* plot = matrix->plotAt(p);
00544
00545 plot->activateLegend( _labels_status != LabelStatus::HIDDEN );
00546 if( _labels_status == LabelStatus::LEFT)
00547 {
00548 plot->setLegendAlignment( Qt::AlignLeft );
00549 }
00550 else if( _labels_status == LabelStatus::RIGHT)
00551 {
00552 plot->setLegendAlignment( Qt::AlignRight);
00553 }
00554 plot->replot();
00555 }
00556 }
00557 }
00558
00559 void TabbedPlotWidget::closeEvent(QCloseEvent *event)
00560 {
00561 TabbedPlotWidget::_instances.erase(name());
00562 }
00563
00564 const std::map<QString, TabbedPlotWidget *> &TabbedPlotWidget::instances()
00565 {
00566 return TabbedPlotWidget::_instances;
00567 }
00568
00569 TabbedPlotWidget* TabbedPlotWidget::instance(const QString &key)
00570 {
00571 auto it = TabbedPlotWidget::_instances.find(key);
00572 if( it == TabbedPlotWidget::_instances.end())
00573 {
00574 return nullptr;
00575 }
00576 else{
00577 return it->second;
00578 }
00579 }
00580
00581 void TabbedPlotWidget::setControlsVisible(bool visible)
00582 {
00583 ui->widgetControls->setVisible(visible);
00584 }
00585
00586
00587